# 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.
