mirror of
https://github.com/Tria-plc/edr-platform.git
synced 2026-08-28 13:40:57 +00:00
Muluhabt ERP modules
This commit is contained in:
116
apps/finance-web/src/auth/permissions.ts
Normal file
116
apps/finance-web/src/auth/permissions.ts
Normal file
@@ -0,0 +1,116 @@
|
||||
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;
|
||||
};
|
||||
Reference in New Issue
Block a user