Commit Graph

4922 Commits

Author SHA1 Message Date
marshal
d4bc419d43 Merge pull request #1356 from Tria-plc/freight_feature/usermanagement
feat(bookings): two-level clearance charges (port + misc) billed to c…
2026-08-20 08:49:26 +03:00
Marshal
c38fcff00d feat(bookings): two-level clearance charges (port + misc) billed to customer with invoices 2026-08-20 05:48:16 +00:00
Abubeker Yasin
a00606823e Merge pull request #1355 from Tria-plc/dev
Merge dev to alpha
2026-08-20 08:45:14 +03:00
Nathnael
62f7b91315 feat(exports): dataset-driven table export, starting with bookings
Adds a parallel export system the reports module can also draw on. A dataset
describes a table's exportable fields — including related-entity detail the
list page never shows — and the engine assembles a query from whichever fields
the caller picked.

GET /exports                 catalog (metadata only; select/requires never ship)
GET /exports/:key/count      exact row count + per-format caps
GET /exports/:key/download   csv | xlsx | pdf

Two invariants carry the design:

- Every lazy join is a LEFT join, and ExportJoin has no 'kind' field to make
  anything else expressible. An inner join added because a checkbox was ticked
  would change the rowset, so two exports of the same filters would disagree on
  their row count.
- Because of that, the count cannot depend on field selection, so /count runs
  base + alwaysJoin only and is exact rather than an estimate. Verified: count
  and the delivered file both report 223 rows.

