diff --git a/apps/edr-freight-api/src/modules/eims/eims-invoice.controller.ts b/apps/edr-freight-api/src/modules/eims/eims-invoice.controller.ts index 9dab21107..f47cfe9d9 100644 --- a/apps/edr-freight-api/src/modules/eims/eims-invoice.controller.ts +++ b/apps/edr-freight-api/src/modules/eims/eims-invoice.controller.ts @@ -7,8 +7,16 @@ import { ResolveEimsRegistrationDto } from "./dto/resolve-eims-registration.dto" import { EimsInvoiceRegistrationService } from "./eims-invoice-registration.service"; /** - * Staff-triggered EIMS actions on an existing invoice. Registration is manual and one invoice at a - * time — nothing in invoice creation submits automatically. + * Manual EIMS actions on an existing invoice. + * + * Invoices are produced by the freight workflow, not by a person, so these routes are **not** the + * normal production path — they exist for controlled testing and exceptional operations. Automatic + * submission after an invoice is issued is a separate phase; nothing here is called by it. + * + * `eims_register` and `eims_resolve` are intentionally left out of every role preset and assigned + * to named admins instead. They are also separate permissions: resolving clears the system-wide + * chain block and can record an IRN against an invoice, which is a supervisor action, not an + * operational one. Only `eims/status` rides on the ordinary `invoices:view`. * * Filing gets its own permission (`invoices:eims_register`) rather than riding on an existing key: * registration is irreversible at MoR, so it must not follow from the right to download a PDF. @@ -39,7 +47,7 @@ export class EimsInvoiceController { } @Post(":id/eims/resolve") - @BookingStaff(FREIGHT_PERMS.invoices.eimsRegister) + @BookingStaff(FREIGHT_PERMS.invoices.eimsResolve) @ApiOperation({ summary: "Resolve an unacknowledged submission: record the IRN confirmed with MoR, or discard it. Clears the system-wide block.", diff --git a/apps/edr-freight-api/src/seed/freight-permissions.registry.ts b/apps/edr-freight-api/src/seed/freight-permissions.registry.ts index 7fa994060..3c2be2e09 100644 --- a/apps/edr-freight-api/src/seed/freight-permissions.registry.ts +++ b/apps/edr-freight-api/src/seed/freight-permissions.registry.ts @@ -474,6 +474,14 @@ export const FINANCE_PERMISSIONS: FreightPermissionSeed[] = [ "edr_freight_app:invoices:eims_register", "Register invoice with MoR EIMS", ), + // Separate from registering: resolving an unacknowledged submission clears the + // system-wide chain block and can record an IRN against an invoice, so it is a + // supervisor/admin action rather than an operational one. + perm( + "d2b00001-0001-4000-8000-000000000006", + "edr_freight_app:invoices:eims_resolve", + "Resolve a blocked MoR EIMS submission", + ), ]; // E. First / last mile operations @@ -1599,6 +1607,7 @@ export const FREIGHT_PERMS = { view: "edr_freight_app:invoices:view", export: "edr_freight_app:invoices:export", eimsRegister: "edr_freight_app:invoices:eims_register", + eimsResolve: "edr_freight_app:invoices:eims_resolve", }, firstMile: { view: "edr_freight_app:first_mile:view", @@ -2088,6 +2097,10 @@ export const ROLE_PERMISSION_PRESETS = { FREIGHT_PERMS.bookings.view, FREIGHT_PERMS.invoices.view, FREIGHT_PERMS.invoices.export, + // Deliberately NOT granted here: invoices:eims_register and invoices:eims_resolve. + // Invoices are filed with MoR by the workflow, not by a person, so filing is not a + // Finance job function — the endpoints exist for controlled testing and exceptional + // operations, and are assigned to named admins rather than a role preset. FREIGHT_PERMS.payments.view, FREIGHT_PERMS.bookings.wagonCancellationView, ],