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

131 lines
6.6 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# نکات پیاده‌سازی — تسک ۱۰
## ۱. rollback اجباری، سه لایه
```php
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` **بعد** از این بلوک و در تراکنش خودش.
## ۲. تست «هیچ چیزی ننوشت» — با شمارش، نه با اعتماد
```php
$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 — بدون استثنا
```tsx
// ❌ اولین وسوسه
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` می‌گیرد.
```php
// 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` یک بخش «چرا آزمایش اجباری است»
اضافه کن با ارجاع به ریسک دوم مستند بند ۱۷.