Files
clinicpro/tests/Insurance/RequireVisitPriceTest.php
T

260 lines
10 KiB
PHP

<?php
namespace App\Tests\Insurance;
use App\Appointment\Entity\Appointment;
use App\Auth\Entity\User;
use App\Clinic\Entity\Clinic;
use App\Doctor\Entity\Doctor;
use App\Insurance\Entity\EntityInsurancePricing;
use App\Patient\Entity\PatientRecord;
use App\Shared\Constant\ErrorCodes;
use App\Tests\ApiTestCase;
/**
* فلگ «الزامی کردن هزینه ویزیت» (require_visit_price روی ردیف free-visit):
* - PUT /insurance-pricing: فلگ فعال بدون قیمت > 0 → 422؛ فلگ-فقط بدون ردیف → ردیف ساخته نشود.
* - POST /patient/{uuid}/session: با فلگ فعال، visit_price_rials <= 0 → 422.
* - POST /my/appointment و /admin/appointment: فلگ برای پزشکِ نوبت resolve می‌شود
* (ردیف پزشک، وگرنه کلینیکِ واحد او)؛ بدون هزینه ویزیت → 422.
*/
class RequireVisitPriceTest extends ApiTestCase
{
/** @return array{0: User, 1: Doctor} */
private function doctor(): array
{
$owner = $this->createUser(['ROLE_DOCTOR']);
$doctor = new Doctor($owner, 'دکتر');
$this->em->persist($doctor);
$this->em->flush();
return [$owner, $doctor];
}
private function pricingRow(string $type, int $entityId, int $priceRials, bool $require): EntityInsurancePricing
{
$row = new EntityInsurancePricing($type, $entityId, null, $priceRials);
$row->setRequireVisitPrice($require);
$this->em->persist($row);
$this->em->flush();
return $row;
}
private function recordFor(Doctor $doctor): PatientRecord
{
$patient = $this->createUser(['ROLE_USER']);
$record = new PatientRecord('doctor', $doctor->getId(), $patient, 'doctor', $doctor->getId());
$this->em->persist($record);
$this->em->flush();
return $record;
}
private function appointmentBody(string $doctorUuid): array
{
$start = time() + 86_400 + random_int(0, 3_600) * 100;
return [
'doctor_uuid' => $doctorUuid,
'slot_start' => $start,
'slot_end' => $start + 1_800,
'patient_mobile' => '09' . str_pad((string) random_int(0, 999_999_999), 9, '0', STR_PAD_LEFT),
'patient_name' => 'بیمار تست',
'patient_national_code' => '00' . str_pad((string) random_int(0, 99_999_999), 8, '0', STR_PAD_LEFT),
];
}
// ── PUT /api/v1/insurance-pricing ────────────────────────────────────────
public function testSaveFlagOnWithZeroPriceIs422(): void
{
[$owner] = $this->doctor();
$res = $this->authJson('PUT', '/api/v1/insurance-pricing', $owner, [
'free_visit_price_rials' => 0,
'require_visit_price' => true,
]);
self::assertSame(422, $this->responseCode());
self::assertSame(ErrorCodes::ERR_VALIDATION_001, $res['errors'][0]['code']);
}
public function testSaveFlagOnWithValidPricePersistsBoth(): void
{
[$owner] = $this->doctor();
$this->authJson('PUT', '/api/v1/insurance-pricing', $owner, [
'free_visit_price_rials' => 500_000,
'require_visit_price' => true,
]);
self::assertSame(200, $this->responseCode());
$res = $this->authJson('GET', '/api/v1/insurance-pricing', $owner);
self::assertSame(500_000, $res['data']['free_visit_price_rials']);
self::assertTrue($res['data']['require_visit_price']);
}
public function testSaveFlagAloneKeepsStoredPrice(): void
{
[$owner, $doctor] = $this->doctor();
$this->pricingRow(EntityInsurancePricing::TYPE_DOCTOR, $doctor->getId(), 300_000, false);
$this->authJson('PUT', '/api/v1/insurance-pricing', $owner, ['require_visit_price' => true]);
self::assertSame(200, $this->responseCode());
$res = $this->authJson('GET', '/api/v1/insurance-pricing', $owner);
self::assertSame(300_000, $res['data']['free_visit_price_rials']);
self::assertTrue($res['data']['require_visit_price']);
}
public function testSaveFlagAloneOnEnabledRowWithZeroStoredPriceIs422(): void
{
[$owner, $doctor] = $this->doctor();
$this->pricingRow(EntityInsurancePricing::TYPE_DOCTOR, $doctor->getId(), 0, false);
$this->authJson('PUT', '/api/v1/insurance-pricing', $owner, ['require_visit_price' => true]);
self::assertSame(422, $this->responseCode());
}
public function testSaveFlagFalseWithoutRowDoesNotCreateRow(): void
{
[$owner, $doctor] = $this->doctor();
$this->authJson('PUT', '/api/v1/insurance-pricing', $owner, ['require_visit_price' => false]);
self::assertSame(200, $this->responseCode());
$row = $this->em->getRepository(EntityInsurancePricing::class)->findOneBy([
'entityType' => EntityInsurancePricing::TYPE_DOCTOR,
'entityId' => $doctor->getId(),
'insuranceId' => null,
]);
self::assertNull($row);
}
// ── POST /api/v1/patient/{uuid}/session ─────────────────────────────────
public function testSessionWithoutVisitPriceIs422WhenFlagOn(): void
{
[$owner, $doctor] = $this->doctor();
$this->pricingRow(EntityInsurancePricing::TYPE_DOCTOR, $doctor->getId(), 500_000, true);
$record = $this->recordFor($doctor);
$res = $this->authJson('POST', '/api/v1/patient/' . $record->getUuid() . '/session', $owner, [
'visit_price_rials' => 0,
]);
self::assertSame(422, $this->responseCode());
self::assertSame(ErrorCodes::ERR_VALIDATION_001, $res['errors'][0]['code']);
}
public function testSessionWithVisitPriceIs201WhenFlagOn(): void
{
[$owner, $doctor] = $this->doctor();
$this->pricingRow(EntityInsurancePricing::TYPE_DOCTOR, $doctor->getId(), 500_000, true);
$record = $this->recordFor($doctor);
$res = $this->authJson('POST', '/api/v1/patient/' . $record->getUuid() . '/session', $owner, [
'visit_price_rials' => 400_000,
]);
self::assertSame(201, $this->responseCode());
self::assertSame(400_000, $res['data']['visit_price_rials']);
}
public function testSessionWithZeroVisitPriceStaysAllowedWhenFlagOff(): void
{
[$owner, $doctor] = $this->doctor();
$this->pricingRow(EntityInsurancePricing::TYPE_DOCTOR, $doctor->getId(), 500_000, false);
$record = $this->recordFor($doctor);
$this->authJson('POST', '/api/v1/patient/' . $record->getUuid() . '/session', $owner, [
'visit_price_rials' => 0,
]);
self::assertSame(201, $this->responseCode());
}
// ── POST /api/v1/my/appointment ─────────────────────────────────────────
public function testMyAppointmentWithoutVisitPriceIs422WhenFlagOn(): void
{
[$owner, $doctor] = $this->doctor();
$this->pricingRow(EntityInsurancePricing::TYPE_DOCTOR, $doctor->getId(), 500_000, true);
$res = $this->authJson('POST', '/api/v1/my/appointment', $owner, $this->appointmentBody($doctor->getUuid()));
self::assertSame(422, $this->responseCode());
self::assertSame('visit_price_rials', $res['errors'][0]['field']);
}
public function testMyAppointmentWithVisitPriceIs201AndStored(): void
{
[$owner, $doctor] = $this->doctor();
$this->pricingRow(EntityInsurancePricing::TYPE_DOCTOR, $doctor->getId(), 500_000, true);
$body = $this->appointmentBody($doctor->getUuid());
$body['visit_price_rials'] = 500_000;
$res = $this->authJson('POST', '/api/v1/my/appointment', $owner, $body);
self::assertSame(201, $this->responseCode());
$appointment = $this->em->getRepository(Appointment::class)->findOneBy(['uuid' => $res['data']['uuid']]);
self::assertSame(500_000, $appointment->getVisitPriceRials());
}
public function testMyAppointmentWithoutVisitPriceIs201WhenFlagOff(): void
{
[$owner, $doctor] = $this->doctor();
$this->authJson('POST', '/api/v1/my/appointment', $owner, $this->appointmentBody($doctor->getUuid()));
self::assertSame(201, $this->responseCode());
}
public function testFlagFallsBackToSingleClinicOfDoctor(): void
{
[$owner, $doctor] = $this->doctor();
$clinicOwner = $this->createUser(['ROLE_CLINIC']);
$clinic = new Clinic($clinicOwner);
$clinic->getDoctors()->add($doctor);
$this->em->persist($clinic);
$this->em->flush();
$this->pricingRow(EntityInsurancePricing::TYPE_CLINIC, $clinic->getId(), 500_000, true);
$this->authJson('POST', '/api/v1/my/appointment', $owner, $this->appointmentBody($doctor->getUuid()));
self::assertSame(422, $this->responseCode());
}
// ── POST /api/v1/admin/appointment ──────────────────────────────────────
public function testAdminAppointmentWithoutVisitPriceIs422WhenFlagOn(): void
{
[, $doctor] = $this->doctor();
$this->pricingRow(EntityInsurancePricing::TYPE_DOCTOR, $doctor->getId(), 500_000, true);
$admin = $this->createUser(['ROLE_ADMIN']);
$res = $this->authJson('POST', '/api/v1/admin/appointment', $admin, $this->appointmentBody($doctor->getUuid()));
self::assertSame(422, $this->responseCode());
self::assertSame('visit_price_rials', $res['errors'][0]['field']);
}
public function testAdminAppointmentWithVisitPriceIs201(): void
{
[, $doctor] = $this->doctor();
$this->pricingRow(EntityInsurancePricing::TYPE_DOCTOR, $doctor->getId(), 500_000, true);
$admin = $this->createUser(['ROLE_ADMIN']);
$body = $this->appointmentBody($doctor->getUuid());
$body['visit_price_rials'] = 500_000;
$res = $this->authJson('POST', '/api/v1/admin/appointment', $admin, $body);
self::assertSame(201, $this->responseCode());
$appointment = $this->em->getRepository(Appointment::class)->findOneBy(['uuid' => $res['data']['uuid']]);
self::assertSame(500_000, $appointment->getVisitPriceRials());
}
}