Commit Graph

990 Commits

Author SHA1 Message Date
Nathnael
dcc94643d0 feat(companies): enforce customers:* permissions on customer endpoints
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>
2026-07-23 13:48:42 +00:00
Nathnael
346d4718bf fix(backoffice): scope permission copying to the selected organization
"Copy permissions from" listed every position type in every
organization, because the dropdown read an unfiltered GET
/position-types. IAM exposes no organization-scoped route and carries no
organizationId on a position type, so the list is now narrowed
client-side to the built-in (isSystem) types plus those whose unit
belongs to the selected organization, with the type being edited
excluded.

Also in position management:

- Invalidate every position-type cache key root after a mutation. React
  Query matches prefixes element by element, so ["position-type"] never
  reached ["position-types-common", ...] and the department pickers kept
  serving a stale list. invalidatePositionTypeQueries() covers all three
  roots and is shared by the hook and the form.
- Drop getByOrganizationId and getCommonTypesByOrganizationId. Both
  issued the same requests as their unit counterparts and had no callers.
- Surface errors that were being swallowed. Three mutations had empty
  onError handlers, hiding IAM's 403 for built-in position types, and
  CreatePositionForm's bare catch discarded the reason for every failure.
- Move organization and unit into the zod schema so they validate with
  translated messages and inline errors instead of an ad-hoc toast, and
  keep submit disabled through the permission-assignment call that
  follows the save.
- Report the two outcomes the form used to hide: a save that succeeded
  while permission assignment failed, and clearing every permission,
  which assign-seconds-for-first cannot express.
- Fix the list page's loading and error states, which rendered the
  "Add User" string as a spinner, ignored the unit-scoped query, and
  left the export button stuck after a failed download.
- Halve PermissionSearch's requests. It fetched 50 rows, read the total
  off the response and immediately refetched, and it re-filtered results
  on the undebounced term, blanking the list while typing.

Remove the three record toggles. They never worked: IAM's
PositionTypeConfiguration holds only { id, organizationId,
positionTypeId, timeframe } in every published build, canAssignRecord
and canCreateBankRecord exist nowhere in the package, and the global
ValidationPipe runs with forbidNonWhitelisted, so every write was a 400.
The reads were broken too, passing a positionTypeId to a route that
filters on organizationId. A TODO records where the real flag lives:
PositionConfiguration.canReceiveRecord, keyed by positionId.

Delete ActionsColumn.tsx, which had no references.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-23 13:42:08 +00:00
Nathnael
063a8799f5 fix(backoffice): make position type optional for unit departments
Creating a department under a unit required picking a position type,
which is not always known at that point. Sub-department creation is
unchanged and still requires one.

