Commit Graph

1006 Commits

Author SHA1 Message Date
Marshal
540f4a1ff6 changes 2026-07-20 11:41:57 +00:00
Marshal
0727de0f4d Refactor clearance terminology to document approval across services and UI components 2026-07-20 11:41:57 +00:00
Nathnael
38bccaccf9 fix: normalized the region and logged the otp properly 2026-07-20 11:41:57 +00:00
Nathnael
6cb921e617 fix: 194 and 183 plane takss 2026-07-20 11:41:57 +00:00
Nathnael
9fbe1c5236 feat: setup attachment to the freight chat 2026-07-20 11:41:57 +00:00
Hagernesh
c1c362ab9d fix(mile): only send bookings that bought EDR haulage to the mile queues
The rule that a customer's own truck and an EDR road leg are alternatives
existed on the truck side only (CustomerTruckService.assertSelfHaulPaid).
LastMileService had no counterpart: create checked payment and nothing else,
so any paid booking could be accepted into the queue. A booking took a
customer truck at 06:42 and an EDR last-mile leg with a real EDR truck at
06:47, neither side aware of the other, on a contract that had chosen no
road legs at all.

The road legs are chosen on the contract and copied onto the booking, and
the pickup/delivery address is the only per-booking record of that choice.
service_types cannot serve: every type ships with includes_first_mile and
includes_last_mile set to true, so reading them would mean no booking could
ever self-haul. That same always-true flag had already killed the first-mile
guard, whose `address || serviceType.includesFirstMile` admitted every paid
export booking.

One shared rule now answers it for both sides, so the two halves cannot
drift apart again: last-mile create rejects a booking that chose no road
legs and one already carrying a customer truck; first-mile no longer honours
the service-type flag; the customer-truck guard reads the same helper.

Existing legs are untouched — the guards are on creation, so the one booking
already carrying both needs a human to reconcile it.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-20 08:03:16 +00:00
Hagernesh
2906c6bb45 test(train-scheduling): cover the checkpoint backdating guard on the DTO
The IsNotBackdated validator was covered in isolation, but not on the DTO
that actually carries it. Asserts a backdated occurredAt is rejected, that
"now" passes, and that omitting the field still validates so the service can
stamp it.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-20 07:13:52 +00:00
Hagernesh
536d6043c2 fix(clearance): allow customs risk to be corrected and keep the trail
RiskStep returned early to a badge as soon as a risk level existed, so the
control was unreachable and a mis-assigned level could never be corrected.
Both the server and the sibling AssignRiskCard treat risk as correctable
until duty is advised off it — completeWithMetadata has no already-completed
guard and overwrites metadata.riskLevel. RiskStep was stricter than either.

It now keeps the control mounted alongside the assigned badge, offers
"Reassign risk", and locks to badge-only once DUTY_TAXES_ADVISED completes.
The control also reads the persisted level (it was hardcoded to GREEN, so
unhiding it alone would have misreported the assignment), and the T1 gate is
skipped once a level exists, since risk cannot be assigned without a closed
T1 and stale T1 data must not hide the badge.

Correcting a level previously left no record of the old value, who changed
it, or when — thin ground for a customer-visible level that may be disputed.
assignRisk now appends each decision to metadata.riskHistory: the level, the
level it replaced, the timestamp, the user id, and a display name resolved
at assignment time so the trail shows a person rather than a UUID. riskLevel
still carries the current value and always equals the last entry, so
existing consumers are unchanged.

History lives on the existing metadata JSONB column, so no migration is
needed, and the logic sits in assignRisk rather than the shared
completeWithMetadata that adviseDuty and others also use. Re-picking the
level already in force is not recorded — it changed nothing.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-20 07:13:52 +00:00
Hagernesh
21df861979 back date validator 2026-07-20 07:13:52 +00:00
Hagernesh
c0fdffa7ef Marshalling document empty wagon rendering 2026-07-20 07:13:52 +00:00
Hagernesh
eb998bfddd Facility 2026-07-20 07:13:51 +00:00
Hagernesh
ee6ad7da8b Auto arrival train 2026-07-20 07:13:51 +00:00
Hagernesh
a5973de6e1 feat(intercity): a facility only handles the cargo its equipment can lift
Containers need a reach stacker or gantry, so only Indode, Modjo and Dire Dawa
take them. Bulk needs far less and is handled at all five facilities. Having a
facility was previously enough to load anything, so a container booking through
Sebeta or Adama would have been accepted and then had nothing to lift it.

- yard_facilities gains handles_container / handles_bulk, both defaulting true so
  a facility handles everything unless told otherwise; the seeder states the real
  capability.
- The intercity gate now refuses cargo a facility cannot lift, saying which type,
  not just "no facility". canHandleFreight keeps that rule in the resolver so
  callers cannot get it subtly wrong.
- The intercity list resolves each end against the booking's own freight type, so
  the view flags a container booking routed through a bulk-only yard while the
  train is still coming rather than when the load is refused.

