# نکات پیاده‌سازی — تسک ۰۱ ## ۱. چرا شعبه بالای آدرس می‌نشیند، نه جای آن سه مصرف‌کنندهٔ زنده به `doctor_addresses.id` وابسته‌اند: 1. `WeeklySchedule.setting[day].sessions[].location_id` (JSON) 2. `SlotCalculatorService::buildSessionSlots()` که آن را در هر اسلات کپی می‌کند 3. `AppointmentController::bookingLocations()` که به سایت عمومی `location_uuid` می‌دهد عوض کردن این قرارداد در build هیچ‌کدام از سه ریپو خطا نمی‌دهد — فقط در runtime آدرس گم می‌شود. پس `branch_id` روی آدرس اضافه می‌شود و آدرس همان‌جا می‌ماند. ## ۲. پزشک مستقل هم شعبه دارد وسوسه می‌شود که شعبه را فقط برای `entity_type=clinic` بسازیم. نکن. اگر پزشک مستقل شعبه نداشته باشد، تسک ۰۲ باید دو مسیر کد برای «منبع مال شعبه» و «منبع مال پزشک» داشته باشد و تسک ۰۶ هر دو را جدا حساب کند. مطب شخصی = شعبه‌ای با `entity_type=doctor`. ## ۳. حذف شعبه هرگز `CASCADE` روی حذف شعبه به منابع و نوبت‌ها نده. `DELETE` فقط وقتی مجاز است که: - هیچ `Room` فعالی نداشته باشد، **و** - هیچ `Resource` فعالی (تسک ۰۲) نداشته باشد، **و** - هیچ نوبت آیندهٔ فعالی روی منابعش نباشد (تسک ۰۷) تا آن تسک‌ها نیامده‌اند، فقط شرط اول را چک کن ولی سرویس را طوری بنویس که افزودن دو شرط بعدی یک خط باشد (لیست `DeletionGuardInterface` و تزریق آرایه‌ای از گاردها). `active=false` مسیر اصلی است، نه `DELETE`. ## ۴. ساعت کاری — دقیقه، نه رشته ```php // ❌ اشتباه: مقایسهٔ رشته‌ای در تسک ۰۶ می‌شکند ("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 ← باید سبز بماند ``` اجرا: ```bash 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` جدول طبقه‌بندی را با سه جدول جدید به‌روز کن.