Files
hamed 021d0eb6b2 feat: implement cancellation policy, no-show tracking, and waitlist management
- Add implementation notes for cancellation and waitlist features.
- Create task documentation outlining goals, current status, and acceptance criteria for cancellation policy and resource utilization reporting.
- Establish architecture for domain events and outbox pattern to ensure reliable event publishing.
- Define database schema for domain events and necessary queries for resource utilization and plan accuracy reports.
- Implement detailed implementation notes covering edge cases, testing strategies, and documentation requirements.
2026-07-30 11:43:58 +03:30

6.3 KiB
Raw Permalink Blame History

تسک ۰۹ — موتور قوانین شش‌دسته‌ای

فاز: ۲ (قوانین) · وابستگی: ۰۵، ۰۶، ۰۸ · زمان: ۲۰-۲۴ ساعت


هدف

مستند بند ۸: شش دستهٔ قانون، هر کدام با نقطهٔ اجرای مشخص، شرط از فهرست بسته، اولویت‌دار، نسخه‌بندی‌شده، و بدون کد دلخواه.

دسته کِی اجرا می‌شود نقطهٔ اتصال در کد
انتخاب (selection) موقع انتخاب آیتم ServiceSelectionValidator (تسک ۰۴)
صلاحیت بیمار (eligibility) قبل از جستجوی وقت AvailabilityEngine::search ابتدا · BookingService::confirm مرحلهٔ ۳
منبع (resource) موقع ساخت برنامه AppointmentPlanBuilder مرحلهٔ ۷ (تسک ۰۵)
زمان (timing) موقع ساخت برنامه همان
فاصلهٔ زمانی (spacing) موقع جستجوی وقت AvailabilityEngine مرحلهٔ ۶
قیمت (pricing) بعد از نهایی شدن برنامه PricingEngine مرحلهٔ ۳ (تسک ۰۸)

همهٔ این قلاب‌ها در تسک‌های ۰۴ تا ۰۸ از قبل به‌صورت no-op گذاشته شده‌اند. این تسک پُرشان می‌کند.

وضعیت فعلی

DiscountRule + DiscountEngine تنها موتور قانون موجود است و الگوی درستی دارد:

// src/Discount/Entity/DiscountRule.php
public const TYPES = [TYPE_PATIENT_TAG, TYPE_INVOICE_AMOUNT, TYPE_SPECIFIC_PATIENT,
                      TYPE_OCCASION, TYPE_SERVICE, TYPE_VISIT_COUNT];   // enum بسته ✅
private int  $priority;        // ✅
private bool $combinable;      // ✅
private ?int $validFrom;       // ✅
private ?int $validTo;         // ✅

فقط دستهٔ «قیمت» را پوشش می‌دهد، نسخه‌بندی ندارد، و محیط آزمایش ندارد.

دامنه

هست:

  • Policy — قانون با دسته، شرط، نتیجه، اولویت، بازهٔ اعتبار، نسخه
  • PolicyVersionLog — تاریخچهٔ نسخه‌ها (قانون ویرایش نمی‌شود، نسخه می‌گیرد)
  • ConditionEvaluator — ارزیابی شرط از فهرست بسته
  • شش موتور دسته، هر کدام یک کلاس
  • حل تناقض: اولویت → اختصاصی‌بودن → قدمت
  • ترکیب اثرها طبق جدول مستند بند ۸
  • ثبت قانون‌های اعمال‌شده روی نوبت (applied_policy_ids تسک ۰۸)

نیست: فرم ساخت قانون و محیط آزمایش (تسک ۱۰ — همان تسک UI است). DiscountRule موجود مهاجرت نمی‌کند؛ کنار Policy دستهٔ pricing زندگی می‌کند (دلیل در implementation_notes).

Endpoint ها

متد مسیر توضیح
GET /api/v1/policies لیست با فیلتر دسته/وضعیت
POST /api/v1/policy ساخت (نسخهٔ ۱)
GET /api/v1/policy/{uuid} جزئیات + تاریخچهٔ نسخه
POST /api/v1/policy/{uuid}/version نسخهٔ جدید با تاریخ شروع
POST /api/v1/policy/{uuid}/activate | /deactivate
GET /api/v1/policy-schema فهرست بستهٔ فیلدها، عملگرها و اثرها per دسته

GET /policy-schema برای تسک ۱۰ حیاتی است: فرم ساخت قانون از همین schema ساخته می‌شود، نه از کد hard-coded در فرانت.

معیار پذیرش

  • موفق: قانون «خدمات دسته جراحی به جراح نیاز دارند» (دسته resource) → POST /appointment-plan/preview برای سرویسی در آن دسته، یک نیازمندی اضافه دارد.
  • موفق: قانون «حداقل ۷ روز از جلسهٔ قبلی» (دسته spacing) → جستجوی وقت برای بیماری که ۳ روز پیش جلسه داشته، روزهای زودتر از روز هفتم را برنمی‌گرداند.
  • موفق: قانون «بیمار زیر ۱۸ سال بدون رضایت والدین نمی‌شود» (دسته eligibility) → confirm با 422 و پیام قابل فهم رد می‌شود.
  • موفق: قانون «بیمار VIP ۱۰٪ تخفیف» (دسته pricing) → در price_snapshot_lines یک ردیف discount با نام قانون ظاهر می‌شود و شناسه+نسخه در applied_policy_ids ثبت می‌شود.
  • موفق (قانون پنجم مستند): بعد از ثبت نوبت، قانون نسخهٔ ۲ می‌گیرد → نوبت قبلی همان نسخهٔ ۱ را در applied_policy_ids دارد و فاکتورش تغییر نمی‌کند.
  • موفق: دو قانون «حداقل مدت» با مقادیر ۴۵ و ۶۰ دقیقه → بیشترین برنده است (۶۰).
  • موفق: دو قانون «اضافه کردن زمان» ۱۰ و ۵ دقیقه → جمع می‌شوند (۱۵).
  • موفق: یک قانون «ممنوعیت» → کل عملیات رد می‌شود، حتی اگر ده قانون مجازکننده باشند.
  • خطا: شرط با فیلد خارج از فهرست بسته → 422 با نام فیلد مجاز.
  • خطا: نتیجه با نوع اثر ناسازگار با دسته → 422.
  • ⚠️ مرزی: دو قانون با اولویت مساوی → اختصاصی‌تر (شعبه بر محیط، سرویس بر دسته) برنده.
  • ⚠️ مرزی: باز هم مساوی → قانون قدیمی‌تر برنده (مستند بند ۸).
  • ⚠️ مرزی: قانون با valid_from آینده → در محاسبهٔ امروز اعمال نمی‌شود.
  • ⚠️ مرزی: هیچ قانونی وجود ندارد → همه‌چیز مثل قبل کار می‌کند (تست سازگاری).
  • ⚠️ مرزی: قانون spacing باید قابل تبدیل به کوئری باشد؛ ارزیابی per-slot در PHP برای ۹۰ روز غیرقابل قبول است (مستند بند ۸ صریح).

خروجی

  • src/Policy/
  • docs/api/policy.md + سند معماری docs/architecture/policy-engine.md
  • پر کردن همهٔ قلاب‌های no-op تسک‌های ۰۴ تا ۰۸