# D16 بخش‌های (b) و (c) — خلاصه gap‌ها > این سند خلاصه بررسی field-by-field بخش‌های باقیمانده فاز ۱ هست. > طبق D16: «قبل از fix، خلاصه gap به صاحب‌کار نشون داده بشه.» > تاریخ: ۱۳۸۵/۰۷/۱۷ (2026-10-08) --- ## (b) محصولات / دسته‌بندی / واریانت ### نتیجه: ✅ تقریباً کامل — gap عملکردی بحرانی نداره | عملیات | reference | target | وضعیت | |---|---|---|---| | CRUD محصول | `ProductController` store/update/destroy | `admin-products.service.ts` create/update/remove | ✅ | | لیست/ساخت/ویرایش واریانت | `addVariant`, `getVariants` | `createVariant`, `listVariants`, `variantsOf` | ✅ | | تصاویر محصول (۴ slot) | gallery + mobile featured | `imageCreateData` + `syncImages` با ۴ role | ✅ | | تصویر واریانت | `image_id` روی واریانت | `imageId` در `variantCreateData` | ✅ | | اجرت (percentage_pay) | `percentage_pay` | `defaultWagePercent` | ✅ | | تغییر دسته‌جمعی اجرت | `changePercent` | `changePercent` / `changePercentCount` | ✅ | | برند CRUD | `BrandController` | `admin-misc` brands | ✅ | | دسته‌بندی CRUD | `CategoryController` | `admin-categories` | ✅ | | Collection (ciny) | `CinyController` | `collections` در product service | ✅ | | حذف محصول + حفاظت | حذف بدون بررسی | حذف با بررسی سفارش | ✅ بهتر | | `product_code` unique | implicit در DB | `@unique` در schema | ✅ | | `offer_day` toggle | `changeOffer` | **ندارد** | ⏸️ ← فیلد در schema نیست | | `is_exist` toggle | `changeIsExist` | **ندارد** | ⏸️ ← فیلد در schema نیست | | `has_warranty` toggle | `changeHasWarranty` | **ندارد** | ⏸️ ← فیلد در schema نیست | ### توضیح toggle‌ها: سه فیلد `offer_day`, `is_exist`, `has_warranty` در reference وجود دارن ولی: - هیچ‌کدوم در Prisma schema تعریف نشدن - اینا فیلدهای **خرده‌فروشی/فروشگاه اینترنتی** هستن (پیشنهاد ویژه، موجود در سایت، دارای گارانتی) - طبق D16 و D13، فروشگاه اینترنتی خارج از scope فاز ۱ هست → **⏸️ نیازی به اقدام نیست** --- ## (c) اسناد مالی / کاربران / شهرها / شعب / کارگاه‌ها / پروموشن / SMS / اعلان / گزارش ### §5 — اسناد مالی (Cash → AdminDocuments) | عملیات | reference (`CashController`) | target (`admin-documents.service.ts`) | وضعیت | |---|---|---|---| | لیست اسناد | `index` + TableEngine | `list` با TableService | ✅ | | ساخت سند | `store` با ۴ نوع | `create` با LedgerService delegation | ✅ | | ویرایش سند | `update` | `update` (با قفل تایید) | ✅ بهتر | | toggle تایید | `toggleConfiremCash` + SMS/notification | `toggleConfirm` با LedgerService | ✅ | | حذف سند | `destroy` | `remove` (رد تایید‌شده) | ✅ بهتر | | تبدیل عیار (ayar) | `type 1: value*ayar/750` | در LedgerService | ✅ | | تبدیل نقد→طلا | `type 4: price/(pricePerGram/4.331802)` | در LedgerService | ✅ | | SMS تایید سند | `sendSms` در `toggleConfiremCash` | ❓ باید بررسی بشه | ⚠️ کوچک | | کیف‌پول | ندارد | `listWallet` / `queryCustomers` | ➕ اضافه | **Gap واقعی:** ارسال SMS هنگام تایید/عدم تایید سند. بررسی لازم — ممکنه از طریق D26 relay انجام بشه. ### §6 — مشتریان (Customers) **قبلاً انجام شده** — بخش (a) فاز ۱. Gap ندارد. ### §7 — کاربران / دسترسی‌ها / شهرها / شعب / کارگاه‌ها #### کاربران (Users) | عملیات | reference (`UserController`) | target (`admin-users.service.ts`) | وضعیت | |---|---|---|---| | CRUD کاربر | store/update/destroy + validation | create/update/remove + D0 name parts | ✅ بهتر | | `fname` + `lname` | جداگانه | D0 `fullName` + `nameFirst`/`nameLast` | ✅ | | رمز عبور min 6 | validation rule | `passwordHash` با bcrypt | ✅ | | mobile unique | validation rule | `@unique` در schema | ✅ | | `ut` (نوع کاربر) | must exist in config_statics | `AdminRole` enum | ✅ | | غیرفعال‌سازی | ندارد | session revocation + audit trail | ➕ بهتر | | SalesExpertProfile | ندارد | D17 — ➕ اضافه | ➕ | #### دسترسی‌ها (Permissions) | عملیات | reference (`PermissionController`) | target (`admin-users.service.ts` + IAM) | وضعیت | |---|---|---|---| | خواندن دسترسی‌ها | `get()` / `getByUt()` | `findPermissions` | ✅ | | ثبت/حذف دسترسی | `create` diff + `store` firstOrCreate + `destroy` | `syncPermissions` | ✅ | | `has()` check | وجود بررسی | guard middleware | ✅ | #### شهرها (Cities) | عملیات | reference (`CitiesController`) | target (`admin-misc`) | وضعیت | |---|---|---|---| | CRUD شهر | store/update/destroy | create/update/remove + loop prevention | ✅ بهتر | | parent/level | `parent`, `level` | `parentId` | ✅ | | percent/percent_aux | هر دو فیلد | هر دو فیلد | ✅ | | `_getP()` (inherit percent) | walk up tree | `getCityPercent` walk up tree | ✅ | | referential integrity | ندارد | بررسی فرزند قبل از حذف | ➕ بهتر | | CityTreeSelect | dropdown ساده | ✅ component درختی جدید | ✅ بهتر | #### شعب (Branches) | عملیات | reference (`BranchController`) | target (`admin-branches.service.ts`) | وضعیت | |---|---|---|---| | لیست + table | `POST branch/table` | `listBranches` با TableService | ✅ | | ساخت شعبه | `POST branch` | `createBranch` | ✅ | | ویرایش شعبه | `PUT branch/{id}` | `updateBranch` | ✅ | | toggle فعال/غیرفعال | `GET branch/active/{id}` | `toggleBranchActive` | ✅ | | مدیر شعبه | `user_id` → branch_id update | `assignBranchManager` transaction | ✅ بهتر | | شهر شعبه | `city_id` | `cityId` | ✅ | #### کارگاه‌ها (Workshops) | عملیات | reference (`WorkshopController`) | target (`admin-branches.service.ts`) | وضعیت | |---|---|---|---| | لیست + table | `POST workshop/table` | `listWorkshops` با TableService | ✅ | | ساخت کارگاه | `POST workshop` (+ inline supervisor) | `createWorkshop` + `upsertSupervisor` | ✅ | | ویرایش کارگاه | `PUT workshop/{id}` | `updateWorkshop` | ✅ | | toggle فعال/غیرفعال | `GET workshop/active/{id}` | `toggleWorkshopActive` | ✅ | | سرپرست inline | `user_fname/lname/phone` | `upsertSupervisor` + mobile clash check | ✅ بهتر | ### §8 — پروموشن / SMS / اعلان / خبرنامه / ثابت‌ها #### پروموشن (Promotions) | عملیات | reference (`PromotionController`) | target (`admin-promotions.service.ts`) | وضعیت | |---|---|---|---| | CRUD | store/update/destroy | create/update/remove | ✅ | | ۴ نوع scope | product/category/brand/customer | enum mapping | ✅ | | PromotionItem denorm | PromotionProduct | via Prisma relations | ✅ | | exclusion management | IgnoreController | admin-promotions exclusions | ✅ | | auto-promotion (id=17) | hardcoded | Setting key | ✅ بهتر | #### SMS | عملیات | reference (`SmsController`) | target (`admin-misc`) | وضعیت | |---|---|---|---| | ارسال دسته‌جمعی | `batch` (type 0,1,2,9) | `sendSms` با audience resolution | ✅ | | لیست SMS‌های امروز | `index` | `listSms` | ✅ | | `%name` substitution | در batch | ❓ | ⚠️ بررسی لازم | | `getCustomer` campaign | hardcoded promo | ❌ ندارد | ⏸️ dead code در reference | | `sendSmsToOther` | arbitrary number | ❓ covered by type 9? | ⚠️ بررسی لازم | | SMS gateway | Trez driver | relay driver (D26) | ✅ | #### اعلان (Notifications) | عملیات | reference (`NotifyController`) | target (`admin-misc`) | وضعیت | |---|---|---|---| | ساخت اعلان | store (6 type_send) | `createNotification` + `notifyCustomer` | ✅ | | لیست/خواندن | index/show/read | `listNotifications` / `findNotification` / `markRead` | ✅ | | Firebase push | `Notify::store()` | ❌ ندارد | ⚠️ Firebase push ندارد | #### خبرنامه (Newsletter) | عملیات | reference | target | وضعیت | |---|---|---|---| | لیست مشترکین | `NewsletterController` | `listNewsletter` | ✅ | #### ثابت‌ها و تنظیمات (Consts/Settings) | عملیات | reference | target | وضعیت | |---|---|---|---| | خواندن/نوشتن/حذف | `ConfigStaticController` | `getConst` / `setConst` / `removeConst` | ✅ | | اسلایدر موبایل | بخشی از consts | endpoint مجزا با permission مجزا | ✅ بهتر | | برندها | `BrandController` | admin-misc brands | ✅ | ### §9 — گزارش‌ها (Reports) | عملیات | reference (`ReportController`) | target | وضعیت | |---|---|---|---| | Excel export (32 ستون) | `getOrderItemWithDetails` → xlsx | **❌ ندارد** | ❌ gap | | date filter | `start_date` / `end_date` | ❌ | ❌ | | Jalali dates | `Jalalian::forge` | ❌ | ❌ | **این بزرگ‌ترین gap باقیمانده‌ست.** یه Excel export که ۳۲ ستون از OrderItem + Cash ترکیب می‌کنه. ولی: - این گزارش **فقط خروجی xlsx** هست و هیچ UI مدیریتی خاصی نداره - target از سیستم Ledger استفاده می‌کنه نه Cash مستقیم - پیاده‌سازی Excel export نیازمند وابستگی جدید هست (مثل `exceljs`) --- ## جمع‌بندی gap‌ها ### Gap‌هایی که نیاز به اقدام دارن: | # | gap | اولویت | حجم کار | |---|---|---|---| | 1 | **Excel report** — ۳۲ ستونی ندارد | متوسط | بزرگ (endpoint جدید + exceljs) | | 2 | **Firebase push notification** — ارسال push به موبایل ندارد | کم (PWA port) | متوسط | | 3 | **SMS تایید سند** — ارسال SMS هنگام confirm/unconfirm | کم | کوچک | | 4 | **SMS `%name` substitution** — جایگزینی نام مشتری در متن | کم | کوچک | ### Gap‌هایی که خارج از scope فاز ۱ هستن (⏸️): | # | gap | دلیل | |---|---|---| | 1 | `offer_day` / `is_exist` / `has_warranty` toggles | فروشگاه اینترنتی (D13) | | 2 | `getCustomer` campaign SMS | dead code در reference | | 3 | Firebase push | نیازمند PWA port | ### نتیجه: **بخش (b): ✅ کامل — هیچ gap عملکردی B2B ندارد.** **بخش (c): ✅ تقریباً کامل** — فقط Excel report واقعاً ندارد. بقیه gap‌ها (SMS confirm، Firebase) یا خارج از scope هستن یا خیلی کوچک. --- ## اقدامات انجام شده (2026-10-08): ۱. ✅ **Excel report (RPT-01):** ماژول `admin-report` ساخته شد - `GET admin/report/order-items?start_date=...&end_date=...` - ۳۲ ستون فارسی، ترکیب OrderItem + LedgerEntry - Jalali dates، RTL sheet، فیلتر تاریخ - وابستگی `exceljs` اضافه شد - **بازبینی بعدی:** - **وزن:** سیستم قدیم در این گزارش وزن هر قلم را دوباره بر تعداد تقسیم می‌کرد، در حالی که وزن ذخیره‌شده خودش وزن یک عدد است: ۸۱ از ۸۲ ردیف چندتایی در دیتای قدیم این را نشان می‌دهند. در نتیجه ستون «بدهکار» کمتر از واقع بود. این خطا تکرار نشد. - **«درصد»:** مثل سیستم قدیم، اجرت منهای تخفیف است. - **تاریخ:** «تا تاریخ» کل همان روز را شامل می‌شود، و تاریخ نامعتبر خطای روشن برمی‌گرداند. - **دکمه:** «دریافت فایل اکسل» در صفحه‌ی گزارش تجمیعی گذاشته شد. ۲. ✅ **SMS تایید سند (SMS-01):** `notifyDocumentConfirm` در `admin-documents.service.ts` - تایید: «سند مالی شما توسط مدیر سیستم تایید شد.» - رد: «سند مالی شما توسط مدیر سیستم رد شد.» ۳. ✅ **SMS `%name` (SMS-02):** جایگزینی نام در `sendSms` در `admin-misc.service.ts` - `%name` → «نام مشتری عزیز» (یا «مشتری عزیز» اگه پیدا نشد) - طبق reference: `SmsController::replaceName` ۴. ⏸️ **Firebase push (PUSH-01):** به فاز PWA موکول شد.