Merge branch 'freight/hot_fix' of github.com:Tria-plc/edr-platform into freight/hot_fix

This commit is contained in:
yaschalew
2026-06-30 15:29:54 +03:00
5 changed files with 247 additions and 2 deletions

View File

@@ -21,8 +21,8 @@ export class FuelConsumption extends BaseEntity {
@Column({ name: 'total_cost', type: 'numeric', precision: 14, scale: 2 })
totalCost!: number;
@Column({ name: 'total_distance_km', type: 'numeric', precision: 10, scale: 2 })
totalDistanceKm!: number;
@Column({ name: 'total_distance_km', type: 'numeric', precision: 10, scale: 2, default: 0 })
totalDistanceKm: number = 0;
@Column({ name: 'fuel_efficiency_km_per_l', type: 'numeric', precision: 10, scale: 2, nullable: true })
fuelEfficiencyKmPerL?: number;

View File

@@ -0,0 +1,87 @@
import { IsUUID, IsString, IsDateString, IsNumber, IsOptional, IsEnum } from 'class-validator';
import { MaintenanceType, MaintenanceStatus } from '../entities/maintenance-schedule.entity';
export class CreateMaintenanceScheduleDto {
@IsUUID()
vehicleId!: string;
@IsEnum(MaintenanceType)
maintenanceType!: MaintenanceType;
@IsString()
description!: string;
@IsDateString()
scheduledDate!: string;
@IsOptional()
@IsNumber()
estimatedCost?: number;
@IsOptional()
@IsString()
serviceProvider?: string;
@IsOptional()
@IsString()
notes?: string;
@IsOptional()
@IsNumber()
nextDueKm?: number;
@IsOptional()
@IsDateString()
nextDueDate?: string;
}
export class CreateMaintenanceCostDto {
@IsUUID()
vehicleId!: string;
@IsOptional()
@IsUUID()
maintenanceScheduleId?: string;
@IsDateString()
incurredDate!: string;
@IsNumber()
costAmount!: number;
@IsString()
costType!: string;
@IsString()
description!: string;
@IsOptional()
@IsString()
serviceProvider?: string;
@IsOptional()
@IsString()
invoiceNumber?: string;
@IsOptional()
@IsString()
notes?: string;
}
export class UpdateMaintenanceScheduleDto {
@IsOptional()
@IsEnum(MaintenanceStatus)
status?: MaintenanceStatus;
@IsOptional()
@IsDateString()
completedDate?: string;
@IsOptional()
@IsNumber()
actualCost?: number;
@IsOptional()
@IsString()
notes?: string;
}

View File

@@ -0,0 +1,43 @@
import { BaseEntity } from '@edr/api-common';
import { Entity, Column, ManyToOne, JoinColumn, Index } from 'typeorm';
import { Vehicle } from '../../vehicles/entities/vehicle.entity';
import { MaintenanceSchedule } from './maintenance-schedule.entity';
@Entity({ name: 'maintenance_costs', schema: 'freight' })
@Index(['vehicleId', 'incurredDate'])
export class MaintenanceCost extends BaseEntity {
@Column({ name: 'vehicle_id', type: 'uuid' })
vehicleId!: string;
@ManyToOne(() => Vehicle, { eager: false })
@JoinColumn({ name: 'vehicle_id' })
vehicle!: Vehicle;
@Column({ name: 'maintenance_schedule_id', type: 'uuid', nullable: true })
maintenanceScheduleId?: string;
@ManyToOne(() => MaintenanceSchedule, { eager: false, onDelete: 'SET NULL' })
@JoinColumn({ name: 'maintenance_schedule_id' })
maintenanceSchedule?: MaintenanceSchedule;
@Column({ name: 'incurred_date', type: 'timestamptz' })
incurredDate!: Date;
@Column({ name: 'cost_amount', type: 'numeric', precision: 14, scale: 2 })
costAmount!: number;
@Column({ name: 'cost_type' })
costType!: string; // 'PARTS', 'LABOR', 'DIAGNOSTICS', 'OTHER'
@Column({ name: 'description' })
description!: string;
@Column({ name: 'service_provider', nullable: true })
serviceProvider?: string;
@Column({ name: 'invoice_number', nullable: true })
invoiceNumber?: string;
@Column({ type: 'text', nullable: true })
notes?: string;
}

View File

@@ -0,0 +1,65 @@
import { BaseEntity } from '@edr/api-common';
import { Entity, Column, ManyToOne, JoinColumn, Index } from 'typeorm';
import { Vehicle } from '../../vehicles/entities/vehicle.entity';
export enum MaintenanceType {
PREVENTIVE = 'PREVENTIVE',
CORRECTIVE = 'CORRECTIVE',
INSPECTION = 'INSPECTION',
REPAIR = 'REPAIR',
}
export enum MaintenanceStatus {
SCHEDULED = 'SCHEDULED',
IN_PROGRESS = 'IN_PROGRESS',
COMPLETED = 'COMPLETED',
CANCELLED = 'CANCELLED',
OVERDUE = 'OVERDUE',
}
@Entity({ name: 'maintenance_schedules', schema: 'freight' })
@Index(['vehicleId', 'scheduledDate'])
export class MaintenanceSchedule extends BaseEntity {
@Column({ name: 'vehicle_id', type: 'uuid' })
vehicleId!: string;
@ManyToOne(() => Vehicle, { eager: false })
@JoinColumn({ name: 'vehicle_id' })
vehicle!: Vehicle;
@Column({ name: 'maintenance_type', type: 'varchar' })
maintenanceType!: MaintenanceType;
@Column({ name: 'description' })
description!: string;
@Column({ name: 'scheduled_date', type: 'timestamptz' })
scheduledDate!: Date;
@Column({ name: 'completed_date', type: 'timestamptz', nullable: true })
completedDate?: Date;
@Column({ name: 'estimated_cost', type: 'numeric', precision: 14, scale: 2, nullable: true })
estimatedCost?: number;
@Column({ name: 'actual_cost', type: 'numeric', precision: 14, scale: 2, nullable: true })
actualCost?: number;
@Column({ name: 'status', type: 'varchar', default: MaintenanceStatus.SCHEDULED })
status!: MaintenanceStatus;
@Column({ name: 'odometer_reading', type: 'numeric', nullable: true })
odometerReading?: number;
@Column({ name: 'service_provider', nullable: true })
serviceProvider?: string;
@Column({ name: 'notes', type: 'text', nullable: true })
notes?: string;
@Column({ name: 'next_due_km', type: 'numeric', nullable: true })
nextDueKm?: number;
@Column({ name: 'next_due_date', type: 'timestamptz', nullable: true })
nextDueDate?: Date;
}

View File

@@ -0,0 +1,50 @@
import { Injectable } from '@nestjs/common';
import { InjectRepository } from '@nestjs/typeorm';
import { BaseRepository } from '@edr/api-common';
import { Repository, Between } from 'typeorm';
import { MaintenanceSchedule, MaintenanceStatus } from './entities/maintenance-schedule.entity';
import { MaintenanceCost } from './entities/maintenance-cost.entity';
@Injectable()
export class MaintenanceRepository extends BaseRepository<MaintenanceSchedule> {
constructor(
@InjectRepository(MaintenanceSchedule)
private readonly scheduleRepository: Repository<MaintenanceSchedule>,
@InjectRepository(MaintenanceCost)
private readonly costRepository: Repository<MaintenanceCost>,
) {
super(scheduleRepository);
}
async getUpcomingMaintenance(vehicleId: string, daysAhead: number = 30) {
const futureDate = new Date(Date.now() + daysAhead * 24 * 60 * 60 * 1000);
return this.scheduleRepository.find({
where: {
vehicleId,
scheduledDate: Between(new Date(), futureDate),
status: MaintenanceStatus.SCHEDULED,
},
order: { scheduledDate: 'ASC' },
});
}
async getMaintenanceCosts(vehicleId: string, startDate: Date, endDate: Date) {
return this.costRepository.find({
where: {
vehicleId,
incurredDate: Between(startDate, endDate),
},
order: { incurredDate: 'DESC' },
});
}
async getTotalMaintenanceCost(vehicleId: string, startDate: Date, endDate: Date) {
const result = await this.costRepository
.createQueryBuilder()
.select('SUM(cost_amount)', 'total')
.where('vehicle_id = :vehicleId', { vehicleId })
.andWhere('incurred_date BETWEEN :startDate AND :endDate', { startDate, endDate })
.getRawOne();
return result?.total || 0;
}
}