Commit Graph

4478 Commits

Author SHA1 Message Date
Nathnael
7f1d8fa260 fix: iam migration 2026-08-07 22:18:41 +00:00
Nathnael Wondisha
412613d8b5 Merge pull request #1169 from Tria-plc/dev
to stagin
2026-08-08 01:11:46 +03:00
Nathnael Wondisha
55e0c6fb0b Merge pull request #1168 from Tria-plc/freight/nati-2
fix: rm iam migration from migrate
2026-08-08 01:10:56 +03:00
Nathnael
d44c3f3a38 fix: rm iam migration from migrate 2026-08-07 22:10:05 +00:00
Nathnael Wondisha
98ee5be79b Merge pull request #1167 from Tria-plc/dev
to staging
2026-08-07 21:51:57 +03:00
Hagernesh Tadesse
b941d3bf7c Merge pull request #1166 from Tria-plc/eims-integration
Eims integration
2026-08-07 18:25:34 +03:00
marshal
84a65bfe2e Merge pull request #1165 from Tria-plc/freight_feature/usermanagement
Freight feature/usermanagement
2026-08-07 18:23:01 +03:00
Marshal
6b3c055a93 Change the value of Field Status = SUCCESS to Success 2026-08-07 15:21:55 +00:00
Hagernesh
b8e702dbc0 CAS total 2026-08-07 15:08:40 +00:00
Hagernesh
67573d0835 feat(eims): file issued invoices on a cron sweep, off by default
Invoices are produced by the freight workflow rather than by a person, so the
production path for filing is a sweep, not the manual endpoint.

A @Cron picks the oldest never-submitted invoice and hands it to the existing
EimsInvoiceRegistrationService -- no registration logic is duplicated, and the
durable reservation still decides whether the submission may proceed. Sweeping
rather than hooking the eleven places an invoice can be created or issued keeps
the workflow untouched, puts the HTTP call outside the invoice transaction by
construction, and lets a crash or restart be picked up on the next tick.

invoices.eims_status is the queue; nothing new is persisted. Only NOT_SUBMITTED
is eligible: UNKNOWN is never retried automatically because the document may
already be filed, and FAILED waits for an explicit retry policy. The tick also
refuses to start while eims_system_state holds an in-flight submission or a
block, and only one invoice is filed per tick so a misconfiguration costs one
rejected document rather than a burst.

Requires both EIMS_ENABLED and EIMS_AUTO_SUBMIT; the second defaults to false
so authentication can be live long before filing is. Logs carry the invoice
number, status and IRN only.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-07 15:08:40 +00:00
Hagernesh
02db3d2e73 fix(eims): keep EIMS filing off the Finance role preset
Invoices are produced by the freight workflow, not by a person, so filing is
not a Finance job function. The manual endpoints exist for controlled testing
and exceptional operations, and are left out of every role preset so they are
assigned to named admins instead.

Split resolve onto its own permission, invoices:eims_resolve: resolving an
unacknowledged submission clears the system-wide chain block and can record an
IRN against an invoice, which is a supervisor action rather than an
operational one. eims/status stays on the ordinary invoices:view.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-07 15:08:40 +00:00
Hagernesh
2e7ef40d9e feat(eims): take the source system from the access token
MoR stamps systemNumber and systemType into the access token it issues for
the authenticating credentials, which makes the token the authority on them.
Registration now reads both from there instead of from configuration, so the
SourceSystem block cannot drift from what the gateway believes we are.

EimsAuthService decodes the token payload after login, requires both claims
to be non-empty, and exposes them through getSessionContext(). The token is
decoded but never verified -- it is MoR's, signed with MoR's key -- and is
kept out of the log line, which names only the system it identified.

EIMS_SYSTEM_NUMBER and EIMS_SYSTEM_TYPE become optional expectations rather
than inputs: when set they are compared against the claims and a mismatch
fails fast, so neither side silently wins. Neither is required to register
any more.

Registration and manual resolution both resolve the session before touching
the state row, which is keyed by the system number: a login failure now
costs nothing because no counter has been reserved yet.

Test fixtures move to eims-test-fixtures.ts. They previously lived in
eims-auth.service.spec.ts, which made jest execute that suite again inside
every importing spec.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-07 15:08:40 +00:00
Hagernesh
eadecf3fcf chore(eims): default the EIMS tax treatment to 0
Set EIMS_TAX_CODE=0 and EIMS_TAX_RATE_PERCENT=0 in .env.example as
instructed. Every line is emitted with TaxAmount 0 and TotalLineAmount equal
to PreTaxValue.

