Commit Graph

5165 Commits

Author SHA1 Message Date
marshal
400c4a1e14 Merge pull request #1491 from Tria-plc/dev
dev
2026-09-04 13:55:47 +03:00
marshal
fad2bf5af2 Merge pull request #1490 from Tria-plc/freight_feature/usermanagement
feat: implement manual consolidation candidate selection logic and up…
2026-09-04 13:54:07 +03:00
marshalyordanos
ca84aac456 feat: implement manual consolidation candidate selection logic and update related tests 2026-09-04 13:52:19 +03:00
marshal
92a8f0ff64 Merge pull request #1488 from Tria-plc/dev
dev
2026-09-04 09:44:01 +03:00
marshal
7519f2e805 Merge pull request #1487 from Tria-plc/freight_feature/usermanagement
Freight feature/usermanagement
2026-09-04 09:42:45 +03:00
marshalyordanos
eaa006a932 feat: add wagon performance report and export functionality
- Implemented  for Excel download of wagon performance report sections, including column width adjustment and timestamped filenames.
- Created  to compute derived wagon performance figures based on movements and status logs, ensuring consistency with API data.
- Enhanced  with new fields for tracking last movement and statistics window days for improved reporting capabilities.
2026-09-03 23:25:19 +03:00
marshalyordanos
380ba4c4c9 Refactor code structure for improved readability and maintainability 2026-09-03 22:27:00 +03:00
Abubeker Yasin
ad791e2074 Merge pull request #1486 from Tria-plc/reschedule
Reschedule
2026-09-03 16:17:11 +03:00
Abubeker Yasin
6eb5f2ee1e Update page.tsx 2026-09-03 16:16:10 +03:00
marshal
82ba9e3f1c Merge pull request #1485 from Tria-plc/dev
dev
2026-09-03 16:11:19 +03:00
Abubeker Yasin
38df7f034c feat: ( notifications ) send SMS and email for applied and expired reschedules and upgrades 2026-09-03 16:09:03 +03:00
marshal
b741b875f5 Merge pull request #1484 from Tria-plc/freight_feature/usermanagement
feat: add BookingWagonsPanel component for displaying allocated wagon…
2026-09-03 15:54:00 +03:00
marshalyordanos
9c57aa1c0c feat: add BookingWagonsPanel component for displaying allocated wagons in booking details
- Implemented BookingWagonsPanel to show allocated wagons, their containers, and export functionality.
- Integrated the new panel into BookingRequestDetailPage and BookingRequestsPage.
- Enhanced wagon cancellation modal to support rebooking of wagon cancellations with partner units.
- Updated API service to include a method for downloading wagons workbook.
- Modified types and constants to accommodate new features related to wagons.
- Adjusted various components and pages to ensure compatibility with the new wagon-related functionality.
2026-09-03 15:39:27 +03:00
Abubeker Yasin
9a9955cbaa fix: (bookings) release the seat hold when a reschedule or upgrade expires and honour shortened payment windows 2026-09-03 14:39:40 +03:00
Abubeker Yasin
165146c09d Merge pull request #1483 from Tria-plc/alpha
Alpha
2026-09-03 11:36:56 +03:00
Abubeker Yasin
599fa932c5 feat: ( reports ) add revenue-type filter for baggage, outstanding and other charges 2026-09-03 11:35:52 +03:00
Abubeker Yasin
e71a525c15 fix: ( reports ) scale finance segment chart from minor units 2026-09-03 10:55:20 +03:00
Abubeker Yasin
d86867b2f7 feat: ( reports ) add regular/package booking-type filter to finance summary 2026-09-03 10:30:21 +03:00
Hagernesh Tadesse
7a9c44ec97 Merge pull request #1482 from Tria-plc/Emty-container-return
feat(warehouses): load trains from the warehouse, mirroring the schedule
2026-09-03 07:41:28 +03:00
hager
076884623e feat(warehouses): load trains from the warehouse, mirroring the schedule
Ready to Load listed inventory and offered an auto-load picker, but the
warehouse floor works train by train: a train stands at the yard, its
bookings board wagon by wagon, it rolls. That view existed only on the
train schedule workspace, which the warehouse staff do not run.

Ready to Load now has an Items / By train switch. By train is a mirror
of the schedule's own column, placed where the loading actually happens.

