mirror of
https://github.com/Tria-plc/edr-platform.git
synced 2026-08-30 12:18:11 +00:00
- Added new milestones for 'Transit Permit Uploaded' and 'Export Transport Document Issued' in the clearance milestone catalog. - Implemented methods in ClearanceMilestoneService to skip milestones and complete them with metadata. - Updated ContractBookingService to check boundary conditions before booking creation. - Introduced new endpoints in ContractsController for uploading customs declarations, advising duty, and handling various document uploads. - Enhanced the UI to support new clearance actions and display relevant components based on milestone statuses.
298 lines
15 KiB
TypeScript
298 lines
15 KiB
TypeScript
const EDR_FREIGHT_APP_KEY = 'edr_freight_app';
|
||
|
||
export type FreightPermissionSeed = {
|
||
id: string;
|
||
key: string;
|
||
name: { am: string; en: string };
|
||
applicationKey: string;
|
||
};
|
||
|
||
export const RULE_ENGINE_RESOURCE_SLUGS = [
|
||
'cargo-types',
|
||
'container-types',
|
||
'wagon-types',
|
||
'service-types',
|
||
'yards',
|
||
'shipping-lines',
|
||
'weight-limit-rules',
|
||
'priority-configs',
|
||
'rates',
|
||
'approval-rules',
|
||
] as const;
|
||
|
||
export type RuleEngineResourceSlug = (typeof RULE_ENGINE_RESOURCE_SLUGS)[number];
|
||
|
||
const slugToResourceKey = (slug: RuleEngineResourceSlug): string =>
|
||
slug.replace(/-/g, '_');
|
||
|
||
const perm = (
|
||
id: string,
|
||
key: string,
|
||
en: string,
|
||
): FreightPermissionSeed => ({
|
||
id,
|
||
key,
|
||
name: { am: en, en },
|
||
applicationKey: EDR_FREIGHT_APP_KEY,
|
||
});
|
||
|
||
export const BOOKING_PERMISSIONS: FreightPermissionSeed[] = [
|
||
perm('a1000001-0001-4000-8000-000000000001', 'edr_freight_app:bookings:view', 'View bookings'),
|
||
perm('a1000001-0001-4000-8000-000000000002', 'edr_freight_app:bookings:staff_accept', 'Accept booking intake'),
|
||
perm('a1000001-0001-4000-8000-000000000003', 'edr_freight_app:bookings:request_changes', 'Request booking changes'),
|
||
perm('a1000001-0001-4000-8000-000000000004', 'edr_freight_app:bookings:reject', 'Reject booking submission'),
|
||
perm('a1000001-0001-4000-8000-000000000005', 'edr_freight_app:bookings:approve_line_staff', 'Approve as line staff'),
|
||
perm('a1000001-0001-4000-8000-000000000006', 'edr_freight_app:bookings:approve_director', 'Approve as director'),
|
||
perm('a1000001-0001-4000-8000-000000000007', 'edr_freight_app:bookings:approve_ceo', 'Approve as CEO'),
|
||
perm('a1000001-0001-4000-8000-000000000008', 'edr_freight_app:bookings:reject_approval', 'Reject at approval step'),
|
||
perm('a1000001-0001-4000-8000-000000000009', 'edr_freight_app:bookings:generate_contract', 'Generate contract'),
|
||
perm('a1000001-0001-4000-8000-00000000000a', 'edr_freight_app:bookings:sign_staff', 'Staff contract signature'),
|
||
perm('a1000001-0001-4000-8000-00000000000b', 'edr_freight_app:bookings:payment_pnr', 'Generate PNR'),
|
||
perm('a1000001-0001-4000-8000-00000000000c', 'edr_freight_app:bookings:payment_verify', 'Verify payment'),
|
||
perm('a1000001-0001-4000-8000-00000000000d', 'edr_freight_app:bookings:operations', 'Booking operations'),
|
||
perm('a1000001-0001-4000-8000-00000000000e', 'edr_freight_app:bookings:cancel', 'Cancel booking'),
|
||
perm('a1000001-0001-4000-8000-000000000023', 'edr_freight_app:bookings:clearance_view', 'View customs-clearance queue'),
|
||
perm('a1000001-0001-4000-8000-000000000020', 'edr_freight_app:bookings:review_documents', 'Review clearance documents'),
|
||
perm('a1000001-0001-4000-8000-000000000021', 'edr_freight_app:bookings:upload_clearance_output', 'Upload customs output documents'),
|
||
perm('a1000001-0001-4000-8000-000000000022', 'edr_freight_app:bookings:finalize_clearance', 'Finalize document clearance'),
|
||
perm('a1000001-0001-4000-8000-00000000000f', 'edr_freight_app:train_scheduling:view', 'View train scheduling'),
|
||
perm('a1000001-0001-4000-8000-000000000010', 'edr_freight_app:train_scheduling:manage', 'Manage train scheduling'),
|
||
perm('a1000001-0001-4000-8000-000000000011', 'edr_freight_app:fleet:view', 'View fleet'),
|
||
perm('a1000001-0001-4000-8000-000000000012', 'edr_freight_app:fleet:manage', 'Manage fleet'),
|
||
perm('a1000001-0001-4000-8000-000000000013', 'edr_freight_app:admin', 'Freight administration'),
|
||
];
|
||
|
||
/**
|
||
* Contract-phase permissions (contract–booking 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'),
|
||
perm('a3000001-0001-4000-8000-00000000000d', 'edr_freight_app:contracts:ops_clearance_review', 'Operations review of self-clearance docs (Path A)'),
|
||
perm('a3000001-0001-4000-8000-00000000000e', 'edr_freight_app:contracts:clearance_et_actions', 'GL Ethiopia phased clearance actions'),
|
||
perm('a3000001-0001-4000-8000-00000000000f', 'edr_freight_app:contracts:clearance_dj_actions', 'GL Djibouti phased clearance actions'),
|
||
perm('a3000001-0001-4000-8000-000000000010', 'edr_freight_app:contracts:clearance_duty_advise', 'Advise contract duty/tax'),
|
||
];
|
||
|
||
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' },
|
||
'wagon-types': { view: 'b2000001-0001-4000-8000-000000000015', manage: 'b2000001-0001-4000-8000-000000000016' },
|
||
'service-types': { view: 'b2000001-0001-4000-8000-000000000005', manage: 'b2000001-0001-4000-8000-000000000006' },
|
||
yards: { view: 'b2000001-0001-4000-8000-000000000007', manage: 'b2000001-0001-4000-8000-000000000008' },
|
||
'shipping-lines': { view: 'b2000001-0001-4000-8000-000000000009', manage: 'b2000001-0001-4000-8000-00000000000a' },
|
||
'weight-limit-rules': { view: 'b2000001-0001-4000-8000-00000000000b', manage: 'b2000001-0001-4000-8000-00000000000c' },
|
||
'priority-configs': { view: 'b2000001-0001-4000-8000-00000000000f', manage: 'b2000001-0001-4000-8000-000000000010' },
|
||
rates: { view: 'b2000001-0001-4000-8000-000000000011', manage: 'b2000001-0001-4000-8000-000000000012' },
|
||
'approval-rules': { view: 'b2000001-0001-4000-8000-000000000013', manage: 'b2000001-0001-4000-8000-000000000014' },
|
||
};
|
||
|
||
export const RULE_ENGINE_PERMISSIONS: FreightPermissionSeed[] = RULE_ENGINE_RESOURCE_SLUGS.flatMap(
|
||
(slug) => {
|
||
const resource = slugToResourceKey(slug);
|
||
const ids = RULE_ENGINE_PERMISSION_IDS[slug];
|
||
return [
|
||
perm(ids.view, `edr_freight_app:rule_engine:${resource}:view`, `View ${slug}`),
|
||
perm(ids.manage, `edr_freight_app:rule_engine:${resource}:manage`, `Manage ${slug}`),
|
||
];
|
||
},
|
||
);
|
||
|
||
export const BOOKING_RULE_ENGINE_PERMISSIONS = [
|
||
...BOOKING_PERMISSIONS,
|
||
...CONTRACT_PERMISSIONS,
|
||
...RULE_ENGINE_PERMISSIONS,
|
||
];
|
||
|
||
export const BOOKING_RULE_ENGINE_PERMISSION_KEYS = BOOKING_RULE_ENGINE_PERMISSIONS.map(
|
||
(p) => p.key,
|
||
);
|
||
|
||
export const FREIGHT_PERMS = {
|
||
bookings: {
|
||
view: 'edr_freight_app:bookings:view',
|
||
clearanceView: 'edr_freight_app:bookings:clearance_view',
|
||
staffAccept: 'edr_freight_app:bookings:staff_accept',
|
||
requestChanges: 'edr_freight_app:bookings:request_changes',
|
||
reject: 'edr_freight_app:bookings:reject',
|
||
approveLineStaff: 'edr_freight_app:bookings:approve_line_staff',
|
||
approveDirector: 'edr_freight_app:bookings:approve_director',
|
||
approveCeo: 'edr_freight_app:bookings:approve_ceo',
|
||
rejectApproval: 'edr_freight_app:bookings:reject_approval',
|
||
generateContract: 'edr_freight_app:bookings:generate_contract',
|
||
signStaff: 'edr_freight_app:bookings:sign_staff',
|
||
operations: 'edr_freight_app:bookings:operations',
|
||
cancel: 'edr_freight_app:bookings:cancel',
|
||
reviewDocuments: 'edr_freight_app:bookings:review_documents',
|
||
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',
|
||
opsClearanceReview: 'edr_freight_app:contracts:ops_clearance_review',
|
||
clearanceEtActions: 'edr_freight_app:contracts:clearance_et_actions',
|
||
clearanceDjActions: 'edr_freight_app:contracts:clearance_dj_actions',
|
||
clearanceDutyAdvise: 'edr_freight_app:contracts:clearance_duty_advise',
|
||
},
|
||
trainScheduling: {
|
||
view: 'edr_freight_app:train_scheduling:view',
|
||
manage: 'edr_freight_app:train_scheduling:manage',
|
||
},
|
||
fleet: {
|
||
view: 'edr_freight_app:fleet:view',
|
||
manage: 'edr_freight_app:fleet:manage',
|
||
},
|
||
admin: 'edr_freight_app:admin',
|
||
ruleEngine: {
|
||
view: (slug: RuleEngineResourceSlug) =>
|
||
`edr_freight_app:rule_engine:${slugToResourceKey(slug)}:view`,
|
||
manage: (slug: RuleEngineResourceSlug) =>
|
||
`edr_freight_app:rule_engine:${slugToResourceKey(slug)}:manage`,
|
||
},
|
||
} as const;
|
||
|
||
const allRuleEngineViewKeys = () =>
|
||
RULE_ENGINE_RESOURCE_SLUGS.map((s) => FREIGHT_PERMS.ruleEngine.view(s));
|
||
|
||
export const ROLE_PERMISSION_PRESETS = {
|
||
// Marketing / line staff: drives a booking from intake through line-staff
|
||
// approval and contract generation/signing — i.e. until the contract is ready
|
||
// and signed. No director/CEO approval, no scheduling, no operations.
|
||
lineStaff: [
|
||
FREIGHT_PERMS.bookings.view,
|
||
FREIGHT_PERMS.bookings.staffAccept,
|
||
FREIGHT_PERMS.bookings.requestChanges,
|
||
FREIGHT_PERMS.bookings.reject,
|
||
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
|
||
// + fleet management (wagons, trains, locomotives, routes, containers, cargo).
|
||
operationsOfficer: [
|
||
FREIGHT_PERMS.bookings.view,
|
||
FREIGHT_PERMS.bookings.operations,
|
||
FREIGHT_PERMS.trainScheduling.view,
|
||
FREIGHT_PERMS.trainScheduling.manage,
|
||
FREIGHT_PERMS.fleet.view,
|
||
FREIGHT_PERMS.fleet.manage,
|
||
// Path A (no customs): Operations reviews the customer's self-clearance docs
|
||
// on the contract before the customer may create a shipment booking.
|
||
FREIGHT_PERMS.contracts.view,
|
||
FREIGHT_PERMS.contracts.opsClearanceReview,
|
||
...allRuleEngineViewKeys(),
|
||
],
|
||
director: [
|
||
FREIGHT_PERMS.bookings.view,
|
||
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],
|
||
// Global Logistics: manages ONLY the customs-clearance queue. Scoped out of
|
||
// the general booking-request list (no bookings:view) — instead a dedicated
|
||
// clearance:view permission lists the clearance bookings. Reviews customer
|
||
// clearance documents, uploads customs output documents, and finalizes the
|
||
// clearance gate.
|
||
// 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.contracts.clearanceEtActions,
|
||
FREIGHT_PERMS.contracts.clearanceDutyAdvise,
|
||
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.contracts.clearanceDjActions,
|
||
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
|
||
// documents from the booking detail (customs bookings go to Global Logistics).
|
||
marketing: [
|
||
FREIGHT_PERMS.bookings.view,
|
||
FREIGHT_PERMS.bookings.staffAccept,
|
||
FREIGHT_PERMS.bookings.requestChanges,
|
||
FREIGHT_PERMS.bookings.reject,
|
||
FREIGHT_PERMS.bookings.approveLineStaff,
|
||
FREIGHT_PERMS.bookings.rejectApproval,
|
||
FREIGHT_PERMS.bookings.cancel,
|
||
FREIGHT_PERMS.bookings.generateContract,
|
||
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;
|
||
|
||
export const PERMISSIONS_CATALOG = BOOKING_RULE_ENGINE_PERMISSIONS.map((p) => ({
|
||
key: p.key,
|
||
label: p.name.en,
|
||
module: p.key.includes(':bookings:')
|
||
? 'bookings'
|
||
: p.key.includes(':contracts:')
|
||
? 'contracts'
|
||
: 'rule_engine',
|
||
}));
|