fix(money,orders): wage/gram-debt parity, atomic document confirm, expert edit, made-to-order, QA fixes

- Rial wage and gram-debt arithmetic matched to the reference; order corrections keep debt in sync
- Document confirm/un-confirm is a compare-and-swap (no double-applied cache under concurrency)
- Settlement document and expert inputs validated (400 instead of 500)
- Expert: edit / activate-deactivate own customers, adjust raw weights only
- Made-to-order flow in the app, PreOrder.quantity migration
- Order grids: customer column, sort keys; panel select search; pre-order quantity save
- Rebuilt release artifacts

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
This commit is contained in:
mahmood
2026-10-29 18:09:02 +03:30
co-authored by Claude Sonnet 5.5
parent 959a272227
commit 6514b05d18
321 changed files with 6984 additions and 2170 deletions
+1 -1
View File
@@ -305,7 +305,7 @@ Target: `apps/api/src/modules/admin-documents/admin-documents.service.ts` (447 l
| Operation | Reference | Target | Status |
|---|---|---|---|
| List documents | `index` + TableEngine | `list` scoped to REPAYMENT types | ✅ |
| Create document (4 types) | `store` auto-confirms | `create` delegates to LedgerService | ✅ |
| Create document (4 types) | `store` auto-confirms | `create` delegates to LedgerService and posts it confirmed (2026-10-29) | ✅ |
| 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 |
+99
View File
@@ -1435,3 +1435,102 @@ _No schema change, no data deleted._
- **Matrix row:** n/a
_No schema change, no data deleted._
---
## 2026-10-29 — Money audit against henza_server/henza_pwa — wage, rial wage, gram debt
Unit confirmed by the owner: `rial_pay`, `rial_remain` and every rial figure are
**ریال** (the reference PWA's «تومان» suffix was a labelling slip). Nothing to convert.
- **Verified equal to the reference (parity test, hand-typed PHP):** order grams /
making-charge grams / rial charge (`setOrder`), debt only while delivered
(`updateRemain`), melted-gold and cash conversions (`CashController::store`).
`accounting/reference-parity.spec.ts`.
- **Fixed — formula:** the promotion exclusion list («استثنا») is honoured when pricing
(`pricing/wage.service.ts`; reference `checkIgnoreList`). A product, category, brand or
customer on it no longer gets a discount taken off its making charge.
- **Fixed — gram debt:** editing an unconfirmed melted-gold / cash document recomputes
the credited grams from the corrected raw figures and stores the karat / price per gram
(`admin-documents.service.ts`, shared `repaymentToGrams`). A document an admin files is
confirmed at once, as `CashController::store` does (`confirmed = 1`); only a customer's own
submission would wait (`set-findoc`, not built).
- **Fixed — rial wage display:** the app cart, cart total and order detail show «اجرت
ریالی»; the wholesale and sourcing grids bind the columns the API now returns (`input`,
`wage`, `rial_input`, `discount`) and the order modal shows each line's weight and rial
charge × quantity.
- **Added — reference features that were missing:** admin line edit / delete
(`order-item/edit`, `DELETE major/order_item/{id}`) and the sales expert's real-weight
entry + «ثبت نهایی» (`PUT order/child/{id}`). Both recompute the order's totals and the
debt server-side (a correction entry on the ledger) and re-queue an already-sent accounting
document. Reference defects NOT copied: `orderItemEdit` set the order's `wage` to its whole
weight and left `rial_input` stale; `updateChildOrder` dropped the making charge (read a
`percent_pay` its lines do not have) and let any customer edit any order.
- **Fixed — consistency:** a cancelled order no longer counts in «میزان فروش» (legacy
deleted it); the panel's order builder prices a line with no typed percent like the app
does (customer % + variant % − promotion); the accounting document's weight check allows
for the four-decimal rounding of stored unit weights (`accounting-sync/document-check.ts`).
- **Left alone, owner's call:** dashboard «طلای فروش‌رفته» counts delivered orders only
(reference summed all; area is in PROTECTED.md).
## 2026-10-29 — Reference app menus that were missing in user-app-v2
Checked every entry of `henza_pwa` (router, dashboard, «مشتریان من») against the app.
«سفارش مشتریان» (`ChildOrder`) already existed. What was missing, now added:
- **«مشتریان من» — فعال/غیرفعال کردن and ویرایش** (reference `customer/toggle-active`,
`customer/update`): `PUT expert/customers/:id`, `POST expert/customers/:id/toggle-active`
(`sales-expert-app`). Scoped to the expert's OWN customers (the reference toggle checked
nothing). The edit takes contact details only — name, mobile (one-mobile-one-person rule),
phones, store, pasazh, address, national code, birth date, city — and never the percent,
credit limit, kind, expert or account switches, whatever the request carries. Switching a
customer back on texts them, as the reference did.
- **«سفارش ساخت»** (`makeOrder` / `makeOrder_status`, reference `create-custom-order`,
`get-custom-orders`): `POST|GET customer/me/pre-orders`, two app screens
(`MakeOrderView`, `MakeOrderStatusView`), a dashboard row with the reference's two-button
choice. New column `PreOrder.quantity` (legacy `sum`; migration
`20261029120000_preorder_quantity`), shown and editable in the admin grid instead of a
hard-coded 1. The reference's «تایید سفارش / ارسال سفارش» buttons for workshop users are
not carried over (both posted the same status, with no ownership check).
- **Not added, owner's call:** adding a NEW customer from the app (reference `addUser`,
`customer/create` with `from_reagent`) — it created the account with a fixed 100 g credit
limit and 3 % percent; that is a money decision.
## 2026-10-29 — Browser run against the real stack (Chrome, scratch database)
API (migrated from empty by its own runner), admin panel and user app were started and
driven in Chrome. The earlier fixes held; three defects that unit tests could not see were
found and fixed:
- **Order grids showed no customer.** The «مشتری» column of «لیست سفارش‌های عمده» / «لیست
تامین» binds `customer.name`, which the row never carried (blank cell for every order, since
before this audit). `admin-orders.service.ts` now sends `customer.name`; covered by a spec.
- **Typed search in the panel selects found nothing.** ant-design-vue 4 hands a custom
`filter-option` the option slot as a function, so the legacy `option.children[0].children`
was always undefined. New `admin-panel/src/utils/select.ts` (`optionText`) replaces it in all
11 views that had the pattern (order builders, documents, invoices, branches, categories,
promotions, …).
- **«سفارش ساختنی»: quantity edited in the panel was not saved** — the edit field existed but
the save only sent colours. `MakeOrder.vue` now sends `sum` too.
Not touched: `/add/findoc` (direct link) renders blank — `open` is a Boolean prop, so Vue
makes it `false` rather than `undefined` and the page thinks it is a modal. Nothing links to
the route (the list page opens the form as a modal), so it was left.
## 2026-10-29 — QA round: money paths, sorting, malformed input
- **Money — fixed:** confirming / un-confirming a settlement document was check-then-update, so
simultaneous requests (a double-click, two admins) each moved the cached balance. 10 at once
left the book at 8.04 g and `Customer.goldDebtGrams` at 71.44 g. `LedgerService.setConfirmed` now flips
the flag as a compare-and-swap and only the winner moves the balance, in one transaction;
`AdminDocumentsService.toggleConfirm` and `confirmEntry` use it, and the customer is texted only when
the state really changed. Re-run live: cache = book = recomputed after 10 simultaneous toggles.
- **500s turned into 400s:** settlement-document figures that are not numbers or do not fit their
columns; the expert weight entry when `items` is not a list; expert edit fields that are objects.
An order line is capped at 100 000 pieces (the panel line edit already was).
- **Order grids:** sorting by «جمع کل سفارش», «اجرت ریالی», «مشتری» answered 400 on the wholesale and
sourcing grids. The grid now sorts by a column's filter key when it has one, and the API keeps the
default order for a sort field it cannot map instead of failing the request.
- **Not changed on purpose:** the wallet charge (`customers-omde/wallet/charge`) still answers 500 to
a non-numeric value; the wallet belongs to the retail site (owner). An admin can still move a
credit-limit-SUSPENDED order straight to delivered (the limit is checked at placement only).