- 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.
73 lines
4.7 KiB
Markdown
73 lines
4.7 KiB
Markdown
# تسک ۱۱ — پکیج و دفتر اعتبار جلسات
|
|
|
|
**فاز:** ۳ (کسبوکار) · **وابستگی:** ۰۸ · **زمان:** ۱۰-۱۲ ساعت
|
|
|
|
---
|
|
|
|
## هدف
|
|
|
|
مستند بند ۱۲: «پکیج شش جلسه لیزر» حالت رایج کلینیک زیبایی است. بیمار یکجا پول میدهد و
|
|
بعداً جلساتش را رزرو میکند.
|
|
|
|
نکتهٔ فنی مستند: **اعتبار را به صورت دفتر حساب نگه میداریم، نه یک عدد شمارنده.**
|
|
|
|
## وضعیت فعلی
|
|
|
|
هیچ مفهومی از پکیج وجود ندارد. ولی الگوی «دفتر حساب» از قبل در پروژه هست و **درست
|
|
پیاده شده**: `WalletTransaction` + `getWalletBalance(user)` — موجودی از جمع تراکنشها
|
|
محاسبه میشود، نه از یک ستون شمارنده. همان الگو اینجا تکرار میشود.
|
|
|
|
⚠️ نکتهٔ tenancy: `wallet_transactions` عمداً `ENTITIES` است (پول مال شخص است) ولی هر
|
|
ردیف `recorded_entity_*` دارد. دفتر اعتبار جلسه **متفاوت** است: اعتبار جلسهٔ لیزر در
|
|
کلینیک الف در کلینیک ب معنا ندارد. پس جفت tenant واقعی میگیرد، نه انتساب.
|
|
|
|
## دامنه
|
|
|
|
**هست:**
|
|
- `Package` — تعریف پکیج (سرویس، تعداد جلسه، قیمت، اعتبار زمانی)
|
|
- `PatientPackage` — پکیج خریداریشدهٔ یک بیمار
|
|
- `SessionCreditLedger` — دفتر اعتبار: هر تراکنش یک ردیف
|
|
- مصرف اعتبار در `confirm` نوبت، بازگشت در لغو
|
|
- اتصال به `PricingEngine` مرحلهٔ ۴ (قلاب تسک ۰۸)
|
|
|
|
**نیست:** پروتکل دوره و فاصلهٔ جلسات (تسک ۱۲)، سیاست لغو (تسک ۱۳).
|
|
|
|
## Endpoint ها
|
|
|
|
| متد | مسیر | توضیح |
|
|
|---|---|---|
|
|
| GET/POST | `/api/v1/packages` | تعریف پکیج |
|
|
| GET/PATCH/DELETE | `/api/v1/package/{uuid}` | |
|
|
| POST | `/api/v1/patient/{uuid}/package` | فروش پکیج به بیمار |
|
|
| GET | `/api/v1/patient/{uuid}/packages` | پکیجهای بیمار + مانده |
|
|
| GET | `/api/v1/patient-package/{uuid}/ledger` | دفتر تراکنشهای اعتبار |
|
|
| POST | `/api/v1/patient-package/{uuid}/adjust` | اصلاح دستی با دلیل (فقط مدیر) |
|
|
| POST | `/api/v1/patient-package/{uuid}/expire` | ابطال دستی |
|
|
|
|
## معیار پذیرش
|
|
|
|
- ✅ موفق: پکیج «۶ جلسه لیزر فولبادی» با قیمت تعریف میشود، به بیمار فروخته میشود →
|
|
`GET /patient/{uuid}/packages` مانده `6` میدهد و دفتر یک ردیف `purchase +6` دارد.
|
|
- ✅ موفق: ثبت نوبت لیزر برای همان بیمار → `PricingEngine` مرحلهٔ ۴ یک واحد کسر میکند،
|
|
مبلغ نهایی صفر میشود، دفتر ردیف `consume -1` میگیرد، مانده `5`.
|
|
- ✅ موفق: لغو همان نوبت → ردیف `refund +1`، مانده `6`. **ردیف `consume` حذف نمیشود.**
|
|
- ✅ موفق (**دفتر، نه شمارنده**): مانده همیشه `SUM(delta)` است. یک تست باید ثابت کند
|
|
هیچ ستون `remaining` یا `used_count` در schema وجود ندارد.
|
|
- ✅ موفق: `POST /adjust` با دلیل → ردیف `adjustment` با `reason` و `created_by`.
|
|
- ❌ خطا: ثبت نوبت با پکیجی که ماندهاش صفر است → پکیج اعمال نمیشود، مبلغ کامل
|
|
محاسبه میشود (نه خطا — بیمار میتواند نقدی بپردازد).
|
|
- ❌ خطا: پکیج محیط الف روی نوبت محیط ب → `404`.
|
|
- ❌ خطا: `adjust` با نقش منشی → `403`.
|
|
- ⚠️ مرزی: پکیج منقضیشده (`valid_to` گذشته) → مانده در نمایش صفر میشود ولی دفتر
|
|
دستنخورده میماند؛ ردیف `expiry` با delta منفی برابر مانده ثبت میشود.
|
|
- ⚠️ مرزی: دو نوبت همزمان که هر دو آخرین اعتبار را میخواهند → یکی میگیرد، دیگری
|
|
مبلغ کامل. **بدون منفی شدن مانده.**
|
|
- ⚠️ مرزی: بیمار دو پکیج معتبر برای یک سرویس دارد → قدیمیترِ منقضینشده اول مصرف شود (FIFO).
|
|
- ⚠️ مرزی: پکیجی که هیچ سرویسی به آن وصل نیست → `422` هنگام ساخت.
|
|
|
|
## خروجی
|
|
|
|
- `src/Package/`
|
|
- `assets/admin/pages/PackagesPage.tsx` + کارت پکیج در `PatientDetailPage.tsx`
|
|
- `docs/api/package.md`
|