Files
clinicpro/docs/new_feture/taskes/task-09-policy-engine/database.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

127 lines
5.1 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.
# دیتابیس — تسک ۰۹
## `policies`
| ستون | نوع | توضیح |
|---|---|---|
| `id` | INT PK AI | |
| `uuid` | VARCHAR(36) UNIQUE | |
| `entity_type` / `entity_id` | VARCHAR(10) / INT NOT NULL | |
| `branch_id` | INT NULL | NULL = همهٔ شعب — FK ON DELETE CASCADE |
| `name` | VARCHAR(200) NOT NULL | «خدمات جراحی به جراح نیاز دارند» |
| `category` | VARCHAR(20) NOT NULL | یکی از شش دسته |
| `priority` | SMALLINT NOT NULL DEFAULT 0 | |
| `specificity` | SMALLINT NOT NULL DEFAULT 0 | محاسبه‌شده هنگام ذخیره |
| `version` | SMALLINT NOT NULL DEFAULT 1 | |
| `valid_from` | INT NULL | |
| `valid_to` | INT NULL | |
| `active` | TINYINT(1) NOT NULL DEFAULT 0 | **پیش‌فرض غیرفعال** |
| `conditions` | JSON NOT NULL | |
| `effects` | JSON NOT NULL | |
| `created_at`/`updated_at` | INT NOT NULL | |
```sql
KEY idx_policies_lookup (entity_type, entity_id, category, active, valid_from)
KEY idx_policies_branch (branch_id, category, active)
```
`idx_policies_lookup` کوئری داغ است: «قوانین فعال دستهٔ X این محیط که امروز معتبرند».
با ۶ دسته و معمولاً < ۵۰ قانون per محیط، این کوئری همیشه ارزان است — و باید بماند.
اگر روزی قوانین به هزاران رسیدند، کش per (محیط، دسته) اضافه شود، نه ایندکس پیچیده‌تر.
## `policy_version_log`
```sql
CREATE TABLE policy_version_log (
id INT PRIMARY KEY AUTO_INCREMENT,
policy_id INT NOT NULL,
version SMALLINT NOT NULL,
snapshot JSON NOT NULL, -- کل قانون در آن نسخه، نه diff
valid_from INT NULL,
valid_to INT NULL,
changed_by INT NULL, -- FK users ON DELETE SET NULL
changed_at INT NOT NULL,
UNIQUE KEY uniq_policy_version (policy_id, version),
KEY idx_pvl_policy (policy_id, version),
CONSTRAINT fk_pvl_policy FOREIGN KEY (policy_id) REFERENCES policies(id) ON DELETE CASCADE
);
```
`snapshot` کامل است، نه diff: بازسازی نسخهٔ ۳ از یک قانون که الان نسخهٔ ۹ است، باید یک
`SELECT` باشد. حجم ناچیز (JSON چند کیلوبایتی × چند ده نسخه).
## هیچ تغییری در `discount_rules`
جدول و entity موجود دست‌نخورده می‌مانند. `PricingPolicyEngine` **هر دو** را می‌خواند:
```php
$effects = array_merge(
$this->discountEngine->evaluate($ctx), // DiscountRule موجود
$this->pricingPolicies->evaluate($ctx), // Policy دستهٔ pricing
);
$combined = $this->combiner->combine($effects); // ترکیب واحد
```
دلیل عدم مهاجرت در implementation_notes بند ۱.
## `applied_policy_ids` — تسک ۰۸
ستون از قبل در `price_snapshots` هست. قرارداد مقدارش اینجا تعیین می‌شود:
```json
[
{ "source": "policy", "id": 12, "version": 3, "name": "تخفیف VIP" },
{ "source": "discount_rule", "id": 5, "version": 1, "name": "تخفیف تولد" }
]
```
`name` کپی متنی — همان دلیل `price_snapshot_lines.label`: قانون ممکن است حذف شود.
## ذخیرهٔ قوانین اعمال‌شده روی خودِ نوبت
قوانین غیرقیمتی هم باید ثبت شوند (مستند: «هر نوبت فهرست قانون‌هایی که رویش اعمال شده را
ذخیره می‌کند»)، ولی `price_snapshots` جای قوانین منبع و زمان نیست.
```sql
ALTER TABLE appointments
ADD COLUMN applied_policies JSON NULL;
```
همان قرارداد بالا. یک ستون JSON کافی است — هیچ کوئری‌ای روی آن زده نمی‌شود، فقط برای
آدیت و پاسخ به «چرا این نوبت ۷۵ دقیقه شد؟» خوانده می‌شود.
## Migration
```bash
ddev exec php bin/console doctrine:migrations:diff --no-interaction
ddev exec php bin/console doctrine:migrations:migrate --no-interaction
```
بدون backfill. هیچ قانونی از قبل وجود ندارد و `DiscountRule` ها سر جایشان می‌مانند.
## نمونهٔ داده برای تست
```bash
ddev exec php bin/console app:policy:seed-examples --tenant=clinic:12 --force
```
پنج قانون نمونه (یکی از هر دسته جز `selection`)، همه با `active=false` تا کلینیک اول
آزمایششان کند:
```
resource | خدمات جراحی به جراح نیاز دارند
timing | حداقل مدت درمان پیچیده یک ساعت است
spacing | حداقل ۲۱ روز از جلسهٔ قبلی لیزر
eligibility | بیمار زیر ۱۸ سال بدون رضایت والدین نمی‌شود
pricing | بیمار VIP ده درصد تخفیف
```
این نمونه‌ها هم مستندات زنده‌اند و هم داده تست تسک ۱۰.
## طبقه‌بندی tenant
| جدول | وضعیت |
|---|---|
| `policies` | جفت tenant |
| `policy_version_log` | `AGGREGATE_CHILDREN` → ریشه `Policy` |