Two bugs from the Slice B1 Berufung rollout, one fix surface:
Bug A — duplicate side selectors collapse into ONE proactive-side
picker with per-proceeding role labels. The Verfahrensablauf used to
show both ?side= (Klägerseite/Beklagtenseite) AND ?appellant= (same
labels in case-form) on the Berufung tile. Now: one side picker, with
labels that swap to Berufungskläger/Berufungsbeklagter on the unified
upc.apl.unified tile (and Antragsteller/Antragsgegner Nichtigkeit on
upc.rev.cfi, Einsprechende(r)/Patentinhaber(in) on epa.opp.*).
Bug B — 'Auslösendes Ereignis' label derives from appeal_target on
the unified Berufung tile (5 target-specific strings) instead of the
proceeding's own trigger_event_label. Endentscheidung (R.118) /
Kostenentscheidung / Anordnung / Entscheidung im
Schadensbemessungsverfahren / Anordnung der Bucheinsicht.
Migration 137 (additive, no triggers on proceeding_types — verified
via mcp__supabase__execute_sql before drafting; no updated_at on the
table — lesson from mig 134 HOTFIX 3; no audit_reason setup needed):
- ADD COLUMN role_proactive_label_de (text NULL)
- ADD COLUMN role_proactive_label_en (text NULL)
- ADD COLUMN role_reactive_label_de (text NULL)
- ADD COLUMN role_reactive_label_en (text NULL)
- Audit-first DO block lists the rows the UPDATE will touch.
- Backfill 4 proceedings (upc.apl.unified + upc.rev.cfi +
epa.opp.opd + epa.opp.boa); every other proceeding stays NULL
and the renderer falls back to default labels.
- Down drops the 4 columns.
Package additions (pkg/litigationplanner):
- ProceedingType gains 4 *string fields (RoleProactive/Reactive
LabelDE/EN) — db tags match the new columns; existing scans pick
them up via the proceedingTypeColumns extension.
- TriggerEventLabelForAppealTarget(target, lang) — Go-side map of
the 5 appeal-target slugs to their DE/EN trigger-event labels.
Empty result on unknown target signals "fall back to proceeding's
own trigger_event_label".
- Engine override: when CalcOptions.AppealTarget is set, the
resulting Timeline.TriggerEventLabel/EN are replaced from the
per-target map.
Frontend:
- Removed #appellant-row div (was a separate 3-radio selector
duplicating side).
- Dropped ?appellant= URL state + the change handler + the init
readback. The engine still consumes "appellant" — sourced from
currentSide for role-swap proceedings; null otherwise.
- applyRoleLabels(proceedingType) swaps the side-row radio labels
from a hardcoded ROLE_LABELS map mirroring mig 137's backfill.
Falls back to deadlines.side.claimant/defendant i18n keys for
proceedings without overrides.
- syncTriggerEventLabel reads data.triggerEventLabel from the calc
response — which the engine override now sets per appeal_target,
so no client-side mapping needed.
- i18n cleanup: removed orphan deadlines.appellant.* keys (label /
claimant / defendant / none) in both DE + EN.
Tests:
- pkg/litigationplanner/appeal_target_label_test.go pins the 5×2
label matrix + a coverage test that fails if a new entry in
AppealTargets is added without populating the label switch.
Acceptance:
- go build + go test all green (incl. new lp test).
- bun run build clean (i18n codegen drops 4 keys, regenerates).
- Live-DB audit before drafting confirmed: 4 target columns don't
exist on proceeding_types, zero triggers on the table, exact
column inventory matches the design.
56 lines
2.1 KiB
Go
56 lines
2.1 KiB
Go
package litigationplanner
|
|
|
|
import "testing"
|
|
|
|
// TestTriggerEventLabelForAppealTarget pins the per-target trigger-
|
|
// event label matrix (t-paliad-301 / m/paliad#132 Bug B). The 5
|
|
// canonical AppealTargets each have a DE + EN label; unknown targets
|
|
// return empty so the caller can fall back to the proceeding's own
|
|
// trigger_event_label.
|
|
func TestTriggerEventLabelForAppealTarget(t *testing.T) {
|
|
cases := []struct {
|
|
target string
|
|
lang string
|
|
want string
|
|
}{
|
|
{AppealTargetEndentscheidung, "de", "Endentscheidung (R.118)"},
|
|
{AppealTargetEndentscheidung, "en", "Final decision (R.118)"},
|
|
{AppealTargetKostenentscheidung, "de", "Kostenentscheidung"},
|
|
{AppealTargetKostenentscheidung, "en", "Cost decision"},
|
|
{AppealTargetAnordnung, "de", "Anordnung"},
|
|
{AppealTargetAnordnung, "en", "Order"},
|
|
{AppealTargetSchadensbemessung, "de", "Entscheidung im Schadensbemessungsverfahren"},
|
|
{AppealTargetSchadensbemessung, "en", "Damages-assessment decision"},
|
|
{AppealTargetBucheinsicht, "de", "Anordnung der Bucheinsicht"},
|
|
{AppealTargetBucheinsicht, "en", "Book-inspection order"},
|
|
// Unknown lang falls through to DE so the caller never gets
|
|
// an empty string for a known target.
|
|
{AppealTargetEndentscheidung, "fr", "Endentscheidung (R.118)"},
|
|
// Unknown target → empty so caller falls back to proceeding's
|
|
// trigger_event_label.
|
|
{"", "de", ""},
|
|
{"foo", "en", ""},
|
|
}
|
|
for _, c := range cases {
|
|
if got := TriggerEventLabelForAppealTarget(c.target, c.lang); got != c.want {
|
|
t.Errorf("TriggerEventLabelForAppealTarget(%q, %q) = %q, want %q",
|
|
c.target, c.lang, got, c.want)
|
|
}
|
|
}
|
|
}
|
|
|
|
// TestAppealTargetsCoverage ensures every entry in AppealTargets has
|
|
// a non-empty label in both languages. Adding a target to the slice
|
|
// without populating the switch would silently emit empty labels —
|
|
// this test catches that.
|
|
func TestAppealTargetsCoverage(t *testing.T) {
|
|
for _, target := range AppealTargets {
|
|
for _, lang := range []string{"de", "en"} {
|
|
if got := TriggerEventLabelForAppealTarget(target, lang); got == "" {
|
|
t.Errorf("AppealTarget %q has empty label for lang %q — add it to the switch",
|
|
target, lang)
|
|
}
|
|
}
|
|
}
|
|
}
|