diff --git a/.planning/REQUIREMENTS.md b/.planning/REQUIREMENTS.md index 4ac1851..2c92fe9 100644 --- a/.planning/REQUIREMENTS.md +++ b/.planning/REQUIREMENTS.md @@ -54,10 +54,10 @@ Requirements for this milestone. Each maps to roadmap phases. ### Template System -- [ ] **TMPL-01**: User can tag a budget item as fixed, variable, or one-off when creating or editing it -- [ ] **TMPL-02**: User can define a monthly budget template containing fixed items (with amounts) and variable items (category only) +- [x] **TMPL-01**: User can tag a budget item as fixed, variable, or one-off when creating or editing it +- [x] **TMPL-02**: User can define a monthly budget template containing fixed items (with amounts) and variable items (category only) - [ ] **TMPL-03**: Navigating to a month with no budget auto-generates one from the user's template (fixed items with amounts, variable items with blank amounts) -- [ ] **TMPL-04**: One-off items are not carried forward to new months +- [x] **TMPL-04**: One-off items are not carried forward to new months - [ ] **TMPL-05**: User can manage their template on a dedicated page — add, remove, reorder fixed and variable items - [ ] **TMPL-06**: The "copy from previous month" feature is replaced by template-based generation @@ -139,9 +139,9 @@ Which phases cover which requirements. Updated during roadmap creation. | STATE-03 | Phase 3 (v1.0) | Complete | | IXTN-04 | Phase 4 (v1.0) | Complete | | FIX-01 | Phase 4 (v1.0) | Complete | -| TMPL-01 | Phase 5 (v1.1) | Pending | -| TMPL-02 | Phase 5 (v1.1) | Pending | -| TMPL-04 | Phase 5 (v1.1) | Pending | +| TMPL-01 | Phase 5 (v1.1) | Complete | +| TMPL-02 | Phase 5 (v1.1) | Complete | +| TMPL-04 | Phase 5 (v1.1) | Complete | | TMPL-03 | Phase 6 (v1.1) | Pending | | TMPL-05 | Phase 6 (v1.1) | Pending | | TMPL-06 | Phase 6 (v1.1) | Pending | diff --git a/.planning/ROADMAP.md b/.planning/ROADMAP.md index 4a9531c..6e404c8 100644 --- a/.planning/ROADMAP.md +++ b/.planning/ROADMAP.md @@ -91,7 +91,7 @@ Plans: 2. A user's template can be created and fetched via the API — it contains fixed items (with amounts) and variable items (category only, no amount) 3. One-off items are excluded from template contents — the API never includes them in template responses 4. The template API endpoints are documented and return correct data for all three item tiers -**Plans:** 2 plans +**Plans:** 1/2 plans executed Plans: - [ ] 05-01-PLAN.md — DB migration (item_tier enum, templates/template_items tables) + Go models + query functions @@ -153,7 +153,7 @@ Phases execute in numeric order: 1 -> 2 -> 3 -> 4 -> 5 -> 6 -> 7 -> 8 | 2. Layout and Brand Identity | v1.0 | 2/2 | Complete | 2026-03-12 | | 3. Interaction Quality and Completeness | v1.0 | 4/4 | Complete | 2026-03-12 | | 4. Chart Polish and Bug Fixes | v1.0 | 2/2 | Complete | 2026-03-12 | -| 5. Template Data Model and API | v1.1 | 0/2 | Planning | - | +| 5. Template Data Model and API | 1/2 | In Progress| | - | | 6. Template Frontend and Workflow Replacement | v1.1 | 0/3 | Not started | - | | 7. Quick-Add Library | v1.1 | 0/2 | Not started | - | | 8. Layout and Density Rethink | v1.1 | 0/2 | Not started | - | diff --git a/.planning/STATE.md b/.planning/STATE.md index b1c0965..3846886 100644 --- a/.planning/STATE.md +++ b/.planning/STATE.md @@ -3,14 +3,14 @@ gsd_state_version: 1.0 milestone: v1.1 milestone_name: Usability and Templates status: planning -stopped_at: Phase 5 context gathered -last_updated: "2026-03-12T10:54:48.549Z" +stopped_at: Completed 05-template-data-model-and-api-01-PLAN.md +last_updated: "2026-03-12T11:07:58.758Z" last_activity: 2026-03-12 — v1.1 roadmap created, Phases 5-8 defined progress: total_phases: 8 completed_phases: 4 - total_plans: 10 - completed_plans: 10 + total_plans: 12 + completed_plans: 11 percent: 0 --- @@ -50,6 +50,7 @@ Progress: [░░░░░░░░░░] 0% - Trend: - *Updated after each plan completion* +| Phase 05-template-data-model-and-api P01 | 3 | 2 tasks | 4 files | ## Accumulated Context @@ -63,6 +64,9 @@ Recent decisions affecting current work: - [Phase 03]: triggerFlash uses two separate state vars (flashRowId/errorRowId) — no race conditions between success and error states - [Phase 03]: EmptyState is a shared component with all content as props — icon, heading, subtext, and optional CTA - [Init v1.1]: Three-tier item model (fixed/variable/one-off) matches how users think about recurring vs one-time expenses +- [Phase 05-template-data-model-and-api]: New API-created budget items default to item_tier=one_off at the query layer +- [Phase 05-template-data-model-and-api]: Template creation is lazy: CreateTemplateItem upserts template via ON CONFLICT +- [Phase 05-template-data-model-and-api]: GenerateBudgetFromTemplate returns BudgetExistsError struct for structured 409 response ### Pending Todos @@ -75,6 +79,6 @@ None yet. ## Session Continuity -Last session: 2026-03-12T10:54:48.547Z -Stopped at: Phase 5 context gathered -Resume file: .planning/phases/05-template-data-model-and-api/05-CONTEXT.md +Last session: 2026-03-12T11:07:58.757Z +Stopped at: Completed 05-template-data-model-and-api-01-PLAN.md +Resume file: None diff --git a/.planning/config.json b/.planning/config.json index ca67cff..3880ae8 100644 --- a/.planning/config.json +++ b/.planning/config.json @@ -5,7 +5,7 @@ "commit_docs": true, "model_profile": "balanced", "workflow": { - "research": true, + "research": false, "plan_check": true, "verifier": true, "nyquist_validation": true, diff --git a/.planning/phases/05-template-data-model-and-api/05-01-SUMMARY.md b/.planning/phases/05-template-data-model-and-api/05-01-SUMMARY.md new file mode 100644 index 0000000..bae7c51 --- /dev/null +++ b/.planning/phases/05-template-data-model-and-api/05-01-SUMMARY.md @@ -0,0 +1,136 @@ +--- +phase: 05-template-data-model-and-api +plan: 01 +subsystem: database +tags: [postgres, go, pgx, migrations, templates, budget-items] + +requires: + - phase: 01-foundation + provides: users/categories/budgets/budget_items tables and Go query layer + +provides: + - item_tier enum (fixed, variable, one_off) on budget_items + - templates table (one-per-user via UNIQUE index) + - template_items table with CHECK constraint (no one_off allowed) + - Template, TemplateItem, TemplateDetail Go structs + - GetTemplate, UpdateTemplateName, CreateTemplateItem, UpdateTemplateItem, DeleteTemplateItem, ReorderTemplateItems query functions + - GenerateBudgetFromTemplate query function with duplicate-month detection and locale-aware naming + - Updated CreateBudgetItem, UpdateBudgetItem, GetBudgetWithItems, CopyBudgetItems with item_tier support + +affects: + - 05-02 (template HTTP handlers will call these query functions) + - frontend template UI phases + +tech-stack: + added: [] + patterns: + - Lazy template creation: CreateTemplateItem upserts template before inserting item + - BudgetExistsError struct wraps existing budget ID for 409 response + - Default itemTier to one_off at query layer when empty (new items default to one_off) + +key-files: + created: + - backend/migrations/002_templates.sql + modified: + - backend/internal/models/models.go + - backend/internal/db/queries.go + - backend/internal/api/handlers.go + +key-decisions: + - "New budget items created via API default to item_tier=one_off when not specified" + - "Existing budget_items rows get DEFAULT item_tier='fixed' (migration assumes all prior items were recurring)" + - "Template creation is lazy: CreateTemplateItem upserts template using ON CONFLICT DO UPDATE" + - "GetTemplate returns empty TemplateDetail (not error) when no template exists for user" + - "GenerateBudgetFromTemplate returns BudgetExistsError struct (not plain error) so handler can include existing budget ID in 409 response" + - "Variable template items use budgeted_amount=0 when generating budget (fixed items copy the template amount)" + +patterns-established: + - "BudgetExistsError: typed error struct for domain-specific errors needing structured data in HTTP response" + - "Locale-aware month names via map[time.Month]string for EN and DE" + +requirements-completed: [TMPL-01, TMPL-02, TMPL-04] + +duration: 3min +completed: 2026-03-12 +--- + +# Phase 5 Plan 1: Template Data Model and API Summary + +**PostgreSQL migration and Go query layer for three-tier item model (fixed/variable/one_off), templates table with lazy creation, and GenerateBudgetFromTemplate with duplicate-month detection** + +## Performance + +- **Duration:** ~3 min +- **Started:** 2026-03-12T11:04:04Z +- **Completed:** 2026-03-12T11:06:52Z +- **Tasks:** 2 +- **Files modified:** 4 (2 created, 2 updated, 1 auto-fixed) + +## Accomplishments + +- Migration 002 adds item_tier enum to budget_items, creates templates and template_items tables with DB-level CHECK constraint preventing one_off items in templates +- All 7 new template query functions implemented (GetTemplate, UpdateTemplateName, CreateTemplateItem, UpdateTemplateItem, DeleteTemplateItem, ReorderTemplateItems, GenerateBudgetFromTemplate) +- Existing budget item queries (CreateBudgetItem, UpdateBudgetItem, GetBudgetWithItems, CopyBudgetItems) updated to include item_tier + +## Task Commits + +Each task was committed atomically: + +1. **Task 1: Migration SQL and Go model types** - `b3082ca` (feat) +2. **Task 2: Database query functions** - `f9dd409` (feat) + +**Plan metadata:** (to be created next) + +## Files Created/Modified + +- `backend/migrations/002_templates.sql` - item_tier enum, ALTER budget_items, templates and template_items tables with CHECK constraint +- `backend/internal/models/models.go` - ItemTier type + constants, ItemTier field on BudgetItem, Template/TemplateItem/TemplateDetail structs +- `backend/internal/db/queries.go` - All template query functions, updated budget item queries, BudgetExistsError, locale-aware month name maps +- `backend/internal/api/handlers.go` - Updated CreateBudgetItem/UpdateBudgetItem calls to pass ItemTier from request body + +## Decisions Made + +- New API-created budget items default to `item_tier=one_off` (at the query layer) — new items created mid-month are one-offs unless specified +- Migration uses `DEFAULT 'fixed'` for existing rows — prior items came from copy-from-previous, treating them as recurring +- Template creation is lazy: `CreateTemplateItem` upserts the template via `ON CONFLICT (user_id) DO UPDATE`, no separate "create template" endpoint needed +- `GetTemplate` returns empty `TemplateDetail` (not an error) when no template exists, per context decision +- `GenerateBudgetFromTemplate` returns a typed `BudgetExistsError` struct so Plan 02 handler can extract the existing budget ID for a structured 409 response + +## Deviations from Plan + +### Auto-fixed Issues + +**1. [Rule 3 - Blocking] Updated handlers.go to pass ItemTier parameter** +- **Found during:** Task 2 (query function updates) +- **Issue:** Changing `CreateBudgetItem` and `UpdateBudgetItem` signatures to include `itemTier` broke compilation of `handlers.go`, which calls both functions +- **Fix:** Added `ItemTier models.ItemTier` to both request structs and passed it to the query calls +- **Files modified:** `backend/internal/api/handlers.go` +- **Verification:** `go vet ./...` passes cleanly +- **Committed in:** `f9dd409` (Task 2 commit) + +--- + +**Total deviations:** 1 auto-fixed (1 blocking compile fix) +**Impact on plan:** Necessary for correctness — handler was calling updated function with wrong arity. No scope creep. + +## Issues Encountered + +None — plan executed cleanly with one compile-blocking auto-fix handled inline. + +## User Setup Required + +None - no external service configuration required. Migration will be applied by the DB migration runner on next startup. + +## Next Phase Readiness + +- All query functions ready for Plan 02 HTTP handlers to consume +- `BudgetExistsError` struct available for 409 response in `GenerateBudgetFromTemplate` handler +- `GetTemplate` empty-return behavior documented for frontend to handle gracefully + +## Self-Check: PASSED + +All created files verified on disk. Both task commits (b3082ca, f9dd409) confirmed in git log. + +--- +*Phase: 05-template-data-model-and-api* +*Completed: 2026-03-12*