From 538970797d4a58456eb3edd5418d1b8b42c6cf88 Mon Sep 17 00:00:00 2001 From: liuyihe <2513762445@qq.com> Date: Thu, 15 Oct 2026 18:25:23 +0800 Subject: [PATCH] =?UTF-8?q?=E4=BC=98=E5=8C=96?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../api/warehouseTransfers.ts | 1 + .../WarehouseTransferReceiptPanel.test.ts | 17 +++++++++++++++++ .../WarehouseTransferReceiptPanel.vue | 16 ++++++++++------ 3 files changed, 28 insertions(+), 6 deletions(-) diff --git a/src/modules/overseas-warehouse/api/warehouseTransfers.ts b/src/modules/overseas-warehouse/api/warehouseTransfers.ts index 3b62351f..03793bfa 100644 --- a/src/modules/overseas-warehouse/api/warehouseTransfers.ts +++ b/src/modules/overseas-warehouse/api/warehouseTransfers.ts @@ -41,6 +41,7 @@ export interface WarehouseTransferItemDto { } export interface WarehouseTransferCartonDto { + items?: Array<{ transferItemId: string; skuCode: string; quantity: number | string }> transferItemId?: string | null transferPalletId?: string | null length?: number | string | null diff --git a/src/modules/overseas-warehouse/components/WarehouseTransferReceiptPanel.test.ts b/src/modules/overseas-warehouse/components/WarehouseTransferReceiptPanel.test.ts index ae8bcb9b..7ad5d450 100644 --- a/src/modules/overseas-warehouse/components/WarehouseTransferReceiptPanel.test.ts +++ b/src/modules/overseas-warehouse/components/WarehouseTransferReceiptPanel.test.ts @@ -29,6 +29,23 @@ function fixture(): WarehouseTransferDetailDto { beforeEach(() => { vi.mocked(getWarehouseTransfer).mockReset(); vi.mocked(getWarehouseTransferCartonLabel).mockReset() }) afterEach(() => vi.restoreAllMocks()) describe('转运预报与实际签收明细', () => { + it('shows every SKU in a mixed box and counts its physical measurements once', async () => { + const value = fixture() + value.cartons = [{ ...value.cartons[0]!, quantity: 10, items: [{ transferItemId: '10', skuCode: 'SKU-A', quantity: 3 }, { transferItemId: '20', skuCode: 'SKU-B', quantity: 7 }] }] + value.pallets = [value.pallets[0]!] + vi.mocked(getWarehouseTransfer).mockResolvedValue(value) + const wrapper = render(); await flushPromises() + expect(wrapper.emitted('summary')!.at(-1)![0]).toMatchObject({ boxes: 1, skus: 2, quantity: 10, weight: 2.5, volume: .1 }) + const comparison = wrapper.findAllComponents(DataTableStub)[0]!.props('data') + expect(comparison[0]).toMatchObject({ receivedQuantity: 3, receivedBoxes: 1 }) + expect(comparison[1]).toMatchObject({ receivedQuantity: 7, receivedBoxes: 1 }) + expect(wrapper.findAll('.test-table')[1]!.text()).toContain('SKU-A × 3 件;SKU-B × 7 件') + await wrapper.findAll('button').find(button => button.text() === '查看箱号明细')!.trigger('click') + await wrapper.findAll('button').find(button => button.text() === '箱内明细')!.trigger('click') + const contents = wrapper.findAll('[data-test="box-dialog"]')[1]!.text() + expect(contents).toContain('SKU-A'); expect(contents).toContain('SKU-B') + expect(contents).toContain('产品 A'); expect(contents).toContain('产品 B') + }) it('keeps declarations separate and totals only counted boxes across pallets', async () => { vi.mocked(getWarehouseTransfer).mockResolvedValue(fixture()) const wrapper = render(); await flushPromises() diff --git a/src/modules/overseas-warehouse/components/WarehouseTransferReceiptPanel.vue b/src/modules/overseas-warehouse/components/WarehouseTransferReceiptPanel.vue index 8635fb06..57496409 100644 --- a/src/modules/overseas-warehouse/components/WarehouseTransferReceiptPanel.vue +++ b/src/modules/overseas-warehouse/components/WarehouseTransferReceiptPanel.vue @@ -23,18 +23,22 @@ const cartonsOpen = ref(false) let generation = 0 const cartons = computed(() => (detail.value?.cartons || []).filter(box => box.status === 'COUNTED')) function itemFor(box: WarehouseTransferCartonDto) { return detail.value?.items.find(item => box.transferItemId != null && String(item.id) === String(box.transferItemId)) } +function boxLines(box: WarehouseTransferCartonDto) { + if (box.items?.length) return box.items + return [{ transferItemId: box.transferItemId, skuCode: itemFor(box)?.skuCode || '—', quantity: box.quantity || 0 }] +} function volume(box: WarehouseTransferCartonDto) { return box.length == null || box.width == null || box.height == null ? null : Number(box.length) * Number(box.width) * Number(box.height) / 1_000_000 } function formatNumber(value: number | null, digits = 0) { return value == null || !Number.isFinite(value) ? '—' : value.toFixed(digits) } const totals = computed(() => ({ quantity: cartons.value.reduce((sum, box) => sum + Number(box.quantity || 0), 0), weight: cartons.value.reduce((sum, box) => sum + Number(box.weight || 0), 0), volume: cartons.value.reduce((sum, box) => sum + Number(volume(box) || 0), 0), - skus: new Set(cartons.value.map(box => itemFor(box)?.skuCode).filter(Boolean)).size, + skus: new Set(cartons.value.flatMap(box => boxLines(box).map(line => line.skuCode)).filter(Boolean)).size, })) watch([detail, totals], () => emit('summary', detail.value ? { ...totals.value, boxes: cartons.value.length, forecastBoxes: detail.value.packageCount, forecastSkus: new Set(detail.value.items.map(item => item.skuCode).filter(Boolean)).size, pallets: detail.value.pallets.length, warehouseId: detail.value.operatingWarehouseId } : null)) const comparisonRows = computed(() => (detail.value?.items || []).map(item => { - const boxes = cartons.value.filter(box => box.transferItemId != null && String(box.transferItemId) === String(item.id)) - const receivedQuantity = boxes.reduce((sum, box) => sum + Number(box.quantity || 0), 0) + const boxes = cartons.value.filter(box => boxLines(box).some(line => String(line.transferItemId) === String(item.id))) + const receivedQuantity = boxes.reduce((sum, box) => sum + boxLines(box).filter(line => String(line.transferItemId) === String(item.id)).reduce((total, line) => total + Number(line.quantity), 0), 0) return { ...item, receivedQuantity, receivedBoxes: boxes.length, quantityDifference: receivedQuantity - Number(item.quantity || 0), boxDifference: boxes.length - Number(item.packageCount || 0) } })) function differenceText(value: unknown) { @@ -45,11 +49,11 @@ function differenceText(value: unknown) { const palletRows = computed(() => (detail.value?.pallets || []).map(pallet => { const boxes = cartons.value.filter(box => box.transferPalletId != null && String(box.transferPalletId) === String(pallet.id)) const skus = new Map() - for (const box of boxes) { const sku = itemFor(box)?.skuCode || 'SKU 未留存'; skus.set(sku, (skus.get(sku) || 0) + Number(box.quantity || 0)) } + for (const box of boxes) for (const line of boxLines(box)) skus.set(line.skuCode, (skus.get(line.skuCode) || 0) + Number(line.quantity)) return { ...pallet, receivedBoxes: boxes.length, skuCount: skus.size, quantity: boxes.reduce((sum, box) => sum + Number(box.quantity || 0), 0), skuSummary: [...skus].map(([sku, quantity]) => `${sku} × ${quantity} 件`).join(';') || '—' } })) -const cartonRows = computed(() => cartons.value.map(box => ({ ...box, sku: itemFor(box)?.skuCode || '—', palletNo: detail.value?.pallets.find(pallet => box.transferPalletId != null && String(pallet.id) === String(box.transferPalletId))?.palletNo || '未关联托盘', dimensions: [box.length, box.width, box.height].every(value => value != null) ? `${box.length} × ${box.width} × ${box.height}` : '—', volumeText: formatNumber(volume(box), 6) }))) -const boxItems = computed(() => selectedBox.value ? [{ sku: itemFor(selectedBox.value)?.skuCode || '—', name: itemFor(selectedBox.value)?.productNameZh || '—', quantity: selectedBox.value.quantity ?? '—' }] : []) +const cartonRows = computed(() => cartons.value.map(box => ({ ...box, sku: boxLines(box).map(line => line.skuCode).join(';'), palletNo: detail.value?.pallets.find(pallet => box.transferPalletId != null && String(pallet.id) === String(box.transferPalletId))?.palletNo || '未关联托盘', dimensions: [box.length, box.width, box.height].every(value => value != null) ? `${box.length} × ${box.width} × ${box.height}` : '—', volumeText: formatNumber(volume(box), 6) }))) +const boxItems = computed(() => selectedBox.value ? boxLines(selectedBox.value).map(line => ({ sku: line.skuCode, name: detail.value?.items.find(item => String(item.id) === String(line.transferItemId))?.productNameZh || '—', quantity: line.quantity })) : []) const receiptLeadingColumns: DataTableColumn[] = [ { key: 'receivedQuantity', label: '实收件数', width: 100, align: 'center', headerAlign: 'center' }, { key: 'receivedBoxes', label: '实收箱数', width: 100, align: 'center', headerAlign: 'center' },