"""Add 'deduced' to parental_leave_fetch.source's CHECK constraint. `source` gains a third value alongside 'scraped'/'manual': 'deduced', for a snapshot the app derived on the user's behalf (e.g. defaulting an unentered total) rather than one FK reported or the user typed in directly. SQLite has no `ALTER TABLE ... DROP/ADD CONSTRAINT`, so the table is recreated with the new CHECK and its rows copied across — the standard SQLite migration dance (see e.g. https://sqlite.org/lang_altertable.html#otheralter). Recreating the table also drops its indexes, so they're recreated afterwards. Must stay byte-for-byte equivalent, CHECK-constraint-wise, to `parental_leave_fetch` in `app/db/connection.py`'s `_FULL_SCHEMA` — `tests/integration/test_schema_parity.py` asserts exactly that. Revision ID: 002 Revises: 001 Create Date: 2026-09-19 """ from __future__ import annotations from alembic import op revision: str = "002" down_revision: str | None = "001" branch_labels: str | None = None depends_on: str | None = None _UPGRADE_SQL = """ CREATE TABLE parental_leave_fetch_new ( id TEXT PRIMARY KEY, personal_data_uuid TEXT NOT NULL, workspace_id TEXT NOT NULL, child_id TEXT NOT NULL, source TEXT NOT NULL CHECK (source IN ('scraped','manual','deduced')), fetched_at TIMESTAMP NOT NULL, total_days INTEGER NOT NULL, days_used INTEGER NOT NULL, days_left INTEGER NOT NULL, reserved_days_total INTEGER NOT NULL DEFAULT 90, reserved_days_left INTEGER NOT NULL, transferable_days_left INTEGER NOT NULL, dubbeldagar_left INTEGER NOT NULL, tier_data TEXT NOT NULL, created_at TIMESTAMP NOT NULL, updated_at TIMESTAMP NOT NULL, FOREIGN KEY (personal_data_uuid) REFERENCES personal_data(uuid) ON DELETE CASCADE, FOREIGN KEY (workspace_id) REFERENCES workspace(id) ON DELETE CASCADE, FOREIGN KEY (child_id) REFERENCES child(id) ON DELETE CASCADE ); INSERT INTO parental_leave_fetch_new ( id, personal_data_uuid, workspace_id, child_id, source, fetched_at, total_days, days_used, days_left, reserved_days_total, reserved_days_left, transferable_days_left, dubbeldagar_left, tier_data, created_at, updated_at ) SELECT id, personal_data_uuid, workspace_id, child_id, source, fetched_at, total_days, days_used, days_left, reserved_days_total, reserved_days_left, transferable_days_left, dubbeldagar_left, tier_data, created_at, updated_at FROM parental_leave_fetch; DROP TABLE parental_leave_fetch; ALTER TABLE parental_leave_fetch_new RENAME TO parental_leave_fetch; CREATE INDEX IF NOT EXISTS idx_plf_parent_child_fetched ON parental_leave_fetch (personal_data_uuid, child_id, fetched_at); CREATE INDEX IF NOT EXISTS idx_plf_workspace ON parental_leave_fetch (workspace_id); CREATE INDEX IF NOT EXISTS idx_plf_child ON parental_leave_fetch (child_id); """ _DOWNGRADE_SQL = """ CREATE TABLE parental_leave_fetch_old ( id TEXT PRIMARY KEY, personal_data_uuid TEXT NOT NULL, workspace_id TEXT NOT NULL, child_id TEXT NOT NULL, source TEXT NOT NULL CHECK (source IN ('scraped','manual')), fetched_at TIMESTAMP NOT NULL, total_days INTEGER NOT NULL, days_used INTEGER NOT NULL, days_left INTEGER NOT NULL, reserved_days_total INTEGER NOT NULL DEFAULT 90, reserved_days_left INTEGER NOT NULL, transferable_days_left INTEGER NOT NULL, dubbeldagar_left INTEGER NOT NULL, tier_data TEXT NOT NULL, created_at TIMESTAMP NOT NULL, updated_at TIMESTAMP NOT NULL, FOREIGN KEY (personal_data_uuid) REFERENCES personal_data(uuid) ON DELETE CASCADE, FOREIGN KEY (workspace_id) REFERENCES workspace(id) ON DELETE CASCADE, FOREIGN KEY (child_id) REFERENCES child(id) ON DELETE CASCADE ); INSERT INTO parental_leave_fetch_old ( id, personal_data_uuid, workspace_id, child_id, source, fetched_at, total_days, days_used, days_left, reserved_days_total, reserved_days_left, transferable_days_left, dubbeldagar_left, tier_data, created_at, updated_at ) SELECT id, personal_data_uuid, workspace_id, child_id, source, fetched_at, total_days, days_used, days_left, reserved_days_total, reserved_days_left, transferable_days_left, dubbeldagar_left, tier_data, created_at, updated_at FROM parental_leave_fetch WHERE source != 'deduced'; DROP TABLE parental_leave_fetch; ALTER TABLE parental_leave_fetch_old RENAME TO parental_leave_fetch; CREATE INDEX IF NOT EXISTS idx_plf_parent_child_fetched ON parental_leave_fetch (personal_data_uuid, child_id, fetched_at); CREATE INDEX IF NOT EXISTS idx_plf_workspace ON parental_leave_fetch (workspace_id); CREATE INDEX IF NOT EXISTS idx_plf_child ON parental_leave_fetch (child_id); """ def upgrade() -> None: from sqlalchemy import text for stmt in [s.strip() for s in _UPGRADE_SQL.split(";") if s.strip()]: op.execute(text(stmt)) def downgrade() -> None: from sqlalchemy import text for stmt in [s.strip() for s in _DOWNGRADE_SQL.split(";") if s.strip()]: op.execute(text(stmt))