Document the resource-first model and retire the deleted tasks' checklists

docs/architecture/resource-first-model.md describes the shape: the three
entities, why an option is a ServiceItem rather than a fourth table, the
four-level resolution chain, the two conditions on the eligibility filter and
what each of them prevented, and why containment is a graph beside the display
tree rather than the tree itself.

docs/api/resource.md gains both offering endpoints with the response captured
from a real call, including a row where the price comes from the branch and one
where it comes from the resource — the two cases the *_source fields exist for.
docs/api/appointment.md documents resource_uuid, the doctor inference, and the
nullable resource/service_option in the response.

The checklists for tasks 9 to 14 keep their rows but open with a banner saying
the task was removed, when, by whose decision, and which commit to revert. They
are history now; deleting them would erase the record of work that shipped and
was then withdrawn.

Verified end to end: 1304 tests, slot-mode-frozen green, phpstan at 14, tsc
clean, 648 panel tests, and app:seed-scenarios --reset builds all three
environments.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
hamed
2026-08-01 23:05:33 +03:30
co-authored by Claude Opus 5
parent 26425bec31
commit 1972fdd20f
11 changed files with 300 additions and 19 deletions
+8
View File
@@ -250,6 +250,7 @@ Book an appointment slot.
| `slot_start` | integer | ✅ | Slot start (Unix timestamp) |
| `slot_end` | integer | ⚠️ | Slot end (Unix timestamp). **با `service_item_uuids` نادیده گرفته می‌شود** و سرور خودش حساب می‌کند (به مقدار کلاینت اعتماد نمی‌شود)؛ در آن حالت الزامی هم نیست. بدون سرویس، مقدار کلاینت حفظ می‌شود و الزامی است |
| `service_item_uuids` | string[] | ❌ | یک یا چند UUID سرویس. سرویس‌ها **ذخیره** می‌شوند (`service_items`)، اولین سرویس سرویسِ اصلی (`service_item`) است، و مدت/بافر روی نوبت ثبت می‌شود (`service_total_minutes` / `service_buffer_minutes`). UUID ناموجود، سرویسِ غیرbookable، سرویس بدون مدت، یا سرویسِ محیطی دیگر ⇒ `422` |
| `resource_uuid` | string (UUID) | ⚠️ | منبعی که نوبت **برایش** گرفته می‌شود (دستگاه، اتاق، یا خودِ پزشک). اگر داده شود `doctor_uuid` اختیاری است و برای منبعِ پزشک از خودش استنتاج می‌شود؛ محل نوبت هم از شعبهٔ همان منبع می‌آید. منبع باید در همان محیط رزرو باشد و اگر سرویس انتخاب‌شده را ارائه ندهد ⇒ `422` |
| `for_self` | boolean | ❌ | `true` (default) = patient is the logged-in payer; `false` = booking for someone else |
| `patient_name` | string | ⚠️ | Required when `for_self=false`; otherwise filled from the payer's profile |
| `patient_mobile` | string | ⚠️ | Required when `for_self=false`; otherwise the payer's mobile |
@@ -259,6 +260,13 @@ Book an appointment slot.
| `note` | string | ❌ | Patient note |
| `city_id` | integer | ❌ | شناسه‌ی شهرِ دامنه‌ی جاری (از `city.json` سایت). برای گاردِ پورسانت نماینده: اگر شهر نماینده‌ی فعال داشته باشد، `booking_representation_id` نوبت ست می‌شود. پورسانت فقط وقتی واریز می‌شود که این نماینده با نماینده‌ی پزشک یکی باشد. خالی/ناموجود ⇒ بدون پورسانت |
> **`doctor_uuid` یا `resource_uuid`:** دست‌کم یکی الزامی است؛ نبودِ هر دو ⇒ `422`. مسیر
> قدیمیِ فقط-`doctor_uuid` دست‌نخورده است و سایت عمومی همان را می‌فرستد.
>
> **پاسخ:** علاوه بر فیلدهای قبلی، `resource` (`uuid`, `name`, `type`) و `service_option`
> (`uuid`, `name`) برمی‌گردند. نوبت‌های پیش از مدل منبع‌محور هر دو را `null` دارند، پس
> کلاینت باید با `null` کنار بیاید.
>
> **مدت در حالت سرویسی:** مدت از `ServiceBookingCalculator` می‌آید — همان مؤلفه‌ای که
> `GET /api/v1/appointment-service-slots` هم با آن اسلات‌ها را می‌سازد. یعنی `solo` و
> `additional` سرویس‌ها لحاظ می‌شوند و نه جمعِ سادهٔ `duration_minutes`؛ وگرنه نوبتِ ثبت‌شده