diff --git a/apps/edr-freight-api/src/modules/train-scheduling/train-scheduling.service.ts b/apps/edr-freight-api/src/modules/train-scheduling/train-scheduling.service.ts
index 8f44665ad..98b6d70c2 100644
--- a/apps/edr-freight-api/src/modules/train-scheduling/train-scheduling.service.ts
+++ b/apps/edr-freight-api/src/modules/train-scheduling/train-scheduling.service.ts
@@ -721,27 +721,37 @@ export class TrainSchedulingService {
: null;
if (route) {
- const origin = route.originYard;
- const destination = route.destinationYard;
+ // `route.milestones` is the complete ordered corridor and already includes
+ // the origin (first) and destination (last) yards — `route.originYardId`
+ // and `route.destinationYardId` are derived from them. Use the milestones
+ // directly so the endpoints aren't double-counted (Addis…Addis, Dire…Dire).
const milestones = [...(route.milestones ?? [])].sort(
(a: RouteMilestone, b: RouteMilestone) => a.sequenceNo - b.sequenceNo,
);
+
+ if (milestones.length > 0) {
+ milestones.forEach((m, i) =>
+ stations.push({
+ sequenceNo: i,
+ yardId: m.yardId,
+ label: m.yard?.label ?? m.yard?.code ?? `Stop ${i + 1}`,
+ code: m.yard?.code ?? '',
+ }),
+ );
+ return stations;
+ }
+
+ // Route with no milestones recorded — fall back to its origin/destination.
+ const origin = route.originYard;
+ const destination = route.destinationYard;
stations.push({
sequenceNo: 0,
yardId: route.originYardId,
label: origin?.label ?? origin?.code ?? 'Origin',
code: origin?.code ?? '',
});
- milestones.forEach((m, i) =>
- stations.push({
- sequenceNo: i + 1,
- yardId: m.yardId,
- label: m.yard?.label ?? m.yard?.code ?? `Stop ${i + 1}`,
- code: m.yard?.code ?? '',
- }),
- );
stations.push({
- sequenceNo: milestones.length + 1,
+ sequenceNo: 1,
yardId: route.destinationYardId,
label: destination?.label ?? destination?.code ?? 'Destination',
code: destination?.code ?? '',
@@ -775,8 +785,16 @@ export class TrainSchedulingService {
const stations = await this.buildScheduleStations(schedule);
const events = await this.trainCheckpointEventsRepository.findBySchedule(scheduleId);
+
+ // Resolve each checkpoint's position by its yard against the canonical
+ // corridor rather than the stored sequenceNo, so legacy checkpoints logged
+ // under an older station numbering still line up with the current stations.
+ const seqByYard = new Map(stations.map((s) => [s.yardId, s.sequenceNo]));
+ const resolvedSeq = (e: TrainCheckpointEvent) =>
+ seqByYard.get(e.yardId) ?? e.sequenceNo;
+
const currentSequenceNo = events.length
- ? Math.max(...events.map((e) => e.sequenceNo))
+ ? Math.max(...events.map(resolvedSeq))
: -1;
return {
@@ -802,7 +820,7 @@ export class TrainSchedulingService {
currentSequenceNo,
checkpoints: events.map((e) => ({
id: e.id,
- sequenceNo: e.sequenceNo,
+ sequenceNo: resolvedSeq(e),
yardId: e.yardId,
label: e.yard?.label ?? e.yard?.code ?? null,
kind: e.kind,
diff --git a/apps/edr-freight-web/portal/src/pages/MyPortalPage/components/BookingRow.tsx b/apps/edr-freight-web/portal/src/pages/MyPortalPage/components/BookingRow.tsx
index 27b923467..ffd59666e 100644
--- a/apps/edr-freight-web/portal/src/pages/MyPortalPage/components/BookingRow.tsx
+++ b/apps/edr-freight-web/portal/src/pages/MyPortalPage/components/BookingRow.tsx
@@ -2,6 +2,7 @@ import { Box, Group, Stack, Text } from "@mantine/core";
import { memo } from "react";
import { ACTION_PROPS, STATUS_CONFIG, cv } from "../constants";
import { Stepper } from "./Stepper";
+import { PayNowButton } from "@/pages/bookings/payments/PayNowButton";
interface BookingRowProps {
booking: any;
@@ -18,6 +19,10 @@ export const BookingRow = memo(function BookingRow({
const Icon = cfg.icon;
const AIcon = cfg.action.icon;
const ap = ACTION_PROPS[cfg.action.kind];
+ // Payable bookings get an inline "Pay now" that opens the payment modal
+ // instead of navigating to the detail page.
+ const canPay =
+ booking.status === "SELECTED_FOR_BATCH" && booking.paymentStatus !== "PAID";
const origin = booking.originYard?.label ?? booking.originYard?.code ?? "—";
const dest =
booking.destinationYard?.label ?? booking.destinationYard?.code ?? "—";
@@ -78,25 +83,29 @@ export const BookingRow = memo(function BookingRow({
{cfg.badgeLabel}
-