feat: Enhance insurance billing system to support supplementary insurance
- Updated CoverageRule and related entities to include franchise_percent instead of franchise_rials. - Modified Appointment entity to carry supplementary insurance ID alongside base insurance. - Implemented SessionBillingService to ensure finalized invoices for insured patient sessions. - Created InvoiceFinalized event to trigger claims creation upon invoice finalization. - Added BackfillMissingClaimsCommand to generate claims for finalized invoices without existing claims. - Developed tests to validate the new functionality for supplementary insurance handling in appointments and claims.
This commit is contained in:
@@ -47,10 +47,18 @@ export function useAppointmentInsurance(enabled: boolean) {
|
||||
[contractsQuery.data],
|
||||
);
|
||||
|
||||
const activeContracts = useMemo(() => contracts.filter((c) => c.is_active !== false), [contracts]);
|
||||
const basicContracts = useMemo(
|
||||
() => contracts.filter((c) => c.is_active !== false && c.insurance_kind !== 'supplementary'),
|
||||
[contracts],
|
||||
() => activeContracts.filter((c) => c.insurance_kind !== 'supplementary'),
|
||||
[activeContracts],
|
||||
);
|
||||
const supplementaryContracts = useMemo(
|
||||
() => activeContracts.filter((c) => c.insurance_kind === 'supplementary'),
|
||||
[activeContracts],
|
||||
);
|
||||
|
||||
const optionsOf = (list: TenantContract[]) =>
|
||||
list.map((c) => ({ value: String(c.insurance_id), label: c.insurance_name ?? `#${c.insurance_id}` }));
|
||||
|
||||
return {
|
||||
/**
|
||||
@@ -70,18 +78,26 @@ export function useAppointmentInsurance(enabled: boolean) {
|
||||
categoryLabelOf: (key: string | null | undefined) =>
|
||||
(pricing.service_categories ?? []).find((c) => c.key === key)?.label ?? null,
|
||||
|
||||
insuranceOptions: basicContracts.map((c) => ({
|
||||
value: String(c.insurance_id),
|
||||
label: c.insurance_name ?? `#${c.insurance_id}`,
|
||||
})),
|
||||
insuranceOptions: optionsOf(basicContracts),
|
||||
/** قراردادهای تکمیلیِ فعال — روی باقیماندهٔ بعد از بیمهٔ پایه اعمال میشوند. */
|
||||
supplementaryOptions: optionsOf(supplementaryContracts),
|
||||
hasSupplementary: supplementaryContracts.length > 0,
|
||||
contractOf: (insuranceId: string | number | null | undefined): TenantContract | null =>
|
||||
basicContracts.find((c) => String(c.insurance_id) === String(insuranceId)) ?? null,
|
||||
insuranceNameOf: (insuranceId: string | number | null | undefined): string | null =>
|
||||
contracts.find((c) => String(c.insurance_id) === String(insuranceId))?.insurance_name ?? null,
|
||||
|
||||
/** تفکیک سهم بیمه/بیمار برای ردیفهای دادهشده تحت قرارداد یک بیمه. */
|
||||
breakdown: (lines: BillableLine[], insuranceId: string | number | null | undefined): ShareBreakdown =>
|
||||
breakdownOf(lines, basicContracts.find((c) => String(c.insurance_id) === String(insuranceId)) ?? null),
|
||||
/** تفکیک زنجیرهای سهمها: پایه روی کل، تکمیلی روی باقیمانده. */
|
||||
breakdown: (
|
||||
lines: BillableLine[],
|
||||
insuranceId: string | number | null | undefined,
|
||||
supplementaryId: string | number | null | undefined = null,
|
||||
): ShareBreakdown =>
|
||||
breakdownOf(
|
||||
lines,
|
||||
basicContracts.find((c) => String(c.insurance_id) === String(insuranceId)) ?? null,
|
||||
supplementaryContracts.find((c) => String(c.insurance_id) === String(supplementaryId)) ?? null,
|
||||
),
|
||||
|
||||
isLoading: pricingQuery.isLoading || contractsQuery.isLoading,
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user