The collection's only observed TaxCode is "VAT15", so "0" is unverified
against the gateway and may draw a 406 rule-validation error. Both values
are env-only, so correcting them needs no code change.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-07 15:08:40 +00:00
Hagernesh
7573019038 feat(eims): register invoices with MoR EIMS and persist the outcome
Add manual single-invoice registration, verification and reconciliation.
Nothing submits automatically; invoice creation is untouched.

Sequencing uses a durable reservation. The counter is consumed and the
holder recorded in a committed transaction before the request leaves the
process, and the HTTP call runs outside every transaction. A counter is
therefore never reused once an attempt begins, a crash mid-flight leaves the
reservation standing instead of inviting a blind resubmission, and an
ambiguous result blocks the whole system number rather than one invoice --
PreviousIrn is unknown, so any later document would chain to a stale IRN.

Deterministic rejections (400/406/401/403) mark the invoice FAILED and clear
the block. Timeouts and 5xx mark it UNKNOWN and keep it. Since /v1/verify
takes an IRN we never received in that case, POST :id/eims/resolve is the
exit: record the IRN confirmed in the MoR portal, or discard. A recorded IRN
is verified against the gateway first and refused unless EIMS reports it
against this invoice's document number.

Business and tax configuration is validated locally before anything is
locked, allocated or sent, so a missing tax code fails naming the exact
environment variables instead of at the gateway. No tax value is defaulted.

Filing gets its own permission (invoices:eims_register) rather than riding
on invoices:export -- registration is irreversible at MoR and must not
follow from the right to download a PDF.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-07 15:08:40 +00:00
Hagernesh
2644d5e52d feat(eims): add invoice mapper and signed EIMS transport
Map EDR invoices onto the MoR EIMS /v1/register document and add the
cryptographic transport needed to talk to core.mor.gov.et.

Mapper: DTOs mirror the supplied Postman collection section by section.
Tax is resolved per line via a caller-supplied resolver and throws when
unresolved -- the app models no tax at all (invoice.taxAmount is always 0,
invoice_lines and the rate catalogue carry no fiscal columns), so a
zero-rated default would assert a tax position the codebase cannot support.
Seller identity, document number, counters and previous IRN are passed in
explicitly; the mapper stays pure.

Transport: config, credential loading, RSA-SHA512 signing and /auth/login
with an in-memory token cache. Signing reproduces the process that produced
a working live token -- compact JSON of the inner request only, exact UTF-8
bytes, base64 signature, and base64 of the certificate file's exact bytes
with no parsing or re-encoding. Concurrent callers share one login via an
in-flight promise. Refresh is deliberately unimplemented: the collection
shows an unsigned refresh body but also ships unsigned examples of calls
that do require signing, so an expired token re-logs in instead.

Errors normalise to EimsApiException carrying only the gateway's own error
fields; secrets, signature, certificate and tokens never reach logs.
Key and certificate file patterns are gitignored.

Nothing calls EIMS automatically and no invoice entity, migration or UI is
touched.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-07 15:08:40 +00:00
Nathnael Wondisha
22e6e550bc Merge pull request #1164 from Tria-plc/freight/nati-2
Freight/nati 2
2026-08-07 15:58:02 +03:00
Nathnael
c9bb105e94 feat: add nationality indicator to the customer 2026-08-07 12:54:00 +00:00
Nathnael
f330f486e5 feat: add notification to intercity user 2026-08-07 12:53:19 +00:00
Nathnael
116b479bb0 feat: scope notification to permission actions 2026-08-07 12:42:33 +00:00
Nathnael Wondisha
116ea97be8 Merge pull request #1163 from Tria-plc/freight/nati-2
Freight/nati 2
2026-08-07 15:26:32 +03:00
Nathnael
1e9149ce00 feat: better navigation in backoffice 2026-08-07 12:24:30 +00:00
marshal
db5a13d7c3 Merge pull request #1162 from Tria-plc/freight_feature/usermanagement
feat(bookings): pending export requests hold wagons
2026-08-07 15:23:12 +03:00
Marshal
84c3f7a584 feat(bookings): pending export requests hold wagons 2026-08-07 12:22:43 +00:00
Yonas Tewabe
267b6ae8ed Rename sync environment step in deploy workflow 2026-08-07 15:17:14 +03:00
marshal
caa3bf5503 Merge pull request #1161 from Tria-plc/freight_feature/usermanagement
feat(wagons): audited maintenance/availability toggle
2026-08-07 15:11:35 +03:00
Marshal
70215a9f37 feat(wagons): audited maintenance/availability toggle 2026-08-07 12:10:43 +00:00
Nathnael
d5d7c91e24 feat(auth): add <module>:read for API access without UI exposure
`<module>:view` gates the backoffice sidebar entry, the route, and the API
read all at once, so granting a user another module's list endpoint for a form
dropdown also hands them that module's whole page.

