Procházet zdrojové kódy

feat: add per-row load and delivery qty helpers for plan list

Co-authored-by: Cursor <cursoragent@cursor.com>
wuzilong před 3 týdny
rodič
revize
c19318d0d0

+ 64 - 0
web-admin/src/utils/pl-plan-list-display-rows.test.ts

@@ -5,6 +5,9 @@ import {
   buildPlPlanListSpanMethod,
   flattenPlanArchivePairRows,
   flattenPlanListRows,
+  formatDashNumber,
+  getRowSideIoQty,
+  getRowSidePlanQty,
   matchLoadProduct,
   PL_DELIVERY_SPAN_PROPS,
   PL_PLAN_SPAN_PROPS
@@ -189,3 +192,64 @@ describe('buildPlPlanListSpanMethod two-level', () => {
     )
   })
 })
+
+describe('row side qty helpers', () => {
+  it('reads load and delivery qtys from matched products', () => {
+    const rows = flattenPlanArchivePairRows([samplePlan()])
+    assert.equal(getRowSidePlanQty(rows[0], 'load'), 30)
+    assert.equal(getRowSidePlanQty(rows[0], 'delivery'), 20)
+    assert.equal(getRowSidePlanQty(rows[1], 'load'), 10)
+    assert.equal(getRowSidePlanQty(rows[1], 'delivery'), 8)
+    assert.equal(getRowSidePlanQty(rows[2], 'load'), 30)
+    assert.equal(getRowSidePlanQty(rows[2], 'delivery'), 12)
+  })
+
+  it('returns null and dash when qty is missing', () => {
+    const rows = flattenPlanArchivePairRows([samplePlan()])
+    const empty = { ...rows[0], loadProduct: undefined, deliveryProduct: undefined }
+    assert.equal(getRowSidePlanQty(empty, 'load'), null)
+    assert.equal(formatDashNumber(null), '—')
+    assert.equal(formatDashNumber(12), '12')
+  })
+
+  it('maps IO qty by internal customer direction', () => {
+    const plan = samplePlan()
+    plan.load = {
+      ...plan.load,
+      customerName: '安徽云栋化工有限公司',
+      products: [
+        sp({
+          planProductId: 'pp-u',
+          productId: 'u',
+          productName: '尿素',
+          planQty: 30,
+          inboundQty: 1,
+          outboundQty: 28
+        })
+      ]
+    }
+    plan.deliveries = [
+      {
+        seq: 1,
+        customerName: '宜昌磷肥厂',
+        products: [
+          sp({
+            planProductId: 'pp-u',
+            productId: 'u',
+            productName: '尿素',
+            planQty: 20,
+            inboundQty: 2,
+            outboundQty: 19
+          })
+        ]
+      }
+    ] as Plan['deliveries']
+    const rows = flattenPlanArchivePairRows([plan])
+    // 装货内部客户 → 出库列展示;数量仍读装货段 inboundQty
+    assert.equal(getRowSideIoQty(rows[0], 'load', 'outbound'), 1)
+    assert.equal(getRowSideIoQty(rows[0], 'load', 'inbound'), null)
+    // 送货外部客户 → 出库列展示;数量读送货段 outboundQty
+    assert.equal(getRowSideIoQty(rows[0], 'delivery', 'outbound'), 19)
+    assert.equal(getRowSideIoQty(rows[0], 'delivery', 'inbound'), null)
+  })
+})

+ 32 - 0
web-admin/src/utils/pl-plan-list-display-rows.ts

@@ -602,6 +602,38 @@ export function resolveArchiveListIoColumn(
   return internal ? 'inbound' : 'outbound'
 }
 
+/** 列表行:读取装货/送货侧计划数量(单件) */
+export function getRowSidePlanQty(
+  row: PlPlanListDisplayRow,
+  side: 'load' | 'delivery'
+): number | null {
+  const product = side === 'load' ? row.loadProduct : row.deliveryProduct
+  if (product?.planQty == null || product.planQty === ('' as unknown)) return null
+  const num = Number(product.planQty)
+  return Number.isFinite(num) ? num : null
+}
+
+export function formatDashNumber(value: number | null | undefined): string {
+  if (value == null) return '—'
+  return String(value)
+}
+
+/**
+ * 列表行:按装/送侧与入出库列读取数量。
+ * 列是否展示由 resolveArchiveListIoColumn 决定;数值仍走 getStopArchiveActualQty。
+ */
+export function getRowSideIoQty(
+  row: PlPlanListDisplayRow,
+  side: 'load' | 'delivery',
+  column: 'inbound' | 'outbound'
+): number | null {
+  const stop = side === 'load' ? getPairLoadStop(row) : getPairDeliveryStop(row)
+  const product = side === 'load' ? row.loadProduct : row.deliveryProduct
+  if (!product) return null
+  if (resolveArchiveListIoColumn(side, stop?.customerName) !== column) return null
+  return getStopArchiveActualQty(product, side)
+}
+
 /** 段次归档实际数量(装货读 inboundQty,送货读 outboundQty) */
 export function getStopArchiveActualQty(product: PlanStopProduct, kind: PlPlanListDisplayRowKind): number | null {
   const raw = kind === 'load' ? product.inboundQty : product.outboundQty