feat: add ownership-costs feature capsule (refs #15)
- Create ownership_costs table for recurring vehicle costs - Add backend feature capsule with types, repository, service, routes - Update TCO calculation to use ownership_costs (with fallback to legacy vehicle fields) - Add taxCosts and otherCosts to TCO response - Create frontend ownership-costs feature with form, list, API, hooks - Update TCODisplay to show all cost types This implements a more flexible approach to tracking recurring ownership costs (insurance, registration, tax, other) with explicit date ranges and optional document association. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
@@ -28,6 +28,7 @@ import { NHTSAClient, NHTSADecodeResponse, DecodedVehicleData, MatchedField } fr
|
||||
import { canAddVehicle, getVehicleLimitConfig } from '../../../core/config/feature-tiers';
|
||||
import { UserProfileRepository } from '../../user-profile/data/user-profile.repository';
|
||||
import { SubscriptionTier } from '../../user-profile/domain/user-profile.types';
|
||||
import { OwnershipCostsService } from '../../ownership-costs';
|
||||
import { FuelLogsService } from '../../fuel-logs/domain/fuel-logs.service';
|
||||
import { FuelLogsRepository } from '../../fuel-logs/data/fuel-logs.repository';
|
||||
import { MaintenanceService } from '../../maintenance/domain/maintenance.service';
|
||||
@@ -430,20 +431,35 @@ export class VehiclesService {
|
||||
// Get fixed costs from vehicle record
|
||||
const purchasePrice = vehicle.purchasePrice || 0;
|
||||
|
||||
// Normalize recurring costs based on purchase date
|
||||
const insuranceCosts = this.normalizeRecurringCost(
|
||||
vehicle.insuranceCost,
|
||||
vehicle.insuranceInterval,
|
||||
vehicle.purchaseDate
|
||||
);
|
||||
const registrationCosts = this.normalizeRecurringCost(
|
||||
vehicle.registrationCost,
|
||||
vehicle.registrationInterval,
|
||||
vehicle.purchaseDate
|
||||
);
|
||||
// Get recurring ownership costs from ownership-costs service
|
||||
const ownershipCostsService = new OwnershipCostsService(this.pool);
|
||||
let insuranceCosts = 0;
|
||||
let registrationCosts = 0;
|
||||
let taxCosts = 0;
|
||||
let otherCosts = 0;
|
||||
try {
|
||||
const ownershipStats = await ownershipCostsService.getVehicleCostStats(id, userId);
|
||||
insuranceCosts = ownershipStats.insuranceCosts || 0;
|
||||
registrationCosts = ownershipStats.registrationCosts || 0;
|
||||
taxCosts = ownershipStats.taxCosts || 0;
|
||||
otherCosts = ownershipStats.otherCosts || 0;
|
||||
} catch {
|
||||
// Vehicle may have no ownership cost records
|
||||
// Fall back to legacy vehicle fields if they exist
|
||||
insuranceCosts = this.normalizeRecurringCost(
|
||||
vehicle.insuranceCost,
|
||||
vehicle.insuranceInterval,
|
||||
vehicle.purchaseDate
|
||||
);
|
||||
registrationCosts = this.normalizeRecurringCost(
|
||||
vehicle.registrationCost,
|
||||
vehicle.registrationInterval,
|
||||
vehicle.purchaseDate
|
||||
);
|
||||
}
|
||||
|
||||
// Calculate lifetime total
|
||||
const lifetimeTotal = purchasePrice + insuranceCosts + registrationCosts + fuelCosts + maintenanceCosts;
|
||||
// Calculate lifetime total (includes all ownership costs: insurance, registration, tax, other)
|
||||
const lifetimeTotal = purchasePrice + insuranceCosts + registrationCosts + taxCosts + otherCosts + fuelCosts + maintenanceCosts;
|
||||
|
||||
// Calculate cost per distance
|
||||
const odometerReading = vehicle.odometerReading || 0;
|
||||
@@ -454,6 +470,8 @@ export class VehiclesService {
|
||||
purchasePrice,
|
||||
insuranceCosts,
|
||||
registrationCosts,
|
||||
taxCosts,
|
||||
otherCosts,
|
||||
fuelCosts,
|
||||
maintenanceCosts,
|
||||
lifetimeTotal,
|
||||
|
||||
@@ -201,6 +201,8 @@ export interface TCOResponse {
|
||||
purchasePrice: number;
|
||||
insuranceCosts: number;
|
||||
registrationCosts: number;
|
||||
taxCosts: number;
|
||||
otherCosts: number;
|
||||
fuelCosts: number;
|
||||
maintenanceCosts: number;
|
||||
lifetimeTotal: number;
|
||||
|
||||
Reference in New Issue
Block a user