Refactor production and stock tests to remove UOM dependency

- Updated production.spec.ts, stock-adjustments.spec.ts, and stock-transfers.spec.ts to eliminate UOM references in API seeder and test cases.
- Adjusted ApiSeeder methods to remove UOM parameters from stock receiving and production template creation.
- Revised documentation to reflect changes in UOM handling, emphasizing that stock is counted in base UOM only.
- Introduced new enums for MeasureUnit and StageQtyUnit to clarify content size and stage input quantities.
- Implemented ItemContent service to validate and normalize content sizes.
- Updated smoke tests to validate production stage inputs expressed in content units, ensuring correct consumption calculations.
- Modified frontend UOM label handling to reflect the removal of per-line UOMs in document lines.
This commit is contained in:
2026-08-11 11:32:40 +05:30
parent d37824cecc
commit 15ddac178c
108 changed files with 1221 additions and 987 deletions
+31
View File
@@ -4,6 +4,37 @@ Legend: `[ ]` not started · `[~]` in progress · `[x]` done
Spec: `docs/10-BACKEND-PHASE1.md` (model + rules) · `docs/11-BACKEND-PHASE1.md` (API)
Convention: `docs/01-DOC-GUIDE.md §6`. Update this file in the **same commit** as the code. When ticking `[x]`, append a short note + any deviation.
## UOM re-model (2026-08-11) — supersedes every "UOM conversion" note below
Per-item UOM conversion is **gone**. Entries further down this file that describe
`IUomConverter`, `PUT /items/{id}/uom-conversions`, `uom_conversions`, or a line-level
`uomId` are historical and no longer describe the code.
What replaced it:
- **One unit per item.** `Item.BaseUomId` is the pack an item is stocked and counted in, and
every quantity in the system — stock layers, ledger rows, GRN/PO/sales/bundle/transfer lines
— is a plain count of it. A differently sized pack is a different item. `UomId` was dropped
from all six document-line entities; `Uom` itself survives as the lookup.
- **Optional content size on `Item`**: `ContentQty` + `ContentUnit` as entered (`Ml|L|G|Kg`),
normalised on write into `ContentBaseQty` + `ContentBaseUnit` (only ever `Ml` or `G`, L/Kg
×1000). All four null ⇒ nothing measurable to hold. `ItemContent` is the pure normaliser.
- **Production is the only content consumer.** `StageInput.QtyUnit` (`Pack|Content`) says what
`QtyPerBatch` means; `IItemMeasure` divides a `Content` quantity by the item's content size
to get packs. **Fractional packs are legal** — 300 ml of a 500 ml bottle consumes 0.6000.
Outputs are always pack counts, so scrap stays in whole broken bottles.
- **WIP keeps a label.** `StageOutput.UomId`/`RunStageOutput.UomId` are now nullable and
required *only* when `ItemId` is null (`422 WIP_UNIT_REQUIRED`), since an item-bearing output
takes its unit from the item. WIP never touches stock, so the label is never converted.
- **Two live defects fixed as a consequence.** `SalesPostingService` injected `IUomConverter`
and never called it, so a sales line in a non-base UOM consumed the wrong quantity outright;
`GrnService` accrued `poLine.QtyReceived += line.Qty` and range-checked over-receipt across
the same unit boundary. Both are now like-for-like by construction.
- **`BaseUomId` is frozen once an item has stock history** (`409 MASTER_IN_USE`) — it is the
sole meaning of every recorded quantity, so changing it would silently reinterpret all of it.
- Smoke: `m4b_uom_conversion.py` deleted; `m4c_content_units.py` added (whole packs, fractional
packs, and the contentless-item guard).
## 8. Sales
- [x] Sales bootstrap data seeded locally for development: warehouses, UOMs, categories, items, customers, current-year `SI`/`SSL` sequences, plus sample invoice/slip headers and lines. Existing data is preserved.
- [x] Sales report API consolidated into `GET /api/v1/reports/sales` (catalog), `GET /api/v1/reports/sales/{reportId}` (report metadata), and `POST /api/v1/reports/sales/query` (filtered data). Legacy per-report GET routes removed; invalid report/filter combinations now fail validation.