260 lines
10 KiB
PHP
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());
|
|
}
|
|
}
|