mirror of
https://github.com/Tria-plc/edr-platform.git
synced 2026-08-27 22:30:55 +00:00
117 lines
3.8 KiB
TypeScript
117 lines
3.8 KiB
TypeScript
import type { AuthPermission, AuthUser } from "./types";
|
|
|
|
/**
|
|
* Mirror of the API's FINANCE_PERMS registry
|
|
* (apps/finance-api/src/seed/finance-permissions.registry.ts).
|
|
*
|
|
* Kept as a hand-written mirror rather than an import: the API is a separate
|
|
* deployable and this app must not take a build dependency on it. The keys are a
|
|
* wire contract, so drift here surfaces as a UI control that is visible but
|
|
* 403s — which is why the server check is the real gate and this is only used to
|
|
* hide or disable controls.
|
|
*/
|
|
export const FINANCE_PERMS = {
|
|
account: {
|
|
view: "can:view:gl_account",
|
|
manage: "can:manage:gl_account",
|
|
},
|
|
journal: {
|
|
create: "can:create:journal_entry",
|
|
view: "can:view:journal_entry",
|
|
post: "can:post:journal_entry",
|
|
reverse: "can:reverse:journal_entry",
|
|
},
|
|
period: {
|
|
view: "can:view:fiscal_period",
|
|
manage: "can:manage:fiscal_period",
|
|
close: "can:close:fiscal_period",
|
|
},
|
|
receivable: {
|
|
view: "can:view:receivable",
|
|
manageCustomer: "can:manage:finance_customer",
|
|
recordReceipt: "can:record:receipt",
|
|
manageRevenueMapping: "can:manage:revenue_mapping",
|
|
},
|
|
payable: {
|
|
view: "can:view:payable",
|
|
manageSupplier: "can:manage:supplier",
|
|
manageBill: "can:manage:supplier_bill",
|
|
approveBill: "can:approve:supplier_bill",
|
|
recordPayment: "can:record:supplier_payment",
|
|
postPayroll: "can:post:payroll_to_gl",
|
|
manageStatutory: "can:manage:statutory_payable",
|
|
},
|
|
budget: {
|
|
view: "can:view:budget",
|
|
manage: "can:manage:budget",
|
|
approve: "can:approve:budget",
|
|
manageCostCenter: "can:manage:cost_center",
|
|
},
|
|
asset: {
|
|
view: "can:view:fixed_asset",
|
|
manage: "can:manage:fixed_asset",
|
|
depreciate: "can:run:depreciation",
|
|
dispose: "can:dispose:fixed_asset",
|
|
},
|
|
report: {
|
|
view: "can:view:finance_report",
|
|
export: "can:export:finance_report",
|
|
},
|
|
} as const;
|
|
|
|
const SUPER_ADMIN_ROLE = "super_admin";
|
|
|
|
const positionsOf = (user: AuthUser | null | undefined) => {
|
|
const employee = user?.employee;
|
|
if (!employee) return [];
|
|
if (Array.isArray(employee)) return employee.flatMap((e) => e.positions ?? []);
|
|
return [
|
|
...(employee.position ? [employee.position] : []),
|
|
...(employee.positions ?? []),
|
|
...(employee.delegatedPositions ?? []),
|
|
];
|
|
};
|
|
|
|
const keysOf = (permissions: AuthPermission[] | undefined) =>
|
|
(permissions ?? []).map((p) => p.key).filter((k): k is string => Boolean(k));
|
|
|
|
/** Every permission key the token carries — roles, positions, position types. */
|
|
export const collectPermissionKeys = (
|
|
user: AuthUser | null | undefined,
|
|
): string[] => {
|
|
if (!user) return [];
|
|
const keys = new Set<string>(keysOf(user.permissions));
|
|
for (const position of positionsOf(user)) {
|
|
keysOf(position.permissions).forEach((key) => keys.add(key));
|
|
keysOf(position.positionType?.permissions).forEach((key) => keys.add(key));
|
|
}
|
|
return [...keys];
|
|
};
|
|
|
|
export const isSuperAdmin = (user: AuthUser | null | undefined): boolean =>
|
|
Boolean(user?.roles?.some((role) => role.key === SUPER_ADMIN_ROLE));
|
|
|
|
/** An array is "any of" — kept in step with edr-hr-web, where a screen
|
|
* reachable by either of two permissions made the widening necessary. */
|
|
export const hasPermission = (
|
|
user: AuthUser | null | undefined,
|
|
permission: string | string[],
|
|
): boolean => {
|
|
if (!user) return false;
|
|
if (isSuperAdmin(user)) return true;
|
|
const keys = collectPermissionKeys(user);
|
|
return Array.isArray(permission)
|
|
? permission.some((p) => keys.includes(p))
|
|
: keys.includes(permission);
|
|
};
|
|
|
|
/** The caller's `iam.employees.id` — who a posting is recorded against. */
|
|
export const currentEmployeeId = (
|
|
user: AuthUser | null | undefined,
|
|
): string | null => {
|
|
const employee = user?.employee;
|
|
if (!employee) return null;
|
|
if (Array.isArray(employee)) return employee[0]?.id ?? null;
|
|
return employee.id ?? null;
|
|
};
|