Files
paliad/internal/db/migrations/038_concept_links_and_legal_source.up.sql
m 78966ec098 feat(t-paliad-131): Phase A — concept layer + AnchorOverrides + click-to-edit dates
PR-1 of the Unified Fristenrechner. Purely additive: new search-grouping
layer + per-rule date override capability. No coverage changes yet
(those land in PR-2 = Phase B1 UPC counterclaim cross-flows).

Migrations:
- 037: paliad.deadline_concepts (id, slug, name_de/en, aliases text[],
  party, category, sort_order). Trigram + GIN indexes for the search bar.
- 038: deadline_rules.concept_id (uuid FK), legal_source (text);
  event_deadlines.legal_source; trigger_events.concept_id (text slug,
  soft-link — youpc imports keep their bigint PK).
- 039: deadline_rules.condition_flag text → text[] (USING ARRAY[old]).
  Semantic: rule renders iff every element is in CalcOptions.Flags.
  Single-element arrays preserve the legacy with_ccr swap exactly.
- 040: seed 30 concept rows + backfill all 74 fristenrechner deadline_rules
  with concept_id; backfill legal_source from existing rule_code
  (e.g. 'RoP.023' → 'UPC.RoP.23.1', '§ 276 ZPO' → 'DE.ZPO.276.1',
  'Art. 108 EPÜ' → 'EU.EPÜ.108', 'R. 79(1) EPÜ' → 'EU.EPC-R.79.1').

Calculator (services/fristenrechner.go):
- ConditionFlag is now pq.StringArray (matches text[] schema). New
  allFlagsSet() helper gates rule rendering; rules with multi-element
  flags require ALL of them set (prep for Phase B1 with_amend ∧ with_cci).
- CalcOptions.AnchorOverrides map[string]string (rule_code → YYYY-MM-DD).
  The tree-walk consults overrideDates[parent.code] before reading the
  computed-date map; lets a downstream rule re-anchor on a user-set date.
- IsCourtSet rows that get an override stop being placeholder and emit
  the user's date as a real anchor (so downstream cost_app etc. compute
  off it). New IsOverridden flag in UIDeadline so the UI can highlight
  user-edited rows.
- LegalSource surfaced on UIDeadline for future search-card display.

UI (frontend/src/client/fristenrechner.ts + global.css + i18n):
- Each timeline / column rule date is click-to-edit. Click → inline
  date input → blur or Enter → POST with anchorOverrides → re-render.
  Empty value clears the override. Escape cancels. Root-event rows
  (the trigger anchor) stay non-editable — that's the trigger-date input.
- Override map cleared on proceeding switch / reset; persists across
  trigger-date / flag toggle changes within the same proceeding.
- New CSS: subtle hover underline on .frist-date-edit; lime border on
  .timeline-date--overridden + .frist-date-edit-input.
- New i18n key deadlines.date.edit.hint (DE + EN).

Handler (handlers/fristenrechner.go):
- POST body gains optional anchorOverrides map<string,string>; passed
  through to CalcOptions.

Tests:
- TestAllFlagsSet covers single-flag legacy semantic, two-flag AND
  semantic, empty-required unconditional, extra-flags-no-effect.
- Existing TestIsCourtDeterminedRule unchanged.

Phase A ships standalone — Phase B1 (UPC counterclaim cross-flows) and
Phase C/D (search backend + concept-card UI) follow.
2026-05-05 00:05:12 +02:00

61 lines
2.9 KiB
SQL

-- t-paliad-131 Phase A2: link deadline_rules / event_deadlines /
-- trigger_events to the new concept layer + introduce structured
-- legal_source citations.
--
-- legal_source format: <JURIS>.<CODE>.<§/Art./R>[.<Para>][.<Sub>]
-- UPC.RoP.23.1, UPC.RoP.29.a, UPC.UPCA.49.5, UPC.Statute.21
-- EU.EPÜ.108, EU.EPC-R.79.1, EU.RPBA.12.1.c
-- DE.ZPO.276.1, DE.PatG.111.1, DE.DPMAV.5
--
-- Distinct from rule_code (the internal canonical id the calculator
-- uses): legal_source is the human citation, indexed for search and
-- usable as a filter prefix (e.g. legal_source LIKE 'DE.PatG.%').
--
-- trigger_events.concept_id is text-by-slug because trigger_events has
-- bigint PK, was imported verbatim from youpc with IDs preserved for
-- diffable resync, and a uuid FK would touch that invariant.
ALTER TABLE paliad.deadline_rules
ADD COLUMN IF NOT EXISTS concept_id uuid REFERENCES paliad.deadline_concepts(id),
ADD COLUMN IF NOT EXISTS legal_source text;
COMMENT ON COLUMN paliad.deadline_rules.concept_id IS
'Links this rule to its canonical paliad.deadline_concepts entry. '
'NULL until backfilled in migration 040; thereafter required for any '
'rule that should appear in the search bar concept-card results.';
COMMENT ON COLUMN paliad.deadline_rules.legal_source IS
'Structured citation code per t-paliad-131 §4.5 — '
'<JURIS>.<CODE>.<§/Art./R>[.<Para>][.<Sub>]. Distinct from rule_code '
'(internal calculator id). Indexed for trigram search; filterable as '
'a prefix (LIKE ''DE.PatG.%'').';
CREATE INDEX deadline_rules_concept_id ON paliad.deadline_rules (concept_id);
CREATE INDEX deadline_rules_legal_source ON paliad.deadline_rules (legal_source);
CREATE INDEX deadline_rules_legal_src_trgm ON paliad.deadline_rules
USING gin (legal_source gin_trgm_ops);
ALTER TABLE paliad.event_deadlines
ADD COLUMN IF NOT EXISTS legal_source text;
COMMENT ON COLUMN paliad.event_deadlines.legal_source IS
'Same structured citation format as deadline_rules.legal_source. '
'For event-driven deadlines, the rule_code linkage already lives in '
'event_deadline_rule_codes; legal_source is the searchable citation.';
CREATE INDEX event_deadlines_legal_src_trgm ON paliad.event_deadlines
USING gin (legal_source gin_trgm_ops);
-- trigger_events: soft-link to a concept by slug (text). No FK constraint
-- — concept_id stays valid even if a youpc resync re-IDs the trigger row.
ALTER TABLE paliad.trigger_events
ADD COLUMN IF NOT EXISTS concept_id text;
COMMENT ON COLUMN paliad.trigger_events.concept_id IS
'Slug of the paliad.deadline_concepts row this trigger represents. '
'Soft link (text, no FK) so a youpc resync that re-IDs the trigger '
'does not break the concept linkage. NULL = trigger has no canonical '
'concept (rare; mostly the cross-procedural Wiedereinsetzung family).';
CREATE INDEX trigger_events_concept_id ON paliad.trigger_events (concept_id);