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', 'yard-distances', ] 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'), perm('a1000001-0001-4000-8000-000000000024', 'edr_freight_app:bookings:create', 'Create booking'), ]; /** * 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 = { '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' }, 'yard-distances': { view: 'b2000001-0001-4000-8000-000000000018', manage: 'b2000001-0001-4000-8000-000000000019' }, }; /** * Slugs whose changes go through a separate approver. `manage` lets a staff * member propose a change; only `approve` lets someone put it into effect. * Only listed slugs get the permission — the rest are manage-only. */ const RULE_ENGINE_APPROVE_PERMISSION_IDS: Partial> = { rates: 'b2000001-0001-4000-8000-000000000017', }; export type RuleEngineApprovableSlug = 'rates'; export const RULE_ENGINE_PERMISSIONS: FreightPermissionSeed[] = RULE_ENGINE_RESOURCE_SLUGS.flatMap( (slug) => { const resource = slugToResourceKey(slug); const ids = RULE_ENGINE_PERMISSION_IDS[slug]; const approveId = RULE_ENGINE_APPROVE_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}`), ...(approveId ? [perm(approveId, `edr_freight_app:rule_engine:${resource}:approve`, `Approve ${slug} changes`)] : []), ]; }, ); /** * Container-allocation permission for the previously-unguarded * booking allocate-containers endpoint. */ export const GAP_CONTROLLER_PERMISSIONS: FreightPermissionSeed[] = [ perm('c1000001-0001-4000-8000-000000000001', 'edr_freight_app:allocation:manage', 'Allocate containers to vehicles'), ]; /** * Advanced backoffice resources — full CRUD + workflow-action keys. * See docs/rbac/freight-backoffice-permissions.md. Additive only: the existing * bookings/contracts/rule-engine/allocation keys above are unchanged. */ // C. Customers export const CUSTOMER_PERMISSIONS: FreightPermissionSeed[] = [ perm('d1a00001-0001-4000-8000-000000000001', 'edr_freight_app:customers:view', 'View customers'), perm('d1a00001-0001-4000-8000-000000000002', 'edr_freight_app:customers:create', 'Create customer'), perm('d1a00001-0001-4000-8000-000000000003', 'edr_freight_app:customers:update', 'Update customer'), perm('d1a00001-0001-4000-8000-000000000004', 'edr_freight_app:customers:deactivate', 'Deactivate customer'), perm('d1a00001-0001-4000-8000-000000000005', 'edr_freight_app:customers:verify', 'Verify customer (KYC/Fayda)'), perm('d1a00001-0001-4000-8000-000000000006', 'edr_freight_app:customers:reset-password', 'Trigger customer password reset'), ]; // D. Finance — payments + invoices export const FINANCE_PERMISSIONS: FreightPermissionSeed[] = [ perm('d2a00001-0001-4000-8000-000000000001', 'edr_freight_app:payments:view', 'View payments'), perm('d2a00001-0001-4000-8000-000000000002', 'edr_freight_app:payments:verify', 'Verify/settle payment'), perm('d2a00001-0001-4000-8000-000000000003', 'edr_freight_app:payments:refund', 'Refund payment'), perm('d2b00001-0001-4000-8000-000000000001', 'edr_freight_app:invoices:view', 'View invoices'), perm('d2b00001-0001-4000-8000-000000000002', 'edr_freight_app:invoices:create', 'Generate invoice'), perm('d2b00001-0001-4000-8000-000000000003', 'edr_freight_app:invoices:cancel', 'Cancel invoice'), perm('d2b00001-0001-4000-8000-000000000004', 'edr_freight_app:invoices:export', 'Download invoice document'), ]; // E. First / last mile operations export const MILE_PERMISSIONS: FreightPermissionSeed[] = [ perm('d3a00001-0001-4000-8000-000000000001', 'edr_freight_app:first_mile:view', 'View first-mile'), perm('d3a00001-0001-4000-8000-000000000002', 'edr_freight_app:first_mile:accept', 'Accept first-mile request'), perm('d3a00001-0001-4000-8000-000000000003', 'edr_freight_app:first_mile:create', 'Create first-mile'), perm('d3a00001-0001-4000-8000-000000000004', 'edr_freight_app:first_mile:update', 'Update first-mile'), perm('d3a00001-0001-4000-8000-000000000005', 'edr_freight_app:first_mile:delete', 'Delete first-mile'), perm('d3a00001-0001-4000-8000-000000000006', 'edr_freight_app:first_mile:assign_vehicles', 'Assign first-mile vehicles'), perm('d3a00001-0001-4000-8000-000000000007', 'edr_freight_app:first_mile:set_distances', 'Set first-mile distances'), perm('d3a00001-0001-4000-8000-000000000008', 'edr_freight_app:first_mile:generate_invoice', 'Generate first-mile invoice'), perm('d3b00001-0001-4000-8000-000000000001', 'edr_freight_app:last_mile:view', 'View last-mile'), perm('d3b00001-0001-4000-8000-000000000002', 'edr_freight_app:last_mile:accept', 'Accept last-mile request'), perm('d3b00001-0001-4000-8000-000000000003', 'edr_freight_app:last_mile:create', 'Create last-mile'), perm('d3b00001-0001-4000-8000-000000000004', 'edr_freight_app:last_mile:update', 'Update last-mile'), perm('d3b00001-0001-4000-8000-000000000005', 'edr_freight_app:last_mile:delete', 'Delete last-mile'), perm('d3b00001-0001-4000-8000-000000000006', 'edr_freight_app:last_mile:assign_vehicles', 'Assign last-mile vehicles'), perm('d3b00001-0001-4000-8000-000000000007', 'edr_freight_app:last_mile:set_distances', 'Set last-mile distances'), perm('d3b00001-0001-4000-8000-000000000008', 'edr_freight_app:last_mile:generate_invoice', 'Generate last-mile invoice'), ]; // F. Fleet — rail assets (splits the flat fleet:view/manage) export const FLEET_RAIL_PERMISSIONS: FreightPermissionSeed[] = [ perm('e1a00001-0001-4000-8000-000000000001', 'edr_freight_app:locomotives:view', 'View locomotives'), perm('e1a00001-0001-4000-8000-000000000002', 'edr_freight_app:locomotives:create', 'Create locomotive'), perm('e1a00001-0001-4000-8000-000000000003', 'edr_freight_app:locomotives:update', 'Update locomotive'), perm('e1a00001-0001-4000-8000-000000000004', 'edr_freight_app:locomotives:delete', 'Delete locomotive'), perm('e1b00001-0001-4000-8000-000000000001', 'edr_freight_app:wagons:view', 'View wagons'), perm('e1b00001-0001-4000-8000-000000000002', 'edr_freight_app:wagons:create', 'Create wagon'), perm('e1b00001-0001-4000-8000-000000000003', 'edr_freight_app:wagons:update', 'Update wagon'), perm('e1b00001-0001-4000-8000-000000000004', 'edr_freight_app:wagons:delete', 'Delete wagon'), perm('e1b00001-0001-4000-8000-000000000005', 'edr_freight_app:wagons:transfer_request', 'Request wagon transfer'), perm('e1b00001-0001-4000-8000-000000000006', 'edr_freight_app:wagons:transfer_fulfill', 'Fulfil wagon transfer (OCC)'), perm('e1b00001-0001-4000-8000-000000000007', 'edr_freight_app:wagons:transfer_history_all', "View all staff's transfer history"), perm('e1c00001-0001-4000-8000-000000000001', 'edr_freight_app:trains:view', 'View trains'), perm('e1c00001-0001-4000-8000-000000000002', 'edr_freight_app:trains:create', 'Create train'), perm('e1c00001-0001-4000-8000-000000000003', 'edr_freight_app:trains:update', 'Update train'), perm('e1c00001-0001-4000-8000-000000000004', 'edr_freight_app:trains:delete', 'Delete train'), perm('e1c00001-0001-4000-8000-000000000005', 'edr_freight_app:trains:assign_wagons', 'Assign wagons to train'), perm('e1d00001-0001-4000-8000-000000000001', 'edr_freight_app:routes:view', 'View routes'), perm('e1d00001-0001-4000-8000-000000000002', 'edr_freight_app:routes:create', 'Create route'), perm('e1d00001-0001-4000-8000-000000000003', 'edr_freight_app:routes:update', 'Update route'), perm('e1d00001-0001-4000-8000-000000000004', 'edr_freight_app:routes:delete', 'Delete route'), perm('e1e00001-0001-4000-8000-000000000001', 'edr_freight_app:containers:view', 'View containers'), perm('e1e00001-0001-4000-8000-000000000002', 'edr_freight_app:containers:create', 'Create container'), perm('e1e00001-0001-4000-8000-000000000003', 'edr_freight_app:containers:update', 'Update container'), perm('e1e00001-0001-4000-8000-000000000004', 'edr_freight_app:containers:delete', 'Delete container'), perm('e1f00001-0001-4000-8000-000000000001', 'edr_freight_app:cargoes:view', 'View cargoes'), perm('e1f00001-0001-4000-8000-000000000002', 'edr_freight_app:cargoes:create', 'Create cargo'), perm('e1f00001-0001-4000-8000-000000000003', 'edr_freight_app:cargoes:update', 'Update cargo'), perm('e1f00001-0001-4000-8000-000000000004', 'edr_freight_app:cargoes:delete', 'Delete cargo'), ]; // G. Fleet — road & telemetry export const FLEET_ROAD_PERMISSIONS: FreightPermissionSeed[] = [ perm('e2a00001-0001-4000-8000-000000000001', 'edr_freight_app:vehicles:view', 'View vehicles'), perm('e2a00001-0001-4000-8000-000000000002', 'edr_freight_app:vehicles:create', 'Create vehicle'), perm('e2a00001-0001-4000-8000-000000000003', 'edr_freight_app:vehicles:update', 'Update vehicle'), perm('e2a00001-0001-4000-8000-000000000004', 'edr_freight_app:vehicles:delete', 'Delete vehicle'), perm('e2b00001-0001-4000-8000-000000000001', 'edr_freight_app:drivers:view', 'View drivers'), perm('e2b00001-0001-4000-8000-000000000002', 'edr_freight_app:drivers:create', 'Create driver'), perm('e2b00001-0001-4000-8000-000000000003', 'edr_freight_app:drivers:update', 'Update driver'), perm('e2b00001-0001-4000-8000-000000000004', 'edr_freight_app:drivers:delete', 'Delete driver'), perm('e2c00001-0001-4000-8000-000000000001', 'edr_freight_app:tracking:view', 'Track vehicles'), perm('e2c00001-0001-4000-8000-000000000002', 'edr_freight_app:tracking:manage', 'Manage GPS trackers'), perm('e2d00001-0001-4000-8000-000000000001', 'edr_freight_app:fuel:view', 'View fuel purchases'), perm('e2d00001-0001-4000-8000-000000000002', 'edr_freight_app:fuel:create', 'Create fuel purchase'), perm('e2d00001-0001-4000-8000-000000000003', 'edr_freight_app:fuel:update', 'Update fuel purchase'), perm('e2d00001-0001-4000-8000-000000000004', 'edr_freight_app:fuel:delete', 'Delete fuel purchase'), perm('e2d00001-0001-4000-8000-000000000005', 'edr_freight_app:fuel:approve', 'Approve fuel purchase'), perm('e2e00001-0001-4000-8000-000000000001', 'edr_freight_app:maintenance:view', 'View maintenance'), perm('e2e00001-0001-4000-8000-000000000002', 'edr_freight_app:maintenance:create', 'Create maintenance'), perm('e2e00001-0001-4000-8000-000000000003', 'edr_freight_app:maintenance:update', 'Update maintenance'), perm('e2e00001-0001-4000-8000-000000000004', 'edr_freight_app:maintenance:delete', 'Delete maintenance'), perm('e2e00001-0001-4000-8000-000000000005', 'edr_freight_app:maintenance:complete', 'Complete maintenance'), perm('e2f00001-0001-4000-8000-000000000001', 'edr_freight_app:fleet_reports:view', 'View fleet financial reports'), perm('e2f00001-0001-4000-8000-000000000002', 'edr_freight_app:fleet_reports:export', 'Export fleet financial reports'), perm('e2000001-0001-4000-8000-000000000001', 'edr_freight_app:fleet_dashboard:view', 'View fleet dashboard'), ]; // H. Warehouse management export const WAREHOUSE_PERMISSIONS: FreightPermissionSeed[] = [ perm('f1000001-0001-4000-8000-000000000001', 'edr_freight_app:warehouse_dashboard:view', 'View warehouse dashboard'), perm('f1a00001-0001-4000-8000-000000000001', 'edr_freight_app:warehouses:view', 'View warehouses'), perm('f1a00001-0001-4000-8000-000000000002', 'edr_freight_app:warehouses:create', 'Create warehouse'), perm('f1a00001-0001-4000-8000-000000000003', 'edr_freight_app:warehouses:update', 'Update warehouse'), perm('f1a00001-0001-4000-8000-000000000004', 'edr_freight_app:warehouses:delete', 'Delete warehouse'), perm('f1b00001-0001-4000-8000-000000000001', 'edr_freight_app:warehouse_yards:view', 'View warehouse yards'), perm('f1b00001-0001-4000-8000-000000000002', 'edr_freight_app:warehouse_yards:create', 'Create warehouse yard'), perm('f1b00001-0001-4000-8000-000000000003', 'edr_freight_app:warehouse_yards:update', 'Update warehouse yard'), perm('f1b00001-0001-4000-8000-000000000004', 'edr_freight_app:warehouse_yards:delete', 'Delete warehouse yard'), perm('f1c00001-0001-4000-8000-000000000001', 'edr_freight_app:warehouse_zones:view', 'View warehouse zones'), perm('f1c00001-0001-4000-8000-000000000002', 'edr_freight_app:warehouse_zones:create', 'Create warehouse zone'), perm('f1c00001-0001-4000-8000-000000000003', 'edr_freight_app:warehouse_zones:update', 'Update warehouse zone'), perm('f1d00001-0001-4000-8000-000000000001', 'edr_freight_app:warehouse_allocation_rules:view', 'View allocation rules'), perm('f1d00001-0001-4000-8000-000000000002', 'edr_freight_app:warehouse_allocation_rules:create', 'Create allocation rule'), perm('f1d00001-0001-4000-8000-000000000003', 'edr_freight_app:warehouse_allocation_rules:update', 'Update allocation rule'), perm('f1d00001-0001-4000-8000-000000000004', 'edr_freight_app:warehouse_allocation_rules:delete', 'Delete allocation rule'), perm('f1e00001-0001-4000-8000-000000000001', 'edr_freight_app:warehouse_fee_rules:view', 'View fee rules'), perm('f1e00001-0001-4000-8000-000000000002', 'edr_freight_app:warehouse_fee_rules:create', 'Create fee rule'), perm('f1e00001-0001-4000-8000-000000000003', 'edr_freight_app:warehouse_fee_rules:update', 'Update fee rule'), perm('f1e00001-0001-4000-8000-000000000004', 'edr_freight_app:warehouse_fee_rules:delete', 'Delete fee rule'), perm('f1f00001-0001-4000-8000-000000000001', 'edr_freight_app:warehouse_inspection_reports:view', 'View inspection reports'), perm('f1f00001-0001-4000-8000-000000000002', 'edr_freight_app:warehouse_inspection_reports:create', 'Create inspection report'), perm('f1f00001-0001-4000-8000-000000000003', 'edr_freight_app:warehouse_inspection_reports:update', 'Update inspection report'), ]; // I. Port & terminal — inventory movement + interchange + fee invoices export const PORT_TERMINAL_PERMISSIONS: FreightPermissionSeed[] = [ perm('f2a00001-0001-4000-8000-000000000001', 'edr_freight_app:warehouse_inventory:view', 'View terminal inventory'), perm('f2a00001-0001-4000-8000-000000000002', 'edr_freight_app:warehouse_inventory:receive', 'Receive inventory'), perm('f2a00001-0001-4000-8000-000000000003', 'edr_freight_app:warehouse_inventory:move', 'Move/store/reserve inventory'), perm('f2a00001-0001-4000-8000-000000000004', 'edr_freight_app:warehouse_inventory:load', 'Load inventory'), perm('f2a00001-0001-4000-8000-000000000005', 'edr_freight_app:warehouse_inventory:unload', 'Unload inventory'), perm('f2a00001-0001-4000-8000-000000000006', 'edr_freight_app:warehouse_inventory:dispatch', 'Dispatch inventory'), perm('f2a00001-0001-4000-8000-000000000007', 'edr_freight_app:warehouse_inventory:gate_pass', 'Gate-clearance inventory'), perm('f2a00001-0001-4000-8000-000000000008', 'edr_freight_app:warehouse_inventory:release', 'Release inventory'), perm('f2a00001-0001-4000-8000-000000000009', 'edr_freight_app:warehouse_inventory:deliver', 'Deliver inventory'), perm('f2a00001-0001-4000-8000-00000000000a', 'edr_freight_app:warehouse_inventory:inspect', 'Inspect inventory'), perm('f2b00001-0001-4000-8000-000000000001', 'edr_freight_app:interchange_documents:view', 'View interchange documents'), perm('f2b00001-0001-4000-8000-000000000002', 'edr_freight_app:interchange_documents:generate', 'Generate interchange document'), perm('f2b00001-0001-4000-8000-000000000003', 'edr_freight_app:interchange_documents:acknowledge', 'Acknowledge interchange document'), perm('f2b00001-0001-4000-8000-000000000004', 'edr_freight_app:interchange_documents:dispute', 'Dispute interchange document'), perm('f2b00001-0001-4000-8000-000000000005', 'edr_freight_app:interchange_documents:cancel', 'Cancel interchange document'), perm('f2c00001-0001-4000-8000-000000000001', 'edr_freight_app:warehouse_fee_invoices:view', 'View warehouse fee invoices'), perm('f2c00001-0001-4000-8000-000000000002', 'edr_freight_app:warehouse_fee_invoices:generate', 'Generate warehouse fee invoice'), perm('f2c00001-0001-4000-8000-000000000003', 'edr_freight_app:warehouse_fee_invoices:cancel', 'Cancel warehouse fee invoice'), perm('f2c00001-0001-4000-8000-000000000004', 'edr_freight_app:warehouse_fee_invoices:pay', 'Pay warehouse fee invoice'), ]; // E'. Train-scheduling finer actions (augment existing view/manage) export const SCHEDULING_EXTRA_PERMISSIONS: FreightPermissionSeed[] = [ perm('a2a00001-0001-4000-8000-000000000001', 'edr_freight_app:train_scheduling:create', 'Create train schedule'), perm('a2a00001-0001-4000-8000-000000000002', 'edr_freight_app:train_scheduling:update', 'Update train schedule'), perm('a2a00001-0001-4000-8000-000000000003', 'edr_freight_app:train_scheduling:cancel', 'Cancel train schedule'), perm('a2a00001-0001-4000-8000-000000000004', 'edr_freight_app:train_scheduling:reschedule', 'Reschedule train'), perm('a2a00001-0001-4000-8000-000000000005', 'edr_freight_app:train_scheduling:rules_manage', 'Manage global scheduling rules'), ]; // L. Administration & settings (split from the coarse admin umbrella) export const CONFIG_SETTINGS_PERMISSIONS: FreightPermissionSeed[] = [ perm('b3a00001-0001-4000-8000-000000000001', 'edr_freight_app:config:contract_validity:view', 'View contract validity periods'), perm('b3a00001-0001-4000-8000-000000000002', 'edr_freight_app:config:contract_validity:manage', 'Manage contract validity periods'), perm('b4a00001-0001-4000-8000-000000000001', 'edr_freight_app:settings:file_upload:view', 'View file-upload settings'), perm('b4a00001-0001-4000-8000-000000000002', 'edr_freight_app:settings:file_upload:manage', 'Manage file-upload settings'), perm('b4b00001-0001-4000-8000-000000000001', 'edr_freight_app:settings:dropdown:view', 'View dropdown settings'), perm('b4b00001-0001-4000-8000-000000000002', 'edr_freight_app:settings:dropdown:manage', 'Manage dropdown settings'), ]; // M. Staff / IAM admin — NEW keys only. The employee_registration / role_assignment // / hierarchy_* / position_types:view keys are seeded separately in edr-freight.seed.ts. export const STAFF_IAM_PERMISSIONS: FreightPermissionSeed[] = [ perm('c2a00001-0001-4000-8000-000000000001', 'edr_freight_app:staff:roles:view', 'View roles'), perm('c2a00001-0001-4000-8000-000000000002', 'edr_freight_app:staff:roles:create', 'Create role'), perm('c2a00001-0001-4000-8000-000000000003', 'edr_freight_app:staff:roles:update', 'Update role'), perm('c2a00001-0001-4000-8000-000000000004', 'edr_freight_app:staff:roles:delete', 'Delete role'), perm('c2b00001-0001-4000-8000-000000000001', 'edr_freight_app:staff:permissions:view', 'View permission assignments'), perm('c2b00001-0001-4000-8000-000000000002', 'edr_freight_app:staff:permissions:assign', 'Assign permissions'), perm('c2c00001-0001-4000-8000-000000000001', 'edr_freight_app:position_types:create', 'Create position type'), perm('c2c00001-0001-4000-8000-000000000002', 'edr_freight_app:position_types:update', 'Update position type'), perm('c2c00001-0001-4000-8000-000000000003', 'edr_freight_app:position_types:delete', 'Delete position type'), ]; export const ADVANCED_BACKOFFICE_PERMISSIONS: FreightPermissionSeed[] = [ ...CUSTOMER_PERMISSIONS, ...FINANCE_PERMISSIONS, ...MILE_PERMISSIONS, ...FLEET_RAIL_PERMISSIONS, ...FLEET_ROAD_PERMISSIONS, ...WAREHOUSE_PERMISSIONS, ...PORT_TERMINAL_PERMISSIONS, ...SCHEDULING_EXTRA_PERMISSIONS, ...CONFIG_SETTINGS_PERMISSIONS, ...STAFF_IAM_PERMISSIONS, ]; export const BOOKING_RULE_ENGINE_PERMISSIONS = [ ...BOOKING_PERMISSIONS, ...CONTRACT_PERMISSIONS, ...RULE_ENGINE_PERMISSIONS, ...GAP_CONTROLLER_PERMISSIONS, ...ADVANCED_BACKOFFICE_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', create: 'edr_freight_app:bookings:create', 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', create: 'edr_freight_app:train_scheduling:create', update: 'edr_freight_app:train_scheduling:update', cancel: 'edr_freight_app:train_scheduling:cancel', reschedule: 'edr_freight_app:train_scheduling:reschedule', rulesManage: 'edr_freight_app:train_scheduling:rules_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`, approve: (slug: RuleEngineApprovableSlug) => `edr_freight_app:rule_engine:${slugToResourceKey(slug)}:approve`, }, allocation: { manage: 'edr_freight_app:allocation:manage', }, customers: { view: 'edr_freight_app:customers:view', create: 'edr_freight_app:customers:create', update: 'edr_freight_app:customers:update', deactivate: 'edr_freight_app:customers:deactivate', verify: 'edr_freight_app:customers:verify', resetPassword: 'edr_freight_app:customers:reset-password', }, payments: { view: 'edr_freight_app:payments:view', verify: 'edr_freight_app:payments:verify', refund: 'edr_freight_app:payments:refund', }, invoices: { view: 'edr_freight_app:invoices:view', create: 'edr_freight_app:invoices:create', cancel: 'edr_freight_app:invoices:cancel', export: 'edr_freight_app:invoices:export', }, firstMile: { view: 'edr_freight_app:first_mile:view', accept: 'edr_freight_app:first_mile:accept', create: 'edr_freight_app:first_mile:create', update: 'edr_freight_app:first_mile:update', delete: 'edr_freight_app:first_mile:delete', assignVehicles: 'edr_freight_app:first_mile:assign_vehicles', setDistances: 'edr_freight_app:first_mile:set_distances', generateInvoice: 'edr_freight_app:first_mile:generate_invoice', }, lastMile: { view: 'edr_freight_app:last_mile:view', accept: 'edr_freight_app:last_mile:accept', create: 'edr_freight_app:last_mile:create', update: 'edr_freight_app:last_mile:update', delete: 'edr_freight_app:last_mile:delete', assignVehicles: 'edr_freight_app:last_mile:assign_vehicles', setDistances: 'edr_freight_app:last_mile:set_distances', generateInvoice: 'edr_freight_app:last_mile:generate_invoice', }, locomotives: { view: 'edr_freight_app:locomotives:view', create: 'edr_freight_app:locomotives:create', update: 'edr_freight_app:locomotives:update', delete: 'edr_freight_app:locomotives:delete', }, wagons: { view: 'edr_freight_app:wagons:view', create: 'edr_freight_app:wagons:create', update: 'edr_freight_app:wagons:update', delete: 'edr_freight_app:wagons:delete', // Requester creates a transfer request; OCC fulfils it (picks the wagons and // executes the move). Distinct keys so OCC can hold fulfil without request. transferRequest: 'edr_freight_app:wagons:transfer_request', transferFulfill: 'edr_freight_app:wagons:transfer_fulfill', // Admin: read every staffer's transfer history. Without it, a user only sees // their own (the /history endpoint uses the caller id, backend-enforced). transferHistoryAll: 'edr_freight_app:wagons:transfer_history_all', }, trains: { view: 'edr_freight_app:trains:view', create: 'edr_freight_app:trains:create', update: 'edr_freight_app:trains:update', delete: 'edr_freight_app:trains:delete', assignWagons: 'edr_freight_app:trains:assign_wagons', }, routes: { view: 'edr_freight_app:routes:view', create: 'edr_freight_app:routes:create', update: 'edr_freight_app:routes:update', delete: 'edr_freight_app:routes:delete', }, containers: { view: 'edr_freight_app:containers:view', create: 'edr_freight_app:containers:create', update: 'edr_freight_app:containers:update', delete: 'edr_freight_app:containers:delete', }, cargoes: { view: 'edr_freight_app:cargoes:view', create: 'edr_freight_app:cargoes:create', update: 'edr_freight_app:cargoes:update', delete: 'edr_freight_app:cargoes:delete', }, vehicles: { view: 'edr_freight_app:vehicles:view', create: 'edr_freight_app:vehicles:create', update: 'edr_freight_app:vehicles:update', delete: 'edr_freight_app:vehicles:delete', }, drivers: { view: 'edr_freight_app:drivers:view', create: 'edr_freight_app:drivers:create', update: 'edr_freight_app:drivers:update', delete: 'edr_freight_app:drivers:delete', }, tracking: { view: 'edr_freight_app:tracking:view', manage: 'edr_freight_app:tracking:manage', }, fuel: { view: 'edr_freight_app:fuel:view', create: 'edr_freight_app:fuel:create', update: 'edr_freight_app:fuel:update', delete: 'edr_freight_app:fuel:delete', approve: 'edr_freight_app:fuel:approve', }, maintenance: { view: 'edr_freight_app:maintenance:view', create: 'edr_freight_app:maintenance:create', update: 'edr_freight_app:maintenance:update', delete: 'edr_freight_app:maintenance:delete', complete: 'edr_freight_app:maintenance:complete', }, fleetReports: { view: 'edr_freight_app:fleet_reports:view', export: 'edr_freight_app:fleet_reports:export', }, fleetDashboard: { view: 'edr_freight_app:fleet_dashboard:view', }, warehouseDashboard: { view: 'edr_freight_app:warehouse_dashboard:view', }, warehouses: { view: 'edr_freight_app:warehouses:view', create: 'edr_freight_app:warehouses:create', update: 'edr_freight_app:warehouses:update', delete: 'edr_freight_app:warehouses:delete', }, warehouseYards: { view: 'edr_freight_app:warehouse_yards:view', create: 'edr_freight_app:warehouse_yards:create', update: 'edr_freight_app:warehouse_yards:update', delete: 'edr_freight_app:warehouse_yards:delete', }, warehouseZones: { view: 'edr_freight_app:warehouse_zones:view', create: 'edr_freight_app:warehouse_zones:create', update: 'edr_freight_app:warehouse_zones:update', }, warehouseAllocationRules: { view: 'edr_freight_app:warehouse_allocation_rules:view', create: 'edr_freight_app:warehouse_allocation_rules:create', update: 'edr_freight_app:warehouse_allocation_rules:update', delete: 'edr_freight_app:warehouse_allocation_rules:delete', }, warehouseFeeRules: { view: 'edr_freight_app:warehouse_fee_rules:view', create: 'edr_freight_app:warehouse_fee_rules:create', update: 'edr_freight_app:warehouse_fee_rules:update', delete: 'edr_freight_app:warehouse_fee_rules:delete', }, warehouseInspectionReports: { view: 'edr_freight_app:warehouse_inspection_reports:view', create: 'edr_freight_app:warehouse_inspection_reports:create', update: 'edr_freight_app:warehouse_inspection_reports:update', }, warehouseInventory: { view: 'edr_freight_app:warehouse_inventory:view', receive: 'edr_freight_app:warehouse_inventory:receive', move: 'edr_freight_app:warehouse_inventory:move', load: 'edr_freight_app:warehouse_inventory:load', unload: 'edr_freight_app:warehouse_inventory:unload', dispatch: 'edr_freight_app:warehouse_inventory:dispatch', gatePass: 'edr_freight_app:warehouse_inventory:gate_pass', release: 'edr_freight_app:warehouse_inventory:release', deliver: 'edr_freight_app:warehouse_inventory:deliver', inspect: 'edr_freight_app:warehouse_inventory:inspect', }, interchangeDocuments: { view: 'edr_freight_app:interchange_documents:view', generate: 'edr_freight_app:interchange_documents:generate', acknowledge: 'edr_freight_app:interchange_documents:acknowledge', dispute: 'edr_freight_app:interchange_documents:dispute', cancel: 'edr_freight_app:interchange_documents:cancel', }, warehouseFeeInvoices: { view: 'edr_freight_app:warehouse_fee_invoices:view', generate: 'edr_freight_app:warehouse_fee_invoices:generate', cancel: 'edr_freight_app:warehouse_fee_invoices:cancel', pay: 'edr_freight_app:warehouse_fee_invoices:pay', }, config: { contractValidity: { view: 'edr_freight_app:config:contract_validity:view', manage: 'edr_freight_app:config:contract_validity:manage', }, }, settings: { fileUpload: { view: 'edr_freight_app:settings:file_upload:view', manage: 'edr_freight_app:settings:file_upload:manage', }, dropdown: { view: 'edr_freight_app:settings:dropdown:view', manage: 'edr_freight_app:settings:dropdown:manage', }, }, staff: { roles: { view: 'edr_freight_app:staff:roles:view', create: 'edr_freight_app:staff:roles:create', update: 'edr_freight_app:staff:roles:update', delete: 'edr_freight_app:staff:roles:delete', }, permissions: { view: 'edr_freight_app:staff:permissions:view', assign: 'edr_freight_app:staff:permissions:assign', }, // Seeded in edr-freight.seed.ts (EDR_FREIGHT_PERMISSIONS) — surfaced here for gating. employeeRegistration: { view: 'edr_freight_app:employee_registration:view', create: 'edr_freight_app:employee_registration:create', update: 'edr_freight_app:employee_registration:update', activate: 'edr_freight_app:employee_registration:activate', deactivate: 'edr_freight_app:employee_registration:deactivate', }, roleAssignment: { view: 'edr_freight_app:role_assignment:view', assign: 'edr_freight_app:role_assignment:assign', replace: 'edr_freight_app:role_assignment:replace', }, hierarchyUnits: { view: 'edr_freight_app:hierarchy_units:view', create: 'edr_freight_app:hierarchy_units:create', update: 'edr_freight_app:hierarchy_units:update', delete: 'edr_freight_app:hierarchy_units:delete', }, hierarchyPositions: { view: 'edr_freight_app:hierarchy_positions:view', create: 'edr_freight_app:hierarchy_positions:create', update: 'edr_freight_app:hierarchy_positions:update', delete: 'edr_freight_app:hierarchy_positions:delete', changeParent: 'edr_freight_app:hierarchy_positions:change_parent', }, hierarchyEmployeeAssignment: { view: 'edr_freight_app:hierarchy_employee_assignment:view', invite: 'edr_freight_app:hierarchy_employee_assignment:invite', assign: 'edr_freight_app:hierarchy_employee_assignment:assign', }, positionTypes: { view: 'edr_freight_app:position_types:view', create: 'edr_freight_app:position_types:create', update: 'edr_freight_app:position_types:update', delete: 'edr_freight_app:position_types:delete', }, }, } 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 for ONE_TIME contracts, and PER BOOKING for GENERAL // contracts (booking-level document review → finalize → CLEARANCE_READY). FREIGHT_PERMS.contracts.view, FREIGHT_PERMS.contracts.opsClearanceReview, FREIGHT_PERMS.bookings.clearanceView, FREIGHT_PERMS.bookings.reviewDocuments, FREIGHT_PERMS.bookings.finalizeClearance, ...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; /** * Position permission presets (positions-as-roles). Grants flow to users via * Position → PositionPermission (NOT Role/RolePermission). Each reuses the * matching ROLE_PERMISSION_PRESETS key-array as a building block and adds the * gap-controller keys the position needs. Deduped via Set. */ const dedupe = (keys: string[]): string[] => [...new Set(keys)]; export const POSITION_PERMISSION_PRESETS = { // Chief: senior operational role — intake/line-staff approval + director // approval + scheduling/ops, plus container allocation. chief: dedupe([ ...ROLE_PERMISSION_PRESETS.lineStaff, ...ROLE_PERMISSION_PRESETS.director, ...ROLE_PERMISSION_PRESETS.operationsOfficer, FREIGHT_PERMS.allocation.manage, ]), director: dedupe([...ROLE_PERMISSION_PRESETS.director]), ceo: dedupe([...ROLE_PERMISSION_PRESETS.ceo]), ethiopianGl: dedupe([...ROLE_PERMISSION_PRESETS.glEthiopia]), djiboutiGl: dedupe([...ROLE_PERMISSION_PRESETS.glDjibouti]), marketer: dedupe([...ROLE_PERMISSION_PRESETS.marketing]), operation: dedupe([ ...ROLE_PERMISSION_PRESETS.operationsOfficer, FREIGHT_PERMS.allocation.manage, ]), } as const; /** Derive the module bucket from the resource segment of a permission key. */ const moduleOf = (key: string): string => key.split(':')[1] ?? 'other'; export const PERMISSIONS_CATALOG = BOOKING_RULE_ENGINE_PERMISSIONS.map((p) => ({ key: p.key, label: p.name.en, module: moduleOf(p.key), }));