feat: Add online share functionality for secretaries

- Introduced `online_share_enabled` and `online_share_percent` fields in the `doctor_secretaries` table to manage secretary shares from online appointments.
- Added `bank_account` field in the `profiles` table to store user-level IBANs for settlements.
- Created `secretary_earnings` table to track earnings per secretary from online appointments, including a foreign key relationship with `financial_breakdowns`.
- Implemented `SecretaryEarning` entity and repository for managing secretary earnings.
- Developed `SecretaryShareResolver` service to determine which secretaries earn from online payments.
- Added `UserIbanResolver` service to handle user IBAN retrieval and management.
- Created `HasIbansTrait` for entities to manage IBANs in a JSON format.
- Implemented tests for secretary earnings and API endpoints for managing secretary shares and IBANs.
This commit is contained in:
hamed
2026-07-25 18:34:18 +03:30
parent 73a608c3dd
commit 8d2b0d908a
33 changed files with 2564 additions and 76 deletions
+27 -1
View File
@@ -11,6 +11,7 @@ import {
ClipboardDocumentCheckIcon,
Cog6ToothIcon,
CreditCardIcon,
CurrencyDollarIcon,
DevicePhoneMobileIcon,
DocumentTextIcon,
FolderOpenIcon,
@@ -30,6 +31,7 @@ import { useState } from "react";
import { NavLink, useLocation, useNavigate } from "react-router-dom";
import { useSubscription } from "../../hooks/useSubscription";
import { usePermissions } from "../../hooks/usePermissions";
import { useSecretaryEarnings } from "../../hooks/useSecretaryEarnings";
import { useAuthStore } from "../../stores/authStore";
import { useUiStore } from "../../stores/uiStore";
@@ -65,6 +67,8 @@ function buildSections(
dbUuid: string | null,
scope: string | null,
can: (resource: string, action: string) => boolean,
/** سهم درآمد نوبت آنلاین برای این منشی فعال است؟ آیتم‌های مالی به آن گِیت می‌شوند. */
secretaryShareEnabled = false,
): Section[] {
// پزشکِ مهمان در محیط کلینیک (scope=clinic): منو از روی مجوزهایی که کلینیک
// برایش تعیین کرده ساخته می‌شود، نه به‌صورت hardcode.
@@ -446,6 +450,21 @@ function buildSections(
label: "انبارداری",
});
}
// درآمد و تسویه فقط وقتی ادمین سهم نوبت آنلاین را برای این منشی فعال کرده باشد.
if (secretaryShareEnabled) {
items.push(
{
to: "/admin/secretary-earnings",
icon: CurrencyDollarIcon,
label: "درآمد من",
},
{
to: "/admin/secretary-settlement",
icon: BanknotesIcon,
label: "تسویه حساب",
},
);
}
// منابعِ زیرمجموعهٔ «تنظیمات» (staff/discounts/sms/tags/appointment_settings/
// clinic_doctors) در سایدبار اصلی نمی‌آیند — دقیقاً مثل پزشک/کلینیک، فقط داخل
@@ -705,7 +724,14 @@ export default function Sidebar({ mobileOpen: _m, onMobileClose: _c }: Props) {
const navigate = useNavigate();
const { can } = usePermissions();
const sections = buildSections(primaryRole, dbUuid, context?.scope ?? null, can);
const { summary: secretaryEarnings } = useSecretaryEarnings(primaryRole === "secretary");
const sections = buildSections(
primaryRole,
dbUuid,
context?.scope ?? null,
can,
secretaryEarnings?.enabled ?? false,
);
const initials = (userName ?? "U").charAt(0).toUpperCase();
return (