"""Add preschool_planning_days: per-year record of "studiedagar" taken. `Child.preschool_planning_days_per_year` (see `app/entities/child.py`) is a single default count of annual preschool closure/planning days used by `app/services/allocation_service.py::_preschool_reserve_days`, which multiplies it by the number of years in the planning horizon and subtracts the lump sum from both parents' parental-leave pools. There was never a per-year record of the true count, nor of how many were actually taken. This table adds that per-year record: `planned_days` (may differ from the child's default for a specific year) and `taken_days` (nullable — `NULL` means "not recorded yet", distinct from "recorded as zero", which is the whole point of the feature). A missing row for a (child, year) still falls back to `child.preschool_planning_days_per_year` at the service layer (see `app/services/preschool_planning_days_service.py`); wiring the allocation engine to consume these rows is a follow-up, not part of this migration. A plain `CREATE TABLE` suffices — nothing existing changes, and there is nothing to backfill: before this migration no per-year figure could be recorded, so the correct history is "none". Must stay byte-for-byte equivalent to the `preschool_planning_days` table in `app/db/connection.py`'s `_FULL_SCHEMA` — `tests/integration/test_schema_parity.py` asserts exactly that. Revision ID: 007 Revises: 006 Create Date: 2026-09-20 """ from __future__ import annotations from alembic import op revision: str = "007" down_revision: str | None = "006" branch_labels: str | None = None depends_on: str | None = None def upgrade() -> None: op.execute( """ CREATE TABLE IF NOT EXISTS preschool_planning_days ( id TEXT PRIMARY KEY, child_id TEXT NOT NULL, year INTEGER NOT NULL, planned_days INTEGER NOT NULL, taken_days INTEGER, created_at TIMESTAMP NOT NULL, updated_at TIMESTAMP NOT NULL, UNIQUE(child_id, year), FOREIGN KEY (child_id) REFERENCES child(id) ON DELETE CASCADE ) """ ) op.execute( """ CREATE INDEX IF NOT EXISTS idx_preschool_planning_days_child ON preschool_planning_days (child_id) """ ) def downgrade() -> None: op.execute("DROP INDEX IF EXISTS idx_preschool_planning_days_child") op.execute("DROP TABLE IF EXISTS preschool_planning_days")