feat(t-paliad-131): Phase B1 — UPC counterclaim cross-flows
Closes m's primary complaint: today's `with_ccr` flag on UPC_INF only
swaps the Replik / Duplik durations. Per UPC RoP R.29 the with-CCR flow
ALSO adds 5–7 new submissions across the claimant / defendant exchange.
Same gap on UPC_REV: Application to amend (R.49.2.a → R.55 = R.32 m.m.)
and Counterclaim for infringement (R.49.2.b → R.50, R.56 cycle) were
entirely missing.
UPC_INF gets a nested `with_amend` flag under `with_ccr` (R.30 amend
is only available with a CCR). UPC_REV gets two parallel independent
flags `with_amend` + `with_cci`; both can be on. Citations verified
against data.laws_contents (youpcdb, UPCRoP).
Migration 041 (waved INSERTs because each subsequent rule references
the prior wave's parent_id):
- Wave 0: 11 new concept rows (counterclaim-for-revocation,
defence-to-counterclaim-for-revocation, defence-to-application-to-amend,
reply-to-defence-to-counterclaim-for-revocation,
reply-to-defence-to-application-to-amend,
rejoinder-on-reply-to-defence-to-ccr, rejoinder-on-reply-to-amend,
counterclaim-for-infringement, defence-to-counterclaim-for-infringement,
reply-to-defence-to-counterclaim-for-infringement,
rejoinder-on-counterclaim-for-infringement). counterclaim-for-revocation
also seeded for the search bar even though its rule lives implicitly
in inf.sod (the with_ccr flag captures it).
- UPC_INF + UPC_REV sequence_orders renumbered to leave gaps (10/20/30…)
so new cross-flow rows interleave chronologically with the backbone.
- 7 new UPC_INF rules: inf.def_to_ccr (R.29.a), inf.app_to_amend (R.30.1),
inf.def_to_amend (R.32.1), inf.reply_def_ccr (R.29.d),
inf.reply_def_amd (R.32.3), inf.rejoin_reply_ccr (R.29.e),
inf.rejoin_amd (R.32.3).
- 8 new UPC_REV rules: rev.app_to_amend (R.49.2.a), rev.def_to_amend
(R.43.3), rev.reply_def_amd (R.32.3 m.m.), rev.rejoin_amd (R.32.3 m.m.),
rev.cc_inf (R.49.2.b), rev.def_cci (R.56.1), rev.reply_def_cci (R.56.3),
rev.rejoin_cci (R.56.4).
Calculator (services/fristenrechner.go):
- Zero-duration rules now split into 4 buckets, not 2:
1. parent=nil + non-court → IsRootEvent (existing)
2. parent=nil + court → IsCourtSet (existing, e.g. inf.oral when stand-alone)
3. parent set + court → IsCourtSet (existing, waypoints)
4. parent set + non-court → "filed-with-parent" — inherit parent's
date. NEW. Used by rev.app_to_amend / rev.cc_inf which per
R.49(2) are filed AS PART OF the Defence to revocation.
- AnchorOverrides on a zero-duration rule short-circuits to the user's
date, propagating downstream as before.
Frontend:
- New checkboxes inf-amend-flag (UPC_INF, nested under ccr-flag),
rev-amend-flag, rev-cci-flag (UPC_REV). Visibility per proceeding
type; inf-amend disabled until ccr is on (R.30 dependency).
- Three new i18n keys (DE+EN). Small CSS for nested-checkbox indent
and disabled-state colour.
Live-verified via curl on paliad.de against tester@hlc.de:
UPC_INF + with_ccr+with_amend, trigger 2026-05-04 → all 7 new rules
render at correct dates (R.29.a 2mo, R.30.1 2mo, R.32.1 2mo from
app_to_amend, R.29.d 2mo from def_to_ccr, R.32.3 1mo, R.29.e 1mo,
R.32.3 1mo).
UPC_REV + with_amend+with_cci → rev.app_to_amend / rev.cc_inf show
rev.defence's date (filed-with-parent), R.43.3 2mo / R.56.1 2mo /
R.32.3 + R.56.3 1mo / R.32.3 + R.56.4 1mo all line up.
This commit is contained in:
@@ -202,10 +202,24 @@ async function calculate() {
|
||||
const priorityInput = document.getElementById("priority-date") as HTMLInputElement | null;
|
||||
const priorityDate = selectedType === "EP_GRANT" && priorityInput?.value ? priorityInput.value : "";
|
||||
|
||||
// Flags — UPC_INF surfaces "Mit Widerklage auf Nichtigkeit" toggle.
|
||||
const ccrFlag = document.getElementById("ccr-flag") as HTMLInputElement | null;
|
||||
// Flags — three proceeding-specific checkboxes:
|
||||
// UPC_INF: with_ccr (always available); with_amend (nested under
|
||||
// with_ccr — R.30 application is only available with a CCR).
|
||||
// UPC_REV: with_amend (R.49.2.a) and with_cci (R.49.2.b) as two
|
||||
// independent gates; both can be on simultaneously.
|
||||
const ccrFlag = document.getElementById("ccr-flag") as HTMLInputElement | null;
|
||||
const infAmendFlag = document.getElementById("inf-amend-flag") as HTMLInputElement | null;
|
||||
const revAmendFlag = document.getElementById("rev-amend-flag") as HTMLInputElement | null;
|
||||
const revCciFlag = document.getElementById("rev-cci-flag") as HTMLInputElement | null;
|
||||
const flags: string[] = [];
|
||||
if (selectedType === "UPC_INF" && ccrFlag?.checked) flags.push("with_ccr");
|
||||
if (selectedType === "UPC_INF") {
|
||||
if (ccrFlag?.checked) flags.push("with_ccr");
|
||||
if (ccrFlag?.checked && infAmendFlag?.checked) flags.push("with_amend");
|
||||
}
|
||||
if (selectedType === "UPC_REV") {
|
||||
if (revAmendFlag?.checked) flags.push("with_amend");
|
||||
if (revCciFlag?.checked) flags.push("with_cci");
|
||||
}
|
||||
|
||||
// Forward any user-set per-rule date overrides so downstream rules
|
||||
// re-anchor off them. Empty map → omitted from the payload.
|
||||
@@ -686,16 +700,41 @@ function selectProceeding(btn: HTMLButtonElement) {
|
||||
const name = btn.querySelector("strong")?.textContent || "";
|
||||
document.getElementById("trigger-event")!.textContent = name;
|
||||
|
||||
// Conditional inputs: priority date for EP_GRANT, CCR toggle for UPC_INF.
|
||||
// Conditional inputs:
|
||||
// priority-date → EP_GRANT
|
||||
// ccr-flag → UPC_INF only
|
||||
// inf-amend-flag → UPC_INF only, but disabled until ccr-flag is on
|
||||
// (R.30 amend only available with a CCR)
|
||||
// rev-amend-flag → UPC_REV only
|
||||
// rev-cci-flag → UPC_REV only
|
||||
const priorityRow = document.getElementById("priority-date-row");
|
||||
if (priorityRow) priorityRow.style.display = selectedType === "EP_GRANT" ? "" : "none";
|
||||
const ccrRow = document.getElementById("ccr-flag-row");
|
||||
if (ccrRow) ccrRow.style.display = selectedType === "UPC_INF" ? "" : "none";
|
||||
const infAmendRow = document.getElementById("inf-amend-flag-row");
|
||||
if (infAmendRow) infAmendRow.style.display = selectedType === "UPC_INF" ? "" : "none";
|
||||
const revAmendRow = document.getElementById("rev-amend-flag-row");
|
||||
if (revAmendRow) revAmendRow.style.display = selectedType === "UPC_REV" ? "" : "none";
|
||||
const revCciRow = document.getElementById("rev-cci-flag-row");
|
||||
if (revCciRow) revCciRow.style.display = selectedType === "UPC_REV" ? "" : "none";
|
||||
|
||||
syncInfAmendEnabled();
|
||||
|
||||
showStep(2);
|
||||
scheduleProcCalc(0);
|
||||
}
|
||||
|
||||
// inf-amend-flag is only meaningful when ccr-flag is on (R.30 application
|
||||
// is filed within the Defence to CCR). When ccr-flag flips off, also
|
||||
// untick inf-amend-flag so the calc payload stays coherent.
|
||||
function syncInfAmendEnabled() {
|
||||
const ccr = document.getElementById("ccr-flag") as HTMLInputElement | null;
|
||||
const infAmend = document.getElementById("inf-amend-flag") as HTMLInputElement | null;
|
||||
if (!ccr || !infAmend) return;
|
||||
infAmend.disabled = !ccr.checked;
|
||||
if (!ccr.checked) infAmend.checked = false;
|
||||
}
|
||||
|
||||
// View toggle wiring. Persist the choice in `?view=…` so reload / share-link
|
||||
// restores the same layout.
|
||||
function initViewToggle() {
|
||||
@@ -755,7 +794,17 @@ document.addEventListener("DOMContentLoaded", () => {
|
||||
}
|
||||
|
||||
const ccrFlag = document.getElementById("ccr-flag") as HTMLInputElement | null;
|
||||
if (ccrFlag) ccrFlag.addEventListener("change", () => scheduleProcCalc(0));
|
||||
if (ccrFlag) ccrFlag.addEventListener("change", () => {
|
||||
syncInfAmendEnabled();
|
||||
scheduleProcCalc(0);
|
||||
});
|
||||
|
||||
const infAmendFlag = document.getElementById("inf-amend-flag") as HTMLInputElement | null;
|
||||
if (infAmendFlag) infAmendFlag.addEventListener("change", () => scheduleProcCalc(0));
|
||||
const revAmendFlag = document.getElementById("rev-amend-flag") as HTMLInputElement | null;
|
||||
if (revAmendFlag) revAmendFlag.addEventListener("change", () => scheduleProcCalc(0));
|
||||
const revCciFlag = document.getElementById("rev-cci-flag") as HTMLInputElement | null;
|
||||
if (revCciFlag) revCciFlag.addEventListener("change", () => scheduleProcCalc(0));
|
||||
|
||||
// Click-to-edit on timeline / column dates: open an inline date input
|
||||
// and persist the user's choice as an anchor override so downstream
|
||||
|
||||
@@ -208,6 +208,9 @@ const translations: Record<Lang, Record<string, string>> = {
|
||||
"deadlines.trigger.label": "Ausgangsdatum",
|
||||
"deadlines.priority.date": "Priorit\u00e4tstag (optional):",
|
||||
"deadlines.flag.ccr": "Mit Widerklage auf Nichtigkeit",
|
||||
"deadlines.flag.inf_amend": "Mit Antrag auf Patentänderung (R.30)",
|
||||
"deadlines.flag.rev_amend": "Mit Antrag auf Patentänderung (R.49.2.a)",
|
||||
"deadlines.flag.rev_cci": "Mit Verletzungswiderklage (R.49.2.b)",
|
||||
"deadlines.calculate": "Fristen berechnen",
|
||||
"deadlines.print": "Drucken",
|
||||
"deadlines.reset": "\u2190 Neu berechnen",
|
||||
@@ -1728,6 +1731,9 @@ const translations: Record<Lang, Record<string, string>> = {
|
||||
"deadlines.trigger.label": "Trigger date",
|
||||
"deadlines.priority.date": "Priority date (optional):",
|
||||
"deadlines.flag.ccr": "Counterclaim for revocation filed",
|
||||
"deadlines.flag.inf_amend": "Application to amend the patent filed (R.30)",
|
||||
"deadlines.flag.rev_amend": "Application to amend the patent filed (R.49.2.a)",
|
||||
"deadlines.flag.rev_cci": "Counterclaim for infringement filed (R.49.2.b)",
|
||||
"deadlines.calculate": "Calculate Deadlines",
|
||||
"deadlines.print": "Print",
|
||||
"deadlines.reset": "\u2190 Start Over",
|
||||
|
||||
@@ -128,6 +128,24 @@ export function renderFristenrechner(): string {
|
||||
<span data-i18n="deadlines.flag.ccr">Mit Widerklage auf Nichtigkeit</span>
|
||||
</label>
|
||||
</div>
|
||||
<div className="date-field-row date-field-row--nested" id="inf-amend-flag-row" style="display:none">
|
||||
<label className="date-label">
|
||||
<input type="checkbox" id="inf-amend-flag" />
|
||||
<span data-i18n="deadlines.flag.inf_amend">Mit Antrag auf Patentänderung (R.30)</span>
|
||||
</label>
|
||||
</div>
|
||||
<div className="date-field-row" id="rev-amend-flag-row" style="display:none">
|
||||
<label className="date-label">
|
||||
<input type="checkbox" id="rev-amend-flag" />
|
||||
<span data-i18n="deadlines.flag.rev_amend">Mit Antrag auf Patentänderung (R.49.2.a)</span>
|
||||
</label>
|
||||
</div>
|
||||
<div className="date-field-row" id="rev-cci-flag-row" style="display:none">
|
||||
<label className="date-label">
|
||||
<input type="checkbox" id="rev-cci-flag" />
|
||||
<span data-i18n="deadlines.flag.rev_cci">Mit Verletzungswiderklage (R.49.2.b)</span>
|
||||
</label>
|
||||
</div>
|
||||
<button type="button" id="calculate-btn" className="calculate-btn" data-i18n="deadlines.calculate">
|
||||
Fristen berechnen
|
||||
</button>
|
||||
|
||||
@@ -638,6 +638,9 @@ export type I18nKey =
|
||||
| "deadlines.filter.thisweek"
|
||||
| "deadlines.filter.today"
|
||||
| "deadlines.flag.ccr"
|
||||
| "deadlines.flag.inf_amend"
|
||||
| "deadlines.flag.rev_amend"
|
||||
| "deadlines.flag.rev_cci"
|
||||
| "deadlines.heading"
|
||||
| "deadlines.kalender.empty"
|
||||
| "deadlines.kalender.heading"
|
||||
|
||||
@@ -1826,6 +1826,16 @@ input[type="range"]::-moz-range-thumb {
|
||||
gap: 0.75rem;
|
||||
}
|
||||
|
||||
/* Nested checkbox under a parent flag (e.g. UPC_INF inf-amend-flag is
|
||||
only meaningful with ccr-flag on — indent so the dependency is
|
||||
visible). */
|
||||
.date-field-row--nested {
|
||||
margin-left: 1.5rem;
|
||||
}
|
||||
.date-field-row--nested input[type="checkbox"]:disabled + span {
|
||||
color: var(--color-text-muted);
|
||||
}
|
||||
|
||||
.date-label {
|
||||
font-size: 0.85rem;
|
||||
font-weight: 500;
|
||||
|
||||
@@ -0,0 +1,60 @@
|
||||
-- Reverses 041_upc_counterclaim_cross_flows. Removes the new rules first,
|
||||
-- then the new concepts, then restores the original sequence_order on the
|
||||
-- existing UPC_INF / UPC_REV rows. Concept rows that are still referenced
|
||||
-- elsewhere are kept (defensive — concepts may be reused by future
|
||||
-- migrations).
|
||||
|
||||
DELETE FROM paliad.deadline_rules
|
||||
WHERE proceeding_type_id IN (
|
||||
SELECT id FROM paliad.proceeding_types WHERE code IN ('UPC_INF','UPC_REV')
|
||||
)
|
||||
AND code IN (
|
||||
'inf.def_to_ccr','inf.app_to_amend','inf.def_to_amend','inf.reply_def_ccr',
|
||||
'inf.reply_def_amd','inf.rejoin_reply_ccr','inf.rejoin_amd',
|
||||
'rev.app_to_amend','rev.def_to_amend','rev.reply_def_amd','rev.rejoin_amd',
|
||||
'rev.cc_inf','rev.def_cci','rev.reply_def_cci','rev.rejoin_cci'
|
||||
);
|
||||
|
||||
DELETE FROM paliad.deadline_concepts
|
||||
WHERE slug IN (
|
||||
'counterclaim-for-revocation','defence-to-counterclaim-for-revocation',
|
||||
'application-to-amend','defence-to-application-to-amend',
|
||||
'reply-to-defence-to-counterclaim-for-revocation',
|
||||
'reply-to-defence-to-application-to-amend',
|
||||
'rejoinder-on-reply-to-defence-to-ccr','rejoinder-on-reply-to-amend',
|
||||
'counterclaim-for-infringement','defence-to-counterclaim-for-infringement',
|
||||
'reply-to-defence-to-counterclaim-for-infringement',
|
||||
'rejoinder-on-counterclaim-for-infringement'
|
||||
)
|
||||
AND id NOT IN (SELECT concept_id FROM paliad.deadline_rules WHERE concept_id IS NOT NULL);
|
||||
|
||||
WITH proc AS (SELECT id FROM paliad.proceeding_types WHERE code = 'UPC_INF')
|
||||
UPDATE paliad.deadline_rules dr
|
||||
SET sequence_order = CASE dr.code
|
||||
WHEN 'inf.soc' THEN 0
|
||||
WHEN 'inf.sod' THEN 1
|
||||
WHEN 'inf.reply' THEN 2
|
||||
WHEN 'inf.rejoin' THEN 3
|
||||
WHEN 'inf.interim' THEN 4
|
||||
WHEN 'inf.oral' THEN 5
|
||||
WHEN 'inf.decision' THEN 6
|
||||
WHEN 'inf.cost_app' THEN 7
|
||||
ELSE dr.sequence_order
|
||||
END
|
||||
FROM proc p
|
||||
WHERE dr.proceeding_type_id = p.id;
|
||||
|
||||
WITH proc AS (SELECT id FROM paliad.proceeding_types WHERE code = 'UPC_REV')
|
||||
UPDATE paliad.deadline_rules dr
|
||||
SET sequence_order = CASE dr.code
|
||||
WHEN 'rev.app' THEN 0
|
||||
WHEN 'rev.defence' THEN 1
|
||||
WHEN 'rev.reply' THEN 2
|
||||
WHEN 'rev.rejoin' THEN 3
|
||||
WHEN 'rev.interim' THEN 4
|
||||
WHEN 'rev.oral' THEN 5
|
||||
WHEN 'rev.decision' THEN 6
|
||||
ELSE dr.sequence_order
|
||||
END
|
||||
FROM proc p
|
||||
WHERE dr.proceeding_type_id = p.id;
|
||||
303
internal/db/migrations/041_upc_counterclaim_cross_flows.up.sql
Normal file
303
internal/db/migrations/041_upc_counterclaim_cross_flows.up.sql
Normal file
@@ -0,0 +1,303 @@
|
||||
-- t-paliad-131 Phase B1: UPC counterclaim cross-flows.
|
||||
--
|
||||
-- Closes m's primary complaint: today's `with_ccr` flag on UPC_INF only
|
||||
-- swaps the rejoinder duration from 1mo to 2mo. Per R.29 (UPC RoP) the
|
||||
-- with-CCR flow ALSO adds 5–7 new submissions across the claimant /
|
||||
-- defendant exchange:
|
||||
-- R.29(a) — claimant: Defence to CCR + (opt) Application to amend (R.30)
|
||||
-- R.29(d) — defendant: Reply to Defence to CCR + (opt) Defence to App to amend
|
||||
-- R.29(e) — claimant: Rejoinder + (opt) Reply to Defence to amend
|
||||
-- R.32(1) / R.32(3) — defendant amend cycle: Defence (2mo) / Reply (1mo) / Rejoinder (1mo)
|
||||
--
|
||||
-- And on the revocation side (UPC_REV) per R.49(2):
|
||||
-- (a) Application to amend → R.55 (= R.32 m.m.) cycle
|
||||
-- (b) Counterclaim for infringement → R.56 cycle
|
||||
--
|
||||
-- Citations verified against data.laws_contents (youpcdb, UPCRoP).
|
||||
--
|
||||
-- Two new flags on UPC_INF: nested `with_amend` (only meaningful when
|
||||
-- with_ccr is also set, since R.30 application is only available with
|
||||
-- a CCR). UPC_REV gets two parallel independent flags: `with_amend`
|
||||
-- (R.49.2(a)) and `with_cci` (R.49.2(b)). Both can be on simultaneously.
|
||||
--
|
||||
-- New rules are inserted in waves because subsequent rules reference
|
||||
-- prior-wave inserts as their parent_id (a single INSERT statement can't
|
||||
-- self-reference its rows in the same SELECT).
|
||||
|
||||
-- ============================================================================
|
||||
-- 1. New concept rows
|
||||
-- ============================================================================
|
||||
|
||||
INSERT INTO paliad.deadline_concepts (slug, name_de, name_en, description, aliases, party, category, sort_order) VALUES
|
||||
('counterclaim-for-revocation', 'Nichtigkeitswiderklage', 'Counterclaim for Revocation', 'Eingeschlossene Widerklage auf Nichtigerklärung des Patents in der Klageerwiderung (UPC R.25).', ARRAY['Nichtigkeitswiderklage', 'Counterclaim for Revocation', 'CCR', 'Widerklage Nichtigkeit'], 'defendant', 'submission', 23),
|
||||
('defence-to-counterclaim-for-revocation', 'Erwiderung auf Nichtigkeitswiderklage', 'Defence to Counterclaim for Revocation', 'Erwiderung des Klägers auf eine in der Klageerwiderung enthaltene Nichtigkeitswiderklage (R.29.a).', ARRAY['Erwiderung Nichtigkeitswiderklage', 'Defence to CCR', 'Defence to Counterclaim for Revocation'], 'claimant', 'submission', 24),
|
||||
('defence-to-application-to-amend', 'Erwiderung auf Patentänderungsantrag', 'Defence to Application to Amend', 'Erwiderung der Gegenseite auf den Patentänderungsantrag (R.32.1 / R.43.3).', ARRAY['Erwiderung Patentänderungsantrag', 'Defence to App to amend', 'Defence to Application to Amend'], 'defendant', 'submission', 26),
|
||||
('reply-to-defence-to-counterclaim-for-revocation', 'Replik auf Erwiderung zur Nichtigkeitswiderklage', 'Reply to Defence to Counterclaim for Revocation', 'Replik des Beklagten/Patentinhabers auf die Erwiderung zur Nichtigkeitswiderklage (R.29.d).', ARRAY['Replik Erwiderung CCR', 'Reply to Defence to CCR'], 'defendant', 'submission', 27),
|
||||
('reply-to-defence-to-application-to-amend', 'Replik auf Erwiderung zum Patentänderungsantrag', 'Reply to Defence to Application to Amend', 'Replik des Patentinhabers auf die Erwiderung zum Patentänderungsantrag (R.32.3).', ARRAY['Replik Patentänderungsantrag', 'Reply to Defence to amend'], 'claimant', 'submission', 28),
|
||||
('rejoinder-on-reply-to-defence-to-ccr', 'Duplik auf Replik zur Erwiderung Nichtigkeitsw.', 'Rejoinder to Reply to Defence to Counterclaim for Revocation', 'Duplik des Klägers auf die Replik zur Erwiderung der Nichtigkeitswiderklage (R.29.e).', ARRAY['Duplik Replik CCR', 'Rejoinder Reply to Defence to CCR'], 'claimant', 'submission', 29),
|
||||
('rejoinder-on-reply-to-amend', 'Duplik auf Replik zum Patentänderungsantrag', 'Rejoinder to Reply on Application to Amend', 'Duplik der Gegenseite auf die Replik zum Patentänderungsantrag (R.32.3).', ARRAY['Duplik Patentänderungsantrag', 'Rejoinder on amend'], 'defendant', 'submission', 30),
|
||||
('counterclaim-for-infringement', 'Verletzungswiderklage', 'Counterclaim for Infringement', 'Verletzungswiderklage des Patentinhabers innerhalb des Nichtigkeitsverfahrens, eingeschlossen in der Erwiderung zur Nichtigkeitsklage (R.49.2.b).', ARRAY['Verletzungswiderklage', 'Counterclaim for Infringement', 'CCI'], 'defendant', 'submission', 31),
|
||||
('defence-to-counterclaim-for-infringement', 'Erwiderung auf Verletzungswiderklage', 'Defence to Counterclaim for Infringement', 'Erwiderung des Klägers im Nichtigkeitsverfahren auf die Verletzungswiderklage (R.56.1).', ARRAY['Erwiderung Verletzungswiderklage', 'Defence to CCI'], 'claimant', 'submission', 32),
|
||||
('reply-to-defence-to-counterclaim-for-infringement','Replik auf Erwiderung zur Verletzungswiderklage', 'Reply to Defence to Counterclaim for Infringement', 'Replik des Patentinhabers auf die Erwiderung zur Verletzungswiderklage (R.56.3).', ARRAY['Replik Verletzungswiderklage', 'Reply to Defence to CCI'], 'defendant', 'submission', 33),
|
||||
('rejoinder-on-counterclaim-for-infringement', 'Duplik auf Replik zur Erwiderung Verletzungswiderkl.', 'Rejoinder to Reply on Counterclaim for Infringement', 'Duplik des Klägers auf die Replik zur Erwiderung der Verletzungswiderklage (R.56.4).', ARRAY['Duplik Verletzungswiderklage', 'Rejoinder on CCI'], 'claimant', 'submission', 34)
|
||||
ON CONFLICT (slug) DO UPDATE SET
|
||||
name_de = EXCLUDED.name_de,
|
||||
name_en = EXCLUDED.name_en,
|
||||
description = EXCLUDED.description,
|
||||
aliases = EXCLUDED.aliases,
|
||||
party = EXCLUDED.party,
|
||||
sort_order = EXCLUDED.sort_order,
|
||||
updated_at = now();
|
||||
|
||||
-- ============================================================================
|
||||
-- 2. Renumber existing UPC_INF / UPC_REV sequence_orders to leave gaps so
|
||||
-- new rules can interleave chronologically.
|
||||
-- ============================================================================
|
||||
|
||||
WITH proc AS (SELECT id FROM paliad.proceeding_types WHERE code = 'UPC_INF')
|
||||
UPDATE paliad.deadline_rules dr
|
||||
SET sequence_order = CASE dr.code
|
||||
WHEN 'inf.soc' THEN 0
|
||||
WHEN 'inf.sod' THEN 10
|
||||
WHEN 'inf.reply' THEN 20
|
||||
WHEN 'inf.rejoin' THEN 30
|
||||
WHEN 'inf.interim' THEN 40
|
||||
WHEN 'inf.oral' THEN 50
|
||||
WHEN 'inf.decision' THEN 60
|
||||
WHEN 'inf.cost_app' THEN 70
|
||||
ELSE dr.sequence_order
|
||||
END
|
||||
FROM proc p
|
||||
WHERE dr.proceeding_type_id = p.id;
|
||||
|
||||
WITH proc AS (SELECT id FROM paliad.proceeding_types WHERE code = 'UPC_REV')
|
||||
UPDATE paliad.deadline_rules dr
|
||||
SET sequence_order = CASE dr.code
|
||||
WHEN 'rev.app' THEN 0
|
||||
WHEN 'rev.defence' THEN 10
|
||||
WHEN 'rev.reply' THEN 20
|
||||
WHEN 'rev.rejoin' THEN 30
|
||||
WHEN 'rev.interim' THEN 50
|
||||
WHEN 'rev.oral' THEN 60
|
||||
WHEN 'rev.decision' THEN 70
|
||||
ELSE dr.sequence_order
|
||||
END
|
||||
FROM proc p
|
||||
WHERE dr.proceeding_type_id = p.id;
|
||||
|
||||
-- ============================================================================
|
||||
-- 3. UPC_INF cross-flow rules — wave 1 (parents pre-existing inf.sod)
|
||||
-- ============================================================================
|
||||
|
||||
INSERT INTO paliad.deadline_rules (
|
||||
proceeding_type_id, parent_id, code, name, name_en, primary_party, event_type,
|
||||
is_mandatory, duration_value, duration_unit, timing, rule_code, deadline_notes, deadline_notes_en,
|
||||
sequence_order, condition_flag, concept_id, legal_source, is_spawn, is_active
|
||||
)
|
||||
SELECT
|
||||
pt.id, parent_rule.id, new.code, new.name, new.name_en, new.primary_party, new.event_type,
|
||||
true, new.duration_value, new.duration_unit, 'after', new.rule_code, new.deadline_notes, new.deadline_notes_en,
|
||||
new.sequence_order, new.condition_flag,
|
||||
(SELECT id FROM paliad.deadline_concepts WHERE slug = new.concept_slug),
|
||||
new.legal_source, false, true
|
||||
FROM (VALUES
|
||||
('inf.sod', 'inf.def_to_ccr', 'Erwiderung auf Nichtigkeitswiderklage', 'Defence to Counterclaim for Revocation', 'claimant', 'filing', 2, 'months', 'RoP.029.a',
|
||||
'Innerhalb von 2 Monaten nach Zustellung der Klageerwiderung mit Nichtigkeitswiderklage. Schließt Replik zur Klageerwiderung und ggf. Patentänderungsantrag (R.30) ein.',
|
||||
'Within 2 months of service of the SoD which includes a CCR. Includes the Reply to SoD and (if applicable) the Application to amend per R.30.',
|
||||
11, ARRAY['with_ccr']::text[], 'defence-to-counterclaim-for-revocation', 'UPC.RoP.29.a'),
|
||||
('inf.sod', 'inf.app_to_amend', 'Antrag auf Patentänderung', 'Application to Amend the Patent', 'claimant', 'filing', 2, 'months', 'RoP.030.1',
|
||||
'Optional in der Erwiderung zur Nichtigkeitswiderklage einzureichen — siehe R.30. Spätere Änderungsanträge nur mit Zustimmung des Gerichts (R.30.2).',
|
||||
'Optional, lodged together with the Defence to CCR per R.30. Later amendment requests admitted only with leave of the Court (R.30.2).',
|
||||
12, ARRAY['with_ccr','with_amend']::text[], 'application-to-amend', 'UPC.RoP.30.1')
|
||||
) AS new(parent_code, code, name, name_en, primary_party, event_type, duration_value, duration_unit, rule_code, deadline_notes, deadline_notes_en, sequence_order, condition_flag, concept_slug, legal_source)
|
||||
JOIN paliad.proceeding_types pt ON pt.code = 'UPC_INF'
|
||||
JOIN paliad.deadline_rules parent_rule ON parent_rule.proceeding_type_id = pt.id AND parent_rule.code = new.parent_code;
|
||||
|
||||
-- ============================================================================
|
||||
-- 4. UPC_INF cross-flow rules — wave 2 (parents = wave-1 inserts)
|
||||
-- ============================================================================
|
||||
|
||||
INSERT INTO paliad.deadline_rules (
|
||||
proceeding_type_id, parent_id, code, name, name_en, primary_party, event_type,
|
||||
is_mandatory, duration_value, duration_unit, timing, rule_code, deadline_notes, deadline_notes_en,
|
||||
sequence_order, condition_flag, concept_id, legal_source, is_spawn, is_active
|
||||
)
|
||||
SELECT
|
||||
pt.id, parent_rule.id, new.code, new.name, new.name_en, new.primary_party, new.event_type,
|
||||
true, new.duration_value, new.duration_unit, 'after', new.rule_code, new.deadline_notes, new.deadline_notes_en,
|
||||
new.sequence_order, new.condition_flag,
|
||||
(SELECT id FROM paliad.deadline_concepts WHERE slug = new.concept_slug),
|
||||
new.legal_source, false, true
|
||||
FROM (VALUES
|
||||
('inf.app_to_amend', 'inf.def_to_amend', 'Erwiderung auf Patentänderungsantrag', 'Defence to Application to Amend', 'defendant', 'filing', 2, 'months', 'RoP.032.1',
|
||||
'Innerhalb von 2 Monaten nach Zustellung des Patentänderungsantrags.',
|
||||
'Within 2 months of service of the Application to amend the patent.',
|
||||
21, ARRAY['with_ccr','with_amend']::text[], 'defence-to-application-to-amend', 'UPC.RoP.32.1'),
|
||||
('inf.def_to_ccr', 'inf.reply_def_ccr', 'Replik auf Erwiderung zur Nichtigkeitswiderklage', 'Reply to Defence to Counterclaim for Revocation', 'defendant', 'filing', 2, 'months', 'RoP.029.d',
|
||||
'Innerhalb von 2 Monaten nach Zustellung der Erwiderung zur Nichtigkeitswiderklage. Schließt Duplik zur Replik auf SoD und ggf. Erwiderung zum Patentänderungsantrag ein.',
|
||||
'Within 2 months of service of the Defence to CCR. Includes the Rejoinder to Reply to SoD and (if applicable) the Defence to Application to amend per R.32.',
|
||||
22, ARRAY['with_ccr']::text[], 'reply-to-defence-to-counterclaim-for-revocation', 'UPC.RoP.29.d')
|
||||
) AS new(parent_code, code, name, name_en, primary_party, event_type, duration_value, duration_unit, rule_code, deadline_notes, deadline_notes_en, sequence_order, condition_flag, concept_slug, legal_source)
|
||||
JOIN paliad.proceeding_types pt ON pt.code = 'UPC_INF'
|
||||
JOIN paliad.deadline_rules parent_rule ON parent_rule.proceeding_type_id = pt.id AND parent_rule.code = new.parent_code;
|
||||
|
||||
-- ============================================================================
|
||||
-- 5. UPC_INF cross-flow rules — wave 3 (parents = wave-2 inserts)
|
||||
-- ============================================================================
|
||||
|
||||
INSERT INTO paliad.deadline_rules (
|
||||
proceeding_type_id, parent_id, code, name, name_en, primary_party, event_type,
|
||||
is_mandatory, duration_value, duration_unit, timing, rule_code, deadline_notes, deadline_notes_en,
|
||||
sequence_order, condition_flag, concept_id, legal_source, is_spawn, is_active
|
||||
)
|
||||
SELECT
|
||||
pt.id, parent_rule.id, new.code, new.name, new.name_en, new.primary_party, new.event_type,
|
||||
true, new.duration_value, new.duration_unit, 'after', new.rule_code, new.deadline_notes, new.deadline_notes_en,
|
||||
new.sequence_order, new.condition_flag,
|
||||
(SELECT id FROM paliad.deadline_concepts WHERE slug = new.concept_slug),
|
||||
new.legal_source, false, true
|
||||
FROM (VALUES
|
||||
('inf.def_to_amend', 'inf.reply_def_amd', 'Replik auf Erwiderung zum Patentänderungsantrag', 'Reply to Defence to Application to Amend', 'claimant', 'filing', 1, 'months', 'RoP.032.3',
|
||||
'Innerhalb von 1 Monat nach Zustellung der Erwiderung zum Patentänderungsantrag.',
|
||||
'Within 1 month of service of the Defence to the Application to amend the patent.',
|
||||
31, ARRAY['with_ccr','with_amend']::text[], 'reply-to-defence-to-application-to-amend', 'UPC.RoP.32.3'),
|
||||
('inf.reply_def_ccr','inf.rejoin_reply_ccr', 'Duplik auf Replik zur Erwiderung Nichtigkeitswiderklage','Rejoinder to Reply to Defence to Counterclaim for Revocation','claimant', 'filing', 1, 'months', 'RoP.029.e',
|
||||
'Innerhalb von 1 Monat nach Zustellung der Replik auf Erwiderung zur Nichtigkeitswiderklage. Beschränkt auf Erwiderung der in der Replik aufgeworfenen Punkte.',
|
||||
'Within 1 month of service of the Reply to Defence to CCR. Limited to a response to matters raised in the Reply.',
|
||||
32, ARRAY['with_ccr']::text[], 'rejoinder-on-reply-to-defence-to-ccr', 'UPC.RoP.29.e')
|
||||
) AS new(parent_code, code, name, name_en, primary_party, event_type, duration_value, duration_unit, rule_code, deadline_notes, deadline_notes_en, sequence_order, condition_flag, concept_slug, legal_source)
|
||||
JOIN paliad.proceeding_types pt ON pt.code = 'UPC_INF'
|
||||
JOIN paliad.deadline_rules parent_rule ON parent_rule.proceeding_type_id = pt.id AND parent_rule.code = new.parent_code;
|
||||
|
||||
-- ============================================================================
|
||||
-- 6. UPC_INF cross-flow rules — wave 4 (final amend rejoinder)
|
||||
-- ============================================================================
|
||||
|
||||
INSERT INTO paliad.deadline_rules (
|
||||
proceeding_type_id, parent_id, code, name, name_en, primary_party, event_type,
|
||||
is_mandatory, duration_value, duration_unit, timing, rule_code, deadline_notes, deadline_notes_en,
|
||||
sequence_order, condition_flag, concept_id, legal_source, is_spawn, is_active
|
||||
)
|
||||
SELECT
|
||||
pt.id, parent_rule.id, 'inf.rejoin_amd', 'Duplik auf Replik zum Patentänderungsantrag', 'Rejoinder to Reply on Application to Amend',
|
||||
'defendant', 'filing', true, 1, 'months', 'after', 'RoP.032.3',
|
||||
'Innerhalb von 1 Monat nach Zustellung der Replik auf Erwiderung zum Patentänderungsantrag. Beschränkt auf die in der Replik aufgeworfenen Punkte.',
|
||||
'Within 1 month of service of the Reply to Defence to Application to amend. Limited to matters raised in the Reply.',
|
||||
33, ARRAY['with_ccr','with_amend']::text[],
|
||||
(SELECT id FROM paliad.deadline_concepts WHERE slug = 'rejoinder-on-reply-to-amend'),
|
||||
'UPC.RoP.32.3', false, true
|
||||
FROM paliad.proceeding_types pt
|
||||
JOIN paliad.deadline_rules parent_rule ON parent_rule.proceeding_type_id = pt.id AND parent_rule.code = 'inf.reply_def_amd'
|
||||
WHERE pt.code = 'UPC_INF';
|
||||
|
||||
-- ============================================================================
|
||||
-- 7. UPC_REV cross-flow rules — wave 1 (parents = pre-existing rev.defence)
|
||||
-- ============================================================================
|
||||
|
||||
INSERT INTO paliad.deadline_rules (
|
||||
proceeding_type_id, parent_id, code, name, name_en, primary_party, event_type,
|
||||
is_mandatory, duration_value, duration_unit, timing, rule_code, deadline_notes, deadline_notes_en,
|
||||
sequence_order, condition_flag, concept_id, legal_source, is_spawn, is_active
|
||||
)
|
||||
SELECT
|
||||
pt.id, parent_rule.id, new.code, new.name, new.name_en, new.primary_party, new.event_type,
|
||||
true, new.duration_value, new.duration_unit, 'after', new.rule_code, new.deadline_notes, new.deadline_notes_en,
|
||||
new.sequence_order, new.condition_flag,
|
||||
(SELECT id FROM paliad.deadline_concepts WHERE slug = new.concept_slug),
|
||||
new.legal_source, false, true
|
||||
FROM (VALUES
|
||||
('rev.defence', 'rev.app_to_amend', 'Antrag auf Patentänderung', 'Application to Amend the Patent', 'defendant', 'filing', 0, 'months', 'RoP.049.2.a',
|
||||
'Optional in der Erwiderung zur Nichtigkeitsklage einzureichen (R.49.2.a). Reicht der Patentinhaber einen Patentänderungsantrag ein, eröffnet das den R.55 (= R.32 m.m.) Zyklus.',
|
||||
'Optional, lodged together with the Defence to revocation per R.49.2(a). Filing this opens the R.55 (= R.32 m.m.) cycle.',
|
||||
11, ARRAY['with_amend']::text[], 'application-to-amend', 'UPC.RoP.49.2.a'),
|
||||
('rev.defence', 'rev.cc_inf', 'Verletzungswiderklage', 'Counterclaim for Infringement', 'defendant', 'filing', 0, 'months', 'RoP.049.2.b',
|
||||
'Optional in der Erwiderung zur Nichtigkeitsklage einzuschließen (R.49.2.b, R.50). Eröffnet den R.56-Zyklus.',
|
||||
'Optional, included in the Defence to revocation per R.49.2(b) and R.50. Opens the R.56 cycle.',
|
||||
12, ARRAY['with_cci']::text[], 'counterclaim-for-infringement', 'UPC.RoP.49.2.b')
|
||||
) AS new(parent_code, code, name, name_en, primary_party, event_type, duration_value, duration_unit, rule_code, deadline_notes, deadline_notes_en, sequence_order, condition_flag, concept_slug, legal_source)
|
||||
JOIN paliad.proceeding_types pt ON pt.code = 'UPC_REV'
|
||||
JOIN paliad.deadline_rules parent_rule ON parent_rule.proceeding_type_id = pt.id AND parent_rule.code = new.parent_code;
|
||||
|
||||
-- ============================================================================
|
||||
-- 8. UPC_REV cross-flow rules — wave 2 (defences to amend / CCI)
|
||||
-- ============================================================================
|
||||
|
||||
INSERT INTO paliad.deadline_rules (
|
||||
proceeding_type_id, parent_id, code, name, name_en, primary_party, event_type,
|
||||
is_mandatory, duration_value, duration_unit, timing, rule_code, deadline_notes, deadline_notes_en,
|
||||
sequence_order, condition_flag, concept_id, legal_source, is_spawn, is_active
|
||||
)
|
||||
SELECT
|
||||
pt.id, parent_rule.id, new.code, new.name, new.name_en, new.primary_party, new.event_type,
|
||||
true, new.duration_value, new.duration_unit, 'after', new.rule_code, new.deadline_notes, new.deadline_notes_en,
|
||||
new.sequence_order, new.condition_flag,
|
||||
(SELECT id FROM paliad.deadline_concepts WHERE slug = new.concept_slug),
|
||||
new.legal_source, false, true
|
||||
FROM (VALUES
|
||||
('rev.app_to_amend', 'rev.def_to_amend', 'Erwiderung auf Patentänderungsantrag', 'Defence to Application to Amend', 'claimant', 'filing', 2, 'months', 'RoP.043.3',
|
||||
'Innerhalb von 2 Monaten nach Zustellung des Patentänderungsantrags (R.43.3 i.V.m. R.55, R.32.1 m.m.).',
|
||||
'Within 2 months of service of the Application to amend (R.43.3 read with R.55 and R.32.1 m.m.).',
|
||||
21, ARRAY['with_amend']::text[], 'defence-to-application-to-amend', 'UPC.RoP.43.3'),
|
||||
('rev.cc_inf', 'rev.def_cci', 'Erwiderung auf Verletzungswiderklage', 'Defence to Counterclaim for Infringement', 'claimant', 'filing', 2, 'months', 'RoP.056.1',
|
||||
'Innerhalb von 2 Monaten nach Zustellung der Verletzungswiderklage (R.56.1).',
|
||||
'Within 2 months of service of the Counterclaim for infringement (R.56.1).',
|
||||
22, ARRAY['with_cci']::text[], 'defence-to-counterclaim-for-infringement','UPC.RoP.56.1')
|
||||
) AS new(parent_code, code, name, name_en, primary_party, event_type, duration_value, duration_unit, rule_code, deadline_notes, deadline_notes_en, sequence_order, condition_flag, concept_slug, legal_source)
|
||||
JOIN paliad.proceeding_types pt ON pt.code = 'UPC_REV'
|
||||
JOIN paliad.deadline_rules parent_rule ON parent_rule.proceeding_type_id = pt.id AND parent_rule.code = new.parent_code;
|
||||
|
||||
-- ============================================================================
|
||||
-- 9. UPC_REV cross-flow rules — wave 3 (replies)
|
||||
-- ============================================================================
|
||||
|
||||
INSERT INTO paliad.deadline_rules (
|
||||
proceeding_type_id, parent_id, code, name, name_en, primary_party, event_type,
|
||||
is_mandatory, duration_value, duration_unit, timing, rule_code, deadline_notes, deadline_notes_en,
|
||||
sequence_order, condition_flag, concept_id, legal_source, is_spawn, is_active
|
||||
)
|
||||
SELECT
|
||||
pt.id, parent_rule.id, new.code, new.name, new.name_en, new.primary_party, new.event_type,
|
||||
true, new.duration_value, new.duration_unit, 'after', new.rule_code, new.deadline_notes, new.deadline_notes_en,
|
||||
new.sequence_order, new.condition_flag,
|
||||
(SELECT id FROM paliad.deadline_concepts WHERE slug = new.concept_slug),
|
||||
new.legal_source, false, true
|
||||
FROM (VALUES
|
||||
('rev.def_to_amend', 'rev.reply_def_amd', 'Replik auf Erwiderung zum Patentänderungsantrag', 'Reply to Defence to Application to Amend', 'defendant', 'filing', 1, 'months', 'RoP.032.3',
|
||||
'Innerhalb von 1 Monat nach Zustellung der Erwiderung zum Patentänderungsantrag (R.32.3 m.m.).',
|
||||
'Within 1 month of service of the Defence to Application to amend (R.32.3 m.m.).',
|
||||
31, ARRAY['with_amend']::text[], 'reply-to-defence-to-application-to-amend','UPC.RoP.32.3'),
|
||||
('rev.def_cci', 'rev.reply_def_cci', 'Replik auf Erwiderung zur Verletzungswiderklage', 'Reply to Defence to Counterclaim for Infringement', 'defendant', 'filing', 1, 'months', 'RoP.056.3',
|
||||
'Innerhalb von 1 Monat nach Zustellung der Erwiderung zur Verletzungswiderklage (R.56.3).',
|
||||
'Within 1 month of service of the Defence to Counterclaim for infringement (R.56.3).',
|
||||
32, ARRAY['with_cci']::text[], 'reply-to-defence-to-counterclaim-for-infringement','UPC.RoP.56.3')
|
||||
) AS new(parent_code, code, name, name_en, primary_party, event_type, duration_value, duration_unit, rule_code, deadline_notes, deadline_notes_en, sequence_order, condition_flag, concept_slug, legal_source)
|
||||
JOIN paliad.proceeding_types pt ON pt.code = 'UPC_REV'
|
||||
JOIN paliad.deadline_rules parent_rule ON parent_rule.proceeding_type_id = pt.id AND parent_rule.code = new.parent_code;
|
||||
|
||||
-- ============================================================================
|
||||
-- 10. UPC_REV cross-flow rules — wave 4 (rejoinders)
|
||||
-- ============================================================================
|
||||
|
||||
INSERT INTO paliad.deadline_rules (
|
||||
proceeding_type_id, parent_id, code, name, name_en, primary_party, event_type,
|
||||
is_mandatory, duration_value, duration_unit, timing, rule_code, deadline_notes, deadline_notes_en,
|
||||
sequence_order, condition_flag, concept_id, legal_source, is_spawn, is_active
|
||||
)
|
||||
SELECT
|
||||
pt.id, parent_rule.id, new.code, new.name, new.name_en, new.primary_party, new.event_type,
|
||||
true, new.duration_value, new.duration_unit, 'after', new.rule_code, new.deadline_notes, new.deadline_notes_en,
|
||||
new.sequence_order, new.condition_flag,
|
||||
(SELECT id FROM paliad.deadline_concepts WHERE slug = new.concept_slug),
|
||||
new.legal_source, false, true
|
||||
FROM (VALUES
|
||||
('rev.reply_def_amd','rev.rejoin_amd', 'Duplik auf Replik zum Patentänderungsantrag', 'Rejoinder to Reply on Application to Amend', 'claimant', 'filing', 1, 'months', 'RoP.032.3',
|
||||
'Innerhalb von 1 Monat nach Zustellung der Replik (R.32.3 m.m.). Beschränkt auf die in der Replik aufgeworfenen Punkte.',
|
||||
'Within 1 month of service of the Reply (R.32.3 m.m.). Limited to matters raised in the Reply.',
|
||||
40, ARRAY['with_amend']::text[], 'rejoinder-on-reply-to-amend', 'UPC.RoP.32.3'),
|
||||
('rev.reply_def_cci','rev.rejoin_cci', 'Duplik auf Replik zur Erwiderung Verletzungswiderklage', 'Rejoinder to Reply on Counterclaim for Infringement', 'claimant', 'filing', 1, 'months', 'RoP.056.4',
|
||||
'Innerhalb von 1 Monat nach Zustellung der Replik (R.56.4). Beschränkt auf die in der Replik aufgeworfenen Punkte.',
|
||||
'Within 1 month of service of the Reply (R.56.4). Limited to matters raised in the Reply.',
|
||||
41, ARRAY['with_cci']::text[], 'rejoinder-on-counterclaim-for-infringement','UPC.RoP.56.4')
|
||||
) AS new(parent_code, code, name, name_en, primary_party, event_type, duration_value, duration_unit, rule_code, deadline_notes, deadline_notes_en, sequence_order, condition_flag, concept_slug, legal_source)
|
||||
JOIN paliad.proceeding_types pt ON pt.code = 'UPC_REV'
|
||||
JOIN paliad.deadline_rules parent_rule ON parent_rule.proceeding_type_id = pt.id AND parent_rule.code = new.parent_code;
|
||||
@@ -234,47 +234,83 @@ func (s *FristenrechnerService) Calculate(ctx context.Context, proceedingCode, t
|
||||
}
|
||||
parentIsCourtSet := r.ParentID != nil && courtSet[*r.ParentID] && !parentOverridden
|
||||
|
||||
// Zero-duration rules either anchor the timeline (trigger date) or
|
||||
// represent court-set waypoints with no calculable date. The court
|
||||
// path covers two flavours:
|
||||
// 1. zero-duration with a parent_id (waypoint chained off another
|
||||
// rule, original behaviour).
|
||||
// 2. zero-duration with no parent but flagged as a court-driven
|
||||
// event (Zwischenverfahren / Mündliche Verhandlung /
|
||||
// Entscheidung etc.) — without this, those rendered as
|
||||
// IsRootEvent and emitted the trigger date as their own date,
|
||||
// which then leaked into any downstream rule that chained off
|
||||
// them (e.g. RoP.151 Antrag auf Kostenentscheidung).
|
||||
// Zero-duration rules fall into one of four buckets:
|
||||
// 1. parent=nil, not court-determined → IsRootEvent (trigger anchor)
|
||||
// 2. parent=nil, court-determined → IsCourtSet (Zwischenverfahren /
|
||||
// Mündliche Verhandlung / Entscheidung etc.)
|
||||
// 3. parent set, court-determined → IsCourtSet (waypoint)
|
||||
// 4. parent set, NOT court-determined → "filed-with-parent"
|
||||
// semantic: rule is filed AT THE SAME TIME as its parent
|
||||
// (e.g. UPC_REV.rev.app_to_amend, rev.cc_inf — R.49(2) says
|
||||
// Application to amend / Counterclaim for infringement are
|
||||
// INCLUDED in the Defence to revocation). Use the parent's
|
||||
// computed date.
|
||||
//
|
||||
// AnchorOverrides: when the user has set a date for this court-set
|
||||
// rule, surface it as a real date and propagate it as the anchor
|
||||
// for downstream rules.
|
||||
// AnchorOverrides: when the user has set a date for any
|
||||
// zero-duration rule, that override wins over both the
|
||||
// court-set placeholder and the parent-inheritance.
|
||||
if r.DurationValue == 0 {
|
||||
// User override always wins.
|
||||
if r.Code != nil {
|
||||
if ov, ok := overrideDates[*r.Code]; ok {
|
||||
d.DueDate = ov.Format("2006-01-02")
|
||||
d.OriginalDate = d.DueDate
|
||||
d.IsOverridden = true
|
||||
computed[*r.Code] = ov
|
||||
deadlines = append(deadlines, d)
|
||||
continue
|
||||
}
|
||||
}
|
||||
|
||||
if r.ParentID == nil && !isCourtDeterminedRule(r) {
|
||||
// Bucket 1: timeline anchor.
|
||||
d.IsRootEvent = true
|
||||
d.DueDate = triggerDateStr
|
||||
d.OriginalDate = triggerDateStr
|
||||
if r.Code != nil {
|
||||
computed[*r.Code] = triggerDate
|
||||
}
|
||||
} else if r.Code != nil {
|
||||
if ov, ok := overrideDates[*r.Code]; ok {
|
||||
// User has filled in this court-set date; treat as a
|
||||
// real anchor. Don't apply holiday adjustment — the
|
||||
// user's date is authoritative, even if it lands on
|
||||
// a non-working day (real-world example: a court
|
||||
// decision can be issued on a Saturday).
|
||||
d.DueDate = ov.Format("2006-01-02")
|
||||
d.OriginalDate = d.DueDate
|
||||
d.IsOverridden = true
|
||||
computed[*r.Code] = ov
|
||||
} else {
|
||||
} else if r.ParentID != nil && !isCourtDeterminedRule(r) {
|
||||
// Bucket 4: filed-with-parent. Inherit parent's date.
|
||||
// If parent is court-set, we have nothing to inherit —
|
||||
// fall through to court-set marking.
|
||||
if parentIsCourtSet {
|
||||
d.IsCourtSet = true
|
||||
d.DueDate = ""
|
||||
d.OriginalDate = ""
|
||||
courtSet[r.ID] = true
|
||||
} else {
|
||||
var parentDate time.Time
|
||||
var haveParentDate bool
|
||||
for _, prev := range rules {
|
||||
if prev.ID == *r.ParentID {
|
||||
if prev.Code != nil {
|
||||
if ov, ok := overrideDates[*prev.Code]; ok {
|
||||
parentDate = ov
|
||||
haveParentDate = true
|
||||
} else if ref, ok := computed[*prev.Code]; ok {
|
||||
parentDate = ref
|
||||
haveParentDate = true
|
||||
}
|
||||
}
|
||||
break
|
||||
}
|
||||
}
|
||||
if haveParentDate {
|
||||
d.DueDate = parentDate.Format("2006-01-02")
|
||||
d.OriginalDate = d.DueDate
|
||||
if r.Code != nil {
|
||||
computed[*r.Code] = parentDate
|
||||
}
|
||||
} else {
|
||||
d.IsCourtSet = true
|
||||
d.DueDate = ""
|
||||
d.OriginalDate = ""
|
||||
courtSet[r.ID] = true
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// Buckets 2 + 3: court-determined.
|
||||
d.IsCourtSet = true
|
||||
d.DueDate = ""
|
||||
d.OriginalDate = ""
|
||||
|
||||
Reference in New Issue
Block a user