fix(treasury): scope finance access and hide it when the module is off
Accounts can be edited, including the opening balance, and finance sections and accounts can be granted on a role or on one user. The finance menu stays hidden when the organization module is disabled. Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
@@ -1,7 +1,16 @@
|
||||
import { CanActivate, ExecutionContext, ForbiddenException, Injectable } from '@nestjs/common';
|
||||
import { PrismaService } from '../prisma/prisma.service';
|
||||
import {
|
||||
parseUserTreasuryAccess,
|
||||
treasurySectionAllowed,
|
||||
type TreasurySectionKey,
|
||||
} from '@mento/shared';
|
||||
import type { Actor } from '../common/record-scope';
|
||||
import { PrismaService } from '../prisma/prisma.service';
|
||||
|
||||
/**
|
||||
* حقوق فقط با treasury_payroll.
|
||||
* بقیه مسیرها با treasury باز میشوند؛ اگر بخش ریز انتخاب شده باشد فقط همانها.
|
||||
*/
|
||||
@Injectable()
|
||||
export class TreasuryAccessGuard implements CanActivate {
|
||||
constructor(private readonly prisma: PrismaService) {}
|
||||
@@ -10,20 +19,56 @@ export class TreasuryAccessGuard implements CanActivate {
|
||||
const req = context.switchToHttp().getRequest<{ user?: Actor; originalUrl?: string; url?: string }>();
|
||||
const actor = req.user;
|
||||
if (!actor) throw new ForbiddenException('دسترسی مالی مجاز نیست.');
|
||||
const url = req.originalUrl || req.url || '';
|
||||
const payroll = url.includes('/treasury/payroll');
|
||||
if (actor.role === 'owner' || actor.role === 'admin') return true;
|
||||
const membership = await this.prisma.membership.findFirst({
|
||||
where: { userId: actor.userId, organizationId: actor.organizationId, isActive: true },
|
||||
select: { orgRole: { select: { permissions: true } } },
|
||||
select: {
|
||||
orgRole: { select: { permissions: true } },
|
||||
treasuryAccessOverride: true,
|
||||
},
|
||||
});
|
||||
const permissions = Array.isArray(membership?.orgRole?.permissions)
|
||||
const rolePermissions = Array.isArray(membership?.orgRole?.permissions)
|
||||
? membership.orgRole.permissions.map(String)
|
||||
: [];
|
||||
const allowed = payroll
|
||||
? permissions.includes('treasury_payroll')
|
||||
: permissions.includes('treasury') || permissions.includes('treasury_payroll');
|
||||
if (!allowed) throw new ForbiddenException('دسترسی مالی برای نقش شما مجاز نیست.');
|
||||
const permissions = permissionsForTreasury(
|
||||
rolePermissions,
|
||||
parseUserTreasuryAccess(membership?.treasuryAccessOverride),
|
||||
);
|
||||
const url = req.originalUrl || req.url || '';
|
||||
if (!treasurySectionAllowed(actor.role, permissions, sectionForUrl(url))) {
|
||||
throw new ForbiddenException('به این بخش مدیریت مالی دسترسی ندارید.');
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
function permissionsForTreasury(
|
||||
rolePermissions: string[],
|
||||
override: ReturnType<typeof parseUserTreasuryAccess>,
|
||||
): string[] {
|
||||
if (!override) return rolePermissions;
|
||||
const rest = rolePermissions.filter((key) => key !== 'treasury' && !key.startsWith('treasury_'));
|
||||
if (!override.enabled) return rest;
|
||||
return ['treasury', ...override.sections, ...rest];
|
||||
}
|
||||
|
||||
function sectionForUrl(url: string): TreasurySectionKey | 'treasury_documents' {
|
||||
if (url.includes('/treasury/payroll')) return 'treasury_payroll';
|
||||
if (url.includes('/treasury/receivables') || url.includes('/treasury/installments')) {
|
||||
return 'treasury_receivables';
|
||||
}
|
||||
if (url.includes('/treasury/recurring')) return 'treasury_recurring';
|
||||
if (url.includes('/treasury/reports')) return 'treasury_reports';
|
||||
if (url.includes('/treasury/audit')) return 'treasury_audit';
|
||||
if (url.includes('/treasury/categories')) return 'treasury_categories';
|
||||
if (
|
||||
url.includes('/treasury/accounts') ||
|
||||
url.includes('/treasury/transfers') ||
|
||||
url.includes('/treasury/reconciliations') ||
|
||||
url.includes('/treasury/adjustments')
|
||||
) {
|
||||
return 'treasury_accounts';
|
||||
}
|
||||
if (url.includes('/treasury/dashboard')) return 'treasury_dashboard';
|
||||
return 'treasury_documents';
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user