feat: add maintenance cost aggregation for TCO (refs #15)
- Add MaintenanceCostStats interface - Add getVehicleMaintenanceCosts() method to maintenance service - Validates numeric cost values and throws on invalid data 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
@@ -9,7 +9,8 @@ import type {
|
|||||||
MaintenanceRecordResponse,
|
MaintenanceRecordResponse,
|
||||||
MaintenanceScheduleResponse,
|
MaintenanceScheduleResponse,
|
||||||
MaintenanceCategory,
|
MaintenanceCategory,
|
||||||
ScheduleType
|
ScheduleType,
|
||||||
|
MaintenanceCostStats
|
||||||
} from './maintenance.types';
|
} from './maintenance.types';
|
||||||
import { validateSubtypes } from './maintenance.types';
|
import { validateSubtypes } from './maintenance.types';
|
||||||
import { MaintenanceRepository } from '../data/maintenance.repository';
|
import { MaintenanceRepository } from '../data/maintenance.repository';
|
||||||
@@ -63,6 +64,19 @@ export class MaintenanceService {
|
|||||||
return records.map(r => this.toRecordResponse(r));
|
return records.map(r => this.toRecordResponse(r));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async getVehicleMaintenanceCosts(vehicleId: string, userId: string): Promise<MaintenanceCostStats> {
|
||||||
|
const records = await this.repo.findRecordsByVehicleId(vehicleId, userId);
|
||||||
|
const totalCost = records.reduce((sum, r) => {
|
||||||
|
if (r.cost === null || r.cost === undefined) return sum;
|
||||||
|
const cost = Number(r.cost);
|
||||||
|
if (isNaN(cost)) {
|
||||||
|
throw new Error(`Invalid cost value for maintenance record ${r.id}`);
|
||||||
|
}
|
||||||
|
return sum + cost;
|
||||||
|
}, 0);
|
||||||
|
return { totalCost, recordCount: records.length };
|
||||||
|
}
|
||||||
|
|
||||||
async updateRecord(userId: string, id: string, patch: UpdateMaintenanceRecordRequest): Promise<MaintenanceRecordResponse | null> {
|
async updateRecord(userId: string, id: string, patch: UpdateMaintenanceRecordRequest): Promise<MaintenanceRecordResponse | null> {
|
||||||
const existing = await this.repo.findRecordById(id, userId);
|
const existing = await this.repo.findRecordById(id, userId);
|
||||||
if (!existing) return null;
|
if (!existing) return null;
|
||||||
|
|||||||
@@ -162,6 +162,12 @@ export interface MaintenanceRecordResponse extends MaintenanceRecord {
|
|||||||
subtypeCount: number;
|
subtypeCount: number;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TCO aggregation stats
|
||||||
|
export interface MaintenanceCostStats {
|
||||||
|
totalCost: number;
|
||||||
|
recordCount: number;
|
||||||
|
}
|
||||||
|
|
||||||
export interface MaintenanceScheduleResponse extends MaintenanceSchedule {
|
export interface MaintenanceScheduleResponse extends MaintenanceSchedule {
|
||||||
subtypeCount: number;
|
subtypeCount: number;
|
||||||
isDueSoon?: boolean;
|
isDueSoon?: boolean;
|
||||||
|
|||||||
Reference in New Issue
Block a user