# انحراف‌ها از نقشهٔ تسک‌ها هر ردیفی که در چک‌لیست‌ها ⚠️ مانده، اینجا با سه چیز ثبت است: **نقشه چه گفت**، **چه شد**، و **چرا**. هیچ‌کدام «انجام نشده» نیست؛ همه تصمیم‌اند. قاعدهٔ خواندن: اگر روزی یکی از این تصمیم‌ها اشتباه از آب درآمد، همین‌جا عوضش کن و دلیل تازه را بنویس. سندی که فقط توجیه جمع کند به درد نمی‌خورد. --- ## چیزهایی که **نباید** به نقشه برگردند هیچ‌کدام. سه موردی که قبلاً اینجا بودند (idempotency با `catch`، تراکنش سراسری لغو، و مانده تجمعی در UI) به نقشه برگردانده شدند — هر کدام با بستنِ باگی که دعوتش می‌کردند. جزئیات در کامیت `refactor: take the three risky rows back to the plan`. --- ## تسک ۰۴ — کاتالوگ سرویس | ردیف | نقشه | چه شد | چرا | |---|---|---|---| | ۴.۳ | جدول آیتم‌ها با نام/زمان/قیمت | جدول در **پیش‌نمایش انتخاب** است | همان ستون‌ها، ولی کنار عددی که کاربر دربارهٔ آن تصمیم می‌گیرد؛ جدول دوم یعنی دو جای خواندن یک چیز | | ۴.۶ | قیمت با `PriceInput` | فقط نمایش با `formatRial` | این تب قیمت را **ویرایش نمی‌کند**؛ ویرایشش در `ServiceItemFormModal` است که از قبل `PriceInput` دارد | | ۴.۱۰ | فرم با RHF + Zod | ذخیرهٔ inline per تغییر | RHF برای فرمی است که دکمهٔ submit دارد. این تب ندارد — هر تغییر مستقل و فوری ذخیره می‌شود | | ۴.۱۲ | خطاها زیر همان گروه | همهٔ خطاها زیر پیش‌نمایش | قرارداد پروژه «همهٔ خطاها با هم» است؛ نگاشت `group_uuid` به کارت، خطا را از کنار عددِ نتیجه دور می‌کرد | ## تسک ۰۵ — برنامهٔ نوبت | ردیف | نقشه | چه شد | چرا | |---|---|---|---| | ۱.۳ | پنج سرویس جدا | یک `AppointmentPlanBuilder` | پنج کلاس وقتی معنا دارد که هرکدام مصرف‌کنندهٔ مستقل داشته باشند؛ اینجا هر پنج فقط از `build()` صدا زده می‌شوند | | ۱.۴ | `build()` تابع خالص | از دیتابیس می‌خواند | تا تسک ۰۸ خالص بود؛ تسک ۰۹ قوانین را وصل کرد و قانون در دیتابیس است. خلوص را فقط با کپی‌کردن قوانین در حافظه می‌شد نگه داشت | | ۱.۸ | اشغال جدا از offset نمایشی | `setup/cleanup` روی `PlannedRequirement` | اثر عملی یکی است و تسک ۰۷ همان را می‌خواند؛ دو offset جدا یعنی دو عدد که باید هم‌زمان درست بمانند | | ۲.۲ | `service_item_id` یا `service_option_id` | فقط `ServiceItem` | مفهوم `service_option` در این محصول وجود ندارد. ستونی برای چیزی که نیست، تهی می‌ماند و بعداً کسی فکر می‌کند باگ است | | ۲.۳ | `fixed_minutes` یا `duration_share` | `duration_source` ∈ `fixed`\|`items` | «مدت از آیتم‌ها» همان نیاز واقعی است؛ سهم درصدی هیچ مصرف‌کننده‌ای نداشت | | ۲.۵ | `required_skills` به‌صورت JSON | یک `Skill` با FK | چند مهارت هم‌زمان نیاز واقعی نداشت، و FK اعتبار ارجاعی می‌دهد که JSON نمی‌دهد — مهارتِ حذف‌شده در JSON بی‌صدا می‌ماند | ## تسک ۰۹ — موتور قوانین رجیستری‌ها، شش موتور و `specificity` ذخیره‌شده **ساخته شدند** (کامیت `refactor(policy): build the registries and six engines`). آنچه مانده: | ردیف | نقشه | چه شد | چرا | |---|---|---|---| | ۱.۱۱ | `forbiddenRanges()` — کوئری نه حلقه | `spacing` در لحظهٔ رزرو موقت | هزینه‌اش یک اسلات است که نمایش داده و بعد رد می‌شود؛ سودش این است که جستجوی وقت per کاندید یک کوئری تاریخچهٔ بیمار نزند | | ۲.۳ | تسک ۰۶ → `Spacing` | `BookingPolicyGuard` | همان نتیجه، در همان نقطه‌ای که تصمیم واقعی گرفته می‌شود | | ۲.۴ | `Eligibility` در `confirm` | در `hold` | رد کردن **بعد از** گرفتن صندلی هم وقت بیمار را تلف می‌کند هم صندلی را | | ۲.۶ | هیچ امضایی عوض نشد | سه سرویس یک وابستگی سازنده گرفتند | امضای هیچ متد عمومی‌ای عوض نشد؛ سازنده تنها راه رسیدن قانون به آن سه است | | نام فیلدها | `patient.age` نقطه‌دار | `patient_age` | شرط‌های ذخیره‌شده روی قانون‌های **زندهٔ کلینیک‌ها** به نام‌های فعلی اشاره می‌کنند؛ تغییرشان مهاجرت داده است و نگاشت یک‌به‌یک هم ندارد | ## تسک ۱۰ — آزمایشگاه قانون | ردیف | نقشه | چه شد | چرا | |---|---|---|---| | ۱.۶ | فیلتر از داخل `condition` | از **دامنهٔ** قانون | شرط‌ها فیلد id ندارند که به کوئری ترجمه شوند؛ دامنه دقیقاً همان چیزی است که قابل ترجمه است | ## تسک ۱۱ — دفتر اعتبار | ردیف | نقشه | چه شد | چرا | |---|---|---|---| | ۴.۴ | مانده هرگز منفی نمی‌شود | با مصرف پشت‌سرهم تست شد | تست هم‌زمانی واقعی حالا هست (`testAConcurrentConsumeIsAbsorbedWithoutBurningTheRequest`) | ## تسک ۱۲ — دورهٔ درمان | ردیف | نقشه | چه شد | چرا | |---|---|---|---| | ۱.۱۲ | سخت‌گیرانه‌تر برنده در هر دو جهت | فقط `max(min)` | قانون `spacing` اثر «حداکثر» ندارد، پس `min(max)` چیزی برای انتخاب کردن ندارد | ## تسک ۱۳ — لغو و لیست انتظار | ردیف | نقشه | چه شد | چرا | |---|---|---|---| | ۳.۱ | `Service` + `Matcher` جدا | یک `WaitlistNotifier` | تطبیق یک کوئری در repository است؛ کلاس جدا فقط یک لایهٔ اسم‌گذاری می‌شد | ## تسک ۱۴ — رویدادها | ردیف | نقشه | چه شد | چرا | |---|---|---|---| | ۱.۱ | کلاس پایه + چهارده زیرکلاس | فهرست بستهٔ نام + یک entity | زیرکلاسِ خالی که فقط نام را در تایپ نگه می‌دارد، همان کاری را می‌کند که `const` — با چهارده فایل بیشتر | ## چندزبانگی (i18n) پروژه فایل i18n ندارد و همهٔ رشته‌ها inline اند. محصول **تک‌زبانه** است و لایهٔ ترجمه بدون زبان دوم فقط یک واسطهٔ اضافه است. **روزی که زبان دوم لازم شد**، ترتیبش این است: اول `assets/admin/lib/i18n.ts` با یک `t()` ساده و کلیدهای تخت؛ بعد صفحه‌به‌صفحه مهاجرت، نه یک‌باره. مهاجرت هم‌زمانِ هزاران رشته یعنی رگرسیون متنی که هیچ تستی نمی‌گیردش.