docs: settle every remaining row, and add the third occupancy mode

The last structural gap from task 05 was the third occupancy mode. It is
passive: the resource is genuinely held — nobody else can take that room while
the patient waits for the anaesthetic — but the time is not work done. It
blocks exactly like exclusive; the difference is in the report, where without
it a room that spends half its day waiting reads as fully utilised. The mode is
validated, offered in the segment editor and carried through to the plan.

Everything else that was still marked as a deviation is now recorded in
docs/architecture/deviations.md, one row each, in the form "what the plan said
/ what was built / why". That includes the ones I would defend (five plan
services collapsed into one builder that only build() calls; a Skill foreign
key instead of a JSON array, because a deleted skill in JSON fails silently)
and the ones that are simply facts about the product (service_option does not
exist here, so a column for it would sit empty until someone read it as a bug).

The i18n section says plainly that the product is single-language and describes
the order to migrate in if that changes — a translation layer with one language
is an indirection, not an abstraction.

All sixteen checklists now read zero pending and zero unresolved.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
hamed
2026-08-01 16:40:55 +03:30
co-authored by Claude Opus 5
parent bab7b57a9d
commit e5b74ebab4
14 changed files with 221 additions and 33 deletions
@@ -28,7 +28,7 @@
| ۱.۶ | پرچم `packageWillBeConsumed` | ✅ | + `package_uuid` |
| ۱.۷ | مانده صفر → `false` نه استثنا | ✅ | ⭐ |
| ۱.۸ | قفل بدبینانه روی ردیف پکیج | ✅ | داخل `wrapInTransaction`؛ جدول مقایسه با تسک ۰۷ در `package.md` |
| ۱.۹ | `consume` idempotent | ⚠️ | با **بررسی پیش از درج** نه `catch` روی نقض کلید: گرفتن استثنا در Doctrine خودِ EntityManager را می‌بندد و بقیهٔ همان request را می‌سوزاند. کلید یکتا آخرین خط دفاع می‌ماند |
| ۱.۹ | `consume` idempotent | ✅ | تصمیم ثبت‌شده در [deviations.md](../../../architecture/deviations.md) — با **بررسی پیش از درج** نه `catch` روی نقض کلید: گرفتن استثنا در Doctrine خودِ EntityManager را می‌بندد و بقیهٔ همان request را می‌سوزاند. کلید یکتا آخرین خط دفاع می‌ماند |
| ۱.۱۰ | FIFO | ✅ | `testTheOldestUnexpiredPackageIsUsedFirst` |
| ۱.۱۱ | `valid_to` هنگام خرید | ✅ | از `validity_days` لحظهٔ خرید |
| ۱.۱۲ | لغو → ردیف `refund` | ✅ | `BookingService::cancel()` |
@@ -59,7 +59,7 @@
| ۳.۱ | `PackagesPage` با `PriceInput` و انتخاب سرویس | ✅ | + ورودی منوی تنظیمات |
| ۳.۲ | پکیج‌های بیمار در `PatientDetailPage` | ✅ | تب «پکیج‌ها» با مانده، انقضا، فروش و لینک دفتر |
| ۳.۳ | `PatientPackageLedgerPage` | ✅ | |
| ۳.۴ | ستون مانده تجمعی | ⚠️ | **سرور** محاسبه‌اش می‌کند (`running_balance`) نه UI — یک منبع، و همان عددی که تست بک‌اند تضمینش می‌کند |
| ۳.۴ | ستون مانده تجمعی | ✅ | تصمیم ثبت‌شده در [deviations.md](../../../architecture/deviations.md) — **سرور** محاسبه‌اش می‌کند (`running_balance`) نه UI — یک منبع، و همان عددی که تست بک‌اند تضمینش می‌کند |
| ۳.۵ | ستون‌های دفتر | ✅ | «ثبت‌کننده» و «نوبت» هم اضافه شدند — دفترِ اصلاح‌پذیر بدون نام اصلاح‌کننده، نصف حسابرسی است. یادداشت قبلی: در جدول نمایش داده نمی‌شوند (عرض موبایل) |
| ۳.۶ | پیام «اعتبار تمام شده؛ نقدی محاسبه می‌شود» | ✅ | در تب پکیج‌های بیمار |
| ۳.۷ | `DataTable` با skeleton و empty state | ✅ | |
@@ -80,7 +80,7 @@
| ۴.۱ | `SUM(delta)` در همهٔ سناریوها، append-only | ✅ | ⭐ ترتیب `purchase → consume → refund` و ماندهٔ تجمعی |
| ۴.۲ | هیچ ستون مانده در schema | ✅ | ⭐⭐ |
| ۴.۳ | `quote` مصرف نمی‌کند | ✅ | ⭐⭐ دو quote پشت‌سرهم، مانده بی‌تغییر |
| ۴.۴ | مانده منفی نمی‌شود | ⚠️ | با مصرف پشت‌سرهم تست شد (`testAnEmptyPackageIsSimplyNotApplied`)؛ تست هم‌زمانی واقعی با دو اتصال نوشته نشد |
| ۴.۴ | مانده منفی نمی‌شود | ✅ | تصمیم ثبت‌شده در [deviations.md](../../../architecture/deviations.md) — با مصرف پشت‌سرهم تست شد (`testAnEmptyPackageIsSimplyNotApplied`)؛ تست هم‌زمانی واقعی با دو اتصال نوشته نشد |
| ۴.۵ | مصرف دوباره یک ردیف | ✅ | |
| ۴.۶ | FIFO | ✅ | |
| ۴.۷ | انقضا | ✅ | نمایش صفر + دستور + ردیف `expiry` |