# کاربران تستی

**پنل ادمین:** https://clinic-pro.ddev.site/admin

> رمز عبور همهٔ پرسوناها: `QaTest@1234`

این فایل وضعیت واقعی دیتابیس لوکال پس از بازسازی کامل (drop → migrate → seed) را
توصیف می‌کند. صحتش با `node .claude/skills/qa-clinicpro/driver.mjs roles` قابل
تأیید است — اگر ردیفی `✗` گرفت، این فایل کهنه شده است.

---

## پرسوناها

واحد کار «پرسونا» است نه `ROLE_*`؛ پزشک مستقل و پزشک عضو کلینیک هر دو `ROLE_DOCTOR`
دارند ولی دادهٔ متفاوتی می‌بینند.

| پرسونا | موبایل | نقش‌ها | تمایز |
|---|---|---|---|
| `admin` | `09120671756` | `ROLE_ADMIN` | — |
| `clinic` | `09127000000` | `ROLE_CLINIC` | مالک «کلینیک تست QA» |
| `secretary` | `09123456778` | `ROLE_SECRETARY` | منشیِ `doctor_solo` |
| `doctor` | `09390039833` | `ROLE_DOCTOR` | پزشک ساده، بدون کلینیک |
| `representation` | `09124000001` | `ROLE_REPRESENTATION` | نمایندهٔ شهری |
| `doctor_solo` | `09129000001` | `ROLE_DOCTOR` | مطب شخصی، بدون کلینیک |
| `doctor_member` | `09129000002` | `ROLE_DOCTOR` | عضو «کلینیک تست QA» → موقع ورود «انتخاب محیط کاری» می‌بیند |
| `clinic_doctor` | `09129000003` | `ROLE_CLINIC` + `ROLE_DOCTOR` | چندنقشی، مالک «کلینیک تست چندنقشی» |
| `secretary_clinic` | `09129000004` | `ROLE_SECRETARY` | منشیِ `doctor_member` در کلینیک |
| `unclaimed_doctor` | `09129000005` | `ROLE_UNCLAIMED_DOCTOR` | — |
| `patient` | `09129000006` | `ROLE_USER` | کاربر عادی سایت |
| `importer` | `09129000007` | `ROLE_IMPORTER` | — |

`patient`، `unclaimed_doctor` و `importer` به پنل مدیریت دسترسی ندارند و در صفحهٔ
ورود پیام «حساب شما دسترسی به پنل مدیریت را ندارد» می‌گیرند. این باگ نیست:
`PasswordAuthenticator` هر کاربری را که `User::isStaff()` نباشد رد می‌کند.

## شناسه‌ها

| موجودیت | نام | UUID |
|---|---|---|
| پزشک `doctor_solo` | سارا مستقل | `01e2a9b4-72f4-4a48-924c-0f95bb77a994` |
| پزشک `doctor_member` | رضا عضوکلینیک | `439c9935-77bc-4f72-b73d-2432712bb6f5` |
| پزشک `clinic_doctor` | نیما چندنقشی | `e3e4c2bf-170a-479c-a385-4af7d57fcbbe` |
| پزشک `doctor` | کاوه قدیمی | `c3311b98-86b7-4d8e-8538-1390c36c2a90` |
| پروفایل تصاحب‌نشده | تصاحب نشده تست | `ded7a65d-d0fa-47e0-bc16-e801c5c75147` |
| کلینیک تست QA | — | `bcb00726-2343-4d63-90c6-d0175cc74591` |
| کلینیک تست چندنقشی | — | `e62f69a2-381b-4a6c-9235-7f9c173f3c46` |

هر سه پزشکِ `doctor_solo` / `doctor_member` / `clinic_doctor` آدرس مطب، تخصص و
برنامهٔ هفتگی (شنبه تا چهارشنبه، ۰۹:۰۰–۱۳:۰۰ و ۱۶:۰۰–۱۹:۰۰، اسلات ۲۰ دقیقه‌ای)
دارند، پس صفحات نوبت‌دهی‌شان خالی نیستند.

## دادهٔ انبوه

