mirror of
https://github.com/Tria-plc/edr-platform.git
synced 2026-08-30 11:08:12 +00:00
feat(eims): surface filing state in backoffice and alert on failures
Two gaps that only bite in production: nobody could see an invoice's filing state, and a blocked chain was visible only in the logs. A failed filing now notifies the staff who can act on it. An ambiguous result is HIGH priority because it blocks every further invoice for the system number until someone resolves it, and nothing else would surface that -- the sweep just goes quiet. A deterministic rejection affects one invoice, so it is normal priority. The alert never throws: it must not mask the filing outcome. The backoffice invoice detail page gains an EIMS card showing status, IRN, counter, submitted and acknowledged timestamps, and the gateway's own error message, with actions gated on invoices:eims_register. FAILED offers "File again" -- the reservation model already allows re-registering a rejected invoice, so retry needed no new endpoint. UNKNOWN offers no re-file button at all, since resubmitting risks a duplicate registration, and instead explains that a supervisor must record the IRN or discard the attempt. Also aligns the migration class name with its renamed file. The DDL is idempotent, so re-applying under the new name is a no-op against the columns; it leaves one superseded row in freight.migrations. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
@@ -148,6 +148,8 @@ import {
|
||||
import { containerTypesService } from "./container-types.service";
|
||||
import { containerService, type Container } from "./containerService";
|
||||
import { customersService } from "./customers.service";
|
||||
import { eimsService } from "./eims.service";
|
||||
import type { EimsInvoiceStatusView, EimsVerifyResult } from "@/types/eims";
|
||||
import { invoicesService } from "./invoices.service";
|
||||
import { dropdownSettingsService } from "./dropdownSettings.service";
|
||||
import { fileUploadSettingsService } from "./fileUploadSettings.service";
|
||||
@@ -2914,6 +2916,36 @@ export const api = {
|
||||
({ id }) => invoicesService.getById(id),
|
||||
({ id }) => QUERY_KEYS.INVOICES.byId(id),
|
||||
),
|
||||
|
||||
eimsStatus: endpoint<{ id: string }, EimsInvoiceStatusView>(
|
||||
"invoices",
|
||||
"eimsStatus",
|
||||
({ id }) => eimsService.status(id),
|
||||
({ id }) => QUERY_KEYS.INVOICES.eimsStatus(id),
|
||||
),
|
||||
|
||||
// Both mutations refresh the filing panel; register also moves the invoice's own row.
|
||||
eimsRegister: endpoint<{ id: string }, EimsInvoiceStatusView>(
|
||||
"invoices",
|
||||
"eimsRegister",
|
||||
({ id }) => eimsService.register(id),
|
||||
undefined,
|
||||
({ id }) => [QUERY_KEYS.INVOICES.eimsStatus(id), QUERY_KEYS.INVOICES.byId(id)],
|
||||
),
|
||||
|
||||
eimsVerify: endpoint<{ id: string }, EimsVerifyResult>(
|
||||
"invoices",
|
||||
"eimsVerify",
|
||||
({ id }) => eimsService.verify(id),
|
||||
),
|
||||
|
||||
eimsResolve: endpoint<{ id: string; irn?: string; discard?: boolean }, EimsInvoiceStatusView>(
|
||||
"invoices",
|
||||
"eimsResolve",
|
||||
({ id, irn, discard }) => eimsService.resolve(id, { irn, discard }),
|
||||
undefined,
|
||||
({ id }) => [QUERY_KEYS.INVOICES.eimsStatus(id), QUERY_KEYS.INVOICES.byId(id)],
|
||||
),
|
||||
},
|
||||
|
||||
overview: {
|
||||
|
||||
39
apps/edr-freight-web/backoffice/src/services/eims.service.ts
Normal file
39
apps/edr-freight-web/backoffice/src/services/eims.service.ts
Normal file
@@ -0,0 +1,39 @@
|
||||
import { api as apiClient } from "@/auth/http";
|
||||
import { URL_CONSTANTS } from "@/constants/URLS";
|
||||
import type { EimsInvoiceStatusView, EimsVerifyResult } from "@/types/eims";
|
||||
|
||||
/**
|
||||
* MoR EIMS filing actions on an invoice.
|
||||
*
|
||||
* Registration is irreversible at the tax authority, so these are admin actions rather than part
|
||||
* of the ordinary invoice screen: the normal production path is the API's cron sweep.
|
||||
*/
|
||||
export const eimsService = {
|
||||
status(invoiceId: string): Promise<EimsInvoiceStatusView> {
|
||||
return apiClient
|
||||
.get<EimsInvoiceStatusView>(URL_CONSTANTS.EIMS.STATUS(invoiceId))
|
||||
.then((r) => r.data);
|
||||
},
|
||||
|
||||
register(invoiceId: string): Promise<EimsInvoiceStatusView> {
|
||||
return apiClient
|
||||
.post<EimsInvoiceStatusView>(URL_CONSTANTS.EIMS.REGISTER(invoiceId))
|
||||
.then((r) => r.data);
|
||||
},
|
||||
|
||||
verify(invoiceId: string): Promise<EimsVerifyResult> {
|
||||
return apiClient
|
||||
.post<EimsVerifyResult>(URL_CONSTANTS.EIMS.VERIFY(invoiceId))
|
||||
.then((r) => r.data);
|
||||
},
|
||||
|
||||
/** Record an IRN confirmed with MoR, or discard the attempt. Clears the system-wide block. */
|
||||
resolve(
|
||||
invoiceId: string,
|
||||
input: { irn?: string; discard?: boolean },
|
||||
): Promise<EimsInvoiceStatusView> {
|
||||
return apiClient
|
||||
.post<EimsInvoiceStatusView>(URL_CONSTANTS.EIMS.RESOLVE(invoiceId), input)
|
||||
.then((r) => r.data);
|
||||
},
|
||||
};
|
||||
Reference in New Issue
Block a user