mirror of
https://github.com/Tria-plc/edr-platform.git
synced 2026-09-06 15:55:02 +00:00
implement intercity booking management and booking window websocket integration
This commit is contained in:
@@ -176,6 +176,44 @@ export class BookingsService {
|
||||
}
|
||||
|
||||
/** Resolve trade direction from yard countries; reject client mismatch. */
|
||||
/**
|
||||
* An intercity corridor is valid when both yards are Ethiopian and at least
|
||||
* one non-retired route passes the origin strictly before the destination in
|
||||
* its milestone order — that is the corridor an import/export train can
|
||||
* serve the booking on.
|
||||
*/
|
||||
private async assertIntercityCorridorExists(
|
||||
originYardId: string,
|
||||
destinationYardId: string,
|
||||
): Promise<void> {
|
||||
const yards = await this.dataSource.getRepository(Yard).find({
|
||||
where: { id: In([originYardId, destinationYardId]) },
|
||||
});
|
||||
if (yards.some((y) => y.country !== 'Ethiopia')) {
|
||||
throw new BadRequestException(
|
||||
'Intercity bookings only run between Ethiopian yards',
|
||||
);
|
||||
}
|
||||
const rows: Array<{ id: string }> = await this.dataSource.query(
|
||||
`SELECT r.id
|
||||
FROM freight.routes r
|
||||
JOIN freight.route_milestones mo
|
||||
ON mo.route_id = r.id AND mo.yard_id = $1 AND mo.deleted_at IS NULL
|
||||
JOIN freight.route_milestones md
|
||||
ON md.route_id = r.id AND md.yard_id = $2 AND md.deleted_at IS NULL
|
||||
WHERE mo.sequence_no < md.sequence_no
|
||||
AND r.status = 'AVAILABLE'
|
||||
AND r.deleted_at IS NULL
|
||||
LIMIT 1`,
|
||||
[originYardId, destinationYardId],
|
||||
);
|
||||
if (rows.length === 0) {
|
||||
throw new BadRequestException(
|
||||
'No route passes through this origin and destination in order — intercity service is not available on this corridor',
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
private async resolveTradeDirectionForBooking(
|
||||
originYardId: string,
|
||||
destinationYardId: string,
|
||||
@@ -607,6 +645,23 @@ export class BookingsService {
|
||||
dto.tradeDirection,
|
||||
);
|
||||
|
||||
// Intercity (DOMESTIC) bookings never get their own train — they ride on a
|
||||
// passing import/export train, so there is no booking window and no date to
|
||||
// pin. All we require at creation is that the corridor actually lies on a
|
||||
// route (origin before destination in some route's milestone order); staff
|
||||
// accept the booking onto a concrete train at finalize time.
|
||||
if (tradeDirection === 'DOMESTIC') {
|
||||
if (dto.scheduledDate || dto.trainScheduleId) {
|
||||
throw new BadRequestException(
|
||||
'Intercity bookings cannot pin a date or schedule — staff assign them to a passing train later',
|
||||
);
|
||||
}
|
||||
await this.assertIntercityCorridorExists(
|
||||
dto.originYardId,
|
||||
dto.destinationYardId,
|
||||
);
|
||||
}
|
||||
|
||||
// Stamp the operational profile this booking belongs to (importer/exporter)
|
||||
// so the customer portal can scope lists/KPIs to the active mode. Best-effort
|
||||
// for non-government bookings with a resolved company; never blocks creation.
|
||||
|
||||
Reference in New Issue
Block a user