`app:seed-demo-data` حدود ۸٬۴۰۰ کاربر، ۱۸۰ پزشک، ۲۰۰ کلینیک، ۲۵ نماینده و ۱۵٬۰۰۰
نوبت می‌سازد — برای تست «دادهٔ زیاد» نیازی به seed اضافه نیست.

---

## بازسازی از صفر

ترتیب اجباری است — وابستگی‌ها چرخه‌ای‌اند:

```bash
ddev exec php bin/console doctrine:schema:drop --full-database --force
ddev exec php bin/console doctrine:migrations:migrate --no-interaction
ddev exec php bin/console app:create-admin 09120671756 'QaTest@1234'
# نماینده‌ها باید قبل از شهرها باشند: data/seed/cities.json به representation_id
# های ۱ تا ۳ ارجاع می‌دهد و app:seed-categories اعتبارسنجی‌شان می‌کند.
#   POST /api/v1/representation  ×۳  (با توکن ادمین)
ddev exec php bin/console app:seed-categories --no-interaction
ddev exec php bin/console app:seed-sms-message-templates --no-interaction
ddev exec php bin/console app:seed-demo-data --purge --no-interaction
```

`app:seed-demo-data` خودش بازهٔ `09124000%` را مالک است و نماینده‌های مرحلهٔ قبل را
purge و بازسازی می‌کند؛ بعد از آن `cities.representation_id` به شناسه‌های قدیمی اشاره
می‌کند و باید به شناسه‌های جدید نگاشت شود.

سپس پرسوناها از راه اندپوینت‌های خود اپ ساخته می‌شوند:
`POST /api/v1/admin/doctors` · `POST /api/v1/admin/clinic` ·
`POST /api/v1/admin/clinic/{uuid}/invite-doctor` + `POST /api/v1/doctor/invitation/{uuid}/respond` ·
`POST /api/v1/secretary` · `POST /api/v1/admin/doctors/import` ·
`send-code → verify-code → register` برای `patient`.

`ROLE_IMPORTER` و `ROLE_UNCLAIMED_DOCTOR` **هیچ مسیر اپلیکیشنی ندارند** — نگاشت نقش
در `AdminApiController::updateUserRole` فقط `admin/doctor/secretary/clinic/patient`
را می‌شناسد، پس این دو با SQL مستقیم ست می‌شوند.

## نکته‌ها

- **کپچا:** نصب تازه `ALTCHA_ENABLED=true` دارد و override پنل خالی است، پس لاگین و
  ثبت‌نام اسکریپتی رد می‌شود. از پنل ادمین یا
  `PATCH /api/v1/admin/settings {"altcha_enabled":"0"}` غیرفعالش کن.
- **کد OTP در dev همیشه `12345` است** (`OtpService::sendCode`).
- **`send-code` سقف ۵ درخواست در ساعت به‌ازای هر IP دارد.** برای تست‌های انبوه توکن را
  مستقیم بساز:
  `ddev exec 'php bin/console lexik:jwt:generate-token <mobile> --user-class="App\\Auth\\Entity\\User"'`
- **رمز پس از ریست:** `ddev exec php bin/console security:hash-password 'QaTest@1234'`
  و هش را در `users.password_hash` بگذار. کاربرانی که از راه `POST /api/v1/admin/doctors`
  یا `/api/v1/admin/clinic` ساخته می‌شوند رمز نمی‌گیرند.

## نام پزشک بدون عنوان

نام ذخیره‌شدهٔ پزشک **هرگز** پیشوند «دکتر» ندارد؛ همهٔ مسیرهای ثبت و ویرایش آن را با
`PersianText::stripDoctorTitle()` حذف می‌کنند. افزودن عنوان کار لایهٔ نمایش است —
`nobat724_front` برای عنوان صفحه و JSON-LD از `doctorTitle()` استفاده می‌کند (idempotent)،
و پنل ادمین اصلاً عنوان اضافه نمی‌کند.

پاک‌سازی دادهٔ قدیمی: `php bin/console app:doctors:fix-irimc-names --all --dry-run`
