New Transit Agents admin table (name, valid-from/to, active/suspended, validity badge) — DJ assign step now picks from it, active+valid only.

This commit is contained in:
Marshal
2026-07-29 05:38:35 +00:00
parent 891311d861
commit b17a72c280
59 changed files with 2903 additions and 451 deletions

View File

@@ -3,6 +3,7 @@ export type ClearanceWorkflowFileOwner = "customer" | "gl_et" | "gl_dj";
export type ClearanceWorkflowFileCategory =
| "declaration"
| "draft_declaration"
| "duty"
| "transit"
| "djibouti";
@@ -120,6 +121,18 @@ export function declarationFileLabel(code: string, index?: number): string {
return code;
}
/** Multi-file draft declaration uploads use `draft_declaration_0`, `draft_declaration_1`, … */
export const DRAFT_DECLARATION_FILE_PREFIX = "draft_declaration_";
export function isDraftDeclarationFileCode(code: string | null | undefined): boolean {
if (!code) return false;
return code.toLowerCase().startsWith(DRAFT_DECLARATION_FILE_PREFIX);
}
export function draftDeclarationFileLabel(index?: number): string {
return index != null ? `Draft declaration document ${index + 1}` : "Draft declaration";
}
/** Legacy single import transit permit code. */
export const LEGACY_IMPORT_TRANSIT_PERMIT_CODE = "transit_permitted";

View File

