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>
138 lines
10 KiB
Markdown
138 lines
10 KiB
Markdown
# کاربران تستی
|
||
|
||
**پنل ادمین:** https://clinic-pro.ddev.site/admin — **سایت عمومی:** `yasuj-nobat.localhost:3000`
|
||
|
||
> رمز عبور همهٔ حسابهای کارکنان: `QaTest@1234` · کد OTP در dev همیشه `12345`
|
||
|
||
کل این محیط با **یک دستور** ساخته میشود:
|
||
|
||
```bash
|
||
ddev exec php bin/console app:seed-scenarios --reset -n
|
||
```
|
||
|
||
`--reset` دیتابیس را کامل خالی میکند، migrationها را از صفر میزند، دادهٔ پایه
|
||
(نمایندهها → دستهبندیها → کاتالوگ بیمه → خاموشکردن کپچا) را میسازد و بعد سه سناریو
|
||
را میچیند. بدون `--reset` فقط سناریوها ساخته میشوند و روی دیتابیسی که قبلاً seed شده
|
||
با خطا برمیگردد.
|
||
|
||
> **UUIDها با هر اجرا عوض میشوند.** جدولهای زیر شکل داده را نشان میدهند نه شناسههای
|
||
> ثابت؛ برای گرفتن uuidهای فعلی از خود API یا دیتابیس بپرسید.
|
||
|
||
---
|
||
|
||
## سناریوی ۱ — پزشک مستقل، نوبتدهی سرویسی
|
||
|
||
| نقش | موبایل | توضیح |
|
||
|---|---|---|
|
||
| پزشک | `0912000101` | سارا مرادی — مطب شخصی، بدون کلینیک |
|
||
| منشی | `0912000109` | منشیِ همان پزشک |
|
||
| بیماران | `09120001100` … `09120001104` | ۵ بیمار با پرونده |
|
||
|
||
- **حالت نوبتدهی:** `service` · بافر ۱۰ دقیقه · شنبه تا چهارشنبه ۰۹:۰۰–۱۷:۰۰
|
||
- **سرویسها:** مشاوره پوست (۲۰د) · لیزر صورت (۳۰د، additional ۲۰) · تزریق ژل (۴۵د) ·
|
||
میکرونیدلینگ (۶۰د، additional ۴۰)
|
||
- **بیمه:** تامین اجتماعی، بیمه ایران
|
||
- **نوبتها:** ۹ نوبت در ۵ وضعیت (انجامشده، لغو کاربر، عدم حضور، تأییدشده، در انتظار پرداخت)
|
||
که دو تای آنها **امروز** است
|
||
|
||
## سناریوی ۲ — پزشکی که مالک کلینیک است
|
||
|
||
| نقش | موبایل | توضیح |
|
||
|---|---|---|
|
||
| پزشک + مالک کلینیک | `0912000201` | امیر کاظمی — مالک «کلینیک تخصصی مهر»، خودش هم در کلینیک نوبت میدهد |
|
||
| پزشک عضو (سرویسی) | `0912000202` | نگار سلطانی |
|
||
| پزشک عضو (اسلاتی) | `0912000203` | بهرام فتحی |
|
||
| پزشک عضو (اسلاتی) | `0912000204` | الهام قاسمی |
|
||
| منشی | `0912000209` | روی هر ۴ پزشک |
|
||
| بیماران | `09120001200` … `09120001207` | ۸ بیمار |
|
||
|
||
- **ترکیب حالتها:** ۲ سرویسی + ۲ اسلاتی، همه شنبه تا چهارشنبه ۱۶:۰۰–۲۱:۰۰
|
||
- **دستگاهها:** ۲ لیزر (الکساندرایت، دایود) با setup/cleanup و تقویم کاری + ۲ اتاق درمان
|
||
- **سرویسها:** لیزر کامل بدن (۹۰د) · لیزر زیربغل (۲۰د) · ویزیت پوست (۱۵د) · هیدرودرم (۴۵د)
|
||
- **وابستگی به دستگاه:** دو سرویس لیزری هرکدام یک بخش (`SegmentTemplate`) با نیازمندی
|
||
انحصاری روی نوع منبع «دستگاه لیزر» دارند
|
||
- **بیمه:** تامین اجتماعی، سلامت ایرانیان، بیمه آسیا
|
||
|
||
این حساب **دو محیط کاری** دارد (مطب شخصی + کلینیک). سرویسها و دستگاهها زیر محیط
|
||
*کلینیک* اند، پس تا وقتی محیط عوض نشود لیستشان خالی است — این درست است، نه باگ:
|
||
|
||
```bash
|
||
POST /api/v1/auth/switch-context {"db_uuid": "<clinic_uuid>"}
|
||
```
|
||
|
||
## سناریوی ۳ — کلینیک مستقل با مالکِ غیرپزشک
|
||
|
||
| نقش | موبایل | توضیح |
|
||
|---|---|---|
|
||
| مالک کلینیک | `0912000301` | رضا شریفی — **پزشک نیست**، فقط `ROLE_CLINIC` |
|
||
| پزشک (اسلاتی) | `0912000302` | پیمان اکبری |
|
||
| پزشک (سرویسی) | `0912000303` | مینا یوسفی |
|
||
| پزشک (منبعمحور) | `0912000304` | آرش نوری |
|
||
| منشی | `0912000309` | روی هر ۳ پزشک |
|
||
| بیماران | `09120001300` … `09120001307` | ۸ بیمار |
|
||
|
||
- **هر سه حالت نوبتدهی** اینجا زندهاند: `slot`، `service` و `resource`
|
||
- **ساعت کاری:** شنبه تا پنجشنبه ۰۸:۰۰–۱۴:۰۰
|
||
- **دستگاهها:** ۳ لیزر (CO2 فرکشنال، NdYAG، دایود) + دستگاه RF + دستگاه کرایو
|
||
- **سرویسها:** ویزیت عمومی (۱۵د) · لیزر CO2 (۴۰د) · کرایوتراپی (۲۵د) · RF فرکشنال (۵۰د)
|
||
- **بیمه:** تامین اجتماعی، سلامت ایرانیان، بیمه دی
|
||
|
||
مالکِ غیرپزشک عمدی است: هر مسیری که فرض کند مالکِ کلینیک یک `Doctor` دارد، باید
|
||
همینجا بشکند نه در محیط واقعی.
|
||
|
||
---
|
||
|
||
## موتور نوبتدهی جدید (تسکهای `docs/new_feture`)
|
||
|
||
هر سه محیط، علاوه بر دادههای بالا، دادهٔ زندهٔ موتور جدید هم دارند:
|
||
|
||
| تسک | چه چیزی seed میشود | کجا دیده میشود |
|
||
|---|---|---|
|
||
| ۰۱ شعبه و اتاق | `DoctorAddress` بهعنوان شعبه + منبع اتاق با تقویم | `GET /api/v1/resources` |
|
||
| ۰۲ مدل منبع | نوع منبع، دستگاه، **مهارت** (`کار با لیزر`) با سطح، **استخر منبع** با اولویت | `GET /api/v1/resources` · `/skills` |
|
||
| ۰۳ تقویم منبع | تقویم هفتگی هر دستگاه + یک **استثنای تعمیرات** هفتهٔ بعد | جستجوی آزاد آن بازه را رد میکند |
|
||
| ۰۴ کاتالوگ نسل دوم | دستهٔ درختی، **گروه انتخاب** با بازهٔ ۱ تا ۲، **ناسازگاری** دو سرویس، **override شعبه**، مدت solo/additional | `GET /api/v1/service-items` |
|
||
| ۰۵ برنامهٔ چندبخشی | سرویس اصلی هر محیط سه بخش دارد: بیحسی ۵د (اتاق انحصاری) → انتظار ۳۰د (اتاق **passive**) → لیزر ۲۰د (اتاق + دستگاه) | `GET /api/v1/service-item/{uuid}/segments` |
|
||
| ۰۶ موتور دسترسپذیری | همان برنامه با منابع واقعی جستجو میشود | `POST /api/v1/appointment-availability` |
|
||
| ۰۷ نگهداشتن و ثبت | **۲ نوبت در هر محیط از مسیر واقعی** hold → confirm، با بخش و اشغال منبع | `GET /api/v1/appointment/{uuid}/segments` |
|
||
| **منبع↔سرویس** | رابطهٔ چندبهچند با مدت و قیمت اختصاصی: دو دستگاه یک سرویس را با اعداد متفاوت میدهند | `GET /api/v1/resource/{uuid}/services` |
|
||
| **دستهٔ مشترک** | دسته روی سرویس و منبع؛ یال «شامل بودن» بین دستهها | مودال «سرویسها» در صفحهٔ منابع |
|
||
| ۰۸ عکس قیمت | لیست قیمت فعال + `PriceSnapshot` روی نوبتهای واقعی با بیعانه | `GET /api/v1/price-lists` |
|
||
|
||
> **تسکهای ۹ تا ۱۴ (سیاست، پکیج، دوره، لغو/انتظار، رویداد و گزارش) به تصمیم مالک محصول
|
||
> از محصول حذف شدهاند.** مدل فعلی فقط منبع، سرویس و گزینه است —
|
||
> [`docs/architecture/resource-first-model.md`](docs/architecture/resource-first-model.md).
|
||
|
||
نوبتهای موتور جدید **از `new Appointment(...)` ساخته نمیشوند**؛ از
|
||
`AppointmentPlanBuilder` → `AvailabilityEngine` → `HoldService` → `BookingService` رد
|
||
میشوند. برای همین اشغال منبع واقعی است و تقویم دستگاه واقعاً پر است — نه ردیفهایی که
|
||
با INSERT ساخته شدهاند و هیچوقت از موتور رد نشدهاند.
|
||
|
||
## چه چیزی با این داده قابل تست است
|
||
|
||
| قابلیت | کجا |
|
||
|---|---|
|
||
| نوبتدهی اسلاتی | `0912000203` · `0912000204` · `0912000302` |
|
||
| نوبتدهی سرویسی | `0912000101` · `0912000201` · `0912000202` · `0912000303` |
|
||
| نوبتدهی منبعمحور | `0912000304` (با تخصیص واقعی دستگاه) |
|
||
| مطب شخصی در برابر کلینیک | سناریوی ۲ (یک کاربر، دو محیط) |
|
||
| مالک پزشک در برابر مالک اداری | سناریوی ۲ در برابر سناریوی ۳ |
|
||
| دستگاه و وابستگی سرویس به دستگاه | سناریوی ۲ و ۳ |
|
||
| بیمه (پایه و تکمیلی، فرانشیز، سقف) | هر سه محیط |
|
||
| پرداخت | ۳۲ پرداخت موفق روی نوبتهای پرداختشده |
|
||
| پرونده بیمار | ۲۱ پرونده با کد ملی روی پروفایل |
|
||
|
||
## نکتهها
|
||
|
||
- **کپچا:** روی دیتابیس تازه پیشفرضش **روشن** است و هیچکس نمیتواند وارد شود.
|
||
سیدر ردیف `site_config.altcha_enabled = '0'` را میگذارد تا محیط قابل ورود بماند.
|
||
- **لاگین با رمز فقط برای کارکنان است.** فیلد درخواست `mobile_number` است نه `mobile`:
|
||
`POST /api/v1/user/login {"mobile_number": "...", "password": "..."}`.
|
||
بیماران (`ROLE_USER`) عمداً فقط با OTP وارد میشوند
|
||
(`send-code` → `verify-code` با `12345` → `otp-login`).
|
||
- **`send-code` سقف ۵ درخواست در ساعت بهازای هر IP دارد.**
|
||
- **نام پزشک بدون عنوان ذخیره میشود** — «دکتر» را لایهٔ نمایش اضافه میکند
|
||
(`PersianText::stripDoctorTitle`).
|
||
- **دادهٔ انبوه** جداست: `app:seed-demo-data` حدود ۵۰۰ پزشک و ۲۰هزار نوبت با INSERT خام
|
||
میسازد و برای تست کارایی و ماژول نمایندگان است، نه برای تست سناریویی.
|