Import/export untouched — the gate is still DOMESTIC-only.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-20 07:13:51 +00:00
Marshal
771aa2a605 Add train deactivation feature and import train number management
- Introduced DEACTIVATED status for trains, allowing staff to park trains indefinitely.
- Implemented methods to deactivate and reactivate trains in the TrainBuilderService.
- Added UI components for train deactivation and reactivation in TrainBuilderDetailPage.
- Created a dropdown setting for admin-managed import train numbers, with corresponding migrations.
- Updated yard code length to accommodate soft-delete suffix.
- Enhanced train status handling to include DEACTIVATED state.
2026-07-20 07:04:23 +00:00
marshal
f672d66a92 Merge pull request #826 from Tria-plc/freight_feature/usermanagement
changes
2026-07-20 06:06:06 +03:00
Marshal
4969f62896 changes 2026-07-20 03:05:28 +00:00
marshal
9170c12a73 Merge pull request #808 from Tria-plc/freight_feature/usermanagement
add contract clearance detail page and enhance contract requests fil…
2026-07-19 13:25:44 +03:00
marshal
3ab7a09d2d Merge pull request #807 from Tria-plc/freight_feature/usermanagement
add contract clearance detail page and enhance contract requests fil…
2026-07-19 12:48:00 +03:00
Marshal
de816ea9d4 add contract clearance detail page and enhance contract requests filtering and ui fix 2026-07-19 09:47:36 +00:00
Marshal
5de1853c0d add contract clearance detail page and enhance contract requests filtering and ui fix 2026-07-19 09:37:48 +00:00
marshal
5ac48bf05b Merge pull request #799 from Tria-plc/freight_feature/usermanagement
add per-container handling options for hazardous, reefer, and return…
2026-07-18 22:22:46 +03:00
Marshal
0dead281ce add per-container handling options for hazardous, reefer, and return services
- Introduced new boolean fields (isHazardous, isReefer, isReturn) in UnitDraft and related interfaces to allow individual container handling options.
- Updated emptyUnit function to initialize these new fields.
- Modified GlCreateBookingForm to handle and display these options for each container.
- Adjusted calculations for hazardous, reefer, and return quantities based on the new handling options.
- Updated the schema for container units and booking container lines to include handling options.
- Added migration to support the new return flag in the database.
- Enhanced various components to reflect gross weight calculations, ensuring consistency across the application.
2026-07-18 19:20:45 +00:00
marshal
bc6999cd10 Merge pull request #786 from Tria-plc/freight_feature/usermanagement
split export
2026-07-18 12:20:48 +03:00
Marshal
406fbf6c45 split export 2026-07-18 09:12:52 +00:00
natib21
dc4512c15b gMerge branch 'dev' of github.com:Tria-plc/edr-platform into dev 2026-07-18 09:04:52 +00:00
natib21
d5e87449b5 fix gps 2026-07-18 09:04:34 +00:00
natib21
20eb54521b fix 2026-07-18 08:34:43 +00:00
marshal
f8d5030e97 Merge pull request #782 from Tria-plc/freight_feature/usermanagement
add lashing surcharge for cargo types with hasLashing flag
2026-07-18 02:27:50 +03:00
Marshal
3a1a08b1e1 add lashing surcharge for cargo types with hasLashing flag
add lashing surcharge for cargo types with hasLashing flag
2026-07-17 23:25:53 +00:00
natib21
c8ec0be6b2 Merge branch 'dev' of github.com:Tria-plc/edr-platform into freight/feature/user_management_UI 2026-07-17 15:01:51 +00:00
natib21
6ee771c953 fix search 2026-07-17 15:01:30 +00:00
marshal
2e3e5d20dd Merge pull request #771 from Tria-plc/freight_feature/usermanagement
fix
2026-07-17 16:59:36 +03:00
Marshal
6467173c76 fix 2026-07-17 13:57:59 +00:00
natib21
6f126a2345 Merge branch 'dev' of github.com:Tria-plc/edr-platform into freight/feature/user_management_UI 2026-07-17 12:01:39 +00:00
Hagernesh Tadesse
41d7a04d2f Merge pull request #768 from Tria-plc/intercity-load-unload
feat(intercity): show intercity cargo across every train
2026-07-17 15:00:15 +03:00
natib21
30188c1031 Merge branch 'dev' of github.com:Tria-plc/edr-platform into freight/feature/user_management_UI 2026-07-17 11:59:13 +00:00
natib21
47780c614a fix 2026-07-17 11:59:00 +00:00
marshal
f690b635eb Merge pull request #767 from Tria-plc/freight_feature/usermanagement
Freight feature/usermanagement
2026-07-17 14:40:59 +03:00
Marshal
3a697e12f2 changes 2026-07-17 11:40:25 +00:00
natib21
838ea5efc8 Merge branch 'dev' of github.com:Tria-plc/edr-platform into freight/feature/user_management_UI 2026-07-17 11:30:49 +00:00
natib21
149e4105f4 seed vehicle 2026-07-17 11:30:25 +00:00
Hagernesh
30f48ea37f feat(intercity): show intercity cargo across every train
Intercity bookings never get their own train — they ride whichever import/export
train passes through their corridor — so the work is scattered across other
people's schedules and there was nowhere to see it as a whole. The per-schedule
ride-along panel answers "what can THIS train carry"; this answers "what is
happening to intercity cargo".