Nothing in it has its own rules. Train position, the per-yard loading and
unloading windows, and the per-booking Load / Wagons / Unload actions all
come from the train-scheduling endpoints the schedule workspace already
calls, so a Load that would be refused there is disabled here with the
same reason and the two surfaces cannot disagree. No train-scheduling
code is touched.

What the warehouse adds is what the schedule cannot see: which of the
train's bookings are physically in the shed, with their GRN and
inspection state, laid out along the flow the staff follow -- receive and
GRN, inspect, ready, open the loading window, train at yard, load per
wagon, dispatch, unload at port.

Wagons
- The wagon modal calls the same per-wagon journey endpoints, so every
  server gate (train at the yard, window started, PAID, GRN) is the
  schedule's own.
- Wagons go one at a time in order: the server flips the booking to
  IN_TRANSIT or ARRIVED on whichever call clears the last wagon, so
  sequential is required, not merely tidy. A failure stops the run, the
  wagons already sent stay done, and the toast says how many, so a retry
  only resends the rest.
- Deliberately not mirrored: cancelling wagons that will not ride, and
  the direct truck-to-train handover. Both are commercial decisions
  (fees, credits, GRN waiver) that belong to the schedule workspace.

loadable-trains takes includeDispatched. Loading follows the train after
it rolls, since a mid-corridor warehouse boards its cargo when the train
stands at its yard, and the train-centric view needs the same set the
schedule offers Load on. The default stays pre-dispatch only, so the
existing auto-load picker is unchanged.

Also fixes the backoffice build: ReceiveInventoryModal used MultiSelect
without importing it, left behind by the self-haul assignment work.
2026-09-03 04:38:46 +00:00
marshal
31cdf2ebcc Merge pull request #1480 from Tria-plc/origin/freight_feature/transit
Origin/freight feature/transit
2026-09-03 02:37:27 +03:00
marshal
3fd94c9314 feat(transit-agent): timed document uploads and a real-data overview 2026-09-02 23:36:35 +00:00
marshal
42ca020a1f Merge branch 'dev' of github.com:Tria-plc/edr-platform into origin/freight_feature/transit 2026-09-02 22:38:15 +00:00
marshal
4e6a5496c8 Merge pull request #1479 from Tria-plc/freight_feature/usermanagement
Freight feature/usermanagement
2026-09-03 01:36:24 +03:00
marshal
13ac3144e2 merge conflict resolved 2026-09-02 22:35:18 +00:00
marshal
1d3361d442 feat(freight): GL rebook of cancelled wagons, seal+train required on completion, train/voyage in SMS 2026-09-02 22:28:11 +00:00
marshal
6efa9dab93 feat(freight): GL rebook of cancelled wagons, seal+train required on completion, train/voyage in SMS 2026-09-02 21:23:24 +00:00
marshal
3e274cc2a2 fix(freight): gate loading on booking paymentStatus only; default schedule voyage no. to train's voyage number 2026-09-02 21:15:48 +00:00
Hagernesh Tadesse
78086422ca Merge pull request #1478 from Tria-plc/Emty-container-return
Emty container return
2026-09-02 23:49:17 +03:00
hager
29c1a21b68 feat(bookings): make customer self-haul assignment work end to end
The customer truck card on the booking's Logistics tab is now usable for
every cargo type, with an Excel template and bulk upload that match what
the API enforces.

Excel template and bulk upload
- The template is built per booking. Container bookings get the booking's
  own containers with their sizes on a reference sheet and sample rows
  paired 20ft+20ft; bulk (PER_TON) bookings get a Planned Tons column;
  counted cargo (PER_ITEM: machinery, RoRo vehicles) gets Planned Quantity.
- Parsing validates the whole file before anything is posted: plate,
  driver, truck type, ISO container numbers, containers on the booking,
  duplicates across rows, containers already on a truck, and the capacity
  rule (one 40ft alone, or two 20ft). Errors quote the Excel row number.
- The bulk DTO reuses AddCustomerTruckDto instead of a drifted copy that
  lacked plannedTons/plannedQuantity, so bulk cargo can be uploaded at all.
- A partial failure is reported per row in the modal instead of closing it
  as if every truck had been created.

Capacity rule in the form
- The container picker shows sizes and stops offering a second container
  once a 40ft is picked, or a 40ft once a 20ft is picked.
- PER_ITEM cargo commits by item count; tonnage becomes optional.

Shared vocabulary
- CUSTOMER_TRUCK_TYPES and ISO_CONTAINER_NUMBER move to @edr/types so the
  API validators, the dropdown and the template read one list.

