contrat,booking,global logestic

This commit is contained in:
Marshal
2026-06-26 23:24:48 +00:00
parent f931342f31
commit 01d53c218c
105 changed files with 19573 additions and 909 deletions

View File

@@ -26,7 +26,8 @@ const DEMO_STAFF_USERS = [
{ email: 'operations@edr.local', username: 'operations', roleKey: 'edr_operations_officer' },
{ email: 'director@edr.local', username: 'director', roleKey: 'edr_director' },
{ email: 'ceo@edr.local', username: 'ceo', roleKey: 'edr_ceo' },
{ email: 'gl@edr.local', username: 'gl', roleKey: 'edr_global_logistics' },
{ email: 'gl-et@edr.local', username: 'gl_et', roleKey: 'edr_gl_ethiopia' },
{ email: 'gl-dj@edr.local', username: 'gl_dj', roleKey: 'edr_gl_djibouti' },
] as const;
/**

View File

@@ -238,9 +238,14 @@ export const EDR_FREIGHT_ROLES: FreightSeedRole[] = [
permissionKeys: [...ROLE_PERMISSION_PRESETS.marketing],
},
{
key: "edr_global_logistics",
name: { en: "EDR Global Logistics" },
permissionKeys: [...ROLE_PERMISSION_PRESETS.globalLogistics],
key: "edr_gl_ethiopia",
name: { en: "EDR Global Logistics — Ethiopia" },
permissionKeys: [...ROLE_PERMISSION_PRESETS.glEthiopia],
},
{
key: "edr_gl_djibouti",
name: { en: "EDR Global Logistics — Djibouti" },
permissionKeys: [...ROLE_PERMISSION_PRESETS.glDjibouti],
},
{
key: "edr_org_manager",

View File

@@ -352,6 +352,80 @@ const CLEARANCE_DOCUMENT_SETTINGS: OnboardingDocumentSetting[] = [
},
];
// ── Contract pre-booking clearance settings (Path B) ────────────────────────
// For customs-clearance contracts the customer uploads clearance documents on
// the CONTRACT (before any booking exists). Same customer doc sets as the legacy
// booking clearance, plus the GL output sets, keyed on the contract. Resolved by
// contract-clearance.util.ts (codes: contract_clearance_{op}_{freight} and
// contract_clearance_output_{op}_container).
const CONTRACT_CLEARANCE_ENTITY = "contract_clearance";
const CONTRACT_CLEARANCE_SETTINGS: OnboardingDocumentSetting[] = [
{
code: "contract_clearance_import_container",
label: "Contract clearance documents (import container)",
entity: CONTRACT_CLEARANCE_ENTITY,
fields: IMPORT_CONTAINER_FIELDS,
},
{
code: "contract_clearance_export_container",
label: "Contract clearance documents (export container)",
entity: CONTRACT_CLEARANCE_ENTITY,
fields: EXPORT_CONTAINER_FIELDS,
},
{
code: "contract_clearance_import_bulk",
label: "Contract clearance documents (import bulk)",
entity: CONTRACT_CLEARANCE_ENTITY,
fields: IMPORT_BULK_FIELDS,
},
{
code: "contract_clearance_export_bulk",
label: "Contract clearance documents (export bulk)",
entity: CONTRACT_CLEARANCE_ENTITY,
fields: EXPORT_BULK_FIELDS,
},
// GL ET output sets uploaded during pre-booking clearance.
{
code: "contract_clearance_output_import_container",
label: "Contract customs output documents (import container)",
entity: CONTRACT_CLEARANCE_ENTITY,
fields: IMPORT_CONTAINER_OUTPUT_FIELDS,
},
{
code: "contract_clearance_output_export_container",
label: "Contract customs output documents (export container)",
entity: CONTRACT_CLEARANCE_ENTITY,
fields: EXPORT_CONTAINER_OUTPUT_FIELDS,
},
];
// ── Contract intake settings ────────────────────────────────────────────────
// Commercial/framework documents attached at contract submission (wizard step 5),
// distinct from the post-sign clearance docs above.
const CONTRACT_INTAKE_ENTITY = "contract_intake";
const CONTRACT_INTAKE_FIELDS: OnboardingField[] = [
clearanceField("commercial_framework", "Commercial Framework / Agreement", 1, {
required: false,
}),
clearanceField("onboarding_attachment", "Onboarding Attachment", 2, {
required: false,
}),
clearanceField("supporting_document", "Supporting Document", 3, {
required: false,
}),
];
const CONTRACT_INTAKE_SETTINGS: OnboardingDocumentSetting[] = [
{
code: "contract_intake_documents",
label: "Contract intake documents",
entity: CONTRACT_INTAKE_ENTITY,
fields: CONTRACT_INTAKE_FIELDS,
},
];
const CLEARANCE_DESCRIPTION =
"Operation/clearance documents collected after contract execution, by operation, freight type and customs.";
@@ -377,6 +451,16 @@ export class FileUploadSettingsSeeder {
...s,
description: CLEARANCE_DESCRIPTION,
})),
...CONTRACT_CLEARANCE_SETTINGS.map((s) => ({
...s,
description:
"Pre-booking clearance documents collected on the contract (Path B), by operation and freight type.",
})),
...CONTRACT_INTAKE_SETTINGS.map((s) => ({
...s,
description:
"Commercial/framework documents attached at contract submission.",
})),
];
for (const documentSetting of allSettings) {

View File

@@ -62,6 +62,25 @@ export const BOOKING_PERMISSIONS: FreightPermissionSeed[] = [
perm('a1000001-0001-4000-8000-000000000013', 'edr_freight_app:admin', 'Freight administration'),
];
/**
* Contract-phase permissions (contractbooking separation). Mirror the booking
* approval/sign/clearance permissions but scoped to the new contracts module.
*/
export const CONTRACT_PERMISSIONS: FreightPermissionSeed[] = [
perm('a3000001-0001-4000-8000-000000000001', 'edr_freight_app:contracts:view', 'View contracts'),
perm('a3000001-0001-4000-8000-000000000002', 'edr_freight_app:contracts:staff_accept', 'Accept contract intake'),
perm('a3000001-0001-4000-8000-000000000003', 'edr_freight_app:contracts:request_changes', 'Request contract changes'),
perm('a3000001-0001-4000-8000-000000000004', 'edr_freight_app:contracts:reject', 'Reject contract'),
perm('a3000001-0001-4000-8000-000000000005', 'edr_freight_app:contracts:approve_line_staff', 'Approve contract as line staff'),
perm('a3000001-0001-4000-8000-000000000006', 'edr_freight_app:contracts:approve_director', 'Approve contract as director'),
perm('a3000001-0001-4000-8000-000000000007', 'edr_freight_app:contracts:approve_ceo', 'Approve contract as CEO'),
perm('a3000001-0001-4000-8000-000000000008', 'edr_freight_app:contracts:generate_contract', 'Generate contract document'),
perm('a3000001-0001-4000-8000-000000000009', 'edr_freight_app:contracts:sign_staff', 'Staff contract signature'),
perm('a3000001-0001-4000-8000-00000000000a', 'edr_freight_app:contracts:clearance_review', 'Review pre-booking clearance docs'),
perm('a3000001-0001-4000-8000-00000000000b', 'edr_freight_app:contracts:finalize_clearance', 'Finalize pre-booking clearance'),
perm('a3000001-0001-4000-8000-00000000000c', 'edr_freight_app:contracts:create_booking', 'GL ET create booking under contract'),
];
const RULE_ENGINE_PERMISSION_IDS: Record<RuleEngineResourceSlug, { view: string; manage: string }> = {
'cargo-types': { view: 'b2000001-0001-4000-8000-000000000001', manage: 'b2000001-0001-4000-8000-000000000002' },
'container-types': { view: 'b2000001-0001-4000-8000-000000000003', manage: 'b2000001-0001-4000-8000-000000000004' },
@@ -88,6 +107,7 @@ export const RULE_ENGINE_PERMISSIONS: FreightPermissionSeed[] = RULE_ENGINE_RESO
export const BOOKING_RULE_ENGINE_PERMISSIONS = [
...BOOKING_PERMISSIONS,
...CONTRACT_PERMISSIONS,
...RULE_ENGINE_PERMISSIONS,
];
@@ -114,6 +134,20 @@ export const FREIGHT_PERMS = {
uploadClearanceOutput: 'edr_freight_app:bookings:upload_clearance_output',
finalizeClearance: 'edr_freight_app:bookings:finalize_clearance',
},
contracts: {
view: 'edr_freight_app:contracts:view',
staffAccept: 'edr_freight_app:contracts:staff_accept',
requestChanges: 'edr_freight_app:contracts:request_changes',
reject: 'edr_freight_app:contracts:reject',
approveLineStaff: 'edr_freight_app:contracts:approve_line_staff',
approveDirector: 'edr_freight_app:contracts:approve_director',
approveCeo: 'edr_freight_app:contracts:approve_ceo',
generateContract: 'edr_freight_app:contracts:generate_contract',
signStaff: 'edr_freight_app:contracts:sign_staff',
clearanceReview: 'edr_freight_app:contracts:clearance_review',
finalizeClearance: 'edr_freight_app:contracts:finalize_clearance',
createBooking: 'edr_freight_app:contracts:create_booking',
},
trainScheduling: {
view: 'edr_freight_app:train_scheduling:view',
manage: 'edr_freight_app:train_scheduling:manage',
@@ -146,6 +180,11 @@ export const ROLE_PERMISSION_PRESETS = {
FREIGHT_PERMS.bookings.approveLineStaff,
FREIGHT_PERMS.bookings.rejectApproval,
FREIGHT_PERMS.bookings.cancel,
FREIGHT_PERMS.contracts.view,
FREIGHT_PERMS.contracts.staffAccept,
FREIGHT_PERMS.contracts.requestChanges,
FREIGHT_PERMS.contracts.reject,
FREIGHT_PERMS.contracts.approveLineStaff,
...allRuleEngineViewKeys(),
],
// Operations Officer: train scheduling + wagon allocation + transit/complete
@@ -164,12 +203,17 @@ export const ROLE_PERMISSION_PRESETS = {
FREIGHT_PERMS.bookings.approveDirector,
FREIGHT_PERMS.bookings.rejectApproval,
FREIGHT_PERMS.bookings.generateContract,
FREIGHT_PERMS.contracts.view,
FREIGHT_PERMS.contracts.approveDirector,
FREIGHT_PERMS.contracts.generateContract,
...allRuleEngineViewKeys(),
],
ceo: [
FREIGHT_PERMS.bookings.view,
FREIGHT_PERMS.bookings.approveCeo,
FREIGHT_PERMS.bookings.rejectApproval,
FREIGHT_PERMS.contracts.view,
FREIGHT_PERMS.contracts.approveCeo,
...allRuleEngineViewKeys(),
],
finance: [FREIGHT_PERMS.bookings.view],
@@ -178,11 +222,28 @@ export const ROLE_PERMISSION_PRESETS = {
// clearance:view permission lists the clearance bookings. Reviews customer
// clearance documents, uploads customs output documents, and finalizes the
// clearance gate.
globalLogistics: [
// GL Ethiopia (edr_gl_ethiopia): reviews pre-booking clearance docs on the
// contract, uploads ET output docs, finalizes clearance, and EXCLUSIVELY creates
// the booking under a customs contract (Path B). Also runs post-booking ET
// milestones + the legacy booking-clearance permissions during migration.
glEthiopia: [
FREIGHT_PERMS.contracts.view,
FREIGHT_PERMS.contracts.clearanceReview,
FREIGHT_PERMS.contracts.finalizeClearance,
FREIGHT_PERMS.contracts.createBooking,
FREIGHT_PERMS.bookings.clearanceView,
FREIGHT_PERMS.bookings.reviewDocuments,
FREIGHT_PERMS.bookings.uploadClearanceOutput,
FREIGHT_PERMS.bookings.finalizeClearance,
FREIGHT_PERMS.bookings.operations,
],
// GL Djibouti (edr_gl_djibouti): DO/RO collection, gatepass, loading milestones,
// damage reports. Read-only on the contract; no booking creation.
glDjibouti: [
FREIGHT_PERMS.contracts.view,
FREIGHT_PERMS.bookings.clearanceView,
FREIGHT_PERMS.bookings.uploadClearanceOutput,
FREIGHT_PERMS.bookings.operations,
],
// Marketing handles intake through contract (same as line staff here) and,
// for non-customs bookings, reviews/finalizes the customer's clearance
@@ -199,6 +260,13 @@ export const ROLE_PERMISSION_PRESETS = {
FREIGHT_PERMS.bookings.signStaff,
FREIGHT_PERMS.bookings.reviewDocuments,
FREIGHT_PERMS.bookings.finalizeClearance,
FREIGHT_PERMS.contracts.view,
FREIGHT_PERMS.contracts.staffAccept,
FREIGHT_PERMS.contracts.requestChanges,
FREIGHT_PERMS.contracts.reject,
FREIGHT_PERMS.contracts.approveLineStaff,
FREIGHT_PERMS.contracts.generateContract,
FREIGHT_PERMS.contracts.signStaff,
],
orgManager: [...BOOKING_RULE_ENGINE_PERMISSION_KEYS],
} as const;
@@ -206,5 +274,9 @@ export const ROLE_PERMISSION_PRESETS = {
export const PERMISSIONS_CATALOG = BOOKING_RULE_ENGINE_PERMISSIONS.map((p) => ({
key: p.key,
label: p.name.en,
module: p.key.includes(':bookings:') ? 'bookings' : 'rule_engine',
module: p.key.includes(':bookings:')
? 'bookings'
: p.key.includes(':contracts:')
? 'contracts'
: 'rule_engine',
}));

View File

@@ -18,7 +18,8 @@ const STAFF_USERS = [
{ email: 'linestaff@edr.local', username: 'linestaff', roleKey: 'edr_line_staff' },
{ email: 'director@edr.local', username: 'director', roleKey: 'edr_director' },
{ email: 'ceo@edr.local', username: 'ceo', roleKey: 'edr_ceo' },
{ email: 'gl@edr.local', username: 'gl', roleKey: 'edr_global_logistics' },
{ email: 'gl-et@edr.local', username: 'gl_et', roleKey: 'edr_gl_ethiopia' },
{ email: 'gl-dj@edr.local', username: 'gl_dj', roleKey: 'edr_gl_djibouti' },
] as const;
@Injectable()