feat: add inbound hold controls
This commit is contained in:
@@ -1,18 +1,21 @@
|
||||
import { flushPromises, shallowMount } from '@vue/test-utils'
|
||||
import { beforeEach, describe, expect, it, vi } from 'vitest'
|
||||
import { getLoadingAssociationSummary, getOverseasInboundCodeRelationship } from '@/shared/api/inboundCodeRelations'
|
||||
import { getPlatformUnifiedOrder } from '@/modules/unified-orders/api/unifiedOrders'
|
||||
import { getPlatformUnifiedOrder, listPlatformUnifiedOrders } from '@/modules/unified-orders/api/unifiedOrders'
|
||||
import { getPlatformInboundHold, holdPlatformInbound, releasePlatformInbound } from '@/modules/inbounds'
|
||||
import UnifiedOrdersView from './UnifiedOrdersView.vue'
|
||||
|
||||
vi.mock('vue-router', () => ({
|
||||
useRoute: () => ({ params: { orderId: '5001' }, query: { tenantId: '6001' } }),
|
||||
useRouter: () => ({ push: vi.fn() }),
|
||||
}))
|
||||
vi.mock('@/core/access/accessPreview', () => ({ useAccessPreviewStore: () => ({ can: () => true }) }))
|
||||
const { canPermission } = vi.hoisted(() => ({ canPermission: vi.fn((..._permissions: string[]) => true) }))
|
||||
vi.mock('@/core/access/accessPreview', () => ({ useAccessPreviewStore: () => ({ can: canPermission }) }))
|
||||
vi.mock('@/modules/iam', () => ({ listPlatformTenants: vi.fn().mockResolvedValue({ records: [], page: 1, size: 200, total: 0, pages: 0 }) }))
|
||||
vi.mock('@/modules/warehouses', () => ({ listAllPlatformWarehouses: vi.fn().mockResolvedValue([]) }))
|
||||
vi.mock('@/modules/inbounds', () => ({
|
||||
downloadWeightImage: vi.fn(), getInboundMeasurement: vi.fn(),
|
||||
getPlatformInboundHold: vi.fn(), holdPlatformInbound: vi.fn(), releasePlatformInbound: vi.fn(),
|
||||
InboundFeePanel: { template: '<div />' }, InboundMeasurementDialog: { template: '<div />' }, MeasurementEvidenceThumbnail: { template: '<div />' },
|
||||
}))
|
||||
vi.mock('@/modules/unified-orders/workflows', () => ({ UnifiedOrderWorkflowPanel: { template: '<div />' } }))
|
||||
@@ -34,6 +37,7 @@ const detail = {
|
||||
internalOrderStatus: 'WAIT_SPLIT', customerId: '6001', customerName: '测试客户', channelName: '纽约渠道',
|
||||
receivingWarehouseId: '8', receivingWarehouseName: '义乌仓', destinationWarehouseId: '9', destinationWarehouseName: 'ONT8',
|
||||
inboundId: '7001', inboundVersion: 1, inboundCode: 'NYEXX2608310001', expectedShipDate: null, totalPackageCount: 2,
|
||||
holdFlag: false, currentHoldReason: null,
|
||||
locationCodes: null, actualGrossWeightKg: null, actualVolumeCbm: null, measurement: null, preload: null, loading: null, delivery: null, transport: null,
|
||||
coLoadGroup: null, createdAt: null, updatedAt: null, expectedArrivalAt: null, feeReviewStatus: null, requestedPickupAt: null,
|
||||
totalPalletCount: null, chargeableVolumeCbm: null, volumetricWeightKg: null, totalNetWeightKg: null,
|
||||
@@ -56,6 +60,11 @@ function mountDetail() {
|
||||
ApiErrorMeta: true,
|
||||
StatusBadge: true,
|
||||
'el-button': { template: '<button @click="$emit(\'click\')"><slot /></button>' },
|
||||
'el-table': { props: ['data'], template: '<div><slot /></div>' },
|
||||
'el-table-column': { template: '<div />' },
|
||||
'el-tag': { template: '<span><slot /></span>' },
|
||||
'el-empty': { props: ['description'], template: '<div>{{ description }}</div>' },
|
||||
'el-alert': { props: ['title'], template: '<div>{{ title }}</div>' },
|
||||
},
|
||||
},
|
||||
})
|
||||
@@ -64,7 +73,22 @@ function mountDetail() {
|
||||
describe('UnifiedOrdersView downstream fulfillment detail', () => {
|
||||
beforeEach(() => {
|
||||
vi.clearAllMocks()
|
||||
canPermission.mockImplementation(() => true)
|
||||
vi.mocked(getPlatformUnifiedOrder).mockResolvedValue(detail as never)
|
||||
vi.mocked(listPlatformUnifiedOrders).mockResolvedValue({ records: [], page: 1, size: 20, total: 0, pages: 0 })
|
||||
vi.mocked(getPlatformInboundHold).mockResolvedValue({
|
||||
inboundId: '7001', tenantId: '6001', holdFlag: false, currentHoldReason: null, version: 3,
|
||||
canHold: true, canRelease: false, actionBlockReason: null,
|
||||
history: [{ id: '8801', action: 'RELEASE', reason: '货款已核对', evidenceFileId: '9901', operatorName: '操作员A', operatedAt: '2026-09-07T10:00:00' }],
|
||||
})
|
||||
vi.mocked(holdPlatformInbound).mockResolvedValue({
|
||||
inboundId: '7001', tenantId: '6001', holdFlag: true, currentHoldReason: '等待客户补款', version: 4,
|
||||
canHold: false, canRelease: true, actionBlockReason: null, history: [],
|
||||
})
|
||||
vi.mocked(releasePlatformInbound).mockResolvedValue({
|
||||
inboundId: '7001', tenantId: '6001', holdFlag: false, currentHoldReason: null, version: 4,
|
||||
canHold: true, canRelease: false, actionBlockReason: null, history: [],
|
||||
})
|
||||
vi.mocked(getLoadingAssociationSummary).mockResolvedValue({
|
||||
inboundId: '7001', inboundCode: 'NYEXX2608310001', loadingPlanId: null, loadingPlanNo: null,
|
||||
containerBookingId: null, cabinetNo: null, containerNo: null, loadingStatus: null, taskId: null,
|
||||
@@ -112,4 +136,53 @@ describe('UnifiedOrdersView downstream fulfillment detail', () => {
|
||||
expect(wrapper.text()).not.toContain('9001')
|
||||
expect(wrapper.text()).not.toContain('9004')
|
||||
})
|
||||
|
||||
it('loads server hold capabilities and history only after opening the other tab', async () => {
|
||||
const wrapper = mountDetail()
|
||||
await flushPromises()
|
||||
expect(getPlatformInboundHold).not.toHaveBeenCalled()
|
||||
await wrapper.findAll('.detail-tabs button').find((button) => button.text().includes('其他'))!.trigger('click')
|
||||
await flushPromises()
|
||||
expect(getPlatformInboundHold).toHaveBeenCalledWith('6001', '7001')
|
||||
expect(wrapper.text()).toContain('未扣货')
|
||||
const loaded = (wrapper.vm as unknown as { holdDetail: { history: Array<{ reason: string; evidenceFileId: string }> } }).holdDetail
|
||||
expect(loaded.history).toEqual([expect.objectContaining({ reason: '货款已核对', evidenceFileId: '9901' })])
|
||||
expect(wrapper.findAll('button').map((button) => button.text())).toContain('扣货')
|
||||
})
|
||||
|
||||
it('submits the required reason and server version, then refreshes detail and list', async () => {
|
||||
const wrapper = mountDetail()
|
||||
await flushPromises()
|
||||
await wrapper.findAll('.detail-tabs button').find((button) => button.text().includes('其他'))!.trigger('click')
|
||||
await flushPromises()
|
||||
const vm = wrapper.vm as unknown as { holdReason: string; openHoldDialog(action: 'HOLD'): void; submitHoldAction(): Promise<void> }
|
||||
vm.openHoldDialog('HOLD')
|
||||
vm.holdReason = ' 等待客户补款 '
|
||||
await vm.submitHoldAction()
|
||||
expect(holdPlatformInbound).toHaveBeenCalledWith('6001', '7001', { expectedVersion: 3, reason: '等待客户补款' })
|
||||
expect(getPlatformUnifiedOrder).toHaveBeenCalledTimes(2)
|
||||
expect(listPlatformUnifiedOrders).toHaveBeenCalledTimes(1)
|
||||
})
|
||||
|
||||
it('stays read-only without the independent hold permission', async () => {
|
||||
canPermission.mockImplementation((...permissions: string[]) => permissions[0] !== 'platform:firstleg:inbound:hold')
|
||||
const wrapper = mountDetail()
|
||||
await flushPromises()
|
||||
await wrapper.findAll('.detail-tabs button').find((button) => button.text().includes('其他'))!.trigger('click')
|
||||
await flushPromises()
|
||||
expect(getPlatformInboundHold).toHaveBeenCalledWith('6001', '7001')
|
||||
expect(wrapper.text()).toContain('未扣货')
|
||||
expect(wrapper.findAll('button').map((button) => button.text())).not.toEqual(expect.arrayContaining(['扣货', '解除扣货']))
|
||||
})
|
||||
|
||||
it('stays read-only and does not request hold detail before an inbound exists', async () => {
|
||||
vi.mocked(getPlatformUnifiedOrder).mockResolvedValue({ ...detail, summary: { ...detail.summary, inboundId: null, inboundCode: null, holdFlag: null, currentHoldReason: null } } as never)
|
||||
const wrapper = mountDetail()
|
||||
await flushPromises()
|
||||
await wrapper.findAll('.detail-tabs button').find((button) => button.text().includes('其他'))!.trigger('click')
|
||||
await flushPromises()
|
||||
expect(getPlatformInboundHold).not.toHaveBeenCalled()
|
||||
expect(wrapper.text()).toContain('订单尚未生成入库单,扣货状态仅可查看')
|
||||
expect(wrapper.findAll('button').map((button) => button.text())).not.toEqual(expect.arrayContaining(['扣货', '解除扣货']))
|
||||
})
|
||||
})
|
||||
|
||||
Reference in New Issue
Block a user