Files
accounted/supabase/migrations/20260416120000_sie_files_and_fiscal_period_sync.sql

103 lines
3.7 KiB
PL/PgSQL

-- Sync two schema objects that production carries but the repo lost
-- in the PR #244 consolidation:
-- 1. sie-files storage bucket + RLS policies (originally 20260408130000)
-- 2. fiscal_periods trigger that only lets the FIRST period start mid-month
-- (originally 20260409165300)
--
-- Both sections are idempotent so re-applying is safe on prod, staging,
-- preview branches, and fresh installs.
--
-- Fixes:
-- - Archive upload fails with "new row violates row-level security policy"
-- because prod policies still required the path to start with auth.uid(),
-- but the code (post multi-tenant refactor 1534979) uploads to
-- {company_id}/{import_id}.se. New policies scope access to every member
-- of the company that owns the path prefix.
-- - Fresh/preview DBs had no equivalent of the non-first-of-month trigger,
-- so local tests didn't catch the constraint that bit prod users.
-- =============================================================================
-- 1. sie-files bucket
-- =============================================================================
INSERT INTO storage.buckets (id, name, public, file_size_limit, allowed_mime_types)
VALUES (
'sie-files',
'sie-files',
false,
52428800, -- 50 MB, matches MAX_FILE_SIZE in the SIE parse route
ARRAY['text/plain', 'application/octet-stream']
)
ON CONFLICT (id) DO UPDATE SET
file_size_limit = EXCLUDED.file_size_limit,
allowed_mime_types = EXCLUDED.allowed_mime_types;
-- Drop the legacy user_id-scoped policies (pre multi-tenant refactor).
DROP POLICY IF EXISTS "Users can upload SIE files to own folder" ON storage.objects;
DROP POLICY IF EXISTS "Users can read own SIE files" ON storage.objects;
-- Drop the names used by this migration in case it's re-applied.
DROP POLICY IF EXISTS sie_files_insert ON storage.objects;
DROP POLICY IF EXISTS sie_files_select ON storage.objects;
CREATE POLICY sie_files_insert
ON storage.objects
FOR INSERT
TO authenticated
WITH CHECK (
bucket_id = 'sie-files'
AND (storage.foldername(name))[1]::uuid IN (SELECT public.user_company_ids())
);
CREATE POLICY sie_files_select
ON storage.objects
FOR SELECT
TO authenticated
USING (
bucket_id = 'sie-files'
AND (storage.foldername(name))[1]::uuid IN (SELECT public.user_company_ids())
);
-- No UPDATE or DELETE policies — WORM semantics for BFL 7 kap. retention.
-- Service role bypasses RLS for admin/cron cleanup.
-- =============================================================================
-- 2. fiscal_periods: only the first period per company may start mid-month
-- =============================================================================
-- Drop the old unconditional CHECK constraint if it's still around from
-- 20260224190818 (it was superseded by the trigger in prod but may still
-- exist on fresh installs that replayed the early migrations).
ALTER TABLE public.fiscal_periods
DROP CONSTRAINT IF EXISTS fiscal_period_start_first_of_month;
CREATE OR REPLACE FUNCTION public.enforce_first_of_month_for_subsequent_periods()
RETURNS trigger
LANGUAGE plpgsql
AS $$
BEGIN
IF EXTRACT(DAY FROM NEW.period_start) = 1 THEN
RETURN NEW;
END IF;
IF EXISTS (
SELECT 1 FROM public.fiscal_periods
WHERE company_id = NEW.company_id
AND id IS DISTINCT FROM NEW.id
) THEN
RAISE EXCEPTION 'Non-first fiscal period must start on the 1st of a month';
END IF;
RETURN NEW;
END;
$$;
DROP TRIGGER IF EXISTS enforce_period_start_day ON public.fiscal_periods;
CREATE TRIGGER enforce_period_start_day
BEFORE INSERT OR UPDATE ON public.fiscal_periods
FOR EACH ROW
EXECUTE FUNCTION public.enforce_first_of_month_for_subsequent_periods();
NOTIFY pgrst, 'reload schema';