When assignment is open
- The card always renders and states why assignment is closed (EDR
  haulage, unpaid, train not arrived, cargo already loaded) rather than
  vanishing.
- Self-haul is blocked only once EDR has committed to the road leg: an
  approved last-mile request or an existing last-mile leg. A delivery
  address whose request is still awaiting confirmation, submitted or
  rejected no longer blocks the customer from bringing their own truck.
  Collection on exports has no approval step and still blocks as before.
  Both the multi-truck service and the legacy single-truck path read the
  same SQL fragment (edrHaulsThisBooking), and the portal applies the same
  rule with a notice that assigning a truck makes the pending request
  unapprovable. The last-mile side already refuses to approve a booking
  carrying a customer truck, so the two paths stay mutually exclusive.

Verified: EXPLAIN on the new SQL against edr_dev, type-check clean for
freight-api and portal, 12 util specs pass (5 new). Backoffice type-check
fails only in pre-existing user-management files.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-09-02 20:47:18 +00:00
hager
d6f730fa3e fix(empty-return-requests): show the queue to the staff who run returns
The menu item was gated on the brand-new empty_return_requests:view key,
which on the dev DB is held by one position (Operations Chief) — so the
Director and Dispatcher who already work Container Returns two rows above
it never saw the entry at all.

Viewing the queue now accepts that key OR warehouse_inventory:view, the
key the rest of the Imports menu runs on, in the sidebar, the route guard
and the API's read routes alike. Approving and rejecting stay on the
review key on their own, and the buttons come up disabled (with the
reason) for a viewer who lacks it rather than firing into a 403.

The dedicated keys are still there to hand out per position whenever
that call gets made.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-02 20:47:18 +00:00
Nathnael Wondisha
42f7a5a7b4 Merge pull request #1477 from Tria-plc/freight/hot-fixes
fix: export colmn
2026-09-02 16:51:12 +03:00
nathnael
a06faa4254 fix: export colmn 2026-09-02 13:47:23 +00:00
Abubeker Yasin
e914aaeb53 feat: (upgrade) implement per-passenger fare class upgrade with configurable policies 2026-09-02 16:18:36 +03:00
Hagernesh Tadesse
0f979862cd Merge pull request #1476 from Tria-plc/Emty-container-return
Emty container return
2026-09-02 14:42:51 +03:00
hager
de55d809c7 fix(empty-return-requests): pick the containers off the booking
Typing container numbers let a customer request a return for a box that
never came in on that booking — and gave them a blank field to guess at
when they did not have the numbers to hand.

The portal now lists the booking's own containers, minus any already
spoken for by a live request, as a tick list; the count follows the
ticks. The API backs that up: every submitted number must be one of the
booking's containers, and a booking with no container numbers recorded
says so instead of offering an empty form.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-02 11:24:43 +00:00
hager
3f7b744987 feat(empty-return-requests): customer-requested empty return, priced and paid
A booking sold WITHOUT the return service had no way to send its empties
back: the containers were the customer's problem and nothing in the
system priced, billed or planned the movement.

Adds the request flow end to end. The customer opens the booking, says
how many containers are coming back and types each number; the request
lands in a new backoffice queue (Empty Return Requests). Approval prices
it off the same live WITH_RETURN route rate the rule engine bills when
the service IS bought up front — per container, converted to birr, and
overridable by the reviewer — and issues the invoice there and then.
Payment settles through the normal invoice path, whose
`empty_return_request.invoice.paid` event moves the request to PAID; the
customer then books the return date and the truck.

Scheduled requests surface on Container Returns as Planned Empty
Returns, where confirming the arrival records the containers through the
existing empty-container-return flow — which in turn closes the request
once its last container is in.

Container freight only, never a booking that already ships WITH_RETURN,
and only from IN_TRANSIT onward: the empty comes back after delivery, so
the option has to outlive ARRIVED.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-02 11:05:03 +00:00
Hagernesh Tadesse
bc2e005bbc Merge pull request #1475 from Tria-plc/Emty-container-return
feat(import-operations): record empty container return per booking
2026-09-02 13:20:09 +03:00
hager
2e51342d1e feat(import-operations): record empty container return per booking
Container Returns only ever offered the last-mile path: a booking reached
the list once it had a truck assigned and warehouse inventory flagged as
returning. Bookings that ship WITH equipment return had no way in, so the
empties they owe were invisible until that path happened to fire.

