fix(stocktake): enforce session_id in resume, make missing generation idempotent, update UI to show SN, and fix excel time offset
This commit is contained in:
@ -804,13 +804,13 @@ def export_stocktake():
|
|||||||
return str(user_id)
|
return str(user_id)
|
||||||
|
|
||||||
def to_beijing_time(dt):
|
def to_beijing_time(dt):
|
||||||
"""转换为北京时间(+8小时)"""
|
"""直接使用数据库中存储的标准时间(服务器时区已正确)"""
|
||||||
if not dt:
|
if not dt:
|
||||||
return ''
|
return ''
|
||||||
try:
|
try:
|
||||||
if isinstance(dt, str):
|
if isinstance(dt, str):
|
||||||
return dt[:19]
|
return dt[:19]
|
||||||
return (dt + timedelta(hours=8)).strftime('%Y-%m-%d %H:%M:%S')
|
return dt.strftime('%Y-%m-%d %H:%M:%S')
|
||||||
except:
|
except:
|
||||||
return str(dt)[:19]
|
return str(dt)[:19]
|
||||||
|
|
||||||
@ -1130,9 +1130,13 @@ def export_stocktake():
|
|||||||
@permission_required('inventory_stocktake:operation')
|
@permission_required('inventory_stocktake:operation')
|
||||||
def generate_missing_stocktake():
|
def generate_missing_stocktake():
|
||||||
"""
|
"""
|
||||||
生成漏盘数据:
|
生成漏盘数据(幂等性):
|
||||||
找出所有真实库存 > 0,但未被当前会话盘点扫描到的物料,
|
找出所有真实库存 > 0,但未被当前会话盘点扫描到的物料,
|
||||||
自动生成盘点草稿,标记为盘亏(实盘=0,差异=-库存数)
|
自动生成盘点草稿,标记为盘亏(实盘=0,差异=-库存数)
|
||||||
|
|
||||||
|
幂等性保护:在重新计算差集之前,先删除当前 session 下所有
|
||||||
|
由系统自动生成的漏盘记录(quantity==0, user_id=='system'),
|
||||||
|
保证该接口多次调用结果一致。
|
||||||
"""
|
"""
|
||||||
try:
|
try:
|
||||||
# ★ 获取 session_id 参数,用于隔离当前会话
|
# ★ 获取 session_id 参数,用于隔离当前会话
|
||||||
@ -1142,6 +1146,16 @@ def generate_missing_stocktake():
|
|||||||
if not session_id:
|
if not session_id:
|
||||||
return jsonify({'code': 400, 'msg': '缺少 session_id 参数'}), 400
|
return jsonify({'code': 400, 'msg': '缺少 session_id 参数'}), 400
|
||||||
|
|
||||||
|
# ★ 幂等性保护:先删除当前 session 下系统自动生成的漏盘记录
|
||||||
|
# 特征:user_id == 'system' (表示由系统自动生成)
|
||||||
|
deleted_count = StocktakeDraft.query.filter(
|
||||||
|
StocktakeDraft.session_id == session_id,
|
||||||
|
StocktakeDraft.user_id == 'system'
|
||||||
|
).delete()
|
||||||
|
if deleted_count > 0:
|
||||||
|
db.session.commit()
|
||||||
|
print(f"[generate_missing] 已清理 {deleted_count} 条历史漏盘记录")
|
||||||
|
|
||||||
# 1. 获取当前会话已有盘点记录的 (source_table, stock_id) 集合
|
# 1. 获取当前会话已有盘点记录的 (source_table, stock_id) 集合
|
||||||
existing_records = db.session.query(
|
existing_records = db.session.query(
|
||||||
StocktakeDraft.source_table,
|
StocktakeDraft.source_table,
|
||||||
@ -1192,11 +1206,11 @@ def generate_missing_stocktake():
|
|||||||
if key not in scanned_keys:
|
if key not in scanned_keys:
|
||||||
# 生成漏盘草稿
|
# 生成漏盘草稿
|
||||||
draft = StocktakeDraft(
|
draft = StocktakeDraft(
|
||||||
user_id='system',
|
user_id='system', # ★ 标记为系统自动生成,用于幂等性清理
|
||||||
uuid=f'MISSING-{stock["source_table"]}-{stock["stock_id"]}',
|
uuid=f'MISSING-{stock["source_table"]}-{stock["stock_id"]}',
|
||||||
quantity=0, # 实盘数为0
|
quantity=0, # 实盘数为0
|
||||||
scan_time=beijing_time(),
|
scan_time=beijing_time(),
|
||||||
session_id='AUTO_GENERATED',
|
session_id=session_id, # ★ 使用传入的 session_id
|
||||||
source_table=stock['source_table'],
|
source_table=stock['source_table'],
|
||||||
stock_id=stock['stock_id'],
|
stock_id=stock['stock_id'],
|
||||||
stock_qty=stock['stock_qty'],
|
stock_qty=stock['stock_qty'],
|
||||||
|
|||||||
@ -74,7 +74,7 @@ export function getBom(parentId: number) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// 获取应盘物资清单(盘点基数)
|
// 获取应盘物资清单(盘点基数)
|
||||||
export function getAllStocktakeItems(params?: { keyword?: string }) {
|
export function getAllStocktakeItems(params?: { keyword?: string; session_id?: string }) {
|
||||||
return request({
|
return request({
|
||||||
url: '/v1/inbound/stock/stocktake/all-items',
|
url: '/v1/inbound/stock/stocktake/all-items',
|
||||||
method: 'get',
|
method: 'get',
|
||||||
|
|||||||
@ -167,8 +167,8 @@
|
|||||||
<span class="value">{{ currentItem.sku }}</span>
|
<span class="value">{{ currentItem.sku }}</span>
|
||||||
</div>
|
</div>
|
||||||
<div class="info-row">
|
<div class="info-row">
|
||||||
<span class="label">批号:</span>
|
<span class="label">序列号(SN):</span>
|
||||||
<span class="value">{{ currentItem.batch_no || '-' }}</span>
|
<span class="value">{{ currentItem.sn || currentItem.batch_no || '-' }}</span>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
@ -429,6 +429,7 @@ interface StockItem {
|
|||||||
standard: string
|
standard: string
|
||||||
sku: string
|
sku: string
|
||||||
batch_no: string
|
batch_no: string
|
||||||
|
sn?: string // ★ 序列号
|
||||||
serial_number?: string
|
serial_number?: string
|
||||||
uuid: string
|
uuid: string
|
||||||
bar_code: string
|
bar_code: string
|
||||||
@ -484,6 +485,7 @@ const listLoading = ref(false)
|
|||||||
const listData = ref<any[]>([])
|
const listData = ref<any[]>([])
|
||||||
const listStatusFilter = ref<'all' | 'counted' | 'uncounted'>('all')
|
const listStatusFilter = ref<'all' | 'counted' | 'uncounted'>('all')
|
||||||
const allStockItems = ref<any[]>([]) // 全量应盘物资(盘点基数)
|
const allStockItems = ref<any[]>([]) // 全量应盘物资(盘点基数)
|
||||||
|
const totalStockCount = ref(0) // ★ 全量应盘物资总数(不受limit限制)
|
||||||
const allScannedDrafts = ref<any[]>([]) // 全量草稿记录(脱离分页和过滤)
|
const allScannedDrafts = ref<any[]>([]) // 全量草稿记录(脱离分页和过滤)
|
||||||
const listTotalFiltered = ref(0) // 过滤后的总数
|
const listTotalFiltered = ref(0) // 过滤后的总数
|
||||||
|
|
||||||
@ -493,9 +495,12 @@ const currentSessionId = ref<string>('')
|
|||||||
// 获取应盘物资清单(盘点基数)
|
// 获取应盘物资清单(盘点基数)
|
||||||
const fetchAllStockItems = async () => {
|
const fetchAllStockItems = async () => {
|
||||||
try {
|
try {
|
||||||
const res: any = await getAllStocktakeItems()
|
// ★ 必须传递 session_id,用于隔离会话
|
||||||
|
const res: any = await getAllStocktakeItems({ session_id: currentSessionId.value })
|
||||||
if (res && res.code === 200) {
|
if (res && res.code === 200) {
|
||||||
allStockItems.value = res.data.items || []
|
allStockItems.value = res.data.items || []
|
||||||
|
// ★ 使用返回的 total 获取真实总数,而不是受限的数组长度
|
||||||
|
totalStockCount.value = res.data.total || allStockItems.value.length
|
||||||
}
|
}
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
console.error('获取应盘物资清单失败', e)
|
console.error('获取应盘物资清单失败', e)
|
||||||
|
|||||||
Reference in New Issue
Block a user