eims integration master test complete

This commit is contained in:
Hagernesh
2026-08-12 14:08:28 +00:00
parent 8d53dc17ce
commit 2d4da8110b
38 changed files with 2270 additions and 173 deletions

View File

@@ -59,6 +59,47 @@ export function assertEimsInvoiceConfig(config: EimsConfig): void {
}
assertSellerFormats(config.invoice);
assertChargeTypeOverrides(config.invoice);
}
/**
* Per-`chargeType` tax overrides must be internally consistent before anything is filed:
* `EIMS_TAX_CODE_BY_CHARGE_TYPE` and `EIMS_TAX_RATE_BY_CHARGE_TYPE` must name the same charge
* types (a code with no rate, or vice versa, is a half-finished override), and every rate/excise/
* discount value must parse as a number — checked once here rather than once per line at
* registration time.
*/
function assertChargeTypeOverrides(invoice: EimsConfig["invoice"]): void {
const codeKeys = Object.keys(invoice.taxCodeByChargeType);
const rateKeys = Object.keys(invoice.taxRateByChargeType);
const mismatched = [...new Set([...codeKeys, ...rateKeys])].filter(
(k) => !(codeKeys.includes(k) && rateKeys.includes(k)),
);
if (mismatched.length > 0) {
throw new BadRequestException({
code: "EIMS_INVOICE_CONFIG_INVALID",
message:
`EIMS_TAX_CODE_BY_CHARGE_TYPE and EIMS_TAX_RATE_BY_CHARGE_TYPE must list the same charge ` +
`types; mismatched: ${mismatched.join(", ")}`,
});
}
const numericMaps: { env: string; map: Record<string, string> }[] = [
{ env: "EIMS_TAX_RATE_BY_CHARGE_TYPE", map: invoice.taxRateByChargeType },
{ env: "EIMS_EXCISE_BY_CHARGE_TYPE", map: invoice.exciseByChargeType },
{ env: "EIMS_DISCOUNT_BY_CHARGE_TYPE", map: invoice.discountByChargeType },
];
const badNumbers = numericMaps.flatMap(({ env, map }) =>
Object.entries(map)
.filter(([, value]) => !Number.isFinite(Number(value)))
.map(([chargeType]) => `${env}[${chargeType}]`),
);
if (badNumbers.length > 0) {
throw new BadRequestException({
code: "EIMS_INVOICE_CONFIG_INVALID",
message: `EIMS charge-type overrides must be numbers: ${badNumbers.join(", ")}`,
});
}
}
/**
@@ -135,9 +176,25 @@ export function buildEimsContext(config: EimsConfig, input: EimsContextInput): E
salesPersonName: invoice.salesPersonName,
transactionType: invoice.transactionType,
payment: { mode: invoice.paymentMode, term: invoice.paymentTerm },
// One treatment for every line today. The mapper resolves tax per line, so a future
// charge-type-specific rule slots in here without touching the mapper.
taxForLine: (_line: EimsMapperLine) => ({ code: taxCode, ratePercent, exciseTaxValue }),
// Per-`chargeType` override when one is configured (validated symmetric in
// assertChargeTypeOverrides), else the single invoice-wide default.
taxForLine: (line: EimsMapperLine) => {
const { chargeType } = line;
const code = invoice.taxCodeByChargeType[chargeType] ?? taxCode;
const rate =
chargeType in invoice.taxRateByChargeType
? Number(invoice.taxRateByChargeType[chargeType])
: ratePercent;
const excise =
chargeType in invoice.exciseByChargeType
? Number(invoice.exciseByChargeType[chargeType])
: exciseTaxValue;
const discount =
chargeType in invoice.discountByChargeType
? Number(invoice.discountByChargeType[chargeType])
: 0;
return { code, ratePercent: rate, exciseTaxValue: excise, discount };
},
natureOfSupplies: invoice.natureOfSupplies,
unitDefault: invoice.unitDefault,
incomeWithholdValue: invoice.incomeWithholdValue!,
@@ -145,6 +202,9 @@ export function buildEimsContext(config: EimsConfig, input: EimsContextInput): E
buyerCountryCode: invoice.buyerCountryCode,
buyerRegionCodes: invoice.buyerRegionCodes,
buyerWeredaCodes: invoice.buyerWeredaCodes,
// TEMPORARY — see EimsInvoiceConfig.buyerIdType.
buyerIdType: invoice.buyerIdType,
buyerIdNumber: invoice.buyerIdNumber,
exchangeRate: input.exchangeRate ?? null,
};
}