108 lines
4.6 KiB
Markdown
108 lines
4.6 KiB
Markdown
# Semi-Finished Measure Basis Implementation Plan
|
|
|
|
> **For agentic workers:** REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development (recommended) or superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (`- [ ]`) syntax for tracking.
|
|
|
|
**Goal:** Make semi-finished inventory use one locked measure basis per semi-finished item-operation in a warehouse: by piece or by weight.
|
|
|
|
**Architecture:** Do not add a database column unless tests prove derivation is insufficient. Derive the measure basis from semi-finished stock lots: `remaining_qty/inbound_qty` means by piece, `remaining_weight_kg/inbound_weight_kg` means by weight, both means legacy mixed. Backend enforces exactly one inbound basis and blocks mismatched new inbound for existing semi-finished stock; frontend presents only the correct input and availability wording.
|
|
|
|
**Tech Stack:** FastAPI, SQLAlchemy, pytest, Vue 3, Vite, Node assertion script.
|
|
|
|
---
|
|
|
|
### Task 1: Backend Measure Basis Enforcement
|
|
|
|
**Files:**
|
|
- Modify: `backend/app/api/routes/inventory.py`
|
|
- Modify: `backend/tests/test_semi_finished_operation_inventory.py`
|
|
|
|
- [ ] **Step 1: Write failing backend tests**
|
|
|
|
Add tests that prove:
|
|
- semi-finished inbound rejects filling both quantity and weight;
|
|
- semi-finished inbound rejects filling neither quantity nor weight;
|
|
- once a semi-finished item-operation has by-piece stock, later by-weight inbound is rejected;
|
|
- once a semi-finished item-operation has by-weight stock, later by-piece inbound is rejected.
|
|
|
|
- [ ] **Step 2: Run red tests**
|
|
|
|
Run: `.venv/bin/python -m pytest tests/test_semi_finished_operation_inventory.py -q`
|
|
|
|
Expected before implementation: at least the mismatch/both-filled tests fail because current backend accepts both fields and does not enforce existing basis.
|
|
|
|
- [ ] **Step 3: Implement minimal backend helper**
|
|
|
|
Add helper logic in `create_warehouse_inbound`:
|
|
- determine requested basis: `PIECE` when `inbound_qty > 0 and inbound_weight == 0`, `WEIGHT` when `inbound_weight > 0 and inbound_qty == 0`;
|
|
- reject both-filled and neither-filled for `WIP_IN` / `OUTSOURCING_IN`;
|
|
- query existing semi-finished lots for same `item_id`, `warehouse_id`, `source_line_id` with remaining quantity or weight;
|
|
- derive existing basis; if it is `MIXED`, reject with a Chinese message asking to clean historical stock first;
|
|
- if existing basis exists and differs from requested basis, reject with a Chinese message that the semi-finished item must follow existing measure basis.
|
|
|
|
- [ ] **Step 4: Run backend tests**
|
|
|
|
Run: `.venv/bin/python -m pytest tests/test_semi_finished_operation_inventory.py -q`
|
|
|
|
Expected: all semi-finished tests pass.
|
|
|
|
### Task 2: Frontend Semi-Finished UI Basis
|
|
|
|
**Files:**
|
|
- Modify: `frontend/src/views/InventoryLedgerView.vue`
|
|
- Modify: `frontend/src/utils/inventoryOperationOptions.js`
|
|
- Modify: `frontend/scripts/test-inventory-ledger-options.mjs`
|
|
|
|
- [ ] **Step 1: Write/update frontend assertions**
|
|
|
|
Assert the source contains:
|
|
- user-facing label `半成品` instead of semi warehouse `产品工序`;
|
|
- `入库类型`, `按件入库`, `按重入库`;
|
|
- helper logic that formats semi availability by basis instead of always `formatWeight(...)`;
|
|
- payload sends only `inbound_qty` for piece basis and only `inbound_weight_kg` for weight basis.
|
|
|
|
- [ ] **Step 2: Run red frontend assertion**
|
|
|
|
Run: `node scripts/test-inventory-ledger-options.mjs`
|
|
|
|
Expected before implementation: fails on missing measure-basis UI or payload logic.
|
|
|
|
- [ ] **Step 3: Implement frontend UI**
|
|
|
|
In `InventoryLedgerView.vue`:
|
|
- rename semi item field labels from `产品工序` to `半成品`;
|
|
- add `genericForm.measure_basis`;
|
|
- when semi inbound item changes, derive existing basis from `semiFinishedBalances` and auto-fill/lock `measure_basis`;
|
|
- show only `入库数量` for `PIECE`, only `入库重量(kg)` for `WEIGHT`;
|
|
- for semi outbound, derive basis from selected semi option and show only the matching outbound input;
|
|
- display `-` for irrelevant quantity/weight values in semi balances and lots.
|
|
|
|
In `inventoryOperationOptions.js`:
|
|
- carry `measure_basis` through semi outbound options.
|
|
|
|
- [ ] **Step 4: Run frontend checks**
|
|
|
|
Run: `node scripts/test-inventory-ledger-options.mjs`
|
|
|
|
Expected: assertion script passes.
|
|
|
|
Run: `npm run build`
|
|
|
|
Expected: Vite build completes successfully.
|
|
|
|
### Task 3: Final Verification
|
|
|
|
**Files:**
|
|
- Verify only.
|
|
|
|
- [ ] **Step 1: Run backend full test suite**
|
|
|
|
Run: `.venv/bin/python -m pytest tests -q`
|
|
|
|
Expected: all backend tests pass.
|
|
|
|
- [ ] **Step 2: Browser smoke**
|
|
|
|
Run frontend dev server and open `http://127.0.0.1:5173/` in the in-app browser.
|
|
|
|
Expected: page loads with title `嘉恒智能五金 ERP` and no console errors.
|