- 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.
82 lines
4.6 KiB
Markdown
82 lines
4.6 KiB
Markdown
# تسک ۰۸ — لیست قیمت بازهدار و snapshot فاکتور
|
||
|
||
**فاز:** ۱ (هسته) · **وابستگی:** ۰۴، ۰۷ · **زمان:** ۱۲-۱۴ ساعت
|
||
|
||
---
|
||
|
||
## هدف
|
||
|
||
مستند بند ۱۲: قیمت یک لایهٔ جداست با زندگی خودش (تاریخ اعتبار، مالیات، بیمه، بیعانه) و
|
||
قانون پنجم: **تغییر قیمت هرگز نوبتهای ثبتشده را عوض نمیکند.**
|
||
|
||
## وضعیت فعلی
|
||
|
||
زنجیرهٔ قیمت امروز واقعاً وجود دارد و کار میکند:
|
||
|
||
```
|
||
ServiceItem.price_rials
|
||
→ Tariff (سالمحور: findForServiceYear)
|
||
→ TenantServiceCoverage / TenantInsurance (بیمهٔ پایه و تکمیلی)
|
||
→ DiscountRule + DiscountEngine
|
||
→ Invoice / InvoiceItem
|
||
→ Payment
|
||
```
|
||
|
||
روی نوبت هم `visit_price_rials`, `deposit_required`, `deposit_amount_rials`,
|
||
`insurance_base_id`, `insurance_supplementary_id` هست.
|
||
|
||
**دو شکاف:**
|
||
1. `Tariff` فقط **سال** دارد، بازهٔ دقیق تاریخ ندارد. تغییر تعرفه وسط سال قابل بیان نیست.
|
||
2. `visit_price_rials` یک عدد است. فاکتور **تفکیکشده** روی نوبت ذخیره نمیشود، پس بعد از
|
||
تغییر قیمت یا تخفیف، نمیشود گفت آن ۲٬۴۰۰٬۰۰۰ ریال از چه تشکیل شده بود.
|
||
|
||
## دامنه
|
||
|
||
**هست:**
|
||
- `PriceList` (بازهٔ تاریخ + شعبه) و `PriceListItem`
|
||
- `PriceSnapshot` — فاکتور تفکیکشدهٔ لحظهٔ ثبت نوبت
|
||
- `PricingEngine` — زنجیرهٔ هفتمرحلهای مستند بند ۱۲
|
||
- سیاست بیعانه per سرویس/محیط
|
||
- اتصال به `BookingService::confirm()` (قلاب مرحلهٔ ۶ تسک ۰۷)
|
||
|
||
**نیست:** پکیج و دفتر اعتبار (تسک ۱۱)، قوانین قیمت پیشرفته (تسک ۰۹ — `DiscountRule`
|
||
موجود فعلاً کافی است).
|
||
|
||
## Endpoint ها
|
||
|
||
| متد | مسیر | توضیح |
|
||
|---|---|---|
|
||
| GET/POST | `/api/v1/price-lists` | لیست قیمت با بازهٔ تاریخ |
|
||
| GET/PATCH/DELETE | `/api/v1/price-list/{uuid}` | |
|
||
| PUT | `/api/v1/price-list/{uuid}/items` | قیمت سرویسها و آیتمها |
|
||
| POST | `/api/v1/price-list/{uuid}/activate` | فعالسازی (بررسی تداخل بازه) |
|
||
| POST | `/api/v1/pricing/quote` | محاسبهٔ قیمت بدون ثبت |
|
||
| GET | `/api/v1/appointment/{uuid}/price-snapshot` | فاکتور تفکیکشدهٔ نوبت |
|
||
|
||
## معیار پذیرش
|
||
|
||
- ✅ موفق: لیست قیمت «نیمهٔ دوم ۱۴۰۵» با بازهٔ ۱۴۰۵/۰۷/۰۱ تا ۱۴۰۵/۱۲/۲۹ فعال میشود؛
|
||
`POST /pricing/quote` برای تاریخ مهر قیمت جدید و برای شهریور قیمت قبلی میدهد.
|
||
- ✅ موفق: `confirm` نوبت → `price_snapshots` یک ردیف با تفکیک کامل دارد:
|
||
قیمت پایه، جمع آیتمها، تخفیفهای اعمالشده (با نام و مبلغ هر کدام)، سهم بیمهٔ پایه،
|
||
سهم تکمیلی، مالیات، مبلغ نهایی، بیعانه.
|
||
- ✅ موفق (**قانون پنجم**): بعد از ثبت نوبت، قیمت سرویس دو برابر میشود →
|
||
`GET /appointment/{uuid}/price-snapshot` **همان اعداد قبلی** را میدهد.
|
||
- ✅ موفق: قیمت override شعبه (تسک ۰۴) بر لیست قیمت محیط اولویت دارد.
|
||
- ❌ خطا: دو لیست قیمت فعال با بازهٔ همپوشان برای یک شعبه → `422` هنگام `activate`.
|
||
- ❌ خطا: `quote` با سرویس محیط دیگر → `404`.
|
||
- ⚠️ مرزی: تاریخی که هیچ لیست قیمتی نمیپوشاند → fallback به `Tariff` سال، بعد به
|
||
`ServiceItem.price_rials`. هرگز صفر یا خطا.
|
||
- ⚠️ مرزی: تخفیف بیشتر از مبلغ → مبلغ نهایی صفر، نه منفی.
|
||
- ⚠️ مرزی: سقف جمع تخفیفها (`max_total_discount_percent` per محیط) → اعمال شود.
|
||
- ⚠️ مرزی: بیعانه بیشتر از مبلغ نهایی → `422` هنگام تنظیم سیاست.
|
||
- ⚠️ مرزی: نوبت بدون سرویس (نوبت ویزیت ساده در حالت `slot`) → snapshot با
|
||
`visit_price_rials` موجود ساخته شود، نه خالی.
|
||
|
||
## خروجی
|
||
|
||
- `src/Pricing/`
|
||
- `assets/admin/pages/PriceListsPage.tsx` + `PriceListFormPage.tsx`
|
||
- `docs/api/pricing.md`
|
||
- بهروزرسانی `docs/architecture/insurance-billing-system.md`
|