mirror of
https://github.com/Tria-plc/edr-platform.git
synced 2026-08-28 07:51:02 +00:00
feat: complete maintenance tracking backend
Service/Controller/Module: - scheduleMaintenanceAsync: schedule work - recordMaintenanceCost: log expenses - getUpcomingMaintenance: due items - getVehicleMaintenanceStats: cost aggregation Endpoints: - POST /maintenance/schedules - POST /maintenance/costs - PATCH /maintenance/schedules/:id - GET /maintenance/upcoming/:vehicleId - GET /maintenance/history/:vehicleId - GET /maintenance/stats/:vehicleId Migration: idempotent maintenance tables creation Co-Authored-By: Claude Haiku 4.5 <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,46 @@
|
||||
import { Controller, Post, Get, Patch, Body, Param } from '@nestjs/common';
|
||||
import { ApiTags, ApiOperation } from '@nestjs/swagger';
|
||||
import { MaintenanceService } from './maintenance.service';
|
||||
import { CreateMaintenanceScheduleDto, CreateMaintenanceCostDto, UpdateMaintenanceScheduleDto } from './dto/create-maintenance.dto';
|
||||
|
||||
@ApiTags('Maintenance Management')
|
||||
@Controller('maintenance')
|
||||
export class MaintenanceController {
|
||||
constructor(private readonly maintenanceService: MaintenanceService) {}
|
||||
|
||||
@Post('schedules')
|
||||
@ApiOperation({ summary: 'Schedule maintenance' })
|
||||
async scheduleMaintenanceAsync(@Body() dto: CreateMaintenanceScheduleDto) {
|
||||
return this.maintenanceService.scheduleMaintenanceAsync(dto);
|
||||
}
|
||||
|
||||
@Post('costs')
|
||||
@ApiOperation({ summary: 'Record maintenance cost' })
|
||||
async recordCost(@Body() dto: CreateMaintenanceCostDto) {
|
||||
return this.maintenanceService.recordMaintenanceCost(dto);
|
||||
}
|
||||
|
||||
@Patch('schedules/:id')
|
||||
@ApiOperation({ summary: 'Update maintenance schedule' })
|
||||
async updateSchedule(@Param('id') id: string, @Body() dto: UpdateMaintenanceScheduleDto) {
|
||||
return this.maintenanceService.updateMaintenanceSchedule(id, dto);
|
||||
}
|
||||
|
||||
@Get('upcoming/:vehicleId')
|
||||
@ApiOperation({ summary: 'Get upcoming maintenance' })
|
||||
async getUpcoming(@Param('vehicleId') vehicleId: string) {
|
||||
return this.maintenanceService.getUpcomingMaintenance(vehicleId);
|
||||
}
|
||||
|
||||
@Get('history/:vehicleId')
|
||||
@ApiOperation({ summary: 'Get maintenance history' })
|
||||
async getHistory(@Param('vehicleId') vehicleId: string) {
|
||||
return this.maintenanceService.getMaintenanceHistory(vehicleId);
|
||||
}
|
||||
|
||||
@Get('stats/:vehicleId')
|
||||
@ApiOperation({ summary: 'Get maintenance statistics' })
|
||||
async getStats(@Param('vehicleId') vehicleId: string) {
|
||||
return this.maintenanceService.getVehicleMaintenanceStats(vehicleId);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,15 @@
|
||||
import { Module } from '@nestjs/common';
|
||||
import { TypeOrmModule } from '@nestjs/typeorm';
|
||||
import { MaintenanceSchedule } from './entities/maintenance-schedule.entity';
|
||||
import { MaintenanceCost } from './entities/maintenance-cost.entity';
|
||||
import { MaintenanceService } from './maintenance.service';
|
||||
import { MaintenanceRepository } from './maintenance.repository';
|
||||
import { MaintenanceController } from './maintenance.controller';
|
||||
|
||||
@Module({
|
||||
imports: [TypeOrmModule.forFeature([MaintenanceSchedule, MaintenanceCost])],
|
||||
providers: [MaintenanceService, MaintenanceRepository],
|
||||
controllers: [MaintenanceController],
|
||||
exports: [MaintenanceService],
|
||||
})
|
||||
export class MaintenanceModule {}
|
||||
@@ -0,0 +1,82 @@
|
||||
import { Injectable } from '@nestjs/common';
|
||||
import { InjectRepository } from '@nestjs/typeorm';
|
||||
import { Repository } from 'typeorm';
|
||||
import { MaintenanceRepository } from './maintenance.repository';
|
||||
import { MaintenanceSchedule } from './entities/maintenance-schedule.entity';
|
||||
import { MaintenanceCost } from './entities/maintenance-cost.entity';
|
||||
import { CreateMaintenanceScheduleDto, CreateMaintenanceCostDto, UpdateMaintenanceScheduleDto } from './dto/create-maintenance.dto';
|
||||
|
||||
@Injectable()
|
||||
export class MaintenanceService {
|
||||
constructor(
|
||||
private readonly maintenanceRepository: MaintenanceRepository,
|
||||
@InjectRepository(MaintenanceSchedule)
|
||||
private readonly scheduleRepository: Repository<MaintenanceSchedule>,
|
||||
@InjectRepository(MaintenanceCost)
|
||||
private readonly costRepository: Repository<MaintenanceCost>,
|
||||
) {}
|
||||
|
||||
async scheduleMaintenanceAsync(dto: CreateMaintenanceScheduleDto): Promise<MaintenanceSchedule> {
|
||||
const schedule = this.scheduleRepository.create({
|
||||
...dto,
|
||||
scheduledDate: new Date(dto.scheduledDate),
|
||||
nextDueDate: dto.nextDueDate ? new Date(dto.nextDueDate) : undefined,
|
||||
});
|
||||
return this.scheduleRepository.save(schedule);
|
||||
}
|
||||
|
||||
async recordMaintenanceCost(dto: CreateMaintenanceCostDto): Promise<MaintenanceCost> {
|
||||
const cost = this.costRepository.create({
|
||||
...dto,
|
||||
incurredDate: new Date(dto.incurredDate),
|
||||
});
|
||||
return this.costRepository.save(cost);
|
||||
}
|
||||
|
||||
async updateMaintenanceSchedule(
|
||||
id: string,
|
||||
dto: UpdateMaintenanceScheduleDto,
|
||||
): Promise<MaintenanceSchedule> {
|
||||
await this.scheduleRepository.update(id, {
|
||||
...dto,
|
||||
completedDate: dto.completedDate ? new Date(dto.completedDate) : undefined,
|
||||
});
|
||||
const updated = await this.scheduleRepository.findOneBy({ id });
|
||||
return updated!;
|
||||
}
|
||||
|
||||
async getUpcomingMaintenance(vehicleId: string) {
|
||||
return this.maintenanceRepository.getUpcomingMaintenance(vehicleId);
|
||||
}
|
||||
|
||||
async getMaintenanceHistory(vehicleId: string, monthsBack: number = 12) {
|
||||
const endDate = new Date();
|
||||
const startDate = new Date(endDate.getFullYear(), endDate.getMonth() - monthsBack, 1);
|
||||
return this.maintenanceRepository.getMaintenanceCosts(vehicleId, startDate, endDate);
|
||||
}
|
||||
|
||||
async getVehicleMaintenanceStats(vehicleId: string, monthsBack: number = 12) {
|
||||
const endDate = new Date();
|
||||
const startDate = new Date(endDate.getFullYear(), endDate.getMonth() - monthsBack, 1);
|
||||
|
||||
const costs = await this.maintenanceRepository.getMaintenanceCosts(vehicleId, startDate, endDate);
|
||||
const totalCost = costs.reduce((sum: number, c: MaintenanceCost) => sum + Number(c.costAmount), 0);
|
||||
|
||||
return {
|
||||
vehicleId,
|
||||
totalCost,
|
||||
numberOfMaintenanceItems: costs.length,
|
||||
averageCostPerMaintenance: costs.length > 0 ? totalCost / costs.length : 0,
|
||||
costByType: this.groupCostsByType(costs),
|
||||
};
|
||||
}
|
||||
|
||||
private groupCostsByType(costs: MaintenanceCost[]) {
|
||||
const grouped: Record<string, number> = {};
|
||||
costs.forEach((c) => {
|
||||
if (!grouped[c.costType]) grouped[c.costType] = 0;
|
||||
grouped[c.costType] += Number(c.costAmount);
|
||||
});
|
||||
return grouped;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user