Purely additive: the existing ride-along panel and the schedule detail page are
untouched, and loading/unloading still happens there, where the train's position
is confirmed. This is a read-only view that points back to it.

Each row carries both ends' facility status, because a booking whose origin or
destination has no equipment can never be worked there — the operator should see
that while the train is still coming, not when the load is refused. Those
bookings are counted and called out.

New GET /train-scheduling/intercity/bookings; the type is IntercityRideAlongRow,
not IntercityBookingRow, which already means the per-schedule candidate row.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-17 11:27:40 +00:00
Nathnael Wondisha
4abc2aa7ae Merge pull request #765 from Tria-plc/freight/feat/chat-app
Freight/feat/chat app
2026-07-17 14:12:05 +03:00
natib21
35303ece58 Merge branch 'dev' of github.com:Tria-plc/edr-platform into freight/feature/user_management_UI 2026-07-17 11:03:37 +00:00
natib21
57f49a9333 fix 2026-07-17 11:03:23 +00:00
Nathnael
031e5ab292 Merge branch 'dev' into freight/feat/chat-app 2026-07-17 11:01:39 +00:00
Nathnael
1fd46afaaa fix: added chat to the freight api 2026-07-17 10:59:25 +00:00
Hagernesh
a73faecbe3 feat(intercity): raise a GRN when a facility loads or unloads cargo
Every facility raises a GRN — the goods changed hands, whether or not anyone
stores them. What differs is what happens next: Indode has a warehouse, so cargo
left there goes through the existing warehouse flow and accrues storage and
demurrage; Sebeta, Modjo, Adama and Dire Dawa only move cargo between train and
truck, so the handling event and its GRN are the whole record.

facility_handling_events carries that record because warehouse_inventory cannot:
its warehouse/yard/zone are NOT NULL, so a facility with equipment but no
warehouse could never have a row there. inventory_id links the storage record
when the facility does keep the cargo, which is what ties an Indode handover to
its demurrage.

generateGrnNumber moves to common/grn.util.ts so a GRN raised at a facility is
indistinguishable from one raised in a warehouse — the two live in different
tables, and a second generator would let the formats drift.

Recording is best-effort: the cargo moved regardless, so paperwork must never
fail the journey.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-17 10:40:16 +00:00
Hagernesh
a411e0bdd0 feat(intercity): only load/unload where the yard has a facility
Intercity cargo rides a passing train and is handled at the booking's own yards,
so those yards need the equipment to do it — a train stopping somewhere is not
the same as somewhere being able to load it. Loading at an origin or unloading at
a destination without has_facility is now refused, naming the yard.

The check sits inside loadBooking/unloadBooking rather than the intercity
controller wrapper, because recording a checkpoint auto-unloads every booking
destined at that yard (autoUnloadAtYard) and would otherwise route around it.

Import/export are untouched: their cargo is handled at the route's terminal
ports, not at an arbitrary mid-corridor yard, so the gate returns early for
anything that isn't DOMESTIC.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-17 10:40:16 +00:00
Hagernesh
ce3ef15e7c feat(yards): record which yards can load/unload cargo
Intercity cargo is loaded at its origin yard and unloaded at its destination, but
only some yards have the equipment. EDR's facilities are Indode, Sebeta, Modjo,
Adama and Dire Dawa — and the set grows, so it has to be data.

- yards.has_facility marks a yard as a load/unload point; the new yard_facilities
  record says what it can do. Only Indode stores cargo (has_warehouse), so only it
  accrues storage/demurrage — the rest just move cargo on and off the train.
- facility_handling_events records each load/unload and carries its GRN.
  warehouse_inventory cannot: its warehouse/yard/zone are NOT NULL, so a facility
  without a warehouse could never have a row. inventory_id links to the storage
  record when there is one.
- YardFacilitiesService.facilityForYard is the single resolver the handling flows
  share, so they cannot drift on what a facility is.
- The seeder flags EXISTING yards and creates none. The codes are historical and
  do not read like the facility names — Indode is KALITY ("Gelan Multi Purpose
  Port (Indode)") and Sebeta is LEGACY_DEST — so it maps by code. Creating fresh
  INDODE/SEBETA yards would have split data that routes and bookings already
  reference.

Negad is deliberately absent: NAGAD ("DCT/SGDT") is in Djibouti while
NEGAD_FY_BCC is in Ethiopia and inactive, and which one is the intercity facility
is unsettled.

No behaviour change yet — nothing reads has_facility until the gate lands.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-17 10:40:16 +00:00