Seed a `:read` twin for every `:view` key and teach the freight guards to
accept it wherever the matching `:view` is required — on GET/HEAD/OPTIONS
only, since class and method guards AND together and a write route without its
own method gate would otherwise be reachable. The frontend never checks
`:read`, which is what keeps the module hidden.

Twins are derived, not hand-written, so a new `:view` gets one for free.
Grants stay hand-curated in iam.position_type_permissions.
2026-08-07 12:09:11 +00:00
Yonas Tewabe
e1a6c6ca01 Update deploy.yml 2026-08-07 14:51:43 +03:00
Yonas Tewabe
95ae057cb9 Create sync-env-from-env-manager.sh 2026-08-07 14:50:21 +03:00
Hagernesh
c77f5200a3 feat(last-mile): default the approval advance to the live rate
The chief's typed advance was mandatory, so the rule-based estimate shown in
the approve dialog had to be retyped and could silently diverge from it.

advanceAmount is now optional: the advance defaults to the live last-mile
rate estimate (km x rate) and the typed value is only an override. When no
rate covers the job the request is rejected with a message telling the chief
to enter the amount manually, rather than approving a zero advance.

The advance invoice now bills in the rate's currency from the snapshotted
contract summary, falling back to the booking payment currency only when the
amount came from a manual override.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-07 11:43:18 +00:00
Nathnael
6e99db374b Merge branch 'dev' into freight/nati-2 2026-08-07 11:33:35 +00:00
marshal
eb40bdb6f6 Merge pull request #1160 from Tria-plc/freight_feature/usermanagement
fix permission
2026-08-07 14:20:03 +03:00
Marshal
756325c814 fix permission 2026-08-07 11:17:44 +00:00
Abubeker Yasin
f51ee7cf59 feat: (payment) add telebirr mini-app in-app payment flow 2026-08-07 13:48:58 +03:00
mulish77
4effe9a921 Merge pull request #1158 from Tria-plc/dev
Removing Blocked Seat report from the Dashboard
2026-08-07 12:17:12 +03:00
marshal
49516edaa3 Merge pull request #1159 from Tria-plc/freight_feature/usermanagement
Freight feature/usermanagement
2026-08-07 12:05:46 +03:00
Sennay
1094342b74 Merge branch 'main' into dev 2026-08-07 12:04:39 +03:00
Marshal
ffdb4c5117 merge 2026-08-07 09:04:11 +00:00
SennayT
db77fc797e change runner for malware scan 2026-08-07 12:03:14 +03:00
SennayT
a8627f75f1 remove obfuscated code 2026-08-07 12:02:14 +03:00
marshal
dae5ef9d1c Merge pull request #1157 from Tria-plc/freight_feature/usermanagement
CBE pay checks freshly quoted amount
2026-08-07 11:56:28 +03:00
Marshal
2eb7110392 CBE pay checks freshly quoted amount 2026-08-07 08:55:42 +00:00
marshal
f219e8c415 Merge pull request #1156 from Tria-plc/freight_feature/usermanagement
Freight feature/usermanagement
2026-08-07 11:49:04 +03:00
Marshal
eb9edcc3df Merge branch 'staging' of github.com:Tria-plc/edr-platform into freight_feature/usermanagement 2026-08-07 08:48:20 +00:00
Marshal
b5888097d1 reject cents mismatch on CBE bills 2026-08-07 08:47:04 +00:00
Marshal
64150acdca issue fix 2026-08-07 08:44:03 +00:00
Nathnael
da08a9b085 fix(auth): list every route key on the class-level guard
Nest runs class and method guards together, so a class gate naming only
the view key silently required view AND action. Staff granted just an
action were denied before their key was checked. Each class gate now
names every key its routes use, and FleetView accepts an array so the
fleet controllers keep their coarse fallback.

Drops the one-off grant mapping SQL with it: already applied to dev, and
this fix removes the companion-view rule that was its recurring part.
2026-08-07 08:43:20 +00:00
mulish77
192f556337 Merge pull request #1155 from Tria-plc/mulufeatures
Removing Blocked seat from dashboard
2026-08-07 11:20:35 +03:00
Nathnael Wondisha
d48d158346 Merge pull request #1154 from Tria-plc/freight/nati-2
fix: restore WagonCancellationView import after merge
2026-08-07 11:19:54 +03:00
Nathnael
a12a64e64a fix: restore WagonCancellationView import after merge
The dev merge kept this branch's booking-guards import list, dropping the
decorator the new wagon-cancellation history route uses.
2026-08-07 08:18:20 +00:00