Skip to content

feat: goal-based savings tracking & milestones (#133)#391

Closed
sinatragian wants to merge 2 commits intorohitdash08:mainfrom
sinatragian:feat/savings-goals
Closed

feat: goal-based savings tracking & milestones (#133)#391
sinatragian wants to merge 2 commits intorohitdash08:mainfrom
sinatragian:feat/savings-goals

Conversation

@sinatragian
Copy link

@sinatragian sinatragian commented Mar 13, 2026

Adds goal-based savings tracking to fix #133.

New SavingsGoal and SavingsMilestone models with full CRUD under /savings/goals. Deposits update current_amount and auto-mark milestones as achieved when their threshold is crossed. Goal responses include progress_percent and remaining_amount.

Migration in app/db/migrations/004_savings_goals.sql, 31 tests.


Review fixes (latest commit)

  1. Withdrawal reverses achieved flags — if a withdrawal drops current_amount below target_amount, goal.achieved is reset to False so it accurately reflects live balance. Same logic for milestones: a withdrawal below a milestone's threshold resets milestone.achieved = False / achieved_at = None. Milestones are intentionally treated as live state (re-earnable), not immutable historical events — a comment in the code explains the trade-off and how to make them permanent if preferred.
  2. amount == 0 → 400 Bad Request — previously a silent no-op; now returns 400 with a descriptive error message.
  3. New tests added:
    • TestDepositEdgeCases.test_withdrawal_below_target_resets_goal_achieved
    • TestDepositEdgeCases.test_deposit_zero_returns_400
    • TestMilestones.test_withdrawal_below_milestone_resets_achieved

Closes #133

- SavingsGoal + SavingsMilestone models
- Full CRUD: GET/POST/PATCH/DELETE /savings/goals
- POST /savings/goals/<id>/deposit (deposit/withdrawal, auto-achievement)
- Milestone CRUD + auto-achievement on deposit
- Progress percentage + remaining_amount calculated fields
- Migration 004_savings_goals.sql
- 28 tests covering CRUD, deposits, milestones, auth
- docs/savings-goals.md

Closes rohitdash08#133
… milestones

- Reject amount==0 on POST /deposit with 400 Bad Request (was silent no-op)
- After a withdrawal that drops current_amount below target_amount, reset
  goal.achieved = False so the flag reflects live balance, not history
- Same reversal logic for milestones: withdrawal below a milestone threshold
  resets milestone.achieved = False / achieved_at = None (milestones are
  treated as live state, not immutable events — comment in code explains
  the trade-off and how to make them permanent if desired)
- Add TestDepositEdgeCases.test_withdrawal_below_target_resets_goal_achieved
- Add TestDepositEdgeCases.test_deposit_zero_returns_400
- Add TestMilestones.test_withdrawal_below_milestone_resets_achieved
@sinatragian
Copy link
Author

This is the canonical implementation — closing #461 to avoid confusion. PR #391 includes: SavingsMilestone model with reversible achievement logic, 31 tests covering edge cases (zero-amount, withdrawal reversals), migration file, and API documentation.

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.

Goal-based savings tracking & milestones

1 participant