Merge pull request #1152 from Tria-plc/freight_feature/usermanagement

fix wagon cancellation
This commit is contained in:
marshal
2026-08-07 10:19:57 +03:00
committed by GitHub
5 changed files with 74 additions and 2 deletions

View File

@@ -8,6 +8,9 @@ import {
Optional,
} from "@nestjs/common";
import { EventEmitter2, OnEvent } from "@nestjs/event-emitter";
import { DataSource } from "typeorm";
import { TrainSchedule } from '../train-schedules/entities/train-schedule.entity';
import {
BookingBatchService,
@@ -65,6 +68,9 @@ export class BookingTransitionService {
private readonly notifier: BookingLifecycleNotifierService,
private readonly events: EventEmitter2,
@Optional() private readonly milestoneService?: ClearanceMilestoneService,
// Optional + last so the hand-constructed service in *.spec.ts files keeps
// compiling; Nest injects it normally at runtime.
@Optional() private readonly dataSource?: DataSource,
) {}
private isPhasedCustoms(booking: Booking): boolean {
@@ -1271,6 +1277,12 @@ export class BookingTransitionService {
/** Flat list of physical container numbers on this booking (for the
* customer truck-assignment container picker). */
containerNumbers: string[];
/** The allocated train, when the booking is placed on a schedule. */
trainSchedule?: {
trainNumber: string | null;
reference: string | null;
scheduledDepartureDate: Date | null;
} | null;
}
> {
// This enrichment runs AFTER the transition has committed. A failure here
@@ -1323,6 +1335,32 @@ export class BookingTransitionService {
`enrichBookingResponse: batch-offer lookup failed for ${booking.id}: ${(err as Error).message}`,
);
}
// Allocated train: number + schedule reference for the detail headers
// (portal and backoffice). Degrades to null like every fragile field here.
let trainSchedule: {
trainNumber: string | null;
reference: string | null;
scheduledDepartureDate: Date | null;
} | null = null;
if (booking.trainScheduleId && this.dataSource) {
try {
const s = await this.dataSource.getRepository(TrainSchedule).findOne({
where: { id: booking.trainScheduleId },
});
if (s) {
trainSchedule = {
trainNumber: s.trainNumber ?? null,
reference: s.reference ?? null,
scheduledDepartureDate: s.scheduledDepartureDate ?? null,
};
}
} catch (err) {
this.logger.warn(
`enrichBookingResponse: train-schedule lookup failed for ${booking.id}: ${(err as Error).message}`,
);
}
}
// Physical container numbers entered at booking time (booking_container
// units), flattened for the customer truck-assignment container picker.
const containerNumbers = (booking.bookingContainers ?? [])
@@ -1337,6 +1375,7 @@ export class BookingTransitionService {
nextStep,
activeBatchOffer,
containerNumbers,
trainSchedule,
};
}
}

View File

@@ -1,6 +1,6 @@
import type { LucideIcon } from "lucide-react";
import type { ReactNode } from "react";
import { Hash, Package, Ship, Weight, Clock } from "lucide-react";
import { Hash, Package, Ship, Weight, Clock, TrainFront } from "lucide-react";
import { Group, Stack, Text, Divider } from "@mantine/core";
import { cargoTonsAndItems } from "@/utils/cargoWeight";
@@ -50,6 +50,21 @@ export function BookingFactsCard({ booking }: BookingFactsCardProps) {
},
{ icon: Clock, label: "Last Updated", value: formatDate(booking.updatedAt) },
];
// Allocated train facts — only once the booking rides a schedule.
if (booking.trainSchedule?.trainNumber || booking.trainSchedule?.reference) {
facts.splice(1, 0, {
icon: TrainFront,
label: "Allocated Train",
value: [
booking.trainSchedule.trainNumber
? `Train ${booking.trainSchedule.trainNumber}`
: null,
booking.trainSchedule.reference ?? null,
]
.filter(Boolean)
.join(" · "),
});
}
return (
<SectionCard icon={Hash} title="Booking Details" accent="cyan">

View File

@@ -144,4 +144,10 @@ export interface BookingDetailView {
bookingContainers?: BookingContainerView[];
reviewNotes?: BookingReviewNoteView[];
files?: BookingFileView[];
/** The allocated train, present once the booking is placed on a schedule. */
trainSchedule?: {
trainNumber: string | null;
reference: string | null;
scheduledDepartureDate: string | null;
} | null;
}

View File

@@ -77,6 +77,12 @@ export type BookingDetail = Freight.IBooking & {
reference: string;
status: string;
} | null;
/** The allocated train, present once the booking is placed on a schedule. */
trainSchedule?: {
trainNumber: string | null;
reference: string | null;
scheduledDepartureDate: string | null;
} | null;
};
/**

View File

@@ -65,6 +65,12 @@ export function ScheduleCard({
const service = serviceTypeLabel(booking);
const equipmentReturn =
booking.equipmentReturn === "WITH_RETURN" ? "With return" : "Without return";
const schedule = booking.trainSchedule;
const trainLabel = schedule?.trainNumber
? `Train ${schedule.trainNumber}${schedule.reference ? ` · ${schedule.reference}` : ""}`
: schedule?.reference
? `Schedule ${schedule.reference}`
: "Track shipment";
const assignedTrain: Row = booking.trainScheduleId
? {
label: "Assigned train",
@@ -85,7 +91,7 @@ export function ScheduleCard({
fontSize: 13,
}}
>
<MapPin size={13} /> Track shipment
<MapPin size={13} /> {trainLabel}
</button>
),
}