mirror of
https://github.com/Tria-plc/edr-platform.git
synced 2026-08-28 21:50:57 +00:00
Merge pull request #1046 from Tria-plc/freight_feature/usermanagement
Freight feature/usermanagement
This commit is contained in:
@@ -83,6 +83,11 @@ export class BookingJourneyService {
|
||||
loadedByUserId: userId ?? null,
|
||||
} as never);
|
||||
await this.setAllocationStatuses(manager, scheduleId, bookingId, 'LOADED');
|
||||
// Keep the schedule↔booking link's tracking flag in sync — the dispatch
|
||||
// readiness warnings and workspace badges read loading_status, not loadedAt.
|
||||
await manager
|
||||
.getRepository(TrainScheduleBooking)
|
||||
.update({ trainScheduleId: scheduleId, bookingId }, { loadingStatus: 'LOADED' });
|
||||
// The facility handed the cargo over — raise its GRN. No-ops for yards
|
||||
// without a facility (import/export terminals), which keep their own flow.
|
||||
await this.facilityHandling.recordHandling(manager, {
|
||||
@@ -236,7 +241,10 @@ export class BookingJourneyService {
|
||||
return {
|
||||
scheduleId,
|
||||
scheduleStatus: schedule.status,
|
||||
trainAtYardId: latest?.yardId ?? (schedule.status === 'DISPATCHED' ? null : schedule.originStationId),
|
||||
// No checkpoint yet ⇒ the train is still at its origin, even just after
|
||||
// dispatch — assertTrainAtYard allows origin loading in that state, so
|
||||
// the UI position must agree or origin Load buttons grey out wrongly.
|
||||
trainAtYardId: latest?.yardId ?? schedule.originStationId,
|
||||
yards: [...byYard.values()],
|
||||
};
|
||||
}
|
||||
|
||||
@@ -1977,6 +1977,16 @@ export class TrainSchedulingService {
|
||||
manager,
|
||||
);
|
||||
|
||||
// Unassign only runs pre-dispatch, so an IN_TRANSIT status here is stale
|
||||
// (e.g. auto-loaded by an earlier dispatch that was rolled back). Left as
|
||||
// is, the booking becomes invisible: the eligible pool only admits PAID,
|
||||
// so it can never be re-added to any train. Revert it to PAID.
|
||||
if (booking?.status === 'IN_TRANSIT' && !booking.arrivedAt) {
|
||||
await manager
|
||||
.getRepository(Booking)
|
||||
.update(bookingId, { status: 'PAID', loadedAt: null } as never);
|
||||
}
|
||||
|
||||
// Recompute the train-set composition from whatever survives this removal.
|
||||
// The removed booking's allocations were already deleted above, so any slot
|
||||
// left with zero allocations was ridden only by this booking — release it
|
||||
@@ -6407,7 +6417,60 @@ export class TrainSchedulingService {
|
||||
action: 'BOOKING_REMOVED',
|
||||
yardLabel: null,
|
||||
}));
|
||||
return [...wagonRows, ...bookingRows].sort(
|
||||
// Per-booking journey events (load at boarding yard / unload at alighting
|
||||
// yard) — sourced from the booking's own loaded_at/arrived_at stamps, so a
|
||||
// multi-stop train's disjoint legs (a→b loads then unloads at b while a→c
|
||||
// rides through) each show as their own row. Append-only: these columns are
|
||||
// only ever set once per booking, never cleared, so rows never disappear.
|
||||
const journeyRows: HistoryRow[] = (
|
||||
await this.dataSource.query(
|
||||
`SELECT b.id,
|
||||
b.reference AS "subject",
|
||||
COALESCE(oy.label, oy.code) AS "yardLabel",
|
||||
COALESCE(u.username, u.email) AS "actor",
|
||||
b.loaded_at AS "occurredAt"
|
||||
FROM freight.bookings b
|
||||
JOIN freight.train_schedule_bookings tsb
|
||||
ON tsb.booking_id = b.id AND tsb.train_schedule_id = $1 AND tsb.deleted_at IS NULL
|
||||
LEFT JOIN freight.yards oy ON oy.id = b.origin_yard_id
|
||||
LEFT JOIN iam.users u ON u.id = b.loaded_by_user_id
|
||||
WHERE b.loaded_at IS NOT NULL
|
||||
AND b.deleted_at IS NULL
|
||||
ORDER BY b.loaded_at DESC
|
||||
LIMIT 200`,
|
||||
[scheduleId],
|
||||
)
|
||||
).map((r: Omit<HistoryRow, 'kind' | 'action' | 'note'>) => ({
|
||||
...r,
|
||||
kind: 'BOOKING' as const,
|
||||
action: 'BOOKING_LOADED',
|
||||
note: null,
|
||||
}));
|
||||
const unloadRows: HistoryRow[] = (
|
||||
await this.dataSource.query(
|
||||
`SELECT b.id,
|
||||
b.reference AS "subject",
|
||||
COALESCE(dy.label, dy.code) AS "yardLabel",
|
||||
COALESCE(u.username, u.email) AS "actor",
|
||||
b.arrived_at AS "occurredAt"
|
||||
FROM freight.bookings b
|
||||
JOIN freight.train_schedule_bookings tsb
|
||||
ON tsb.booking_id = b.id AND tsb.train_schedule_id = $1 AND tsb.deleted_at IS NULL
|
||||
LEFT JOIN freight.yards dy ON dy.id = b.destination_yard_id
|
||||
LEFT JOIN iam.users u ON u.id = b.arrived_by_user_id
|
||||
WHERE b.arrived_at IS NOT NULL
|
||||
AND b.deleted_at IS NULL
|
||||
ORDER BY b.arrived_at DESC
|
||||
LIMIT 200`,
|
||||
[scheduleId],
|
||||
)
|
||||
).map((r: Omit<HistoryRow, 'kind' | 'action' | 'note'>) => ({
|
||||
...r,
|
||||
kind: 'BOOKING' as const,
|
||||
action: 'BOOKING_UNLOADED',
|
||||
note: null,
|
||||
}));
|
||||
return [...wagonRows, ...bookingRows, ...journeyRows, ...unloadRows].sort(
|
||||
(a, b) => new Date(b.occurredAt).getTime() - new Date(a.occurredAt).getTime(),
|
||||
);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user