feat(consignment): 后端支持"手动指定现货"匹配(含规格冲突强制匹配留痕)
需求:客服在后台能人工指定现货与订单匹配,而不是只能用系统算出的候选。 - 新增纯函数 listConsignmentMatchOptions()/classifyConsignmentMatch():把某订单的**全部** 在库现货列出来并逐件判定 exact/partial/conflict,conflict 时给出字段级差异 (describeConsignmentSpecDifferences,例如「系统:订单「苹果」/ 现货「安卓」」) - 新增 describeWorkOrderSpecConflicts():强制匹配时把差异写进工单 material 与事件留痕 - fulfill/assign 两条落地链路支持 allowSpecMismatch(规格真冲突显式放行)+ matchNote (强制匹配必填原因,否则 400 consignment_match_note_required), material 与工单事件都记录 manualMatch/matchType/matchNote - 新增两个只读接口:订单侧 GET /consignment/matches/:workOrderId/options(全部在库现货+判定), 库存侧 GET /consignment/inventories/:id/match-orders(该现货可匹配的待指派/待补资料订单+判定) - 抽取 classifyConsignmentMatch 让正反两个方向共用同一套匹配口径,避免"界面说能选、接口却拒绝" 真实库验证:订单要苹果微信暗潮2钻、现货为安卓QQ苍龙4钻时正确判定 conflict 并列出 4 项差异; 不填原因强制匹配被拒(consignment_match_note_required);不强制则被规格复检拒绝 (consignment_spec_mismatch);填原因强制匹配成功且工单事件与 material 都有留痕。
This commit is contained in:
@@ -25,6 +25,11 @@ import {
|
||||
normalizeWorkerDepositHandling,
|
||||
} from '../../../domain/consignment-deposit-policy.js'
|
||||
|
||||
import {
|
||||
listInventoryMatchableOrders,
|
||||
listWorkOrderInventoryMatchOptions,
|
||||
} from '../../../services/worker-platform/consignment-fulfillment-service.js'
|
||||
|
||||
const router = Router()
|
||||
|
||||
// ---------------- 库存管理与统计 ----------------
|
||||
@@ -202,6 +207,38 @@ router.get(
|
||||
}),
|
||||
)
|
||||
|
||||
// 订单侧「手动指定现货」:全部在库现货 + 逐件标出规格是否相符
|
||||
router.get(
|
||||
'/worker-platform/consignment/matches/:workOrderId/options',
|
||||
requireAdminPermission('consignment.view'),
|
||||
createJsonHandler(
|
||||
(req) => listWorkOrderInventoryMatchOptions(String(req.params.workOrderId || '')),
|
||||
{
|
||||
successMessage: 'ok',
|
||||
errorMessage: '读取可指定现货失败',
|
||||
scope: '[admin/consignment/matches/options]',
|
||||
},
|
||||
),
|
||||
)
|
||||
|
||||
// 库存侧「手动匹配订单」:这件现货可以匹配的待指派/待补资料订单
|
||||
router.get(
|
||||
'/worker-platform/consignment/inventories/:id/match-orders',
|
||||
requireAdminPermission('consignment.view'),
|
||||
createJsonHandler(
|
||||
(req) =>
|
||||
listInventoryMatchableOrders(String(req.params.id || ''), {
|
||||
keyword: String(req.query.keyword || ''),
|
||||
pageSize: Number(req.query.pageSize || 0) || undefined,
|
||||
}),
|
||||
{
|
||||
successMessage: 'ok',
|
||||
errorMessage: '读取可匹配订单失败',
|
||||
scope: '[admin/consignment/inventories/match-orders]',
|
||||
},
|
||||
),
|
||||
)
|
||||
|
||||
router.post(
|
||||
'/worker-platform/consignment/fulfill',
|
||||
requireAdminPermission('consignment.fulfill'),
|
||||
@@ -212,6 +249,8 @@ router.post(
|
||||
inventoryId: String(req.body?.inventoryId || ''),
|
||||
actorName: req.adminSession?.username || 'admin',
|
||||
allowPartial: req.body?.allowPartial === true,
|
||||
allowSpecMismatch: req.body?.allowSpecMismatch === true,
|
||||
matchNote: String(req.body?.matchNote || ''),
|
||||
}),
|
||||
{
|
||||
successMessage: '已使用内部寄售库存直接履约!',
|
||||
@@ -232,6 +271,8 @@ router.post(
|
||||
workerId: req.body?.workerId ? Number(req.body.workerId) : null,
|
||||
actorName: req.adminSession?.username || 'admin',
|
||||
allowPartial: req.body?.allowPartial === true,
|
||||
allowSpecMismatch: req.body?.allowSpecMismatch === true,
|
||||
matchNote: String(req.body?.matchNote || ''),
|
||||
}),
|
||||
{
|
||||
successMessage: '已指派打手,押金已冻结,工单流转进行中!',
|
||||
|
||||
Reference in New Issue
Block a user