148 lines
6.0 KiB
PHP
148 lines
6.0 KiB
PHP
<?php
|
|
|
|
namespace App\Tests\Billing;
|
|
|
|
use App\Appointment\Entity\Appointment;
|
|
use App\Billing\Entity\Invoice;
|
|
use App\Doctor\Entity\Doctor;
|
|
use App\Tests\ApiTestCase;
|
|
|
|
/**
|
|
* نوبتی که از پنل قطعی و پولش ثبت میشود باید در «پرداختها» دیده شود.
|
|
*
|
|
* فهرست پرداختها صورتحسابمحور است و صورتحساب فقط برای مراجعهٔ بیمهدار ساخته
|
|
* میشد؛ نتیجهاش این بود که پرداختِ نوبتِ بدون بیمه هیچجا دیده نمیشد.
|
|
*/
|
|
class ConfirmedAppointmentAppearsInPaymentsTest extends ApiTestCase
|
|
{
|
|
private function makeDoctor(): Doctor
|
|
{
|
|
$user = $this->createUser(['ROLE_USER', 'ROLE_DOCTOR']);
|
|
$doctor = new Doctor($user, 'دکتر تست');
|
|
$doctor->setMobileNumber($user->getMobileNumber());
|
|
$this->em->persist($doctor);
|
|
$this->em->flush();
|
|
|
|
return $doctor;
|
|
}
|
|
|
|
private function makeAppointment(Doctor $doctor, int $visitPriceRials = 5_000_000): Appointment
|
|
{
|
|
$start = strtotime('+30 days') + random_int(0, 500_000) * 7;
|
|
|
|
$appointment = $this->newAppointment($doctor, $this->createUser(), $start, $start + 900);
|
|
$appointment->setVisitPriceRials($visitPriceRials);
|
|
$this->em->persist($appointment);
|
|
$this->em->flush();
|
|
|
|
return $appointment;
|
|
}
|
|
|
|
/** @return array<int, array<string, mixed>> ردیفهای فهرست پرداختهای همان پزشک */
|
|
private function payments(Doctor $doctor): array
|
|
{
|
|
$body = $this->authJson('GET', '/api/v1/my/billing/payments?limit=100', $doctor->getUser());
|
|
self::assertSame(200, $this->responseCode());
|
|
|
|
return $body['data'] ?? [];
|
|
}
|
|
|
|
public function testPartialPaymentOnConfirmShowsUpInThePaymentsList(): void
|
|
{
|
|
$doctor = $this->makeDoctor();
|
|
$appointment = $this->makeAppointment($doctor);
|
|
|
|
self::assertSame([], $this->payments($doctor));
|
|
|
|
$this->authJson('POST', "/api/v1/appointment/{$appointment->getUuid()}/confirm", $doctor->getUser(), [
|
|
'version' => $appointment->getVersion(),
|
|
'payments' => [['method' => 'cash', 'amount_rials' => 1_000_000]],
|
|
]);
|
|
self::assertSame(200, $this->responseCode());
|
|
|
|
$rows = $this->payments($doctor);
|
|
self::assertCount(1, $rows);
|
|
self::assertSame(5_000_000, $rows[0]['amount_rials']);
|
|
self::assertSame(1_000_000, $rows[0]['paid_rials']);
|
|
self::assertSame('partial', $rows[0]['status']);
|
|
}
|
|
|
|
public function testFullPaymentOnConfirmIsListedAsPaid(): void
|
|
{
|
|
$doctor = $this->makeDoctor();
|
|
$appointment = $this->makeAppointment($doctor);
|
|
|
|
$this->authJson('POST', "/api/v1/appointment/{$appointment->getUuid()}/confirm", $doctor->getUser(), [
|
|
'version' => $appointment->getVersion(),
|
|
'payments' => [['method' => 'pos', 'amount_rials' => 5_000_000]],
|
|
]);
|
|
|
|
$rows = $this->payments($doctor);
|
|
self::assertCount(1, $rows);
|
|
self::assertSame('paid', $rows[0]['status']);
|
|
}
|
|
|
|
/** صورتحساب باید نهایی باشد، وگرنه فهرست پرداختها آن را فیلتر میکند. */
|
|
public function testTheGeneratedInvoiceIsFinalized(): void
|
|
{
|
|
$doctor = $this->makeDoctor();
|
|
$appointment = $this->makeAppointment($doctor);
|
|
|
|
$res = $this->authJson('POST', "/api/v1/appointment/{$appointment->getUuid()}/confirm", $doctor->getUser(), [
|
|
'version' => $appointment->getVersion(),
|
|
'payments' => [['method' => 'cash', 'amount_rials' => 2_000_000]],
|
|
]);
|
|
|
|
$this->em->clear();
|
|
$invoices = $this->em->getRepository(Invoice::class)->findBy(['entityId' => $doctor->getId(), 'entityType' => 'doctor']);
|
|
self::assertCount(1, $invoices);
|
|
self::assertSame(Invoice::STATUS_FINALIZED, $invoices[0]->getStatus());
|
|
self::assertSame($res['data']['session']['uuid'] !== null, true);
|
|
}
|
|
|
|
/** مرزی: قطعیکردن بدون پرداخت سندی نمیسازد — همان رفتار قبلی. */
|
|
public function testConfirmWithoutPaymentCreatesNoInvoice(): void
|
|
{
|
|
$doctor = $this->makeDoctor();
|
|
$appointment = $this->makeAppointment($doctor);
|
|
|
|
$this->authJson('POST', "/api/v1/appointment/{$appointment->getUuid()}/confirm", $doctor->getUser(), [
|
|
'version' => $appointment->getVersion(),
|
|
]);
|
|
self::assertSame(200, $this->responseCode());
|
|
|
|
self::assertSame([], $this->payments($doctor));
|
|
}
|
|
|
|
/** خطا: پرداخت بیشتر از مبلغ نوبت رد میشود و صورتحسابی هم نمیماند. */
|
|
public function testOverpaymentIsRejectedAndLeavesNoInvoice(): void
|
|
{
|
|
$doctor = $this->makeDoctor();
|
|
$appointment = $this->makeAppointment($doctor);
|
|
|
|
$this->authJson('POST', "/api/v1/appointment/{$appointment->getUuid()}/confirm", $doctor->getUser(), [
|
|
'version' => $appointment->getVersion(),
|
|
'payments' => [['method' => 'cash', 'amount_rials' => 9_000_000]],
|
|
]);
|
|
|
|
self::assertSame(422, $this->responseCode());
|
|
self::assertSame([], $this->payments($doctor));
|
|
}
|
|
|
|
/** پرداختهای یک پزشک نباید در فهرست پزشک دیگر بیاید. */
|
|
public function testPaymentsStayInsideTheirTenant(): void
|
|
{
|
|
$doctor = $this->makeDoctor();
|
|
$other = $this->makeDoctor();
|
|
$appointment = $this->makeAppointment($doctor);
|
|
|
|
$this->authJson('POST', "/api/v1/appointment/{$appointment->getUuid()}/confirm", $doctor->getUser(), [
|
|
'version' => $appointment->getVersion(),
|
|
'payments' => [['method' => 'cash', 'amount_rials' => 1_000_000]],
|
|
]);
|
|
|
|
self::assertCount(1, $this->payments($doctor));
|
|
self::assertSame([], $this->payments($other));
|
|
}
|
|
}
|