Files
paliad/internal/db/migrations/037_deadline_concepts.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

59 lines
2.8 KiB
SQL
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

-- t-paliad-131 Phase A1: paliad.deadline_concepts — the Unifier shape.
--
-- One concept = one legal idea ("Klageerwiderung", "Berufungsfrist",
-- "Wiedereinsetzung") that adapts duration / legal_source / notes per
-- proceeding-type context. Adding this layer above paliad.deadline_rules
-- lets the search bar group hits per concept (one card with proceeding
-- pills inside) instead of returning one row per (concept × proceeding).
--
-- Slug naming convention:
-- - EN slug for concepts native to UPC/EPC AND for shared concepts that
-- exist in both DE and UPC/EPC (statement-of-defence, reply-to-defence,
-- notice-of-appeal, opposition, re-establishment-of-rights). HLC primary
-- working surface is German for users but slugs are internal.
-- - DE slug only for concepts that exist exclusively in German law
-- (nichtzulassungsbeschwerde, versaeumnisurteil-einspruch,
-- hinweisbeschluss-stellungnahme).
--
-- party = the dominant case across contexts. Per-rule primary_party
-- override on paliad.deadline_rules takes precedence in the UI pill.
CREATE TABLE paliad.deadline_concepts (
id uuid PRIMARY KEY DEFAULT gen_random_uuid(),
slug text NOT NULL UNIQUE,
name_de text NOT NULL,
name_en text NOT NULL,
description text,
aliases text[] NOT NULL DEFAULT '{}',
party text,
category text NOT NULL DEFAULT 'submission'
CHECK (category IN ('submission','decision','order','hearing','other')),
sort_order int NOT NULL DEFAULT 100,
is_active bool NOT NULL DEFAULT true,
created_at timestamptz NOT NULL DEFAULT now(),
updated_at timestamptz NOT NULL DEFAULT now()
);
COMMENT ON TABLE paliad.deadline_concepts IS
'The Unifier layer above paliad.deadline_rules. One row per legal '
'concept (Klageerwiderung, Berufungsfrist, …); deadline_rules.concept_id '
'links rule rows from different proceeding-types to the same concept.';
COMMENT ON COLUMN paliad.deadline_concepts.aliases IS
'Synonyms (DE, EN, colloquial) the search bar matches against. '
'Curated in seed migrations; not user-editable in v1.';
COMMENT ON COLUMN paliad.deadline_concepts.party IS
'Dominant party across contexts (claimant / defendant / both / court). '
'Per-rule paliad.deadline_rules.primary_party overrides for cases where '
'a context has a different responsible party (e.g. EPA Einspruch '
'Erwiderung is owed by the Patentinhaber, but the dominant case for '
'the statement-of-defence concept is defendant).';
CREATE INDEX deadline_concepts_trgm_de ON paliad.deadline_concepts
USING gin (name_de gin_trgm_ops);
CREATE INDEX deadline_concepts_trgm_en ON paliad.deadline_concepts
USING gin (name_en gin_trgm_ops);
CREATE INDEX deadline_concepts_aliases ON paliad.deadline_concepts
USING gin (aliases);