This commit is contained in:
2026-10-15 18:25:23 +08:00
parent af2e1481a6
commit 538970797d
3 changed files with 28 additions and 6 deletions
@@ -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
@@ -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()
@@ -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<string, number>()
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' },