Files
clinicpro/tests/Insurance/CoveragePercentResolutionTest.php
T
hamed 4f4bce9fe2 feat(migrations): update franchise to percentage in tenant_insurances and tenant_service_coverage
- 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.
2026-07-29 13:28:59 +03:30

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));
}
}