Files
clinicpro/TEST_USERS.md
T
hamedandClaude Opus 5 fc6b865c15 feat(seed): make the seeded environments exercise the new booking engine
The first version of the seeder only filled the old skeleton: doctors, clinics,
services, appointments written straight to the table. None of the sixteen tasks
under docs/new_feture had any data, so nothing they built could be tried.

BookingEngineSeeder now seeds, per environment:

- catalog v2: a category tree, an item group with a 1..2 selection range, an
  incompatible pair, and a per-branch price override
- resources: a skill with levels, a resource pool with priorities, and a
  maintenance window next week
- a three-segment plan on the flagship service — numbing (room exclusive), wait
  (room passive, nobody else held), laser (room + device) — which is the whole
  point of the plan model and cannot be seen with single-segment services
- an active price list, and a price snapshot per booked appointment
- six policies, one per category, each with a condition and an effect
- a package with a consumed session in the ledger, and a treatment protocol with
  per-session parameters plus an active course carrying its six sessions
- a general and a per-service cancellation policy, no-show records, waitlist
  entries

Two appointments per environment are booked through the real path —
AppointmentPlanBuilder, AvailabilityEngine, HoldService, BookingService — so
segments, resource occupancy and the domain-event outbox are populated by the
code that will run in production rather than by INSERTs.

Two defects in the seeding surfaced and are fixed here:

- SegmentRequirement is the owning side, so persisting one leaves the template's
  in-memory collection empty. The plan built later in the same process saw
  segments that needed nothing, and the bookings occupied the device but never
  the room — while the same service was correct over HTTP, where the entity is
  read fresh. The collection is now kept in step.
- passing the service as its own selected item produced a different plan than
  the booking flow builds.

A course with no sessions also reported "everything is scheduled"; sessions are
now created from the protocol steps.

Verified over HTTP: segments come back as 5/30/20, utilization reports 110
minutes on the room and 90 on the laser, the course suggests session 1 with
three slots, and the six policies list one per category.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-01 19:28:26 +03:30

10 KiB
Raw Blame History

کاربران تستی

پنل ادمین: https://clinic-pro.ddev.site/adminسایت عمومی: yasuj-nobat.localhost:3000

رمز عبور همهٔ حساب‌های کارکنان: QaTest@1234 · کد OTP در dev همیشه 12345

کل این محیط با یک دستور ساخته می‌شود:

ddev exec php bin/console app:seed-scenarios --reset -n

--reset دیتابیس را کامل خالی می‌کند، migrationها را از صفر می‌زند، دادهٔ پایه (نماینده‌ها → دسته‌بندی‌ها → کاتالوگ بیمه → خاموش‌کردن کپچا) را می‌سازد و بعد سه سناریو را می‌چیند. بدون --reset فقط سناریوها ساخته می‌شوند و روی دیتابیسی که قبلاً seed شده با خطا برمی‌گردد.

UUIDها با هر اجرا عوض می‌شوند. جدول‌های زیر شکل داده را نشان می‌دهند نه شناسه‌های ثابت؛ برای گرفتن uuidهای فعلی از خود API یا دیتابیس بپرسید.


سناریوی ۱ — پزشک مستقل، نوبت‌دهی سرویسی

نقش موبایل توضیح
پزشک 0912000101 سارا مرادی — مطب شخصی، بدون کلینیک
منشی 0912000109 منشیِ همان پزشک
بیماران 0912000110009120001104 ۵ بیمار با پرونده
  • حالت نوبت‌دهی: service · بافر ۱۰ دقیقه · شنبه تا چهارشنبه ۰۹:۰۰–۱۷:۰۰
  • سرویس‌ها: مشاوره پوست (۲۰د) · لیزر صورت (۳۰د، additional ۲۰) · تزریق ژل (۴۵د) · میکرونیدلینگ (۶۰د، additional ۴۰)
  • بیمه: تامین اجتماعی، بیمه ایران
  • نوبت‌ها: ۹ نوبت در ۵ وضعیت (انجام‌شده، لغو کاربر، عدم حضور، تأییدشده، در انتظار پرداخت) که دو تای آن‌ها امروز است

سناریوی ۲ — پزشکی که مالک کلینیک است

