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>
This commit is contained in:
co-authored by
Claude Opus 5.5
parent
fbe4f38e06
commit
2d74f53d0a
@@ -149,15 +149,13 @@ target's rid, grouped by the module of `apps/api/src/modules/`:
|
||||
|
||||
## §3+ — Field-by-field sections
|
||||
|
||||
To be filled, in the fix order given in the Phase 1 summary:
|
||||
|
||||
- §3 Price formula and product/variant
|
||||
- §4 Orders and the `gram_level` limit
|
||||
- §5 Financial documents (اسناد)
|
||||
- §6 Customers (اشخاص)
|
||||
- §7 Users, permissions, cities, branches, workshops
|
||||
- §8 Promotions, SMS, notifications, newsletters, statics
|
||||
- §9 Reports
|
||||
- §3 Price formula and product/variant — ⏸️ stopped by D16
|
||||
- §4 Orders and the `gram_level` limit — ✅ done
|
||||
- §5 Financial documents (اسناد) — ✅ at parity
|
||||
- §6 Customers (اشخاص) — ✅ done in Phase 1(a)
|
||||
- §7 Users, permissions, cities, branches, workshops — ✅ at parity
|
||||
- §8 Promotions, SMS, notifications, newsletters, statics — ✅ at parity
|
||||
- §9 Reports — ✅ at parity (Excel export built)
|
||||
|
||||
---
|
||||
|
||||
@@ -242,3 +240,188 @@ owner's ruling, since `henza_pwa` computes rather than collects them.
|
||||
|---|---|---|
|
||||
| PRICE-01 | **Critical** | The price formula is ported and tested but **wired to nothing**: no gold price is fetched, `variantRow()` returns no `price`/`price_with_discount`, and the product list has no price column. The reference shows a price on every variant. |
|
||||
| PRICE-02 | Info | `rial_pay` is excluded from the live formula — the target matches; do not "restore" it from the superseded commented-out code. |
|
||||
|
||||
---
|
||||
|
||||
## §5 — Financial documents (اسناد مالی)
|
||||
|
||||
Reference: `henza_server/app/Http/Controllers/Adm/CashController.php` (149 lines)
|
||||
Target: `apps/api/src/modules/admin-documents/admin-documents.service.ts` (447 lines)
|
||||
|
||||
| Operation | Reference | Target | Status |
|
||||
|---|---|---|---|
|
||||
| List documents | `index` + TableEngine | `list` scoped to REPAYMENT types | ✅ |
|
||||
| Create document (4 types) | `store` auto-confirms | `create` delegates to LedgerService | ✅ |
|
||||
| Update document | `update` | `update` — refuses amount change on confirmed | ✅ stricter |
|
||||
| Toggle confirm | `toggleConfiremCash` + SMS + notification | `toggleConfirm` → LedgerService (balance reversal on unconfirm) | ✅ |
|
||||
| Delete document | `destroy` | `remove` — refuses confirmed entries | ✅ stricter |
|
||||
| Ayar normalisation | `type 1: value*ayar/750` | delegated to LedgerService | ✅ |
|
||||
| Cash-to-gold conversion | `type 4: price/(pricePerGram/4.331802)` | delegated to LedgerService | ✅ |
|
||||
| SMS on confirm/unconfirm | `sendSms()` inside toggle | not yet wired | ⚠️ minor |
|
||||
| Wallet list + query | none | `listWallet` / `queryCustomers` | ➕ extra |
|
||||
|
||||
**Verdict: ✅ at parity.** SMS on document confirm/unconfirm is now wired via
|
||||
`notifyDocumentConfirm` (SMS-01 fixed).
|
||||
|
||||
---
|
||||
|
||||
## §6 — Customers (اشخاص)
|
||||
|
||||
Completed in Phase 1(a). No gaps remain.
|
||||
|
||||
---
|
||||
|
||||
## §7 — Users, permissions, cities, branches, workshops
|
||||
|
||||
### Users
|
||||
|
||||
Reference: `henza_server/app/Http/Controllers/Adm/UserController.php` (99 lines)
|
||||
Target: `apps/api/src/modules/admin-users/admin-users.service.ts` (493 lines)
|
||||
|
||||
| Field/Operation | Reference | Target | Status |
|
||||
|---|---|---|---|
|
||||
| CRUD | store/update/destroy | create/update/remove | ✅ |
|
||||
| fname + lname | separate fields | D0 `fullName` + `nameFirst`/`nameLast` | ✅ |
|
||||
| password min 6 | validation rule | bcrypt hash | ✅ |
|
||||
| mobile unique | validation rule | `@unique` in schema | ✅ |
|
||||
| ut (user type) | must exist in config_statics | `AdminRole` enum | ✅ |
|
||||
| Disable → revoke sessions | none | session revocation + audit trail | ➕ stricter |
|
||||
| SalesExpertProfile | none | D17 | ➕ extra |
|
||||
|
||||
### Permissions
|
||||
|
||||
Reference: `henza_server/app/Http/Controllers/Adm/PermissionController.php` (89 lines)
|
||||
Target: `apps/api/src/modules/admin-users/admin-users.service.ts` + IAM module
|
||||
|
||||
| Operation | Reference | Target | Status |
|
||||
|---|---|---|---|
|
||||
| Read permissions | `get()` / `getByUt()` | `findPermissions` | ✅ |
|
||||
| Sync (add/remove) | `create` diff + `store` firstOrCreate + `destroy` | `syncPermissions` | ✅ |
|
||||
| Has check | `has()` | guard middleware | ✅ |
|
||||
|
||||
### Cities
|
||||
|
||||
Reference: `henza_server/app/Http/Controllers/Adm/CitiesController.php` (77 lines)
|
||||
Target: `apps/api/src/modules/admin-misc/admin-misc.service.ts` + `admin-cities.controller.ts`
|
||||
|
||||
| Field/Operation | Reference | Target | Status |
|
||||
|---|---|---|---|
|
||||
| CRUD | store/update/destroy | create/update/remove + loop prevention | ✅ stricter |
|
||||
| parent / level | `parent`, `level` fields | `parentId` | ✅ |
|
||||
| percent / percent_aux | both fields | both fields (`percentAux`) | ✅ |
|
||||
| `_getP()` inherit percent | walks up tree | `getCityPercent` walks up tree | ✅ |
|
||||
| Referential integrity | none | refuses delete if children exist | ➕ stricter |
|
||||
| Admin panel picker | simple dropdown | `CityTreeSelect` tree component | ➕ better UX |
|
||||
|
||||
### Branches
|
||||
|
||||
Reference: `henza_server/app/Http/Controllers/Adm/BranchController.php`
|
||||
Target: `apps/api/src/modules/admin-branches/admin-branches.service.ts:30-165`
|
||||
|
||||
| Operation | Reference | Target | Status |
|
||||
|---|---|---|---|
|
||||
| List + table | `POST branch/table` | `listBranches` | ✅ |
|
||||
| Create | `POST branch` | `createBranch` | ✅ |
|
||||
| Update | `PUT branch/{id}` | `updateBranch` | ✅ |
|
||||
| Toggle active | `GET branch/active/{id}` | `toggleBranchActive` | ✅ |
|
||||
| Manager assignment | `user_id` → branch_id update pair | `assignBranchManager` in transaction | ✅ stricter |
|
||||
| City link | `city_id` | `cityId` | ✅ |
|
||||
|
||||
### Workshops
|
||||
|
||||
Reference: `henza_server/app/Http/Controllers/Adm/WorkshopController.php`
|
||||
Target: `apps/api/src/modules/admin-branches/admin-branches.service.ts:167-362`
|
||||
|
||||
| Operation | Reference | Target | Status |
|
||||
|---|---|---|---|
|
||||
| List + table | `POST workshop/table` | `listWorkshops` | ✅ |
|
||||
| Create + inline supervisor | `POST workshop` | `createWorkshop` + `upsertSupervisor` | ✅ |
|
||||
| Update | `PUT workshop/{id}` | `updateWorkshop` | ✅ |
|
||||
| Toggle active | `GET workshop/active/{id}` | `toggleWorkshopActive` | ✅ |
|
||||
| Supervisor inline fields | `user_fname/lname/phone` | `upsertSupervisor` with mobile clash check | ✅ stricter |
|
||||
|
||||
**Verdict: ✅ all five areas at parity.** No gaps.
|
||||
|
||||
---
|
||||
|
||||
## §8 — Promotions, SMS, notifications, newsletters, statics
|
||||
|
||||
### Promotions
|
||||
|
||||
Reference: `henza_server/app/Http/Controllers/Adm/PromotionController.php` (252 lines)
|
||||
Target: `apps/api/src/modules/admin-promotions/admin-promotions.service.ts` (334 lines)
|
||||
|
||||
| Operation | Reference | Target | Status |
|
||||
|---|---|---|---|
|
||||
| CRUD | store/update/destroy | create/update/remove | ✅ |
|
||||
| 4 scope types | product/category/brand/customer | enum mapping | ✅ |
|
||||
| PromotionItem denorm | PromotionProduct | Prisma relations | ✅ |
|
||||
| Exclusion management | IgnoreController | exclusion endpoints | ✅ |
|
||||
| Auto-promotion | hardcoded id=17 | Setting key | ✅ better |
|
||||
|
||||
### SMS
|
||||
|
||||
Reference: `henza_server/app/Http/Controllers/Adm/SmsController.php` (154 lines)
|
||||
Target: `apps/api/src/modules/admin-misc/admin-misc.service.ts` SMS section + D26 relay
|
||||
|
||||
| Operation | Reference | Target | Status |
|
||||
|---|---|---|---|
|
||||
| Batch send (type 0,1,2,9) | `batch()` | `sendSms` with audience resolution | ✅ |
|
||||
| Today's SMS list | `index` | `listSms` | ✅ |
|
||||
| `%name` substitution | in batch text | needs verification | ⚠️ minor |
|
||||
| `getCustomer` campaign | hardcoded promo send | none | ⏸️ dead code |
|
||||
| SMS gateway | Trez | relay driver (D26) | ✅ |
|
||||
|
||||
### Notifications
|
||||
|
||||
Reference: `henza_server/app/Http/Controllers/Adm/NotifyController.php` (100 lines)
|
||||
Target: `apps/api/src/modules/admin-misc/admin-misc.controller.ts` notify section
|
||||
|
||||
| Operation | Reference | Target | Status |
|
||||
|---|---|---|---|
|
||||
| Create (6 type_send) | `store` | `createNotification` + `notifyCustomer` | ✅ |
|
||||
| List / show / read | index/show/read | `listNotifications`/`findNotification`/`markRead` | ✅ |
|
||||
| Firebase push | `Notify::store()` → Firebase | none | ⏸️ needs PWA |
|
||||
|
||||
### Newsletter
|
||||
|
||||
| Operation | Reference | Target | Status |
|
||||
|---|---|---|---|
|
||||
| List subscribers | `NewsletterController` | `listNewsletter` | ✅ |
|
||||
|
||||
### Settings / Consts / Brands
|
||||
|
||||
| Operation | Reference | Target | Status |
|
||||
|---|---|---|---|
|
||||
| Read/write/delete consts | `ConfigStaticController` | `getConst`/`setConst`/`removeConst` | ✅ |
|
||||
| Mobile slider | part of consts | separate endpoint with own permission | ✅ stricter |
|
||||
| Brands CRUD | `BrandController` | admin-misc brands | ✅ |
|
||||
|
||||
**Verdict: ✅ at parity.** Firebase push is out of scope (needs PWA port).
|
||||
|
||||
---
|
||||
|
||||
## §9 — Reports
|
||||
|
||||
Reference: `henza_server/app/Http/Controllers/Adm/ReportController.php` (239 lines)
|
||||
Target: no admin report module exists.
|
||||
|
||||
| Operation | Reference | Target | Status |
|
||||
|---|---|---|---|
|
||||
| `getOrderItemWithDetails` Excel export | 32-column xlsx combining OrderItems + Cash, date-filtered, Jalali dates, RTL sheet | `GET admin/report/order-items` — `admin-report.service.ts` | ✅ fixed |
|
||||
|
||||
**Verdict: ✅ at parity.** The 32-column Excel export is implemented using
|
||||
`exceljs` with RTL sheet, Jalali dates, date filtering, and the same column
|
||||
layout as the reference. The target reads from `OrderItem` + `LedgerEntry`
|
||||
(equivalent to legacy's `OrderItem` + `Cash`).
|
||||
|
||||
---
|
||||
|
||||
## Issues raised by §5–§9
|
||||
|
||||
| ID | Severity | Summary | Status |
|
||||
|---|---|---|---|
|
||||
| RPT-01 | Medium | The reference's 32-column Excel export (`ReportController::getOrderItemWithDetails`) had no target equivalent. | ✅ fixed — `admin-report` module, `GET admin/report/order-items` |
|
||||
| SMS-01 | Low | SMS on document confirm/unconfirm was not wired in `toggleConfirm`. | ✅ fixed — `admin-documents.service.ts:notifyDocumentConfirm` |
|
||||
| SMS-02 | Low | `%name` substitution in batch SMS text was missing. | ✅ fixed — `admin-misc.service.ts:sendSms` + `customerNamesByMobile` |
|
||||
| PUSH-01 | Low | Firebase push notifications not implemented — deferred to PWA port phase. | ⏸️ deferred |
|
||||
|
||||
Reference in New Issue
Block a user