fix(consignment): 兼容历史整单履约的赠送操作
无占用记录时按库存绑定工单兜底;前端无 allocation/share 时不再传 workOrderId,修复卖家提交赠送截图的订单占用报错。
This commit is contained in:
@@ -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',
|
||||
|
||||
@@ -63,6 +63,7 @@ import type {
|
||||
} from '@/types/consignment'
|
||||
import type { UploadedFile } from '@/types/worker-platform'
|
||||
import { DEFAULT_CONSIGNMENT_INTAKE_FIELDS, buildSpecItemName } from '@/utils/consignment-spec'
|
||||
import { resolveConsignmentActionTarget } from '@/utils/consignment-action-target'
|
||||
import { buildConsignmentCategoryGroups } from '@/utils/consignment-category-tree'
|
||||
import { formatConsignmentContactSupporter } from '@/utils/consignment-contact'
|
||||
import { formatGiftCooldownRemaining } from '@/utils/consignment-cooldown'
|
||||
@@ -757,14 +758,6 @@ export default function WorkerConsignmentPage() {
|
||||
return null
|
||||
}
|
||||
|
||||
function resolveActionTarget(item: WorkerConsignmentRecord) {
|
||||
return {
|
||||
workOrderId: item.workOrderId,
|
||||
shareId: item.shareId ?? null,
|
||||
allocationId: item.allocationId ?? null,
|
||||
}
|
||||
}
|
||||
|
||||
function handleConfirmFriend(item: WorkerConsignmentRecord) {
|
||||
const submissionId = resolveActionSubmissionId(item)
|
||||
if (submissionId == null) return
|
||||
@@ -789,7 +782,10 @@ export default function WorkerConsignmentPage() {
|
||||
centered: true,
|
||||
onOk: async () => {
|
||||
try {
|
||||
await confirmWorkerConsignmentFriendAdded(submissionId, resolveActionTarget(item))
|
||||
await confirmWorkerConsignmentFriendAdded(
|
||||
submissionId,
|
||||
resolveConsignmentActionTarget(item),
|
||||
)
|
||||
message.success('已确认加好友,赠送冷却开始')
|
||||
await myConsignmentsQuery.refetch()
|
||||
} catch (err: any) {
|
||||
@@ -831,7 +827,7 @@ export default function WorkerConsignmentPage() {
|
||||
...(note ? { note } : {}),
|
||||
}
|
||||
await submitWorkerConsignmentGiftEvidence(submissionId, {
|
||||
...resolveActionTarget(evidenceItem),
|
||||
...resolveConsignmentActionTarget(evidenceItem),
|
||||
...payload,
|
||||
})
|
||||
message.success('赠送截图已提交,等待客服验收')
|
||||
|
||||
@@ -0,0 +1,29 @@
|
||||
export type ConsignmentActionTargetInput = {
|
||||
workOrderId?: number | null
|
||||
shareId?: number | null
|
||||
allocationId?: number | null
|
||||
}
|
||||
|
||||
export type ConsignmentActionTarget = {
|
||||
workOrderId?: number | null
|
||||
shareId?: number | null
|
||||
allocationId?: number | null
|
||||
}
|
||||
|
||||
/**
|
||||
* 卖家侧操作定位参数。
|
||||
*
|
||||
* 有 allocation/share 时按新占用模型传参;历史整单履约没有占用记录,
|
||||
* 这时不要传 workOrderId,让后端回退到库存自身的 bound_work_order_id。
|
||||
*/
|
||||
export function resolveConsignmentActionTarget(
|
||||
item: ConsignmentActionTargetInput,
|
||||
): ConsignmentActionTarget {
|
||||
const hasAllocationTarget = Number(item.allocationId || 0) > 0 || Number(item.shareId || 0) > 0
|
||||
if (!hasAllocationTarget) return {}
|
||||
return {
|
||||
workOrderId: item.workOrderId ?? null,
|
||||
shareId: item.shareId ?? null,
|
||||
allocationId: item.allocationId ?? null,
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,48 @@
|
||||
import assert from 'node:assert/strict'
|
||||
import test from 'node:test'
|
||||
|
||||
import { resolveConsignmentActionTarget } from '../src/utils/consignment-action-target.ts'
|
||||
|
||||
test('历史整单履约没有占用/份额时不传 workOrderId', () => {
|
||||
assert.deepEqual(
|
||||
resolveConsignmentActionTarget({
|
||||
workOrderId: 204855,
|
||||
shareId: null,
|
||||
allocationId: null,
|
||||
}),
|
||||
{},
|
||||
)
|
||||
})
|
||||
|
||||
test('allocationId/shareId 为 0 也按无占用处理', () => {
|
||||
assert.deepEqual(
|
||||
resolveConsignmentActionTarget({
|
||||
workOrderId: 204855,
|
||||
shareId: 0,
|
||||
allocationId: 0,
|
||||
}),
|
||||
{},
|
||||
)
|
||||
})
|
||||
|
||||
test('有占用记录时携带完整定位参数', () => {
|
||||
assert.deepEqual(
|
||||
resolveConsignmentActionTarget({
|
||||
workOrderId: 204855,
|
||||
shareId: null,
|
||||
allocationId: 99,
|
||||
}),
|
||||
{ workOrderId: 204855, shareId: null, allocationId: 99 },
|
||||
)
|
||||
})
|
||||
|
||||
test('拼单份额同样保留定位参数', () => {
|
||||
assert.deepEqual(
|
||||
resolveConsignmentActionTarget({
|
||||
workOrderId: 204855,
|
||||
shareId: 88,
|
||||
allocationId: null,
|
||||
}),
|
||||
{ workOrderId: 204855, shareId: 88, allocationId: null },
|
||||
)
|
||||
})
|
||||
Reference in New Issue
Block a user