feat: insurance & medical billing system (6 phases)
Multi-tenant insurance contracts, service coverage, versioned tariffs, invoice calculation, and insurance claims with debt reporting. - TenantInsurance: per-tenant insurance contracts (coverage/franchise/ceiling, versioning, soft-deactivate) + active guard - ServiceItem.insuranceCovered + TenantServiceCoverage per-service overrides - Tariff: versioned yearly tariffs with fallback to ServiceItem price - Billing domain: Money/ShareBreakdown VOs, BillingCalculator (unit-tested), Invoice/InvoiceItem aggregate, InvoiceService.createFromSession - Claim/ClaimItem with state machine (pending->submitted->approved/rejected->paid), ClaimService, insurance-debt report - ClaimSubmitterInterface + ManualClaimSubmitter (future insurance API ready) - Admin UI: insurance-pricing page, claims page, service tariff modal, service insurance toggle; routes + sidebar entries - Architecture doc + billing/insurance/clinic-services API docs Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,53 @@
|
||||
<?php
|
||||
|
||||
namespace App\Billing\Service;
|
||||
|
||||
use App\Billing\ValueObject\Money;
|
||||
use App\Billing\ValueObject\ShareBreakdown;
|
||||
use App\Insurance\ValueObject\CoverageRule;
|
||||
|
||||
class BillingCalculator
|
||||
{
|
||||
/**
|
||||
* محاسبهی سهم برای یک آیتم.
|
||||
* ترتیب: کل → پوشش پایه (با سقف) → باقیمانده → پوشش مکمل روی باقیمانده (با سقف) → فرانشیز سهم بیمار.
|
||||
*/
|
||||
public function calculateItem(
|
||||
Money $total,
|
||||
?CoverageRule $base,
|
||||
?CoverageRule $supplementary,
|
||||
): ShareBreakdown {
|
||||
$baseShare = Money::zero();
|
||||
$remaining = $total;
|
||||
|
||||
if ($base !== null && $base->covered) {
|
||||
$baseShare = $total->percent($base->coveragePercent);
|
||||
if ($base->ceilingRials !== null) {
|
||||
$baseShare = $baseShare->min(new Money($base->ceilingRials));
|
||||
}
|
||||
$remaining = $total->sub($baseShare);
|
||||
}
|
||||
|
||||
$suppShare = Money::zero();
|
||||
if ($supplementary !== null && $supplementary->covered) {
|
||||
$suppShare = $remaining->percent($supplementary->coveragePercent);
|
||||
if ($supplementary->ceilingRials !== null) {
|
||||
$suppShare = $suppShare->min(new Money($supplementary->ceilingRials));
|
||||
}
|
||||
$remaining = $remaining->sub($suppShare);
|
||||
}
|
||||
|
||||
// فرانشیز سهم بیمار است؛ از سهم بیمه کم نمیکند ولی سهم بیمار از کل بیشتر نمیشود.
|
||||
$franchise = new Money(
|
||||
($base?->franchiseRials ?? 0) + ($supplementary?->franchiseRials ?? 0)
|
||||
);
|
||||
$patient = $remaining->add($franchise)->min($total);
|
||||
|
||||
return new ShareBreakdown(
|
||||
totalRials: $total->rials,
|
||||
baseInsuranceRials: $baseShare->rials,
|
||||
supplementaryRials: $suppShare->rials,
|
||||
patientRials: $patient->rials,
|
||||
);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user