- 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.
144 lines
6.1 KiB
Markdown
144 lines
6.1 KiB
Markdown
# دیتابیس — تسک ۰۸
|
|
|
|
## `price_lists`
|
|
|
|
| ستون | نوع | توضیح |
|
|
|---|---|---|
|
|
| `id` | INT PK AI | |
|
|
| `uuid` | VARCHAR(36) UNIQUE | |
|
|
| `entity_type` / `entity_id` | VARCHAR(10) / INT NOT NULL | |
|
|
| `branch_id` | INT NULL | NULL = همهٔ شعب محیط |
|
|
| `name` | VARCHAR(150) NOT NULL | «نیمهٔ دوم ۱۴۰۵» |
|
|
| `valid_from` | INT NOT NULL | نیمهشب روز شروع |
|
|
| `valid_to` | INT NULL | NULL = بیپایان |
|
|
| `status` | VARCHAR(10) NOT NULL DEFAULT 'draft' | `draft`\|`active`\|`archived` |
|
|
| `created_at`/`updated_at` | INT NOT NULL | |
|
|
|
|
```sql
|
|
KEY idx_price_lists_tenant (entity_type, entity_id, status, valid_from)
|
|
KEY idx_price_lists_branch (branch_id, status, valid_from)
|
|
```
|
|
|
|
تداخل بازه در سطح اپلیکیشن بررسی میشود (`activate`)، نه DB — MariaDB محدودیت بازهای ندارد
|
|
و راه سطل زمانی تسک ۰۷ اینجا بیمورد است چون تعداد لیستها کم و تغییرشان نادر است.
|
|
|
|
## `price_list_items`
|
|
|
|
```sql
|
|
CREATE TABLE price_list_items (
|
|
id INT PRIMARY KEY AUTO_INCREMENT,
|
|
price_list_id INT NOT NULL,
|
|
service_item_id INT NULL, -- قیمت سرویس
|
|
service_option_id INT NULL, -- قیمت آیتم
|
|
price_rials INT NOT NULL,
|
|
UNIQUE KEY uniq_pli_service (price_list_id, service_item_id),
|
|
UNIQUE KEY uniq_pli_option (price_list_id, service_option_id),
|
|
KEY idx_pli_list (price_list_id),
|
|
CONSTRAINT fk_pli_list FOREIGN KEY (price_list_id) REFERENCES price_lists(id) ON DELETE CASCADE,
|
|
CONSTRAINT fk_pli_service FOREIGN KEY (service_item_id) REFERENCES service_items(id) ON DELETE CASCADE,
|
|
CONSTRAINT fk_pli_option FOREIGN KEY (service_option_id) REFERENCES service_options(id) ON DELETE CASCADE
|
|
);
|
|
```
|
|
|
|
دقیقاً یکی از `service_item_id` / `service_option_id` غیر-NULL (قید اپلیکیشنی).
|
|
فرزند aggregate با ریشهٔ `PriceList`.
|
|
|
|
## `price_snapshots`
|
|
|
|
| ستون | نوع | توضیح |
|
|
|---|---|---|
|
|
| `id` | INT PK AI | |
|
|
| `uuid` | VARCHAR(36) UNIQUE | |
|
|
| `entity_type` / `entity_id` | VARCHAR(10) / INT NOT NULL | |
|
|
| `appointment_id` | INT NOT NULL UNIQUE | FK ON DELETE CASCADE — یک snapshot per نوبت |
|
|
| `base_rials` | INT NOT NULL | |
|
|
| `options_rials` | INT NOT NULL DEFAULT 0 | |
|
|
| `discount_rials` | INT NOT NULL DEFAULT 0 | |
|
|
| `insurance_base_rials` | INT NOT NULL DEFAULT 0 | |
|
|
| `insurance_supplementary_rials` | INT NOT NULL DEFAULT 0 | |
|
|
| `tax_rials` | INT NOT NULL DEFAULT 0 | |
|
|
| `final_rials` | INT NOT NULL | |
|
|
| `deposit_rials` | INT NOT NULL DEFAULT 0 | |
|
|
| `applied_policy_ids` | JSON NULL | `[{id, version}]` |
|
|
| `price_list_id` | INT NULL | FK SET NULL — کدام لیست مبنا بود |
|
|
| `created_at` | INT NOT NULL | |
|
|
|
|
```sql
|
|
UNIQUE KEY uniq_snapshot_appointment (appointment_id)
|
|
KEY idx_snapshot_tenant (entity_type, entity_id, created_at)
|
|
```
|
|
|
|
`UNIQUE` روی `appointment_id`: یک نوبت یک فاکتور رزرو دارد. `reschedule` نوبت **جدید**
|
|
میسازد (تسک ۰۷) پس snapshot جدید هم میگیرد و قدیمی سالم میماند.
|
|
|
|
⚠️ همهٔ مبالغ `INT` ریال. `DECIMAL` یا `FLOAT` ننویس — بقیهٔ پروژه (`price_rials`,
|
|
`visit_price_rials`, `deposit_amount_rials`) همه `INT` ریالاند و قاطی کردن دو نوع یعنی
|
|
خطای گردکردن در جمع فاکتور.
|
|
|
|
## `price_snapshot_lines`
|
|
|
|
```sql
|
|
CREATE TABLE price_snapshot_lines (
|
|
id INT PRIMARY KEY AUTO_INCREMENT,
|
|
snapshot_id INT NOT NULL,
|
|
kind VARCHAR(15) NOT NULL, -- base|option|discount|insurance|tax|package
|
|
label VARCHAR(200) NOT NULL, -- snapshot متنی — نام لحظهٔ ثبت
|
|
amount_rials INT NOT NULL, -- منفی برای تخفیف و سهم بیمه
|
|
source_type VARCHAR(20) NULL, -- service|option|discount_rule|policy|insurance
|
|
source_id INT NULL, -- بدون FK — منبع ممکن است حذف شود
|
|
sort_order SMALLINT NOT NULL DEFAULT 0,
|
|
KEY idx_psl_snapshot (snapshot_id, sort_order),
|
|
CONSTRAINT fk_psl_snapshot FOREIGN KEY (snapshot_id) REFERENCES price_snapshots(id) ON DELETE CASCADE
|
|
);
|
|
```
|
|
|
|
`source_id` **بدون FK** عمدی: قانون تخفیف ممکن است فردا حذف شود ولی فاکتور دیروز باید
|
|
همانطور بماند. `label` هم به همین دلیل کپی متنی است، نه JOIN.
|
|
|
|
## `deposit_policies`
|
|
|
|
```sql
|
|
CREATE TABLE deposit_policies (
|
|
id INT PRIMARY KEY AUTO_INCREMENT,
|
|
uuid VARCHAR(36) NOT NULL UNIQUE,
|
|
entity_type VARCHAR(10) NOT NULL,
|
|
entity_id INT NOT NULL,
|
|
service_item_id INT NULL, -- NULL = پیشفرض محیط
|
|
mode VARCHAR(10) NOT NULL, -- none|fixed|percent
|
|
value INT NOT NULL DEFAULT 0,
|
|
min_rials INT NULL,
|
|
max_rials INT NULL,
|
|
active TINYINT(1) NOT NULL DEFAULT 1,
|
|
created_at INT NOT NULL,
|
|
updated_at INT NOT NULL,
|
|
UNIQUE KEY uniq_deposit_scope (entity_type, entity_id, service_item_id),
|
|
KEY idx_deposit_tenant (entity_type, entity_id, active)
|
|
);
|
|
```
|
|
|
|
## تغییر جدول موجود
|
|
|
|
هیچ. `appointments.visit_price_rials`, `deposit_required`, `deposit_amount_rials`,
|
|
`insurance_base_id`, `insurance_supplementary_id` همه استفاده میشوند و کافیاند.
|
|
|
|
`Tariff` هم دستنخورده میماند و در زنجیرهٔ `PriceResolver` سطر ۴ است.
|
|
|
|
## Migration
|
|
|
|
```bash
|
|
ddev exec php bin/console doctrine:migrations:diff --no-interaction
|
|
ddev exec php bin/console doctrine:migrations:migrate --no-interaction
|
|
ddev exec php bin/console app:pricing:backfill-snapshots --force
|
|
```
|
|
|
|
`app:pricing:backfill-snapshots` برای نوبتهای `confirmed` آینده که snapshot ندارند، یکی
|
|
از `visit_price_rials` موجود میسازد (یک ردیف `base`). بدون آن، صفحهٔ فاکتور برای
|
|
نوبتهای موجود خالی است.
|
|
|
|
## طبقهبندی tenant
|
|
|
|
| جدول | وضعیت |
|
|
|---|---|
|
|
| `price_lists`, `price_snapshots`, `deposit_policies` | جفت tenant |
|
|
| `price_list_items`, `price_snapshot_lines` | `AGGREGATE_CHILDREN` |
|