Analytics: date-range filter + fix category endpoint Decimal crash

by-category and daily-spending accept start_date/end_date (range wins
over cycle); Analytics gets a two-month calendar range picker beside
the cycle selector. Fixes a latent float/Decimal TypeError that 500'd
spending_by_category whenever it had data — the category donut had
never rendered. Regression + range tests added.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
Carlos Escalante
2026-07-04 14:06:58 -06:00
parent 5033348320
commit ce7073cf24
4 changed files with 160 additions and 12 deletions

View File

@@ -43,6 +43,8 @@ class DailySpending(BaseModel):
def spending_by_category(
cycle_year: Optional[int] = None,
cycle_month: Optional[int] = None,
start_date: Optional[str] = None,
end_date: Optional[str] = None,
session: Session = Depends(get_session),
_user: str = Depends(get_current_user),
):
@@ -61,13 +63,19 @@ def spending_by_category(
.group_by(Transaction.category_id)
)
if cycle_year and cycle_month:
# Arbitrary date range takes precedence over the cycle filter.
if start_date and end_date:
query = query.where(
Transaction.date >= datetime.fromisoformat(start_date),
Transaction.date < datetime.fromisoformat(end_date),
)
elif cycle_year and cycle_month:
start, end = get_cycle_range(cycle_year, cycle_month)
query = query.where(Transaction.date >= start, Transaction.date < end)
rows = session.exec(query).all()
grand_total = sum(r[1] for r in rows) or 1
grand_total = float(sum(float(r[1]) for r in rows)) or 1.0
results = []
for category_id, total, count in rows:
@@ -165,6 +173,8 @@ def monthly_trend(
def daily_spending(
cycle_year: Optional[int] = None,
cycle_month: Optional[int] = None,
start_date: Optional[str] = None,
end_date: Optional[str] = None,
session: Session = Depends(get_session),
_user: str = Depends(get_current_user),
):
@@ -187,7 +197,12 @@ def daily_spending(
.order_by(func.date(Transaction.date))
)
if cycle_year and cycle_month:
if start_date and end_date:
query = query.where(
Transaction.date >= datetime.fromisoformat(start_date),
Transaction.date < datetime.fromisoformat(end_date),
)
elif cycle_year and cycle_month:
start, end = get_cycle_range(cycle_year, cycle_month)
query = query.where(Transaction.date >= start, Transaction.date < end)