Files
clinicpro/docs/new_feture/taskes/task-00-service-mode-completion/database.md
T
hamed 158dcb58aa feat: implement service mode completion for nobat724_front
- Add task for completing service mode in clinicpro with detailed objectives and acceptance criteria.
- Create architecture documentation for task 00b, outlining involved components and necessary changes.
- Develop checklist for task 00b to ensure all requirements are met.
- Document implementation notes for task 00b, emphasizing API contract checks and design system adherence.
- Update task documentation for task 00b, specifying goals and current issues with service mode.
2026-07-30 11:56:08 +03:30

5.8 KiB

دیتابیس — تسک ۰۰

تغییر appointments — دو ستون تهی‌پذیر

ALTER TABLE appointments
  ADD COLUMN service_total_minutes SMALLINT NULL,
  ADD COLUMN service_buffer_minutes SMALLINT NULL;
ستون معنی چرا لازم است
service_total_minutes مدت محاسبه‌شدهٔ ترکیب سرویس‌ها در لحظهٔ ثبت slot_end - slot_start عدد را دارد ولی نمی‌گوید عمدی بود یا دستی؛ و برای نوبت رزرو (که slot_start = slot_end) هیچ‌جا مدت را نگه نمی‌داریم
service_buffer_minutes buffer_minutes مؤثر در لحظهٔ ثبت تغییر بافر در تنظیمات نباید معنای نوبت‌های ثبت‌شده را عوض کند

هر دو تهی‌پذیر و هر دو در حالت اسلاتی NULL می‌مانند. هیچ ستون موجودی حذف، تغییر نوع یا تغییر معنا نمی‌دهد.

slot_start و slot_end و active_slot_key و is_reserve دست‌نخورده. خط سرخ.

چرا نه یک ستون JSON

وسوسه: یک service_meta JSON با همه‌چیز. رد شد چون تسک ۱۴ (گزارش دقت برنامه) روی plan_total_minutes تجمعی می‌زند و JSON را نمی‌تواند AVG کند. دو ستون SMALLINT ارزان‌ترند و تسک ۰۷ ستون plan_total_minutes را کنارشان اضافه می‌کند (اسم متفاوت، معنی متفاوت: آن یکی مدت برنامهٔ چندبخشی است).

ایندکس

هیچ ایندکس جدیدی. idx_appointments_doctor_slot و idx_appointments_tenant_slot موجود همهٔ کوئری‌های این تسک را پوشش می‌دهند.

appointment_service_items — بدون تغییر schema

جدول واسط ManyToMany موجود. تنها تغییر، رفتاری است:

// Appointment — متد جدید، بدون دست زدن به متدهای موجود
/** جایگزینی کامل سرویس‌ها؛ serviceItem تکی هم با اولی هم‌گام می‌شود. */
public function replaceServiceItems(array $items): self
{
    $this->serviceItems->clear();
    foreach ($items as $item) {
        if (!$this->serviceItems->contains($item)) { $this->serviceItems->add($item); }
    }
    $this->serviceItem = $items[0] ?? null;    // ← سازگاری با مصرف‌کنندهٔ تکی
    $this->updatedAt   = time();
    return $this;
}

/** @return string[] uuid سرویس‌های فعلی، به ترتیب */
public function currentServiceUuids(): array
{
    $uuids = array_map(fn($i) => $i->getUuid(), $this->serviceItems->toArray());
    if ($uuids === [] && $this->serviceItem !== null) { $uuids = [$this->serviceItem->getUuid()]; }
    return $uuids;
}

هم‌گام‌سازی serviceItem تکی اجباری است: AppointmentsPage، ReserveAppointmentsPage، nobat724_front و clinic-pro-tauri هر چهار روی service_item تکی خوانده‌اند. رهاکردنش یعنی نوبت با سرویس‌های جدید ولی نام سرویس قدیمی در لیست.

کدهای خطای جدید

در src/Shared/Constant/ErrorCodes.php:

public const ERR_SERVICE_DURATION_MISMATCH = 'ERR_APPOINTMENT_010';
// پیام: مدت این نوبت با مجموع مدت سرویس‌های انتخابی نمی‌خواند

public const ERR_WRONG_BOOKING_MODE = 'ERR_APPOINTMENT_011';
// پیام: این عملیات با روش نوبت‌دهی این محیط سازگار نیست

شمارهٔ بعدی دامنهٔ APPOINTMENT را از خود فایل بگیر، این دو عدد حدسی‌اند. هر دو کد در تسک‌های ۰۶ و ۰۷ هم استفاده می‌شوند، پس نامشان عمومی است نه مخصوص این تسک.

Migration

ddev exec php bin/console doctrine:migrations:diff --no-interaction
ddev exec php bin/console doctrine:migrations:migrate --no-interaction

backfill

ddev exec php bin/console app:appointment:backfill-service-duration          # dry-run
ddev exec php bin/console app:appointment:backfill-service-duration --force

برای هر نوبت pending/confirmed آیندهٔ یک محیط سرویسی که service_total_minutes ندارد:

service_total_minutes = (slot_end - slot_start) / 60
service_buffer_minutes = buffer_minutes فعلیِ همان برنامه

مقدار از خودِ نوبت گرفته می‌شود، نه از مدت سرویس‌ها — چون نوبت موجود ممکن است با مدت دستی ثبت شده باشد و بازمحاسبه یعنی تغییر گذشته.

نوبت‌های اسلاتی و نوبت‌های گذشته رد می‌شوند. idempotent.

fixture های تست خط سرخ

tests/Appointment/fixtures/slot-mode-contract.json           # پاسخ appointment-slots
tests/Appointment/fixtures/month-availability-contract.json  # پاسخ month-availability
tests/Appointment/fixtures/slot-calculator-signatures.php    # امضای متدهای عمومی

این سه فایل بعد از این تسک read-only اند. هیچ تسکی اجازهٔ به‌روزرسانی‌شان را ندارد. اگر تستی قرمز شد، کد باید برگردد نه fixture. این جمله را در بالای هر سه فایل به‌عنوان کامنت بنویس.

fixture ها با تاریخ ثابت ساخته می‌شوند (2026-01-05 مثلاً)، نه time() — وگرنه فردا قرمز.

طبقه‌بندی tenant

هیچ entity جدیدی. appointments از قبل جفت tenant دارد. TenantSchemaCoverageTest باید بدون تغییر سبز بماند.