From 4e9f66ccd6f458f36e68fbe8ad7a2809e5300aa1 Mon Sep 17 00:00:00 2001 From: SZCJW <792430652@qq.com> Date: Sun, 15 Mar 2026 22:30:41 +0800 Subject: [PATCH] x --- src/api/inventory/stock_movement_routes.py | 17 +++-- static/vue-app.js | 76 +++++++++++++++++++--- 2 files changed, 81 insertions(+), 12 deletions(-) diff --git a/src/api/inventory/stock_movement_routes.py b/src/api/inventory/stock_movement_routes.py index 8758a2b..7917d8d 100644 --- a/src/api/inventory/stock_movement_routes.py +++ b/src/api/inventory/stock_movement_routes.py @@ -22,6 +22,15 @@ from .utils import generate_order_no router = APIRouter(prefix="/stock-movements", tags=["库存变动"]) +INBOUND_TYPES = { + "in", "purchase_in", "return_from_production", "outsource_return", "finish_in" +} +OUTBOUND_TYPES = { + "out", "issue_to_production", "outsource_send", "shipment_out", "scrap_out" +} +ADJUST_TYPES = {"adjust"} +SUPPORTED_MOVEMENT_TYPES = INBOUND_TYPES | OUTBOUND_TYPES | ADJUST_TYPES + @router.post("", response_model=StockMovementResponse, status_code=201) async def create_stock_movement( @@ -29,7 +38,7 @@ async def create_stock_movement( db_session: AsyncSession = Depends(get_db_session), current_user: User = Depends(get_current_active_user) ): - if movement_data.movement_type not in ["in", "out", "adjust"]: + if movement_data.movement_type not in SUPPORTED_MOVEMENT_TYPES: raise HTTPException(status_code=400, detail="无效的变动类型") if movement_data.quantity <= 0: raise HTTPException(status_code=400, detail="数量必须大于0") @@ -70,7 +79,7 @@ async def create_stock_movement( inventory = result.scalar_one_or_none() if not inventory: - if movement_data.movement_type == "out": + if movement_data.movement_type in OUTBOUND_TYPES: raise HTTPException(status_code=400, detail="库存不足") inventory = Inventory( product_id=resolved_product_id, @@ -82,9 +91,9 @@ async def create_stock_movement( before_qty = inventory.quantity - if movement_data.movement_type == "in": + if movement_data.movement_type in INBOUND_TYPES: inventory.quantity += movement_data.quantity - elif movement_data.movement_type == "out": + elif movement_data.movement_type in OUTBOUND_TYPES: if inventory.quantity < movement_data.quantity: raise HTTPException(status_code=400, detail="库存不足") inventory.quantity -= movement_data.quantity diff --git a/static/vue-app.js b/static/vue-app.js index afdf169..4c7623b 100644 --- a/static/vue-app.js +++ b/static/vue-app.js @@ -1498,6 +1498,49 @@ const InventoryView = { form: {} }); + const inboundMovementOptions = [ + { value: 'purchase_in', label: '采购入库' }, + { value: 'return_from_production', label: '生产退料入库' }, + { value: 'outsource_return', label: '外协回库' }, + { value: 'finish_in', label: '完工入库' }, + { value: 'in', label: '其他入库' } + ]; + + const outboundMovementOptions = [ + { value: 'issue_to_production', label: '生产领料出库' }, + { value: 'outsource_send', label: '外协发料出库' }, + { value: 'shipment_out', label: '销售出库' }, + { value: 'scrap_out', label: '报废出库' }, + { value: 'out', label: '其他出库' } + ]; + + const getMovementTypeLabel = (movementType) => { + const movementLabelMap = { + in: '其他入库', + out: '其他出库', + adjust: '库存调整', + purchase_in: '采购入库', + return_from_production: '生产退料入库', + outsource_return: '外协回库', + finish_in: '完工入库', + issue_to_production: '生产领料出库', + outsource_send: '外协发料出库', + shipment_out: '销售出库', + scrap_out: '报废出库' + }; + return movementLabelMap[movementType] || movementType; + }; + + const getMovementBadgeClass = (movementType) => { + if (['purchase_in', 'return_from_production', 'outsource_return', 'finish_in', 'in'].includes(movementType)) { + return 'badge-success'; + } + if (['issue_to_production', 'outsource_send', 'shipment_out', 'scrap_out', 'out'].includes(movementType)) { + return 'badge-error'; + } + return 'badge-warning'; + }; + const loadDashboard = async () => { state.loading = true; try { @@ -1672,7 +1715,8 @@ const InventoryView = { state.form = { product_id: state.products[0]?.id || null, warehouse_id: state.warehouses.find(w => w.is_default)?.id || state.warehouses[0]?.id || null, - quantity: 1 + quantity: 1, + movement_type: type === 'stockIn' ? 'purchase_in' : 'issue_to_production' }; } } @@ -1789,7 +1833,7 @@ const InventoryView = { try { await apiRequest('/api/stock-movements', { method: 'POST', - body: JSON.stringify({ ...state.form, movement_type: 'in' }) + body: JSON.stringify({ ...state.form, movement_type: state.form.movement_type || 'purchase_in' }) }); addNotification('入库成功', 'success'); closeModal(); @@ -1804,7 +1848,7 @@ const InventoryView = { try { await apiRequest('/api/stock-movements', { method: 'POST', - body: JSON.stringify({ ...state.form, movement_type: 'out' }) + body: JSON.stringify({ ...state.form, movement_type: state.form.movement_type || 'issue_to_production' }) }); addNotification('出库成功', 'success'); closeModal(); @@ -1839,7 +1883,11 @@ const InventoryView = { deleteCustomer, stockIn, stockOut, - refreshFinanceByPeriod + refreshFinanceByPeriod, + inboundMovementOptions, + outboundMovementOptions, + getMovementTypeLabel, + getMovementBadgeClass }; }, template: ` @@ -1949,8 +1997,8 @@ const InventoryView = {
- - + +
@@ -2258,8 +2306,8 @@ const InventoryView = { @@ -2392,6 +2440,12 @@ const InventoryView = { +
+ + +
@@ -2430,6 +2484,12 @@ const InventoryView = {
+
+ + +
{{ movement.product_name }}{{ movement.product_sku ? ' (' + movement.product_sku + ')' : '' }} - - {{ movement.movement_type === 'in' ? '入库' : movement.movement_type === 'out' ? '出库' : '调整' }} + + {{ getMovementTypeLabel(movement.movement_type) }} {{ movement.quantity }}