fix(consignment): 兼容历史整单履约的赠送操作

无占用记录时按库存绑定工单兜底;前端无 allocation/share 时不再传 workOrderId,修复卖家提交赠送截图的订单占用报错。
This commit is contained in:
2026-11-13 15:09:40 +08:00
parent 1f3c98c071
commit dd0c56c265
6 changed files with 162 additions and 11 deletions
@@ -0,0 +1,44 @@
import assert from 'node:assert/strict'
import test from 'node:test'
import { isLegacyBoundConsignmentTarget } from './consignment-action-target.js'
function baseInput() {
return {
inventoryStatus: 'consumed',
boundWorkOrderId: 204855,
allocationId: 0,
shareId: 0,
workOrderId: 204855,
hasActiveAllocation: false,
}
}
test('历史整单履约:没有占用记录且指向库存绑定工单时允许继续', () => {
assert.equal(isLegacyBoundConsignmentTarget(baseInput()), true)
})
test('历史兜底只接受 consumed 库存', () => {
assert.equal(
isLegacyBoundConsignmentTarget({ ...baseInput(), inventoryStatus: 'in_stock' }),
false,
)
})
test('有新的有效占用时不得回退到库存绑定工单', () => {
assert.equal(isLegacyBoundConsignmentTarget({ ...baseInput(), hasActiveAllocation: true }), false)
})
test('显式指定 allocation/share 但没有匹配记录时不得兜底', () => {
assert.equal(isLegacyBoundConsignmentTarget({ ...baseInput(), allocationId: 11 }), false)
assert.equal(isLegacyBoundConsignmentTarget({ ...baseInput(), shareId: 22 }), false)
})
test('工单号与库存绑定工单不一致时不得兜底', () => {
assert.equal(isLegacyBoundConsignmentTarget({ ...baseInput(), workOrderId: 204856 }), false)
assert.equal(isLegacyBoundConsignmentTarget({ ...baseInput(), boundWorkOrderId: null }), false)
})
test('没有传工单号时不需要历史兜底', () => {
assert.equal(isLegacyBoundConsignmentTarget({ ...baseInput(), workOrderId: 0 }), false)
})
@@ -0,0 +1,21 @@
/**
* 历史整单履约兼容判断。
*
* 旧的后台「内部库存履约」只写 consignment_inventories.bound_work_order_id,
* 不会生成 consignment_inventory_allocations。卖家端按绑定工单发起操作时,
* 只要库存仍是 consumed 且没有新的有效占用,就允许按整单继续。
*/
export function isLegacyBoundConsignmentTarget(input: {
inventoryStatus: string
boundWorkOrderId: number | string | null | undefined
allocationId: number
shareId: number
workOrderId: number
hasActiveAllocation: boolean
}): boolean {
if (input.hasActiveAllocation) return false
if (input.allocationId > 0 || input.shareId > 0) return false
if (input.workOrderId <= 0) return false
if (String(input.inventoryStatus || '').trim() !== 'consumed') return false
return Number(input.boundWorkOrderId || 0) === input.workOrderId
}
@@ -32,6 +32,7 @@ import {
resolvePriceDropSuggestion,
} from './consignment-profile-service.js'
import { suggestConsignmentPriceFromRules } from './consignment-matching-service.js'
import { isLegacyBoundConsignmentTarget } from './consignment-action-target.js'
import {
getEnabledWorkProductRuleByKey,
parseTemplateSpecs,
@@ -1010,7 +1011,19 @@ async function resolveSellerConsignmentWorkOrder(
errorCode: 'consignment_allocation_selection_required',
})
}
if ((allocationIdInput > 0 || shareIdInput > 0 || workOrderIdInput > 0) && !allocation) {
const isLegacyBoundTarget = isLegacyBoundConsignmentTarget({
inventoryStatus: inventory.status,
boundWorkOrderId: inventory.bound_work_order_id,
allocationId: allocationIdInput,
shareId: shareIdInput,
workOrderId: workOrderIdInput,
hasActiveAllocation: allocations.length > 0,
})
if (
(allocationIdInput > 0 || shareIdInput > 0 || workOrderIdInput > 0) &&
!allocation &&
!isLegacyBoundTarget
) {
throw createHttpError('未找到该寄卖单对应的订单占用,请刷新后重试', {
statusCode: 404,
errorCode: 'consignment_allocation_not_found',