Truck assiggnment and per truc

This commit is contained in:
hagiye
2026-09-02 01:41:08 +03:00
parent 6d6b32feae
commit 031efce92b
12 changed files with 769 additions and 97 deletions

View File

@@ -312,12 +312,27 @@ export class BookingsService {
LEFT JOIN freight.yards ay ON ay.id = tsw.alight_yard_id
LEFT JOIN freight.wagon_allocation_container_items ci
ON ci.wagon_booking_allocation_id = a.id AND ci.deleted_at IS NULL
AND (
$2 <> 'EXPORT' OR $3 <> 'CONTAINER' OR EXISTS (
SELECT 1
FROM freight.booking_container_units received_unit
JOIN freight.booking_container received_line
ON received_line.id = received_unit.booking_container_id
AND received_line.deleted_at IS NULL
WHERE received_line.booking_id = a.booking_id
AND received_unit.container_number = ci.container_number
AND received_unit.received_to_port = true
AND NULLIF(TRIM(received_unit.grn_number), '') IS NOT NULL
AND received_unit.deleted_at IS NULL
)
)
WHERE a.booking_id = $1 AND a.deleted_at IS NULL
GROUP BY tsw.id, a.id, a.status, wt.code, wt.name, w.wagon_number, wt.tare_weight_tons,
s.train_number, s.scheduled_departure_date, so.label, sd.label,
by_.label, ay.label
HAVING $2 <> 'EXPORT' OR $3 <> 'CONTAINER' OR COUNT(ci.id) > 0
ORDER BY tsw.sequence_no`,
[bookingId],
[bookingId, booking.tradeDirection, booking.freightType],
);
// Export acceptance happens at the warehouse gate, not at marshalling: EDR
// takes custody of the cargo when it receives it, and the customer is handed
@@ -352,17 +367,17 @@ export class BookingsService {
)
: booking.tradeDirection === 'EXPORT'
? await this.dataSource.query(
`SELECT inv.weight AS "allocatedWeightTons",
c.container_number AS "containerNumbers"
FROM freight.warehouse_inventory inv
LEFT JOIN freight.containers c
ON c.id = inv.container_id AND c.deleted_at IS NULL
WHERE inv.booking_id = $1 AND inv.deleted_at IS NULL
AND COALESCE(
NULLIF(TRIM(inv.grn_number), ''),
substring(inv.notes FROM 'GRN Number: ([^\\n\\r]+)')
) IS NOT NULL
ORDER BY inv.created_at`,
`SELECT unit.vgm_tons AS "allocatedWeightTons",
unit.container_number AS "containerNumbers",
unit.seal_number AS "sealNumbers"
FROM freight.booking_container_units unit
JOIN freight.booking_container line
ON line.id = unit.booking_container_id AND line.deleted_at IS NULL
WHERE line.booking_id = $1
AND unit.deleted_at IS NULL
AND unit.received_to_port = true
AND NULLIF(TRIM(unit.grn_number), '') IS NOT NULL
ORDER BY unit.received_at, unit.container_number`,
[bookingId],
)
: [];

View File

@@ -35,6 +35,7 @@ interface BookingGuardRow {
lastMile: string | null;
paymentStatus: string | null;
status: string | null;
trainScheduleStatus: string | null;
}
/**
@@ -294,19 +295,16 @@ export class CustomerTruckService {
}
const requested = (dto.containerNumbers ?? []).map((n) => n.trim().toUpperCase());
if (booking.freightType === 'CONTAINER' && !requested.length) {
throw new BadRequestException('Select the containers loaded on this truck');
}
if (requested.length) {
const bookingNumbers = await this.bookingContainerNumbers(bookingId);
for (const n of requested) {
if (!bookingNumbers.includes(n)) {
throw new BadRequestException(`Container ${n} is not one of this booking's containers`);
}
}
const elsewhere = await this.assignedContainerNumbersExcept(bookingId, assignmentId);
for (const n of requested) {
if (elsewhere.includes(n)) {
throw new ConflictException(`Container ${n} is already loaded onto another truck`);
}
}
assertTruckLoad({
containers: requested,
bookingContainers: await this.bookingContainerNumbers(bookingId),
sizes: await bookingContainerSizes(this.dataSource, bookingId, requested),
assignedElsewhere: await this.assignedContainerNumbersExcept(bookingId, assignmentId),
});
}
await this.dataSource.transaction(async (manager) => {
@@ -542,9 +540,16 @@ export class CustomerTruckService {
first_mile_pickup_address AS "firstMile",
last_mile_delivery_address AS "lastMile",
payment_status AS "paymentStatus",
status
FROM freight.bookings
WHERE id = $1 AND deleted_at IS NULL`,
b.status,
(SELECT ts.status
FROM freight.train_schedule_bookings tsb
JOIN freight.train_schedules ts
ON ts.id = tsb.train_schedule_id AND ts.deleted_at IS NULL
WHERE tsb.booking_id = b.id AND tsb.deleted_at IS NULL
ORDER BY ts.updated_at DESC
LIMIT 1) AS "trainScheduleStatus"
FROM freight.bookings b
WHERE b.id = $1 AND b.deleted_at IS NULL`,
[bookingId],
);
if (!row) throw new NotFoundException(`Booking ${bookingId} not found`);
@@ -575,7 +580,7 @@ export class CustomerTruckService {
private assertAssignmentWindow(booking: BookingGuardRow): void {
const status = booking.status ?? '';
if (booking.tradeDirection === 'IMPORT') {
if (status !== 'ARRIVED') {
if (status !== 'ARRIVED' && booking.trainScheduleStatus !== 'ARRIVED') {
throw new BadRequestException(
'Import pickup trucks can only be assigned after the train has arrived',
);