mirror of
https://github.com/Tria-plc/edr-platform.git
synced 2026-08-28 07:51:02 +00:00
122 lines
6.7 KiB
TypeScript
122 lines
6.7 KiB
TypeScript
import { IMPORT_MILESTONES, EXPORT_MILESTONES } from '@edr/types';
|
|
import type { MilestoneOwnerRegion } from './entities/clearance-milestone.entity';
|
|
|
|
/**
|
|
* Static catalog of clearance milestones per trade direction (doc §11.3, §12.2).
|
|
* Drives the rows seeded onto a contract clearance cycle (pre-booking) and
|
|
* booking (post-booking). `phaseBoundaryAfter` marks the last pre-booking
|
|
* milestone — everything after it tracks on the booking.
|
|
*/
|
|
export interface MilestoneDef {
|
|
code: string;
|
|
label: string;
|
|
ownerRegion: MilestoneOwnerRegion;
|
|
triggeredByDoc: boolean;
|
|
}
|
|
|
|
const IMPORT_DEFS: Record<string, Omit<MilestoneDef, 'code'>> = {
|
|
IMPORT_DOCS_UPLOADED: { label: 'Import Documents Uploaded', ownerRegion: 'CUST', triggeredByDoc: false },
|
|
PENDING_DOCUMENT_REVIEW: { label: 'Pending Document Review', ownerRegion: 'ET', triggeredByDoc: true },
|
|
DOCUMENTS_APPROVED: { label: 'Documents Approved', ownerRegion: 'ET', triggeredByDoc: false },
|
|
UNDER_CUSTOMS_CLEARANCE: { label: 'Under Customs Clearance', ownerRegion: 'ET', triggeredByDoc: false },
|
|
DECLARED: { label: 'Declared', ownerRegion: 'ET', triggeredByDoc: true },
|
|
DUTY_TAXES_ADVISED: { label: 'Duty and Taxes Advised', ownerRegion: 'ET', triggeredByDoc: false },
|
|
DUTY_TAX_PAID: { label: 'Duty and Tax Paid', ownerRegion: 'CUST', triggeredByDoc: true },
|
|
TRANSIT_PERMIT_UPLOADED: {
|
|
label: 'Transit Permit Uploaded',
|
|
ownerRegion: 'ET',
|
|
triggeredByDoc: true,
|
|
},
|
|
DO_COLLECTED: { label: 'DO Collected', ownerRegion: 'DJ', triggeredByDoc: true },
|
|
WAGON_REQUESTED: { label: 'Wagon Allocation Requested', ownerRegion: 'ET', triggeredByDoc: false },
|
|
FREIGHT_PAYMENT_SETTLED: { label: 'Payment Settled (freight)', ownerRegion: 'CUST', triggeredByDoc: true },
|
|
WAGON_ALLOCATED: { label: 'Wagon Allocated', ownerRegion: 'OPS', triggeredByDoc: false },
|
|
GATEPASS_GRANTED: { label: 'Gatepass Granted', ownerRegion: 'DJ', triggeredByDoc: false },
|
|
READY_FOR_LOADING: { label: 'Ready for Loading', ownerRegion: 'DJ', triggeredByDoc: false },
|
|
LOADED: { label: 'Loaded', ownerRegion: 'DJ', triggeredByDoc: false },
|
|
DEPARTED_FROM_DJIBOUTI: { label: 'Departed from Djibouti', ownerRegion: 'DJ', triggeredByDoc: false },
|
|
ARRIVED_ETHIOPIA: { label: 'Arrived at Port in Ethiopia', ownerRegion: 'OPS', triggeredByDoc: false },
|
|
OFFLOADED: { label: 'Offloaded', ownerRegion: 'OPS', triggeredByDoc: false },
|
|
T1_CLOSED: { label: 'T1 Closed', ownerRegion: 'ET', triggeredByDoc: false },
|
|
RISK_ASSIGNED: { label: 'Risk Assigned', ownerRegion: 'ET', triggeredByDoc: false },
|
|
SECOND_DUTY_ADVISED: {
|
|
label: 'Additional Duty and Taxes Advised',
|
|
ownerRegion: 'ET',
|
|
triggeredByDoc: false,
|
|
},
|
|
SECOND_DUTY_PAID: {
|
|
label: 'Additional Duty and Tax Paid',
|
|
ownerRegion: 'CUST',
|
|
triggeredByDoc: true,
|
|
},
|
|
IMPORT_RELEASE_GRANTED: { label: 'Import Release Granted', ownerRegion: 'ET', triggeredByDoc: true },
|
|
IMPORT_PROCESS_COMPLETED: { label: 'Import Process Completed', ownerRegion: 'ET', triggeredByDoc: true },
|
|
STORAGE_INVOICE_RAISED: { label: 'Storage Invoice Raised', ownerRegion: 'OPS', triggeredByDoc: false },
|
|
EXIT_NOTE_GENERATED: { label: 'Exit Note Generated', ownerRegion: 'OPS', triggeredByDoc: true },
|
|
};
|
|
|
|
const EXPORT_DEFS: Record<string, Omit<MilestoneDef, 'code'>> = {
|
|
EXPORT_DOCS_UPLOADED: { label: 'Export Documents Uploaded', ownerRegion: 'CUST', triggeredByDoc: false },
|
|
PENDING_DOCUMENT_REVIEW: { label: 'Pending Document Review', ownerRegion: 'ET', triggeredByDoc: true },
|
|
DOCUMENTS_APPROVED: { label: 'Documents Approved', ownerRegion: 'ET', triggeredByDoc: false },
|
|
RELEASE_ORDER_SECURED: { label: 'Release Order Secured', ownerRegion: 'DJ', triggeredByDoc: true },
|
|
UNDER_CUSTOMS_CLEARANCE: { label: 'Under Customs Clearance', ownerRegion: 'ET', triggeredByDoc: false },
|
|
DECLARED: { label: 'Declared', ownerRegion: 'ET', triggeredByDoc: true },
|
|
EXPORT_RELEASED: { label: 'Export Released', ownerRegion: 'ET', triggeredByDoc: false },
|
|
WAGON_REQUESTED: { label: 'Wagon Requested', ownerRegion: 'ET', triggeredByDoc: false },
|
|
FREIGHT_PAYMENT_PENDING: { label: 'Pending Payment', ownerRegion: 'CUST', triggeredByDoc: false },
|
|
FREIGHT_PAYMENT_SETTLED: { label: 'Payment Settled', ownerRegion: 'CUST', triggeredByDoc: true },
|
|
WAGON_ALLOCATED: { label: 'Wagon Allocated', ownerRegion: 'OPS', triggeredByDoc: false },
|
|
EXPORT_TRANSPORT_ISSUED: {
|
|
label: 'Export Transport Document Issued',
|
|
ownerRegion: 'ET',
|
|
triggeredByDoc: true,
|
|
},
|
|
CARGO_ARRIVED: { label: 'Cargo Arrived', ownerRegion: 'OPS', triggeredByDoc: false },
|
|
READY_FOR_LOADING: { label: 'Ready for Loading', ownerRegion: 'OPS', triggeredByDoc: false },
|
|
LOADED: { label: 'Loaded', ownerRegion: 'OPS', triggeredByDoc: false },
|
|
DEPARTED_TO_DJIBOUTI: { label: 'Departed to Djibouti', ownerRegion: 'OPS', triggeredByDoc: false },
|
|
ARRIVED_AT_DJIBOUTI: { label: 'Arrived at Djibouti', ownerRegion: 'DJ', triggeredByDoc: false },
|
|
GATEPASS_GRANTED: { label: 'Gatepass Granted', ownerRegion: 'DJ', triggeredByDoc: false },
|
|
T1_CLOSED: { label: 'T1 Closed', ownerRegion: 'DJ', triggeredByDoc: false },
|
|
OFFLOADED: { label: 'Offloaded', ownerRegion: 'DJ', triggeredByDoc: true },
|
|
};
|
|
|
|
/**
|
|
* Codes BEFORE (and including) this one are pre-booking — they attach to the
|
|
* contract clearance cycle. From the next code onward, milestones attach to the
|
|
* booking GL creates. Per doc §11.3 the booking is created right after DO_COLLECTED
|
|
* (import) / EXPORT_RELEASED (export), i.e. before WAGON_REQUESTED.
|
|
*/
|
|
const IMPORT_PRE_BOOKING_LAST = 'DO_COLLECTED';
|
|
const EXPORT_PRE_BOOKING_LAST = 'EXPORT_RELEASED';
|
|
|
|
function buildDefs(
|
|
codes: readonly string[],
|
|
defs: Record<string, Omit<MilestoneDef, 'code'>>,
|
|
): MilestoneDef[] {
|
|
return codes.map((code) => ({ code, ...defs[code] }));
|
|
}
|
|
|
|
export function milestonesForDirection(tradeDirection: string): MilestoneDef[] {
|
|
if (tradeDirection === 'IMPORT') return buildDefs(IMPORT_MILESTONES, IMPORT_DEFS);
|
|
if (tradeDirection === 'EXPORT') return buildDefs(EXPORT_MILESTONES, EXPORT_DEFS);
|
|
return [];
|
|
}
|
|
|
|
/** Split the milestone list into pre-booking (contract) and post-booking (booking). */
|
|
export function splitMilestones(tradeDirection: string): {
|
|
preBooking: MilestoneDef[];
|
|
postBooking: MilestoneDef[];
|
|
} {
|
|
const all = milestonesForDirection(tradeDirection);
|
|
const boundary =
|
|
tradeDirection === 'IMPORT' ? IMPORT_PRE_BOOKING_LAST : EXPORT_PRE_BOOKING_LAST;
|
|
const idx = all.findIndex((m) => m.code === boundary);
|
|
if (idx < 0) return { preBooking: all, postBooking: [] };
|
|
return { preBooking: all.slice(0, idx + 1), postBooking: all.slice(idx + 1) };
|
|
}
|
|
|
|
/** The handoff milestone that flips primary ownership ET ↔ DJ (doc §11.5/§12.3). */
|
|
export const HANDOFF_MILESTONES = ['DEPARTED_FROM_DJIBOUTI', 'DEPARTED_TO_DJIBOUTI'];
|