This commit is contained in:
2026-09-03 15:00:58 +08:00
parent a25b4ed0f7
commit 764b4cc2e6
8 changed files with 452 additions and 246 deletions
@@ -7,12 +7,15 @@ import { readFileSync } from 'node:fs'
import { resolve } from 'node:path'
import QueryToolbar from '@/shared/components/QueryToolbar.vue'
import GlobalSmartLoadingView from './GlobalSmartLoadingView.vue'
import { resolveSmartLoadingChannelGroup } from '../smartLoadingChannelGroups'
import type { ContainerBookingResource } from '../types'
import {
assignSmartLoadingGroup,
assignSmartLoadingItem,
getSmartLoadingPlanByBooking,
listContainers,
listSmartLoadingCandidates,
listSmartLoadingWorkbenchContainers,
recommendSmartLoading,
removeSmartLoadingGroup,
removeSmartLoadingItem,
@@ -34,6 +37,7 @@ vi.mock('../api/platformContainerLoading', () => ({
listContainers: vi.fn(),
getSmartLoadingPlanByBooking: vi.fn(),
listSmartLoadingCandidates: vi.fn(),
listSmartLoadingWorkbenchContainers: vi.fn(),
createSmartLoadingPlan: vi.fn(),
assignSmartLoadingItem: vi.fn(),
assignSmartLoadingGroup: vi.fn(),
@@ -69,14 +73,41 @@ const candidate = {
availableVolumeCbm: 2.4, coLoadGroupId: null, groupNo: null, memberCount: null, groupTotalBoxes: null,
locked: null, blockReason: null,
}
function candidatePage(records: unknown[], page = 1, size = 20, total = records.length) {
return {
records: records.map((record) => {
const candidate = record as { accountId?: string; customerId?: string; groupMembers?: unknown[] }
return { customerId: candidate.customerId || candidate.accountId || '', groupMembers: [], ...candidate }
}),
page, size, total, pages: total ? Math.ceil(total / size) : 0,
} as Awaited<ReturnType<typeof listSmartLoadingCandidates>>
}
function containerPage(records: ContainerBookingResource[] = [container]) {
return {
records: records.map((item) => ({
bookingId: item.id, bookingNo: item.bookingNo, cabinetNo: item.cabinetNo, containerNo: item.containerNo,
warehouseId: item.warehouseId, warehouseName: item.warehouseName || '', channelCode: item.channelCode || '', channelName: item.channelName || '',
containerType: item.containerType, planId: item.planId, planNo: null, planStatus: item.planStatus, version: item.version,
})),
page: 1, size: 200, total: records.length, pages: records.length ? 1 : 0,
} as Awaited<ReturnType<typeof listSmartLoadingWorkbenchContainers>>
}
function mockContainerSearch(records: ContainerBookingResource[]) {
vi.mocked(listSmartLoadingWorkbenchContainers).mockImplementation(async (query) => containerPage(records.filter((item) => (
item.planStatus !== 'COMPLETED'
&& (!query.warehouseId || item.warehouseId === query.warehouseId)
&& query.channelGroupCodes.includes(resolveSmartLoadingChannelGroup(item.channelCode, item.channelName)?.code || '')
))))
}
describe('GlobalSmartLoadingView', () => {
beforeEach(() => {
setActivePinia(createPinia())
vi.clearAllMocks()
vi.mocked(listContainers).mockResolvedValue({ records: [container], page: 1, size: 100, total: 1, pages: 1 })
mockContainerSearch([container])
vi.mocked(getSmartLoadingPlanByBooking).mockResolvedValue(plan)
vi.mocked(listSmartLoadingCandidates).mockResolvedValue([candidate])
vi.mocked(listSmartLoadingCandidates).mockResolvedValue(candidatePage([candidate]))
})
it('loads real containers, candidates and the selected plan into the three-column workbench', async () => {
@@ -85,7 +116,7 @@ describe('GlobalSmartLoadingView', () => {
expect(listContainers).toHaveBeenCalledWith({ page: 1, size: 100 })
expect(getSmartLoadingPlanByBooking).toHaveBeenCalledWith(container.id)
expect(listSmartLoadingCandidates).toHaveBeenCalledWith(container.id, container.warehouseId)
expect(listSmartLoadingCandidates).toHaveBeenCalledWith({ channelGroupCodes: ['MATSON_CLX'], page: 1, size: 20 })
expect(wrapper.text()).toContain('候选装柜明细')
expect(wrapper.text()).toContain('美森正班')
expect(wrapper.findAll('.channel-tags .el-checkbox-button')).toHaveLength(9)
@@ -113,7 +144,8 @@ describe('GlobalSmartLoadingView', () => {
total: 1,
pages: 1,
})
vi.mocked(listSmartLoadingCandidates).mockResolvedValue([{ accountId: '1', inboundId: '5', orderId: '7', orderNo: 'ORD-2', inboundCode: 'IN-2', warehouseId: '8', channelCode: 'EXX_RAIL_NY', channelName: '纽约EXX海铁', destinationWarehouseId: '9', destinationWarehouseCode: 'LAX9', destinationWarehouseName: 'LAX9', customerName: '另一企业', goodsSummary: '鞋类', availablePackageCount: 12, unitGrossWeightKg: 10, unitNetWeightKg: 9, averageUnitVolumeCbm: 0.2, availableVolumeCbm: 2.4, coLoadGroupId: null, groupNo: null, memberCount: null, groupTotalBoxes: null, locked: null, blockReason: null }])
mockContainerSearch([{ ...container, shippingCompanyId: '32', shippingCompanyName: 'EXX', channelCode: 'EXX_RAIL_NY', channelName: '纽约EXX海铁' }])
vi.mocked(listSmartLoadingCandidates).mockResolvedValue(candidatePage([{ accountId: '1', inboundId: '5', orderId: '7', orderNo: 'ORD-2', inboundCode: 'IN-2', warehouseId: '8', channelCode: 'EXX_RAIL_NY', channelName: '纽约EXX海铁', destinationWarehouseId: '9', destinationWarehouseCode: 'LAX9', destinationWarehouseName: 'LAX9', customerName: '另一企业', goodsSummary: '鞋类', availablePackageCount: 12, unitGrossWeightKg: 10, unitNetWeightKg: 9, averageUnitVolumeCbm: 0.2, availableVolumeCbm: 2.4, coLoadGroupId: null, groupNo: null, memberCount: null, groupTotalBoxes: null, locked: null, blockReason: null }]))
const wrapper = mount(GlobalSmartLoadingView, { global: { plugins: [ElementPlus] } })
await flushPromises()
@@ -131,6 +163,7 @@ describe('GlobalSmartLoadingView', () => {
total: 1,
pages: 1,
})
mockContainerSearch([{ ...container, cabinetNo: 'CAB-260814-0001', containerNo: 'SPLIT-814022117898chain1' }])
const wrapper = mount(GlobalSmartLoadingView, { global: { plugins: [ElementPlus] } })
await flushPromises()
@@ -154,6 +187,7 @@ describe('GlobalSmartLoadingView', () => {
total: 1,
pages: 1,
})
mockContainerSearch([{ ...container, cabinetNo: '2088475410134773762', containerNo: null, channelName: '美森正班海卡包税', warehouseName: '义乌国内仓', containerType: '40HC' }])
const wrapper = mount(GlobalSmartLoadingView, { global: { plugins: [ElementPlus] } })
await flushPromises()
@@ -180,6 +214,7 @@ describe('GlobalSmartLoadingView', () => {
channelName: '合德海卡包税',
}
vi.mocked(listContainers).mockResolvedValue({ records: [container, other], page: 1, size: 100, total: 2, pages: 1 })
mockContainerSearch([container, other])
vi.mocked(getSmartLoadingPlanByBooking).mockImplementation(async (bookingId) => ({ ...plan, bookingId }))
const wrapper = mount(GlobalSmartLoadingView, { global: { plugins: [ElementPlus] } })
await flushPromises()
@@ -213,6 +248,7 @@ describe('GlobalSmartLoadingView', () => {
channelName: '美森正班海派',
}
vi.mocked(listContainers).mockResolvedValue({ records: [container, parcelContainer], page: 1, size: 100, total: 2, pages: 1 })
mockContainerSearch([container, parcelContainer])
const wrapper = mount(GlobalSmartLoadingView, { global: { plugins: [ElementPlus] } })
await flushPromises()
@@ -234,6 +270,7 @@ describe('GlobalSmartLoadingView', () => {
warehouseName: '东莞仓',
}
vi.mocked(listContainers).mockResolvedValue({ records: [container, dongguanContainer], page: 1, size: 100, total: 2, pages: 1 })
mockContainerSearch([container, dongguanContainer])
vi.mocked(getSmartLoadingPlanByBooking).mockImplementation(async (bookingId) => ({ ...plan, bookingId }))
const wrapper = mount(GlobalSmartLoadingView, { global: { plugins: [ElementPlus] } })
await flushPromises()
@@ -246,7 +283,7 @@ describe('GlobalSmartLoadingView', () => {
expect(wrapper.text()).toContain('MSCU-DG')
expect(wrapper.text()).not.toContain('MSCU-1')
expect(listSmartLoadingCandidates).toHaveBeenLastCalledWith(dongguanContainer.id, dongguanContainer.warehouseId)
expect(listSmartLoadingCandidates).toHaveBeenLastCalledWith({ channelGroupCodes: ['MATSON_CLX'], warehouseId: dongguanContainer.warehouseId, page: 1, size: 20 })
})
it('keeps the selected channel when the warehouse changes even if that warehouse has no matching container', async () => {
@@ -262,6 +299,7 @@ describe('GlobalSmartLoadingView', () => {
channelName: '美森加班海卡',
}
vi.mocked(listContainers).mockResolvedValue({ records: [container, dongguanOvertimeContainer], page: 1, size: 100, total: 2, pages: 1 })
mockContainerSearch([container, dongguanOvertimeContainer])
const wrapper = mount(GlobalSmartLoadingView, { global: { plugins: [ElementPlus] } })
await flushPromises()
@@ -271,14 +309,15 @@ describe('GlobalSmartLoadingView', () => {
expect(wrapper.find('.channel-tags .el-checkbox-button.is-checked').text()).toBe('美森正班')
expect(wrapper.findAll('.container-option')).toHaveLength(0)
expect(wrapper.text()).not.toContain('MSCU-DG-MAX')
expect(listSmartLoadingCandidates).toHaveBeenCalledTimes(1)
expect(listSmartLoadingCandidates).toHaveBeenCalledTimes(2)
expect(listSmartLoadingCandidates).toHaveBeenLastCalledWith({ channelGroupCodes: ['MATSON_CLX'], warehouseId: '18', page: 1, size: 20 })
})
it('filters candidates by an active name-only customer selector and accountId', () => {
it('sends the active name-only customer selector to the server candidate query', () => {
const source = readFileSync(resolve(process.cwd(), 'src/modules/container-loading/pages/GlobalSmartLoadingView.vue'), 'utf8')
expect(source).toContain("listPlatformTenants({ page: 1, size: 200, status: 'ACTIVE' })")
expect(source).toContain("label: customer.tenantName")
expect(source).toContain('member.accountId === customerId')
expect(source).toContain('params.customerId = customerId')
expect(source).not.toContain('customer.tenantCode')
})
@@ -288,11 +327,13 @@ describe('GlobalSmartLoadingView', () => {
})
it('filters candidates by expected arrival range and order status', async () => {
vi.mocked(listSmartLoadingCandidates).mockResolvedValue([
vi.mocked(listSmartLoadingCandidates)
.mockResolvedValueOnce(candidatePage([
{ ...candidate, inboundId: '51', inboundCode: 'IN-MATCH', orderStatusSnapshot: 'WAIT_SPLIT', expectedArrivalAt: '2026-09-11T08:00:00' },
{ ...candidate, inboundId: '52', inboundCode: 'IN-WRONG-STATUS', orderStatusSnapshot: 'LOADING', expectedArrivalAt: '2026-09-11T08:00:00' },
{ ...candidate, inboundId: '53', inboundCode: 'IN-WRONG-DATE', orderStatusSnapshot: 'WAIT_SPLIT', expectedArrivalAt: '2026-09-20T08:00:00' },
])
]))
.mockResolvedValueOnce(candidatePage([{ ...candidate, inboundId: '51', inboundCode: 'IN-MATCH', orderStatusSnapshot: 'WAIT_SPLIT', expectedArrivalAt: '2026-09-11T08:00:00' }]))
const wrapper = mount(GlobalSmartLoadingView, { global: { plugins: [ElementPlus] } })
await flushPromises()
@@ -305,15 +346,24 @@ describe('GlobalSmartLoadingView', () => {
expect(candidates).toContain('IN-MATCH')
expect(candidates).not.toContain('IN-WRONG-STATUS')
expect(candidates).not.toContain('IN-WRONG-DATE')
expect(listSmartLoadingCandidates).toHaveBeenLastCalledWith({ channelGroupCodes: ['MATSON_CLX'], expectedArrivalStart: '2026-09-10', expectedArrivalEnd: '2026-09-12', orderStatus: 'WAIT_SPLIT', page: 1, size: 20 })
expect(listSmartLoadingWorkbenchContainers).toHaveBeenLastCalledWith({ channelGroupCodes: ['MATSON_CLX'], expectedArrivalStart: '2026-09-10', expectedArrivalEnd: '2026-09-12', orderStatus: 'WAIT_SPLIT', page: 1, size: 200 })
})
it('paginates large inbound candidate results with twenty rows by default', async () => {
vi.mocked(listSmartLoadingCandidates).mockResolvedValue(Array.from({ length: 25 }, (_, index) => ({
...candidate,
inboundId: String(100 + index),
orderId: String(200 + index),
inboundCode: `IN-${String(index + 1).padStart(3, '0')}`,
})))
vi.mocked(listSmartLoadingCandidates)
.mockResolvedValueOnce(candidatePage(Array.from({ length: 20 }, (_, index) => ({
...candidate,
inboundId: String(100 + index),
orderId: String(200 + index),
inboundCode: `IN-${String(index + 1).padStart(3, '0')}`,
})), 1, 20, 25))
.mockResolvedValueOnce(candidatePage(Array.from({ length: 5 }, (_, index) => ({
...candidate,
inboundId: String(120 + index),
orderId: String(220 + index),
inboundCode: `IN-${String(index + 21).padStart(3, '0')}`,
})), 2, 20, 25))
const wrapper = mount(GlobalSmartLoadingView, { global: { plugins: [ElementPlus] } })
await flushPromises()
@@ -329,11 +379,14 @@ describe('GlobalSmartLoadingView', () => {
})
it('aggregates a two-order candidate group and calls only whole-group assignment', async () => {
vi.mocked(listSmartLoadingCandidates).mockResolvedValue([
{ accountId: '1', inboundId: '201', orderId: '301', orderNo: 'ORD-201', inboundCode: 'NYEXX2608290051', warehouseId: '8', channelCode: 'ZBYHK', channelName: '美森正班海卡包税', destinationWarehouseId: '9', destinationWarehouseCode: 'LAX9', destinationWarehouseName: 'LAX9', customerName: '客户一', goodsSummary: '货物一', availablePackageCount: 4, unitGrossWeightKg: 10, unitNetWeightKg: 9, averageUnitVolumeCbm: 0.2, availableVolumeCbm: 0.8, coLoadGroupId: '2093539779795795969', groupNo: 'CLG-2093539779795795969', memberCount: 3, groupTotalBoxes: 12, locked: false, blockReason: null },
{ accountId: '1', inboundId: '203', orderId: '301', orderNo: 'ORD-201', inboundCode: 'NYEXX2608290051-B', warehouseId: '8', channelCode: 'ZBYHK', channelName: '美森正班海卡包税', destinationWarehouseId: '10', destinationWarehouseCode: 'JFK8', destinationWarehouseName: 'JFK8', customerName: '客户一', goodsSummary: '货物一', availablePackageCount: 2, unitGrossWeightKg: 10, unitNetWeightKg: 9, averageUnitVolumeCbm: 0.2, availableVolumeCbm: 0.4, coLoadGroupId: '2093539779795795969', groupNo: 'CLG-2093539779795795969', memberCount: 3, groupTotalBoxes: 12, locked: false, blockReason: null },
{ accountId: '2', inboundId: '202', orderId: '302', orderNo: 'ORD-202', inboundCode: 'NYEXX2608290052', warehouseId: '8', channelCode: 'ZBYHK', channelName: '美森正班海卡包税', destinationWarehouseId: '9', destinationWarehouseCode: 'LAX9', destinationWarehouseName: 'LAX9', customerName: '客户二', goodsSummary: '货物二', availablePackageCount: 6, unitGrossWeightKg: 10, unitNetWeightKg: 9, averageUnitVolumeCbm: 0.2, availableVolumeCbm: 1.2, coLoadGroupId: '2093539779795795969', groupNo: 'CLG-2093539779795795969', memberCount: 3, groupTotalBoxes: 12, locked: false, blockReason: null },
])
vi.mocked(listSmartLoadingCandidates).mockResolvedValue(candidatePage([{
accountId: '1', inboundId: '201', orderId: '301', orderNo: 'ORD-201', inboundCode: 'NYEXX2608290051', warehouseId: '8', channelCode: 'ZBYHK', channelName: '美森正班海卡包税', destinationWarehouseId: '9', destinationWarehouseCode: 'LAX9', destinationWarehouseName: 'LAX9', customerName: '客户一、客户二', goodsSummary: '货物一、货物二', availablePackageCount: 12, unitGrossWeightKg: 10, unitNetWeightKg: 9, averageUnitVolumeCbm: 0.2, availableVolumeCbm: 2.4, coLoadGroupId: '2093539779795795969', groupNo: 'CLG-2093539779795795969', memberCount: 3, groupTotalBoxes: 12, locked: false, blockReason: null,
groupMembers: [
{ accountId: '1', inboundId: '201', orderId: '301', orderNo: 'ORD-201', inboundCode: 'NYEXX2608290051', customerName: '客户一', availablePackageCount: 4, orderStatusSnapshot: 'WAIT_SPLIT', expectedArrivalAt: null },
{ accountId: '1', inboundId: '203', orderId: '301', orderNo: 'ORD-201', inboundCode: 'NYEXX2608290051-B', customerName: '客户一', availablePackageCount: 2, orderStatusSnapshot: 'WAIT_SPLIT', expectedArrivalAt: null },
{ accountId: '2', inboundId: '202', orderId: '302', orderNo: 'ORD-202', inboundCode: 'NYEXX2608290052', customerName: '客户二', availablePackageCount: 6, orderStatusSnapshot: 'WAIT_SPLIT', expectedArrivalAt: null },
],
}]))
vi.mocked(assignSmartLoadingGroup).mockResolvedValue(plan)
const wrapper = mount(GlobalSmartLoadingView, { attachTo: document.body, global: { plugins: [ElementPlus] } })
await flushPromises()
@@ -356,9 +409,10 @@ describe('GlobalSmartLoadingView', () => {
})
it('blocks an incomplete three-member candidate group without exposing member assignment', async () => {
vi.mocked(listSmartLoadingCandidates).mockResolvedValue(['401', '402', '403'].map((inboundId, index) => ({
accountId: `${index + 1}`, inboundId, orderId: `50${index}`, orderNo: `ORD-${index}`, inboundCode: `IN-${inboundId}`, warehouseId: '8', channelCode: 'ZBYHK', channelName: '美森正班海卡包税', destinationWarehouseId: '9', destinationWarehouseCode: 'LAX9', destinationWarehouseName: 'LAX9', customerName: `客户${index}`, goodsSummary: `货物${index}`, availablePackageCount: 6, unitGrossWeightKg: 10, unitNetWeightKg: 9, averageUnitVolumeCbm: 0.2, availableVolumeCbm: 1.2, coLoadGroupId: '9900', groupNo: 'CLG-9900', memberCount: 3, groupTotalBoxes: 18, locked: false, blockReason: '同柜不可拆组成员候选不齐,缺少 1 票',
})))
const members = ['401', '402', '403'].map((inboundId, index) => ({ accountId: `${index + 1}`, inboundId, orderId: `50${index}`, orderNo: `ORD-${index}`, inboundCode: `IN-${inboundId}`, customerName: `客户${index}`, availablePackageCount: 6, orderStatusSnapshot: 'WAIT_SPLIT', expectedArrivalAt: null }))
vi.mocked(listSmartLoadingCandidates).mockResolvedValue(candidatePage([{
...candidate, inboundId: '401', orderId: '500', inboundCode: 'IN-401', coLoadGroupId: '9900', groupNo: 'CLG-9900', memberCount: 3, groupTotalBoxes: 18, availablePackageCount: 18, locked: false, blockReason: '同柜不可拆组成员候选不齐,缺少 1 票', groupMembers: members,
}]))
const wrapper = mount(GlobalSmartLoadingView, { global: { plugins: [ElementPlus] } })
await flushPromises()
@@ -410,23 +464,21 @@ describe('GlobalSmartLoadingView', () => {
expect(wrapper.text()).not.toContain('是否退税')
})
it('filters completed loading candidates out even when all statuses are selected', async () => {
vi.mocked(listSmartLoadingCandidates).mockResolvedValue([
candidate,
{ ...candidate, inboundId: '6', orderId: '8', inboundCode: 'IN-LOADED', orderStatusSnapshot: 'LOADED' },
{ ...candidate, inboundId: '7', orderId: '9', inboundCode: 'IN-COMPLETED', orderStatusSnapshot: 'COMPLETED' },
])
it('relies on the server contract to exclude completed candidates', async () => {
vi.mocked(listSmartLoadingCandidates).mockResolvedValue(candidatePage([candidate]))
const wrapper = mount(GlobalSmartLoadingView, { global: { plugins: [ElementPlus] } })
await flushPromises()
expect(wrapper.get('.candidate-pane').text()).toContain('IN-2')
expect(wrapper.get('.candidate-pane').text()).not.toContain('IN-LOADED')
expect(wrapper.get('.candidate-pane').text()).not.toContain('IN-COMPLETED')
expect(listSmartLoadingCandidates).toHaveBeenCalledWith({ channelGroupCodes: ['MATSON_CLX'], page: 1, size: 20 })
})
it('prefers an editing container on first load while keeping loading containers available', async () => {
const pushed = { ...container, id: '9007199254740995', planStatus: 'LOADING' as const }
vi.mocked(listContainers).mockResolvedValue({ records: [pushed, container], page: 1, size: 100, total: 2, pages: 1 })
mockContainerSearch([pushed, container])
const wrapper = mount(GlobalSmartLoadingView, { global: { plugins: [ElementPlus] } })
await flushPromises()
@@ -437,6 +489,7 @@ describe('GlobalSmartLoadingView', () => {
it('keeps waiting and editing empty containers without exposing their status labels', async () => {
const waiting = { ...container, id: '9007199254740995', planId: '9007199254740996', cabinetNo: 'CB-WAIT', containerNo: null, planStatus: 'WAIT_LOADING' as const }
vi.mocked(listContainers).mockResolvedValue({ records: [container, waiting], page: 1, size: 100, total: 2, pages: 1 })
mockContainerSearch([container, waiting])
vi.mocked(getSmartLoadingPlanByBooking).mockImplementation(async (bookingId) => (
bookingId === waiting.id
? { ...plan, bookingId: waiting.id, status: 'LOADING' as const }
@@ -449,10 +502,12 @@ describe('GlobalSmartLoadingView', () => {
expect(waitingOption.text()).toContain('CB-WAIT')
expect(waitingOption.text()).not.toContain('待装柜')
const candidateCalls = vi.mocked(listSmartLoadingCandidates).mock.calls.length
await waitingOption.trigger('click')
await flushPromises()
expect(getSmartLoadingPlanByBooking).toHaveBeenCalledWith(waiting.id)
expect(listSmartLoadingCandidates).toHaveBeenCalledTimes(candidateCalls)
expect(waitingOption.text()).toContain('CB-WAIT')
expect(waitingOption.text()).not.toContain('拣货中')
})
@@ -460,12 +515,13 @@ describe('GlobalSmartLoadingView', () => {
it('keeps picking containers available when the plan capability allows increments', async () => {
const pushed = { ...container, id: '9007199254740995', planId: '9007199254740996', cabinetNo: 'CB-2', planStatus: 'LOADING' as const }
vi.mocked(listContainers).mockResolvedValue({ records: [pushed], page: 1, size: 100, total: 1, pages: 1 })
mockContainerSearch([pushed])
vi.mocked(getSmartLoadingPlanByBooking).mockResolvedValue({ ...plan, bookingId: pushed.id, status: 'LOADING', pdaTaskId: '700', pdaTaskStatus: 'LOADING', pdaTaskVersion: 4 })
const wrapper = mount(GlobalSmartLoadingView, { global: { plugins: [ElementPlus] } })
await flushPromises()
expect(getSmartLoadingPlanByBooking).toHaveBeenCalledWith(pushed.id)
expect(listSmartLoadingCandidates).toHaveBeenCalledWith(pushed.id, pushed.warehouseId)
expect(listSmartLoadingCandidates).toHaveBeenCalledWith({ channelGroupCodes: ['MATSON_CLX'], page: 1, size: 20 })
expect(wrapper.findAll('.container-option')).toHaveLength(1)
expect(wrapper.text()).toContain('CB-2')
expect(wrapper.text()).not.toContain('已下发 PDA,仍可维护未完成的装柜明细')
@@ -476,6 +532,7 @@ describe('GlobalSmartLoadingView', () => {
it('hides completed containers from the smart loading workbench', async () => {
const completed = { ...container, id: '9007199254740997', planId: '9007199254740998', cabinetNo: 'CB-3', planStatus: 'COMPLETED' as const }
vi.mocked(listContainers).mockResolvedValue({ records: [completed], page: 1, size: 100, total: 1, pages: 1 })
mockContainerSearch([completed])
const wrapper = mount(GlobalSmartLoadingView, { global: { plugins: [ElementPlus] } })
await flushPromises()
@@ -488,6 +545,7 @@ describe('GlobalSmartLoadingView', () => {
it('isolates a missing-channel row without hiding another valid container', async () => {
const missingChannel = { ...container, id: '9007199254740997', cabinetNo: 'CB-MISSING', channelCode: null, channelName: null }
vi.mocked(listContainers).mockResolvedValue({ records: [missingChannel, container], page: 1, size: 100, total: 2, pages: 1 })
mockContainerSearch([missingChannel, container])
const wrapper = mount(GlobalSmartLoadingView, { global: { plugins: [ElementPlus] } })
await flushPromises()
@@ -498,17 +556,17 @@ describe('GlobalSmartLoadingView', () => {
expect(getSmartLoadingPlanByBooking).toHaveBeenCalledWith(container.id)
})
it('matches candidate goods by their own formal top-level channel code', async () => {
vi.mocked(listSmartLoadingCandidates).mockResolvedValue([
it('renders the channel-filtered candidate page returned by the server', async () => {
vi.mocked(listSmartLoadingCandidates).mockResolvedValue(candidatePage([
{ accountId: '1', inboundId: '5', orderId: '7', orderNo: 'ORD-2', inboundCode: 'IN-MATCH', warehouseId: '8', channelCode: 'ZBYHK', channelName: '美森正班海卡包税', destinationWarehouseId: '9', destinationWarehouseCode: 'LAX9', destinationWarehouseName: 'LAX9', customerName: '匹配企业', goodsSummary: '匹配货物', availablePackageCount: 12, unitGrossWeightKg: 10, unitNetWeightKg: 9, averageUnitVolumeCbm: 0.2, availableVolumeCbm: 2.4, coLoadGroupId: null, groupNo: null, memberCount: null, groupTotalBoxes: null, locked: null, blockReason: null },
{ accountId: '2', inboundId: '8', orderId: '10', orderNo: 'ORD-3', inboundCode: 'IN-OTHER', warehouseId: '8', channelCode: 'OTHER', channelName: '其他渠道', destinationWarehouseId: '9', destinationWarehouseCode: 'LAX9', destinationWarehouseName: 'LAX9', customerName: '其他企业', goodsSummary: '其他货物', availablePackageCount: 8, unitGrossWeightKg: 10, unitNetWeightKg: 9, averageUnitVolumeCbm: 0.2, availableVolumeCbm: 1.6, coLoadGroupId: null, groupNo: null, memberCount: null, groupTotalBoxes: null, locked: null, blockReason: null },
])
]))
const wrapper = mount(GlobalSmartLoadingView, { global: { plugins: [ElementPlus] } })
await flushPromises()
expect(wrapper.text()).toContain('匹配企业')
expect(wrapper.text()).not.toContain('IN-OTHER')
expect(wrapper.text()).not.toContain('其他货物')
expect(listSmartLoadingCandidates).toHaveBeenCalledWith({ channelGroupCodes: ['MATSON_CLX'], page: 1, size: 20 })
})
it('refreshes the latest plan after A0409 without losing the selected container', async () => {
@@ -649,7 +707,7 @@ describe('GlobalSmartLoadingView', () => {
})
it('limits adjusted package count to the assigned count plus remaining availability', async () => {
vi.mocked(listSmartLoadingCandidates).mockResolvedValue([{ accountId: '1', inboundId: '2', orderId: '4', orderNo: 'ORD-1', inboundCode: 'IN-1', warehouseId: '8', channelCode: 'ZBYHK', channelName: '美森正班海卡包税', destinationWarehouseId: '9', destinationWarehouseCode: 'LAX9', destinationWarehouseName: 'LAX9', customerName: '示例企业', goodsSummary: '服装', availablePackageCount: 4, unitGrossWeightKg: 10, unitNetWeightKg: 9, averageUnitVolumeCbm: 0.2, availableVolumeCbm: 0.8, coLoadGroupId: null, groupNo: null, memberCount: null, groupTotalBoxes: null, locked: null, blockReason: null }])
vi.mocked(listSmartLoadingCandidates).mockResolvedValue(candidatePage([{ accountId: '1', inboundId: '2', orderId: '4', orderNo: 'ORD-1', inboundCode: 'IN-1', warehouseId: '8', channelCode: 'ZBYHK', channelName: '美森正班海卡包税', destinationWarehouseId: '9', destinationWarehouseCode: 'LAX9', destinationWarehouseName: 'LAX9', customerName: '示例企业', goodsSummary: '服装', availablePackageCount: 4, unitGrossWeightKg: 10, unitNetWeightKg: 9, averageUnitVolumeCbm: 0.2, availableVolumeCbm: 0.8, coLoadGroupId: null, groupNo: null, memberCount: null, groupTotalBoxes: null, locked: null, blockReason: null }]))
const wrapper = mount(GlobalSmartLoadingView, { global: { plugins: [ElementPlus] } })
await flushPromises()