Commit Graph

387 Commits

Author SHA1 Message Date
ghost2023
aa96bb4086 Merge branch 'dev' into dj-franc 2026-09-04 16:31:21 +03:00
marshalyordanos
ca84aac456 feat: implement manual consolidation candidate selection logic and update related tests 2026-09-04 13:52:19 +03:00
ghost2023
3734ca3897 feat(freight-api): add DJF as a supported currency
Adds DJF to freight.payments_currency_enum (migration 3860000000000,
alone in its own migration per Postgres's ADD VALUE-in-a-transaction
restriction) and to manual_payment_settings (djf_enabled column,
migration 3870000000000, default true).

Replaces the binary ETB/USD assumptions that would have silently
mispriced or discarded a DJF booking:
- booking-pricing / contract-pricing: usdToEtb scalar -> a rate table
  keyed by source currency (ExchangeService.getRateTable), so a
  contract-frozen rate converts into whatever currency the booking is
  paid in instead of being dropped when neither leg is ETB or USD.
- warehouse-fee / booking-wagon-cancellation: normalizeCurrency no
  longer coerces anything non-ETB to USD.
- additional-charge: convertAmount no longer bails out for a currency
  that isn't literally ETB or USD.
- manual-payment-settings: isEnabled/enabledCurrencies cover DJF.

Widens the three @IsIn(['ETB','USD']) DTO validators, and adds DJF to
the export/report currency filter option lists.

Claude-Session: https://claude.ai/code/session_01CZy77vCWhka3pnmVF9NDkL
2026-09-04 11:52:37 +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
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
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
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
hagiye
031efce92b Truck assiggnment and per truc 2026-09-02 07:17:19 +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
Marshal
b5ad46f317 feat: add Transit Clearance Action Panel for managing delivery and release orders
- Implemented TransitClearanceActionPanel component for transit agents to handle DO/RO uploads and amendments.
- Added file picker for uploading documents with validation for vessel arrival and collection dates.
- Introduced modals for uploading T1 documents and requesting RO amendments.
- Enhanced transit assignments service with new API endpoints for clearance history, GL exchange documents, and incident reports.
- Updated types to include new document upload sources and statuses.
- Created CSS styles for transit bookings table to improve layout and responsiveness.
- Exported new TransitAgentBookingDetailPage for detailed booking views.
2026-08-31 13:56:39 +00:00
Hagernesh
cc50a6687a feat(bookings): per-row stations + totals footer on carriage acceptance sheet
The sheet printed Departure/Arrival Station as booking-level constants and
squeezed the footer figures into unrelated table columns (gross weight under
Cargo Name, full/empty under Wagon No.).

Stations now resolve per row from the slot's own board/alight yard, falling
back to the schedule's origin/destination and then the booking yards —
mirroring buildExportLoadListHtml's leg-slot handling.

The paper form's footer becomes its own tile block: In Total Wagon No., Tare
Weight, Load Capacity, Gross Weight, Equated Length, Full Wagon, Empty Wagon,
Total Amount. Rendered as .tile so buildTabularFallbackPdf still prints every
figure where Chromium is absent.
2026-08-31 11:28:36 +00:00
Hagernesh
fd6f0d03b6 feat(loading): notify loaded and left-behind containers, surface the CAS
A booking is routinely loaded in parts, and nothing told the customer which
containers boarded and which stayed behind. The carriage acceptance sheet was
the only record, and it both totalled up cargo still sitting in the yard and
lived inside a Warehouse documents bundle that direct truck-to-train cargo
has no business in.

The sheet now marks each wagon Loaded or Not loaded and totals only the loaded
ones. On load, the customer gets an in-app, SMS and email notice carrying the
train number, route, departure time and both container lists — capped to a
summary on SMS and email, complete in the inbox. Anything left behind also
raises a warehouse-desk notice so somebody owns finding it space.

That desk is addressed by a new warehouse_inventory:get_notification
permission: a recipient selector, not a route guard, so ops can assign who
gets pinged without granting access to anything.

The GRN notice went out over SMS alone, to whatever phone number the gate
clerk typed. Where the receive carries a booking it now resolves the company
and delivers in-app, SMS and email, skipping the typed phone so the customer
is not texted twice; manual and backlog receives keep the old path.
2026-08-29 08:47:39 +00:00
marshal
abf856547a Merge pull request #1453 from Tria-plc/freight_feature/usermanagement
Freight feature/usermanagement
2026-08-29 10:40:53 +03:00
Hagernesh
312c1f1da3 feat(warehouses): backoffice UI for container stack positions
Surfaces the physical stack/slot model in the staff app.

