Files
clinicpro/tests/Billing/InvoiceShowSessionTest.php
T

238 lines
9.2 KiB
PHP
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<?php
namespace App\Tests\Billing;
use App\Auth\Entity\User;
use App\Billing\Entity\Invoice;
use App\Doctor\Entity\Doctor;
use App\Inventory\Entity\InventoryItem;
use App\Patient\Entity\PatientRecord;
use App\Patient\Entity\PatientSession;
use App\Patient\Entity\SessionConsumable;
use App\Patient\Entity\SessionPayment;
use App\Tests\ApiTestCase;
/**
* GET /api/v1/billing/invoices/{uuid} — invoice detail enriched with its
* source session (`session` key: payments, consumables, discount, paid totals).
*/
class InvoiceShowSessionTest extends ApiTestCase
{
/** @return array{0: User, 1: Doctor} owner user + their doctor profile */
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 patientRecord(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 setField(object $obj, string $prop, mixed $value): void
{
$ref = new \ReflectionProperty($obj, $prop);
$ref->setAccessible(true);
$ref->setValue($obj, $value);
}
public function testShowIncludesSessionPaymentsConsumablesAndDiscount(): void
{
[$owner, $doctor] = $this->doctor();
$record = $this->patientRecord($doctor);
$session = new PatientSession($record);
$session->setServicesTotalRials(2_400_000)
->setFinalPriceRials(2_200_000)
->setDiscount('amount', 200_000, 200_000)
->setSessionAt(1_700_000_000)
->setPaidAt(1_700_100_000);
$this->em->persist($session);
$this->em->flush();
$payment = new SessionPayment($session, 'wallet', 1_500_000, 1_700_100_000);
$payment->setCreatedByName('منشی تست');
$this->em->persist($payment);
$session->addPayment($payment);
$item = new InventoryItem('doctor', $doctor->getId(), 'عینک');
$item->setPrice(20_000);
$this->em->persist($item);
$this->em->flush();
$consumable = new SessionConsumable($session, $item, 2);
$this->em->persist($consumable);
$session->addConsumable($consumable);
$this->em->flush();
$invoice = new Invoice('doctor', $doctor->getId());
$invoice->setPatientSessionId($session->getId())
->setPatientRecordId($record->getId());
$this->em->persist($invoice);
$this->em->flush();
$res = $this->authJson('GET', '/api/v1/billing/invoices/' . $invoice->getUuid(), $owner);
self::assertSame(200, $this->responseCode());
$data = $res['data']['data'];
self::assertSame($invoice->getUuid(), $data['uuid']);
self::assertIsArray($data['session']);
$s = $data['session'];
self::assertSame($session->getUuid(), $s['uuid']);
self::assertSame(1_700_000_000, $s['session_at']);
self::assertSame(1_700_100_000, $s['paid_at']);
self::assertSame(2_400_000, $s['services_total_rials']);
self::assertSame(2_200_000, $s['final_price_rials']);
self::assertSame(200_000, $s['discount_rials']);
// payments — real rows, not heuristics
self::assertCount(1, $s['payments']);
self::assertSame('wallet', $s['payments'][0]['method']);
self::assertSame(1_500_000, $s['payments'][0]['amount_rials']);
self::assertSame('منشی تست', $s['payments'][0]['created_by_name']);
self::assertSame(1_500_000, $s['paid_total_rials']);
// consumables — snapshot price × quantity
self::assertCount(1, $s['consumables']);
self::assertSame('عینک', $s['consumables'][0]['item_name']);
self::assertSame(2, $s['consumables'][0]['quantity']);
self::assertSame(40_000, $s['consumables'][0]['line_total_rials']);
self::assertSame(40_000, $s['consumables_total_rials']);
}
/**
* ستون «ثبت‌کننده» باید نامِ زندهٔ کاربر و شناسه‌های لینکش را بدهد؛ عکسِ
* `created_by_name` ممکن است شمارهٔ موبایلِ زمانی باشد که کاربر پروفایل نداشته.
*/
public function testShowResolvesRecorderIdentityFromUser(): void
{
[$owner, $doctor] = $this->doctor();
$record = $this->patientRecord($doctor);
$recorderDoctor = new Doctor($this->createUser(['ROLE_DOCTOR']), 'دکتر ثبت‌کننده');
$this->em->persist($recorderDoctor);
$recorder = $recorderDoctor->getUser();
$recorder->setRealName('دکتر ثبت‌کننده');
$session = new PatientSession($record);
$this->em->persist($session);
$this->em->flush();
$payment = new SessionPayment($session, 'cash', 500_000, 1_700_100_000);
// عکسِ کهنه: همان چیزی که هنگام ثبت ذخیره شده بود.
$payment->setCreatedByName($recorder->getMobileNumber())->setCreatedBy($recorder);
$this->em->persist($payment);
$session->addPayment($payment);
$invoice = new Invoice('doctor', $doctor->getId());
$invoice->setPatientSessionId($session->getId());
$this->em->persist($invoice);
$this->em->flush();
$res = $this->authJson('GET', '/api/v1/billing/invoices/' . $invoice->getUuid(), $owner);
self::assertSame(200, $this->responseCode());
$row = $res['data']['data']['session']['payments'][0];
self::assertSame('دکتر ثبت‌کننده', $row['created_by_name']);
self::assertSame($recorder->getUuid(), $row['created_by']['user_uuid']);
self::assertSame('doctor', $row['created_by']['role']);
self::assertSame($recorderDoctor->getUuid(), $row['created_by']['doctor_uuid']);
}
/** کاربرِ حذف‌شده: هویتی نیست، پس همان عکسِ ذخیره‌شده می‌ماند. */
public function testShowKeepsSnapshotNameWhenRecorderUnknown(): void
{
[$owner, $doctor] = $this->doctor();
$record = $this->patientRecord($doctor);
$session = new PatientSession($record);
$this->em->persist($session);
$this->em->flush();
$payment = new SessionPayment($session, 'cash', 100_000, 1_700_100_000);
$payment->setCreatedByName('منشی قدیمی');
$this->em->persist($payment);
$session->addPayment($payment);
$invoice = new Invoice('doctor', $doctor->getId());
$invoice->setPatientSessionId($session->getId());
$this->em->persist($invoice);
$this->em->flush();
$res = $this->authJson('GET', '/api/v1/billing/invoices/' . $invoice->getUuid(), $owner);
$row = $res['data']['data']['session']['payments'][0];
self::assertSame('منشی قدیمی', $row['created_by_name']);
self::assertNull($row['created_by']);
}
public function testShowSessionNullWhenInvoiceHasNoSession(): void
{
[$owner, $doctor] = $this->doctor();
$record = $this->patientRecord($doctor);
$invoice = new Invoice('doctor', $doctor->getId());
$invoice->setPatientRecordId($record->getId());
$this->em->persist($invoice);
$this->em->flush();
$res = $this->authJson('GET', '/api/v1/billing/invoices/' . $invoice->getUuid(), $owner);
self::assertSame(200, $this->responseCode());
self::assertNull($res['data']['data']['session']);
}
public function testShowNotFoundForUnknownUuid(): void
{
[$owner] = $this->doctor();
$this->authJson('GET', '/api/v1/billing/invoices/00000000-0000-0000-0000-000000000000', $owner);
self::assertSame(404, $this->responseCode());
}
public function testShowNotFoundForOtherTenant(): void
{
[, $doctor] = $this->doctor();
$invoice = new Invoice('doctor', $doctor->getId());
$this->em->persist($invoice);
$this->em->flush();
[$other] = $this->doctor();
$this->authJson('GET', '/api/v1/billing/invoices/' . $invoice->getUuid(), $other);
self::assertSame(404, $this->responseCode());
}
public function testShowSessionEmptyPaymentsAndConsumables(): void
{
[$owner, $doctor] = $this->doctor();
$record = $this->patientRecord($doctor);
$session = new PatientSession($record);
$this->em->persist($session);
$this->em->flush();
$invoice = new Invoice('doctor', $doctor->getId());
$invoice->setPatientSessionId($session->getId());
$this->em->persist($invoice);
$this->em->flush();
$res = $this->authJson('GET', '/api/v1/billing/invoices/' . $invoice->getUuid(), $owner);
self::assertSame(200, $this->responseCode());
$s = $res['data']['data']['session'];
self::assertSame([], $s['payments']);
self::assertSame([], $s['consumables']);
self::assertSame(0, $s['paid_total_rials']);
self::assertSame(0, $s['discount_rials']);
}
}