Files
order_site/apps/frontend/src/utils/consignment-match-coverage.ts
T

83 lines
2.8 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
import type { ConsignmentMatchCandidate } from '../types/consignment'
/** 与寄售仓库「一键查询待匹配订单」同一口径的 0-100 匹配打分。 */
export function computeConsignmentMatchScore(
item: Pick<ConsignmentMatchCandidate, 'matchType' | 'matchDetails'>,
): number {
const details = (item.matchDetails || []).filter((detail) => detail.status !== 'ignored')
if (details.length === 0) {
const type = item.matchType || 'exact'
if (type === 'exact') return 100
if (type === 'partial') return 85
if (type === 'unknown') return 50
return 0
}
const matched = details.reduce((sum, detail) => {
if (detail.status === 'match') return sum + 1
if (detail.status === 'partial') return sum + 0.85
if (detail.status === 'unknown') return sum + 0.5
return sum
}, 0)
return Math.round((matched / details.length) * 100)
}
/** 匹配说明:精确 / 高配 / 缺规格 / 冲突原因。 */
export function describeConsignmentMatch(
item: Pick<ConsignmentMatchCandidate, 'matchType' | 'matchReasons'>,
): string {
const type = item.matchType || 'exact'
if (type === 'exact') return '规格完全一致'
const reasons = (item.matchReasons || []).filter(Boolean)
if (reasons.length > 0) {
return `${type === 'conflict' ? '不匹配' : '待核对'}:${reasons.join(';')}`
}
if (type === 'partial') return '规格高于要求(非精确)'
return '需人工核对'
}
/**
* 选中的库存是否够一次拼满。
*
* - 拼单:按剩余份数累加,必须覆盖「总份数 - 已匹配份数」;
* - 整单:只能选一条,且该条剩余数量必须 >= 订单总数量。
*/
export function resolveMatchCoverage(input: {
sharingEnabled: boolean
totalQuantity: number
activeQuantity: number
selected: Array<Pick<ConsignmentMatchCandidate, 'inventoryId' | 'remainingQuantity'>>
}): {
requiredQuantity: number
selectedQuantity: number
sufficient: boolean
gap: number
} {
const total = Math.max(1, Math.round(Number(input.totalQuantity || 0)))
const active = Math.max(0, Math.round(Number(input.activeQuantity || 0)))
const remaining = input.selected.reduce(
(sum, item) => sum + Math.max(0, Math.round(Number(item.remainingQuantity || 0))),
0,
)
if (input.sharingEnabled) {
const required = Math.max(0, total - active)
return {
requiredQuantity: required,
selectedQuantity: remaining,
sufficient: required > 0 && remaining >= required,
gap: Math.max(0, required - remaining),
}
}
const selectedQuantity = input.selected.reduce(
(max, item) => Math.max(max, Math.round(Number(item.remainingQuantity || 0))),
0,
)
return {
requiredQuantity: total,
selectedQuantity,
sufficient: input.selected.length === 1 && selectedQuantity >= total,
gap: Math.max(0, total - selectedQuantity),
}
}