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
@@ -0,0 +1,83 @@
# باگ: افزودن خودکار بیمار به کلینیک هنگام نوبت با آدرس کلینیک
## پروژه
`clinicpro` (backend)
## زمینه
وقتی یک نوبت برای پزشکی که در یک کلینیک عضو است ثبت و تأیید می‌شود، اگر آن نوبت با **آدرس کلینیک** باشد، بیمار باید **به‌صورت خودکار به پرونده‌های آن کلینیک** هم اضافه شود. در حال حاضر فقط به پرونده‌ی پزشک اضافه می‌شود.
## مشکل / هدف
در `PatientService::autoCreateOnAppointmentConfirm`، entity همیشه `doctor` فرض می‌شود:
```php
public function autoCreateOnAppointmentConfirm(Appointment $appointment): void
{
$doctor = $appointment->getDoctor();
$entityType = 'doctor';
$entityId = $doctor->getId();
...
$record = $this->recordRepo->findByEntityAndUser($entityType, $entityId, $patient);
if ($record === null) {
$record = new PatientRecord($entityType, $entityId, $patient, 'system', $doctor->getId());
$this->recordRepo->save($record);
}
$session = new PatientSession($record, $appointment);
$this->sessionRepo->save($session);
}
```
کلینیک هیچ‌وقت پرونده نمی‌گیرد.
## فایل‌های مرتبط
| فایل | نقش |
|------|-----|
| `src/Patient/Service/PatientService.php` | `autoCreateOnAppointmentConfirm` |
| `src/Appointment/Entity/Appointment.php` | نوبت؛ **فعلاً آدرس/کلینیک انتخابی را ذخیره نمی‌کند** |
| `src/Appointment/Controller/AppointmentController.php` | فراخوانی auto-create در تأیید (خط ~۴۹۹) |
| `src/Doctor/Entity/DoctorAddress.php` | آدرس پزشک؛ دارای `clinicId` (`TYPE_CLINIC`, `forClinic($clinicId)`, `getClinicId()`) |
| `src/Patient/Entity/PatientRecord.php` | پرونده (polymorphic: `entityType` = `doctor|clinic`) |
| `docs/api/patient.md` + `docs/api/appointment.md` | مستندات |
## وضعیت فعلی — یافته‌ی مهم
**`Appointment` آدرس/کلینیکِ انتخاب‌شده را نگه نمی‌دارد.** فیلدهای موجود: `doctor`, `user`, `slotStart`, `slotEnd`, `status`, و فیلدهای مهمان (`patientName`, `patientMobile`, `patientNationalCode`, `patientGender`). در `toArray()` آدرس صرفاً «اولین آدرس پزشک» است:
```php
$firstAddress = $this->doctor->getAddresses()->first() ?: null;
'address' => $firstAddress?->toArray(),
```
پس برای تشخیص اینکه نوبت با کدام آدرس/کلینیک بوده، **اول باید نوبت آدرس/کلینیک را ذخیره کند**.
## وظایف
### ۱. ذخیره‌ی آدرس/کلینیک روی نوبت
- به `Appointment` فیلد `?int $addressId` یا `?int $clinicId` (یا رابطه به `DoctorAddress`) اضافه کن.
- جریان رزرو نوبت (آنجا که Appointment ساخته می‌شود) باید آدرس انتخاب‌شده‌ی کاربر را ست کند. کد رزرو را پیدا کن (`debug:router | grep appointment`، سپس controller/Service رزرو) و آدرس را propagate کن.
- migration بساز و اجرا کن.
اگر در عمل کاربر در رزرو آدرس انتخاب می‌کند، آن انتخاب را تا Appointment دنبال کن؛ اگر فعلاً انتخاب آدرس وجود ندارد، حداقل آدرس نوع `clinic` پیش‌فرض پزشک را ست کن و در پرامپت یادداشت بگذار.
### ۲. افزودن بیمار به کلینیک در تأیید
`autoCreateOnAppointmentConfirm` را طوری تغییر بده که:
1. همیشه پرونده‌ی `doctor` را بسازد (رفتار فعلی).
2. اگر نوبت آدرس کلینیک دارد (`DoctorAddress::getClinicId() !== null` برای آدرس نوبت)، یک `PatientRecord` با `entityType='clinic'`, `entityId=$clinicId` هم بساز (اگر نبود) و در صورت لزوم `PatientSession` متناظر را اضافه کن.
3. قبل از ساخت، `subscriptionService->hasFeature('clinic', $clinicId, 'patient_records')` را چک کن (مثل چک فعلی doctor).
### ۳. مستندات
اگر شکل پاسخ Appointment تغییر کرد (`address_id`/`clinic_id``docs/api/appointment.md` را به‌روز کن. رفتار auto-add را در `docs/api/patient.md` مستند کن.
## نکات مهم
- `PatientRecord` از قبل polymorphic است؛ ساخت نسخه‌ی `clinic` نیاز به schema جدید ندارد.
- duplicate را با `findByEntityAndUser` چک کن (هم برای doctor هم clinic).
- اگر چند آدرس کلینیک متفاوت در نوبت‌های مختلف باشد، هر کلینیک پرونده‌ی خودش را بگیرد.
- منبع کلینیک از `DoctorAddress.clinicId` است، نه از خود پزشک.