Files
admin-web/src/modules/overseas-warehouse/dispatchSelectionGroups.test.ts
T

44 lines
4.1 KiB
TypeScript

import { expect, it } from 'vitest'
import { assertSelectionGroupVersions, expandSelectionGroups, requireSelectionGroup, type SelectionGroup } from './dispatchContracts'
import { candidate, sharedPalletCandidates } from './dispatchTestFixtures'
import { normalizeCandidateList, type CandidateWire } from './dispatchWire'
it('expands a filtered physical group into both exact relabel selections once while retaining one physical pallet', () => {
const [first, second] = sharedPalletCandidates()
expect(expandSelectionGroups([first!])).toEqual([first!.selection, second!.selection])
expect(expandSelectionGroups([first!, second!, first!])).toEqual([first!.selection, second!.selection])
expect(requireSelectionGroup(first!).pallets).toEqual([{ warehousePalletId: '21', palletNo: 'PALLET-1', sourceKeys: ['RELABEL:A', 'RELABEL:B'], packageCount: 8 }])
})
it.each(['INBOUND_PLAN', 'RETURN_RECEIPT'] as const)('allows authoritative nullable business display fields for ordinary %s groups', sourceType => {
const row = candidate({ selection: { ...candidate().selection, sourceType, planPalletId: sourceType === 'INBOUND_PLAN' ? '11' : null, returnReceiptId: sourceType === 'RETURN_RECEIPT' ? '12' : null } })
expect(row.selectionGroup.sources[0]).toMatchObject({ sourceBusinessType: null, sourceBusinessNo: null })
expect(expandSelectionGroups([row])).toEqual([row.selection])
})
it('requires the actual RELABEL_ORDER business identity for relabel sources without accepting a legacy alias', () => {
const row = sharedPalletCandidates()[0]!
row.selectionGroup.sources[0]!.sourceBusinessNo = null
expect(() => expandSelectionGroups([row])).toThrow('来源或数量不完整')
row.selectionGroup.sources[0]!.sourceBusinessNo = 'RL-A'; row.selectionGroup.sources[0]!.sourceBusinessType = 'RELABEL'
expect(() => expandSelectionGroups([row])).toThrow('来源或数量不完整')
})
it.each([undefined, { complete: false }, { blockReasons: ['换标来源尚未核齐'] }])('rejects a missing, incomplete or blocked selection group instead of falling back to the visible row', change => {
const row = sharedPalletCandidates()[0]!
row.selectionGroup = change ? { ...row.selectionGroup, ...change } : undefined as unknown as SelectionGroup
expect(() => expandSelectionGroups([row])).toThrow(/完整选货组|尚未完整|尚未核齐/)
})
it('rejects changed group composition and conflicting source versions before creating a command', () => {
const [first, second] = sharedPalletCandidates()
expect(() => assertSelectionGroupVersions([first!], [{ ...second!, selectionGroup: { ...second!.selectionGroup, groupVersion: 'composition-2' } }])).toThrow('组成已变化')
const conflict = { ...second!, selectionGroup: { ...second!.selectionGroup, sources: second!.selectionGroup.sources.map((source, index) => index ? source : { ...source, selection: { ...source.selection, sourceVersion: 'changed' } }) } }
expect(() => expandSelectionGroups([first!, conflict])).toThrow('来源事实不一致')
})
it('normalizes nested group Long quantities and preserves source identities and versions without loss', () => {
const row = sharedPalletCandidates()[0]!, id = '9007199254740993'
const value: CandidateWire = { ...row, selectionGroup: { ...row.selectionGroup, sources: row.selectionGroup.sources.map(source => ({ ...source, customerId: id, selection: { ...source.selection, sourceVersion: id }, packages: String(source.packages) })), pallets: [{ warehousePalletId: id, palletNo: 'PALLET-1', sourceKeys: ['RELABEL:A', 'RELABEL:B'], packageCount: '8' }] } }
const result = normalizeCandidateList({ candidates: [value], complete: true, snapshotAt: '' }).candidates[0]!
expect(result.selectionGroup.sources.map(source => source.packages)).toEqual([3, 5]); expect(result.selectionGroup.pallets[0]!.packageCount).toBe(8)
expect(result.selectionGroup.sources[0]!.selection.sourceVersion).toBe(id); expect(result.selectionGroup.pallets[0]!.warehousePalletId).toBe(id)
value.selectionGroup.sources[0]!.packages = '9007199254740993'
expect(() => normalizeCandidateList({ candidates: [value], complete: true, snapshotAt: '' })).toThrow('安全整数')
})