Issue #15 (TCO feature) implemented ownership_costs as a separate feature from documents, creating a broken data flow where costs and their source documents are disconnected.
Documents already collect cost data (Premium, Cost fields) but it goes NOWHERE
TCO calculation ONLY reads from ownership_costs, ignores documents entirely
Users must enter insurance/registration costs TWICE
No linkage between an insurance PDF and its cost record
Legacy vehicle table has redundant TCO fields
Current Data Storage (Redundant)
Location
Data
Used By TCO?
documents.details JSONB
premium, cost
NO
ownership_costs table
amount, interval, dates
YES
vehicles table (legacy)
insurance_cost, registration_cost
NO (deprecated)
Proposed Solution: Document-Driven Cost Creation
Target Architecture
Document Upload/Create (Insurance or Registration)
|
Cost Data (manual entry now, scanned later)
|
AUTO-CREATE ownership_cost record <-- document_id FK links back
|
VehiclesService.getTCO() reads from ownership_costs (unchanged)
|
TCODisplay shows costs with document linkage
Key Decisions
ownership_costs remains TCO data source - no changes to calculation logic
Documents CREATE ownership_cost records - when insurance/registration has cost data
Bi-directional linking - use existing document_id FK (currently unused)
Single entry point - remove standalone ownership-cost forms for insurance/registration
Future: Cost Scanning - Pro feature: AI extracts costs from PDFs automatically
Cleanup legacy - remove vehicle table TCO fields
Implementation Phases
Phase 1: Backend Connection
DocumentsService.create() auto-creates ownership_cost when insurance/registration has cost data
Link via ownership_costs.document_id FK
DocumentsService.update() syncs cost changes to linked ownership_cost
DocumentsService.delete() sets document_id to NULL (cost preserved, unlinked)
Phase 2: Frontend Consolidation
Insurance/Registration documents show their costs inline
Remove ownership-costs forms for insurance/registration types
Keep ownership-costs UI for tax/other only
Update vehicle detail page layout
Phase 3: Legacy Cleanup
Remove vehicle table TCO fields via migration
Migrate orphaned ownership_costs without documents
Update documentation
Note: Document cost scanning (OCR/AI) is OUT OF SCOPE - separate issue
Affected Areas
Backend
backend/src/features/documents/domain/documents.service.ts - create ownership_cost on document create
backend/src/features/documents/domain/documents.types.ts - ensure cost fields in details
Issue #29's assumptions are incorrect. This is NEW FEATURE creation, not refactoring:
Assumption in Issue
Actual State
ownership_costs table exists
DOES NOT EXIST
document_id FK exists (unused)
DOES NOT EXIST
Legacy vehicle TCO fields exist
DO NOT EXIST
VehiclesService.getTCO() exists
DOES NOT EXIST
Evidence: Full codebase exploration confirmed no backend/src/features/ownership-costs/ directory, no ownership_costs table in any migration, no document_id FK in any schema.
Decision Critic Verdict: REVISE
Key Insight: The original framing is backwards. Costs are primary entities that exist independently; documents are optional proof, not the source.
Revised Approach:
ownership_costs as standalone entities with OPTIONAL document_id FK
Users can create costs directly for ANY type (not just tax/other)
Documents with cost data can OPTIONALLY create linked cost records
CASCADE delete when document_id is set (preserves audit trail)
// Wrap document+cost creation in transaction for atomicity
returnawaitthis.db.transaction(async(trx)=>{constdoc=awaitthis.repo.insert({...},trx);if(costAmount>0){awaitthis.ownershipCostsService.createCost(...,trx);}returndoc;});
Blocking a user prevents them from interacting with repositories, such as opening or commenting on pull requests or issues. Learn more about blocking a user.
Problem Statement
Issue #15 (TCO feature) implemented
ownership_costsas a separate feature fromdocuments, creating a broken data flow where costs and their source documents are disconnected.Current Architecture (Broken)
Problems:
Current Data Storage (Redundant)
documents.detailsJSONBownership_coststablevehiclestable (legacy)Proposed Solution: Document-Driven Cost Creation
Target Architecture
Key Decisions
Implementation Phases
Phase 1: Backend Connection
Phase 2: Frontend Consolidation
Phase 3: Legacy Cleanup
Note: Document cost scanning (OCR/AI) is OUT OF SCOPE - separate issue
Affected Areas
Backend
backend/src/features/documents/domain/documents.service.ts- create ownership_cost on document createbackend/src/features/documents/domain/documents.types.ts- ensure cost fields in detailsbackend/src/features/ownership-costs/- add document linking, restrict UI typesbackend/src/features/vehicles/- remove legacy TCO fieldsFrontend
frontend/src/features/documents/components/DocumentForm.tsx- ensure cost fields visiblefrontend/src/features/ownership-costs/components/- restrict to tax/other typesfrontend/src/features/vehicles/pages/- update detail layoutfrontend/src/features/vehicles/components/TCODisplay.tsx- show document linksDatabase Migrations
Acceptance Criteria
Technical Analysis Required
Out of Scope (Separate Issues)
References
ownership_costs.document_id(created but unused)DocumentFormalready has Premium and Cost inputsPlan: Link Ownership Costs to Documents Feature
Phase: Planning | Agent: Planner | Status: AWAITING_REVIEW
Pre-Planning Analysis Results
Codebase Analysis Findings (CRITICAL)
Issue #29's assumptions are incorrect. This is NEW FEATURE creation, not refactoring:
ownership_coststable existsdocument_idFK exists (unused)VehiclesService.getTCO()existsEvidence: Full codebase exploration confirmed no
backend/src/features/ownership-costs/directory, noownership_coststable in any migration, nodocument_idFK in any schema.Decision Critic Verdict: REVISE
Key Insight: The original framing is backwards. Costs are primary entities that exist independently; documents are optional proof, not the source.
Revised Approach:
ownership_costsas standalone entities with OPTIONALdocument_idFKdocument_idis set (preserves audit trail)Implementation Plan
Milestone 1: Create ownership_costs Feature Capsule (Backend)
Scope: New feature with table, types, repository, service, controller, routes
Files to Create:
Database Schema:
Key Design Decisions:
document_idis OPTIONAL (NULL allowed) - costs exist independentlyAcceptance Criteria:
Milestone 2: DocumentsService Integration
Scope: Auto-create ownership_cost when insurance/registration document has cost data
Files to Modify:
backend/src/features/documents/domain/documents.service.tsbackend/src/features/documents/domain/documents.types.ts(optional - add cost field types)Integration Pattern (following maintenance.service.ts auto-link pattern):
Update Logic:
Acceptance Criteria:
Milestone 3: TCO Calculation Endpoint
Scope: Add endpoint to aggregate ownership costs for a vehicle
Files to Modify/Create:
backend/src/features/vehicles/domain/vehicles.service.ts- add getTCO methodbackend/src/features/vehicles/api/vehicles.controller.ts- add GET /vehicles/:id/tcoTCO Response Structure:
Acceptance Criteria:
Milestone 4: Frontend - OwnershipCostForm Component
Scope: Create form for standalone cost entry (all cost types)
Files to Create:
Form Fields:
Acceptance Criteria:
Milestone 5: Frontend - TCODisplay Component
Scope: Display TCO breakdown on vehicle detail page
Files to Modify/Create:
frontend/src/features/vehicles/components/TCODisplay.tsx(create)frontend/src/features/vehicles/pages/VehicleDetailPage.tsx(integrate)Display Features:
Acceptance Criteria:
Milestone 6: Integration Testing and Cleanup
Scope: End-to-end testing, cleanup, documentation
Tasks:
Acceptance Criteria:
Out of Scope (Confirmed)
Per original issue, these are OUT OF SCOPE:
Risk Mitigation
Verdict: AWAITING_REVIEW | Next: QR plan-completeness review
QR Plan Review: Consolidated Results
Phase: Plan-Review | Agent: Quality Reviewer | Status: PASS_WITH_CONCERNS
Review Summary
RULE 1 (HIGH): Critical Revisions Required
1. Add mapRow() Requirement (M1)
2. Add Amount Validation (M1)
3. Add Transaction Boundaries (M2)
4. Add Cost Type Mapping Validation (M2)
5. Add Mobile/Desktop Testing Protocol (M6)
RULE 2 (SHOULD_FIX): Recommended Additions
1. Milestone Dependency Graph
2. Expanded Risk Assessment
3. Period Field Semantics
Considered But Not Flagged
Verdict: PASS_WITH_CONCERNS | Next: Incorporate revisions, then proceed to implementation
Milestone Execution: Complete
Phase: Execution | Agent: Planner | Status: COMPLETE
Milestone Summary
81b1c3d7928b87f0deab81d95ebaImplementation Details
M1: Backend Feature Capsule
backend/src/features/ownership-costs/with complete structureCHECK (amount > 0)constraintmapRow()for case conversionM2: DocumentsService Integration
ownership_costwhen insurance/registration document has cost datamapDocumentTypeToCostType()validationextractCostAmount()for premium/cost field extractionsyncOwnershipCost()for document updatesM3: TCO Calculation (Pre-existing)
/api/vehicles/:id/tcoalready implementedTCOResponsewith breakdown by categoryM4: Frontend OwnershipCostForm
M5: TCODisplay (Pre-existing)
M6: Quality Checks
Files Changed
Created:
Modified:
backend/src/features/documents/domain/documents.service.ts(auto-create integration)Verdict: COMPLETE | Next: Create PR for review