mirror of
https://github.com/escalante29/WealthySmart.git
synced 2026-05-19 10:28:48 +02:00
Add cumulative balance tracking with editable overrides
All checks were successful
Deploy to VPS / deploy (push) Successful in 21s
All checks were successful
Deploy to VPS / deploy (push) Successful in 21s
- New BalanceOverride table for manual balance adjustments per month
- Cumulative balance computation with cross-year carryover
- Three new columns: Acum. Anterior, Neto Mes, Balance Acum.
- Inline editing on Balance Acum. cell (pencil icon for overrides)
- Year navigation clamped to 2026–2030, fresh start at March 2026
- PUT/DELETE /budget/balance-override/{year}/{month} endpoints
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -4,6 +4,7 @@ from datetime import datetime
|
||||
from sqlmodel import Session, func, select
|
||||
|
||||
from app.models.models import (
|
||||
BalanceOverride,
|
||||
RecurringFrequency,
|
||||
RecurringItem,
|
||||
RecurringItemType,
|
||||
@@ -12,6 +13,12 @@ from app.models.models import (
|
||||
TransactionType,
|
||||
)
|
||||
|
||||
MIN_YEAR = 2026
|
||||
MAX_YEAR = 2030
|
||||
# Fresh start: months before this are zeroed out
|
||||
FRESH_START_YEAR = 2026
|
||||
FRESH_START_MONTH = 3
|
||||
|
||||
|
||||
def get_effective_amount(item: RecurringItem, month: int, year: int) -> float | None:
|
||||
"""Return the effective amount for a recurring item in a given month, or None if inactive."""
|
||||
@@ -255,3 +262,80 @@ def compute_monthly_projection(
|
||||
"savings_items": savings_items,
|
||||
"actuals_by_source": list(actuals_by_source.values()),
|
||||
}
|
||||
|
||||
|
||||
def _get_december_cumulative(session: Session, year: int) -> float:
|
||||
"""Get the cumulative balance for December of a given year."""
|
||||
# Check for an override first
|
||||
override = session.exec(
|
||||
select(BalanceOverride).where(
|
||||
BalanceOverride.year == year, BalanceOverride.month == 12
|
||||
)
|
||||
).first()
|
||||
if override:
|
||||
return override.override_balance
|
||||
|
||||
# Compute the full year to get December's cumulative
|
||||
overrides = session.exec(
|
||||
select(BalanceOverride).where(BalanceOverride.year == year)
|
||||
).all()
|
||||
override_map = {o.month: o.override_balance for o in overrides}
|
||||
|
||||
cumulative = 0.0
|
||||
if year > FRESH_START_YEAR:
|
||||
cumulative = _get_december_cumulative(session, year - 1)
|
||||
|
||||
for m in range(1, 13):
|
||||
if year == FRESH_START_YEAR and m < FRESH_START_MONTH:
|
||||
continue
|
||||
data = compute_monthly_projection(session, year, m)
|
||||
cumulative += data["net_balance"]
|
||||
if m in override_map:
|
||||
cumulative = override_map[m]
|
||||
|
||||
return cumulative
|
||||
|
||||
|
||||
def compute_yearly_projection_with_cumulative(
|
||||
session: Session, year: int
|
||||
) -> list[dict]:
|
||||
"""Compute all 12 months with cumulative balance tracking."""
|
||||
overrides = session.exec(
|
||||
select(BalanceOverride).where(BalanceOverride.year == year)
|
||||
).all()
|
||||
override_map = {o.month: o.override_balance for o in overrides}
|
||||
|
||||
# Determine January carryover
|
||||
if year <= FRESH_START_YEAR:
|
||||
carryover = 0.0
|
||||
else:
|
||||
carryover = _get_december_cumulative(session, year - 1)
|
||||
|
||||
months = []
|
||||
for m in range(1, 13):
|
||||
data = compute_monthly_projection(session, year, m)
|
||||
|
||||
is_before_fresh_start = (
|
||||
year == FRESH_START_YEAR and m < FRESH_START_MONTH
|
||||
)
|
||||
|
||||
if is_before_fresh_start:
|
||||
data["carryover_balance"] = 0.0
|
||||
data["cumulative_balance"] = 0.0
|
||||
data["balance_overridden"] = False
|
||||
else:
|
||||
data["carryover_balance"] = carryover
|
||||
cumulative = carryover + data["net_balance"]
|
||||
|
||||
if m in override_map:
|
||||
cumulative = override_map[m]
|
||||
data["balance_overridden"] = True
|
||||
else:
|
||||
data["balance_overridden"] = False
|
||||
|
||||
data["cumulative_balance"] = cumulative
|
||||
carryover = cumulative
|
||||
|
||||
months.append(data)
|
||||
|
||||
return months
|
||||
|
||||
Reference in New Issue
Block a user