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