- ZoneLayoutModal: stacks drawn level by level with occupancy colours,
  configured-vs-built-vs-occupied counts, and stack create/delete plus
  block/reserve/free on empty levels
- SlotPicker in the store and move modals, offering only the next
  fillable level of each stack so the form cannot suggest a position
  the API will refuse
- move modal warns when a container is buried, lists the blockers, and
  disables the action instead of firing a 409
- fix: move() now asserts accessibility server-side, matching release —
  both are exits from a stack
2026-08-29 06:39:29 +00:00
Marshal
7e163088d9 fix issue, add transit flow, fix cancellation 2026-08-28 22:13:49 +00:00
Nathnael Wondisha
132374ed03 Merge pull request #1440 from Tria-plc/freight/nati-2
feat(bookings): surface cargo declared on the shipment request
2026-08-28 14:29:02 +03:00
Nathnael
df8e10e041 feat(bookings): surface cargo declared on the shipment request
A GENERAL + customs contract does not let the customer book directly: they
submit a shipment request, and initiateForShipmentRequest opens a BARE booking
from it — "the request itself carries the quantities; the instance carries
none". Between initiation and completeUnderContract the booking legitimately
holds no cargo, so the export reported 0 containers for a customer who had
declared, say, 2 x 20FT. 23 bookings on dev data are in that state.

Adds two columns and one filter reading booking_requests.requested_lines:
- "Requested cargo" — the declared lines as text ("2 x 20FT"), handling the
  bulk shape too (tons / item count), not only containers.
- "Requested containers" — the declared box count, with a matching min/max
  filter on the list and the export.

Deliberately a separate column rather than a fallback inside the real container
count: a declared 2 x 20FT is a request, not two boxes on a booking, and
merging them would overstate operational totals. The two compose instead —
Containers = 0 AND Requested containers >= 1 is exactly the set awaiting
completion after clearance.

requested_lines is free-form jsonb, so the container array is guarded by
jsonb_typeof before jsonb_array_elements; one malformed row would otherwise
500 the whole list.
2026-08-28 11:23:52 +00:00
Nathnael Wondisha
cfd182cfea Merge pull request #1438 from Tria-plc/freight/nati-2
Freight/nati 2
2026-08-28 14:09:18 +03:00
Nathnael
aa3100d161 Merge branch 'dev' into freight/nati-2 2026-08-28 11:08:35 +00:00
Marshal
3015de7508 fix issues 2026-08-28 10:34:02 +00:00
Nathnael
74fb05207c feat(bookings): filter by container count, export per-type quantities
Container filters on the booking-requests list:
- "Container type" — bookings carrying that type.
- "Containers" — a count of BOXES (booking_container is one row per line with
  a quantity, so this sums quantity rather than counting rows), as an exact
  value or a range. It reads the container-type filter when one is set, so the
  one control answers both "10 containers in total" and "10 forty-footers".

