Files
clinicpro/docs/new_feture/taskes/task-11-package-credit-ledger/task.md
T
hamed 021d0eb6b2 feat: implement cancellation policy, no-show tracking, and waitlist management
- 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.
2026-07-30 11:43:58 +03:30

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`