نقش موبایل توضیح
پزشک + مالک کلینیک 0912000201 امیر کاظمی — مالک «کلینیک تخصصی مهر»، خودش هم در کلینیک نوبت می‌دهد
پزشک عضو (سرویسی) 0912000202 نگار سلطانی
پزشک عضو (اسلاتی) 0912000203 بهرام فتحی
پزشک عضو (اسلاتی) 0912000204 الهام قاسمی
منشی 0912000209 روی هر ۴ پزشک
بیماران 0912000120009120001207 ۸ بیمار
  • ترکیب حالت‌ها: ۲ سرویسی + ۲ اسلاتی، همه شنبه تا چهارشنبه ۱۶:۰۰–۲۱:۰۰
  • دستگاه‌ها: ۲ لیزر (الکساندرایت، دایود) با setup/cleanup و تقویم کاری + ۲ اتاق درمان
  • سرویس‌ها: لیزر کامل بدن (۹۰د) · لیزر زیربغل (۲۰د) · ویزیت پوست (۱۵د) · هیدرودرم (۴۵د)
  • وابستگی به دستگاه: دو سرویس لیزری هرکدام یک بخش (SegmentTemplate) با نیازمندی انحصاری روی نوع منبع «دستگاه لیزر» دارند
  • بیمه: تامین اجتماعی، سلامت ایرانیان، بیمه آسیا

این حساب دو محیط کاری دارد (مطب شخصی + کلینیک). سرویس‌ها و دستگاه‌ها زیر محیط کلینیک اند، پس تا وقتی محیط عوض نشود لیستشان خالی است — این درست است، نه باگ:

POST /api/v1/auth/switch-context  {"db_uuid": "<clinic_uuid>"}

سناریوی ۳ — کلینیک مستقل با مالکِ غیرپزشک

نقش موبایل توضیح
مالک کلینیک 0912000301 رضا شریفی — پزشک نیست، فقط ROLE_CLINIC
پزشک (اسلاتی) 0912000302 پیمان اکبری
پزشک (سرویسی) 0912000303 مینا یوسفی
پزشک (منبع‌محور) 0912000304 آرش نوری
منشی 0912000309 روی هر ۳ پزشک
بیماران 0912000130009120001307 ۸ بیمار
  • هر سه حالت نوبت‌دهی اینجا زنده‌اند: 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
۰۸ عکس قیمت لیست قیمت فعال + PriceSnapshot روی نوبت‌های واقعی با بیعانه GET /api/v1/price-lists
۰۹ موتور سیاست ۶ سیاست، یکی از هر دسته (انتخاب، صلاحیت، منبع، زمان، فاصله، قیمت) GET /api/v1/policies
۱۱ پکیج و دفتر اعتبار پکیج ۶ جلسه‌ای + ۲ پکیج خریداری‌شده + ردیف مصرف در دفتر GET /api/v1/packages
۱۲ دورهٔ درمان پروتکل ۶ جلسه‌ای با پارامتر انرژی هر جلسه + دورهٔ فعال با ۶ جلسه GET /api/v1/treatment-course/{uuid}
۱۳ لغو و لیست انتظار سیاست عمومی + سیاست سخت‌گیرانه روی یک سرویس، رکورد عدم حضور، ۲ نفر در لیست انتظار GET /api/v1/waitlist · /cancellation-policy
۱۴ رویداد و بهره‌وری HoldCreated و AppointmentBooked در outbox + دقیقهٔ اشغال واقعی هر منبع GET /api/v1/reports/resource-utilization?branch_uuid=…

نوبت‌های موتور جدید از new Appointment(...) ساخته نمی‌شوند؛ از AppointmentPlanBuilderAvailabilityEngineHoldServiceBookingService رد می‌شوند. برای همین اشغال منبع و رویداد دامنه واقعی‌اند: گزارش بهره‌وری عدد نشان می‌دهد و تقویم دستگاه واقعاً پر است.

چه چیزی با این داده قابل تست است

قابلیت کجا
نوبت‌دهی اسلاتی 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-codeverify-code با 12345otp-login).
  • send-code سقف ۵ درخواست در ساعت به‌ازای هر IP دارد.
  • نام پزشک بدون عنوان ذخیره می‌شود — «دکتر» را لایهٔ نمایش اضافه می‌کند (PersianText::stripDoctorTitle).
  • دادهٔ انبوه جداست: app:seed-demo-data حدود ۵۰۰ پزشک و ۲۰هزار نوبت با INSERT خام می‌سازد و برای تست کارایی و ماژول نمایندگان است، نه برای تست سناریویی.