- Added functionality to cancel contracts, allowing users to provide a reason for cancellation.
- Updated contract statuses to include SUSPENDED and changed CLOSED to COMPLETED.
- Enhanced the UI to reflect the new cancellation option and updated messaging for contract statuses.
- Refactored contract booking actions to accommodate changes in booking logic for ONE_TIME and GENERAL contracts.
- Removed clearance document management from the contract detail page, as it is now handled per booking.
- Introduced a SQL script to reset bookings and train schedules for development purposes.
- Add TransferFulfillModal for fulfilling wagon transfer requests.
- Create TransferRequestFormModal for filing new wagon transfer requests.
- Introduce TransferCloseShortModal for closing requests that cannot be fully fulfilled.
- Develop WagonTransfersPage to manage and display wagon transfer requests.
- Implement utility functions for handling wagon transfer request data and UI components.
- Enhance UI with Mantine components for better user experience.
The customers:* keys were seeded and present in the backoffice constants but
enforced nowhere except reset-password. Customer CRUD sat behind the coarse
edr_freight_app:admin umbrella, and every company read endpoint was unguarded.
Two routes could not be gated on the route alone, because the authority they
need depends on the request BODY, not the path:
- PATCH /companies/:id carries `status` (UpdateCompanyDto extends
PartialType(CreateCompanyDto)), so it both edits fields and blacklists.
- PATCH /company-profiles/:profileId/status is approve, reject, suspend and
blacklist on one route.
Both now take a one-of route guard and assert per-status against a shared
STATUS_PERM map: approving/reactivating needs customers:verify, suspending or
blacklisting needs customers:deactivate. PATCH /companies/:id additionally
requires customers:update when any non-status field is present, so a caller
holding only deactivate cannot rename a company. The backoffice mirrors the
same map so no button is offered that the server would reject.
GET /companies/:companyId/documents is left authenticated-only with the split
in the handler: it is dual-audience. The portal reads its own documents during
onboarding, and the contract-request detail page (gated on contracts:view)
reads the applicant's. Gating it on customers:view alone would have 403'd
customers on their own documents and blanked the contract reviewer's panel.
The two by-company customer-view reads take a one-of guard for the same reason
— otherwise a staffer granted only customers:view gets a detail page whose tabs
403 individually.
Frontend: the customers routes were sidebar-filtered but not wrapped in
RequirePermission, so direct URL navigation rendered them for anyone.
Verified: freight-api type-check clean; backoffice type-check unchanged from
HEAD (pre-existing errors only); 25 tests pass across the companies and
freight-permission suites. Not exercised against a running API.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
- Implement full train import journey with six container bookings filling a 54-wagon train.
- Create tests for split offer and rebooking scenarios, handling payment expiry and waiting list promotions.
- Add tests for handling waiting bookings expiration when the train is full.
- Implement tests for reopening booking windows after expired reservations.
- Seed database with necessary corridor data for import flows, including yards, container types, locomotives, and rates.
- single-row Assign vehicle uses the full single-record flow (details + containers)
- release() rejects exit containers not assigned to the departing truck
- weighing modal offers only the selected truck's assigned containers
Multi-truck self-haul had no list anywhere on the warehouse side. The ops
dashboard counted trucks on site and offered no way to open the list, and
the inventory table showed a blank plate on exactly the bookings that have
several trucks: it read booking.customer_truck_plate_number, which
multi-truck self-haul leaves null because plates live in
customer_truck_assignments. Booking BK-2026-000033 has a truck and a driver
on file and displayed neither.
Adds a Trucks on Site page listing every truck that has arrived and not yet
departed, across bookings, with plate, driver, booking, customer, containers
and dwell time. It covers both haulage paths because the gate does — a
customer's own truck and an EDR last-mile truck reach the same barrier — and
flags anything sitting over four hours. It lives under Warehouse Management
rather than Imports or Exports, since the yard is not per-direction.
The inventory queries now read plates and drivers from the assignments and
keep the booking columns as the fallback for single-truck bookings written
before that table existed.
Both new statements were EXPLAIN-validated against the live schema; the
plate fix returns the data that was previously null.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
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>
- Added status and cargo filters to the ShipmentRequestsPage.
- Implemented date range filtering for preferred dates.
- Introduced sorting options for shipment requests based on submission date and reference.
- Enhanced the display of shipment request details, including status badges and customer information.
- Updated the UI to include a search input with clear functionality and improved layout for filters.
feat: add equipment return option in new shipment form
- Introduced a toggle for equipment return in the NewShipmentPage.
- Updated form schema to include field for container contracts.
- Enhanced user experience with visual feedback on the equipment return selection.
fix: update booking DTO to include equipment return option
- Added field to CreateBookingUnderContractDto for per-shipment override.
- Updated related types and schemas to accommodate the new field for better contract handling.
- Updated the logic in to ensure that a booking only earns its gate pass once the freight charges are settled.
- Added logging for bookings that have not settled freight payment when securing gate passes.
- Modified seeders to ensure that bookings have associated company profiles to prevent data inconsistencies.
- Updated freight permissions to include new clearance actions for bookings.
- Enhanced the UI to reflect changes in the clearance process, including new shipment request pages and improved status handling in the clearance action panel.
- Adjusted the contract clearance list to accommodate both customs contracts and shipment bookings.
- Improved the handling of GENERAL contracts in various components to ensure proper booking flow and visibility.