fix: store appointment address from schedule and auto-add patient to clinic

Appointments now persist address_id resolved from the weekly-schedule
session (location_id) across all booking paths (online, secretary, admin).
On confirm, the patient is added to the clinic owning that address, or to
the doctor's single clinic as fallback. Weekly-schedule create/update now
requires location_id on every active session. PatientSession exposes
doctor_uuid/doctor_name so clinic records show which doctor each visit is for.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
hamed
2026-06-23 16:10:36 +03:30
co-authored by Claude Opus 4.8
parent 51d7e24e04
commit af881231d0
13 changed files with 223 additions and 5 deletions
@@ -8,6 +8,7 @@ use App\Appointment\Repository\AppointmentRepository;
use App\Appointment\Repository\SlotTakenException;
use App\Appointment\Repository\WeeklyScheduleRepository;
use App\Appointment\Service\SlotCalculatorService;
use App\Doctor\Entity\Doctor;
use App\Auth\Entity\User;
use App\Doctor\Repository\DoctorRepository;
use App\Patient\Service\PatientService;
@@ -236,6 +237,12 @@ class AppointmentController extends BaseController
$appointment = new Appointment($doctor, $user, $slotStart, $slotEnd);
if (isset($data['note'])) $appointment->setNote($data['note']);
// آدرس نوبت از روی session متناظر در برنامه‌ی هفتگی تعیین می‌شود (location_id).
$locationId = $this->resolveSlotLocationId($doctor, $slotStart);
if ($locationId !== null) {
$appointment->setAddressId($locationId);
}
if ($forSelf) {
$appointment->setPatientName($user->getRealName());
$appointment->setPatientMobile($user->getMobileNumber());
@@ -423,6 +430,11 @@ class AppointmentController extends BaseController
|| $user->hasRole('ROLE_ADMIN');
}
private function resolveSlotLocationId(Doctor $doctor, int $slotStart): ?int
{
return $this->slotCalculator->resolveSlotLocationId($doctor, $slotStart);
}
#[OA\Patch(
path: '/api/v1/appointment/{uuid}/status',
summary: 'Update the status of an appointment',
@@ -53,6 +53,10 @@ class AppointmentSettingsController extends BaseController
return $this->error(ErrorCodes::ERR_AUTH_006, 'دسترسی ممنوع', 403);
}
if (($err = $this->validateSessionsHaveLocation($data['schedule'] ?? [])) !== null) {
return $this->error(ErrorCodes::ERR_VALIDATION_001, $err, 422);
}
// Only one schedule per doctor — upsert
$schedule = $this->scheduleRepo->findByDoctor($doctor);
if ($schedule !== null) {
@@ -90,6 +94,9 @@ class AppointmentSettingsController extends BaseController
$data = json_decode($request->getContent(), true) ?? [];
if (isset($data['schedule'])) {
if (($err = $this->validateSessionsHaveLocation($data['schedule'])) !== null) {
return $this->error(ErrorCodes::ERR_VALIDATION_001, $err, 422);
}
$schedule->setSetting($data['schedule']);
}
if (isset($data['meta']) && is_array($data['meta'])) {
@@ -356,4 +363,20 @@ class AppointmentSettingsController extends BaseController
return $this->success(['data' => $result]);
}
/**
* هر session فعال در برنامه‌ی هفتگی باید آدرس (location_id) داشته باشد.
* در صورت نقص، پیام خطا برمی‌گرداند؛ در غیر این صورت null.
*/
private function validateSessionsHaveLocation(array $schedule): ?string
{
foreach ($schedule as $day) {
foreach (($day['sessions'] ?? []) as $session) {
if (($session['active'] ?? false) && empty($session['location_id'])) {
return 'برای هر شیفت فعال باید آدرس (مطب/کلینیک) انتخاب شود';
}
}
}
return null;
}
}
@@ -3,6 +3,7 @@
namespace App\Appointment\Controller;
use App\Appointment\Entity\Appointment;
use App\Appointment\Service\SlotCalculatorService;
use App\Auth\Entity\User;
use App\Auth\Repository\UserActiveContextRepository;
use App\Clinic\Repository\ClinicRepository;
@@ -27,6 +28,7 @@ class MyAppointmentsController extends BaseController
private readonly ClinicRepository $clinicRepo,
private readonly DoctorSecretaryRepository $secretaryRepo,
private readonly UserActiveContextRepository $contextRepo,
private readonly SlotCalculatorService $slotCalculator,
) {}
#[Route('/api/v1/my/appointment', methods: ['POST'])]
@@ -79,6 +81,8 @@ class MyAppointmentsController extends BaseController
$appointment = new Appointment($doctor, $patient, $slotStart, $slotEnd);
if (!empty($data['note'])) $appointment->setNote($data['note']);
$locationId = $this->slotCalculator->resolveSlotLocationId($doctor, $slotStart);
if ($locationId !== null) $appointment->setAddressId($locationId);
$this->em->persist($appointment);
$this->em->flush();