Adds GET /import-operations/empty-return-bookings — the containers a
booking flagged is_return, carrying whichever of them already has an
empty return recorded, grouped one row per booking and dropped from the
list once nothing is pending. Covers both spellings of the booking's
equipment_return (WITH_RETURN and the older RETURN) and skips bookings
that never ship.

Backoffice grows a "Bookings With Empty Container Return" card above the
existing sections: pick the booking, tick the containers coming back,
say where they landed, and each tick becomes an empty container return
on that booking — which is what the Returned Containers table then
advances. The existing last-mile, standalone and bulk flows are
untouched.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-02 10:17:19 +00:00
Abubeker Yasin
2e33e9aeb3 Merge pull request #1474 from Tria-plc/alpha
Alpha
2026-09-02 11:59:02 +03:00
Abubeker Yasin
db97c3682f feat: ( reports ) add intercity/international trip-type filter to finance summary 2026-09-02 11:57:48 +03:00
Hagernesh Tadesse
75bbdbf84c Merge pull request #1473 from Tria-plc/Warehousechanges
Customer Truck Assignmnet
2026-09-02 10:24:26 +03:00
hagiye
031efce92b Truck assiggnment and per truc 2026-09-02 07:17:19 +03:00
hagiye
6d6b32feae api key fix 2026-09-02 07:17:01 +03:00
marshal
ec5df4b328 Merge pull request #1472 from Tria-plc/freight_feature/usermanagement
feat: Implement handling for partially loaded bookings in train sched…
2026-09-01 23:56:07 +03:00
marshalyordanos
d51bed5630 feat: Implement handling for partially loaded bookings in train scheduling
- Added  to manage decisions on partially loaded bookings during log-pass and dispatch actions.
- Enhanced  to track and manage bookings left behind when a train departs a yard.
- Updated  and  to pass necessary station data for handling partially loaded bookings.
- Modified  and  to account for customer-fault fees and ensure proper handling of credits.
- Introduced fault tracking in  interface to differentiate between customer and EDR faults.
2026-09-01 23:53:58 +03:00
Nathnael Wondisha
6411dbce9b Merge pull request #1471 from Tria-plc/freight/fix/quick-fixes
fix: matrix
2026-09-01 10:08:20 +03:00
Nathnael
7fd5616188 fix(backoffice): stop sharing the position cookie with Smart Office
Both apps wrote a cookie named `current-position-id` but stored
different ids in it — freight the `employeePositionId`, Smart Office the
`position.id`. On a shared domain each login overwrote the other's desk
selection, and the loser silently fell back to the first position.

Freight now uses `freight-current-position-id` through a small helper
that reads the old name once, so a session live across the deploy keeps
its desk, and clears it on every write.

Also mounts the position switcher in the freight dashboard header. It
only existed under /performance-management, so on every other page a
two-desk user had no way to switch and was stuck on whatever
`useAuthUser` defaulted to. It now hides below two positions rather than
showing a one-option dropdown to the single-desk majority.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-01 07:06:05 +00:00
Nathnael
db9d6e49c7 fix(auth): load every position a user holds
Staff given a post in Smart Office and another in freight only ever
loaded one of them. Two causes, both in how the IAM guard collapses the
login snapshot:

- `x-current-position-id` is read two ways inside one function: the
  employee row is matched on `position.id`, the position on
  `employeePositionId`. Freight sends the latter, Smart Office the
  former, so whichever value arrives one lookup matches nothing and
  falls back to `positions[0]`. FreightJwtGuard now matches both fields.

- IAM keeps one employee row per organization, and EDR and EDR Freight
  are separate organizations, so a user holding a post in each owns two
  rows. Only the active row reached `collectPermissionKeys`, so the
  freight post's permissions disappeared whenever the other row won the
  active slot. `employee.positions` now unions every row, which is what
  the util already does for the array shape.

`delegatedPositions` stays scoped to the active row on purpose: yard
scope widens on it, and someone standing in on another organization's
row is not this desk's stand-in.

/auth/me now returns every employee row, active row first, so the
position picker can offer a desk that is not on the active row.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-01 07:03:58 +00:00
Nathnael
3f2bcf1a1a fix(customers): drop fake before-state in history diff rows
DiffRow rendered onboarding's initial field values as '— → value',
implying a prior state that never existed. Show the value alone when
there is no real before.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-09-01 07:01:27 +00:00