Files
edr-platform/apps/edr-freight-api/src/modules/eims/eims-test-fixtures.ts
Hagernesh c937290019 feat(eims): resolve buyer geography from the MoR location master
BuyerDetails Country/Region/City/Wereda now resolve from the Ministry's own
EIMS_COUNTRY_REGION_VW master instead of the EIMS_BUYER_*_CODES env maps and
the ethiopia-geo-codes table. Both invented their codes and looked names up
globally, so KERSA/GORO/BABILE/BURE — each present in several zones with
different LOCALITY_NOs — could be filed against the wrong jurisdiction.

Resolution is hierarchical and refuses to guess: an unknown or ambiguous
address raises a local validation error naming the level that failed, and
never selects the first matching row. Spelling differences between EDR and
MoR live in a reviewed, parent-scoped alias layer; the dataset itself stays
verbatim so it remains traceable to the Ministry sheet.

Resolution now runs before the counter reservation in both the single and
bulk paths, so a bad company address no longer burns an EIMS sequence number.

Adds eims:import-locations to regenerate the dataset from a future workbook,
reporting duplicate rows and same-hierarchy code conflicts.
2026-08-24 06:59:50 +00:00

88 lines
2.7 KiB
TypeScript

import { EimsConfig, EimsInvoiceConfig } from "../../config/eims.config";
/**
* Fixtures shared by the EIMS specs.
*
* Deliberately not a `.spec.ts`: importing fixtures from a spec file makes jest execute that
* file's `describe` blocks inside every importing suite, so the same tests run — and report —
* twice.
*/
export const EIMS_SYSTEM_NUMBER = "B0360154BA";
export const EIMS_SYSTEM_TYPE = "SYS";
export const eimsInvoiceConfig = (over: Partial<EimsInvoiceConfig> = {}): EimsInvoiceConfig => ({
sellerLegalName: "Ethio-Djibouti Railway S.C.",
sellerVatNumber: "0000000000",
sellerPhone: "0911223344",
sellerEmail: "finance@example.et",
sellerRegion: "13",
sellerWereda: "574",
sellerCity: null,
sellerSubCity: null,
sellerHouseNumber: null,
sellerLocality: null,
taxCode: "VAT15",
taxRatePercent: 15,
exciseTaxValue: 0,
incomeWithholdValue: 0,
transactionWithholdValue: 0,
transactionType: "B2B",
natureOfSupplies: "Service",
paymentMode: "CASH",
paymentTerm: "IMMIDIATE",
unitDefault: "PCS",
taxCodeByChargeType: {},
taxRateByChargeType: {},
exciseByChargeType: {},
discountByChargeType: {},
cashierName: null,
salesPersonName: null,
buyerIdType: null,
buyerIdNumber: null,
...over,
});
export const eimsConfig = (over: Partial<EimsConfig> = {}): EimsConfig => ({
enabled: true,
baseUrl: "https://core.mor.gov.et",
clientId: "cid",
clientSecret: "super-secret-value",
apiKey: "super-secret-apikey",
tin: "0000034558",
systemNumber: EIMS_SYSTEM_NUMBER,
systemType: EIMS_SYSTEM_TYPE,
privateKeyPath: "/dev/null",
certificatePath: "/dev/null",
privateKeyBase64: "",
certificateBase64: "",
privateKeyPem: "",
certificatePem: "",
httpTimeoutMs: 30_000,
tokenSkewMs: 45_000,
autoSubmit: false,
autoSubmitCron: "0 */5 * * * *",
autoSubmitMaxAgeDays: 3,
invoice: eimsInvoiceConfig(),
...over,
});
/**
* A structurally real access token. MoR stamps the source-system identity into the JWT payload and
* `EimsAuthService` reads it from there; only the payload segment is meaningful, since the token is
* never verified locally — it is MoR's, signed with MoR's key.
*
* Pass a claim as `undefined` to omit it (spreading beats `delete`, which the defaults would undo).
*/
export const eimsToken = (claims: Record<string, unknown> = {}): string => {
const payload = { systemNumber: EIMS_SYSTEM_NUMBER, systemType: EIMS_SYSTEM_TYPE, ...claims };
for (const [key, value] of Object.entries(payload)) {
if (value === undefined) delete (payload as Record<string, unknown>)[key];
}
return [
"eyJhbGciOiJSUzI1NiJ9",
Buffer.from(JSON.stringify(payload)).toString("base64url"),
"signature",
].join(".");
};