PositionPayload.positionTypeId becomes optional and the field is omitted
from the request body when unset rather than sent as an empty string.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-23 13:41:39 +00:00
Nathnael
48f1d08faf Merge branch 'dev' into freight/feat/fixes-v1 2026-07-23 11:51:04 +00:00
Nathnael
d47e3ae4f5 docker exefix: reimplement the admin form 2026-07-23 11:48:08 +00:00
Hagernesh
73f36fe46e Export interchange document — a generation error after Djibouti unloading failed the whole request and left the document missing until a manual rerun. Generation is now best-effort (unload never fails on paperwork) and reruns backfill the document. 2026-07-23 11:31:44 +00:00
marshal
4df17f36ce Merge pull request #935 from Tria-plc/freight_feature/usermanagement
changes
2026-07-23 14:07:03 +03:00
Marshal
13609f8d59 changes 2026-07-23 11:06:10 +00:00
Nathnael
cbff529e31 feat: rebuild the admin pages 2026-07-23 07:40:48 +00:00
marshal
8d51425f90 Merge pull request #928 from Tria-plc/freight_feature/usermanagement
fix configratio
2026-07-23 09:22:29 +03:00
Marshal
1576fcdc45 fix configratio 2026-07-23 06:21:51 +00:00
marshal
f5f41e0a5a Merge pull request #926 from Tria-plc/freight_feature/usermanagement
implement freight permissions for trains, wagons, and routes
2026-07-23 08:57:59 +03:00
Marshal
129a4d1d0a implement freight permissions for trains, wagons, and routes 2026-07-23 05:57:14 +00:00
marshal
edfd0a859d Merge pull request #925 from Tria-plc/freight_feature/usermanagement
add e2e test
2026-07-23 08:27:29 +03:00
Marshal
2481f43f1f add e2e test 2026-07-22 23:44:32 +00:00
marshal
091dd0bfa3 Merge pull request #923 from Tria-plc/freight_feature/usermanagement
Freight feature/usermanagement
2026-07-23 00:02:55 +03:00
Marshal
b0f561a935 Add end-to-end tests for import corridor flows
- 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.
2026-07-22 21:02:17 +00:00
Marshal
5709801590 enhance shipment form and booking process 2026-07-22 17:25:01 +00:00
Hagernesh
15540f78e2 fix(portal): Table has no size prop in BulkTruckUploadModal 2026-07-22 14:11:58 +00:00
Hagernesh
ec066f3d29 Fix:
Added assertCapacity() checks before saving (matches single receive)
Added applyCapacityDelta() after save to increment counters
Now validates warehouse → yard → zone capacity hierarchy
Single receive already had both checks; bulk receive was gap.
2026-07-22 13:18:36 +00:00
Nathnael
919c3a84ff Merge branch 'dev' into freight/feat/fixes-v1 2026-07-22 13:05:19 +00:00
Nathnael
9baffb6439 refactor: remove duplicate header components and consolidate layout 2026-07-22 12:57:56 +00:00
Hagernesh
8dc4dd585e feat(procurement): validate acquisition lease fields and enforce bulk-receive capacity
Reject lease start/end and monthly payment on PURCHASE acquisitions (create and
update, validated against the resulting record). Add asset_acquisitions.item_name
column + migration. Enforce warehouse/yard/zone capacity on bulk receive and apply
capacity-counter deltas on save. Adds acquisition-guard spec.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-22 12:15:47 +00:00
Nathnael
8389b2735d fix: sidebar in user management 2026-07-22 11:54:54 +00:00
marshal
2f58986f0a Merge pull request #907 from Tria-plc/freight_feature/usermanagement
chnages
2026-07-22 14:04:45 +03:00
Marshal
2aa405d8e4 chnages 2026-07-22 11:04:17 +00:00
marshal
4f0071894d Merge pull request #906 from Tria-plc/freight_feature/usermanagement
Freight feature/usermanagement
2026-07-22 13:58:28 +03:00
Hagernesh Tadesse
11437cfd35 Merge pull request #905 from Tria-plc/testfixes
Fixes
Download Excel template with instructions
Parse uploaded file, preview trucks
Validate container assignments (1x40ft OR 2x20ft per truck)
Commit bulk upload in one call
2026-07-22 13:54:59 +03:00
Marshal
996df4a7f8 chnages 2026-07-22 10:52:27 +00:00
Marshal
b5546b9d6f feat(freight-me): add position type lookup and enrich user profile with position type 2026-07-22 08:57:47 +00:00
Nathnael Wondisha
0fb8eba4df Merge pull request #899 from Tria-plc/freight/feat/fixes-v1
Freight/feat/fixes v1
2026-07-22 11:02:11 +03:00
Hagernesh
5395d4f346 Customer Name + Customer ID columns removed from the Export Marshalling Document 2026-07-22 08:01:15 +00:00
Hagernesh
19906b273e fix(operations): prefill booking context on single assign; enforce per-truck container loads
- 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
2026-07-22 07:24:14 +00:00
Nathnael
8d4b133277 fix: port 2026-07-22 07:20:51 +00:00
Nathnael
179d6eea9e Merge branch 'dev' into freight/feat/fixes-v1 2026-07-22 07:20:08 +00:00
Marshal
ed3c8307bb refactor(train-scheduling): rename and restructure container movement logic 2026-07-21 23:49:10 +00:00
Marshal
d25612c2f0 feat(user-management): implement staff reference permissions for various controllers and update sidebar permissions 2026-07-21 23:25:43 +00:00
Marshal
835c9e111c feat(train-scheduling): implement container movement between wagons
- Added functionality to move containers between wagons in the train scheduling system.
- Introduced  API endpoint and service method to handle container movement.
- Updated  component to support drag-and-drop for rearranging containers.
- Enhanced  to allow moving containers to other wagons via a context menu.
- Implemented UI feedback for container movement actions, including loading states and success/error notifications.
- Updated relevant types and constants to accommodate new container movement logic.
- Added tests for the rule engine to ensure proper handling of hazardous bookings.
2026-07-21 23:02:06 +00:00
Marshal
00a81fda15 Merge branch 'dev' of github.com:Tria-plc/edr-platform into freight_feature/usermanagement 2026-07-21 13:24:21 +00:00
Marshal
42e5dd3a26 changes 2026-07-21 13:24:05 +00:00
marshal
ca8a7602ef Merge pull request #876 from Tria-plc/freight_feature/usermanagement
changes
2026-07-21 15:15:19 +03:00
Marshal
07c390a3b2 changes 2026-07-21 12:14:47 +00:00
Hagernesh
1fae0752a0 Ticket: Warehouse dashboard Needs Attention cards — fix trucks-on-site aging counters (EDR trucks, UNLOADED status) and make each card open the exact list behind its count via new inventory drill-down filters. 2026-07-21 11:03:40 +00:00
Hagernesh
b616f520ae feat(fleet): validate vehicle plate numbers against a letters-and-digits format
Plate, power-plate and trailer accepted any free text — a vehicle could be
saved with a plate of "assadasd". They must be letters, a hyphen, then digits,
like ET-9875 or AA-8642.

The server now enforces it on CreateVehicleDto (and UpdateVehicleDto via
PartialType): each plate is trimmed and upper-cased, then matched against
^[A-Z]{2,3}-\d{2,6}$, so "et-9875" is accepted and stored as ET-9875 while an
empty optional trailer/power plate still passes.

The fleet form gains the same check inline: FleetFormFieldDef takes an optional
pattern, the dialog tests it on submit against the upper-cased value, and the
vehicle config points plate and trailer at a regex that mirrors the server's.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-21 11:03:40 +00:00
Marshal
9ca4075c1e Implement intercity document handling and rejection notes for contracts 2026-07-21 10:39:21 +00:00
Marshal
6f8456631c Implement intercity document handling and rejection notes for contracts 2026-07-21 10:20:36 +00:00
Marshal
ceb32e0a80 Implement intercity document handling and rejection notes for contracts 2026-07-21 10:15:18 +00:00
Hagernesh
92a0cc197b feat(fleet): make the dashboard stat cards link to their detail
The four headline cards — total vehicles, drivers, fuel spend, maintenance —
were static numbers with no way through to the list behind them. Each now takes
an optional href and, when set, wraps in a link to its detail page (vehicles,
drivers, fuel purchases, maintenance). A card without an href stays exactly as
before. The Card is wrapped rather than turned into a link so Mantine's Card
typing stays clean.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-21 09:22:47 +00:00
Hagernesh
64a4bcbd5c feat(warehouse): make the ops KPI cards link to their detail
The dashboard counters were static — "Trucks on-site" showed a number with no
way to open the list behind it, same for the others. KpiStrip now takes an
optional href per item: a cell with one becomes a link (pointer, hover tint,
aria-label) and a cell without one stays exactly as before, so every existing
strip is unaffected.

The warehouse ops strip wires each card to its detail: trucks on-site to the
Trucks on Site page, received-today and pending-inspection to the inventory
board filtered to RECEIVED, items-aging to the inventory board.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-21 09:22:46 +00:00
Nathnael
649316070d fix(otp): fall back to email for foreign phone numbers
The SMS gateway is domestic-only, but OTP sends fanned out to any phone
on the account - a foreign number meant a code queued into the void
while the response claimed success. isDomesticPhone (+2519/+2517 E.164)
now gates SMS: dual-channel sends with a foreign phone go email-only
(the phone stays on the row so verify still matches it), and a
phone-only foreign target still tries SMS as the only route. The
staff-triggered reset exposes phoneIsDomestic so the backoffice disables
the SMS channel with an explanation, and the API refuses the channel
directly for foreign numbers.

EDRFREIGHT-186
2026-07-21 09:09:45 +00:00