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