mig 134 was inserting code='upc.apl' (2 segments) into paliad.proceeding_types, which carries paliad_proceeding_code_shape CHECK requiring 3 dot-segments OR '^_archived_'. Every container restart hit the constraint, rolled the migration TXN back, and crash-looped paliad.de. Rename the unified Berufung code to 'upc.apl.unified' (3 segments, satisfies the constraint, preserves design intent). The pre-existing constraint is a useful jurisdiction.category.specific invariant — keep it, fix the new row. Touched only string literals: - mig 134 up.sql + down.sql (insert, lookups, post-checks) - frontend/src/verfahrensablauf.tsx (UPC_TYPES code + i18nKey) - frontend/src/client/verfahrensablauf.ts (APPELLANT_AXIS + APPEAL_TARGET sets) - frontend/src/client/i18n.ts (DE + EN translation rows) - frontend/src/i18n-keys.ts (auto-regen via bun build) - internal/services/lookup_events_test.go (anchor-row assertion) Verified: `grep -rn "'upc\.apl'\|\"upc\.apl\""` returns zero hits. go build, bun run build, go test ./... all green.
264 lines
10 KiB
SQL
264 lines
10 KiB
SQL
-- 134_berufung_unification — Slice B1, m/paliad#124, t-paliad-298+
|
|
--
|
|
-- Collapses the 3 active UPC appeal proceeding_types (upc.apl.merits,
|
|
-- upc.apl.cost, upc.apl.order — 16 rules across 3 codes) into ONE
|
|
-- unified upc.apl proceeding type + an `appeal_target` discriminator on
|
|
-- both proceeding_types (top-level marker) and deadline_rules
|
|
-- (per-row applies-to set, text[] for multi-target rules).
|
|
--
|
|
-- ADDITIVE ONLY. The migration:
|
|
-- 1. Adds the two columns + check constraints.
|
|
-- 2. Inserts the new upc.apl proceeding type.
|
|
-- 3. Audit-first: NOTICES every row about to be touched.
|
|
-- 4. Reassigns rule rows from the 3 old types to upc.apl, stamping
|
|
-- applies_to_target by source proceeding code.
|
|
-- 5. Archives (is_active=false) the 3 old proceeding_types — NEVER
|
|
-- deletes them, so any historical project_event_choices / FK
|
|
-- references stay intact.
|
|
--
|
|
-- Schadensbemessung + Bucheinsicht get NO rule rows in this migration
|
|
-- (m's 2026-05-26 decision: distinct rule sets, not shared with
|
|
-- merits). Their appeal_target enum values are defined and addressable
|
|
-- by CalcOptions.AppealTarget; the engine returns an empty timeline
|
|
-- until rules are seeded in a follow-up slice (likely via
|
|
-- /admin/rules, pairing with t-paliad-193 orphan-concept-seed).
|
|
--
|
|
-- See docs/design-litigation-planner-2026-05-26.md §18.1.
|
|
|
|
-- ---------------------------------------------------------------
|
|
-- 1. Schema additions
|
|
-- ---------------------------------------------------------------
|
|
|
|
ALTER TABLE paliad.proceeding_types
|
|
ADD COLUMN appeal_target text NULL;
|
|
|
|
ALTER TABLE paliad.proceeding_types
|
|
ADD CONSTRAINT proceeding_types_appeal_target_chk
|
|
CHECK (appeal_target IS NULL OR appeal_target IN (
|
|
'endentscheidung',
|
|
'kostenentscheidung',
|
|
'anordnung',
|
|
'schadensbemessung',
|
|
'bucheinsicht'
|
|
));
|
|
|
|
COMMENT ON COLUMN paliad.proceeding_types.appeal_target IS
|
|
'Top-level appeal-target marker. NULL on non-appeal proceedings. '
|
|
'Reserved for future variants — today only the unified upc.apl row '
|
|
'has this NULL (the actual per-rule target set lives on '
|
|
'paliad.deadline_rules.applies_to_target).';
|
|
|
|
ALTER TABLE paliad.deadline_rules
|
|
ADD COLUMN applies_to_target text[] NULL;
|
|
|
|
ALTER TABLE paliad.deadline_rules
|
|
ADD CONSTRAINT deadline_rules_applies_to_target_chk
|
|
CHECK (
|
|
applies_to_target IS NULL
|
|
OR applies_to_target <@ ARRAY[
|
|
'endentscheidung',
|
|
'kostenentscheidung',
|
|
'anordnung',
|
|
'schadensbemessung',
|
|
'bucheinsicht'
|
|
]::text[]
|
|
);
|
|
|
|
COMMENT ON COLUMN paliad.deadline_rules.applies_to_target IS
|
|
'Set of appeal_target slugs this rule applies to. NULL on rules '
|
|
'that don''t belong to an appeal proceeding. The engine filters '
|
|
'by CalcOptions.AppealTarget — rules whose applies_to_target '
|
|
'contains the requested slug are emitted; others are suppressed.';
|
|
|
|
-- ---------------------------------------------------------------
|
|
-- 2. Insert the unified upc.apl row.
|
|
--
|
|
-- Inherits default_color from the merits row (the most-used appeal
|
|
-- track today). sort_order follows the cluster of UPC proceedings;
|
|
-- placed just before upc.apl.merits's old slot so the chip-grouped
|
|
-- picker UI lands Berufung in a sensible position. Tweakable later
|
|
-- without a migration.
|
|
-- ---------------------------------------------------------------
|
|
|
|
INSERT INTO paliad.proceeding_types (
|
|
code, name, name_en, description, jurisdiction, category,
|
|
default_color, sort_order, is_active, display_order,
|
|
appeal_target
|
|
)
|
|
SELECT
|
|
'upc.apl.unified',
|
|
'Berufungsverfahren',
|
|
'Appeal',
|
|
'Vereinheitlichtes Berufungsverfahren — wählen Sie anschließend, '
|
|
'worauf die Berufung sich richtet (Endentscheidung, '
|
|
'Kostenentscheidung, Anordnung, Schadensbemessung, Bucheinsicht).',
|
|
'UPC',
|
|
'fristenrechner',
|
|
default_color,
|
|
sort_order,
|
|
true,
|
|
display_order,
|
|
NULL
|
|
FROM paliad.proceeding_types
|
|
WHERE code = 'upc.apl.merits';
|
|
|
|
-- ---------------------------------------------------------------
|
|
-- 3. Audit-first RAISE NOTICE pass.
|
|
--
|
|
-- Lists every rule row that will be reassigned + every proceeding_type
|
|
-- row that will be archived. The migration runs to completion either
|
|
-- way; the operator reads the notices to confirm scope before the
|
|
-- next migration in the chain.
|
|
-- ---------------------------------------------------------------
|
|
|
|
DO $$
|
|
DECLARE
|
|
rec record;
|
|
upc_apl_id int;
|
|
rules_touched int := 0;
|
|
procs_archived int := 0;
|
|
BEGIN
|
|
SELECT id INTO upc_apl_id
|
|
FROM paliad.proceeding_types
|
|
WHERE code = 'upc.apl.unified';
|
|
RAISE NOTICE '[mig 134] new upc.apl.unified proceeding_type_id = %', upc_apl_id;
|
|
|
|
RAISE NOTICE '[mig 134] Rules to reassign to upc.apl.unified with applies_to_target:';
|
|
FOR rec IN
|
|
SELECT dr.id AS rule_id,
|
|
pt.code AS old_proceeding,
|
|
dr.submission_code,
|
|
dr.name
|
|
FROM paliad.deadline_rules dr
|
|
JOIN paliad.proceeding_types pt ON pt.id = dr.proceeding_type_id
|
|
WHERE pt.code IN ('upc.apl.merits', 'upc.apl.cost', 'upc.apl.order')
|
|
AND dr.is_active = true
|
|
ORDER BY pt.code, dr.sequence_order
|
|
LOOP
|
|
RAISE NOTICE '[mig 134] % % % (%)',
|
|
rec.old_proceeding, rec.submission_code, rec.name, rec.rule_id;
|
|
rules_touched := rules_touched + 1;
|
|
END LOOP;
|
|
RAISE NOTICE '[mig 134] Total rules to reassign: %', rules_touched;
|
|
|
|
RAISE NOTICE '[mig 134] Proceeding_types to archive (is_active=false):';
|
|
FOR rec IN
|
|
SELECT id, code, name
|
|
FROM paliad.proceeding_types
|
|
WHERE code IN ('upc.apl.merits', 'upc.apl.cost', 'upc.apl.order')
|
|
ORDER BY sort_order
|
|
LOOP
|
|
RAISE NOTICE '[mig 134] % % (id=%)', rec.code, rec.name, rec.id;
|
|
procs_archived := procs_archived + 1;
|
|
END LOOP;
|
|
RAISE NOTICE '[mig 134] Total proceeding_types to archive: %', procs_archived;
|
|
END $$;
|
|
|
|
-- ---------------------------------------------------------------
|
|
-- 4. Reassign rule rows.
|
|
--
|
|
-- Stamp applies_to_target by source proceeding code, then point all
|
|
-- 16 rules at the new upc.apl row.
|
|
-- ---------------------------------------------------------------
|
|
|
|
-- 4a. upc.apl.merits → applies_to_target = {endentscheidung}
|
|
UPDATE paliad.deadline_rules dr
|
|
SET applies_to_target = ARRAY['endentscheidung']::text[]
|
|
FROM paliad.proceeding_types pt
|
|
WHERE pt.id = dr.proceeding_type_id
|
|
AND pt.code = 'upc.apl.merits'
|
|
AND dr.is_active = true;
|
|
|
|
-- 4b. upc.apl.cost → applies_to_target = {kostenentscheidung}
|
|
UPDATE paliad.deadline_rules dr
|
|
SET applies_to_target = ARRAY['kostenentscheidung']::text[]
|
|
FROM paliad.proceeding_types pt
|
|
WHERE pt.id = dr.proceeding_type_id
|
|
AND pt.code = 'upc.apl.cost'
|
|
AND dr.is_active = true;
|
|
|
|
-- 4c. upc.apl.order → applies_to_target = {anordnung}
|
|
UPDATE paliad.deadline_rules dr
|
|
SET applies_to_target = ARRAY['anordnung']::text[]
|
|
FROM paliad.proceeding_types pt
|
|
WHERE pt.id = dr.proceeding_type_id
|
|
AND pt.code = 'upc.apl.order'
|
|
AND dr.is_active = true;
|
|
|
|
-- 4d. Reassign all 16 rules to the new upc.apl.unified proceeding_type row.
|
|
UPDATE paliad.deadline_rules dr
|
|
SET proceeding_type_id = (
|
|
SELECT id FROM paliad.proceeding_types WHERE code = 'upc.apl.unified'
|
|
)
|
|
FROM paliad.proceeding_types pt
|
|
WHERE pt.id = dr.proceeding_type_id
|
|
AND pt.code IN ('upc.apl.merits', 'upc.apl.cost', 'upc.apl.order');
|
|
|
|
-- ---------------------------------------------------------------
|
|
-- 5. Archive the 3 old proceeding_types.
|
|
--
|
|
-- NEVER DELETE — historical project_event_choices and project FKs
|
|
-- (paliad.projects.proceeding_type_id) may still reference these IDs.
|
|
-- The is_active=false flag stops them appearing in the picker but
|
|
-- preserves FK integrity for historical reads.
|
|
-- ---------------------------------------------------------------
|
|
|
|
UPDATE paliad.proceeding_types
|
|
SET is_active = false,
|
|
updated_at = now()
|
|
WHERE code IN ('upc.apl.merits', 'upc.apl.cost', 'upc.apl.order');
|
|
|
|
-- ---------------------------------------------------------------
|
|
-- 6. Post-migration sanity check.
|
|
-- ---------------------------------------------------------------
|
|
|
|
DO $$
|
|
DECLARE
|
|
unified_count int;
|
|
archived_count int;
|
|
target_distribution record;
|
|
BEGIN
|
|
SELECT COUNT(*) INTO unified_count
|
|
FROM paliad.deadline_rules dr
|
|
JOIN paliad.proceeding_types pt ON pt.id = dr.proceeding_type_id
|
|
WHERE pt.code = 'upc.apl.unified' AND dr.is_active = true;
|
|
RAISE NOTICE '[mig 134] post: rules on unified upc.apl.unified = % (expected 16)', unified_count;
|
|
IF unified_count <> 16 THEN
|
|
RAISE EXCEPTION '[mig 134] FAILED — expected 16 rules on upc.apl.unified, got %', unified_count;
|
|
END IF;
|
|
|
|
SELECT COUNT(*) INTO archived_count
|
|
FROM paliad.proceeding_types
|
|
WHERE code IN ('upc.apl.merits', 'upc.apl.cost', 'upc.apl.order')
|
|
AND is_active = false;
|
|
RAISE NOTICE '[mig 134] post: archived old appeal proceeding_types = % (expected 3)', archived_count;
|
|
IF archived_count <> 3 THEN
|
|
RAISE EXCEPTION '[mig 134] FAILED — expected 3 archived types, got %', archived_count;
|
|
END IF;
|
|
|
|
FOR target_distribution IN
|
|
SELECT unnest(applies_to_target) AS target, COUNT(*) AS n
|
|
FROM paliad.deadline_rules dr
|
|
JOIN paliad.proceeding_types pt ON pt.id = dr.proceeding_type_id
|
|
WHERE pt.code = 'upc.apl.unified' AND dr.is_active = true
|
|
GROUP BY unnest(applies_to_target)
|
|
ORDER BY 1
|
|
LOOP
|
|
RAISE NOTICE '[mig 134] post: applies_to_target=% count=%',
|
|
target_distribution.target, target_distribution.n;
|
|
END LOOP;
|
|
END $$;
|
|
|
|
-- ---------------------------------------------------------------
|
|
-- TODO (follow-up slice, not in 134):
|
|
--
|
|
-- Seed rules for Schadensbemessung-as-appeal + Bucheinsicht-as-appeal.
|
|
-- m's 2026-05-26 decision: distinct rule sets, NOT shared with merits.
|
|
-- - Schadensbemessung: anchor on R.118.4 decision; conjecture 2/4-month
|
|
-- merits-style track but distinct legal basis.
|
|
-- - Bucheinsicht: anchor on R.142 (Lay-open-books decision); conjecture
|
|
-- 15-day track per R.220.2 + R.224.2.b.
|
|
-- Can pair with t-paliad-193 orphan-concept-seed if m wants a combined
|
|
-- editorial pass via /admin/rules.
|
|
-- ---------------------------------------------------------------
|