Export gains a column per container type ("20FT containers", "40FT
containers"), plus the total "Containers" column and the two filters. Container
types are reference rows, not a constant, so `ExportDataset` gains an optional
`dynamicFields` resolver — DB-driven columns appended to the static list and
cached for the process, mirroring the existing `ExportFilterDef.optionsQuery`.
Adding a 45ft container type adds its column with no code change. The type id
is interpolated into raw SQL (ExportField.select has no parameter bag), so the
resolver drops any id that is not a uuid.

Also repoints the export's "Container VGM" column at the per-line sum. It was
projecting bookings.cargo_total_weight_vgm, which the portal wizard leaves at 0
for container freight — the same trap the tonnage fix addressed — so the column
read 0 for every portal-created container booking. Non-zero on dev data goes
from 54 to 170 of 208 container bookings.
2026-08-28 10:02:01 +00:00
Nathnael
339b8a8682 feat(bookings): filter and export booking content
The booking-requests list could filter by freight type but not by what is
actually in the booking, and the export's only cargo column showed the
commodity name — blank for every container booking, which stores no
commodity at all.

Adds one resolver, `bookingContentSql`, that answers "what did the customer
say is in this booking" per freight type: the container lines they entered
("2 × 40FT, 1 × 20FT") for container freight, since the wizard asks them for
no description; the commodity they picked from the cargo tree for bulk,
falling back to their free-text description.

List filters:
- "Content" — a single select flattening the cargo tree the same way the
  booking wizard presents it (group, then each commodity as "Bulk → Wheat").
  Picking a GROUP matches its whole subtree via a recursive walk, so "Bulk"
  returns all 44 bulk bookings rather than the 0 that carry the group id
  itself. This makes the existing, previously unexposed `cargoTypeId` param
  group-aware.
- "Content contains" — a contains-search over the description, the commodity
  name and the container types, so container bookings are reachable by "40FT"
  even though they carry no words of the customer's own.

Both apply through `applyListFilters`, so the list, its summary tiles and its
facets agree, and both are declared on the bookings export dataset — the
export button already forwards the page's filters verbatim.

Export fields: "Content" (default), plus "Cargo description" as its own
column. The old `cargo` column is unchanged and still selectable, relabelled
"Cargo (commodity)"; it loses only its default tick, so saved presets that
name it keep working.
2026-08-28 09:42:56 +00:00
Nathnael
0e9cfbce66 fix(reports): count container tonnage in exports and reports
Every SQL tonnage in the export datasets and report definitions used
`COALESCE(b.bulk_total_weight_tons, b.cargo_total_weight_vgm)`. COALESCE
falls through on NULL, never on 0 — and the portal booking wizard stores
`cargo_total_weight_vgm = 0` for container freight on purpose, because VGM
is captured per container line, not as a booking-level figure. So every
portal-created container booking reported as weighing nothing. The
backoffice wizard does store a booking-level total, so the same table holds
both shapes and the numbers looked erratic rather than uniformly zero.

Extract the resolver the TypeScript side already has three copies of
(bookingCargoTons, cargoTonsAndItems, totalVgmTons) into one SQL helper:
NULLIF both booking-level columns, then fall back to
SUM(booking_container.total_vgm_tons). Applied to the bookings and
train-schedules export datasets, the cargo-summary, contract-utilization and
booking-status-breakdown reports, and the intercity booking list.

On dev data this recovers 116 of 154 zero-weight container bookings and
raises live booking tonnage from 42,973 t to 61,424 t.
2026-08-28 09:20:46 +00:00
Marshal
ba56974e32 feat(train): enhance train history and scheduling features
- Added a reason field to train history entries for detach/maintenance actions.
- Updated TrainHistoryPanel to display the reason for wagon detachments.
- Introduced per-wagon load/unload functionality in ScheduleWorkspacePanel with a modal for managing individual wagons.
- Implemented API endpoints for loading and unloading specific wagons, including the ability to cancel remaining wagons with a reason.
- Refactored detach request handling in TrainBuilderDetailPage to streamline the process and remove the approval flow, requiring a reason for detachments.
- Updated types and services to support new wagon loading/unloading features and booking wagon retrieval.
2026-08-28 07:33:28 +00:00
Marshal
8b8870e85e fix issue 2026-08-27 20:58:44 +00:00
Marshal
b926a3116e feat: enhance train scheduling and contract management features
- Added StationWorkControls to manage loading/unloading phases in TrainScheduleV2DetailPage.
- Implemented API endpoints for recording station work and managing wagon detach requests.
- Updated contract templates to include Ethiopian customs handling options.
- Enhanced shipment forms to collect customs clearing agent details for without-customs bookings.
- Introduced NUMBER_OF_WAGONS as a unit of measure for bulk cargo, allowing customers to specify wagon counts.
- Improved validation for customs clearing agent information in shipment forms.
- Updated various components and services to accommodate new features and ensure data integrity.
2026-08-25 21:44:21 +00:00
Marshal
d5a5085d6d feat: enhance booking and audit log functionalities
- Implemented read-only locking for customer-requested container sizes and billing currency in the GlCreateBookingForm component.
- Added functionality to lock partner quantities based on shipment requests in the ConsolidationPartnerPanel.
- Introduced a new Leave action in the LogPassYardWorkModal to unassign bookings from trains.
- Enhanced the AuditLogsPage to support filtering by action and added a Go button for direct navigation to entity detail pages.
- Updated WagonCancellationsPage to handle odd-20ft credits requiring partner selection during rebooking.
- Improved TrainScheduleV2DetailPage to allow manual loading of cargo and display warnings for unassigned bookings.
- Added a new reference field to the audit logs for better searchability and tracking of actions.
- Created a migration to add the reference column to the audit logs table and established an index for efficient querying.
- Defined a registry for audit reference sources to streamline the retrieval of human identifiers for various entities.
2026-08-24 23:49:20 +00:00
Marshal
2a107e8ba3 fix issue -> consolidation 2026-08-24 12:24:23 +00:00
Marshal
979f024869 feat: enhance booking cancellation logic for consolidated pairs with one paid side 2026-08-24 07:13:50 +00:00
Marshal
31fa313c66 add draft declaration 2026-08-23 19:02:33 +00:00
Marshal
58f88b74f8 add contract link 2026-08-23 05:00:43 +00:00
Marshal
716444e60a feat: add pagination to schedule history and consolidation approvals
- Implemented pagination in ScheduleHistoryPanel to manage large history entries.
- Updated API to support pagination parameters for schedule history.
- Enhanced ConsolidationApprovalsPage with tabbed navigation and pagination for approval rows.
- Introduced new types for paginated responses in bookings and train scheduling services.
- Added a database migration to create an index on wagon_booking_allocations for performance improvements.
2026-08-23 04:51:34 +00:00
Marshal
e2189040fa feat: add pagination to schedule history and consolidation approvals
- Implemented pagination in ScheduleHistoryPanel to manage large history entries.
- Updated API to support pagination parameters for schedule history.
- Enhanced ConsolidationApprovalsPage with tabbed navigation and pagination for approval rows.
- Introduced new types for paginated responses in bookings and train scheduling services.
- Added a database migration to create an index on wagon_booking_allocations for performance improvements.
2026-08-23 04:49:58 +00:00
marshal
105879b531 Merge pull request #1399 from Tria-plc/freight_feature/usermanagement
feat(train-scheduling): mid-route consist changes, audit history, saf…
2026-08-23 06:59:27 +03:00
Marshal
8e6fc09aac feat(train-scheduling): mid-route consist changes, audit history, safer workspace
- planned couples: loose wagons join the train at a route stop, added
  from the schedule yards tab; capacity credits them per corridor edge
  and coupling validates locomotive weight/length caps per leg
- real-cut toggle: a cut wagon permanently leaves the train build at
  its cut yard (soft cut still sits out one trip only)
- fix heaviest-leg display counting a shared slot's full cargo on
  every spanned edge (phantom pull-weight overload on S-2026-00045)
- confirmation dialogs for workspace add/load/unload/remove actions
- train-builder History and Detached-wagons tabs, backed by paginated
  endpoints; builder detaches now always write adjustment-log rows

Migrations 3660 (planned_wagon_couples, planned_wagon_real_cuts) and
3670 (adjustment log train_schedule_id nullable) — both applied to the
dev DB by hand; watch mode does not run migrations.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-23 03:55:54 +00:00
marshal
e2dd761203 Merge pull request #1387 from Tria-plc/freight_feature/usermanagement
fix issue
2026-08-22 03:50:34 +03:00
Marshal
b6c1efa043 fix issue 2026-08-22 00:49:53 +00:00
marshal
ed57490575 Merge pull request #1382 from Tria-plc/freight_feature/usermanagement
Freight feature/usermanagement
2026-08-22 02:17:34 +03:00
Marshal
09deecd04c fix issue and add consolidation 2026-08-21 23:16:06 +00:00
Hagernesh Tadesse
4eb840c090 Merge branch 'dev' into eims-bulk-register 2026-08-21 16:03:03 +03:00
Hagernesh
8181834037 Ticket
Empty container returns now generate a downloadable Equipment Interchange Receipt: container number & size, exact return timestamp, depot/location, condition, and the carrier/booking reference. Customers can download it from their booking's Documents tab (own-booking returns only); staff can pull any via the same endpoint.
2026-08-21 08:59:06 +00:00
Marshal
a9c6b85c08 feat(clearance): preview charge documents before and after upload 2026-08-21 08:59:06 +00:00
Marshal
c5909b8ec7 [200~feat: add CustomsPaymentsCard and PaymentsTab components for handling customs payments and payment summaries 2026-08-21 08:47:47 +00:00
Marshal
4c549029fe feat(clearance): preview charge documents before and after upload 2026-08-21 07:04:22 +00:00
Marshal
a5e6cab248 fix issue 2026-08-20 18:16:03 +00:00