@@ -409,12 +409,34 @@ export interface ClearanceTrainState {
arrivedAt: string | null;
}
/**
* Offload facts for the clearance booking, read-only: what came off the train
* at its destination and where it went. Populated for both directions once the
* booking has ridden a train; every field stays null/0 before that.
*/
export interface ClearanceOffloadState {
offloaded: boolean;
/** When the OFFLOADED milestone completed (falls back to the unload stamp). */
offloadedAt: string | null;
/** Yard the cargo alighted at. */
destination: string | null;
containers: number;
wagons: number;
/** Tonnes recorded on unload (booked VGM until the warehouse records it). */
weightTons: number | null;
grnNumber: string | null;
/** Warehouse yard zone the goods went into. */
location: string | null;
inventoryStatus: string | null;
}
/** Billing invoice `type` for the GL Djibouti post-offload final invoice. */
export const GL_FINAL_INVOICE_TYPE = "GL_FINAL";
/**
* Post-offload final invoice raised by GL Djibouti: customer pays offline and
* attaches a slip; GL (ET or DJ) confirms to mark it paid.
* Post-offload final invoice raised by GL Djibouti: it lands as a DRAFT the
* customer must approve, after which they pay offline and attach a slip; GL
* (ET or DJ) confirms to mark it paid.
*/
export interface ClearanceFinalInvoiceSummary {
id: string;
@@ -425,6 +447,8 @@ export interface ClearanceFinalInvoiceSummary {
description?: string | null;
invoiceFile: { id: string; name: string; url: string } | null;
slipFile: { id: string; name: string; url: string } | null;
/** When the customer approved the invoice (null while it is still a draft). */
approvedAt: string | null;
confirmedAt: string | null;
}
@@ -505,10 +529,23 @@ export interface ContractClearanceView {
noticeFile?: { id: string; name: string; url: string } | null;
} | null;
/**
* The customer's open objection to the advised duty. Present only until GL
* re-advises; `rounds` counts how many times it has been sent back.
* Import only: the draft customs declaration GL Ethiopia sends before filing
* the real one — an estimated price + files the customer must accept (or send
* back with a change request) before the "Customs declaration" step unlocks.
* Present once a draft has been uploaded, regardless of accept state.
*/
dutyDispute?: {
draftDeclaration?: {
price: number;
currency: string;
files: Array<{ id: string; name: string; url: string }>;
accepted: boolean;
} | null;
/**
* The customer's open change request on the current draft declaration.
* Present only until GL sends a corrected draft; `rounds` counts how many
* times it has been sent back.
*/
draftDeclarationChangeRequest?: {
note: string;
raisedAt: string;
rounds: number;
@@ -524,6 +561,8 @@ export interface ContractClearanceView {
t1Closed?: boolean;
t1ClosedAt?: string | null;
offloaded?: boolean;
/** Offload stats for the linked booking (null until one exists). */
offload?: ClearanceOffloadState | null;
/** GL Djibouti post-offload final invoice (export). */
finalInvoice?: ClearanceFinalInvoiceSummary | null;
/** Customs risk level assigned by GL ET (import; visible to the customer). */
@@ -588,6 +627,8 @@ export interface MilestoneMetadata {
dutyAmount?: number;
dutyCurrency?: string;
declarationSerial?: string;
draftDeclarationPrice?: number;
draftDeclarationCurrency?: string;
}
export interface IClearanceMilestone {
@@ -633,6 +674,8 @@ export const IMPORT_MILESTONES = [
"IMPORT_DOCS_UPLOADED",
"PENDING_DOCUMENT_REVIEW",
"DOCUMENTS_APPROVED",
"DRAFT_DECLARATION_UPLOADED",
"DRAFT_DECLARATION_ACCEPTED",
"UNDER_CUSTOMS_CLEARANCE",
"DECLARED",
"DUTY_TAXES_ADVISED",
@@ -664,9 +707,9 @@ export const EXPORT_MILESTONES = [
"EXPORT_DOCS_UPLOADED",
"PENDING_DOCUMENT_REVIEW",
"DOCUMENTS_APPROVED",
"RELEASE_ORDER_SECURED",
"UNDER_CUSTOMS_CLEARANCE",
"DECLARED",
"RELEASE_ORDER_SECURED",
"EXPORT_RELEASED",
"WAGON_REQUESTED",
"FREIGHT_PAYMENT_PENDING",

View File

@@ -759,6 +759,34 @@ export interface ClearanceDocument {
note: string | null;
}
/**
* One document in the GL Ethiopia ↔ GL Djibouti exchange: a free-titled file
* either desk attaches to a shipment (booking) or a contract clearance. Both
* desks see every document; only the uploader may change or remove one, and
* only documents the uploader marked visible reach the customer's portal.
*/
export interface GlExchangeDocument {
id: string;
/** Booking or contract id the document is attached to. */
entityId: string;
title: string;
/** Desk that uploaded it. */
side: "ET" | "DJ";
visibleToCustomer: boolean;
uploadedById: string | null;
uploadedByName: string | null;
uploadedAt: string;
file: {
id: string;
name: string;
url: string;
size: number;
mimeType: string;
};
/** True when the requesting user uploaded it (edit/replace/remove allowed). */
canEdit: boolean;
}
/** The clearance view for a booking, driving both portals' clearance UI. */
export interface ClearanceView {
status: string;
@@ -801,16 +829,35 @@ export interface ClearanceView {
noticeFile?: { id: string; name: string; url: string } | null;
} | null;
/**
* The customer's open objection to the advised duty. Present only until GL
* re-advises; `rounds` counts how many times it has been sent back.
* Import only: the draft customs declaration GL Ethiopia sends before filing
* the real one — an estimated price + files the customer must accept (or send
* back with a change request) before the "Customs declaration" step unlocks.
* Present once a draft has been uploaded, regardless of accept state.
*/
dutyDispute?: {
draftDeclaration?: {
price: number;
currency: string;
files: Array<{ id: string; name: string; url: string }>;
accepted: boolean;
} | null;
/**
* The customer's open change request on the current draft declaration.
* Present only until GL sends a corrected draft; `rounds` counts how many
* times it has been sent back.
*/
draftDeclarationChangeRequest?: {
note: string;
raisedAt: string;
rounds: number;
} | null;
/** Phased customs uploads (IM4, DO, transit permit, etc.) with friendly labels. */
workflowFiles?: import("./clearance-files.catalog").ClearanceWorkflowFile[];
/**
* GL-shared documents the uploader marked visible to the customer — the
* customer-facing slice of the GL Ethiopia ↔ GL Djibouti exchange, covering
* both this booking's thread and its contract's.
*/
exchangeDocuments?: GlExchangeDocument[];
/** Import post-allocation T1 transit document state (null until wagon allocation). */
t1?: import("./contracts").ClearanceT1State | null;
/** Train link state for the booking (both directions). */
@@ -820,6 +867,8 @@ export interface ClearanceView {
t1Closed?: boolean;
t1ClosedAt?: string | null;
offloaded?: boolean;
/** Offload stats for this booking (what came off the train, and where). */
offload?: import("./contracts").ClearanceOffloadState | null;
/** GL Djibouti post-offload final invoice (export). */
finalInvoice?: import("./contracts").ClearanceFinalInvoiceSummary | null;
/** Customs risk level assigned by GL ET (import; visible to the customer). */