Skip to content

Phase 1: Add expense statistics endpoint (GET /expenses/statistics)#191

Open
mnindrazaka wants to merge 2 commits into
mainfrom
claude/expense-stats-api-contract-gibb18
Open

Phase 1: Add expense statistics endpoint (GET /expenses/statistics)#191
mnindrazaka wants to merge 2 commits into
mainfrom
claude/expense-stats-api-contract-gibb18

Conversation

@mnindrazaka

Copy link
Copy Markdown
Collaborator

Summary

Implements the backend for expense statistics, adding a new GET /expenses/statistics endpoint that mirrors the existing transaction statistics pattern. This is Phase 1 of the Expense Statistics feature (see docs/prd-expense-statistics.md), focusing purely on the backend API layer.

Key Changes

Backend API Layer

  • Added ExpenseStatistic domain entity with date, budgetId, budgetName, and total fields
  • Implemented GetExpenseStatistics() repository method in MySQL that:
    • Groups expenses by period (date or month) and budget
    • Joins to budgets table to include budget names (including soft-deleted budgets)
    • Supports optional date range filtering with inclusive endDate (half-open SQL implementation)
    • Orders results chronologically then by budget name
    • Excludes soft-deleted expenses but preserves historical spend under deleted budgets' original names
  • Added GetExpenseStatistics() usecase with validation: rejects requests where startDate > endDate
  • Added GetExpenseStatistics() handler that parses query parameters (groupBy, startDate, endDate) and returns 400 on malformed dates
  • Registered /expenses/statistics route before /expenses/{expenseId} to prevent route capture collision

OpenAPI Contract

  • Added /expenses/statistics endpoint definition with query parameters and response schema
  • Added ExpenseStatistic and ExpenseStatisticResponse schemas (Kubb will regenerate TS client)

Testing

  • Added comprehensive repository tests covering:
    • Chronological ordering across year boundaries
    • Per-budget summation within periods
    • Bounded range filtering
    • Inclusive endDate behavior
    • Soft-deleted expense exclusion
    • Soft-deleted budget history preservation
  • Added usecase tests for validation and error handling
  • Added handler tests for parameter parsing and HTTP status codes
  • Added route test to verify /expenses/statistics is not captured by the {expenseId} wildcard

Implementation Details

  • Response shape: Flat rows {date, budgetId, budgetName, total} matching the SQL query directly; client-side pivot/zero-fill/aggregation is deferred to later phases
  • Date formats: %d-%m-%Y for day grouping, %m-%Y for month grouping (consistent with transaction statistics)
  • Range semantics: Inclusive on both bounds; endDate is implemented as half-open SQL (< endDate + 1 day) to include the entire day
  • Soft deletes: Expenses with deleted_at IS NOT NULL are excluded; budgets with deleted_at IS NOT NULL are included (preserving historical labels)
  • No schema changes: Uses existing expenses.total and idx_expenses_budget_id

This PR is independently shippable and verifiable with curl against seeded data. Subsequent phases will add frontend UI, dashboard restructuring, and mobile support.

https://claude.ai/code/session_014uLjCgvSSBgMyKeBAqDDCK

mnindrazaka and others added 2 commits July 10, 2026 11:24
Mirrors the existing /transactions/statistics pattern: groups non-deleted
expenses by period (date|month, default month) and budget, joined to
budgets so soft-deleted budgets still appear under their original name.
Adds the OpenAPI schema/path needed to generate the response types, plus
usecase/repo/handler/route wiring and unit + MySQL integration tests
covering grouping, per-budget split sums, inclusive date bounds, deleted
expense exclusion, soft-deleted budget history, and the
/expenses/statistics vs /expenses/{expenseId} route ordering.

Co-authored-by: Claude <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant