Commit Graph

43 Commits

Author SHA1 Message Date
Nathnael
25d13baa6f chore: reporting and filtering 2026-08-21 09:25:21 +00:00
Nathnael
2ec818e590 Merge branch 'dev' into freight/nati-2
# Conflicts:
#	apps/edr-freight-api/src/app.module.ts
#	apps/edr-freight-api/src/seed/freight-permissions.registry.ts
#	apps/edr-freight-web/backoffice/src/components/layout/sidebar-sections.tsx
#	apps/edr-freight-web/backoffice/src/constants/URLS.ts
#	apps/edr-freight-web/backoffice/src/lib/permissions.ts
2026-08-20 11:33:19 +00:00
Nathnael
1f11c3a4ca feat(export-ui): mount the export button on the eight remaining list pages
customers, contracts, invoices, payments, train schedules, and locomotives /
trains / wagons via the fleet page's config.

FilterBar pages pass controls.params into the children slot. The four pages
still on ad-hoc filtering pass their own hand-built filter object instead,
which is why ExportButton takes plain params rather than a UseFilters — it
would otherwise have been blocked behind migrating those pages. FleetResource
serves seven slugs from config, so it gets an optional exportKey there and
renders nothing for the four slugs with no dataset yet.

Auditing each page's real filter keys against the dataset declarations turned
up three gaps where an on-screen filter would have silently not applied to the
export: invoices sends a singular "status" (the dataset only had the
multiselect "statuses"), contracts sends paymentCurrency, serviceTypeId and
route origin/destination, and train schedules sends freightType. Added all of
them — contract routes filter through EXISTS on contract_routes since they are
one-to-many, and train-schedule freightType through EXISTS on the bookings
aboard, matching the list service.

Verified in the browser: the button renders on each page, and the invoices
dialog follows that page's own filter object — selecting Paid moves the count
from 126 to 100, which matches the database. Filter pass-through checked
against the database for invoices, payments, wagons, contracts and train
schedules.
2026-08-20 07:17:25 +00:00
Nathnael
0f11d9518f feat(backoffice): flag customers whose registration was typed, not fetched
Two kinds of customer reach approval with a registration nobody checked: a
co-operative union or farm, which holds no trade licence, and a foreign
investor, whose licence comes from the Investment Commission rather than the
trade registry. Both were reviewed on screens that read exactly like an
eTrade-verified company's, with only a small Registration field naming the
difference.

They now carry an orange "Manual entry" badge in the customers list and beside
the company name, and their overview opens with an alert saying the name,
registration and address below are the customer's own statement — pointing the
reviewer at the paper that stands in for the licence (the co-operative
certificate, or the investment licence) before approving. Approval itself is
not blocked.
2026-08-18 08:45:38 +00:00
Nathnael
7143ba1040 feat(customers): notify marketing on returned changes, name actors in history
Three gaps on the backoffice customer detail page:

- Rejecting a change request or sending it back for correction notified
  nobody. Adds CompanyNotifierService.changeRequestReturned, which pings
  the customer desk with the reviewer, the outcome and the note. Marketing
  joins that desk via customers:view + customers:get_notification in the
  role preset — grants still come from the IAM UI, the preset only sets
  the default for new environments.
- submitted_by / reviewed_by / actor_id were stored but never resolved, so
  the History tab could say what changed but never who asked or who sent
  it back. Resolves them through a shared iam-user-name util (deduped from
  the private copy in contract-document-history.service) and renders
  "Requested by" / "Sent back to marketing by" lines. The
  changes_requested badge is relabelled to match the workflow.
- "View" opened an in-page modal one document at a time. Adds
  openFileInNewTab, which opens the tab inside the click gesture and fills
  it once the authenticated fetch resolves, and an "Open all" button that
  loops over the documents table so every file lands in its own tab.
2026-08-17 12:38:06 +00:00
Nathnael
d94c444a51 feat(filter-bar): fix pageSize bug, add client bridge, migrate 4 more pages
fix(useFilters): pageSize was a hardcoded constant, never read from the
URL, with no setter — the DataTable/RuleEngineListFooter page-size
dropdown silently did nothing on every page using the filter bar,
including the already-shipped ContractRequestsPage pilot. Added a `size`
URL param (mirrors `page`), `setPageSize`, and wired
`tableProps().onPaginationChange` to route page-size vs page-index
changes to the right setter instead of only ever calling setPage().

feat(clientFilter): applyClientFilters — the Family-B bridge the plan
called for. Generalizes ListControls'/useListControls' one hardcoded
search box + one date range to every FilterDef, matched against
`row[def.key]`. Reuses matchesDayRange/toDayString from
hooks/useListControls.ts (imported, not duplicated) so the inclusive-
range/timezone-safe semantics stay defined in exactly one place. Lets a
page ship the full pill-bar UI immediately and flip to server-side
filtering later by deleting one function call — no endpoint changes
required up front.

Migrated to the filter bar (mechanical, pattern established by
ContractRequestsPage):
- WarehouseInvoicesPage, LoadedInventoryPage, TrucksOnSitePage — Family B
  (ListControls/useListControls → FilterBar + applyClientFilters)
- CustomersPage — Family A (useState bag → useFilters), no filter pills
  needed here (search + sort only), the SegmentedControl "view" stays
  page-level tab state (like ContractStatusTabs), not a filter pill —
  it now resets the page via controls.setPage(1) on change, the same
  hazard useFilters already guards its own filters against
2026-08-14 14:03:33 +00:00
Nathnael
ab5a4117df fix: ui 2026-08-14 07:04:57 +00:00
Nathnael
89cdc0ad06 style: ui fixes 2026-08-13 13:46:46 +00:00
Nathnael
d261d6ea7c style: inter module integration 2026-08-13 06:54:54 +00:00
Nathnael
068ee49a9f style: invoice 2026-08-12 09:33:21 +00:00
Nathnael
d6e349f329 feat(companies): onboard co-operative unions and farms
They hold a TIN but no business licence, so there is no eTrade record to look
their registration up in. A checkbox on the first wizard step marks them, and
everything that assumed a trade licence bends around it:

- The company step replaces the eTrade lookup with typed registration details
  — name, region, zone, woreda, kebele, house number — required exactly because
  they are now on screen. applyEtradeSourcedFields skips the lookup rather than
  failing it, so what the customer sends is what is stored.
- No freight-forwarder role. Forwarding is licensed work, so the option is not
  offered, and the API refuses it at start-onboarding and at every later
  role-add rather than letting approval fail on a document they cannot produce.
- No per-role business-licence upload, client-side or in the completion gate.
- Their own document set (company_onboarding_documents_cooperative) merges on
  top of the nationality one, admin-managed like every other set. Nationality
  wins a fileKey collision so no slot renders twice, and the DARS paper is not
  injected into it — the set it merges onto already carries one.
- The owner is typed in full; with no eTrade manager on file the licence
  comparison reports "nothing to compare against", which backoffice now
  explains rather than leaving as a bare dash.

Stored as an attributes flag, not a column: everything it changes is
behavioural, and nothing queries or joins on it.
2026-08-11 12:54:51 +00:00
Nathnael
02d9debe1d refactor(freight-backoffice): review one identity and the eTrade owner match
- Replace the general-manager rows with owner rows across the customer
  detail, contract cards, types and service mappings.
- The identity card names its subject ("Verifies for this company") and shows
  whether the asserted owner matches the manager on the eTrade licence:
  amber when it does not, green when it does, dimmed when eTrade named
  nobody. Advisory only, since the comparison is a fuzzy transliteration
  match.
- A missing delegation letter now keys on poaDeclared === "yes".
- Keep the three general-manager labels in the change-request label map so
  historical requests still render readable field names.
2026-08-11 11:54:05 +00:00
Nathnael
c9bb105e94 feat: add nationality indicator to the customer 2026-08-07 12:54:00 +00:00
Nathnael
4f81a0bbb8 feat(freight): non-terminal change-request review + unified customer timeline
Backoffice can now "Request changes" on a pending settings change
request without rejecting it outright: a new ChangesRequested status
keeps the row open so the customer's next edit appends into the same
request instead of starting a fresh cycle, and the reviewer's note
persists across that round instead of being cleared on resubmit.

Version History and Review History (previously two separate,
differently-shaped lists) are merged into one chronological timeline
under a new History tab, including document changes shown as a real
previous-vs-current diff (both files openable).

Bug fixes surfaced while wiring this up:
- Replacing a single-file document slot left the old file live
  alongside the new one instead of retiring it (customer settings +
  onboarding uploads).
- The "previous" file in a document diff 404'd once superseded —
  the preview route now also matches soft-deleted records.
- A document replace was recorded twice in the timeline (once at
  upload, once again at change-request approval).
2026-07-31 14:12:30 +00:00
Nathnael
3952e8bbdf feat(freight): add download for the TIN business registration record
Ticket #181 — eTrade auto-fetch already existed; adds a client-side
download of the fetched record (eTrade returns data, not a document)
on the portal onboarding step and the backoffice customer detail page.
2026-07-31 11:45:24 +00:00
Nathnael
7c5d96795c feat(freight): track onboarding-phase edit history for companies
Ticket #238 — pre-approval edits and document uploads write straight
to the live company row with no approval gate and, until now, no
trace. Adds an append-only company_revisions log (diffed field
changes, document uploads) recorded from updateProfile and
uploadCompanyDocuments, exposed via GET /companies/:id/revisions and
shown as "Version history" on the backoffice customer detail page.
2026-07-31 11:45:09 +00:00
Nathnael
d67297a601 feat(freight): record when a company is approved
Ticket #420 — add approved_at to companies (migration), stamped at
both promotion sites (first-profile auto-approve and manual staff
status change). Surfaced as Submitted on/Approved on in the backoffice
customer list and detail views.
2026-07-31 11:43:36 +00:00
Nathnael
16ef64fae2 feat: expose eTrade registration and fayda birthdate/gender in company profile 2026-07-29 13:38:39 +00:00
Nathnael
1081b813bb feat(freight-backoffice): show fayda and dars status on customer detail
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-07-29 06:57:27 +00:00
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
d76d198a18 fix(companies): default customer list to review-queue ordering
Marketing asked for approval requests to surface in order instead of the
alphabetical default. New sortBy=review tiers the list by what needs
action - submitted applications awaiting first approval, then approved
customers with a pending change request, then everyone else (drafts
included) - newest first within each tier. Exposed as the backoffice
"Needs review first" sort option and made the default on both ends.

EDRFREIGHT-232
2026-07-21 09:08:53 +00:00
Nathnael
fd5aedcec7 feat(companies): per-document change requests and resubmission review queue
Two review-workflow gaps for freight customer onboarding:

Request for change per document. Backoffice can now flag a single uploaded
document (company document, profile licence, or POA delegation letter) with a
note the customer sees, instead of rejecting the whole role over it. Adds
review_status/review_note/reviewed_by/reviewed_at to freight.files (migration
AddFileReviewStatus, partial index for the gate), a POST
documents/:fileId/request-change endpoint, the backoffice action + modal, and a
portal banner/badge so the customer knows what to re-upload. Re-uploading clears
the flag. Approving a role is blocked while any of its documents has an open
correction; the gate check and the status write share a pessimistic write lock
on the company row (as does the change-request write) so a correction can never
slip in between the check and the profile going Active.

Resubmission is visible to reviewers. When a customer resubmits a rejected role
or amends a change request, backoffice staff are notified (allBackoffice inbox
item, deep-linked to the customer) and the resubmission surfaces in a new
"Pending changes" list view + KPI, since such companies are status = active and
never matched the pending-approval filter.
2026-07-21 07:34:40 +00:00
Nathnael
c7195f077a fix: 194 and 183 plane takss 2026-07-20 07:41:25 +00:00
Marshal
221c49fcda fix issue 2026-07-17 11:38:54 +00:00
Nathnael
ef74bc442a fix: prevent the backoffice from approve the user before he submits 2026-07-17 08:54:24 +00:00
Nathnael
e5fce529fe feat: add poa to the changes approval 2026-07-10 08:45:39 +00:00
Nathnael
ea0f264d72 fea: add poa section to backoffice customer detail page 2026-07-10 08:19:12 +00:00
Nathnael
e04b513b8f feat: password reset flow 2026-07-09 08:50:08 +00:00
Nathnael
9e98600a5f fix: add vat to the settings, and docs to the profile 2026-07-08 10:51:22 +00:00
Nathnael
8947ab8614 feat: fix the settings preview for the business licence and changes request to the approval 2026-07-08 10:42:00 +00:00
Nathnael
3bc4514b04 feat: implemented the changes request to the company profile to backoffice 2026-07-08 08:07:10 +00:00
Marshal
7eae1a920e Add contract booking windows feature 2026-07-03 15:01:16 +00:00
Nathnael
515d59fb53 fix: file preview 2026-07-03 08:35:57 +00:00
Nathnael
3925a11dd4 feat: add invoice to customer detail on backoffice 2026-07-03 07:21:19 +00:00
marshal
1c01c792fc merge conflict 2026-06-29 12:45:40 +03:00
Marshal
da533a488d Refactor file handling to improve URL safety and enhance file viewing capabilities across components 2026-06-28 08:19:21 +00:00
Nathnael
1c076e0694 fix: rm the global approve 2026-06-26 13:05:34 +00:00
Nathnael Wondisha
d41013890b Merge branch 'dev' into freight/fix/type-errors 2026-06-25 09:27:14 +03:00
Nathnael
9b48955eaf feat: centralized the user onboaridn requriements 2026-06-24 11:50:28 +00:00
Marshal
6fd1b68389 enhance company approval workflow and TIN validation; add pending approval handling in booking and onboarding forms 2026-06-23 08:37:15 +00:00
Nathnael
a6f3fd5643 feat: finish company profile in the backoffice 2026-06-23 06:45:29 +00:00
Nathnael
c6bc636495 feat(customers): implement customer management page with mock data
- Created CustomersPage component to display a list of companies with search and pagination features.
- Added mock data for companies, including various statuses and profiles.
- Implemented a service layer to simulate API calls for fetching company data, bookings, documents, and payments.
- Defined TypeScript types for company and related entities to ensure type safety.
- Integrated Mantine components for UI consistency and improved user experience.
2026-06-22 12:32:52 +00:00
Michael Abebe
2c9ca6f16b feat(freight:backoffice): added basic frontend integration with auth(login) page 2026-05-22 17:15:46 +03:00