This commit is contained in:
natib21
2026-07-03 15:21:23 +00:00
parent e881e8de84
commit 0e6ebda6f6
15 changed files with 661 additions and 7 deletions

View File

@@ -12,6 +12,8 @@ import { LastMileContainerAllocation } from './entities/last-mile-container-allo
import { LastMileRepository } from './last-mile.repository';
import { InvoiceEventPayload } from '../billing/billing.service';
import { OnEvent } from '@nestjs/event-emitter';
import { FleetHistoryService } from '../fleet-history/fleet-history.service';
import { FleetEventType } from '../fleet-history/entities/fleet-event.entity';
type LastMileListFilter = {
status?: LastMileStatus;
@@ -41,8 +43,21 @@ export class LastMileService {
private readonly driversService: DriversService,
private readonly smsClient: SmsClientService,
private readonly dataSource: DataSource,
private readonly history: FleetHistoryService,
) {}
/** Resolve the driver currently assigned to a vehicle, for stamping mile
* events onto that driver's timeline. Best-effort — never throws. */
private async resolveDriverId(vehicleId?: string | null): Promise<string | null> {
if (!vehicleId) return null;
try {
const vehicle = await this.vehiclesService.findById(vehicleId);
return vehicle.assignedDriverId ?? null;
} catch {
return null;
}
}
async acceptBooking(bookingReference: string): Promise<LastMile | null> {
const booking = await this.bookingsRepository.findByReference(bookingReference);
@@ -132,7 +147,7 @@ export class LastMileService {
}
async create(dto: CreateLastMileDto): Promise<LastMile> {
return this.lastMileRepository.create({
const record = await this.lastMileRepository.create({
bookingId: dto.bookingId,
status: dto.status ?? 'READY_TO_TRANSIT',
advancedPayment: dto.advancedPayment ?? 0,
@@ -142,6 +157,19 @@ export class LastMileService {
vehicleId: dto.vehicleId ?? null,
paid: (dto as any).paid ?? false,
});
if (dto.vehicleId) {
await this.history.record({
eventType: FleetEventType.MILE_VEHICLE_ASSIGNED,
vehicleId: dto.vehicleId,
lastMileId: record.id,
driverId: await this.resolveDriverId(dto.vehicleId),
label: record.status,
metadata: { mile: 'LAST' },
});
}
return record;
}
@OnEvent("lastmile.invoice.paid")
@@ -180,6 +208,42 @@ export class LastMileService {
void this.notifyDriverAssignment(dto.vehicleId, existing);
}
// Audit the mile↔vehicle (re)assignment on both vehicle and driver lines.
if (dto.vehicleId !== undefined && dto.vehicleId !== existing.vehicleId) {
if (existing.vehicleId) {
await this.history.record({
eventType: FleetEventType.MILE_VEHICLE_RELEASED,
vehicleId: existing.vehicleId,
lastMileId: id,
driverId: await this.resolveDriverId(existing.vehicleId),
metadata: { mile: 'LAST' },
});
}
if (dto.vehicleId) {
await this.history.record({
eventType: FleetEventType.MILE_VEHICLE_ASSIGNED,
vehicleId: dto.vehicleId,
lastMileId: id,
driverId: await this.resolveDriverId(dto.vehicleId),
label: updated.status,
metadata: { mile: 'LAST' },
});
}
}
if (dto.status !== undefined && dto.status !== existing.status) {
const vehicleId = updated.vehicleId ?? existing.vehicleId ?? null;
await this.history.record({
eventType: FleetEventType.MILE_STATUS_CHANGED,
lastMileId: id,
vehicleId,
driverId: await this.resolveDriverId(vehicleId),
fromValue: existing.status,
toValue: dto.status,
metadata: { mile: 'LAST' },
});
}
return updated;
}