mirror of
https://github.com/Tria-plc/edr-platform.git
synced 2026-08-27 00:52:50 +00:00
fix
This commit is contained in:
@@ -1,5 +1,5 @@
|
||||
import { Injectable, Logger, NotFoundException } from '@nestjs/common';
|
||||
import { FindOptionsWhere, In } from 'typeorm';
|
||||
import { BadRequestException, Injectable, Logger, NotFoundException } from '@nestjs/common';
|
||||
import { FindOptionsWhere, In, IsNull, Not } from 'typeorm';
|
||||
import { InjectDataSource } from '@nestjs/typeorm';
|
||||
import { DataSource } from 'typeorm';
|
||||
import { VehicleAvailability } from '../vehicles/entities/vehicle.entity';
|
||||
@@ -66,6 +66,19 @@ export class FirstMileService {
|
||||
}
|
||||
}
|
||||
|
||||
/** A leg counts as having a vehicle if it has a direct assignment or at least
|
||||
* one container allocation carrying a vehicle. Gates the IN_TRANSIT move. */
|
||||
private async hasAssignedVehicle(
|
||||
recordId: string,
|
||||
directVehicleId?: string | null,
|
||||
): Promise<boolean> {
|
||||
if (directVehicleId) return true;
|
||||
const count = await this.dataSource.manager.count(FirstMileContainerAllocation, {
|
||||
where: { firstMileId: recordId, vehicleId: Not(IsNull()) },
|
||||
});
|
||||
return count > 0;
|
||||
}
|
||||
|
||||
/** Human booking reference for a first-mile record, for the history timeline. */
|
||||
private async resolveBookingRef(record: FirstMile): Promise<string | null> {
|
||||
const loaded = (record as FirstMile & { booking?: { reference?: string } })
|
||||
@@ -297,6 +310,18 @@ export class FirstMileService {
|
||||
async update(id: string, dto: UpdateFirstMileDto): Promise<FirstMile> {
|
||||
const existing = await this.findById(id);
|
||||
|
||||
// A leg can only go IN_TRANSIT once a vehicle is assigned (allowing a vehicle
|
||||
// assigned in this same request).
|
||||
if (dto.status === 'IN_TRANSIT' && existing.status !== 'IN_TRANSIT') {
|
||||
const vehicleId =
|
||||
dto.vehicleId !== undefined ? dto.vehicleId : existing.vehicleId;
|
||||
if (!(await this.hasAssignedVehicle(id, vehicleId))) {
|
||||
throw new BadRequestException(
|
||||
'Assign a vehicle before marking this first-mile leg in transit',
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
const dtoAny = dto as any;
|
||||
const updated = await this.firstMileRepository.update(id, {
|
||||
...(dto.bookingId !== undefined ? { bookingId: dto.bookingId } : {}),
|
||||
@@ -396,6 +421,15 @@ export class FirstMileService {
|
||||
|
||||
async updateStatus(id: string, status: FirstMileStatus): Promise<FirstMile> {
|
||||
const existing = await this.findById(id);
|
||||
|
||||
if (status === 'IN_TRANSIT' && existing.status !== 'IN_TRANSIT') {
|
||||
if (!(await this.hasAssignedVehicle(id, existing.vehicleId))) {
|
||||
throw new BadRequestException(
|
||||
'Assign a vehicle before marking this first-mile leg in transit',
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
const updated = await this.firstMileRepository.update(id, { status });
|
||||
|
||||
if (!updated) {
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { Injectable, Logger, NotFoundException } from '@nestjs/common';
|
||||
import { DataSource, FindOptionsWhere, In } from 'typeorm';
|
||||
import { BadRequestException, Injectable, Logger, NotFoundException } from '@nestjs/common';
|
||||
import { DataSource, FindOptionsWhere, In, IsNull, Not } from 'typeorm';
|
||||
|
||||
import { BookingsRepository } from '../bookings/bookings.repository';
|
||||
import { DriversService } from '../drivers/drivers.service';
|
||||
@@ -65,6 +65,19 @@ export class LastMileService {
|
||||
}
|
||||
}
|
||||
|
||||
/** A leg counts as having a vehicle if it has a direct assignment or at least
|
||||
* one container allocation carrying a vehicle. Gates the IN_TRANSIT move. */
|
||||
private async hasAssignedVehicle(
|
||||
recordId: string,
|
||||
directVehicleId?: string | null,
|
||||
): Promise<boolean> {
|
||||
if (directVehicleId) return true;
|
||||
const count = await this.dataSource.manager.count(LastMileContainerAllocation, {
|
||||
where: { lastMileId: recordId, vehicleId: Not(IsNull()) },
|
||||
});
|
||||
return count > 0;
|
||||
}
|
||||
|
||||
/** Human booking reference for a last-mile record, for the history timeline.
|
||||
* Uses the already-loaded relation when present, else looks it up. */
|
||||
private async resolveBookingRef(
|
||||
@@ -218,6 +231,18 @@ export class LastMileService {
|
||||
async update(id: string, dto: UpdateLastMileDto): Promise<LastMile> {
|
||||
const existing = await this.findById(id);
|
||||
|
||||
// A leg can only go IN_TRANSIT once a vehicle is assigned (allowing a vehicle
|
||||
// assigned in this same request).
|
||||
if (dto.status === 'IN_TRANSIT' && existing.status !== 'IN_TRANSIT') {
|
||||
const vehicleId =
|
||||
dto.vehicleId !== undefined ? dto.vehicleId : existing.vehicleId;
|
||||
if (!(await this.hasAssignedVehicle(id, vehicleId))) {
|
||||
throw new BadRequestException(
|
||||
'Assign a vehicle before marking this last-mile leg in transit',
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
const dtoAny = dto as any;
|
||||
const updated = await this.lastMileRepository.update(id, {
|
||||
...(dto.bookingId !== undefined ? { bookingId: dto.bookingId } : {}),
|
||||
|
||||
@@ -879,10 +879,14 @@ const FirstMilePage = () => {
|
||||
<Menu.Dropdown>
|
||||
<Menu.Item
|
||||
leftSection={<ArrowRight size={15} />}
|
||||
disabled={!nextStatus}
|
||||
disabled={!nextStatus || (nextStatus === "IN_TRANSIT" && !assigned)}
|
||||
onClick={() => handleAdvanceStatus(row.original)}
|
||||
>
|
||||
{nextStatus ? `Mark ${STATUS_META[nextStatus].label}` : STATUS_META[row.original.status].label}
|
||||
{nextStatus === "IN_TRANSIT" && !assigned
|
||||
? "Assign a vehicle first"
|
||||
: nextStatus
|
||||
? `Mark ${STATUS_META[nextStatus].label}`
|
||||
: STATUS_META[row.original.status].label}
|
||||
</Menu.Item>
|
||||
<Menu.Divider />
|
||||
<Menu.Item
|
||||
|
||||
@@ -967,10 +967,14 @@ const LastMilePage = () => {
|
||||
<Menu.Dropdown>
|
||||
<Menu.Item
|
||||
leftSection={<ArrowRight size={15} />}
|
||||
disabled={!nextStatus}
|
||||
disabled={!nextStatus || (nextStatus === "IN_TRANSIT" && !assigned)}
|
||||
onClick={() => handleAdvanceStatus(row.original)}
|
||||
>
|
||||
{nextStatus ? `Mark ${STATUS_META[nextStatus].label}` : STATUS_META[row.original.status].label}
|
||||
{nextStatus === "IN_TRANSIT" && !assigned
|
||||
? "Assign a vehicle first"
|
||||
: nextStatus
|
||||
? `Mark ${STATUS_META[nextStatus].label}`
|
||||
: STATUS_META[row.original.status].label}
|
||||
</Menu.Item>
|
||||
<Menu.Divider />
|
||||
<Menu.Item
|
||||
|
||||
Reference in New Issue
Block a user