# تسک ۰۸ — لیست قیمت بازه‌دار و 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`
