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
+12 -4
View File
@@ -81,10 +81,11 @@ class LeaveRules(BaseModel):
@property
def max_savable_days_per_parent_at_age_4(self) -> int:
"""Age-4 ceiling — per PARENT. `max_savable_days_at_age_4` (96) is a
per-child pool; when a child has two parents, each parent's own
savable balance is capped at half of that (48), not 96 each (which
would let a two-parent family save 192 combined).
"""Age-4 ceiling — per PARENT, non-poolable. `max_savable_days_at_age_4`
(96) is a per-child pool; as soon as a child has two parents, each
parent's own savable balance is capped at half of that (48), not 96
each (which would let a two-parent family save 192 combined). This
applies purely based on how many parents the child has.
`app.utils.tip_predicates.resolve_age4_savable_cap` is the single
place that decides which of the two applies.
"""
@@ -132,6 +133,13 @@ class Settings(BaseSettings):
# a deliberate opt-in for local debugging only, never for production.
scraper_debug_dir: str | None = None
scraper_debug_enabled: bool = False
# Also dump debug artifacts (HTML + full-page screenshot) after a
# *successful* extraction, not just on error — a capture aid for
# confirming FK's real DOM (e.g. the family panel structure assumed by
# `app.scrapers.fk_scraper._SEL.FAMILY*`). Same personal-data warning as
# above applies; only takes effect when both `scraper_debug_enabled` and
# `scraper_debug_dir` are also set.
scraper_debug_dump_on_success: bool = False
# How long a terminal (done/error/cancelled) session's QR/result stays in
# RAM before `_ttl_watchdog`/`poll` purge it from `_sessions`.
scraper_result_retention_seconds: int = 300