import { Role } from './roles'; /** * All modules available in the permission matrix. * Each module can have: view, create, update, archive, approve, export actions. */ export const MODULES = [ 'dashboard', 'clients', 'subscriptions', 'invoices', 'payments', 'tickets', 'employees', 'payroll', 'expenses', 'assets', 'accounts', 'fund_transfers', 'accounting', 'reports', 'areas', 'plans', 'settings', 'users', ] as const; export type Module = (typeof MODULES)[number]; export const MODULE_LABELS: Record = { dashboard: 'Dashboard', clients: 'Clients', subscriptions: 'Subscriptions', invoices: 'Invoices', payments: 'Payments', tickets: 'Tickets', employees: 'Employees', payroll: 'Payroll', expenses: 'Expenses', assets: 'Assets', accounts: 'Company Accounts', fund_transfers: 'Fund Transfers', accounting: 'Accounting', reports: 'Reports', areas: 'Areas', plans: 'Plans', settings: 'Settings', users: 'Users', }; export const ACTIONS = ['canView', 'canCreate', 'canUpdate', 'canArchive', 'canApprove', 'canExport'] as const; export type Action = (typeof ACTIONS)[number]; export const ACTION_LABELS: Record = { canView: 'View', canCreate: 'Create', canUpdate: 'Update', canArchive: 'Archive', canApprove: 'Approve', canExport: 'Export', }; /** * Defines which actions are applicable per module. * Only these checkboxes should be shown/enforced in the matrix. */ export const MODULE_ACTIONS: Record = { dashboard: ['canView'], clients: ['canView', 'canCreate', 'canUpdate', 'canArchive', 'canExport'], subscriptions: ['canView', 'canCreate', 'canUpdate', 'canArchive', 'canExport'], invoices: ['canView', 'canCreate', 'canUpdate', 'canArchive', 'canApprove', 'canExport'], payments: ['canView', 'canCreate', 'canUpdate', 'canApprove', 'canExport'], tickets: ['canView', 'canCreate', 'canUpdate', 'canArchive', 'canExport'], employees: ['canView', 'canCreate', 'canUpdate', 'canArchive', 'canExport'], payroll: ['canView', 'canCreate', 'canUpdate', 'canApprove', 'canExport'], expenses: ['canView', 'canCreate', 'canUpdate', 'canArchive', 'canApprove', 'canExport'], assets: ['canView', 'canCreate', 'canUpdate', 'canArchive', 'canExport'], accounts: ['canView', 'canCreate', 'canUpdate', 'canApprove', 'canExport'], fund_transfers: ['canView', 'canCreate', 'canApprove', 'canExport'], accounting: ['canView', 'canExport'], reports: ['canView', 'canExport'], areas: ['canView', 'canCreate', 'canUpdate', 'canArchive'], plans: ['canView', 'canCreate', 'canUpdate', 'canArchive'], settings: ['canView', 'canUpdate'], users: ['canView', 'canCreate', 'canUpdate', 'canArchive'], }; /** * Permission matrix type — one row per module with boolean actions. */ export interface PermissionRow { module: Module; canView: boolean; canCreate: boolean; canUpdate: boolean; canArchive: boolean; canApprove: boolean; canExport: boolean; } /** * Default permission matrices for system roles seeded per tenant. */ function allTrue(modules: readonly Module[], actions: readonly Action[]): PermissionRow[] { return MODULES.map((mod) => ({ module: mod, canView: actions.includes('canView') && modules.includes(mod), canCreate: actions.includes('canCreate') && modules.includes(mod), canUpdate: actions.includes('canUpdate') && modules.includes(mod), canArchive: actions.includes('canArchive') && modules.includes(mod), canApprove: actions.includes('canApprove') && modules.includes(mod), canExport: actions.includes('canExport') && modules.includes(mod), })); } const ALL_MODULES = [...MODULES] as Module[]; export const DEFAULT_ROLE_PERMISSIONS: Record = { tenant_admin: MODULES.map((mod) => ({ module: mod, canView: true, canCreate: true, canUpdate: true, canArchive: true, canApprove: true, canExport: true, })), manager: MODULES.map((mod) => { const noAccess: Module[] = ['users']; const viewOnly: Module[] = ['dashboard', 'accounting', 'settings']; if (noAccess.includes(mod)) return { module: mod, canView: false, canCreate: false, canUpdate: false, canArchive: false, canApprove: false, canExport: false }; if (viewOnly.includes(mod)) return { module: mod, canView: true, canCreate: false, canUpdate: false, canArchive: false, canApprove: false, canExport: mod === 'accounting' }; return { module: mod, canView: true, canCreate: true, canUpdate: true, canArchive: true, canApprove: ['invoices', 'payments', 'expenses', 'payroll', 'fund_transfers'].includes(mod), canExport: true, }; }), technician: MODULES.map((mod) => { // Technicians can create/update tickets and record payments in the field. // They can VIEW clients, subscriptions, invoices for context but creating/updating // those requires manager-level API access (@Roles('manager') on POST/PATCH). // Assets require manager via class-level @Roles('manager'). // Subscriptions require manager for all endpoints. const viewOnly: Module[] = ['clients', 'subscriptions', 'invoices', 'dashboard']; const fullAccess: Module[] = ['tickets', 'payments']; if (viewOnly.includes(mod)) return { module: mod, canView: true, canCreate: false, canUpdate: false, canArchive: false, canApprove: false, canExport: false }; if (fullAccess.includes(mod)) return { module: mod, canView: true, canCreate: true, canUpdate: true, canArchive: false, canApprove: false, canExport: false }; return { module: mod, canView: false, canCreate: false, canUpdate: false, canArchive: false, canApprove: false, canExport: false }; }), collector: MODULES.map((mod) => { const canWrite: Module[] = ['payments']; const canView: Module[] = ['dashboard', 'clients', 'invoices', 'payments']; if (!canView.includes(mod)) return { module: mod, canView: false, canCreate: false, canUpdate: false, canArchive: false, canApprove: false, canExport: false }; return { module: mod, canView: true, canCreate: canWrite.includes(mod), canUpdate: false, canArchive: false, canApprove: false, canExport: false, }; }), }; // ─── Legacy support (for super_admin which uses old UserRole system) ─── export const Permission = { READ_DASHBOARD: 'read:dashboard', READ_REPORTS: 'read:reports', READ_CLIENTS: 'read:clients', WRITE_CLIENTS: 'write:clients', DELETE_CLIENTS: 'delete:clients', READ_SUBSCRIPTIONS: 'read:subscriptions', WRITE_SUBSCRIPTIONS: 'write:subscriptions', READ_INVOICES: 'read:invoices', WRITE_INVOICES: 'write:invoices', VOID_INVOICES: 'void:invoices', READ_PAYMENTS: 'read:payments', WRITE_PAYMENTS: 'write:payments', APPROVE_REMITTANCES: 'approve:remittances', READ_TICKETS: 'read:tickets', WRITE_TICKETS: 'write:tickets', ASSIGN_TICKETS: 'assign:tickets', READ_EMPLOYEES: 'read:employees', WRITE_EMPLOYEES: 'write:employees', READ_PAYROLL: 'read:payroll', WRITE_PAYROLL: 'write:payroll', READ_EXPENSES: 'read:expenses', WRITE_EXPENSES: 'write:expenses', APPROVE_EXPENSES: 'approve:expenses', READ_ASSETS: 'read:assets', WRITE_ASSETS: 'write:assets', READ_ACCOUNTS: 'read:accounts', WRITE_ACCOUNTS: 'write:accounts', TRANSFER_ACCOUNTS: 'transfer:accounts', READ_ACCOUNTING: 'read:accounting', WRITE_ACCOUNTING: 'write:accounting', READ_SETTINGS: 'read:settings', WRITE_SETTINGS: 'write:settings', MANAGE_USERS: 'manage:users', MANAGE_TENANTS: 'manage:tenants', } as const; export type PermissionString = (typeof Permission)[keyof typeof Permission]; /** * Get permissions for super_admin (all permissions). */ export function getPermissionsForRoles(roles: string[]): string[] { if (roles.includes(Role.SUPER_ADMIN)) { return Object.values(Permission); } // For tenant users, permissions come from TenantRole → RolePermission in DB return []; }