Files
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

5.5 KiB
Raw Permalink Blame History

تسک ۰۴ — کاتالوگ خدمات نسخهٔ ۲: گروه آیتم، دو نوع زمان، ناسازگاری

فاز: ۱ (هسته) · وابستگی: ۰۱ · زمان: ۱۴-۱۶ ساعت


هدف

مستند بند ۵ می‌گوید انتخاب آیتم خودش قانون دارد و این قوانین نباید به موتور قوانین سپرده شوند: «حتماً یک سطح انرژی، فقط یکی»، «بین ۱ تا ۸ دندان»، «بیکینی با فول‌بادی جمع نمی‌شود». و مهم‌تر: هر آیتم دو زمان دارد — «زمان تنها» و «زمان اضافه».

وضعیت فعلی

// 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 و requires
  • ServiceBranchOverride برای قیمت و مدت اختصاصی شعبه
  • 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=1valid=false با errors[{group_uuid, code: 'min_select', message: 'انتخاب حداقل یک مورد از «نواحی» الزامی است'}].
  • خطا: انتخاب ۹ آیتم از گروهی با max_select=8valid=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