-- t-paliad-238: dedicated Submissions/Schriftsätze page. -- -- paliad.submission_drafts holds the lawyer's per-(project, submission_code) -- draft state for the new editor at /projects/{id}/submissions/{code}/draft. -- Each row is one named draft owned by one user; multiple drafts per -- (project, submission_code, user_id) are supported via the `name` field -- (auto-generated "Entwurf 1", "Entwurf 2", … and lawyer-renameable). -- -- `variables` carries the lawyer's overrides for the placeholder map -- assembled at export time by SubmissionVarsService — empty string forces -- the [KEIN WERT: …] marker; absent key falls back to the resolved bag. -- -- `last_exported_at` / `last_exported_sha` record provenance of the most -- recent .docx export (template SHA pinned for audit). Audit rows live -- in paliad.system_audit_log + paliad.project_events; this table is the -- lawyer's working state, not the audit log. -- -- Visibility: per project CLAUDE.md, every project-scoped resource gates -- on paliad.can_see_project. UPDATE / DELETE additionally require the -- draft's user_id to match auth.uid() so two co-team-members don't stomp -- on each other's drafts (head-confirmed Q-E4 owner-scoped pick). CREATE TABLE IF NOT EXISTS paliad.submission_drafts ( id uuid PRIMARY KEY DEFAULT gen_random_uuid(), project_id uuid NOT NULL REFERENCES paliad.projects(id) ON DELETE CASCADE, submission_code text NOT NULL, user_id uuid NOT NULL REFERENCES paliad.users(id) ON DELETE CASCADE, name text NOT NULL, variables jsonb NOT NULL DEFAULT '{}'::jsonb, last_exported_at timestamptz, last_exported_sha text, created_at timestamptz NOT NULL DEFAULT now(), updated_at timestamptz NOT NULL DEFAULT now(), CONSTRAINT submission_drafts_unique_per_user UNIQUE (project_id, submission_code, user_id, name) ); CREATE INDEX IF NOT EXISTS submission_drafts_project_user_idx ON paliad.submission_drafts (project_id, user_id, submission_code, updated_at DESC); ALTER TABLE paliad.submission_drafts ENABLE ROW LEVEL SECURITY; DROP POLICY IF EXISTS submission_drafts_select ON paliad.submission_drafts; CREATE POLICY submission_drafts_select ON paliad.submission_drafts FOR SELECT TO authenticated USING (paliad.can_see_project(project_id)); DROP POLICY IF EXISTS submission_drafts_insert ON paliad.submission_drafts; CREATE POLICY submission_drafts_insert ON paliad.submission_drafts FOR INSERT TO authenticated WITH CHECK ( user_id = auth.uid() AND paliad.can_see_project(project_id) ); DROP POLICY IF EXISTS submission_drafts_update ON paliad.submission_drafts; CREATE POLICY submission_drafts_update ON paliad.submission_drafts FOR UPDATE TO authenticated USING (user_id = auth.uid() AND paliad.can_see_project(project_id)) WITH CHECK (user_id = auth.uid() AND paliad.can_see_project(project_id)); DROP POLICY IF EXISTS submission_drafts_delete ON paliad.submission_drafts; CREATE POLICY submission_drafts_delete ON paliad.submission_drafts FOR DELETE TO authenticated USING (user_id = auth.uid() AND paliad.can_see_project(project_id)); -- updated_at maintenance: trigger function lives once per schema; reuse if -- it already exists, otherwise create the standard shape. CREATE OR REPLACE FUNCTION paliad.tg_set_updated_at() RETURNS trigger LANGUAGE plpgsql AS $$ BEGIN NEW.updated_at = now(); RETURN NEW; END; $$; DROP TRIGGER IF EXISTS submission_drafts_set_updated_at ON paliad.submission_drafts; CREATE TRIGGER submission_drafts_set_updated_at BEFORE UPDATE ON paliad.submission_drafts FOR EACH ROW EXECUTE FUNCTION paliad.tg_set_updated_at(); COMMENT ON TABLE paliad.submission_drafts IS 't-paliad-238: per-(project, submission_code, user) named drafts for the dedicated Submissions/Schriftsätze page. Each row holds the lawyer-edited variable overrides for the .docx export. Audit rows live in paliad.system_audit_log + paliad.project_events.';