mirror of
https://github.com/escalante29/WealthySmart.git
synced 2026-07-17 13:48:47 +02:00
Models use Money = Annotated[Decimal, PlainSerializer(float)] so storage is exact NUMERIC(15,2) (rates 15,6) while JSON keeps emitting plain numbers — the frontend contract is unchanged, pinned by test_models_serialization.py. Service layers coerce to float at their boundaries (projection math, rate multipliers, agent tool output, the savings constants become Decimal) so no naive Decimal/float mixing can raise. Migration 7f567c8bafdb applied to dev: per-row values and the table sum byte-identical before/after; endpoints verified live on the new schema. 59/59 tests green. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
65 lines
1.6 KiB
Python
65 lines
1.6 KiB
Python
from decimal import Decimal
|
|
|
|
from sqlmodel import Session, select
|
|
|
|
from app.models.models import (
|
|
Account,
|
|
AccountType,
|
|
Bank,
|
|
SavingsAccrual,
|
|
Transaction,
|
|
)
|
|
|
|
MEMP_MONTHLY = Decimal("200000")
|
|
MPAT_MONTHLY = Decimal("200000")
|
|
|
|
|
|
def _get_savings_account(session: Session, bank: Bank) -> Account | None:
|
|
return session.exec(
|
|
select(Account).where(
|
|
Account.account_type == AccountType.SAVINGS,
|
|
Account.bank == bank,
|
|
)
|
|
).first()
|
|
|
|
|
|
def maybe_apply_monthly_savings(session: Session, tx: Transaction) -> SavingsAccrual | None:
|
|
"""Apply monthly savings contribution if this is the first salary of the month.
|
|
|
|
Idempotent: if a SavingsAccrual row already exists for (year, month), do nothing.
|
|
Bumps MEMP and MPAT savings account balances and records the accrual.
|
|
"""
|
|
year = tx.date.year
|
|
month = tx.date.month
|
|
|
|
existing = session.exec(
|
|
select(SavingsAccrual).where(
|
|
SavingsAccrual.year == year,
|
|
SavingsAccrual.month == month,
|
|
)
|
|
).first()
|
|
if existing:
|
|
return None
|
|
|
|
memp = _get_savings_account(session, Bank.MEMP)
|
|
mpat = _get_savings_account(session, Bank.MPAT)
|
|
if memp is None or mpat is None:
|
|
return None
|
|
|
|
memp.balance += MEMP_MONTHLY
|
|
mpat.balance += MPAT_MONTHLY
|
|
session.add(memp)
|
|
session.add(mpat)
|
|
|
|
accrual = SavingsAccrual(
|
|
year=year,
|
|
month=month,
|
|
memp_amount=MEMP_MONTHLY,
|
|
mpat_amount=MPAT_MONTHLY,
|
|
trigger_transaction_id=tx.id,
|
|
)
|
|
session.add(accrual)
|
|
session.commit()
|
|
session.refresh(accrual)
|
|
return accrual
|