One-to-many relations (a booking's containers) aggregate in a correlated
subquery rather than joining, so a row can never multiply.

Export rides each dataset's existing view permission — no new permission keys
and no seeder change. Sensitive columns are simply never declared as fields:
raw gateway payloads, signature blobs, error dumps, raw jsonb snapshots,
internal user UUIDs and review notes are all absent by construction.

bookings ships 77 fields across 10 groups. scripts/validate-export-datasets.ts
EXPLAINs every dataset's widest query, its count query, and each field on its
own against the real database — the per-field pass is what catches a field
referencing a join it forgot to declare, which otherwise only fails when that
one field is picked alone.
2026-08-20 05:29:04 +00:00
Nathnael
ce90be5c88 fix(reports): stop the 'first N rows' option failing on large exports
The export path used one number for two different things: the format's hard
row cap, and the caller's explicit 'give me the first N rows'. Because
resolveExportCap() returned min(requested, formatCap) and runAll() then threw
when the result reached it, picking 'Records: First 100' in the export dialog
400'd on any report with more than 100 rows — the user asked to be truncated
and got an error instead.

Splits them: formatRowCap() is the hard, non-caller-controllable ceiling that
still throws when exceeded (a silently short file hides missing rows), while
resolveRowLimit() is the deliberate truncation and is honoured by slicing.
Verified against a 223-row dataset: limit=5 now returns 5 rows, and no limit
returns all 223.
2026-08-20 05:28:51 +00:00
marshal
d04f35539a Merge pull request #1354 from Tria-plc/freight_feature/usermanagement
Freight feature/usermanagement
2026-08-20 08:28:12 +03:00
Marshal
c138da6137 feat(bookings): two-level clearance charges (port + misc) billed to customer with invoices 2026-08-20 05:24:57 +00:00
Nathnael
fc2b5ee0e3 refactor(reports): export through the shared tabular writer
Completes the writer extraction whose other half landed in fb21ad154.
reports.controller now builds a TabularDoc and calls TabularExportService,
so report-export.service.ts and report-export-request.util.ts are dead and
removed — HEAD was carrying both copies with the controller still on the old
one.

Reports gain CSV for free, and the PDF path now passes buildTabularFallbackPdf
as its fallback: previously it passed none, so a box without Chromium silently
returned PdfRenderService's ~900-character generic text dump instead of a
table. Adds a spec covering the CSV writer's quoting of embedded commas and
double quotes — the reason this uses ExcelJS's csv writer rather than a
hand-rolled join.
2026-08-20 05:16:15 +00:00
Stephanos A.
c52b9229c4 Merge pull request #1353 from Tria-plc/alpha
Alpha
2026-08-20 07:57:24 +03:00
Stephanos A
5d6a21b6be Merge branch 'alpha' of github.com:Tria-plc/edr-platform into alpha 2026-08-20 07:46:33 +03:00
Stephanos A
f93b3cd993 feat: add seat conflict assertion for route bookings 2026-08-20 07:46:15 +03:00
Marshal
4adb53b486 fix issue 2026-08-20 04:27:16 +00:00
marshal
e2d32defc0 Merge pull request #1352 from Tria-plc/freight_feature/usermanagement
Freight feature/usermanagement
2026-08-20 07:04:21 +03:00
Marshal
a58157a7ee feat(bookings): show per-document upload/review audit and download icon on clearance detail 2026-08-20 04:03:27 +00:00
marshalyordanos
b11de162c1 Merge branch 'freight_feature/usermanagement' of github.com:Tria-plc/edr-platform into freight_feature/usermanagement 2026-08-20 07:00:55 +03:00
Marshal
8a2b9306dc feat(bookings): show per-document upload/review audit and download icon on clearance detail 2026-08-20 03:59:14 +00:00
marshalyordanos
6aeab9d538 Merge branch 'freight_feature/usermanagement' of github.com:Tria-plc/edr-platform into freight_feature/usermanagement 2026-08-19 23:11:22 +03:00
Hagernesh
fc55f48371 feat(eims): implement POST /v1/bulkRegister
New endpoints:
  POST invoices/eims/bulk-register  { invoiceIds: [...] }  — trigger
  POST eims/webhook/bulk-register                          — MoR's callback

Fundamentally different shape from single register: bulkRegister
answers only {conversationId, status:202} immediately: MoR processes
the array asynchronously and pushes the real per-invoice results
(a mix of accepted/rejected in one array, per the collection's own
examples) to a webhook configured out of band. So this ships as two
halves that don't share a call stack — EimsBulkRegistrationService.
registerBulk() reserves a contiguous block of counters (durable
reservation, same doctrine as single register, extended to N items)
and submits; handleBulkCallback(), invoked by the new
EimsWebhookController whenever MoR gets around to it, settles.

New EimsSystemState.inFlightConversationId is the bulk equivalent of
inFlightInvoiceId — a whole batch outstanding, not one invoice — and
the two markers block each other since they share the same counter
sequence. The conversation id isn't known until MoR's 202 arrives, so
reservation stamps a locally-generated placeholder first (same
commit-before-the-network-call reasoning as single register), then
swaps it for MoR's real id right after — the only value the callback
can actually use to find the batch again.

Only the first invoice in a bulk batch chains via PreviousIrn — every
other item gets an empty string, matching the collection's own
two-invoice example exactly (MoR doesn't expect a batch to chain to
IRNs that don't exist yet at submission time).

Webhook has no auth (MoR has no JWT to send) — the conversation id
embedded in the payload is what stands between this and a forged
callback: an item only ever touches an invoice actually holding that
exact id, and an unknown id is logged and ignored, never applied.

Migration 3580000000000: eims_system_state.in_flight_conversation_id,
invoices.eims_bulk_conversation_id (tags which batch an invoice was
submitted in, so a stuck batch — webhook never arrived — can be found
and reconciled by conversation id). Applied to dev DB and recorded in
freight.migrations directly (idempotent IF NOT EXISTS DDL).

Not live-testable from this sandbox (no route to MoR's real gateway).
Signing the whole array as one envelope, the way single /v1/register
was confirmed live to need despite the collection's raw example
showing no envelope, is the reasonable extension of that confirmed
behavior, not a blind guess — but it has not itself been exercised
against the real gateway. Left for the first live bulk attempt to
confirm, same as every other MoR-facing assumption this integration
has made.
2026-08-19 18:35:09 +00:00
Marshal
174bc1a7bd Implement new feature for user authentication and improve error handling 2026-08-19 17:07:48 +00:00
Nathnael
fb21ad1541 feat(WIP): filtering, exporting and more reports 2026-08-19 13:51:18 +00:00
Abubeker Yasin
4fcb8d02f4 Merge pull request #1350 from Tria-plc/alpha
fix: ( fayda ) reject a second booking for the same identity on one d…
2026-08-19 15:33:42 +03:00
Abubeker Yasin
014b1a6d47 fix: ( fayda ) reject a second booking for the same identity on one departure 2026-08-19 15:28:10 +03:00
Nathnael Wondisha
8e11b3a168 Merge pull request #1349 from Tria-plc/freight/nati-2
Freight/nati 2
2026-08-19 13:17:07 +03:00
Nathnael
e964a9b8f4 chore: more reporting 2026-08-19 10:14:46 +00:00
Nathnael
54f52f077c fix(page-header): cap title/subtitle width
Title and subtitle grew unbounded, relying only on flex-squeeze from action
buttons to ever truncate. Caps the container at 640px and gives the
subtitle the same truncate treatment the title already had, so a long
page title or description (report descriptions in particular) always
ellipses instead of stretching the header.
2026-08-19 10:12:16 +00:00
Nathnael
6687def4af feat(reports): use the shared FilterBar instead of ReportFilters
Swaps ReportView's bespoke ReportFilters for the same FilterBar/useFilters
combo BookingRequestsPage uses — pills, saved-view-ready state, URL sync.
toFilterDefs() maps the report catalog's own filter vocabulary
(daterange/date/select/multiselect/text) onto FilterDef, matched against
every report definition's backend param handling. The search box only
shows for reports that actually implement `search` server-side, so it's
not a dead control on the rest. ReportFilters.tsx is now dead, removed.
2026-08-19 10:12:03 +00:00
Nathnael
7c56607582 fix(filters): honor a date filter's own operator, not always "between"
DateBody always initialized its op state to DEFAULT_OP.date ("between"),
ignoring a def's `operators` restriction. A single-operator exact-date
filter (e.g. `operators: ["before"]`) opened on the range UI with no way
off it, since OperatorSelect hides itself when there's only one choice.
Default to the def's first allowed operator instead.
2026-08-19 10:11:48 +00:00
marshal
62ff18a301 Merge pull request #1346 from Tria-plc/freight_feature/usermanagement
Freight feature/usermanagement
2026-08-19 13:11:09 +03:00
Marshal
ad479bdcf2 issue fix 2026-08-19 10:10:04 +00:00
marshalyordanos
f2a1b22ad2 Merge branch 'freight_feature/usermanagement' of github.com:Tria-plc/edr-platform into freight_feature/usermanagement 2026-08-19 12:32:32 +03:00
Marshal
13f66dfb66 changes 2026-08-19 08:41:51 +00:00
Marshal
f4a654f273 Merge branch 'dev' of github.com:Tria-plc/edr-platform into freight_feature/usermanagement 2026-08-19 06:42:36 +00:00
Marshal
cd1e9c38a7 feat: add new audit endpoints for consolidation approvals and invoice management 2026-08-19 06:42:19 +00:00
Abubeker Yasin
fbba4add73 Merge pull request #1344 from Tria-plc/alpha
fix: ( fayda ) block one Fayda identity from verifying multiple passe…
2026-08-19 09:26:34 +03:00
Abubeker Yasin
1f38b5a3c6 fix: ( fayda ) block one Fayda identity from verifying multiple passengers 2026-08-19 09:25:51 +03:00
marshal
2942ad8d75 Merge pull request #1342 from Tria-plc/freight_feature/usermanagement
feat: implement parity checks for 20ft container bookings to ensure e…
2026-08-19 09:21:43 +03:00
Marshal
e6e4c07077 feat: implement parity checks for 20ft container bookings to ensure even numbers 2026-08-19 06:20:47 +00:00
marshal
c80d9c5abc Merge pull request #1339 from Tria-plc/freight_feature/usermanagement
feat: implement consolidation approval process for shared-wagon bookings
2026-08-18 17:12:28 +03:00
Marshal
db64a1878b feat: implement consolidation approval process for shared-wagon bookings
- Add migration for consolidation approvals table and status enum
- Create ConsolidationApprovalService to handle approval logic
- Implement repository for managing consolidation approvals
- Add entity for consolidation approval with necessary fields
- Develop frontend components for displaying and managing consolidation approvals
- Create tests for consolidation approval service to ensure correct behavior
2026-08-18 13:59:15 +00:00
marshal
c1150f8af2 Merge pull request #1337 from Tria-plc/freight_feature/usermanagement
Freight feature/usermanagement
2026-08-18 16:19:34 +03:00
Abubeker Yasin
861900dc1b Merge pull request #1338 from Tria-plc/alpha
feat: ( audit ) resolve the actor from the session and audit all back…
2026-08-18 16:19:15 +03:00
Marshal
40904049cf feat: implement consolidation approval process for shared-wagon bookings
- Add migration for consolidation approvals table and status enum
- Create ConsolidationApprovalService to handle approval logic
- Implement repository for managing consolidation approvals
- Add entity for consolidation approval with necessary fields
- Develop frontend components for displaying and managing consolidation approvals
- Create tests for consolidation approval service to ensure correct behavior
2026-08-18 13:17:55 +00:00
Nathnael Wondisha
5e96394450 Merge pull request #1336 from Tria-plc/freight/nati-2
lifecycle
2026-08-18 16:14:38 +03:00
Nathnael
dd6df306f2 lifecycle 2026-08-18 13:11:48 +00:00
Nathnael Wondisha
065f34aa67 Merge pull request #1335 from Tria-plc/freight/feat/yard-loc-ac
feat: yard scoping to position
2026-08-18 15:57:03 +03:00
Nathnael
6823a32fee feat: yard scoping to position 2026-08-18 12:55:11 +00:00
Nathnael Wondisha
bd5d6d72b5 Merge pull request #1334 from Tria-plc/freight/feat/foreign-investors
fix: approval window
2026-08-18 15:51:45 +03:00
Marshal
22a3fb98ee feat: Implement consolidated booking functionality
- Added support for viewing and managing consolidated bookings in BookingRequestDetailPage.
- Enhanced BookingRequestsPage to display paired bookings in a single row.
- Introduced pairedDecision method in bookings service to handle decisions for both halves of a consolidated pair.
- Updated contracts service to include methods for manual consolidation of odd-20ft bookings.
- Created new components for selecting and editing consolidation partners.
- Added tests for paired decision logic and manual consolidation scenarios.
- Updated UI to reflect changes in booking handling and provide user feedback for odd container counts.
2026-08-18 12:50:35 +00:00
Nathnael
cbcc9a02e6 fix: approval window 2026-08-18 12:44:02 +00:00
Abubeker Yasin
0eb64ad9a1 feat: ( audit ) resolve the actor from the session and audit all backoffice mutations 2026-08-18 15:13:22 +03:00