paymentRepo->findSuccessfulByAppointment($appointment); if ($payment === null) { return; } $doctor = $appointment->getDoctor(); $this->commissionService->processAppointment( $payment, $doctor->getRepresentationId(), $this->domainResolver->resolve($payment->getFrontendAddress())->representationId(), $doctor->getId(), ); } /** * idempotent: فراخوانی دوباره برای همان نوبت چیزی نمی‌سازد. * * شکست ساخت پرونده نباید قطعی‌شدن نوبت یا تأیید پرداخت را برگرداند — نوبت * رزرو شده و پول پرداخت شده است؛ پرونده را می‌شود با * `app:appointment:backfill-sessions` ساخت، ولی رول‌بکِ پرداخت برگشت‌ناپذیر است. */ public function onConfirmed(Appointment $appointment): ?PatientSession { // نوبت رزروِ روز-محور اسلات و ساعت مشخص ندارد؛ مراجعهٔ زمان‌دار برایش معنا ندارد. if ($appointment->isReserve()) { return null; } $this->splitPaymentShares($appointment); try { $session = $this->patientService->autoCreateOnAppointmentConfirm($appointment); // هسته نمی‌داند دورهٔ درمان چیست: اگر سرویسِ نوبت پروتکل داشته باشد، // workflowِ حوزهٔ فعالیت تصمیم می‌گیرد چه اتفاقی بیفتد. if ($session !== null) { $this->treatmentCases->onAppointmentConfirmed($appointment, $session); } return $session; } catch (\Throwable $e) { $this->logger->error('Auto-creating the patient record on confirm failed', [ 'appointment_uuid' => $appointment->getUuid(), 'exception' => $e, ]); return null; } } /** * قطعی‌کردنِ صریح از پنل: انتقال وضعیت، ثبت پرونده/مراجعه و ثبت پرداخت‌ها — همه * در یک تراکنش. برخلاف onConfirmed اینجا شکست خاموش نمی‌ماند: کاربر روبه‌روی * مودالی ایستاده که مبلغ نشان داده و منتظر تأیید است؛ «قطعی شد ولی پول ثبت نشد» * بدترین خروجیِ ممکن است. * * @param array $payments * @return PatientSession|null null یعنی این tenant قابلیت پرونده را ندارد * (فقط وقتی مجاز است که پرداختی هم ارسال نشده باشد) */ public function confirmWithPayments( Appointment $appointment, int $expectedVersion, array $payments, User $actor, ): ?PatientSession { return $this->em->wrapInTransaction(function () use ($appointment, $expectedVersion, $payments, $actor) { $appointment->transitionTo(Appointment::STATUS_CONFIRMED); $this->appointmentRepo->saveWithLock($appointment, $expectedVersion); $this->splitPaymentShares($appointment); $session = $this->patientService->autoCreateOnAppointmentConfirm($appointment); if ($session === null) { if ($payments !== []) { throw new AppException(ErrorCodes::ERR_SUBSCRIPTION_REQUIRED, null, 403); } return null; } foreach ($payments as $payment) { $this->patientService->addSessionPayment( $session, $payment['method'], $payment['amount_rials'], null, $actor, $payment['payment_method_uuid'] ?? null, $payment['reference'] ?? null, ); } // همان کاری که onConfirmed می‌کند. نبودنش یعنی نوبتی که از پنل و با // پرداخت قطعی شده — یعنی مسیر عادیِ منشی — هرگز پروندهٔ درمان نمی‌گیرد و // جلسه‌ای هم ساخته نمی‌شود که در پنل پرسنل دیده شود. // starter خودش خطا را می‌گیرد و لاگ می‌کند، پس تراکنش پرداخت را نمی‌شکند. $this->treatmentCases->onAppointmentConfirmed($appointment, $session); return $session; }); } }