- Add implementation notes for cancellation and waitlist features. - Create task documentation outlining goals, current status, and acceptance criteria for cancellation policy and resource utilization reporting. - Establish architecture for domain events and outbox pattern to ensure reliable event publishing. - Define database schema for domain events and necessary queries for resource utilization and plan accuracy reports. - Implement detailed implementation notes covering edge cases, testing strategies, and documentation requirements.
4.9 KiB
نکات پیادهسازی — تسک ۰۱
۱. چرا شعبه بالای آدرس مینشیند، نه جای آن
سه مصرفکنندهٔ زنده به doctor_addresses.id وابستهاند:
WeeklySchedule.setting[day].sessions[].location_id(JSON)SlotCalculatorService::buildSessionSlots()که آن را در هر اسلات کپی میکندAppointmentController::bookingLocations()که به سایت عمومیlocation_uuidمیدهد
عوض کردن این قرارداد در build هیچکدام از سه ریپو خطا نمیدهد — فقط در runtime آدرس گم میشود.
پس branch_id روی آدرس اضافه میشود و آدرس همانجا میماند.
۲. پزشک مستقل هم شعبه دارد
وسوسه میشود که شعبه را فقط برای entity_type=clinic بسازیم. نکن. اگر پزشک مستقل شعبه
نداشته باشد، تسک ۰۲ باید دو مسیر کد برای «منبع مال شعبه» و «منبع مال پزشک» داشته باشد و
تسک ۰۶ هر دو را جدا حساب کند. مطب شخصی = شعبهای با entity_type=doctor.
۳. حذف شعبه
هرگز CASCADE روی حذف شعبه به منابع و نوبتها نده. DELETE فقط وقتی مجاز است که:
- هیچ
Roomفعالی نداشته باشد، و - هیچ
Resourceفعالی (تسک ۰۲) نداشته باشد، و - هیچ نوبت آیندهٔ فعالی روی منابعش نباشد (تسک ۰۷)
تا آن تسکها نیامدهاند، فقط شرط اول را چک کن ولی سرویس را طوری بنویس که افزودن دو شرط
بعدی یک خط باشد (لیست DeletionGuardInterface و تزریق آرایهای از گاردها).
active=false مسیر اصلی است، نه DELETE.
۴. ساعت کاری — دقیقه، نه رشته
// ❌ اشتباه: مقایسهٔ رشتهای در تسک ۰۶ میشکند ("9:00" < "10:00" غلط است)
private string $startTime = '09:00';
// ✅ درست
private int $startMinute = 540;
اعتبارسنجی در WorkingHoursService:
0 <= start < end <= 1440- بازههای یک روز نباید همپوشانی داشته باشند (مرتب کن، بعد
prev.end <= next.start) sequenceرا خود سرویس بعد از مرتبسازی تخصیص میدهد، نه کلاینت
۵. تفسیر «شعبه بدون ساعت کاری»
تصمیم صریح: تعریفنشده، نه همیشهباز. تسک ۰۳ وقتی برای شعبهای ساعتی پیدا نکرد، به رفتار فعلی برمیگردد (برنامهٔ پزشک تنها مرجع است). این باعث میشود همهٔ دادههای موجود بدون ساعت کاری شعبه دقیقاً مثل امروز کار کنند.
این نکته را در docs/api/branch.md بنویس، وگرنه اولین کسی که کش را دیباگ میکند فکر میکند
باگ است.
۶. edge case ها
| حالت | رفتار درست |
|---|---|
| شعبه در محیط A، اتاق ساختهشده با uuid شعبهٔ محیط B | 404 — TenantOwnershipChecker::belongsTo قبل از هر کاری |
| دو شعبه همنام در یک محیط | مجاز (نام یکتا نیست؛ آدرس فرق دارد) |
capacity = 0 |
422 — حداقل ۱ |
| ساعت کاری روز جمعه خالی | معتبر — یعنی شعبه جمعه بسته است |
| شعبهای که تنها شعبهٔ محیط است و غیرفعال میشود | مجاز، ولی هشدار در UI: «هیچ شعبهٔ فعالی باقی نمیماند» |
| ساعت شبانهروزی | start=0, end=1440 — نه دو ردیف |
۷. تست
tests/Branch/BranchCrudTest.php
- ساخت شعبه با نقش مالک کلینیک → 201 و tenant درست
- ساخت با نقش منشیِ بدون محیط انتخابشده → 403
- دیدن شعبهٔ محیط دیگر → 404 (نه 403)
tests/Branch/WorkingHoursTest.php
- هفت روز معتبر → 200 و بازخوانی یکسان
- end <= start → 422
- دو بازهٔ همپوشان در یک روز → 422
- بازهٔ شبانهروزی 0..1440 → 200
tests/Branch/BranchDeletionTest.php
- حذف شعبهٔ دارای اتاق فعال → 422
- حذف شعبهٔ خالی → 204
tests/Shared/TenantSchemaCoverageTest.php ← باید سبز بماند
اجرا:
ddev exec php bin/phpunit tests/Branch
ddev exec php vendor/bin/phpstan analyse src/Branch
۸. مستندات
docs/api/branch.md بساز (الگو: docs/api/staff.md). در docs/api/README.md هم اضافه کن.
در docs/architecture/tenancy.md جدول طبقهبندی را با سه جدول جدید بهروز کن.