feat(pl): hard-block over-limit contract qty on arrange and remind after success
Validate arranged+this plan qty against fixed contract product quantities on transport arrange; return post-success remaining/framework cumulative reminders. Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
1 parent
d326996976
commit
3011cb9dae
10 files changed
+1424
-8
No files matched your search
@@ -149,7 +149,13 @@ bxAxios.interceptors.response.use(
|
||||
await userStore.logOut()
|
||||
} else {
|
||||
const detail = err.response?.data?.detail || err.response?.data?.msg
|
||||
ElMessage.error(detail || err.message || '网络异常')
|
||||
const message =
|
||||
typeof detail === 'string'
|
||||
? detail
|
||||
: detail && typeof detail === 'object'
|
||||
? detail.message || detail.msg || JSON.stringify(detail)
|
||||
: detail
|
||||
ElMessage.error(message || err.message || '网络异常')
|
||||
}
|
||||
return Promise.reject(err)
|
||||
}
|
||||
|
||||
@@ -0,0 +1,108 @@
|
||||
import { ElMessageBox } from 'element-plus'
|
||||
import type { ContractQtyReminder, ContractQtyReminderItem } from '@/api/pl'
|
||||
|
||||
export const CONTRACT_QTY_VIOLATION_CODE = 'CONTRACT_QTY_OVERFLOW'
|
||||
|
||||
function formatQty(n: number | undefined | null): string {
|
||||
if (n == null || Number.isNaN(Number(n))) return '—'
|
||||
return Number(n).toFixed(3)
|
||||
}
|
||||
|
||||
function stopKindLabel(kind?: string): string {
|
||||
if (kind === 'delivery') return '送货'
|
||||
if (kind === 'load') return '装货'
|
||||
return kind || ''
|
||||
}
|
||||
|
||||
export function extractContractQtyViolationDetail(error: unknown): {
|
||||
message: string
|
||||
items: Array<Record<string, unknown>>
|
||||
} | null {
|
||||
const err = error as {
|
||||
response?: { status?: number; data?: { detail?: unknown } }
|
||||
}
|
||||
const detail = err?.response?.data?.detail
|
||||
if (!detail || typeof detail !== 'object') return null
|
||||
const d = detail as { code?: string; message?: string; items?: unknown }
|
||||
if (d.code !== CONTRACT_QTY_VIOLATION_CODE) return null
|
||||
const items = Array.isArray(d.items) ? (d.items as Array<Record<string, unknown>>) : []
|
||||
return {
|
||||
message: d.message || '合同产品安排数量超过合同数量',
|
||||
items
|
||||
}
|
||||
}
|
||||
|
||||
export function formatContractQtyViolationMessage(
|
||||
items: Array<Record<string, unknown>>
|
||||
): string {
|
||||
if (!items.length) return '合同产品安排数量超过合同数量,请调整后再试。'
|
||||
const lines = items.map((it) => {
|
||||
const name = String(it.productName || it.productId || '产品')
|
||||
const contractNo = String(it.contractNo || it.contractId || '')
|
||||
const side = stopKindLabel(String(it.stopKind || ''))
|
||||
const contractQty = formatQty(Number(it.contractQty))
|
||||
const effective = formatQty(Number(it.effectiveTotal ?? it.arrangedTotalQty))
|
||||
const overflow = formatQty(Number(it.overflowQty))
|
||||
const meta = [contractNo, side].filter(Boolean).join(' · ')
|
||||
return `「${name}」安排后合计 ${effective},超过合同数量 ${contractQty}(超 ${overflow})${meta ? `;${meta}` : ''}`
|
||||
})
|
||||
return `以下产品「已安排合计 + 本次安排」超过合同数量,请调整后再试:\n\n${lines.join('\n')}`
|
||||
}
|
||||
|
||||
export function formatContractQtyReminderMessage(reminder: ContractQtyReminder): string {
|
||||
const items = reminder?.items || []
|
||||
if (!items.length) return ''
|
||||
const remaining = items.filter((i) => i.mode === 'remaining')
|
||||
const framework = items.filter((i) => i.mode === 'framework_cumulative')
|
||||
const blocks: string[] = []
|
||||
|
||||
if (remaining.length) {
|
||||
const lines = remaining.map((it: ContractQtyReminderItem) => {
|
||||
const name = it.productName || it.productId
|
||||
const meta = [it.contractNo, stopKindLabel(it.stopKind)].filter(Boolean).join(' · ')
|
||||
return `「${name}」已安排合计(含本次)${formatQty(it.arrangedTotalQty)},剩余可安排 ${formatQty(it.remainingQty)}${meta ? `;${meta}` : ''}`
|
||||
})
|
||||
blocks.push(`合同数量校验通过。各产品剩余可安排量:\n${lines.join('\n')}`)
|
||||
}
|
||||
|
||||
if (framework.length) {
|
||||
const lines = framework.map((it: ContractQtyReminderItem) => {
|
||||
const name = it.productName || it.productId
|
||||
const meta = [it.contractNo, stopKindLabel(it.stopKind)].filter(Boolean).join(' · ')
|
||||
return `「${name}」累计已安排 ${formatQty(it.arrangedTotalQty)}(本次 ${formatQty(it.thisPlanQty)})${meta ? `;${meta}` : ''}`
|
||||
})
|
||||
blocks.push(
|
||||
`关联框架合同无固定数量上限。本次安排后累计已安排:\n${lines.join('\n')}`
|
||||
)
|
||||
}
|
||||
|
||||
return blocks.join('\n\n')
|
||||
}
|
||||
|
||||
/** @returns true 表示已按合同超量处理(调用方勿再抛出/重复提示) */
|
||||
export async function showContractQtyViolationIfMatch(error: unknown): Promise<boolean> {
|
||||
const parsed = extractContractQtyViolationDetail(error)
|
||||
if (!parsed) return false
|
||||
await ElMessageBox.alert(formatContractQtyViolationMessage(parsed.items), '无法安排运输', {
|
||||
type: 'error',
|
||||
confirmButtonText: '知道了'
|
||||
})
|
||||
return true
|
||||
}
|
||||
|
||||
export async function showContractQtyReminderIfAny(
|
||||
reminder?: ContractQtyReminder | null
|
||||
): Promise<void> {
|
||||
const items = reminder?.items || []
|
||||
if (!items.length) return
|
||||
const message = formatContractQtyReminderMessage(reminder!)
|
||||
if (!message) return
|
||||
const hasFramework = items.some((i) => i.mode === 'framework_cumulative')
|
||||
const title = hasFramework && !items.some((i) => i.mode === 'remaining')
|
||||
? '安排成功 · 框架合同累计'
|
||||
: '安排成功 · 合同剩余数量'
|
||||
await ElMessageBox.alert(message, title, {
|
||||
type: 'success',
|
||||
confirmButtonText: '知道了'
|
||||
})
|
||||
}
|
||||
Reference in new issue
Block a user