feat: add generate-missing logic to identify unscanned stock as inventory loss
This commit is contained in:
@ -1,5 +1,5 @@
|
|||||||
from flask import Blueprint, jsonify, request, send_file
|
from flask import Blueprint, jsonify, request, send_file
|
||||||
from app.extensions import db
|
from app.extensions import db, beijing_time
|
||||||
# ★★★ 修复点:必须引入 datetime,否则下方更新时间时会报错 500 ★★★
|
# ★★★ 修复点:必须引入 datetime,否则下方更新时间时会报错 500 ★★★
|
||||||
from datetime import datetime, timedelta
|
from datetime import datetime, timedelta
|
||||||
from app.utils.decorators import permission_required
|
from app.utils.decorators import permission_required
|
||||||
@ -889,3 +889,95 @@ def export_stocktake():
|
|||||||
import traceback
|
import traceback
|
||||||
traceback.print_exc()
|
traceback.print_exc()
|
||||||
return jsonify({"message": f"导出失败: {str(e)}"}), 500
|
return jsonify({"message": f"导出失败: {str(e)}"}), 500
|
||||||
|
|
||||||
|
|
||||||
|
# --------------------------------------------------------
|
||||||
|
# 生成漏盘数据 - 将未扫描的库存标记为全额盘亏
|
||||||
|
# POST /api/v1/inbound/stocktake/generate-missing
|
||||||
|
# --------------------------------------------------------
|
||||||
|
@bp.route('/stocktake/generate-missing', methods=['POST'])
|
||||||
|
@permission_required('inventory_stocktake:operation')
|
||||||
|
def generate_missing_stocktake():
|
||||||
|
"""
|
||||||
|
生成漏盘数据:
|
||||||
|
找出所有真实库存 > 0,但未被盘点扫描到的物料,
|
||||||
|
自动生成盘点草稿,标记为盘亏(实盘=0,差异=-库存数)
|
||||||
|
"""
|
||||||
|
try:
|
||||||
|
# 1. 获取所有已有盘点记录的 (source_table, stock_id) 集合
|
||||||
|
existing_records = db.session.query(
|
||||||
|
StocktakeDraft.source_table,
|
||||||
|
StocktakeDraft.stock_id
|
||||||
|
).distinct().all()
|
||||||
|
|
||||||
|
scanned_keys = set()
|
||||||
|
for src_table, stock_id in existing_records:
|
||||||
|
if stock_id:
|
||||||
|
scanned_keys.add((src_table, stock_id))
|
||||||
|
|
||||||
|
# 2. 获取所有真实库存 > 0 的记录
|
||||||
|
all_stock = []
|
||||||
|
|
||||||
|
# 采购库存
|
||||||
|
for item in StockBuy.query.filter(StockBuy.stock_quantity > 0).all():
|
||||||
|
all_stock.append({
|
||||||
|
'source_table': 'stock_buy',
|
||||||
|
'stock_id': item.id,
|
||||||
|
'base_id': item.base_id,
|
||||||
|
'stock_qty': float(item.stock_quantity or 0)
|
||||||
|
})
|
||||||
|
|
||||||
|
# 半成品库存
|
||||||
|
if StockSemi:
|
||||||
|
for item in StockSemi.query.filter(StockSemi.stock_quantity > 0).all():
|
||||||
|
all_stock.append({
|
||||||
|
'source_table': 'stock_semi',
|
||||||
|
'stock_id': item.id,
|
||||||
|
'base_id': item.base_id,
|
||||||
|
'stock_qty': float(item.stock_quantity or 0)
|
||||||
|
})
|
||||||
|
|
||||||
|
# 成品库存
|
||||||
|
if StockProduct:
|
||||||
|
for item in StockProduct.query.filter(StockProduct.stock_quantity > 0).all():
|
||||||
|
all_stock.append({
|
||||||
|
'source_table': 'stock_product',
|
||||||
|
'stock_id': item.id,
|
||||||
|
'base_id': item.base_id,
|
||||||
|
'stock_qty': float(item.stock_quantity or 0)
|
||||||
|
})
|
||||||
|
|
||||||
|
# 3. 找出漏盘记录(库存中有但盘点中没有的)
|
||||||
|
missing_count = 0
|
||||||
|
for stock in all_stock:
|
||||||
|
key = (stock['source_table'], stock['stock_id'])
|
||||||
|
if key not in scanned_keys:
|
||||||
|
# 生成漏盘草稿
|
||||||
|
draft = StocktakeDraft(
|
||||||
|
user_id='system',
|
||||||
|
uuid=f'MISSING-{stock["source_table"]}-{stock["stock_id"]}',
|
||||||
|
quantity=0, # 实盘数为0
|
||||||
|
scan_time=beijing_time(),
|
||||||
|
session_id='AUTO_GENERATED',
|
||||||
|
source_table=stock['source_table'],
|
||||||
|
stock_id=stock['stock_id'],
|
||||||
|
stock_qty=stock['stock_qty'],
|
||||||
|
diff_qty=-stock['stock_qty'], # 差异 = 0 - 库存数 = 负数
|
||||||
|
remark='未盘点到,系统自动标记为盘亏'
|
||||||
|
)
|
||||||
|
db.session.add(draft)
|
||||||
|
missing_count += 1
|
||||||
|
|
||||||
|
db.session.commit()
|
||||||
|
|
||||||
|
return jsonify({
|
||||||
|
'code': 200,
|
||||||
|
'msg': f'成功生成 {missing_count} 条漏盘记录',
|
||||||
|
'data': {'count': missing_count}
|
||||||
|
})
|
||||||
|
|
||||||
|
except Exception as e:
|
||||||
|
db.session.rollback()
|
||||||
|
import traceback
|
||||||
|
traceback.print_exc()
|
||||||
|
return jsonify({'code': 500, 'msg': f'生成漏盘数据失败: {str(e)}'}), 500
|
||||||
|
|||||||
@ -107,6 +107,11 @@
|
|||||||
结束盘点
|
结束盘点
|
||||||
</el-button>
|
</el-button>
|
||||||
</el-col>
|
</el-col>
|
||||||
|
<el-col :span="12">
|
||||||
|
<el-button v-if="userStore.hasPermission('inventory_stocktake:operation')" type="warning" size="large" class="w-100 action-btn" @click="handleGenerateMissing" :icon="Warning">
|
||||||
|
结束盘点 (计算漏盘)
|
||||||
|
</el-button>
|
||||||
|
</el-col>
|
||||||
</el-row>
|
</el-row>
|
||||||
</div>
|
</div>
|
||||||
</el-card>
|
</el-card>
|
||||||
@ -993,6 +998,46 @@ const openFinishDialog = () => {
|
|||||||
finishStocktake()
|
finishStocktake()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ★ 新增:结束盘点(计算漏盘)- 将未扫描的库存标记为全额盘亏
|
||||||
|
const handleGenerateMissing = async () => {
|
||||||
|
if (stats.value.total === 0) {
|
||||||
|
ElMessage.warning('暂无盘点数据')
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
await ElMessageBox.confirm(
|
||||||
|
'确认结束当前盘点吗?系统将自动把所有未扫描到的库存标记为全额盘亏!',
|
||||||
|
'提示',
|
||||||
|
{
|
||||||
|
confirmButtonText: '确定',
|
||||||
|
cancelButtonText: '取消',
|
||||||
|
type: 'warning'
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
btnLoading.value = true
|
||||||
|
const res = await request({
|
||||||
|
url: '/v1/inbound/stocktake/generate-missing',
|
||||||
|
method: 'post'
|
||||||
|
})
|
||||||
|
|
||||||
|
if (res.code === 200) {
|
||||||
|
ElMessage.success(`成功生成 ${res.data.count} 条漏盘记录`)
|
||||||
|
// 刷新差异列表
|
||||||
|
await checkServerDraft()
|
||||||
|
} else {
|
||||||
|
ElMessage.error(res.msg || '生成漏盘数据失败')
|
||||||
|
}
|
||||||
|
} catch (e) {
|
||||||
|
if (e !== 'cancel') {
|
||||||
|
ElMessage.error('生成漏盘数据失败')
|
||||||
|
}
|
||||||
|
} finally {
|
||||||
|
btnLoading.value = false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// ★ 重写: 结束盘点 - 纯前端状态流转,不再调用后端
|
// ★ 重写: 结束盘点 - 纯前端状态流转,不再调用后端
|
||||||
const finishStocktake = async () => {
|
const finishStocktake = async () => {
|
||||||
try {
|
try {
|
||||||
|
|||||||
Reference in New Issue
Block a user