Rework balances around per-parent days left and deduce the partner from FK

Manual entry now takes one parent's sjukpenningnivå, lägstanivå and
dubbeldagar left (plus optional reserved days) and derives the rest in
build_snapshot, shared with the scraper. The scraper also reads the
family panel and exposes a partner deduction; with two parents it is
saved as a 'deduced' snapshot (migration 002). Parent count is the single
source of entitlement: one member gets 480 days and 96 savable at age 4,
two get 240 and 48 each. Workspaces are capped at two parents.

Also lands the in-flight squash of migrations 002–011 into the initial
schema and the removal of planning_mode.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
This commit is contained in:
2026-09-19 12:27:29 +02:00
parent 375bc64f2f
commit e4719cb9ae
71 changed files with 5705 additions and 1859 deletions
@@ -49,6 +49,7 @@ class LeaveWishService:
parent_mode: str = "either",
parent_personal_data_uuid: UUID | None = None,
window_mode: str = "fixed",
wants_dubbeldag: bool = False,
) -> LeaveWish:
_validate(
window_start_month=window_start_month,
@@ -63,6 +64,7 @@ class LeaveWishService:
parent_mode=parent_mode,
parent_personal_data_uuid=parent_personal_data_uuid,
window_mode=window_mode,
wants_dubbeldag=wants_dubbeldag,
)
now = datetime.now()
entity = LeaveWish(
@@ -84,6 +86,7 @@ class LeaveWishService:
parent_mode=parent_mode,
parent_personal_data_uuid=parent_personal_data_uuid,
window_mode=window_mode,
wants_dubbeldag=wants_dubbeldag,
created_at=now,
updated_at=now,
)
@@ -124,6 +127,7 @@ class LeaveWishService:
parent_mode: str | None = None,
parent_personal_data_uuid: UUID | None = None,
window_mode: str | None = None,
wants_dubbeldag: bool | None = None,
_clear_child_id: bool = False,
_clear_last_year: bool = False,
_clear_parent_personal_data_uuid: bool = False,
@@ -192,6 +196,8 @@ class LeaveWishService:
entity.parent_personal_data_uuid = parent_personal_data_uuid
if window_mode is not None:
entity.window_mode = window_mode
if wants_dubbeldag is not None:
entity.wants_dubbeldag = wants_dubbeldag
_validate(
window_start_month=entity.window_start_month,
@@ -206,6 +212,7 @@ class LeaveWishService:
parent_mode=entity.parent_mode,
parent_personal_data_uuid=entity.parent_personal_data_uuid,
window_mode=entity.window_mode,
wants_dubbeldag=entity.wants_dubbeldag,
)
return await self._repo.update(leave_wish_id, entity)
@@ -250,6 +257,7 @@ def _validate(
parent_mode: str,
parent_personal_data_uuid: UUID | None,
window_mode: str,
wants_dubbeldag: bool = False,
) -> None:
if recurrence not in _VALID_RECURRENCE:
raise ValidationError(f"recurrence must be one of {_VALID_RECURRENCE}")
@@ -257,6 +265,8 @@ def _validate(
raise ValidationError(f"parent_mode must be one of {_VALID_PARENT_MODE}")
if window_mode not in _VALID_WINDOW_MODE:
raise ValidationError(f"window_mode must be one of {_VALID_WINDOW_MODE}")
if wants_dubbeldag and parent_mode != "both":
raise ValidationError("wants_dubbeldag may only be set when parent_mode is 'both'")
_validate_month_day(window_start_month, window_start_day, label="window_start")
_validate_month_day(window_end_month, window_end_day, label="window_end")