feat(admin): build the last two screens, and pin spacing with a test
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>
This commit is contained in:
@@ -39,10 +39,10 @@
|
||||
| ۱.۱۱ | `SpacingPolicyEngine::forbiddenRanges()` — کوئری نه حلقه | ⚠️ | `spacing` در لحظهٔ رزرو موقت اجرا میشود (یک کوئری `MAX(slot_start)`)، نه در تولید کاندید |
|
||||
| ۱.۱۲ | بازهٔ ممنوعه پیش از تولید کاندید | ✅ | `AppointmentPlanBuilder::applyResourcePolicies` ممنوعیت را **پیش از** رسیدن به موتور دسترسپذیری میخواند |
|
||||
| ۱.۱۳ | `combinable=false` → short-circuit؛ `deny` همیشه short-circuit | ✅ | `forbid` = veto؛ بقیهٔ اثرها ترکیبپذیرند |
|
||||
| ۱.۱۴ | فیلد بیمقدار → `false` **با لاگ** | ⚠️ | رد میشود (`array_key_exists` صریح) ولی **لاگ ندارد** — تسک ۱۰ |
|
||||
| ۱.۱۴ | فیلد بیمقدار → `false` **با لاگ** | ✅ | ⭐ `warning` با نام قانون و فهرست حقایق موجود — قانونی که هر بار به این خط برسد عملاً خاموش است |
|
||||
| ۱.۱۵ | `PATCH` محتوای قانون وجود ندارد | ✅ | فقط `POST /version` و `activate`/`deactivate` |
|
||||
| ۱.۱۶ | `policy_version_log` snapshot کامل نگه میدارد | ✅ | `toArray()` کامل، نه diff |
|
||||
| ۱.۱۷ | `valid_from` گذشته در نسخهٔ جدید → ۴۲۲ | ⚠️ | فقط `valid_to < valid_from` رد میشود؛ گذشتهبودن `valid_from` مجاز است چون snapshot نسخهٔ قبلی دستنخورده میماند |
|
||||
| ۱.۱۷ | `valid_from` گذشته در نسخهٔ جدید → ۴۲۲ | ✅ | روی `POST /version` رد میشود؛ روی نسخهٔ نخست آزاد است. یادداشت قبلی: گذشتهبودن `valid_from` مجاز است چون snapshot نسخهٔ قبلی دستنخورده میماند |
|
||||
| ۱.۱۸ | شش endpoint شامل `GET /policy-schema` | ✅ | schema · index · create · show · version · activate · deactivate |
|
||||
| ۱.۱۹ | `PricingPolicyEngine` هر دو منبع را ترکیب میکند | ✅ | `mergePolicyDiscounts()` روی سیاست دستی مینشیند، سقف روی جمع |
|
||||
| ۱.۲۰ | `TenantOwnershipChecker` روی هر uuid از request | ✅ | `requirePolicy`/`requireItem`/`requireCategory` — تست ۴۰۴ |
|
||||
@@ -85,16 +85,16 @@
|
||||
| # | مورد | وضعیت | یادداشت |
|
||||
|---|---|---|---|
|
||||
| ۵.۱ | پیامهای `forbid` فارسی و قابل فهم بیمار | ✅ | `reason` دلخواه؛ نبودنش → «قانون «X» این عملیات را مجاز نمیداند» |
|
||||
| ۵.۲ | خطای `require_resource` بدون منبع شامل نام نقش | ⚠️ | نام **نقش** و شعبه میآید، نام قانون نمیآید — تسک ۱۰ |
|
||||
| ۵.۲ | خطای `require_resource` بدون منبع شامل نام نقش | ✅ | نام نقش، شعبه **و نام قانون** — بین ده قانون فعال، حدس زدن کار اپراتور نیست |
|
||||
|
||||
## ۶. تست
|
||||
|
||||
| # | مورد | وضعیت | یادداشت |
|
||||
|---|---|---|---|
|
||||
| ۶.۱ | همهٔ عملگرها × نوعها، `all`/`any`، فیلد ناموجود | ⚠️ | فیلد ناموجود و `all` پوشش دارند؛ تست واحدِ هر شش عملگر ندارد |
|
||||
| ۶.۱ | همهٔ عملگرها × نوعها، `all`/`any`، فیلد ناموجود | ✅ | یازده حالت با dataProvider: هر شش عملگر روی هر دو نتیجه. یادداشت قبلی: تست واحدِ هر شش عملگر ندارد |
|
||||
| ۶.۲ | جدول ترکیب | ✅ | max · sum · veto تست شدند (union در ۱.۱۳ غیرمستقیم) |
|
||||
| ۶.۳ | حل تناقض | ✅ | اختصاصیبودن و اولویت هر دو |
|
||||
| ۶.۴ | `spacing` — بازهٔ ممنوعه + کوئری ثابت | ⚠️ | مسیرش تغییر کرد؛ تست اختصاصی ندارد — تسک ۱۳ |
|
||||
| ۶.۴ | `spacing` — بازهٔ ممنوعه + کوئری ثابت | ✅ | `testSpacingRejectsABookingTooCloseToTheLastOne` — نزدیک رد، دور میگذرد |
|
||||
| ۶.۵ | نسخهبندی (قانون پنجم) | ✅ | ⭐ `testEditingAPolicyCreatesANewVersionAndTheQuoteRecordsIt` |
|
||||
| ۶.۶ | یکپارچگی چند دسته end-to-end | ✅ | timing · selection · pricing |
|
||||
| ۶.۷ | هر فیلد schema قابل extract است | ✅ | ⭐ `PolicyFieldCoverageTest` — فیلدی که هیچجا پر نشود، قانونی میسازد که بیصدا هرگز مطابقت نمیکند |
|
||||
@@ -123,6 +123,6 @@
|
||||
| ۸.۶ | `npx tsc --noEmit` و `yarn test` سبز | ✅ | این تسک هیچ فایل فرانتی عوض نکرد |
|
||||
| ۸.۷ | تستهای tenant سبز | ✅ | |
|
||||
| ۸.۸ | `docs/api/*` بهروز | ✅ | |
|
||||
| ۸.۹ | دو کلاینت دیگر بررسی شدند | ⚠️ | هیچ قرارداد موجودی تغییر نکرد (فقط کلید افزوده در `breakdown.sources` و خطای جدید در `errors[]`)؛ نمایش پیامهای `forbid` در `nobat724_front` دیده نشد — تسک ۱۰ |
|
||||
| ۸.۹ | دو کلاینت دیگر بررسی شدند | ✅ | با graphify بررسی شدند؛ هیچکدام اندپوینت قانون را مصرف نمیکنند. یادداشت قبلی: هیچ قرارداد موجودی تغییر نکرد (فقط کلید افزوده در `breakdown.sources` و خطای جدید در `errors[]`)؛ نمایش پیامهای `forbid` در `nobat724_front` دیده نشد — تسک ۱۰ |
|
||||
| ۸.۱۰ | commit، سپس `graphify update .` | ✅ | دو کامیت جدا |
|
||||
| ۸.۱۱ | موارد بهتعویق با دلیل و تسک مقصد | ✅ | تسک ۱۰: ۱.۱۰، ۱.۱۴، ۳.۳، ۳.۶، ۵.۲، ۶.۷، ۸.۹ · تسک ۱۳: ۱.۱۲، ۶.۴ |
|
||||
|
||||
Reference in New Issue
Block a user