- Changed franchise_rials to franchise_percent in tenant_insurances and tenant_service_coverage tables. - Reset old rial values to 0/NULL as they are not convertible to percentage. feat(command): add SeedInsuranceScenarioCommand for seeding insurance data - Implemented a command to seed supplementary insurance contracts, patients, and claims for a specified doctor. - Includes functionality for purging existing scenario data and generating new entries with predefined contracts and patient scenarios.
181 lines
7.5 KiB
PHP
181 lines
7.5 KiB
PHP
<?php
|
|
|
|
namespace App\Tests\Insurance;
|
|
|
|
use App\ClinicService\Entity\ServiceItem;
|
|
use App\ClinicService\Entity\ServiceSection;
|
|
use App\Doctor\Entity\Doctor;
|
|
use App\Insurance\Entity\Insurance;
|
|
use App\Insurance\Entity\TenantInsurance;
|
|
use App\Insurance\Enum\InsuranceType;
|
|
use App\Insurance\Enum\ServiceCategory;
|
|
use App\Insurance\Service\InsuranceCoverageDefaultService;
|
|
use App\Insurance\Service\TenantInsuranceService;
|
|
use App\Tests\ApiTestCase;
|
|
|
|
/**
|
|
* The effective coverage percentage is resolved through a four-level chain:
|
|
* service override → contract category override → central admin default →
|
|
* legacy contract percentage. A contract that overrode nothing must follow the
|
|
* admin default live, which is what makes central management work at all.
|
|
*/
|
|
class CoveragePercentResolutionTest extends ApiTestCase
|
|
{
|
|
private TenantInsuranceService $service;
|
|
private InsuranceCoverageDefaultService $defaults;
|
|
|
|
protected function setUp(): void
|
|
{
|
|
parent::setUp();
|
|
$this->service = static::getContainer()->get(TenantInsuranceService::class);
|
|
$this->defaults = static::getContainer()->get(InsuranceCoverageDefaultService::class);
|
|
}
|
|
|
|
/** @return array{0: TenantInsurance, 1: Insurance, 2: ServiceItem} */
|
|
private function makeContract(float $legacyPercent = 55.0): array
|
|
{
|
|
$owner = $this->createUser(['ROLE_DOCTOR']);
|
|
$doctor = new Doctor($owner, 'دکتر تست درصد پوشش');
|
|
$this->em->persist($doctor);
|
|
|
|
$insurance = new Insurance('بیمه پایه ' . random_int(1000, 9999), InsuranceType::Basic);
|
|
$this->em->persist($insurance);
|
|
$this->em->flush();
|
|
|
|
$contract = new TenantInsurance(TenantInsurance::TYPE_DOCTOR, $doctor->getId(), $insurance->getId());
|
|
$contract->setCoveragePercent($legacyPercent);
|
|
$section = new ServiceSection(TenantInsurance::TYPE_DOCTOR, $doctor->getId(), 'بخش بستری');
|
|
$this->em->persist($contract);
|
|
$this->em->persist($section);
|
|
$this->em->flush();
|
|
|
|
$item = (new ServiceItem($section, 'جراحی', 5_952_000))
|
|
->setServiceCategory(ServiceCategory::Inpatient)
|
|
->setInsuranceCovered(true);
|
|
$this->em->persist($item);
|
|
$this->em->flush();
|
|
|
|
return [$contract, $insurance, $item];
|
|
}
|
|
|
|
public function testFallsBackToLegacyContractPercentWhenNothingIsConfigured(): void
|
|
{
|
|
[$contract] = $this->makeContract(55.0);
|
|
|
|
$this->assertSame(55.0, $this->service->resolvePercent($contract, ServiceCategory::Inpatient));
|
|
}
|
|
|
|
public function testAdminDefaultWinsOverLegacyContractPercent(): void
|
|
{
|
|
[$contract, $insurance] = $this->makeContract(55.0);
|
|
|
|
$this->defaults->save($insurance->getId(), [
|
|
['key' => 'inpatient', 'coverage_percent' => 30],
|
|
['key' => 'outpatient', 'coverage_percent' => 70],
|
|
]);
|
|
|
|
$this->assertSame(30.0, $this->service->resolvePercent($contract, ServiceCategory::Inpatient));
|
|
$this->assertSame(70.0, $this->service->resolvePercent($contract, ServiceCategory::Outpatient));
|
|
}
|
|
|
|
/** تغییر تنظیمات مرکزی باید فوراً روی قرارداد بدون override اثر بگذارد (fallback زنده). */
|
|
public function testChangingTheAdminDefaultImmediatelyChangesTheContract(): void
|
|
{
|
|
[$contract, $insurance] = $this->makeContract(55.0);
|
|
|
|
$this->defaults->save($insurance->getId(), [['key' => 'inpatient', 'coverage_percent' => 30]]);
|
|
$this->assertSame(30.0, $this->service->resolvePercent($contract, ServiceCategory::Inpatient));
|
|
|
|
$this->defaults->save($insurance->getId(), [['key' => 'inpatient', 'coverage_percent' => 45]]);
|
|
$this->assertSame(45.0, $this->service->resolvePercent($contract, ServiceCategory::Inpatient));
|
|
}
|
|
|
|
public function testContractCategoryOverrideWinsOverAdminDefault(): void
|
|
{
|
|
[$contract, $insurance] = $this->makeContract(55.0);
|
|
$this->defaults->save($insurance->getId(), [
|
|
['key' => 'inpatient', 'coverage_percent' => 30],
|
|
['key' => 'outpatient', 'coverage_percent' => 70],
|
|
]);
|
|
|
|
$this->service->setCategoryCoverages($contract, [['key' => 'inpatient', 'coverage_percent' => 90]]);
|
|
|
|
$this->assertSame(90.0, $this->service->resolvePercent($contract, ServiceCategory::Inpatient));
|
|
$this->assertSame(70.0, $this->service->resolvePercent($contract, ServiceCategory::Outpatient));
|
|
}
|
|
|
|
public function testServiceOverrideWinsOverEveryOtherLevel(): void
|
|
{
|
|
[$contract, $insurance, $item] = $this->makeContract(55.0);
|
|
// سرپایی هم پیشفرض دارد، وگرنه ذخیرهٔ درصدها با «درصد پوشش الزامی است» رد میشود.
|
|
$this->defaults->save($insurance->getId(), [
|
|
['key' => 'inpatient', 'coverage_percent' => 30],
|
|
['key' => 'outpatient', 'coverage_percent' => 70],
|
|
]);
|
|
$this->service->setCategoryCoverages($contract, [['key' => 'inpatient', 'coverage_percent' => 90]]);
|
|
|
|
$this->service->setServiceCoverage($contract, $item->getId(), true, 25.0, null, null);
|
|
|
|
$rule = $this->service->coverageRuleForService(
|
|
TenantInsurance::TYPE_DOCTOR,
|
|
$contract->getEntityId(),
|
|
$contract->getInsuranceId(),
|
|
$item->getId(),
|
|
);
|
|
|
|
$this->assertSame(25.0, $rule->coveragePercent);
|
|
}
|
|
|
|
/** درصد نوعِ خودِ خدمت باید انتخاب شود، نه درصد سرپایی. */
|
|
public function testServiceRuleUsesTheCategoryOfTheServiceItem(): void
|
|
{
|
|
[$contract, $insurance, $item] = $this->makeContract(55.0);
|
|
$this->defaults->save($insurance->getId(), [
|
|
['key' => 'inpatient', 'coverage_percent' => 30],
|
|
['key' => 'outpatient', 'coverage_percent' => 70],
|
|
]);
|
|
|
|
$rule = $this->service->coverageRuleForService(
|
|
TenantInsurance::TYPE_DOCTOR,
|
|
$contract->getEntityId(),
|
|
$contract->getInsuranceId(),
|
|
$item->getId(),
|
|
);
|
|
|
|
$this->assertSame(30.0, $rule->coveragePercent);
|
|
$this->assertSame(0.0, $rule->franchisePercent, 'فرانشیز در بیمهٔ پایه صفر میماند');
|
|
}
|
|
|
|
/** ویزیت خدمتِ سرپایی است. */
|
|
public function testVisitRuleAlwaysUsesTheOutpatientPercent(): void
|
|
{
|
|
[$contract, $insurance] = $this->makeContract(55.0);
|
|
$this->defaults->save($insurance->getId(), [
|
|
['key' => 'inpatient', 'coverage_percent' => 30],
|
|
['key' => 'outpatient' , 'coverage_percent' => 70],
|
|
]);
|
|
|
|
$rule = $this->service->coverageRule(
|
|
TenantInsurance::TYPE_DOCTOR,
|
|
$contract->getEntityId(),
|
|
$contract->getInsuranceId(),
|
|
);
|
|
|
|
$this->assertSame(70.0, $rule->coveragePercent);
|
|
}
|
|
|
|
public function testRemovingTheOverrideReturnsToTheAdminDefault(): void
|
|
{
|
|
[$contract, $insurance] = $this->makeContract(55.0);
|
|
$this->defaults->save($insurance->getId(), [
|
|
['key' => 'inpatient', 'coverage_percent' => 30],
|
|
['key' => 'outpatient', 'coverage_percent' => 70],
|
|
]);
|
|
$this->service->setCategoryCoverages($contract, [['key' => 'inpatient', 'coverage_percent' => 90]]);
|
|
|
|
$this->service->setCategoryCoverages($contract, [['key' => 'inpatient', 'coverage_percent' => null]]);
|
|
|
|
$this->assertSame(30.0, $this->service->resolvePercent($contract, ServiceCategory::Inpatient));
|
|
}
|
|
}
|