mirror of
https://github.com/Tria-plc/edr-platform.git
synced 2026-09-08 20:38:17 +00:00
feat: empty container Import
This commit is contained in:
@@ -53,24 +53,60 @@ describe('contractTemplateCodeFor', () => {
|
||||
it('only ever resolves to a code that exists', () => {
|
||||
const directions = ['IMPORT', 'EXPORT', 'DOMESTIC', null];
|
||||
const freights = ['BULK', 'CONTAINER', 'BREAK_BULK', null];
|
||||
const conditions = ['LADEN', 'EMPTY', null, undefined];
|
||||
for (const d of directions) {
|
||||
for (const f of freights) {
|
||||
for (const c of [true, false]) {
|
||||
for (const e of [true, false, undefined]) {
|
||||
expect(CONTRACT_TEMPLATE_CODES).toContain(
|
||||
contractTemplateCodeFor(d, f, c, e),
|
||||
);
|
||||
for (const cond of conditions) {
|
||||
expect(CONTRACT_TEMPLATE_CODES).toContain(
|
||||
contractTemplateCodeFor(d, f, c, e, cond),
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
// Empty equipment is a carriage agreement, not a cargo contract: no cargo
|
||||
// liability, no VGM declaration, no commercial documents, no customs leg.
|
||||
it('gives empty container import its own customs-free paper', () => {
|
||||
for (const customs of [true, false]) {
|
||||
for (const ethiopian of [true, false, undefined]) {
|
||||
expect(
|
||||
contractTemplateCodeFor('IMPORT', 'CONTAINER', customs, ethiopian, 'EMPTY'),
|
||||
).toBe('IMPORT_EMPTY_CONTAINER');
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
it('leaves laden contracts on the laden codes', () => {
|
||||
expect(
|
||||
contractTemplateCodeFor('IMPORT', 'CONTAINER', false, false, 'LADEN'),
|
||||
).toBe('IMPORT_CONTAINER_NO_CUSTOMS');
|
||||
expect(contractTemplateCodeFor('IMPORT', 'CONTAINER', false, false)).toBe(
|
||||
'IMPORT_CONTAINER_NO_CUSTOMS',
|
||||
);
|
||||
});
|
||||
|
||||
// Empty rates and empty bookings are import-only, so a stray EMPTY on any
|
||||
// other direction must fall through rather than resolve a template that
|
||||
// describes a Djibouti-to-Ethiopia movement.
|
||||
it('ignores the empty condition outside import', () => {
|
||||
expect(
|
||||
contractTemplateCodeFor('EXPORT', 'CONTAINER', false, false, 'EMPTY'),
|
||||
).toBe('EXPORT_CONTAINER_NO_CUSTOMS');
|
||||
expect(
|
||||
contractTemplateCodeFor('DOMESTIC', 'CONTAINER', false, false, 'EMPTY'),
|
||||
).toBe('INTERCITY_CONTAINER');
|
||||
});
|
||||
});
|
||||
|
||||
describe('CONTRACT_TEMPLATE_DEFAULTS', () => {
|
||||
it('seeds exactly the fourteen declared codes, once each', () => {
|
||||
it('seeds exactly the fifteen declared codes, once each', () => {
|
||||
const seeded = CONTRACT_TEMPLATE_DEFAULTS.map((t) => t.code).sort();
|
||||
expect(seeded).toHaveLength(14);
|
||||
expect(seeded).toHaveLength(15);
|
||||
expect(seeded).toEqual([...CONTRACT_TEMPLATE_CODES].sort());
|
||||
});
|
||||
|
||||
|
||||
@@ -54,6 +54,9 @@ const PREVIEW_TEMPLATE_KEYS: Record<ContractTemplateCode, string> = {
|
||||
EXPORT_CONTAINER_ETHIOPIAN_CUSTOMS: "EXP_CON_USD_FORWARDING",
|
||||
EXPORT_CONTAINER_NO_CUSTOMS: "EXP_CON_USD_TRANSPORT_ONLY",
|
||||
INTERCITY_CONTAINER: "DOM_CON_USD_TRANSPORT_ONLY",
|
||||
// Carriage of the equipment itself — no cargo, no clearing, so it previews
|
||||
// against the transport-only scope like every other non-customs code.
|
||||
IMPORT_EMPTY_CONTAINER: "IMP_CON_USD_TRANSPORT_ONLY",
|
||||
};
|
||||
|
||||
@Injectable()
|
||||
@@ -216,6 +219,7 @@ export class ContractTemplatesService {
|
||||
customsClearingEnabled?: boolean | null,
|
||||
cargoTypeId?: string | null,
|
||||
ethiopianCustomsOnly?: boolean | null,
|
||||
cargoCondition?: string | null,
|
||||
): Promise<ContractTemplate | null> {
|
||||
const isBulk = (freightType ?? "").toUpperCase().includes("BULK");
|
||||
if (isBulk) {
|
||||
@@ -235,6 +239,7 @@ export class ContractTemplatesService {
|
||||
freightType,
|
||||
customsClearingEnabled,
|
||||
ethiopianCustomsOnly,
|
||||
cargoCondition,
|
||||
);
|
||||
const template = await this.repository.findByCode(code);
|
||||
return template?.isActive ? template : null;
|
||||
|
||||
@@ -41,6 +41,14 @@ export const CONTRACT_TEMPLATE_CODES = [
|
||||
"EXPORT_CONTAINER_ETHIOPIAN_CUSTOMS",
|
||||
"EXPORT_CONTAINER_NO_CUSTOMS",
|
||||
"INTERCITY_CONTAINER",
|
||||
/**
|
||||
* Empty container import — bare equipment railed north from Djibouti. No
|
||||
* customs split: an empty box carries no declaration to clear, the same
|
||||
* reason intercity has a single unsuffixed code. Import-only, matching the
|
||||
* rate rule (southbound empties are served by the WITH_RETURN surcharge and
|
||||
* empty_return_requests instead).
|
||||
*/
|
||||
"IMPORT_EMPTY_CONTAINER",
|
||||
] as const;
|
||||
|
||||
export type ContractTemplateCode = (typeof CONTRACT_TEMPLATE_CODES)[number];
|
||||
@@ -74,7 +82,14 @@ export function contractTemplateCodeFor(
|
||||
freightType?: string | null,
|
||||
customsClearingEnabled?: boolean | null,
|
||||
ethiopianCustomsOnly?: boolean | null,
|
||||
cargoCondition?: string | null,
|
||||
): ContractTemplateCode {
|
||||
// Empty equipment is its own paper: a straight carriage agreement with no
|
||||
// cargo liability, no VGM declaration and no customs leg. Import-only, so
|
||||
// anything else falls through to the laden codes below.
|
||||
if (cargoCondition === "EMPTY" && tradeDirection === "IMPORT") {
|
||||
return "IMPORT_EMPTY_CONTAINER";
|
||||
}
|
||||
const direction =
|
||||
tradeDirection === "IMPORT"
|
||||
? "IMPORT"
|
||||
|
||||
Reference in New Issue
Block a user