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>
78 lines
2.9 KiB
PHP
78 lines
2.9 KiB
PHP
<?php
|
|
|
|
namespace App\Tests\Billing;
|
|
|
|
use App\Billing\Service\BillingCalculator;
|
|
use App\Billing\ValueObject\Money;
|
|
use App\Insurance\ValueObject\CoverageRule;
|
|
use PHPUnit\Framework\TestCase;
|
|
|
|
class BillingCalculatorTest extends TestCase
|
|
{
|
|
private BillingCalculator $calc;
|
|
|
|
protected function setUp(): void
|
|
{
|
|
$this->calc = new BillingCalculator();
|
|
}
|
|
|
|
public function testNoInsuranceAllPatient(): void
|
|
{
|
|
$b = $this->calc->calculateItem(new Money(600_000), null, null);
|
|
$this->assertSame(600_000, $b->patientRials);
|
|
$this->assertSame(0, $b->baseInsuranceRials);
|
|
$this->assertSame(0, $b->supplementaryRials);
|
|
}
|
|
|
|
public function testBaseAndSupplementary(): void
|
|
{
|
|
// کل 600,000؛ پایه 70% → 420,000؛ مکمل روی 180,000 با ~66.667% → 120,000؛ بیمار 60,000
|
|
$base = new CoverageRule(coveragePercent: 70, franchiseRials: 0, ceilingRials: null);
|
|
$supp = new CoverageRule(coveragePercent: 66.6667, franchiseRials: 0, ceilingRials: null);
|
|
|
|
$b = $this->calc->calculateItem(new Money(600_000), $base, $supp);
|
|
|
|
$this->assertSame(420_000, $b->baseInsuranceRials);
|
|
$this->assertSame(120_000, $b->supplementaryRials);
|
|
$this->assertSame(60_000, $b->patientRials);
|
|
$this->assertSame(
|
|
$b->totalRials,
|
|
$b->baseInsuranceRials + $b->supplementaryRials + $b->patientRials
|
|
);
|
|
}
|
|
|
|
public function testBaseOnly(): void
|
|
{
|
|
$base = new CoverageRule(coveragePercent: 70, franchiseRials: 0, ceilingRials: null);
|
|
$b = $this->calc->calculateItem(new Money(600_000), $base, null);
|
|
$this->assertSame(420_000, $b->baseInsuranceRials);
|
|
$this->assertSame(0, $b->supplementaryRials);
|
|
$this->assertSame(180_000, $b->patientRials);
|
|
}
|
|
|
|
public function testBaseCeilingCapsShare(): void
|
|
{
|
|
// پایه 70% = 420,000 ولی سقف 300,000 → بیمار باقی را میدهد
|
|
$base = new CoverageRule(coveragePercent: 70, franchiseRials: 0, ceilingRials: 300_000);
|
|
$b = $this->calc->calculateItem(new Money(600_000), $base, null);
|
|
$this->assertSame(300_000, $b->baseInsuranceRials);
|
|
$this->assertSame(300_000, $b->patientRials);
|
|
}
|
|
|
|
public function testFranchiseAddedToPatient(): void
|
|
{
|
|
// پایه 100% ولی فرانشیز 50,000 سهم بیمار
|
|
$base = new CoverageRule(coveragePercent: 100, franchiseRials: 50_000, ceilingRials: null);
|
|
$b = $this->calc->calculateItem(new Money(600_000), $base, null);
|
|
$this->assertSame(50_000, $b->patientRials);
|
|
$this->assertSame(600_000, $b->baseInsuranceRials);
|
|
}
|
|
|
|
public function testNotCoveredRule(): void
|
|
{
|
|
$b = $this->calc->calculateItem(new Money(600_000), CoverageRule::notCovered(), CoverageRule::notCovered());
|
|
$this->assertSame(600_000, $b->patientRials);
|
|
$this->assertSame(0, $b->baseInsuranceRials);
|
|
}
|
|
}
|