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:
@@ -0,0 +1,125 @@
|
||||
<?php
|
||||
|
||||
namespace App\Tests\Billing;
|
||||
|
||||
use App\Auth\Entity\User;
|
||||
use App\Billing\Entity\Invoice;
|
||||
use App\Billing\Entity\InvoiceItem;
|
||||
use App\Billing\Repository\ClaimRepository;
|
||||
use App\Billing\Service\InvoiceService;
|
||||
use App\Billing\ValueObject\ShareBreakdown;
|
||||
use App\Doctor\Entity\Doctor;
|
||||
use App\Patient\Entity\PatientRecord;
|
||||
use App\Tests\ApiTestCase;
|
||||
|
||||
/**
|
||||
* مطالبهٔ بیمه اثر جانبیِ نهاییشدن صورتحساب است، نه کارِ یک endpoint خاص.
|
||||
* پیش از این فقط `POST /api/v1/patient/{uuid}/session` مطالبه میساخت، و هر
|
||||
* صورتحسابی که از مسیر دیگری نهایی میشد بیصدا بدون مطالبه میماند.
|
||||
*/
|
||||
class ClaimAutoCreationTest extends ApiTestCase
|
||||
{
|
||||
private Doctor $doctor;
|
||||
private PatientRecord $record;
|
||||
|
||||
protected function setUp(): void
|
||||
{
|
||||
parent::setUp();
|
||||
|
||||
$owner = $this->createUser(['ROLE_DOCTOR']);
|
||||
$this->doctor = new Doctor($owner, 'دکتر مطالبه');
|
||||
$this->em->persist($this->doctor);
|
||||
$this->em->flush();
|
||||
|
||||
$patient = $this->createUser(['ROLE_USER']);
|
||||
$this->record = new PatientRecord('doctor', $this->doctor->getId(), $patient, 'doctor', $this->doctor->getId());
|
||||
$this->em->persist($this->record);
|
||||
$this->em->flush();
|
||||
}
|
||||
|
||||
/** صورتحساب draft با سهم بیمهٔ پایه/تکمیلی روی تنها آیتمش. */
|
||||
private function draftInvoice(?int $baseId, ?int $suppId, int $baseShare, int $suppShare): Invoice
|
||||
{
|
||||
$total = 10_000_000;
|
||||
$invoice = new Invoice('doctor', $this->doctor->getId());
|
||||
$invoice->setPatientRecordId((int) $this->record->getId())
|
||||
->setBaseInsuranceId($baseId)
|
||||
->setSupplementaryInsuranceId($suppId);
|
||||
|
||||
$item = new InvoiceItem(
|
||||
$invoice,
|
||||
'ویزیت',
|
||||
$total,
|
||||
1,
|
||||
new ShareBreakdown($total, $baseShare, $suppShare, $total - $baseShare - $suppShare),
|
||||
);
|
||||
$invoice->addItem($item);
|
||||
$invoice->recalculateTotals();
|
||||
|
||||
$this->em->persist($invoice);
|
||||
$this->em->persist($item);
|
||||
$this->em->flush();
|
||||
|
||||
return $invoice;
|
||||
}
|
||||
|
||||
private function claimRepo(): ClaimRepository
|
||||
{
|
||||
return static::getContainer()->get(ClaimRepository::class);
|
||||
}
|
||||
|
||||
private function invoiceService(): InvoiceService
|
||||
{
|
||||
return static::getContainer()->get(InvoiceService::class);
|
||||
}
|
||||
|
||||
public function testFinalizingAnInsuredInvoiceCreatesItsClaims(): void
|
||||
{
|
||||
$invoice = $this->draftInvoice(1, 2, 6_000_000, 2_000_000);
|
||||
|
||||
$this->invoiceService()->finalize($invoice);
|
||||
|
||||
$kinds = $this->claimRepo()->kindsForInvoice((int) $invoice->getId());
|
||||
sort($kinds);
|
||||
self::assertSame(['base', 'supplementary'], $kinds);
|
||||
}
|
||||
|
||||
public function testASupplementaryOnlyInvoiceStillGetsItsClaim(): void
|
||||
{
|
||||
// همان حالتی که در پنل دیده شد: بیمار بیمهٔ پایه ندارد، فقط تکمیلی.
|
||||
$invoice = $this->draftInvoice(null, 2, 0, 4_760_000);
|
||||
|
||||
$this->invoiceService()->finalize($invoice);
|
||||
|
||||
self::assertSame(['supplementary'], $this->claimRepo()->kindsForInvoice((int) $invoice->getId()));
|
||||
}
|
||||
|
||||
public function testFinalizingTwiceDoesNotDuplicateClaims(): void
|
||||
{
|
||||
$invoice = $this->draftInvoice(1, null, 3_000_000, 0);
|
||||
|
||||
$this->invoiceService()->finalize($invoice);
|
||||
$this->invoiceService()->finalize($invoice);
|
||||
|
||||
self::assertCount(1, $this->claimRepo()->kindsForInvoice((int) $invoice->getId()));
|
||||
}
|
||||
|
||||
public function testAnInvoiceWithoutAnyInsuranceShareGetsNoClaim(): void
|
||||
{
|
||||
$invoice = $this->draftInvoice(null, null, 0, 0);
|
||||
|
||||
$this->invoiceService()->finalize($invoice);
|
||||
|
||||
self::assertSame([], $this->claimRepo()->kindsForInvoice((int) $invoice->getId()));
|
||||
}
|
||||
|
||||
/** بیمهای که فقط روی صورتحساب ثبت شده ولی سهمی نبرده، مطالبه نمیسازد. */
|
||||
public function testAZeroShareInsuranceIsNotClaimed(): void
|
||||
{
|
||||
$invoice = $this->draftInvoice(1, 2, 6_000_000, 0);
|
||||
|
||||
$this->invoiceService()->finalize($invoice);
|
||||
|
||||
self::assertSame(['base'], $this->claimRepo()->kindsForInvoice((int) $invoice->getId()));
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user