Files
SimpleFinanceDash/.planning/phases/05-template-data-model-and-api/05-01-SUMMARY.md
Jean-Luc Makiola eec8f4a9df 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>
2026-03-12 12:08:11 +01:00

137 lines
6.2 KiB
Markdown

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