Implements the four audit recommendations from §6.1 of docs/audit-fristenrechner-completeness-2026-04-30.md plus a holiday- adjustment cap fix surfaced by PR-2's smoke test. (1) UPC_INF CCR-conditional rejoinder Public Fristenrechner now flips inf.reply (RoP.029.b → RoP.029.a) and inf.rejoin (1mo / RoP.029.c → 2mo / RoP.029.d) when the user ticks "Mit Widerklage auf Nichtigkeit." Implemented via a new `condition_flag` column on paliad.deadline_rules: when the rule names a flag and the API request's flags array contains it, the calculator substitutes alt_duration_value/unit and alt_rule_code. Independent of the existing `condition_rule_id` mechanism (which references a real rule in the same proceeding tree — only useful for matter-attached trees that already seed the CCR rule). (2) UPC_APP / internal APP grounds anchoring `app.grounds` is now anchored on the trigger date (the appealed decision) with a 4-month duration, not chained 2mo after `app.notice`. Per RoP 220.1 the legal rule is "4 months from notification of the decision," independent of when the notice itself was filed. The chain only happened to give the right answer when both legs landed on a working day; under holiday rollover (e.g. notice deadline pushed to Monday) the grounds deadline drifted off the 4mo legal target. (3) EP_GRANT publish anchor on priority date New `anchor_alt` column on paliad.deadline_rules. ep_grant.publish carries `anchor_alt='priority_date'`. The Fristenrechner UI surfaces an optional "Prioritätstag" input (visible only when EP_GRANT is selected) that, when populated, anchors the publish-A1 calculation on the priority date instead of the filing. Falls back to filing date when the priority field is empty (the case for purely-EP applications with no foreign priority claim). (4) Rule-code format normalisation Migration 029 normalises 'RoP 23' → 'RoP.023', 'RoP 29b' / 'RoP.029b' → 'RoP.029.b', 'RoP 220.1' → 'RoP.220.1', etc. across deadline_rules. Matches the canonical youpc format already used by the PR-1 imported event-deadline rule codes. (+) AdjustForNonWorkingDays cap bumped 30 → 60 Surfaced by the PR-2 smoke test: SoD on 2026-04-30 (3mo from trigger) landed on Sat 2026-08-29 instead of Mon 2026-08-31. The 30-iteration safety bound on AdjustForNonWorkingDays cannot walk past the 33-day UPC summer vacation plus flanking weekends. Bumped to 60. Pure-Go one-liner, locked by a follow-up production smoke (real paliad.holidays seed has the UPC vacation). Schema (migration 029): two new nullable text columns on paliad.deadline_rules — `condition_flag` and `anchor_alt`. Both ignored by every existing rule; only the rows updated above carry values. Models: DeadlineRule gains ConditionFlag + AnchorAlt (nilable strings). Service: FristenrechnerService.Calculate now takes a CalcOptions struct (PriorityDateStr, Flags). API handler accepts optional priorityDate and flags fields on POST /api/tools/fristenrechner. Frontend: TSX surfaces the priority-date row + CCR checkbox conditionally on selectedType (only EP_GRANT / UPC_INF respectively). Client TS reads them and threads through the API call. New i18n keys for both DE+EN. Migration 029 dry-run validated on prod Supabase (BEGIN/ROLLBACK): schema + UPDATEs apply cleanly, rule states match expected post-fix shape. Tests + go build/vet + bun build all clean.
129 lines
6.7 KiB
SQL
129 lines
6.7 KiB
SQL
-- t-paliad-086 PR-3: Tier 1 bug fixes from the Fristenrechner audit.
|
|
--
|
|
-- Audit recommendations §6.1 (rec 1-4):
|
|
--
|
|
-- 1. UPC_INF: wire CCR-conditional adaptive rule on inf.reply / inf.rejoin
|
|
-- so the public Fristenrechner can flip duration + rule code when the
|
|
-- defendant counterclaims for revocation. Uses a new `condition_flag`
|
|
-- column (string flag from the API request) instead of condition_rule_id
|
|
-- which would require seeding a real CCR rule under UPC_INF.
|
|
--
|
|
-- 2. UPC_APP / internal APP: re-anchor app.grounds on the trigger date
|
|
-- (decision being appealed) instead of chaining off app.notice. Per
|
|
-- RoP 220.1, grounds is "4 months from the decision", not "2 months
|
|
-- after notice." The chained shape happens to give the right calendar
|
|
-- date when both legs land on a working day, but breaks under holiday
|
|
-- rollover and is the wrong legal model.
|
|
--
|
|
-- 3. EP_GRANT.ep_grant.publish: support an alternate anchor on the
|
|
-- priority date (Art. 93 EPÜ — "18 months ab Prioritätstag"). Existing
|
|
-- parent_id=ep_grant.filing keeps the fallback when no priority date
|
|
-- is supplied. New `anchor_alt` column tells the calculator to prefer
|
|
-- a separately-supplied priority date when present.
|
|
--
|
|
-- 4. Normalise rule_code format: 'RoP 23' / 'RoP.029b' / 'RoP 220.1'
|
|
-- → uniform 'RoP.023' / 'RoP.029.b' / 'RoP.220.1' (matching youpc and
|
|
-- the PR-1 import).
|
|
--
|
|
-- Plus an in-passing fix not in the audit: the AdjustForNonWorkingDays
|
|
-- 30-iteration safety cap is not enough to walk past the 33-day UPC
|
|
-- summer vacation. The Go-side fix lives in internal/services/holidays.go;
|
|
-- the migration just lets the data carry the correct bracket info.
|
|
|
|
-- ============================================================================
|
|
-- 1. Schema columns
|
|
-- ============================================================================
|
|
|
|
ALTER TABLE paliad.deadline_rules
|
|
ADD COLUMN IF NOT EXISTS condition_flag text,
|
|
ADD COLUMN IF NOT EXISTS anchor_alt text;
|
|
|
|
COMMENT ON COLUMN paliad.deadline_rules.condition_flag IS
|
|
'Flag-based conditional rule. When set (e.g. ''with_ccr''), the calculator '
|
|
'uses alt_duration_value/alt_duration_unit/alt_rule_code if the API request '
|
|
'flags include this value. Independent of condition_rule_id (which references '
|
|
'a rule in the same proceeding tree — useful for matter-attached fristen but '
|
|
'not for public Fristenrechner where the CCR variant has no separate rule).';
|
|
|
|
COMMENT ON COLUMN paliad.deadline_rules.anchor_alt IS
|
|
'Named alternate anchor. ''priority_date'' means: when the API request '
|
|
'supplies priorityDate, use it as the base for this rule''s duration '
|
|
'instead of the trigger date or the parent rule''s computed date.';
|
|
|
|
-- ============================================================================
|
|
-- 2. UPC_INF CCR-conditional adaptive rules
|
|
-- ============================================================================
|
|
|
|
UPDATE paliad.deadline_rules
|
|
SET condition_flag = 'with_ccr',
|
|
alt_duration_value = 2,
|
|
alt_duration_unit = 'months',
|
|
alt_rule_code = 'RoP.029.a'
|
|
WHERE proceeding_type_id = (SELECT id FROM paliad.proceeding_types WHERE code = 'UPC_INF')
|
|
AND code = 'inf.reply';
|
|
|
|
UPDATE paliad.deadline_rules
|
|
SET condition_flag = 'with_ccr',
|
|
alt_duration_value = 2,
|
|
alt_duration_unit = 'months',
|
|
alt_rule_code = 'RoP.029.d'
|
|
WHERE proceeding_type_id = (SELECT id FROM paliad.proceeding_types WHERE code = 'UPC_INF')
|
|
AND code = 'inf.rejoin';
|
|
|
|
-- ============================================================================
|
|
-- 3. UPC_APP and internal APP: re-anchor app.grounds on trigger (decision)
|
|
--
|
|
-- Public UPC_APP: app.notice (root, 2mo from decision), app.grounds (was
|
|
-- chained 2mo after notice → now sibling, 4mo from decision).
|
|
-- Internal APP: same fix.
|
|
-- ============================================================================
|
|
|
|
UPDATE paliad.deadline_rules
|
|
SET parent_id = NULL,
|
|
duration_value = 4,
|
|
duration_unit = 'months',
|
|
deadline_notes = 'Ab Zustellung der angegriffenen Entscheidung; nicht ab Berufungseinlegung'
|
|
WHERE proceeding_type_id = (SELECT id FROM paliad.proceeding_types WHERE code = 'UPC_APP')
|
|
AND code = 'app.grounds';
|
|
|
|
UPDATE paliad.deadline_rules
|
|
SET parent_id = NULL,
|
|
duration_value = 4,
|
|
duration_unit = 'months',
|
|
deadline_notes = 'Anchored on decision date (the trigger), not on filing of notice'
|
|
WHERE proceeding_type_id = (SELECT id FROM paliad.proceeding_types WHERE code = 'APP')
|
|
AND code = 'app.grounds';
|
|
|
|
-- ============================================================================
|
|
-- 4. EP_GRANT.ep_grant.publish: alternate anchor on priority date
|
|
-- ============================================================================
|
|
|
|
UPDATE paliad.deadline_rules
|
|
SET anchor_alt = 'priority_date',
|
|
deadline_notes = 'Ab Prioritätstag (oder Anmeldetag, wenn keine Priorität beansprucht)'
|
|
WHERE proceeding_type_id = (SELECT id FROM paliad.proceeding_types WHERE code = 'EP_GRANT')
|
|
AND code = 'ep_grant.publish';
|
|
|
|
-- ============================================================================
|
|
-- 5. Normalise rule_code format → canonical 'RoP.NNN.x' (matches youpc and
|
|
-- PR-1 imported event-deadline rule codes).
|
|
--
|
|
-- Done as targeted UPDATEs to avoid touching unrelated rows. Both spaced
|
|
-- ('RoP 23') and dot-without-period-before-letter ('RoP.029b') variants
|
|
-- are converted.
|
|
-- ============================================================================
|
|
|
|
UPDATE paliad.deadline_rules SET rule_code = 'RoP.023' WHERE rule_code IN ('RoP 23', 'RoP.023');
|
|
UPDATE paliad.deadline_rules SET rule_code = 'RoP.029.a' WHERE rule_code IN ('RoP 29a', 'RoP.029a');
|
|
UPDATE paliad.deadline_rules SET rule_code = 'RoP.029.b' WHERE rule_code IN ('RoP 29b', 'RoP.029b');
|
|
UPDATE paliad.deadline_rules SET rule_code = 'RoP.029.c' WHERE rule_code IN ('RoP 29c', 'RoP.029c');
|
|
UPDATE paliad.deadline_rules SET rule_code = 'RoP.029.d' WHERE rule_code IN ('RoP 29d', 'RoP.029d');
|
|
UPDATE paliad.deadline_rules SET rule_code = 'RoP.029.e' WHERE rule_code IN ('RoP 29e', 'RoP.029e');
|
|
UPDATE paliad.deadline_rules SET rule_code = 'RoP.220.1' WHERE rule_code IN ('RoP 220.1', 'RoP.220.1');
|
|
UPDATE paliad.deadline_rules SET rule_code = 'RoP.050' WHERE rule_code IN ('RoP 50', 'RoP.050');
|
|
|
|
-- Same for alt_rule_code on UPC_INF rows above (we wrote canonical values
|
|
-- but this protects against any prior data drift).
|
|
UPDATE paliad.deadline_rules SET alt_rule_code = 'RoP.029.a' WHERE alt_rule_code IN ('RoP 29a', 'RoP.029a');
|
|
UPDATE paliad.deadline_rules SET alt_rule_code = 'RoP.029.d' WHERE alt_rule_code IN ('RoP 29d', 'RoP.029d');
|