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> ردیف‌های فهرست پرداخت‌های همان پزشک */ 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)); } }