Files
WealthySmart/backend/alembic/versions/7884505b16b3_fk_delete_rules.py
Carlos Escalante 3cdd7d9eab Constraint and query pass: FK delete rules, grouped queries, tiebreakers
- FK rules via migration 7884505b16b3 (verified on dev with a rolled-
  back probe): transactions/recurring items SET NULL on category delete
  (this also fixes the 500 that DELETE /categories raised for in-use
  categories), water readings CASCADE with their receipt. Models declare
  ondelete to stay in sync. (ARCH-09, BE-11)
- compute_actuals_by_source: 3 grouped queries replace 10 single-
  aggregate round-trips; behavior pinned by the existing cycle suite.
  (BE-21)
- compute_cc_by_category prefetches category names (ARCH-15); agent
  pension latest-per-fund resolved in SQL (BE-04); municipal water
  consumption batched into one grouped query (BE-05).
- Deterministic pagination: date DESC, id DESC on all transaction
  listings. (ARCH-12)
- New agent-tool tests (session binding, JSON-safe output, batched
  queries): 64 tests green.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-06-10 14:45:46 -06:00

62 lines
2.2 KiB
Python

"""fk delete rules
Revision ID: 7884505b16b3
Revises: 7f567c8bafdb
Create Date: 2026-06-10 13:50:39.153585
"""
from typing import Sequence, Union
from alembic import op
import sqlalchemy as sa
import sqlmodel
# revision identifiers, used by Alembic.
revision: str = '7884505b16b3'
down_revision: Union[str, Sequence[str], None] = '7f567c8bafdb'
branch_labels: Union[str, Sequence[str], None] = None
depends_on: Union[str, Sequence[str], None] = None
def upgrade() -> None:
"""Add delete rules to the three FKs (review ARCH-09/BE-11).
Transactions and recurring items outlive their category (SET NULL);
water readings die with their receipt (CASCADE). Constraint names match
both pre-Alembic databases and fresh baseline builds (Postgres default
naming).
"""
op.drop_constraint("transaction_category_id_fkey", "transaction", type_="foreignkey")
op.create_foreign_key(
"transaction_category_id_fkey", "transaction", "category",
["category_id"], ["id"], ondelete="SET NULL",
)
op.drop_constraint("recurringitem_category_id_fkey", "recurringitem", type_="foreignkey")
op.create_foreign_key(
"recurringitem_category_id_fkey", "recurringitem", "category",
["category_id"], ["id"], ondelete="SET NULL",
)
op.drop_constraint("watermeterreading_receipt_id_fkey", "watermeterreading", type_="foreignkey")
op.create_foreign_key(
"watermeterreading_receipt_id_fkey", "watermeterreading", "municipalreceipt",
["receipt_id"], ["id"], ondelete="CASCADE",
)
def downgrade() -> None:
op.drop_constraint("watermeterreading_receipt_id_fkey", "watermeterreading", type_="foreignkey")
op.create_foreign_key(
"watermeterreading_receipt_id_fkey", "watermeterreading", "municipalreceipt",
["receipt_id"], ["id"],
)
op.drop_constraint("recurringitem_category_id_fkey", "recurringitem", type_="foreignkey")
op.create_foreign_key(
"recurringitem_category_id_fkey", "recurringitem", "category",
["category_id"], ["id"],
)
op.drop_constraint("transaction_category_id_fkey", "transaction", type_="foreignkey")
op.create_foreign_key(
"transaction_category_id_fkey", "transaction", "category",
["category_id"], ["id"],
)