feat(pl): add arrange-required check for stop product fields

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
zlwuandCursor committed 2026-08-30 17:52:47 +08:00
1 parent 79b33c6ba1
commit 765a27779c
2 files changed
+124

No files matched your search

@@ -0,0 +1,66 @@
import assert from 'node:assert/strict'
import { describe, it } from 'node:test'
import { findFirstStopProductArrangeGap } from './pl-plan-stop-product-arrange-required'
function row(partial: Record<string, unknown> = {}) {
return {
productId: 'p1',
unit: '吨',
planQty: 10,
packageSpec: '槽罐',
deliveryMethod: '送货上门',
...partial
}
}
describe('findFirstStopProductArrangeGap', () => {
it('returns null when load and deliveries are complete', () => {
assert.equal(
findFirstStopProductArrangeGap({
loadProducts: [row()],
deliveries: [{ products: [row()] }]
}),
null
)
})
it('flags load product missing fields including planQty 0', () => {
assert.equal(
findFirstStopProductArrangeGap({
loadProducts: [row({ unit: '', planQty: 0, packageSpec: ' ', deliveryMethod: null })],
deliveries: []
}),
'安排运输前请完善装货产品第1行:单位、计划数量、包装规格、送货方式'
)
})
it('flags delivery segment product and stops at first gap', () => {
assert.equal(
findFirstStopProductArrangeGap({
loadProducts: [row()],
deliveries: [
{ products: [row()] },
{ products: [row({ packageSpec: '' }), row({ deliveryMethod: '' })] }
]
}),
'安排运输前请完善送货段#2产品第1行:包装规格'
)
})
it('treats null/undefined/NaN planQty as missing', () => {
assert.equal(
findFirstStopProductArrangeGap({
loadProducts: [row({ planQty: undefined })],
deliveries: []
}),
'安排运输前请完善装货产品第1行:计划数量'
)
assert.equal(
findFirstStopProductArrangeGap({
loadProducts: [row({ planQty: Number.NaN })],
deliveries: []
}),
'安排运输前请完善装货产品第1行:计划数量'
)
})
})
@@ -0,0 +1,58 @@
export type ArrangeRequiredProductLike = {
unit?: string | null
planQty?: number | null
packageSpec?: string | null
deliveryMethod?: string | null
}
export type ArrangeRequiredDeliveryLike = {
products?: ArrangeRequiredProductLike[] | null
}
const FIELD_LABELS = {
unit: '单位',
planQty: '计划数量',
packageSpec: '包装规格',
deliveryMethod: '送货方式'
} as const
type FieldKey = keyof typeof FIELD_LABELS
function missingFields(row: ArrangeRequiredProductLike): FieldKey[] {
const gaps: FieldKey[] = []
if (!String(row.unit ?? '').trim()) gaps.push('unit')
const qty = Number(row.planQty)
if (!Number.isFinite(qty) || qty <= 0) gaps.push('planQty')
if (!String(row.packageSpec ?? '').trim()) gaps.push('packageSpec')
if (!String(row.deliveryMethod ?? '').trim()) gaps.push('deliveryMethod')
return gaps
}
function formatGap(prefix: string, fields: FieldKey[]): string {
return `${prefix}${fields.map((k) => FIELD_LABELS[k]).join('、')}`
}
/** 返回第一处产品字段缺失提示;齐全则 null */
export function findFirstStopProductArrangeGap(input: {
loadProducts?: ArrangeRequiredProductLike[] | null
deliveries?: ArrangeRequiredDeliveryLike[] | null
}): string | null {
const loadProducts = input.loadProducts || []
for (let i = 0; i < loadProducts.length; i += 1) {
const fields = missingFields(loadProducts[i])
if (fields.length) {
return formatGap(`安排运输前请完善装货产品第${i + 1}行:`, fields)
}
}
const deliveries = input.deliveries || []
for (let d = 0; d < deliveries.length; d += 1) {
const products = deliveries[d].products || []
for (let i = 0; i < products.length; i += 1) {
const fields = missingFields(products[i])
if (fields.length) {
return formatGap(`安排运输前请完善送货段#${d + 1}产品第${i + 1}行:`, fields)
}
}
}
return null
}