fix(web): 扫码页与备选库位带单据ID,按有效可用量限流
配合后端预占回加:扫码/借库/审批页面调用 /outbound/scan 与 /outbound/alternatives 时带上本单 id 与 biz_type,拿到的 available_quantity 即为「实时可用量 + 本单 预占」,本单锁定的行不会再因实时可用量为 0 而被拦住或从列表消失。 后端已在响应边界完成归一化,故前端模板与校验逻辑(:max、库存列展示、超量 警告、草稿存取)一律不动,只多传参数 —— 避免把同一语义散落到多个读写点。 - api/outbound.ts:getStockByBarcode / getStockAlternatives 加 requestId 与 bizType 参数,导出 ScanBizType,ScanResult 补 reserved_quantity 等字段 - api/transaction.ts:同名的历史副本同步签名(当前无调用方,加注释指向唯一来源) - views/outbound/create.vue、views/transaction/borrow.vue:各 3 处调用点带上 单据 id;refreshStockFromDraft 改为显式透传 id,不依赖 computed 已解析 - views/borrow/approval/index.vue:审批页也带上本单 id,否则待审批单 (预占已生效)锁定的行会因实时可用量为 0 而从备选列表消失 借库侧 request_id 是 BorrowApproval.id,靠 biz_type='borrow' 与出库单区分 —— 两张表 ID 空间独立,不可省。
This commit is contained in:
@ -469,10 +469,13 @@ const loadAlternatives = async (row: any) => {
|
||||
altItems.value = []
|
||||
altTotal.value = 0
|
||||
try {
|
||||
const res: any = await getStockAlternatives(row.base_id, {
|
||||
stock_id: row.stock_id,
|
||||
source_table: row.source_table,
|
||||
})
|
||||
// ★ 带上本单 id:本单锁定的批次实时可用量为 0,不回加的话会从列表里消失
|
||||
const res: any = await getStockAlternatives(
|
||||
row.base_id,
|
||||
{ stock_id: row.stock_id, source_table: row.source_table },
|
||||
selectedRequest.value?.id,
|
||||
'outbound'
|
||||
)
|
||||
altItems.value = res?.data?.items || []
|
||||
altTotal.value = res?.data?.total_available || 0
|
||||
} catch (e) {
|
||||
@ -674,7 +677,7 @@ const restoreDraft = async (requestId: number) => {
|
||||
// ★ 刷新库存:草稿里的 available_quantity 是**扫描那一刻**的快照,
|
||||
// 若期间别人出库了,界面会显示陈旧数字,工人扫满后到提交时才被
|
||||
// 后端拒绝(白扫一场)。这里重新拉一次实时可用量。
|
||||
await refreshStockFromDraft()
|
||||
await refreshStockFromDraft(requestId)
|
||||
} catch (e) {
|
||||
console.warn('恢复草稿失败', e)
|
||||
}
|
||||
@ -684,20 +687,28 @@ const restoreDraft = async (requestId: number) => {
|
||||
* 用实时库存刷新购物车行的「库存」列。
|
||||
*
|
||||
* 草稿里的 available_quantity 是扫描那一刻的快照,恢复时可能已过期
|
||||
* (期间别人出库/借出/报废都会消耗可用量)。这里按 base_id 查一次实时
|
||||
* 可用量,避免工人基于陈旧数字扫满、到提交时才发现不够。
|
||||
* (期间别人出库/借出/报废都会消耗可用量)。这里按 base_id 查一次实时值,
|
||||
* 避免工人基于陈旧数字扫满、到提交时才发现不够。
|
||||
*
|
||||
* 复用 /alternatives 端点 —— 它本来就按 base_id 返回各库存行的实时可用量。
|
||||
* 复用 /alternatives 端点 —— 它按 base_id 返回各库存行的实时可用量。
|
||||
* ★ 传入本单 id 后,返回的 available_quantity 是**有效可用量**
|
||||
* (实时值 + 本单预占),故本单锁定行不会因实时值为 0 而被过滤掉,
|
||||
* 下面的「已扫数量 > 最新可用量」判定也不会误报。
|
||||
* 失败不阻断:拿不到实时值就沿用草稿快照,至少不影响继续作业。
|
||||
*/
|
||||
const refreshStockFromDraft = async () => {
|
||||
const refreshStockFromDraft = async (requestId?: number | null) => {
|
||||
const plan = selectedRequest.value?.items || []
|
||||
const baseIds = [...new Set(plan.map((p: any) => p.base_id).filter(Boolean))]
|
||||
if (!baseIds.length) return
|
||||
|
||||
// ★ 显式透传单据 id(而非依赖 selectedRequest 已解析),保证预占回加一定生效
|
||||
const rid = requestId ?? selectedRequest.value?.id
|
||||
|
||||
try {
|
||||
const results = await Promise.all(
|
||||
(baseIds as number[]).map(bid => getStockAlternatives(bid).catch(() => null))
|
||||
(baseIds as number[]).map(bid =>
|
||||
getStockAlternatives(bid, undefined, rid, 'outbound').catch(() => null)
|
||||
)
|
||||
)
|
||||
const latestByKey = new Map<string, number>()
|
||||
for (const r of results) {
|
||||
@ -874,7 +885,9 @@ const handleManualInput = async () => {
|
||||
}
|
||||
|
||||
// 2. 调用 API 查询
|
||||
const res = await getStockByBarcode(code)
|
||||
// ★ 带上本单 id:申请阶段已把本单要的货从 available_quantity 扣掉(预占),
|
||||
// 不回加的话,被本单占满的行会显示 0,工人根本扫不进去。
|
||||
const res = await getStockByBarcode(code, selectedRequest.value?.id, 'outbound')
|
||||
if (res.data) {
|
||||
const item = res.data
|
||||
const availQty = parseFloat(item.available_quantity || 0)
|
||||
|
||||
Reference in New Issue
Block a user