From 765a27779c9b405dfed6fdd393b4cd39e176bcc0 Mon Sep 17 00:00:00 2001 From: zlwu <704541015@qq.com> Date: Sun, 30 Aug 2026 17:52:47 +0800 Subject: [PATCH] feat(pl): add arrange-required check for stop product fields Co-authored-by: Cursor --- ...plan-stop-product-arrange-required.test.ts | 66 +++++++++++++++++++ .../pl-plan-stop-product-arrange-required.ts | 58 ++++++++++++++++ 2 files changed, 124 insertions(+) create mode 100644 web-admin/src/utils/pl-plan-stop-product-arrange-required.test.ts create mode 100644 web-admin/src/utils/pl-plan-stop-product-arrange-required.ts diff --git a/web-admin/src/utils/pl-plan-stop-product-arrange-required.test.ts b/web-admin/src/utils/pl-plan-stop-product-arrange-required.test.ts new file mode 100644 index 000000000..f36c18cf7 --- /dev/null +++ b/web-admin/src/utils/pl-plan-stop-product-arrange-required.test.ts @@ -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 = {}) { + 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行:计划数量' + ) + }) +}) diff --git a/web-admin/src/utils/pl-plan-stop-product-arrange-required.ts b/web-admin/src/utils/pl-plan-stop-product-arrange-required.ts new file mode 100644 index 000000000..9dfc875e0 --- /dev/null +++ b/web-admin/src/utils/pl-plan-stop-product-arrange-required.ts @@ -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 +}