docs(05-01): complete template data model and API plan

- Create 05-01-SUMMARY.md documenting migration, models, and query functions
- Update STATE.md with decisions, metrics, and progress bar (92%)
- Update ROADMAP.md phase 5 progress (1/2 plans complete)
- Mark requirements TMPL-01, TMPL-02, TMPL-04 complete in REQUIREMENTS.md

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-03-12 12:08:11 +01:00
parent f9dd40984c
commit eec8f4a9df
5 changed files with 156 additions and 16 deletions

View File

@@ -54,10 +54,10 @@ Requirements for this milestone. Each maps to roadmap phases.
### Template System ### Template System
- [ ] **TMPL-01**: User can tag a budget item as fixed, variable, or one-off when creating or editing it - [x] **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-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-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-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 - [ ] **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 | | STATE-03 | Phase 3 (v1.0) | Complete |
| IXTN-04 | Phase 4 (v1.0) | Complete | | IXTN-04 | Phase 4 (v1.0) | Complete |
| FIX-01 | Phase 4 (v1.0) | Complete | | FIX-01 | Phase 4 (v1.0) | Complete |
| TMPL-01 | Phase 5 (v1.1) | Pending | | TMPL-01 | Phase 5 (v1.1) | Complete |
| TMPL-02 | Phase 5 (v1.1) | Pending | | TMPL-02 | Phase 5 (v1.1) | Complete |
| TMPL-04 | Phase 5 (v1.1) | Pending | | TMPL-04 | Phase 5 (v1.1) | Complete |
| TMPL-03 | Phase 6 (v1.1) | Pending | | TMPL-03 | Phase 6 (v1.1) | Pending |
| TMPL-05 | Phase 6 (v1.1) | Pending | | TMPL-05 | Phase 6 (v1.1) | Pending |
| TMPL-06 | Phase 6 (v1.1) | Pending | | TMPL-06 | Phase 6 (v1.1) | Pending |

View File

@@ -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) 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 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 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: Plans:
- [ ] 05-01-PLAN.md — DB migration (item_tier enum, templates/template_items tables) + Go models + query functions - [ ] 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 | | 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 | | 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 | | 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 | - | | 6. Template Frontend and Workflow Replacement | v1.1 | 0/3 | Not started | - |
| 7. Quick-Add Library | v1.1 | 0/2 | Not started | - | | 7. Quick-Add Library | v1.1 | 0/2 | Not started | - |
| 8. Layout and Density Rethink | v1.1 | 0/2 | Not started | - | | 8. Layout and Density Rethink | v1.1 | 0/2 | Not started | - |

View File

@@ -3,14 +3,14 @@ gsd_state_version: 1.0
milestone: v1.1 milestone: v1.1
milestone_name: Usability and Templates milestone_name: Usability and Templates
status: planning status: planning
stopped_at: Phase 5 context gathered stopped_at: Completed 05-template-data-model-and-api-01-PLAN.md
last_updated: "2026-03-12T10:54:48.549Z" last_updated: "2026-03-12T11:07:58.758Z"
last_activity: 2026-03-12 — v1.1 roadmap created, Phases 5-8 defined last_activity: 2026-03-12 — v1.1 roadmap created, Phases 5-8 defined
progress: progress:
total_phases: 8 total_phases: 8
completed_phases: 4 completed_phases: 4
total_plans: 10 total_plans: 12
completed_plans: 10 completed_plans: 11
percent: 0 percent: 0
--- ---
@@ -50,6 +50,7 @@ Progress: [░░░░░░░░░░] 0%
- Trend: - - Trend: -
*Updated after each plan completion* *Updated after each plan completion*
| Phase 05-template-data-model-and-api P01 | 3 | 2 tasks | 4 files |
## Accumulated Context ## 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]: 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 - [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 - [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 ### Pending Todos
@@ -75,6 +79,6 @@ None yet.
## Session Continuity ## Session Continuity
Last session: 2026-03-12T10:54:48.547Z Last session: 2026-03-12T11:07:58.757Z
Stopped at: Phase 5 context gathered Stopped at: Completed 05-template-data-model-and-api-01-PLAN.md
Resume file: .planning/phases/05-template-data-model-and-api/05-CONTEXT.md Resume file: None

View File

@@ -5,7 +5,7 @@
"commit_docs": true, "commit_docs": true,
"model_profile": "balanced", "model_profile": "balanced",
"workflow": { "workflow": {
"research": true, "research": false,
"plan_check": true, "plan_check": true,
"verifier": true, "verifier": true,
"nyquist_validation": true, "nyquist_validation": true,

View File

@@ -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*