Files
tehran_taj/docs/parity/D16-BC-GAP-SUMMARY.md
s.esmaeiliandClaude Opus 5.5 2d74f53d0a feat(admin): D16 (b)(c) — Excel order-item report, document-confirm SMS, %name in bulk SMS
Finished from a parallel session that expired (owner: «بساز»).

- RPT-01: GET admin/report/order-items — legacy report/order_item's
  32-column Excel (order items, one row per unit, plus financial
  documents), RTL, Jalali dates. The «گزارش تجمیعی» screen gets
  «دریافت فایل اکسل». Review fixes:
  - weight: computedWeightGrams is already one unit's weight; legacy
    divided it by qty again, under-reporting «بدهکار» on multi-unit
    lines. Not repeated.
  - «درصد»: the final percent (wage − discount), as legacy.
  - dates: the end date covers its whole day; a bad date is a 400.
- SMS-01: confirming or rejecting a financial document texts the customer,
  as legacy CashController::toggleConfiremCash.
- SMS-02: bulk SMS substitutes %name per recipient and is actually sent
  through the configured gateway (D26 relay), no longer only logged.
- Docs: 01-ADMIN-MATRIX and D16-BC-GAP-SUMMARY.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-08 16:34:45 +03:30

224 lines
13 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 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 موکول شد.