- 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.
5.5 KiB
تسک ۰۴ — کاتالوگ خدمات نسخهٔ ۲: گروه آیتم، دو نوع زمان، ناسازگاری
فاز: ۱ (هسته) · وابستگی: ۰۱ · زمان: ۱۴-۱۶ ساعت
هدف
مستند بند ۵ میگوید انتخاب آیتم خودش قانون دارد و این قوانین نباید به موتور قوانین سپرده شوند: «حتماً یک سطح انرژی، فقط یکی»، «بین ۱ تا ۸ دندان»، «بیکینی با فولبادی جمع نمیشود». و مهمتر: هر آیتم دو زمان دارد — «زمان تنها» و «زمان اضافه».
وضعیت فعلی
// src/ClinicService/Entity/ServiceItem.php
private ?int $durationMinutes = null; // یک عدد، تخت
private int $priceRials = 0;
private bool $bookable = false;
و در AppointmentController::serviceSlots():
$totalMinutes += $duration; // ← جمع ساده؛ همان فرمولی که مستند ردش میکند
نتیجه: بیمار که «صورت + بیکینی» میخواهد، ۱۵+۱۵=۳۰ دقیقه ظرفیت میگیرد در حالی که واقعیت ۱۵+۸=۲۳ دقیقه است. هفت دقیقه ضرب در روزی ۲۰ نوبت = یک ساعت ظرفیت هدررفته در روز.
همچنین ServiceSection تکسطحی است و دستهبندی درختی مستند را ندارد.
دامنه
هست:
ServiceCategoryدرختی (جدا ازServiceSectionموجود که «بخش کلینیک» است)ItemGroupباmin_select/max_select- روی
ServiceItem:solo_duration_minutesوadditional_duration_minutes ServiceItemRelationبرایincompatible_withوrequiresServiceBranchOverrideبرای قیمت و مدت اختصاصی شعبهsession_countروی سرویس (تکجلسه یا دورهای — پروتکل کاملش تسک ۱۲)ServiceSelectionValidator— اعتبارسنجی انتخاب کاربر پیش از هر محاسبهDurationCalculator— محاسبهٔ درست مدت با دو نوع زمان
نیست: بخشهای نوبت و نیازمندی منبع (تسک ۰۵)، اعمال روی جستجوی وقت (تسک ۰۶).
Endpoint ها
| متد | مسیر | توضیح |
|---|---|---|
| GET | /api/v1/service-categories/tree |
درخت دستهبندی |
| POST/PATCH/DELETE | /api/v1/service-category[/{uuid}] |
|
| GET/POST | /api/v1/service-item/{uuid}/groups |
گروههای آیتم یک سرویس |
| PATCH/DELETE | /api/v1/item-group/{uuid} |
|
| PUT | /api/v1/item-group/{uuid}/items |
جایگزینی کامل آیتمهای گروه |
| PUT | /api/v1/service-item/{uuid}/relations |
ناسازگاری و پیشنیاز |
| PUT | /api/v1/service-item/{uuid}/branch-overrides |
قیمت/مدت per شعبه |
| POST | /api/v1/service-selection/validate |
اعتبارسنجی انتخاب + مدت و قیمت محاسبهشده |
POST /service-selection/validate مهمترین endpoint این تسک است: سایت عمومی و پنل هر دو
پیش از رفتن به مرحلهٔ انتخاب زمان، آن را صدا میزنند.
معیار پذیرش
- ✅ موفق: سرویس «لیزر» با گروه «نواحی» (
min=1, max=8) و آیتمهای صورت (تنها ۱۵، اضافه ۸) و بیکینی (تنها ۱۲، اضافه ۸). انتخاب هر دو →POST /service-selection/validateبرمیگرداندtotal_duration_minutes = 23(اولین آیتم زمان تنها، بقیه زمان اضافه) وvalid = true. - ✅ موفق: انتخاب فقط بیکینی →
total_duration_minutes = 12. - ✅ موفق: شعبهٔ مرکزی برای همین سرویس
price_rialsبالاتر دارد → باbranch_uuidمرکزی، قیمت override اعمال میشود. - ❌ خطا: انتخاب صفر آیتم از گروهی با
min_select=1→valid=falseباerrors[{group_uuid, code: 'min_select', message: 'انتخاب حداقل یک مورد از «نواحی» الزامی است'}]. - ❌ خطا: انتخاب ۹ آیتم از گروهی با
max_select=8→valid=falseبا کدmax_select. - ❌ خطا: انتخاب دو آیتم ناسازگار →
valid=falseبا کدincompatibleو نام هر دو آیتم. - ❌ خطا: انتخاب آیتمی که پیشنیازش انتخاب نشده →
valid=falseبا کدmissing_prerequisite. - ❌ خطا: uuid آیتم از محیط دیگر →
404(نه ۴۲۲ — نباید وجودش لو برود). - ⚠️ مرزی:
max_select = nullیعنی نامحدود. - ⚠️ مرزی: گروه با
min_select = 0یعنی اختیاری. - ⚠️ مرزی: آیتم بدون
additional_duration_minutes→ ازsolo_duration_minutesاستفاده شود (سازگاری با دادههای موجود که فقط یکduration_minutesدارند). - ⚠️ مرزی: حلقهٔ پیشنیاز (الف پیشنیاز ب، ب پیشنیاز الف) →
422هنگام ثبت رابطه.
خروجی
- توسعهٔ
src/ClinicService/(بدون شکستن endpoint های موجود) assets/admin/pages/ServiceDetailPage.tsxتوسعه: تب «گروهها و آیتمها»docs/api/clinic-services.mdبهروزرسانی- migration + backfill:
duration_minutesموجود →solo_duration_minutes