Customer Truck Assignment and Portal Delivary Approval

This commit is contained in:
hagiye
2026-07-02 15:54:16 +03:00
319 changed files with 24989 additions and 5041 deletions

View File

@@ -0,0 +1,130 @@
/** Phased customs uploads stored by raw file `code` on contract/booking resources. */
export type ClearanceWorkflowFileOwner = "customer" | "gl_et" | "gl_dj";
export type ClearanceWorkflowFileCategory =
| "declaration"
| "duty"
| "transit"
| "djibouti";
export interface ClearanceWorkflowFileCatalogEntry {
code: string;
label: string;
uploadedBy: ClearanceWorkflowFileOwner;
category: ClearanceWorkflowFileCategory;
/** Omit when the file applies to both directions. */
tradeDirection?: "IMPORT" | "EXPORT";
}
export const CLEARANCE_WORKFLOW_FILE_CATALOG: ClearanceWorkflowFileCatalogEntry[] = [
{ code: "im4", label: "IM4 Declaration", uploadedBy: "gl_et", category: "declaration", tradeDirection: "IMPORT" },
{ code: "im5", label: "IM5 Declaration", uploadedBy: "gl_et", category: "declaration", tradeDirection: "IMPORT" },
{ code: "ex3", label: "EX3 Declaration", uploadedBy: "gl_et", category: "declaration", tradeDirection: "EXPORT" },
{ code: "ex8", label: "EX8 Declaration", uploadedBy: "gl_et", category: "declaration", tradeDirection: "EXPORT" },
{ code: "duty_tax_notice", label: "Duty / Tax Notice", uploadedBy: "gl_et", category: "duty" },
{ code: "duty_tax_receipt", label: "Duty / Tax Payment Slip", uploadedBy: "customer", category: "duty" },
{ code: "transit_permitted", label: "Transit Permit", uploadedBy: "gl_et", category: "transit", tradeDirection: "IMPORT" },
{
code: "export_transport_document",
label: "Transit Permit",
uploadedBy: "gl_et",
category: "transit",
tradeDirection: "EXPORT",
},
{ code: "delivery_order", label: "Delivery Order", uploadedBy: "gl_dj", category: "djibouti", tradeDirection: "IMPORT" },
{ code: "release_order", label: "Release Order", uploadedBy: "gl_dj", category: "djibouti", tradeDirection: "EXPORT" },
];
/** Legacy single-type declaration codes (still shown when already uploaded). */
export const LEGACY_DECLARATION_FILE_CODES = ["im4", "im5", "ex3", "ex8"] as const;
/** Multi-file declaration uploads use `declaration_0`, `declaration_1`, … */
export const DECLARATION_FILE_PREFIX = "declaration_";
export function isDeclarationFileCode(code: string | null | undefined): boolean {
if (!code) return false;
const lower = code.toLowerCase();
return (
(LEGACY_DECLARATION_FILE_CODES as readonly string[]).includes(lower) ||
lower === "declaration" ||
lower.startsWith(DECLARATION_FILE_PREFIX)
);
}
export interface ClearanceWorkflowFile {
code: string;
label: string;
uploadedBy: ClearanceWorkflowFileOwner;
category: ClearanceWorkflowFileCategory;
file: { id: string; name: string; url: string } | null;
}
const CATALOG_BY_CODE = new Map(
CLEARANCE_WORKFLOW_FILE_CATALOG.map((e) => [e.code, e]),
);
export function clearanceWorkflowFileLabel(code: string | null | undefined): string | null {
if (!code) return null;
return CATALOG_BY_CODE.get(code)?.label ?? null;
}
export function declarationFileLabel(code: string, index?: number): string {
const lower = code.toLowerCase();
const legacy = CATALOG_BY_CODE.get(lower);
if (legacy?.category === "declaration") return legacy.label;
if (lower.startsWith(DECLARATION_FILE_PREFIX) || lower === "declaration") {
return index != null ? `Declaration document ${index + 1}` : "Customs declaration";
}
return code;
}
/** Legacy single import transit permit code. */
export const LEGACY_IMPORT_TRANSIT_PERMIT_CODE = "transit_permitted";
/** Multi-file import transit permit uploads use `transit_permit_0`, `transit_permit_1`, … */
export const TRANSIT_PERMIT_FILE_PREFIX = "transit_permit_";
export function isImportTransitPermitFileCode(code: string | null | undefined): boolean {
if (!code) return false;
const lower = code.toLowerCase();
return (
lower === LEGACY_IMPORT_TRANSIT_PERMIT_CODE || lower.startsWith(TRANSIT_PERMIT_FILE_PREFIX)
);
}
export function transitPermitFileLabel(code: string, index?: number): string {
const lower = code.toLowerCase();
if (lower === LEGACY_IMPORT_TRANSIT_PERMIT_CODE) return "Transit Permit";
if (lower.startsWith(TRANSIT_PERMIT_FILE_PREFIX)) {
return index != null ? `Transit permit ${index + 1}` : "Transit Permit";
}
return code;
}
export const LEGACY_EXPORT_TRANSPORT_CODE = "export_transport_document";
export function isExportTransportFileCode(code: string | null | undefined): boolean {
if (!code) return false;
const lower = code.toLowerCase();
return (
lower === LEGACY_EXPORT_TRANSPORT_CODE ||
lower.startsWith(`${LEGACY_EXPORT_TRANSPORT_CODE}_`)
);
}
export function exportTransportFileLabel(code: string, index?: number): string {
const lower = code.toLowerCase();
if (lower === LEGACY_EXPORT_TRANSPORT_CODE) return "Transit Permit";
if (lower.startsWith(`${LEGACY_EXPORT_TRANSPORT_CODE}_`)) {
return index != null ? `Transit permit ${index + 1}` : "Transit Permit";
}
return code;
}
export function catalogEntriesForTradeDirection(
tradeDirection: string,
): ClearanceWorkflowFileCatalogEntry[] {
return CLEARANCE_WORKFLOW_FILE_CATALOG.filter(
(e) => !e.tradeDirection || e.tradeDirection === tradeDirection,
);
}

