fix(eims): stop sending our internal fee-basis tag as MoR's ItemList Unit

Confirmed live 2026-08-17 on INV-20260817-00008: MoR rejected the
document with a SCHEMA ERROR on ItemList[0].Unit — 'PER_CONTAINER'
(from the line's own metadata.unit) fails MoR's enum
(LTR/MTR/101/PCS/ROL/MTS/PKG/SET/KLG), its 8-char max, and its
^[A-Za-z]{3,8}$ regex all at once.

line.metadata.unit is our own fee-basis tag (PER_CONTAINER/PER_TON/
PER_ITEM — how a charge is computed) and was never a MoR unit of
measure; the mapper was reusing the same field name for two unrelated
concepts. Every line now sends the single configured
EIMS_UNIT_DEFAULT instead of guessing a per-line value that doesn't
exist in MoR's vocabulary.
This commit is contained in:
Hagernesh
2026-08-17 18:10:55 +00:00
parent 7d8ab932c2
commit e67ccbb9cd
2 changed files with 10 additions and 2 deletions

View File

@@ -151,7 +151,9 @@ describe("toEimsInvoice", () => {
// EimsLineTax.discount comment in eims-invoice.mapper.ts.
Discount: 25,
TotalLineAmount: 1050,
Unit: "CTR",
// Not "CTR" from the line's metadata.unit — that's our internal fee-basis tag, not a MoR
// unit of measure, and is never read for this field (see the mapper's own comment).
Unit: "PCS",
});
expect(doc.ValueDetails).toEqual({
Discount: null,

View File

@@ -463,7 +463,13 @@ export function toEimsInvoice(
const PreTaxValue = round2(num(line.amount));
const TaxAmount = round2((PreTaxValue * tax.ratePercent) / 100);
const ExciseTaxValue = round2(tax.exciseTaxValue);
const unit = typeof line.metadata?.unit === "string" ? line.metadata.unit : context.unitDefault;
// `line.metadata.unit` is our own fee-basis tag (PER_CONTAINER/PER_TON/PER_ITEM — how a charge
// is computed, see the fee-rule docs), never a MoR unit of measure — sending it as-is here
// (confirmed live 2026-08-17: "PER_CONTAINER" fails Unit's enum, its 8-char max, and its regex
// all at once) is what a prior version of this mapper did by mistake. MoR's own enum
// (LTR/MTR/101/PCS/ROL/MTS/PKG/SET/KLG) has no freight-shipment concept at all, so every line
// uses the single configured default rather than guessing a per-line value that doesn't exist.
const unit = context.unitDefault;
return {
Discount: round2(tax.discount),