Skip to content

feat(api): auto-complete savings goals when current_amount reaches target_amount#755

Open
Salmatcre8 wants to merge 1 commit into
Suncrest-Labs:mainfrom
Salmatcre8:feat/savings-goal-autocomplete
Open

feat(api): auto-complete savings goals when current_amount reaches target_amount#755
Salmatcre8 wants to merge 1 commit into
Suncrest-Labs:mainfrom
Salmatcre8:feat/savings-goal-autocomplete

Conversation

@Salmatcre8

Copy link
Copy Markdown
Contributor

Summary

Goals had no status field and never transitioned to completed at 100%, so the frontend couldn't distinguish an in-progress goal from a fully-funded one, and there was no trigger point for completion notifications / celebration UI.

Closes #684

What's included

  • Migration 040 — adds status (active|completed|archived, default active, CHECK-constrained) and completed_at columns, plus a status index.
  • DomainGoalStatus type + ParseStatus; Status and CompletedAt on SavingsGoal; ErrGoalCompleted.
  • Auto-completeenrichProgress transitions an active goal to completed once progress_pct >= 100, stamps completed_at, and persists the change.
  • List filterGET /api/v1/users/savings-goals?status=active|completed|archived (default returns all). Filtering is applied post-enrichment so a goal that just hit 100% shows up under ?status=completed immediately.
  • ImmutabilityPATCH on a completed goal returns 409 (ErrGoalCompleted); a completed goal may only be archived.
  • Repository — reads/writes the new columns; Create defaults status=active.

Acceptance criteria

  • Migration adds status (default active) + completed_at
  • Auto-transition to completed at progress_pct >= 100
  • completed_at set on completion
  • ?status=completed returns only completed goals
  • PATCH on a completed goal → 409
  • Unit test: 99% stays active; 100% transitions to completed
  • Integration test: reaching target flips status to completed

Tests

ok  github.com/suncrestlabs/nester/apps/api/internal/handler

Real SavingsGoalService + in-memory repo, driven through the handler: 99% stays active, 100% auto-completes with completed_at, ?status=completed filter, and PATCH completed → 409.

Notes / judgment calls

  • Tests live in the handler package (real service + in-memory repo, end-to-end via HTTP) because internal/service's existing *_test.go files (savings_goal_service_test.go, savings_schedule_service_test.go) do not compile on main — leftovers from #689 feat(api): fire push notifications when savings goal crosses #710 / the savings-schedule merge (stale mocks missing UpdateMilestones/CreateVault, uuid-keyed balances, missing constructor args). I deliberately kept this PR from touching those files to avoid conflicts with whatever fixes them; the behaviour is fully covered through the handler instead.
  • Pre-existing, unrelated: cmd/api/main.go does not build on main (wsHub missing PushToUser) — untouched here; my internal/... packages build and vet cleanly.
  • A completed goal is intentionally immutable except for archiving (status: archived); manual status: completed via PATCH is rejected (completion is automatic).

Goals had no status field and never transitioned to completed at 100%, so
the frontend couldn't tell an in-progress goal from a fully-funded one and
there was no trigger point for completion notifications (Suncrest-Labs#684).

- Migration 040 adds status (active|completed|archived, default active,
  CHECK-constrained) and completed_at columns + a status index.
- Domain: GoalStatus type + ParseStatus; Status and CompletedAt fields on
  SavingsGoal; ErrGoalCompleted.
- enrichProgress auto-transitions an active goal to completed once
  progress_pct >= 100, stamping completed_at and persisting the change.
- GET /api/v1/users/savings-goals?status=active|completed|archived filters
  (default returns all); filtering is applied post-enrichment so freshly
  completed goals are included.
- PATCH on a completed goal returns 409 (ErrGoalCompleted); a completed
  goal may only be archived.
- Repository reads/writes the new columns; Create defaults status=active.

Tests (handler-level, real service + in-memory repo): goal at 99% stays
active, goal at 100% auto-completes with completed_at set, ?status=completed
filter, and PATCH on a completed goal returns 409.

Closes Suncrest-Labs#684
@Salmatcre8 Salmatcre8 requested a review from 0xDeon as a code owner June 26, 2026 17:23
@drips-wave

drips-wave Bot commented Jun 26, 2026

Copy link
Copy Markdown

@Salmatcre8 Great news! 🎉 Based on an automated assessment of this PR, the linked Wave issue(s) no longer count against your application limits.

You can now already apply to more issues while waiting for a review of this PR. Keep up the great work! 🚀

Learn more about application limits

@0xDeon 0xDeon left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Merge conflict with main. Rebase onto main and resolve conflicts before this can be merged.

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.

feat(api): auto-complete savings goals when current_amount reaches target_amount

2 participants