- 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.
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 باید بدون تغییر سبز بماند.