mirror of
https://github.com/Tria-plc/edr-platform.git
synced 2026-08-27 12:00:59 +00:00
- Added ContainerValidationService to handle 20ft weight-pairing logic. - Introduced validate20ftWeightPairing utility function to check weight differences. - Updated BookingPricingService to include overweight line details and pairing errors in price response. - Enhanced BookingTransitionService to reject submissions with unpairable 20ft containers. - Created ShipmentValidation interface for pre-submit validation of container contracts. - Integrated shipment validation into the contract booking process, providing warnings for overweight containers and hard blocks for pairing errors. - Updated front-end components to display validation results and prevent submission when errors are present.
208 lines
7.2 KiB
TypeScript
208 lines
7.2 KiB
TypeScript
import type { AuthUser } from "@/auth/types";
|
|
import type { RuleEngineResourceSlug } from "@/types/rule-engine";
|
|
|
|
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",
|
|
clearanceDutyAdvise: "edr_freight_app:contracts:clearance_duty_advise",
|
|
clearanceEtActions: "edr_freight_app:contracts:clearance_et_actions",
|
|
clearanceDjActions: "edr_freight_app:contracts:clearance_dj_actions",
|
|
},
|
|
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",
|
|
allocation: {
|
|
manage: "edr_freight_app:allocation:manage",
|
|
},
|
|
} as const;
|
|
|
|
const slugToResourceKey = (slug: RuleEngineResourceSlug): string =>
|
|
slug.replace(/-/g, "_");
|
|
|
|
export function getPermissionKeys(user: AuthUser | null | undefined): string[] {
|
|
if (!user) return [];
|
|
if (user.permissionKeys?.length) return user.permissionKeys;
|
|
|
|
const keys = new Set<string>();
|
|
for (const p of user.permissions ?? []) {
|
|
if (p.key) keys.add(p.key);
|
|
}
|
|
for (const emp of user.employee ?? []) {
|
|
for (const pos of emp.positions ?? []) {
|
|
for (const p of pos.permissions ?? []) {
|
|
if (p.key) keys.add(p.key);
|
|
}
|
|
}
|
|
}
|
|
return [...keys];
|
|
}
|
|
|
|
/** Position keys held by the user (e.g. "ethiopian_gl", "djibouti_gl"). */
|
|
export function getPositionKeys(user: AuthUser | null | undefined): string[] {
|
|
if (!user) return [];
|
|
const keys = new Set<string>();
|
|
for (const emp of user.employee ?? []) {
|
|
for (const pos of emp.positions ?? []) {
|
|
if (pos.key) keys.add(pos.key);
|
|
}
|
|
}
|
|
return [...keys];
|
|
}
|
|
|
|
export function hasPosition(
|
|
user: AuthUser | null | undefined,
|
|
positionKey: string,
|
|
): boolean {
|
|
return getPositionKeys(user).includes(positionKey);
|
|
}
|
|
|
|
export const POSITION_KEYS = {
|
|
ethiopianGl: "ethiopian_gl",
|
|
djiboutiGl: "djibouti_gl",
|
|
} as const;
|
|
|
|
export function isEthiopianGl(user: AuthUser | null | undefined): boolean {
|
|
return hasPosition(user, POSITION_KEYS.ethiopianGl);
|
|
}
|
|
|
|
export function isDjiboutiGl(user: AuthUser | null | undefined): boolean {
|
|
return hasPosition(user, POSITION_KEYS.djiboutiGl);
|
|
}
|
|
|
|
export function isSuperAdmin(user: AuthUser | null | undefined): boolean {
|
|
if (user?.isSuperAdmin) return true;
|
|
return Boolean(user?.roles?.some((r) => r.key === "super_admin"));
|
|
}
|
|
|
|
/** Org-level admins may act on any approval step in the chain. */
|
|
export function isOrganizationAdmin(
|
|
user: AuthUser | null | undefined,
|
|
): boolean {
|
|
return Boolean(user?.roles?.some((r) => r.key === "organization_admin"));
|
|
}
|
|
|
|
export function isFreightApprovalAdmin(
|
|
user: AuthUser | null | undefined,
|
|
): boolean {
|
|
return isSuperAdmin(user) || isOrganizationAdmin(user);
|
|
}
|
|
|
|
export function hasPermission(
|
|
user: AuthUser | null | undefined,
|
|
key: string,
|
|
): boolean {
|
|
if (!user) return false;
|
|
if (isSuperAdmin(user)) return true;
|
|
return getPermissionKeys(user).includes(key);
|
|
}
|
|
|
|
export function canAccessBookings(user: AuthUser | null | undefined): boolean {
|
|
return hasPermission(user, FREIGHT_PERMS.bookings.view);
|
|
}
|
|
|
|
export function canAccessContracts(user: AuthUser | null | undefined): boolean {
|
|
return hasPermission(user, FREIGHT_PERMS.contracts.view);
|
|
}
|
|
|
|
/** Can review the GL Ethiopia pre-booking contract clearance queue (Path B). */
|
|
export function canReviewContractClearance(
|
|
user: AuthUser | null | undefined,
|
|
): boolean {
|
|
return hasPermission(user, FREIGHT_PERMS.contracts.clearanceReview);
|
|
}
|
|
|
|
/** GL Ethiopia: can create a booking under a cleared contract (Path B). */
|
|
export function canCreateContractBooking(
|
|
user: AuthUser | null | undefined,
|
|
): boolean {
|
|
return hasPermission(user, FREIGHT_PERMS.contracts.createBooking);
|
|
}
|
|
|
|
/** Operations: can review the Path A self-clearance queue (non-customs). */
|
|
export function canReviewSelfClearance(
|
|
user: AuthUser | null | undefined,
|
|
): boolean {
|
|
return hasPermission(user, FREIGHT_PERMS.contracts.opsClearanceReview);
|
|
}
|
|
|
|
/** Can see/manage the customs document-clearance queue (Global Logistics). */
|
|
export function canViewClearance(user: AuthUser | null | undefined): boolean {
|
|
return hasPermission(user, FREIGHT_PERMS.bookings.reviewDocuments);
|
|
}
|
|
|
|
export function canViewScheduling(user: AuthUser | null | undefined): boolean {
|
|
return hasPermission(user, FREIGHT_PERMS.trainScheduling.view);
|
|
}
|
|
|
|
export function canManageScheduling(user: AuthUser | null | undefined): boolean {
|
|
return hasPermission(user, FREIGHT_PERMS.trainScheduling.manage);
|
|
}
|
|
|
|
export function canViewFleet(user: AuthUser | null | undefined): boolean {
|
|
return hasPermission(user, FREIGHT_PERMS.fleet.view);
|
|
}
|
|
|
|
export function isFreightAdmin(user: AuthUser | null | undefined): boolean {
|
|
return hasPermission(user, FREIGHT_PERMS.admin);
|
|
}
|
|
|
|
export function ruleEngineViewKey(slug: RuleEngineResourceSlug): string {
|
|
return `edr_freight_app:rule_engine:${slugToResourceKey(slug)}:view`;
|
|
}
|
|
|
|
export function ruleEngineManageKey(slug: RuleEngineResourceSlug): string {
|
|
return `edr_freight_app:rule_engine:${slugToResourceKey(slug)}:manage`;
|
|
}
|
|
|
|
export function canAccessRuleEngineResource(
|
|
user: AuthUser | null | undefined,
|
|
slug: RuleEngineResourceSlug,
|
|
mode: "view" | "manage",
|
|
): boolean {
|
|
const key = mode === "manage" ? ruleEngineManageKey(slug) : ruleEngineViewKey(slug);
|
|
return hasPermission(user, key);
|
|
}
|
|
|
|
export function canAccessAnyRuleEngineView(
|
|
user: AuthUser | null | undefined,
|
|
slugs: RuleEngineResourceSlug[],
|
|
): boolean {
|
|
return slugs.some((slug) => canAccessRuleEngineResource(user, slug, "view"));
|
|
}
|