feat(consignment): 统一寄卖匹配看板口径并收口占用与重匹配
- 看板与引擎统一数量、模板规格、正向规格信号判定,纳入 internal_matching/unassigned - 库存侧改走新引擎匹配,移除旧 /fulfill 手动入口与旧弹窗 - 验收时将 reserved allocation 收口为 consumed,补份额级收口 - 拼单重新匹配通过 material 排除全部原库存/原卖家 - 开发/测试环境放宽迁移顺序校验,生产保持严格;修正集成测试份额报酬口径
This commit is contained in:
@@ -52,7 +52,7 @@ import {
|
||||
updateAdminConsignmentInventory,
|
||||
} from '@/services/admin/consignment'
|
||||
import { fetchAdminWorkerUsers } from '@/services/admin/worker-platform'
|
||||
import ConsignmentManualMatchModal from '@/components/admin/ConsignmentManualMatchModal'
|
||||
import ConsignmentInventoryMatchModal from '@/components/admin/ConsignmentInventoryMatchModal'
|
||||
import type {
|
||||
ConsignmentInventory,
|
||||
ConsignmentInventoryFilterField,
|
||||
@@ -1466,19 +1466,13 @@ export default function ConsignmentInventoryPanel() {
|
||||
</Form>
|
||||
</Modal>
|
||||
|
||||
{/* 手动匹配订单:从现货出发挑订单(与订单侧共用同一弹窗) */}
|
||||
<ConsignmentManualMatchModal
|
||||
{/* 匹配订单:从现货出发,走新引擎(数量/配置/重新匹配排除与看板同口径) */}
|
||||
<ConsignmentInventoryMatchModal
|
||||
key={manualMatchItem?.id ?? 'none'}
|
||||
open={Boolean(manualMatchItem)}
|
||||
inventoryId={manualMatchItem?.id ?? null}
|
||||
inventoryLabel={manualMatchItem?.itemName}
|
||||
onClose={() => setManualMatchItem(null)}
|
||||
inventory={
|
||||
manualMatchItem
|
||||
? {
|
||||
inventoryId: manualMatchItem.id,
|
||||
label: manualMatchItem.itemName,
|
||||
inboundAt: manualMatchItem.inboundAt || manualMatchItem.createdAt,
|
||||
}
|
||||
: null
|
||||
}
|
||||
onMatched={() => void inventoriesQuery.refetch()}
|
||||
/>
|
||||
|
||||
|
||||
@@ -92,20 +92,17 @@ import {
|
||||
import type { CustomerRefundPreview } from '@/services/admin/worker-platform'
|
||||
import {
|
||||
checkWorkOrderConsignmentMatches,
|
||||
fulfillWorkOrderWithConsignment,
|
||||
matchAdminConsignmentWorkOrder,
|
||||
revokeWorkOrderConsignmentFulfillment,
|
||||
} from '@/services/admin/consignment'
|
||||
import { notifyVoucherConsume } from '@/utils/voucher-consume'
|
||||
import type { ConsignmentFulfillmentInfo, WorkOrderInventoryMatchCheck } from '@/types/consignment'
|
||||
import type { UploadedFile, WorkOrder, WorkOrderShare } from '@/types/worker-platform'
|
||||
import { canManuallyMatchConsignmentOrder } from '@/utils/consignment-manual-match'
|
||||
import { formatConsignmentContactSupporter } from '@/utils/consignment-contact'
|
||||
import {
|
||||
isConsignmentOrder,
|
||||
isInternalConsignmentOrder,
|
||||
resolveConsignmentScreenshotUrls,
|
||||
} from '@/utils/consignment-display'
|
||||
import ConsignmentManualMatchModal from '@/components/admin/ConsignmentManualMatchModal'
|
||||
import ConsignmentWorkOrderMatchModal from '@/components/admin/ConsignmentWorkOrderMatchModal'
|
||||
import { AdminRangePicker } from '@/components/admin/AdminDatePicker'
|
||||
import dayjs, { ADMIN_DATE_FORMAT } from '@/lib/dayjs'
|
||||
@@ -433,7 +430,6 @@ export default function WorkOrdersPanel() {
|
||||
}>()
|
||||
|
||||
// 寄售库存匹配拦截弹窗状态
|
||||
const [manualMatchOrder, setManualMatchOrder] = useState<WorkOrder | null>(null)
|
||||
const [consignmentMatchOrder, setConsignmentMatchOrder] = useState<WorkOrder | null>(null)
|
||||
const [consignmentInterceptModal, setConsignmentInterceptModal] = useState<{
|
||||
open: boolean
|
||||
@@ -1207,17 +1203,23 @@ export default function WorkOrdersPanel() {
|
||||
if (!consignmentInterceptModal.order) return
|
||||
setConsignmentInterceptModal((prev) => ({ ...prev, submitting: true }))
|
||||
try {
|
||||
const res = await fulfillWorkOrderWithConsignment({
|
||||
const res = await matchAdminConsignmentWorkOrder({
|
||||
workOrderId: consignmentInterceptModal.order.workOrderId,
|
||||
inventoryId,
|
||||
allowPartial,
|
||||
inventoryIds: [inventoryId],
|
||||
allowNonExact: allowPartial,
|
||||
reason: allowPartial ? '内部寄售库存手动匹配(非精确)' : '',
|
||||
})
|
||||
message.success('已占用内部库存,跳过打手资料,已进入待加好友阶段')
|
||||
notifyVoucherConsume(res?.data?.voucherConsume)
|
||||
if (res.data?.matched === false) {
|
||||
message.warning('工单状态或库存已变化,请刷新后重试')
|
||||
setConsignmentInterceptModal((prev) => ({ ...prev, submitting: false }))
|
||||
await refreshAll()
|
||||
return
|
||||
}
|
||||
message.success('已占用内部库存并匹配工单')
|
||||
setConsignmentInterceptModal({ open: false, order: null, matchData: null, submitting: false })
|
||||
await refreshAll()
|
||||
} catch (err: any) {
|
||||
message.error(err.message || '内部履约失败')
|
||||
message.error(err.message || '内部库存匹配失败')
|
||||
setConsignmentInterceptModal((prev) => ({ ...prev, submitting: false }))
|
||||
}
|
||||
}
|
||||
@@ -2066,18 +2068,9 @@ export default function WorkOrdersPanel() {
|
||||
</Button>
|
||||
) : null}
|
||||
{/*
|
||||
手动指定现货必须**常驻**:只在"检测到候选"的提醒弹窗里给入口,
|
||||
订单没有任何系统候选时客服反而进不去——而那正是最需要人工指定的时候。
|
||||
寄卖匹配只保留新引擎入口:旧的 /fulfill 手动匹配不支持数量拆分,
|
||||
还会绕过 profile/重新匹配排除,已从 UI 收敛掉。
|
||||
*/}
|
||||
{canFulfillConsignment && canManuallyMatchConsignmentOrder(row) ? (
|
||||
<Button
|
||||
icon={<ThunderboltOutlined />}
|
||||
onClick={() => setManualMatchOrder(row)}
|
||||
title="从全部在库现货里人工指定一件(支持规格冲突强制匹配,需填原因)"
|
||||
>
|
||||
手动匹配寄售
|
||||
</Button>
|
||||
) : null}
|
||||
{canFulfillConsignment &&
|
||||
['internal_matching', 'unassigned', 'open'].includes(String(row.status || '')) ? (
|
||||
<Button
|
||||
@@ -4411,10 +4404,10 @@ export default function WorkOrdersPanel() {
|
||||
matchData: null,
|
||||
submitting: false,
|
||||
})
|
||||
setManualMatchOrder(currentOrder)
|
||||
setConsignmentMatchOrder(currentOrder)
|
||||
}}
|
||||
>
|
||||
手动指定现货匹配
|
||||
内部库存匹配
|
||||
</Button>
|
||||
) : null,
|
||||
<Button
|
||||
@@ -4627,23 +4620,6 @@ export default function WorkOrdersPanel() {
|
||||
}}
|
||||
/>
|
||||
|
||||
<ConsignmentManualMatchModal
|
||||
open={Boolean(manualMatchOrder)}
|
||||
onClose={() => setManualMatchOrder(null)}
|
||||
workOrder={
|
||||
manualMatchOrder
|
||||
? {
|
||||
workOrderId: manualMatchOrder.workOrderId,
|
||||
label: `#${manualMatchOrder.workOrderId} ${manualMatchOrder.productName || ''}`,
|
||||
createdAt: manualMatchOrder.createdAt,
|
||||
}
|
||||
: null
|
||||
}
|
||||
onMatched={() => {
|
||||
void refreshAll()
|
||||
}}
|
||||
/>
|
||||
|
||||
{/* 撤单 / 撤销寄售履约弹窗 */}
|
||||
<Modal
|
||||
title={`撤单 / 撤销寄售履约 · ${getDisplayOrderNo(revokeConsignmentModal.order)}`}
|
||||
|
||||
Reference in New Issue
Block a user