Keep "both parents" wishes both-off, and warn when they can't be

A lower-priority 'both' wish (e.g. a yearly autumn week) quietly fell back
to one parent per day once summer/Jul had used both parents' vacation, and
still counted as funded, so nothing warned.

- Allocator rescue pass: frees a vacation day by switching one parent's
  vacation to FP on another date (interior v+v, then Mon/Fri bridge days
  without creating an FP weekend wrap, then solo days) and spends it on the
  one-parent date as co-leave. Pinned days count as present.
- WindowOutcome.unmet_both_dates + new both_parents_unmet plan warning.
- Frontend maps the new fields; Dashboard warning card, year-row flag and a
  Calendar save-toast line list the affected years (sv + en).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
2026-09-22 23:24:28 +02:00
parent 1a02bdcdbf
commit 906cf70a98
20 changed files with 1265 additions and 14 deletions
+5
View File
@@ -116,6 +116,10 @@ class WindowOutcomeResponse(BaseModel):
# returned `wish.id`) find every OTHER outcome in this same batch that
# belongs to the same repeating wish.
series_id: UUID | None = None
# Mirrors `WindowOutcome.unmet_both_dates` — dates in this window still
# funded for only one parent after the backend's own repair pass
# (`_Ledger.rescue_degraded_both_windows`) already tried to fix them.
unmet_both_dates: list[date] = Field(default_factory=list)
class PlanWarningResponse(BaseModel):
@@ -217,6 +221,7 @@ class PlanProposalResponse(BaseModel):
],
shift_gain_days=o.shift_gain_days,
series_id=o.series_id,
unmet_both_dates=o.unmet_both_dates,
)
for o in proposal.outcomes
],