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:
@@ -424,6 +424,11 @@ export interface OrgUserDto {
|
||||
* اگر کل آبجکت خالی باشد و نقش هم خالی باشد → بدون محدودیت عمقی.
|
||||
*/
|
||||
dataAccessOverride?: DataAccessPolicy;
|
||||
/**
|
||||
* null = دسترسی مالی از نقش میآید.
|
||||
* آبجکت = جایگزین بخشها و حسابهای مالی همین کاربر.
|
||||
*/
|
||||
treasuryAccessOverride?: UserTreasuryAccess | null;
|
||||
/** سیاست مؤثر پس از resolve (نقش + override) — فقط برای نمایش UI */
|
||||
effectiveDataAccess?: DataAccessPolicy;
|
||||
/** @deprecated از effectiveDataAccess.customers استفاده کنید */
|
||||
@@ -596,6 +601,39 @@ export interface UpdateUserDataAccessRequest {
|
||||
* آبجکت یعنی جایگزینی کامل override (کلید غایب در آبجکت = آن بخش از override پاک میشود).
|
||||
*/
|
||||
dataAccessOverride?: DataAccessPolicy | null;
|
||||
/**
|
||||
* null = دسترسی مالی از نقش.
|
||||
* نبودن فیلد یعنی این ذخیره به دسترسی مالی دست نمیزند.
|
||||
*/
|
||||
treasuryAccessOverride?: UserTreasuryAccess | null;
|
||||
}
|
||||
|
||||
/** دسترسی مالی اختصاصی یک کاربر؛ جایگزین نقش میشود */
|
||||
export type UserTreasuryAccess = {
|
||||
enabled: boolean;
|
||||
sections: RolePermissionKey[];
|
||||
ownAccounts: boolean;
|
||||
accountIds: string[];
|
||||
};
|
||||
|
||||
export function parseUserTreasuryAccess(raw: unknown): UserTreasuryAccess | null {
|
||||
if (raw == null || typeof raw !== "object" || Array.isArray(raw)) return null;
|
||||
const obj = raw as Record<string, unknown>;
|
||||
const sections = Array.isArray(obj.sections)
|
||||
? obj.sections.filter(
|
||||
(item): item is RolePermissionKey =>
|
||||
typeof item === "string" && item.startsWith("treasury_"),
|
||||
)
|
||||
: [];
|
||||
const accountIds = Array.isArray(obj.accountIds)
|
||||
? obj.accountIds.filter((item): item is string => typeof item === "string" && item.trim().length > 0)
|
||||
: [];
|
||||
return {
|
||||
enabled: obj.enabled === true,
|
||||
sections: [...new Set(sections)],
|
||||
ownAccounts: obj.ownAccounts === true,
|
||||
accountIds: [...new Set(accountIds.map((id) => id.trim()))],
|
||||
};
|
||||
}
|
||||
|
||||
export function emptySectionDataAccess(): SectionDataAccess {
|
||||
@@ -741,7 +779,16 @@ export type RolePermissionKey =
|
||||
| "conversations"
|
||||
| "conversations_groups"
|
||||
| "treasury"
|
||||
| "treasury_dashboard"
|
||||
| "treasury_incomes"
|
||||
| "treasury_expenses"
|
||||
| "treasury_receivables"
|
||||
| "treasury_accounts"
|
||||
| "treasury_payroll"
|
||||
| "treasury_recurring"
|
||||
| "treasury_reports"
|
||||
| "treasury_audit"
|
||||
| "treasury_categories"
|
||||
| "behavior";
|
||||
|
||||
/** بخشی که مسئولش را میشود به کاربر دیگری داد */
|
||||
@@ -787,10 +834,59 @@ export const ROLE_PERMISSION_ITEMS: Array<{
|
||||
{ key: "conversations", label: "گفتگو" },
|
||||
{ key: "conversations_groups", label: "ساخت گروه گفتگو" },
|
||||
{ key: "treasury", label: "مدیریت مالی" },
|
||||
{ key: "treasury_payroll", label: "حقوق و دستمزد" },
|
||||
{ key: "treasury_dashboard", label: "مالی — داشبورد" },
|
||||
{ key: "treasury_incomes", label: "مالی — دریافتها و درآمدها" },
|
||||
{ key: "treasury_expenses", label: "مالی — پرداختها و هزینهها" },
|
||||
{ key: "treasury_receivables", label: "مالی — اقساط و مطالبات" },
|
||||
{ key: "treasury_accounts", label: "مالی — صندوق و حسابها" },
|
||||
{ key: "treasury_payroll", label: "مالی — حقوق و دستمزد" },
|
||||
{ key: "treasury_recurring", label: "مالی — تکرارشونده" },
|
||||
{ key: "treasury_reports", label: "مالی — گزارش" },
|
||||
{ key: "treasury_audit", label: "مالی — سوابق" },
|
||||
{ key: "treasury_categories", label: "مالی — دستهبندیها" },
|
||||
{ key: "behavior", label: "شخصیتشناسی و دستیار فروش" },
|
||||
];
|
||||
|
||||
/** بخشهای ریز مدیریت مالی. حقوق جداست و با تیک خودش باز میشود. */
|
||||
export const TREASURY_SECTION_KEYS = [
|
||||
"treasury_dashboard",
|
||||
"treasury_incomes",
|
||||
"treasury_expenses",
|
||||
"treasury_receivables",
|
||||
"treasury_accounts",
|
||||
"treasury_payroll",
|
||||
"treasury_recurring",
|
||||
"treasury_reports",
|
||||
"treasury_audit",
|
||||
"treasury_categories",
|
||||
] as const satisfies readonly RolePermissionKey[];
|
||||
|
||||
export type TreasurySectionKey = (typeof TREASURY_SECTION_KEYS)[number];
|
||||
|
||||
/**
|
||||
* دسترسی کلی «مدیریت مالی» در را باز میکند.
|
||||
* اگر هیچ بخش ریزی انتخاب نشده باشد همه بخشها بهجز حقوق بازند.
|
||||
* با انتخاب حتی یک بخش ریز، فقط همانها باز میمانند.
|
||||
*/
|
||||
export function treasurySectionAllowed(
|
||||
role: string | null | undefined,
|
||||
permissions: readonly string[] | null | undefined,
|
||||
section: TreasurySectionKey | "treasury_documents",
|
||||
): boolean {
|
||||
if (role === "owner" || role === "admin") return true;
|
||||
const perms = permissions ?? [];
|
||||
if (section === "treasury_payroll") return perms.includes("treasury_payroll");
|
||||
if (!perms.includes("treasury")) return false;
|
||||
const fine = TREASURY_SECTION_KEYS.filter(
|
||||
(key) => key !== "treasury_payroll" && perms.includes(key),
|
||||
);
|
||||
if (fine.length === 0) return true;
|
||||
if (section === "treasury_documents") {
|
||||
return perms.includes("treasury_incomes") || perms.includes("treasury_expenses");
|
||||
}
|
||||
return perms.includes(section);
|
||||
}
|
||||
|
||||
export interface OrgRoleMemberDto {
|
||||
id: string;
|
||||
fullName: string;
|
||||
@@ -816,6 +912,10 @@ export interface OrgRoleDto {
|
||||
childCount: number;
|
||||
userCount: number;
|
||||
members: OrgRoleMemberDto[];
|
||||
/** حسابهای اضافی؛ خالی بهتنهایی یعنی هنوز محدود نشده */
|
||||
treasuryAccountIds: string[];
|
||||
/** true یعنی اعضای نقش حسابهایی را میبینند که خودشان مسئول آنها هستند */
|
||||
treasuryOwnAccounts: boolean;
|
||||
}
|
||||
|
||||
export interface CreateOrgRoleRequest {
|
||||
@@ -826,6 +926,10 @@ export interface CreateOrgRoleRequest {
|
||||
dataAccessPolicy?: DataAccessPolicy;
|
||||
/** اگر ست شود، این نقش زیرمجموعهٔ نقش والد خواهد بود */
|
||||
parentId?: string | null;
|
||||
/** حسابهای اضافی، جدا از حسابهایی که خودش مسئول است */
|
||||
treasuryAccountIds?: string[];
|
||||
/** دسترسی به حسابهایی که نام خود عضو در مسئول ثبت شده */
|
||||
treasuryOwnAccounts?: boolean;
|
||||
}
|
||||
|
||||
export interface UpdateOrgRoleRequest {
|
||||
@@ -836,6 +940,10 @@ export interface UpdateOrgRoleRequest {
|
||||
dataAccessPolicy?: DataAccessPolicy;
|
||||
/** null یعنی جدا کردن از والد و تبدیل به ریشه */
|
||||
parentId?: string | null;
|
||||
/** حسابهای اضافی، جدا از حسابهایی که خودش مسئول است */
|
||||
treasuryAccountIds?: string[];
|
||||
/** دسترسی به حسابهایی که نام خود عضو در مسئول ثبت شده */
|
||||
treasuryOwnAccounts?: boolean;
|
||||
}
|
||||
|
||||
export interface InviteUserRequest {
|
||||
|
||||
Reference in New Issue
Block a user