116 lines
5.7 KiB
Markdown
116 lines
5.7 KiB
Markdown
# Phase 5: Template Data Model and API - Context
|
|
|
|
**Gathered:** 2026-03-12
|
|
**Status:** Ready for planning
|
|
|
|
<domain>
|
|
## Phase Boundary
|
|
|
|
Backend infrastructure for the template system — new DB tables (templates, template_items), migration to add item_tier to budget_items, and REST endpoints for template CRUD and budget-from-template generation. No frontend changes in this phase. One-off items are excluded from templates at the data layer.
|
|
|
|
</domain>
|
|
|
|
<decisions>
|
|
## Implementation Decisions
|
|
|
|
### Item type storage
|
|
- New PostgreSQL ENUM `item_tier` with values: `fixed`, `variable`, `one_off`
|
|
- Added as column on `budget_items` table via migration 002
|
|
- Existing budget items default to `'fixed'` (they were created via copy-from-previous, treating all as recurring)
|
|
- Column named `item_tier` (not `item_type`) to avoid collision with existing `category_type` field on BudgetItem
|
|
- Settable via existing `POST /api/budgets/{id}/items` and `PUT /api/budgets/{id}/items/{itemId}` endpoints — optional field, defaults to `'one_off'` for new items
|
|
|
|
### Template ownership
|
|
- One template per user — `templates` table with UNIQUE constraint on `user_id`
|
|
- Lazy creation: template auto-created when user first adds a template item (POST /api/template/items)
|
|
- GET /api/template returns `{ "id": null, "items": [] }` when no template exists
|
|
- Template auto-named "My Template" on creation; user can rename via PUT /api/template
|
|
|
|
### Template item data model
|
|
- `template_items` table: id, template_id (FK), category_id (FK), item_tier, budgeted_amount (NULLABLE), sort_order, created_at, updated_at
|
|
- Fixed items: category + budgeted_amount set
|
|
- Variable items: category + budgeted_amount NULL
|
|
- CHECK constraint on template_items: `item_tier IN ('fixed', 'variable')` — one_off cannot exist in templates (DB-level enforcement)
|
|
|
|
### Generation endpoint
|
|
- `POST /api/budgets/generate` with body `{ "month": "2026-04", "currency": "EUR" }`
|
|
- Single atomic transaction: creates budget + all template-derived items together
|
|
- Fixed items generated with their template amounts; variable items generated with budgeted_amount = 0
|
|
- Returns 201 Created with full BudgetDetail response (same shape as GET /api/budgets/{id})
|
|
- Returns 409 Conflict with `{ "error": "budget already exists", "budget_id": "..." }` if budget for that month exists
|
|
- If user has no template: creates empty budget with zero items (no error)
|
|
- Budget auto-named from month using user's preferred_locale (localized month name + year, e.g. "April 2026")
|
|
- Start/end dates computed from month parameter (first and last day of month)
|
|
|
|
### API routes
|
|
- Singular `/api/template` (one template per user, no ID needed):
|
|
- `GET /api/template` — get template with items (joined category name, type, icon)
|
|
- `PUT /api/template` — update template name
|
|
- `POST /api/template/items` — add item (auto-creates template if needed)
|
|
- `PUT /api/template/items/{itemId}` — update item
|
|
- `DELETE /api/template/items/{itemId}` — remove item
|
|
- `PUT /api/template/items/reorder` — batch update sort_order
|
|
- Generation: `POST /api/budgets/generate` (under budgets, since the output is a budget)
|
|
|
|
### API response shape
|
|
- `item_tier` always included in budget item JSON responses (additive, non-breaking)
|
|
- Template GET response includes joined category details: category_name, category_type, category_icon
|
|
- Generation endpoint returns standard BudgetDetail shape
|
|
|
|
### Claude's Discretion
|
|
- Go struct design for Template and TemplateItem models
|
|
- Exact migration SQL structure and index choices
|
|
- Handler implementation patterns (error messages, validation order)
|
|
- Query function signatures and SQL query structure
|
|
- Month name localization approach (map vs library)
|
|
|
|
</decisions>
|
|
|
|
<specifics>
|
|
## Specific Ideas
|
|
|
|
- Migration defaults existing items to 'fixed' since the old copy-from-previous treated everything as recurring
|
|
- Lazy template creation removes friction — user doesn't need to "set up" a template before using the app
|
|
- 409 Conflict on duplicate month matches existing error patterns (409 for unique constraint violations)
|
|
- Template response joins category details so Phase 6 frontend doesn't need extra API calls
|
|
|
|
</specifics>
|
|
|
|
<code_context>
|
|
## Existing Code Insights
|
|
|
|
### Reusable Assets
|
|
- `models.go`: BudgetItem struct — add ItemTier field, BudgetDetail response shape reused by generation endpoint
|
|
- `queries.go`: 22 existing query functions — CopyBudgetItems as reference for template-to-budget generation pattern
|
|
- `handlers.go`: Handlers struct with queries dependency injection — add template handlers following same pattern
|
|
- `router.go`: Chi router with auth middleware group — add `/api/template` route group and `/api/budgets/generate` endpoint
|
|
|
|
### Established Patterns
|
|
- PostgreSQL ENUM types (category_type) — same pattern for item_tier
|
|
- User-scoped data isolation: all queries filter by userID from auth context
|
|
- JSON serialization with `json:"field_name"` tags on structs
|
|
- Error responses: `writeError(w, status, message)` helper
|
|
- UUID primary keys with `uuid_generate_v4()` default
|
|
- Decimal amounts via `shopspring/decimal` and `NUMERIC(12,2)` in PostgreSQL
|
|
|
|
### Integration Points
|
|
- `001_initial.sql` existing schema — new migration 002 adds item_tier column and template tables
|
|
- `handlers.go` CreateBudgetItem/UpdateBudgetItem — add item_tier to request parsing and query calls
|
|
- `queries.go` GetBudgetWithItems — add item_tier to SELECT and Scan
|
|
- `router.go` protected routes group — add template routes and generate endpoint
|
|
- `models.go` — add ItemTier type, Template struct, TemplateItem struct
|
|
|
|
</code_context>
|
|
|
|
<deferred>
|
|
## Deferred Ideas
|
|
|
|
None — discussion stayed within phase scope
|
|
|
|
</deferred>
|
|
|
|
---
|
|
|
|
*Phase: 05-template-data-model-and-api*
|
|
*Context gathered: 2026-03-12*
|