# Production Surplus Lightweight Source-Lot Return Implementation Plan > **For agentic workers:** REQUIRED SUB-SKILL: Use superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (`- [ ]`) syntax for tracking. **Goal:** Make 原材料库「生产余料入库」hide unnecessary free-text fields and return surplus weight against one or more selected source stock lots. **Architecture:** Keep the existing generic `/inventory/inbound` API and existing `PRODUCTION_SURPLUS` business type. The frontend special-cases only the generic drawer when `activeOperation.bizType === "PRODUCTION_SURPLUS"`: hide batch/provider fields, replace free-text source lot input with a searchable multi-select, show per-source-lot return weight rows, and submit one inbound record per selected source lot. **Tech Stack:** Vue 3 Composition API, existing `StockLotTagSelect.vue`, existing `fetchResource`/`postResource`, FastAPI existing generic inbound endpoint. --- ## File Structure - Modify: `frontend/src/views/InventoryLedgerView.vue` - Add source-lot option state for generic production surplus inbound. - Hide `批次号` and `来源/提供方` for `PRODUCTION_SURPLUS`. - Replace the single text input for `来源库存批次号` with `StockLotTagSelect` for `PRODUCTION_SURPLUS`. - Add a per-source-lot return weight table and submit one inbound request per source lot. - No backend model/table changes. - No new SQL. ## Task 1: Frontend State And Option Loading **Files:** - Modify: `frontend/src/views/InventoryLedgerView.vue` - [ ] **Step 1: Add generic source lot state** Add refs near the existing `productionStockLotOptions` and `selectedProductionStockLots` declarations: ```js const genericSourceLotOptions = ref([]); const selectedGenericSourceLots = ref([]); let genericSourceLotOptionsRequestSeq = 0; ``` - [ ] **Step 2: Add computed helpers** Add computed values near `genericRequiresLogistics`: ```js const isProductionSurplusInbound = computed(() => activeOperation.value?.direction === "in" && activeOperation.value?.bizType === "PRODUCTION_SURPLUS" ); const genericSourceLotReturnWeight = computed(() => selectedGenericSourceLots.value.reduce((total, lot) => total + Number(lot.return_weight_kg || 0), 0) ); ``` - [ ] **Step 3: Reset selected source lots when generic form resets** Update `resetGenericForm()` so it clears the selected lots and option list: ```js genericSourceLotOptionsRequestSeq += 1; selectedGenericSourceLots.value = []; genericSourceLotOptions.value = []; ``` - [ ] **Step 4: Load source lot options by material** Add this function near `loadProductionStockLotOptions`: ```js async function loadGenericSourceLotOptions(materialItemId) { const requestSeq = ++genericSourceLotOptionsRequestSeq; const id = Number(materialItemId || 0); if (!id || !isProductionSurplusInbound.value) { genericSourceLotOptions.value = []; selectedGenericSourceLots.value = []; return; } try { const rows = await fetchResource(`/inventory/stock-lot-options?material_item_id=${id}&limit=1000`, []); if (requestSeq !== genericSourceLotOptionsRequestSeq) { return; } genericSourceLotOptions.value = Array.isArray(rows) ? rows : []; const optionMap = new Map(genericSourceLotOptions.value.map((lot) => [Number(lot.lot_id), lot])); selectedGenericSourceLots.value = selectedGenericSourceLots.value .filter((lot) => optionMap.has(Number(lot.lot_id))) .map((lot) => ({ ...optionMap.get(Number(lot.lot_id)), return_weight_kg: Number(lot.return_weight_kg || 0) })); } catch (error) { if (requestSeq === genericSourceLotOptionsRequestSeq) { genericSourceLotOptions.value = []; selectedGenericSourceLots.value = []; errorMessage.value = error.message || "来源库存批次号加载失败"; } } } ``` - [ ] **Step 5: Watch material and operation changes** Add watchers near the existing Vue watchers: ```js watch( () => [genericForm.item_id, activeOperation.value?.bizType, genericDrawerOpen.value], async () => { if (!genericDrawerOpen.value || !isProductionSurplusInbound.value) { return; } selectedGenericSourceLots.value = []; await loadGenericSourceLotOptions(genericForm.item_id); } ); watch( selectedGenericSourceLots, () => { if (isProductionSurplusInbound.value) { genericForm.weight_kg = Number(genericSourceLotReturnWeight.value.toFixed(3)); } }, { deep: true } ); ``` ## Task 2: Frontend Template **Files:** - Modify: `frontend/src/views/InventoryLedgerView.vue` - [ ] **Step 1: Hide batch/provider fields for production surplus inbound** Change this condition: ```vue
| 来源库存批次号 | 原材料 | 剩余重量 | 归还重量(kg) |
|---|---|---|---|
| {{ lot.lot_no }} | {{ lot.item_code }} · {{ lot.item_name }} | {{ formatWeight(lot.remaining_weight_kg) }} |