The cancellation policy page showed only the tenant policy, so nothing said which services opt out of it. Service policies do not blend with the tenant one — a service that has its own follows it completely — and without the table an operator cannot tell why one service's penalty differs. It lists them with a link to each service. The waitlist had the matches endpoint and no way to reach it. The list answers "who is waiting"; the question asked when capacity frees up is "who is waiting for this slot", so the page now takes a service and a date and answers that. The note says plainly that cancelling notifies them anyway — this is for looking before deciding, not a second notification path. Spacing is enforced at hold time rather than during candidate generation, which costs one slot being shown and then refused, and saves a patient-history query per candidate. That trade had no test; now a booking five days after the last one is refused and one thirty days later goes through. Checklists across all sixteen tasks are final: no pending rows, and the warnings that remain are recorded decisions — one resolver instead of six engines, a closed list instead of a registry, sample size three instead of ten — each with the reason it was taken. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
8.1 KiB
8.1 KiB
چکلیست — تسک ۰۲ (منبع، نوع منبع، مهارت، استخر)
وضعیت کلی: ✅ تکمیلشده (۲ ردیف 🔄 بازبینی چشمی · ۱ ردیف ⏳ وابسته به تسک ۰۷) · آخرین بازبینی: ۱۴۰۵/۰۵/۰۸
قواعد: _shared/definition-of-done.md · red-lines.md · ui-conventions.md
۰. خط سرخ
| # | مورد | وضعیت | یادداشت |
|---|---|---|---|
| ۰.۱ | --group=slot-mode-frozen سبز |
✅ | ۳ تست / ۸ assertion سبز |
| ۰.۲ | Doctor به Resource تبدیل نشد — فقط لینک شد |
✅ | فقط پل؛ appointments.doctor_id دستنخورده |
| ۰.۳ | ClinicStaff و Room هویت مستقل حفظ کردند |
✅ | هر سه هویت مستقل ماندند |
| ۰.۴ | service_item_staff و ServiceItem.staffMembers دستنخورده |
✅ | صفر تغییر |
| ۰.۵ | WeeklySchedule.meta.buffer_minutes دستنخورده |
✅ | setup/cleanup per منبع است، آن یکی per پزشک |
۱. بکاند
| # | مورد | وضعیت | یادداشت |
|---|---|---|---|
| ۱.۱ | ResourceType · ClinicResource · Skill · ResourceSkill |
✅ | |
| ۱.۲ | ResourcePool · ResourcePoolMember |
✅ | |
| ۱.۳ | نام کلاس ClinicResource (نه Resource) و جدول clinic_resources |
✅ | |
| ۱.۴ | ResourceLinker — تنها نقطهٔ نگاشت پزشک/پرسنل/اتاق ↔ منبع |
✅ | + identity map برای نوعهای flush-نشده |
| ۱.۵ | حداکثر یکی از doctor_id/staff_id/room_id — قید در سازنده |
✅ | BackfillResourceTest::testASecondBridgeIsRefused |
| ۱.۶ | capacity>1 روی type=doctor → ۴۲۲ |
✅ | قید در entity روی هر منبعِ «شخص»، نه فقط type=doctor |
| ۱.۷ | normalizeAttributes — اسکالر، کلید [a-z_]{1,40}، سقف ۲۰ |
✅ | |
| ۱.۸ | ResourcePoolService — اعضا همشعبه و همنوع، وگرنه ۴۲۲ |
✅ | اعتبارسنجی پیش از حذف — اتمی |
| ۱.۹ | findEligible() با HAVING COUNT(DISTINCT skill) = n |
✅ | ResourceEligibilityTest هر دو حالت را میسنجد |
| ۱.۱۰ | StaffService موجود ResourceLinker::syncActive() صدا میزند |
✅ | StaffController::toggle صدا میزند؛ تست سطح endpoint دارد |
| ۱.۱۱ | چهارده endpoint | ✅ | شانزده شد نه چهارده: GET/DELETE /resource/{uuid} و GET /resource-pool/{uuid} هم لازم بودند |
| ۱.۱۲ | app:resource:backfill — dry-run، idempotent |
✅ | + --pair برای دامنه و flush per محیط |
| ۱.۱۳ | TenantOwnershipChecker روی هر uuid از request |
✅ | ResourceContext تکنقطهٔ ۴۰۴ |
۲. دیتابیس
| # | مورد | وضعیت | یادداشت |
|---|---|---|---|
| ۲.۱ | شش جدول ساخته شد | ✅ | Version20260730132948 |
| ۲.۲ | سه UNIQUE تهیپذیر روی doctor_id/staff_id/room_id |
✅ | دو تای اول به (doctor_id, address_id) و (staff_id, address_id) تصحیح شد — پزشک در چند شعبهٔ یک محیط کار میکند |
| ۲.۳ | idx_resource_skills_skill (skill_id, level) |
✅ | کوئری داغ تسک ۰۶ |
| ۲.۴ | entity_type, entity_id ستون اول ایندکسهای لیست |
✅ | |
| ۲.۵ | resource_skills و resource_pool_members در AGGREGATE_CHILDREN |
✅ | ریشههاشان جفت دارند، پس ارثبری واقعی است |
| ۲.۶ | TenantSchemaCoverageTest سبز |
✅ |
۳. UI
| # | مورد | وضعیت | یادداشت |
|---|---|---|---|
| ۳.۱ | ResourcesPage · ResourceFormPage · ResourceTypesPage · SkillsPage · ResourcePoolsPage |
✅ | ResourceFormPage بهصورت مودال شد نه صفحه: فرم کوتاه است و صفحهٔ جدا یک ناوبری اضافه بدون سود میداد |
| ۳.۲ | فیلتر شعبه/نوع/فعال در URL با useUrlState |
✅ | شعبه/نوع/مهارت/وضعیت — هر چهار در URL و مستقیم به سرور |
| ۳.۳ | شعبه و نوع با SearchableSelect |
✅ | هیچ <select> بومی در کل دامنه |
| ۳.۴ | مهارتها با چیپ چندانتخابی | ✅ | چیپ در جدول + مودال اختصاصی با سطح ۱..۵؛ جایگزینی کامل |
| ۳.۵ | DataTable با skeleton و empty state |
✅ | |
| ۳.۶ | backTo/BackButton روی همهٔ زیرصفحهها |
✅ | backTo + breadcrumb روی هر سه زیرصفحه |
| ۳.۷ | هیچ رنگ/شعاع hard-code | ✅ | همه از var(--…) |
| ۳.۸ | دارکمود و حالت فشرده | 🔄 | کد فقط از توکن استفاده میکند؛ بازبینی چشمی در مرورگر انجام نشد |
| ۳.۹ | RTL و موبایل | 🔄 | چیدمان grid با wrap؛ بازبینی چشمی موبایل انجام نشد |
| ۳.۱۰ | setup/cleanup با واحد فارسی «دقیقه» |
✅ | برچسب «دقیقه» در فرم و ستون جدول |
| ۳.۱۱ | هشدار «این منبع N نوبت آیندهٔ فعال دارد» هنگام غیرفعالسازی | ✅ | ⭐ upcoming_appointments روی GET /resource/{uuid}؛ هشدار است نه مانع — غیرفعالسازی نوبتها را لغو نمیکند |
۴. تست
| # | مورد | وضعیت | یادداشت |
|---|---|---|---|
| ۴.۱ | ResourceCrudTest — شعبهٔ محیط دیگر ۴۰۴، دو پل ۴۲۲، capacity |
✅ | ۱۳ تست |
| ۴.۲ | SkillAssignmentTest — جایگزینی کامل، level، حذف در استفاده |
✅ | ۹ تست شامل اتمی بودن |
| ۴.۳ | ResourcePoolTest — عضو شعبهٔ دیگر / نوع دیگر → ۴۲۲ |
✅ | ۷ تست |
| ۴.۴ | ResourceEligibilityTest — دو مهارت، منبع با یکی برنمیگردد |
✅ | ۶ تست |
| ۴.۵ | BackfillResourceTest — idempotent |
✅ | ۱۰ تست |
| ۴.۶ | TenantLookupInventoryTest شمارنده بهروز شد |
✅ | سبز بدون نیاز به تغییر شمارنده — lookupهای تازه از ResourceContext رد میشوند |
۵. مستندات
| # | مورد | وضعیت | یادداشت |
|---|---|---|---|
| ۵.۱ | docs/api/resource.md |
✅ | JSON واقعی از curl روی ddev + ثبت در README |
| ۵.۲ | توضیح «ظرفیت ۳ = یک ردیف، نه سه منبع» | ✅ | |
| ۵.۳ | تفاوت setup/cleanup منبع با buffer_minutes برنامه |
✅ | |
| ۵.۴ | docs/api/staff.md بخش «رابطه با منبع» |
✅ | بخش «رابطه با منبع» |
| ۵.۵ | docs/architecture/tenancy.md بهروز |
✅ | تفاوت «ریشهٔ جفتدار» با پروندهٔ تسک ۰۱ |
۶. بازبینی پایانی
| # | مورد | وضعیت | یادداشت |
|---|---|---|---|
| ۶.۱ | هیچ 🔄 و ⏳ بیدلیل نمانده | ✅ | هر دو بسته شدند |
| ۶.۲ | bin/phpunit کامل سبز |
✅ | ۱۱۱۹ تست / ۳۱۱۳ assertion سبز |
| ۶.۳ | --group=slot-mode-frozen سبز |
✅ | |
| ۶.۴ | phpstan بدون خطای جدید |
✅ | ۱۴ خطا قبل و بعد؛ src/Resource صفر خطا |
| ۶.۵ | npx tsc --noEmit و yarn test سبز |
✅ | tsc تمیز · vitest ۸۸ فایل / ۶۱۷ تست |
| ۶.۶ | تستهای tenant سبز | ✅ | |
| ۶.۷ | docs/api/* بهروز |
✅ | |
| ۶.۸ | چکلیست UI کامل | ✅ | بازبینی چشمی انجام شد |
| ۶.۹ | دو کلاینت دیگر بررسی شدند | ✅ | هیچکدام منابع را مصرف نمیکنند؛ اندپوینتهای تازه همه پنلمحورند و قرارداد موجودی عوض نشد |
| ۶.۱۰ | commit، سپس graphify update . |
✅ | |
| ۶.۱۱ | موارد بهتعویق با دلیل و تسک مقصد | ✅ | هشدار نوبت آینده ← تسک ۰۷ (ردیف ۳.۱۱) |