View File

@@ -259,6 +259,39 @@ export interface ContractClearanceView {
documents: ContractClearanceDocument[];
/** True once every required customer document is APPROVED. */
allApproved: boolean;
/** Current phased clearance step (ONE_TIME customs). */
phase?: ContractDocPhase | null;
/** Pre-booking milestone rows for this contract cycle. */
milestones?: IClearanceMilestone[];
/** What should happen next in the workflow. */
nextAction?: ClearanceNextAction | null;
dutyRequired?: boolean | null;
roHold?: boolean;
roHoldReason?: string | null;
vesselDepartureDate?: string | null;
roAmendmentRequestedAt?: string | null;
bookingReady?: boolean;
preClearanceFinalized?: boolean;
/** Export post-booking clearance finalized after transit permit upload. */
exportClearanceFinalized?: boolean;
linkedBookingId?: string | null;
dutyAdvice?: {
amount: number;
currency: string;
declarationSerial?: string | null;
noticeFile?: { id: string; name: string; url: string } | null;
} | null;
/** Phased customs uploads (IM4, DO, transit permit, etc.) with friendly labels. */
workflowFiles?: import("./clearance-files.catalog").ClearanceWorkflowFile[];
}
export type ClearanceActorRole = "CUSTOMER" | "GL_ET" | "GL_DJ" | "OPERATIONS";
export interface ClearanceNextAction {
actor: ClearanceActorRole;
action: string;
milestoneCode?: string | null;
blockedReason?: string | null;
}
/** Phased clearance document upload slots (doc §5.13). */
@@ -334,6 +367,7 @@ export const IMPORT_MILESTONES = [
"DECLARED",
"DUTY_TAXES_ADVISED",
"DUTY_TAX_PAID",
"TRANSIT_PERMIT_UPLOADED",
"DO_COLLECTED",
"WAGON_REQUESTED",
"FREIGHT_PAYMENT_SETTLED",
@@ -366,6 +400,7 @@ export const EXPORT_MILESTONES = [
"FREIGHT_PAYMENT_PENDING",
"FREIGHT_PAYMENT_SETTLED",
"WAGON_ALLOCATED",
"EXPORT_TRANSPORT_ISSUED",
"CARGO_ARRIVED",
"READY_FOR_LOADING",
"LOADED",
@@ -411,7 +446,10 @@ export interface IContract extends BaseEntity {
id: string;
code: string;
serviceName: string;
description?: string | null;
canBeBookedAlone: boolean;
includesFirstMile?: boolean;
includesLastMile?: boolean;
includesCustoms: boolean;
} | null;
paymentCurrency: string;
@@ -557,6 +595,7 @@ export interface CreateBulkLineDto {
cargoWeightTons?: number;
itemCount?: number;
hazardousQuantity?: number;
reeferQuantity?: number;
}
/** Shipment booking created under a contract (Path A customer, Path B GL ET). */
@@ -614,6 +653,39 @@ export interface IBookingRequest extends BaseEntity {
reviewedByStaffId?: string | null;
reviewedAt?: string | null;
reviewNote?: string | null;
/** Loaded contract relation (request detail response includes it). */
contract?: BookingRequestContract | null;
}
/** Customer (company) summary carried on a request's contract. */
export interface BookingRequestCompany {
id: string;
name?: string | null;
tin?: string | null;
email?: string | null;
phone?: string | null;
address?: string | null;
contactPersonName?: string | null;
contactPersonPhone?: string | null;
}
/**
* The slice of the contract surfaced on the shipment-request detail page:
* identity, service type (mile/customs flags), customer, routes and cargo scope.
*/
export interface BookingRequestContract {
id: string;
reference: string;
contractKind: ContractKind;
tradeDirection: ContractTradeDirection;
freightType: ContractFreightType;
customsClearingEnabled: boolean;
paymentCurrency: string;
contractValidUntil?: string | null;
company?: BookingRequestCompany | null;
serviceType?: IContract["serviceType"];
routes?: IContractRoute[];
cargoScope?: IContractCargoScope[];
}
export interface CreateBookingRequestDto {

View File

@@ -1,10 +1,12 @@
import type { BaseEntity } from "../common";
import { ClearanceNextAction, ContractDocPhase, IClearanceMilestone } from "./contracts";
export * from "./dropdown_settings";
export * from "./file_upload_settings";
export * from "./overview";
export * from "./etrade";
export * from "./contracts";
export * from "./clearance-files.catalog";
export enum TradeDirection {
IMPORT = "IMPORT",
@@ -141,6 +143,8 @@ export enum InvoiceStatus {
Overdue = "OVERDUE",
Cancelled = "CANCELLED",
Refunded = "REFUNDED",
/** Pay window closed before settlement; terminal, cannot be paid. */
Expired = "EXPIRED",
}
/** Originating subsystem an invoice bills for; namespaces invoice events. */
@@ -148,14 +152,8 @@ export enum InvoiceSource {
Booking = "booking",
Warehouse = "warehouse",
Demurrage = "demurrage",
}
/**
* What an invoice bills for within its source — the discriminator when one
* entity carries several invoices (e.g. a booking's up-front vs final charge).
*/
export enum InvoiceType {
Prepaid = "PREPAID",
FirstMile = "firstmile",
LastMile = "lastmile"
}
export enum SchedulingStatus {
@@ -443,6 +441,10 @@ export interface IBooking extends BaseEntity {
isHazardous: boolean;
isRefrigerated: boolean;
/** Bulk-only hazardous amount in the cargo's unit (tons/items); 0 otherwise. */
bulkHazardousQuantity?: number;
/** Bulk-only refrigerated amount in the cargo's unit (tons/items); 0 otherwise. */
bulkReeferQuantity?: number;
tradeDirection: "IMPORT" | "EXPORT";
paymentCurrency: string;
@@ -533,6 +535,26 @@ export interface ClearanceView {
documents: ClearanceDocument[];
/** True once every required customer document is APPROVED (the 100% gate). */
allApproved: boolean;
/** Phased clearance (GENERAL + customs per-booking). */
phase?: ContractDocPhase | null;
milestones?: IClearanceMilestone[];
nextAction?: ClearanceNextAction | null;
dutyRequired?: boolean | null;
roHold?: boolean;
roHoldReason?: string | null;
vesselDepartureDate?: string | null;
roAmendmentRequestedAt?: string | null;
/** Boundary milestone complete — customer may proceed to operations. */
operationReady?: boolean;
preClearanceFinalized?: boolean;
dutyAdvice?: {
amount: number;
currency: string;
declarationSerial?: string | null;
noticeFile?: { id: string; name: string; url: string } | null;
} | null;
/** Phased customs uploads (IM4, DO, transit permit, etc.) with friendly labels. */
workflowFiles?: import("./clearance-files.catalog").ClearanceWorkflowFile[];
}
/** Company an invoice is billed to (minimal projection). */
@@ -571,6 +593,10 @@ export interface IInvoice extends BaseEntity {
companyProfileId: string;
companyProfile?: IInvoiceCompanyProfile;
totalAmount: number;
/** Cumulative amount settled so far (supports partial payment). */
paidAmount: number;
/** Outstanding balance = totalAmount - paidAmount (0 once fully paid). */
balanceAmount: number;
currency: string;
status: InvoiceStatus;
/** Originating subsystem: booking / warehouse / demurrage. */
@@ -638,7 +664,6 @@ export interface BookingReferenceCargoTypeChild {
id: string;
name: string;
code: string;
show_free_text_box: boolean;
/** How this cargo is measured (PER_TON / PER_ITEM); null when unset. */
unit_of_measure?: CargoUnitOfMeasure | null;
}
@@ -728,6 +753,10 @@ export interface CreateBookingContainerDto {
containerTypeId: string;
quantity: number;
vgmPerUnitTons: number;
/** How many of this line's containers are hazardous (0..quantity). */
hazardousQuantity?: number;
/** How many of this line's containers are refrigerated (0..quantity). */
reeferQuantity?: number;
}
/** A contracted route+quantity line for a GENERAL contract. */
@@ -779,6 +808,10 @@ export interface CreateBookingDto {
isHazardous?: boolean | undefined;
/** Booking-level refrigerated flag (bulk freight only; containers derive reefer from the container type). */
isReefer?: boolean | undefined;
/** Bulk-only: hazardous amount in the cargo's unit (tons or items), <= cargoTotalWeightVgm. */
bulkHazardousQuantity?: number | undefined;
/** Bulk-only: refrigerated amount in the cargo's unit (tons or items), <= cargoTotalWeightVgm. */
bulkReeferQuantity?: number | undefined;
paymentCurrency: string;
pnrCode?: string | undefined;
startDate?: string | undefined;