Files
paliad/internal/db/migrations/012_fristenrechner_rules.up.sql
m d1909c766e feat: Phase C — Fristenrechner → DB-backed via FristenrechnerService
- Delete internal/calc/deadlines.go/deadline_rules.go/holidays.go (ported to services)
- fristenrechner handler routes through FristenrechnerService when pool present
- Returns 503 with German message when DATABASE_URL unset (page still renders)
- Migration 012: add name_en columns + seed 9 UI-facing proceeding types
- Commit captures cronus's work after session termination
2026-04-16 17:11:02 +02:00

231 lines
22 KiB
SQL

-- Phase C: migrate Fristenrechner's in-memory rule tree into paliad.deadline_rules.
--
-- The Paliad public Fristenrechner UI exposes 9 simple proceeding types
-- (UPC_INF/UPC_REV/UPC_PI/UPC_APP/DE_INF/DE_NULL/EPA_OPP/EPA_APP/EP_GRANT)
-- with bilingual names. These sit alongside the richer KanzlAI-ported types
-- (INF/REV/CCR/APM/APP/AMD/ZPO_CIVIL) used for matter-attached Fristen.
--
-- Rules ported verbatim from the pre-Phase-C internal/calc/deadline_rules.go
-- so /tools/fristenrechner results remain byte-identical after the swap.
-- Codes intentionally reuse the old string values (e.g. "inf.soc") even
-- though some collide with KanzlAI rule codes — proceeding_type_id
-- disambiguates, and the client renders by name/nameEN, not code.
ALTER TABLE paliad.proceeding_types
ADD COLUMN IF NOT EXISTS name_en text NOT NULL DEFAULT '';
ALTER TABLE paliad.deadline_rules
ADD COLUMN IF NOT EXISTS name_en text NOT NULL DEFAULT '';
-- Backfill name_en for existing KanzlAI-ported entries.
UPDATE paliad.proceeding_types SET name_en = name WHERE name_en = '';
UPDATE paliad.deadline_rules SET name_en = name WHERE name_en = '';
-- ============================================================================
-- UI-facing proceeding types. Name/NameEN copied verbatim from the old
-- in-memory ProceedingType structs.
-- ============================================================================
INSERT INTO paliad.proceeding_types (code, name, name_en, description, jurisdiction, category, default_color, sort_order, is_active) VALUES
('UPC_INF', 'Verletzungsverfahren', 'Infringement Action', 'UPC-Verletzungsklage', 'UPC', 'fristenrechner', '#3b82f6', 101, true),
('UPC_REV', 'Nichtigkeitsklage', 'Revocation Action', 'UPC-Nichtigkeitsklage', 'UPC', 'fristenrechner', '#ef4444', 102, true),
('UPC_PI', 'Einstweilige Maßnahmen', 'Provisional Measures', 'UPC-Einstweilige Maßnahmen', 'UPC', 'fristenrechner', '#f59e0b', 103, true),
('UPC_APP', 'Berufung', 'Appeal', 'UPC-Berufung', 'UPC', 'fristenrechner', '#8b5cf6', 104, true),
('DE_INF', 'Verletzungsklage (LG)', 'Infringement (Regional Court)', 'Deutsche Verletzungsklage', 'DE', 'fristenrechner', '#6366f1', 201, true),
('DE_NULL', 'Nichtigkeitsverfahren (BPatG)', 'Nullity (Federal Patent Court)', 'Deutsche Nichtigkeitsklage', 'DE', 'fristenrechner', '#6366f1', 202, true),
('EPA_OPP', 'Einspruchsverfahren', 'Opposition Proceedings', 'EPA-Einspruchsverfahren', 'EPA', 'fristenrechner', '#059669', 301, true),
('EPA_APP', 'Beschwerdeverfahren', 'Appeal Proceedings', 'EPA-Beschwerdeverfahren', 'EPA', 'fristenrechner', '#059669', 302, true),
('EP_GRANT', 'EP-Erteilungsverfahren', 'EP Grant Procedure', 'EP-Erteilungsverfahren', 'EPA', 'fristenrechner', '#059669', 303, true)
ON CONFLICT (code) DO NOTHING;
-- ============================================================================
-- Rules — every field ported 1:1 from deadline_rules.go. Parent codes follow
-- the original RelativeTo chains. Zero-duration rules with NULL parent_id
-- render as IsRootEvent (due = trigger date); zero-duration rules with a
-- parent render as IsCourtSet (empty due date). This matches the old
-- in-memory classification.
-- ============================================================================
DO $$
DECLARE
v_upc_inf int; v_upc_rev int; v_upc_pi int; v_upc_app int;
v_de_inf int; v_de_null int;
v_epa_opp int; v_epa_app int; v_ep_grant int;
-- UPC_INF
r_inf_soc uuid; r_inf_sod uuid;
r_inf_reply uuid; r_inf_rejoin uuid;
-- UPC_REV
r_rev_app uuid; r_rev_defence uuid;
r_rev_reply uuid; r_rev_rejoin uuid;
-- UPC_PI
r_pi_app uuid;
-- UPC_APP
r_app_notice uuid; r_app_grounds uuid;
-- DE_INF
r_de_inf_klage uuid; r_de_inf_berufung uuid;
-- DE_NULL
r_de_null_klage uuid; r_de_null_berufung uuid;
-- EPA_OPP
r_epa_opp_grant uuid; r_epa_opp_frist uuid;
r_epa_opp_beschwerde uuid;
-- EPA_APP
r_epa_app_entsch uuid;
-- EP_GRANT
r_ep_grant_filing uuid; r_ep_grant_publish uuid;
BEGIN
SELECT id INTO v_upc_inf FROM paliad.proceeding_types WHERE code = 'UPC_INF';
SELECT id INTO v_upc_rev FROM paliad.proceeding_types WHERE code = 'UPC_REV';
SELECT id INTO v_upc_pi FROM paliad.proceeding_types WHERE code = 'UPC_PI';
SELECT id INTO v_upc_app FROM paliad.proceeding_types WHERE code = 'UPC_APP';
SELECT id INTO v_de_inf FROM paliad.proceeding_types WHERE code = 'DE_INF';
SELECT id INTO v_de_null FROM paliad.proceeding_types WHERE code = 'DE_NULL';
SELECT id INTO v_epa_opp FROM paliad.proceeding_types WHERE code = 'EPA_OPP';
SELECT id INTO v_epa_app FROM paliad.proceeding_types WHERE code = 'EPA_APP';
SELECT id INTO v_ep_grant FROM paliad.proceeding_types WHERE code = 'EP_GRANT';
-- Idempotent: skip if we've already seeded.
IF EXISTS (
SELECT 1 FROM paliad.deadline_rules
WHERE proceeding_type_id = v_upc_inf AND code = 'inf.soc'
) THEN
RETURN;
END IF;
-- ========================================================================
-- UPC_INF — 7 rules
-- ========================================================================
r_inf_soc := gen_random_uuid();
r_inf_sod := gen_random_uuid();
r_inf_reply := gen_random_uuid();
r_inf_rejoin := gen_random_uuid();
INSERT INTO paliad.deadline_rules (id, proceeding_type_id, parent_id, code, name, name_en, primary_party, event_type, is_mandatory, duration_value, duration_unit, rule_code, deadline_notes, sequence_order, is_active) VALUES
(r_inf_soc, v_upc_inf, NULL, 'inf.soc', 'Klageerhebung', 'Statement of Claim', 'claimant', 'filing', true, 0, 'months', NULL, NULL, 0, true),
(r_inf_sod, v_upc_inf, r_inf_soc, 'inf.sod', 'Klageerwiderung', 'Statement of Defence','defendant', 'filing', true, 3, 'months', 'RoP 23', NULL, 1, true),
(r_inf_reply, v_upc_inf, r_inf_sod, 'inf.reply', 'Replik', 'Reply to Defence', 'claimant', 'filing', true, 2, 'months', 'RoP 29b', NULL, 2, true),
(r_inf_rejoin, v_upc_inf, r_inf_reply, 'inf.rejoin', 'Duplik', 'Rejoinder', 'defendant', 'filing', true, 1, 'months', 'RoP 29c', NULL, 3, true),
(gen_random_uuid(), v_upc_inf, NULL, 'inf.interim', 'Zwischenverfahren', 'Interim Conference', 'court', 'hearing', true, 0, 'months', NULL, 'Termin vom Gericht bestimmt',4, true),
(gen_random_uuid(), v_upc_inf, NULL, 'inf.oral', 'Mündliche Verhandlung', 'Oral Hearing', 'court', 'hearing', true, 0, 'months', NULL, NULL, 5, true),
(gen_random_uuid(), v_upc_inf, NULL, 'inf.decision', 'Entscheidung', 'Decision', 'court', 'decision',true, 0, 'months', NULL, NULL, 6, true);
-- ========================================================================
-- UPC_REV — 7 rules
-- ========================================================================
r_rev_app := gen_random_uuid();
r_rev_defence := gen_random_uuid();
r_rev_reply := gen_random_uuid();
r_rev_rejoin := gen_random_uuid();
INSERT INTO paliad.deadline_rules (id, proceeding_type_id, parent_id, code, name, name_en, primary_party, event_type, is_mandatory, duration_value, duration_unit, rule_code, deadline_notes, sequence_order, is_active) VALUES
(r_rev_app, v_upc_rev, NULL, 'rev.app', 'Nichtigkeitsklage', 'Application for Revocation', 'claimant', 'filing', true, 0, 'months', NULL, NULL, 0, true),
(r_rev_defence, v_upc_rev, r_rev_app, 'rev.defence', 'Klageerwiderung', 'Defence to Revocation', 'defendant', 'filing', true, 3, 'months', NULL, NULL, 1, true),
(r_rev_reply, v_upc_rev, r_rev_defence, 'rev.reply', 'Replik', 'Reply', 'claimant', 'filing', true, 2, 'months', NULL, NULL, 2, true),
(r_rev_rejoin, v_upc_rev, r_rev_reply, 'rev.rejoin', 'Duplik', 'Rejoinder', 'defendant', 'filing', true, 2, 'months', NULL, NULL, 3, true),
(gen_random_uuid(), v_upc_rev, NULL, 'rev.interim', 'Zwischenverfahren', 'Interim Conference', 'court', 'hearing', true, 0, 'months', NULL, NULL, 4, true),
(gen_random_uuid(), v_upc_rev, NULL, 'rev.oral', 'Mündliche Verhandlung', 'Oral Hearing', 'court', 'hearing', true, 0, 'months', NULL, NULL, 5, true),
(gen_random_uuid(), v_upc_rev, NULL, 'rev.decision', 'Entscheidung', 'Decision', 'court', 'decision',true, 0, 'months', NULL, NULL, 6, true);
-- ========================================================================
-- UPC_PI — 4 rules
-- ========================================================================
r_pi_app := gen_random_uuid();
INSERT INTO paliad.deadline_rules (id, proceeding_type_id, parent_id, code, name, name_en, primary_party, event_type, is_mandatory, duration_value, duration_unit, rule_code, deadline_notes, sequence_order, is_active) VALUES
(r_pi_app, v_upc_pi, NULL, 'pi.app', 'Antrag', 'Application', 'claimant', 'filing', true, 0, 'months', NULL, NULL, 0, true),
(gen_random_uuid(), v_upc_pi, NULL, 'pi.response', 'Erwiderung', 'Response', 'defendant','filing', true, 0, 'months', NULL, 'Frist vom Gericht bestimmt', 1, true),
(gen_random_uuid(), v_upc_pi, NULL, 'pi.oral', 'Mündliche Verhandlung', 'Oral Hearing', 'court', 'hearing', true, 0, 'months', NULL, NULL, 2, true),
(gen_random_uuid(), v_upc_pi, NULL, 'pi.order', 'Beschluss', 'Order', 'court', 'decision',true, 0, 'months', NULL, NULL, 3, true);
-- ========================================================================
-- UPC_APP — 5 rules
-- ========================================================================
r_app_notice := gen_random_uuid();
r_app_grounds := gen_random_uuid();
INSERT INTO paliad.deadline_rules (id, proceeding_type_id, parent_id, code, name, name_en, primary_party, event_type, is_mandatory, duration_value, duration_unit, rule_code, deadline_notes, sequence_order, is_active) VALUES
(r_app_notice, v_upc_app, NULL, 'app.notice', 'Berufungseinlegung', 'Notice of Appeal', 'both', 'filing', true, 2, 'months', 'RoP 220.1', NULL, 0, true),
(r_app_grounds, v_upc_app, r_app_notice, 'app.grounds', 'Berufungsbegründung', 'Statement of Grounds', 'both', 'filing', true, 2, 'months', 'RoP 220.1', NULL, 1, true),
(gen_random_uuid(), v_upc_app, r_app_grounds, 'app.response', 'Berufungserwiderung', 'Response to Appeal', 'both', 'filing', true, 2, 'months', NULL, NULL, 2, true),
(gen_random_uuid(), v_upc_app, NULL, 'app.oral', 'Mündliche Verhandlung','Oral Hearing', 'court', 'hearing', true, 0, 'months', NULL, NULL, 3, true),
(gen_random_uuid(), v_upc_app, NULL, 'app.decision', 'Entscheidung', 'Decision', 'court', 'decision',true, 0, 'months', NULL, NULL, 4, true);
-- ========================================================================
-- DE_INF — 8 rules
-- ========================================================================
r_de_inf_klage := gen_random_uuid();
r_de_inf_berufung := gen_random_uuid();
INSERT INTO paliad.deadline_rules (id, proceeding_type_id, parent_id, code, name, name_en, primary_party, event_type, is_mandatory, duration_value, duration_unit, rule_code, deadline_notes, sequence_order, is_active) VALUES
(r_de_inf_klage, v_de_inf, NULL, 'de_inf.klage', 'Klageerhebung', 'Filing of Action', 'claimant', 'filing', true, 0, 'months', NULL, NULL, 0, true),
(gen_random_uuid(), v_de_inf, r_de_inf_klage, 'de_inf.erwidg', 'Klageerwiderung', 'Statement of Defence','defendant', 'filing', true, 6, 'weeks', '§ 276 ZPO', 'Regelfrist, kann verlängert werden', 1, true),
(gen_random_uuid(), v_de_inf, NULL, 'de_inf.replik', 'Replik', 'Reply', 'claimant', 'filing', false, 4, 'weeks', NULL, 'Frist vom Gericht bestimmt', 2, true),
(gen_random_uuid(), v_de_inf, NULL, 'de_inf.duplik', 'Duplik', 'Rejoinder', 'defendant', 'filing', false, 4, 'weeks', NULL, 'Frist vom Gericht bestimmt', 3, true),
(gen_random_uuid(), v_de_inf, NULL, 'de_inf.termin', 'Haupttermin', 'Main Hearing', 'court', 'hearing', true, 0, 'months', NULL, NULL, 4, true),
(gen_random_uuid(), v_de_inf, NULL, 'de_inf.urteil', 'Urteil', 'Judgment', 'court', 'decision',true, 0, 'months', NULL, NULL, 5, true),
(r_de_inf_berufung, v_de_inf, NULL, 'de_inf.berufung', 'Berufungsfrist', 'Appeal Period', 'both', 'filing', true, 1, 'months', '§ 517 ZPO', 'Ab Zustellung des Urteils', 6, true),
(gen_random_uuid(), v_de_inf, r_de_inf_berufung, 'de_inf.beruf_begr', 'Berufungsbegründung', 'Appeal Statement', 'both', 'filing', true, 2, 'months', '§ 520 ZPO', NULL, 7, true);
-- ========================================================================
-- DE_NULL — 6 rules
-- ========================================================================
r_de_null_klage := gen_random_uuid();
r_de_null_berufung := gen_random_uuid();
INSERT INTO paliad.deadline_rules (id, proceeding_type_id, parent_id, code, name, name_en, primary_party, event_type, is_mandatory, duration_value, duration_unit, rule_code, deadline_notes, sequence_order, is_active) VALUES
(r_de_null_klage, v_de_null, NULL, 'de_null.klage', 'Nichtigkeitsklage', 'Nullity Action', 'claimant', 'filing', true, 0, 'months', NULL, NULL, 0, true),
(gen_random_uuid(), v_de_null, r_de_null_klage, 'de_null.erwidg', 'Klageerwiderung', 'Defence', 'defendant', 'filing', true, 2, 'months', '§ 82 PatG', NULL, 1, true),
(gen_random_uuid(), v_de_null, NULL, 'de_null.termin', 'Mündliche Verhandlung', 'Oral Hearing', 'court', 'hearing', true, 0, 'months', NULL, NULL, 2, true),
(gen_random_uuid(), v_de_null, NULL, 'de_null.urteil', 'Urteil', 'Judgment', 'court', 'decision',true, 0, 'months', NULL, NULL, 3, true),
(r_de_null_berufung, v_de_null, NULL, 'de_null.berufung', 'Berufungsfrist', 'Appeal Period', 'both', 'filing', true, 1, 'months', '§ 110 PatG','Ab Zustellung des Urteils', 4, true),
(gen_random_uuid(), v_de_null, r_de_null_berufung, 'de_null.beruf_begr', 'Berufungsbegründung', 'Appeal Statement', 'both', 'filing', true, 1, 'months', '§ 111 PatG',NULL, 5, true);
-- ========================================================================
-- EPA_OPP — 6 rules
-- ========================================================================
r_epa_opp_grant := gen_random_uuid();
r_epa_opp_frist := gen_random_uuid();
r_epa_opp_beschwerde := gen_random_uuid();
INSERT INTO paliad.deadline_rules (id, proceeding_type_id, parent_id, code, name, name_en, primary_party, event_type, is_mandatory, duration_value, duration_unit, rule_code, deadline_notes, sequence_order, is_active) VALUES
(r_epa_opp_grant, v_epa_opp, NULL, 'epa_opp.grant', 'Veröffentlichung der Erteilung','Publication of Grant', 'court', 'decision',true, 0, 'months', NULL, NULL, 0, true),
(r_epa_opp_frist, v_epa_opp, r_epa_opp_grant, 'epa_opp.frist', 'Einspruchsfrist', 'Opposition Period', 'both', 'filing', true, 9, 'months', 'Art. 99 EPÜ', NULL, 1, true),
(gen_random_uuid(), v_epa_opp, r_epa_opp_frist, 'epa_opp.erwidg', 'Erwiderung des Patentinhabers', 'Proprietor''s Response','defendant', 'filing', true, 4, 'months', 'R. 79(1) EPÜ',NULL, 2, true),
(gen_random_uuid(), v_epa_opp, NULL, 'epa_opp.entsch', 'Entscheidung', 'Decision', 'court', 'decision',true, 0, 'months', NULL, NULL, 3, true),
(r_epa_opp_beschwerde, v_epa_opp, NULL, 'epa_opp.beschwerde', 'Beschwerdefrist', 'Appeal Period', 'both', 'filing', true, 2, 'months', 'Art. 108 EPÜ','Ab Zustellung der Entscheidung', 4, true),
(gen_random_uuid(), v_epa_opp, r_epa_opp_beschwerde, 'epa_opp.beschwerde_begr', 'Beschwerdebegründung', 'Statement of Grounds', 'both', 'filing', true, 4, 'months', 'Art. 108 EPÜ',NULL, 5, true);
-- ========================================================================
-- EPA_APP — 6 rules
-- ========================================================================
r_epa_app_entsch := gen_random_uuid();
INSERT INTO paliad.deadline_rules (id, proceeding_type_id, parent_id, code, name, name_en, primary_party, event_type, is_mandatory, duration_value, duration_unit, rule_code, deadline_notes, sequence_order, is_active) VALUES
(r_epa_app_entsch, v_epa_app, NULL, 'epa_app.entsch', 'Zustellung der Entscheidung', 'Notification of Decision','court', 'decision',true, 0, 'months', NULL, NULL, 0, true),
(gen_random_uuid(), v_epa_app, r_epa_app_entsch, 'epa_app.beschwerde', 'Beschwerdeeinlegung', 'Filing of Appeal', 'both', 'filing', true, 2, 'months', 'Art. 108 EPÜ', NULL, 1, true),
(gen_random_uuid(), v_epa_app, r_epa_app_entsch, 'epa_app.begr', 'Beschwerdebegründung', 'Statement of Grounds', 'both', 'filing', true, 4, 'months', 'Art. 108 EPÜ', 'Ab Zustellung, nicht ab Beschwerdeeinlegung',2, true),
(gen_random_uuid(), v_epa_app, NULL, 'epa_app.erwidg', 'Erwiderung', 'Response', 'both', 'filing', false, 0, 'months', NULL, 'Frist von der Beschwerdekammer bestimmt', 3, true),
(gen_random_uuid(), v_epa_app, NULL, 'epa_app.oral', 'Mündliche Verhandlung', 'Oral Proceedings', 'court', 'hearing', false, 0, 'months', NULL, NULL, 4, true),
(gen_random_uuid(), v_epa_app, NULL, 'epa_app.entsch2', 'Entscheidung', 'Decision', 'court', 'decision',true, 0, 'months', NULL, NULL, 5, true);
-- ========================================================================
-- EP_GRANT — 7 rules
-- ========================================================================
r_ep_grant_filing := gen_random_uuid();
r_ep_grant_publish := gen_random_uuid();
INSERT INTO paliad.deadline_rules (id, proceeding_type_id, parent_id, code, name, name_en, primary_party, event_type, is_mandatory, duration_value, duration_unit, rule_code, deadline_notes, sequence_order, is_active) VALUES
(r_ep_grant_filing, v_ep_grant, NULL, 'ep_grant.filing', 'Anmeldung', 'Filing', 'claimant', 'filing', true, 0, 'months', NULL, NULL, 0, true),
(gen_random_uuid(), v_ep_grant, r_ep_grant_filing, 'ep_grant.search', 'Recherchenbericht', 'Search Report', 'court', 'decision',true, 6, 'months', NULL, 'Richtwert, kann länger dauern', 1, true),
(r_ep_grant_publish, v_ep_grant, r_ep_grant_filing, 'ep_grant.publish', 'Veröffentlichung (A1)', 'Publication (A1)', 'court', 'decision',true, 18, 'months', 'Art. 93 EPÜ', 'Ab Prioritätstag', 2, true),
(gen_random_uuid(), v_ep_grant, r_ep_grant_publish, 'ep_grant.exam_req', 'Prüfungsantrag', 'Request for Examination', 'claimant', 'filing', true, 6, 'months', 'R. 70(1) EPÜ', 'Ab Hinweis auf Möglichkeit', 3, true),
(gen_random_uuid(), v_ep_grant, NULL, 'ep_grant.r71_3', 'Mitteilung nach R. 71(3)', 'Communication under R. 71(3)', 'court', 'decision',true, 0, 'months', 'R. 71(3) EPÜ', NULL, 4, true),
(gen_random_uuid(), v_ep_grant, NULL, 'ep_grant.approval', 'Zustimmung + Übersetzung', 'Approval + Translation', 'claimant', 'filing', true, 4, 'months', 'R. 71(3) EPÜ', 'Erteilungs- und Druckgebühr', 5, true),
(gen_random_uuid(), v_ep_grant, NULL, 'ep_grant.grant', 'Erteilung (B1)', 'Grant (B1)', 'court', 'decision',true, 0, 'months', NULL, NULL, 6, true);
END $$;