feat(insurance): add insurance coverage flag to selected services and update coverage logic

This commit is contained in:
hamed
2026-06-24 06:09:05 +03:30
parent 17f41117f1
commit 69beb7b944
3 changed files with 13 additions and 3 deletions
+4 -2
View File
@@ -61,7 +61,7 @@ export default function NewSessionPage() {
const nav = useNavigate();
const qc = useQueryClient();
const [selectedServices, setSelectedServices] = useState<{ uuid: string; name: string; price: number; qty: number }[]>([]);
const [selectedServices, setSelectedServices] = useState<{ uuid: string; name: string; price: number; qty: number; insured: boolean }[]>([]);
const [sectionUuid, setSectionUuid] = useState('');
const [itemUuid, setItemUuid] = useState('');
const [baseId, setBaseId] = useState('');
@@ -154,7 +154,7 @@ export default function NewSessionPage() {
if (!itemUuid) return;
const found = itemsData?.data?.find(i => i.uuid === itemUuid);
if (!found || selectedServices.some(s => s.uuid === found.uuid)) return;
setSelectedServices(p => [...p, { uuid: found.uuid, name: found.name, price: found.price_rials, qty: 1 }]);
setSelectedServices(p => [...p, { uuid: found.uuid, name: found.name, price: found.price_rials, qty: 1, insured: !!found.insurance_covered }]);
setItemUuid('');
};
@@ -168,6 +168,8 @@ export default function NewSessionPage() {
const servicesPatient = useMemo(
() => selectedServices.reduce((sum, x) => {
const total = x.price * x.qty;
// پرچم سرویس gate نهایی: اگر «شامل بیمه» نباشد، کامل سهم بیمار است.
if (!x.insured) return sum + total;
return sum + patientShareOf(
total,
ruleFor(baseContract, baseCoverage, x.uuid),
+1 -1
View File
@@ -455,4 +455,4 @@ override پوشش یک خدمت خاص تحت قرارداد یک بیمه. فی
پاسخ `200`: `{ success, data: { message } }`.
خطاها: `404 ERR_NOT_FOUND_001` قرارداد یافت نشد · `422 ERR_VALIDATION_001` سرویس یافت نشد · `403 ERR_FORBIDDEN_001` سرویس متعلق به شما نیست.
> منطق resolve: `TenantInsuranceService::coverageRuleForService()` ابتدا override خدمت را بررسی می‌کند؛ اگر `covered=false` → `CoverageRule::notCovered()`؛ در غیر این صورت فیلدهای null از قرارداد پر می‌شوند. این `CoverageRule` ورودی `BillingCalculator` است و سهم بیمه‌ی هر `InvoiceItem` را تعیین می‌کند؛ همان سهم‌ها در `ClaimService::createFromInvoice()` به `ClaimItem` (مطالبات بیمه) تبدیل می‌شوند. پنل ادمین این endpoint را از مودال «پوشش بیمه» در صفحه سرویس‌های کلینیک فراخوانی می‌کند.
> منطق resolve: `TenantInsuranceService::coverageRuleForService()` ابتدا **پرچم `ServiceItem.insurance_covered`** را چک می‌کند؛ اگر این خدمت «شامل بیمه» نباشد، بدون توجه به override یا قرارداد، `CoverageRule::notCovered()` برمی‌گردد (gate نهایی). سپس override خدمت بررسی می‌شود؛ اگر `covered=false` → `notCovered()`؛ در غیر این صورت فیلدهای null از قرارداد پر می‌شوند. این `CoverageRule` ورودی `BillingCalculator` است و سهم بیمه‌ی هر `InvoiceItem` را تعیین می‌کند؛ همان سهم‌ها در `ClaimService::createFromInvoice()` به `ClaimItem` (مطالبات بیمه) تبدیل می‌شوند. پنل ادمین این endpoint را از مودال «پوشش بیمه» در صفحه سرویس‌های کلینیک فراخوانی می‌کند.
@@ -2,6 +2,7 @@
namespace App\Insurance\Service;
use App\ClinicService\Repository\ServiceItemRepository;
use App\Insurance\Entity\TenantInsurance;
use App\Insurance\Repository\InsuranceRepository;
use App\Insurance\Repository\TenantInsuranceRepository;
@@ -16,6 +17,7 @@ class TenantInsuranceService
private readonly TenantInsuranceRepository $repo,
private readonly InsuranceRepository $insuranceRepo,
private readonly TenantServiceCoverageRepository $coverageRepo,
private readonly ServiceItemRepository $serviceItemRepo,
) {}
/**
@@ -101,6 +103,12 @@ class TenantInsuranceService
return CoverageRule::notCovered();
}
// پرچم سرویس gate نهایی است: اگر «این خدمت شامل بیمه می‌شود» غیرفعال باشد، هیچ پوششی محاسبه نمی‌شود.
$service = $this->serviceItemRepo->find($serviceItemId);
if ($service === null || !$service->isInsuranceCovered()) {
return CoverageRule::notCovered();
}
$contract = $this->repo->findActiveContract($entityType, $entityId, $insuranceId);
if ($contract === null) {
return CoverageRule::notCovered();