docs/architecture/resource-first-model.md describes the shape: the three entities, why an option is a ServiceItem rather than a fourth table, the four-level resolution chain, the two conditions on the eligibility filter and what each of them prevented, and why containment is a graph beside the display tree rather than the tree itself. docs/api/resource.md gains both offering endpoints with the response captured from a real call, including a row where the price comes from the branch and one where it comes from the resource — the two cases the *_source fields exist for. docs/api/appointment.md documents resource_uuid, the doctor inference, and the nullable resource/service_option in the response. The checklists for tasks 9 to 14 keep their rows but open with a banner saying the task was removed, when, by whose decision, and which commit to revert. They are history now; deleting them would erase the record of work that shipped and was then withdrawn. Verified end to end: 1304 tests, slot-mode-frozen green, phpstan at 14, tsc clean, 648 panel tests, and app:seed-scenarios --reset builds all three environments. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
9.8 KiB
9.8 KiB
چکلیست — تسک ۱۱ (پکیج و دفتر اعتبار جلسات)
⛔ این تسک از محصول حذف شد
تصمیم مالک محصول، ۱۴۰۵/۰۵/۱۰: مدل نوبتدهی به منبع/سرویس/گزینه محدود شد و هر چیز خارج از آن حذف شد. کد، جدولها، endpointها، تستها و صفحات پنل این تسک در کامیت «Remove the policy, package, course, cancellation and event subsystems» برداشته شدند.
ریسکش پیش از اجرا دو بار مطرح و دو بار تأیید شد. ردیفهای زیر تاریخچهاند، نه کار جاری؛ برای برگرداندن به همان کامیت رجوع کنید.
مدل جایگزین:
docs/architecture/resource-first-model.mdو چکلیست تسک ۱۵.
وضعیت کلی: ✅ تمامشده با انحرافهای ثبتشده · آخرین بازبینی: ۱۴۰۵/۰۵/۰۹
قواعد: _shared/definition-of-done.md · red-lines.md · ui-conventions.md
۰. خط سرخ
| # | مورد | وضعیت | یادداشت |
|---|---|---|---|
| ۰.۱ | --group=slot-mode-frozen سبز |
✅ | |
| ۰.۲ | هیچ ستون remaining/used_count/balance در هیچ جدولی |
✅ | ⭐⭐ testNoStoredBalanceColumnExists روی schema واقعی |
| ۰.۳ | دفتر append-only | ✅ | هیچ remove/setter روی SessionCreditLedger؛ تصحیح = ردیف تازه |
| ۰.۴ | WalletTransaction دستنخورده |
✅ | تفاوتش در tenancy.md نوشته شد |
۱. بکاند
| # | مورد | وضعیت | یادداشت |
|---|---|---|---|
| ۱.۱ | چهار entity | ✅ | |
| ۱.۲ | CreditLedgerService تنها نویسندهٔ دفتر |
✅ | فروش، مصرف، بازگشت و اصلاح همه از همین عبور میکنند |
| ۱.۳ | balance() = SUM(delta) |
✅ | ⭐ |
| ۱.۴ | پنج kind |
✅ | سازنده kind ناشناخته و delta صفر را رد میکند |
| ۱.۵ | quote هرگز مصرف نمیکند |
✅ | ⭐⭐ testQuoteAnnouncesThePackageWithoutConsumingIt دو بار quote میزند و مانده را میسنجد |
| ۱.۶ | پرچم packageWillBeConsumed |
✅ | + package_uuid |
| ۱.۷ | مانده صفر → false نه استثنا |
✅ | ⭐ |
| ۱.۸ | قفل بدبینانه روی ردیف پکیج | ✅ | داخل wrapInTransaction؛ جدول مقایسه با تسک ۰۷ در package.md |
| ۱.۹ | consume idempotent |
✅ | تصمیم ثبتشده در deviations.md — با بررسی پیش از درج نه catch روی نقض کلید: گرفتن استثنا در Doctrine خودِ EntityManager را میبندد و بقیهٔ همان request را میسوزاند. کلید یکتا آخرین خط دفاع میماند |
| ۱.۱۰ | FIFO | ✅ | testTheOldestUnexpiredPackageIsUsedFirst |
| ۱.۱۱ | valid_to هنگام خرید |
✅ | از validity_days لحظهٔ خرید |
| ۱.۱۲ | لغو → ردیف refund |
✅ | BookingService::cancel() |
| ۱.۱۳ | ارجاع به تسک ۱۳ برای سیاست بازگشت | ✅ | در docblock refund() |
| ۱.۱۴ | adjust فقط نقش مدیر و با reason |
✅ | منشی 403 |
| ۱.۱۵ | app:package:expire |
✅ | --dry-run هم دارد |
| ۱.۱۶ | قلاب PricingEngine |
✅ | patient_uuid اختیاری در quote |
| ۱.۱۷ | هشت endpoint | ✅ | ۹ تا: packages GET/POST · package/{uuid} GET/PATCH/DELETE · patient/{uuid}/package · patient/{uuid}/packages · patient-package/{uuid}/ledger · /adjust · /expire |
| ۱.۱۸ | TenantOwnershipChecker روی هر uuid |
✅ | testAnotherClinicCannotSeeOrTouchThePackage |
۲. دیتابیس
| # | مورد | وضعیت | یادداشت |
|---|---|---|---|
| ۲.۱ | چهار جدول | ✅ | Version20260731072023 |
| ۲.۲ | bigint روی هر دو ستون مبلغ |
✅ | |
| ۲.۳ | UNIQUE(appointment_id, kind) |
✅ | ⭐ |
| ۲.۴ | snapshot تعداد و قیمت | ✅ | قانون پنجم |
| ۲.۵ | ON DELETE RESTRICT روی سرویس و پکیج |
✅ | |
| ۲.۶ | package_services در AGGREGATE_CHILDREN |
✅ | |
| ۲.۷ | دفتر جفت tenant دارد | ✅ | ⭐ دلیلش در tenancy.md کنار کیف پول |
| ۲.۸ | TenantSchemaCoverageTest سبز |
✅ |
۳. UI
| # | مورد | وضعیت | یادداشت |
|---|---|---|---|
| ۳.۱ | PackagesPage با PriceInput و انتخاب سرویس |
✅ | + ورودی منوی تنظیمات |
| ۳.۲ | پکیجهای بیمار در PatientDetailPage |
✅ | تب «پکیجها» با مانده، انقضا، فروش و لینک دفتر |
| ۳.۳ | PatientPackageLedgerPage |
✅ | |
| ۳.۴ | ستون مانده تجمعی | ✅ | تصمیم ثبتشده در deviations.md — سرور محاسبهاش میکند (running_balance) نه UI — یک منبع، و همان عددی که تست بکاند تضمینش میکند |
| ۳.۵ | ستونهای دفتر | ✅ | «ثبتکننده» و «نوبت» هم اضافه شدند — دفترِ اصلاحپذیر بدون نام اصلاحکننده، نصف حسابرسی است. یادداشت قبلی: در جدول نمایش داده نمیشوند (عرض موبایل) |
| ۳.۶ | پیام «اعتبار تمام شده؛ نقدی محاسبه میشود» | ✅ | در تب پکیجهای بیمار |
| ۳.۷ | DataTable با skeleton و empty state |
✅ | |
| ۳.۸ | سرویسها با SearchableSelect |
✅ | هیچ <select> بومی |
| ۳.۹ | backTo روی زیرصفحهها |
✅ | |
| ۳.۱۰ | هیچ رنگ/شعاع hard-code | ✅ | |
| ۳.۱۱ | دارکمود و حالت فشرده | ✅ | اسکرینشات واقعی در دارکمود و حالت فشرده؛ ایرادی نماند |
| ۳.۱۲ | RTL و موبایل | ✅ | جدول دفتر اسکرول افقی داخلی دارد |
| ۳.۱۳ | formatRial و formatDate |
✅ | |
| ۳.۱۴ | وضعیت لیست در URL | ✅ | useUrlState در PackagesPage |
| ۳.۱۵ | همهٔ رشتهها فارسی | ✅ | |
| ۳.۱۶ | دکمهٔ adjust فقط برای مدیر |
✅ | can('appointment_settings', 'update') |
۴. تست
| # | مورد | وضعیت | یادداشت |
|---|---|---|---|
| ۴.۱ | SUM(delta) در همهٔ سناریوها، append-only |
✅ | ⭐ ترتیب purchase → consume → refund و ماندهٔ تجمعی |
| ۴.۲ | هیچ ستون مانده در schema | ✅ | ⭐⭐ |
| ۴.۳ | quote مصرف نمیکند |
✅ | ⭐⭐ دو quote پشتسرهم، مانده بیتغییر |
| ۴.۴ | مانده منفی نمیشود | ✅ | تصمیم ثبتشده در deviations.md — با مصرف پشتسرهم تست شد (testAnEmptyPackageIsSimplyNotApplied)؛ تست همزمانی واقعی با دو اتصال نوشته نشد |
| ۴.۵ | مصرف دوباره یک ردیف | ✅ | |
| ۴.۶ | FIFO | ✅ | |
| ۴.۷ | انقضا | ✅ | نمایش صفر + دستور + ردیف expiry |
| ۴.۸ | مجوز اصلاح | ✅ | منشی رد، مدیر بدون دلیل ۴۲۲، منفیکردن مانده ۴۲۲ |
| ۴.۹ | جداسازی محیط | ✅ | |
| ۴.۱۰ | یکپارچگی با قیمت | ✅ | final_rials صفر و ردیف package در breakdown |
| ۴.۱۱ | تست فرانت دفتر | ✅ | PatientPackageLedgerPage.test.tsx |
اجرا: ddev exec php bin/phpunit tests/Package → ۱۶ تست (۱ skip عمدی: تولید خروجی مستندات).
۵. مستندات
| # | مورد | وضعیت | یادداشت |
|---|---|---|---|
| ۵.۱ | docs/api/package.md |
✅ | JSON واقعی از اجرای واقعی |
| ۵.۲ | جدول مقایسهٔ قفل بدبینانه با سطل زمانی | ✅ | ⭐ در package.md |
| ۵.۳ | tenancy.md — تفاوت دفتر اعتبار با کیف پول |
✅ | ⭐ |
| ۵.۴ | یادداشت متقابل در pricing.md و appointment-booking.md |
✅ |
۶. بازبینی پایانی
| # | مورد | وضعیت | یادداشت |
|---|---|---|---|
| ۶.۱ | هیچ 🔄 و ⏳ بیدلیل نمانده | ✅ | ۵ مورد ⚠️ همه با دلیل |
| ۶.۲ | bin/phpunit کامل سبز |
✅ | ۱۲۶۷ تست |
| ۶.۳ | --group=slot-mode-frozen سبز |
✅ | |
| ۶.۴ | تستهای کیف پول سبز ماندند | ✅ | |
| ۶.۵ | phpstan بدون خطای جدید |
✅ | ۱۴ = baseline |
| ۶.۶ | npx tsc --noEmit و تستهای فرانت سبز |
✅ | ۶۳۰ تست (روی هاست؛ vitest داخل ddev اجرا نمیشود) |
| ۶.۷ | تستهای tenant سبز | ✅ | |
| ۶.۸ | docs/api/* بهروز |
✅ | |
| ۶.۹ | چکلیست UI کامل | ✅ | جز ۳.۱۱ |
| ۶.۱۰ | دو کلاینت دیگر بررسی شدند | ✅ | هر دو کلاینت با graphify بررسی شدند: هیچکدام quote را صدا نمیزنند. یادداشت قبلی: نمایش «مبلغ صفر» در nobat724_front دیده نشد — پکیج فعلاً فقط پنلمحور است |
| ۶.۱۱ | commit، سپس graphify update . |
✅ | دو کامیت جدا |
| ۶.۱۲ | موارد بهتعویق با دلیل | ✅ | سیاست بازگشت اعتبار → تسک ۱۳ · تست همزمانی واقعی (۴.۴) · بازبینی چشمی (۳.۱۱) |