Files
clinicpro/docs/new_feture/taskes/task-10-policy-admin-sandbox/implementation_notes.md
T
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.6 KiB
Raw Blame History

نکات پیاده‌سازی — تسک ۱۰

۱. rollback اجباری، سه لایه

public function simulate(Policy $policy, int $size): SimulationReport
{
    $this->em->beginTransaction();
    try {
        return $this->runInternal($policy, $size);
    } finally {
        $this->em->rollback();      // ← حتی اگر استثنا پرت شود
        $this->em->clear();         // ← identity map پاک شود
    }
}

finally نه catch: اگر شبیه‌سازی استثنا داد، هنوز باید rollback شود. clear() بدون آن، entity های تغییرکردهٔ درون تراکنش در حافظه می‌مانند و اولین flush در ادامهٔ همان request آن‌ها را ثبت می‌کند — یک باگ که پیدا کردنش روزها می‌برد.

ثبت PolicySimulationRun بعد از این بلوک و در تراکنش خودش.

۲. تست «هیچ چیزی ننوشت» — با شمارش، نه با اعتماد

$before = $this->countRows(['appointments','price_snapshots','resource_occupancy',
                            'resource_occupancy_slot','appointment_segments']);
$this->simulator->simulate($policy, 50);
$after = $this->countRows([...]);
self::assertSame($before, $after, 'شبیه‌سازی نباید هیچ ردیفی بنویسد');

این تست ارزشمندترین تست این تسک است. هر بار که کسی PolicySimulator را تغییر دهد، همین تست جلوی فاجعه را می‌گیرد.

۳. evaluateIsolated روی همهٔ شش موتور

اضافه کردن این متد به شش موتور تسک ۰۹، تغییر اینترفیس است. پس در تسک ۰۹ اضافه شود، نه اینجا — وگرنه شش کلاس دوباره ویرایش می‌شوند.

اگر تسک ۰۹ تمام شده و این متد نیست، اضافه‌اش کن ولی به‌عنوان یک متد در همان اینترفیس موجود، نه یک اینترفیس جدید.

۴. فرم از schema — بدون استثنا

// ❌ اولین وسوسه
const FIELDS = ['patient.age', 'patient.tags', 'service.category_path'];

// ✅
const { data: schema } = useQuery({ queryKey: ['policy-schema'], staleTime: 300_000 });

اگر فیلدها را در فرانت hard-code کنی، هر فیلد جدید در FieldRegistry نیاز به تغییر فرانت دارد و بعد از دو ماه دو فهرست ناهمگام داریم. staleTime بلند اشکالی ندارد — schema تقریباً هرگز عوض نمی‌شود.

۵. شدت none هم هشدار است

قانونی که روی هیچ نوبتی اثر نداشت، دو حالت دارد:

  • شرطش هرگز true نمی‌شود (اشتباه نوشته شده)
  • نمونهٔ ۵۰ نوبتی آن حالت را نداشت (شاید درست است)

پیام باید هر دو را بگوید:

«این قانون روی هیچ‌کدام از ۵۰ نوبت نمونه اثر نداشت. یا شرط آن هرگز برقرار نمی‌شود، یا این حالت در نوبت‌های اخیر پیش نیامده. فعال‌سازی مجاز است.»

فعال‌سازی را نبند — کلینیک جدید هیچ نوبتی ندارد و باید بتواند قانون بسازد.

۶. الگوها باید واقعاً کار کنند

هر الگو در PolicyTemplateRegistry باید یک تست داشته باشد که آن را می‌سازد، شبیه‌سازی می‌کند و فعال می‌کند. الگویی که conditions نامعتبر تولید کند، بدترین حالت است: کاربر فرم آماده را پر می‌کند و 422 می‌گیرد.

// tests/Policy/PolicyTemplateTest.php
/** @dataProvider templates */
public function testTemplateProducesValidPolicy(string $key): void
{
    $policy = $this->registry->build($key, $this->sampleInputs($key));
    $this->validator->assertValid($policy);      // همان اعتبارسنجی POST /policy
}

۷. edge case ها

حالت رفتار درست
محیط بدون هیچ نوبت گزارش خالی، severity=none، activate مجاز
قانون deny که همه را رد می‌کند severity=high، فعال‌سازی با تأیید دوباره
simulate نسخهٔ ۱، بعد نسخهٔ ۲ ساخته شد activate نسخهٔ ۲ → 422
simulate دو بار پشت‌سرهم آخری معیار است؛ قبلی می‌ماند
قانون فعال که دوباره simulate می‌شود مجاز — کاربر می‌خواهد اثرش را ببیند
نوبت نمونه‌ای که سرویسش حذف شده از نمونه حذف شود، در sample_size نیاید
قانون pricing روی نوبتی بدون snapshot آن ردیف رد شود با علت no_snapshot
sample_size بزرگ‌تر از ۵۰ سقف ۵۰ — درخواست بیشتر 422

۸. تست

tests/Policy/PolicySimulatorTest.php               ← ⭐
  - هیچ ردیفی نوشته نمی‌شود (شمارش قبل/بعد)
  - رفتار درست وقتی evaluateIsolated استثنا می‌دهد (rollback + clear)
  - گزارش فقط نوبت‌های تحت تأثیر را دارد
tests/Policy/SimulationSamplerTest.php
  - فیلتر شعبه و سرویس از شرط قانون استخراج می‌شود
  - فقط confirmed/completed
  - سقف ۵۰
tests/Policy/PolicyActivationGuardTest.php         ← ⭐
  - activate بدون simulate → 422
  - activate با simulate نسخهٔ قبلی → 422
  - activate با simulate نسخهٔ جاری → 200
  - محیط بدون نوبت: simulate خالی → activate مجاز
tests/Policy/PolicyTemplateTest.php
  - هر الگو قانون معتبر تولید می‌کند (dataProvider روی همهٔ الگوها)
tests/Policy/SeverityTest.php
  - ۰٪ → none · ۱۰٪ → low · ۴۰٪ → medium · ۸۰٪ → high
assets/admin/pages/PolicyFormPage.test.tsx
  - فیلدها از schema می‌آیند (mock schema با فیلد ساختگی → در UI ظاهر شود)
  - عملگرهای نامعتبر برای نوع فیلد نمایش داده نمی‌شوند

۹. مستندات

docs/api/policy.md را با simulate و policy-templates و شرط جدید activate به‌روز کن. در docs/architecture/policy-engine.md یک بخش «چرا آزمایش اجباری است» اضافه کن با ارجاع به ریسک دوم مستند بند ۱۷.