fix(container): refresh workbench after empty return
This commit is contained in:
@@ -55,7 +55,7 @@ describe('ContainersView', () => {
|
|||||||
vi.mocked(getTerminalDrayage).mockResolvedValue({ ...drayage, allowedActions: [...drayage.allowedActions] })
|
vi.mocked(getTerminalDrayage).mockResolvedValue({ ...drayage, allowedActions: [...drayage.allowedActions] })
|
||||||
vi.mocked(getTerminalDrayageHistory).mockResolvedValue([])
|
vi.mocked(getTerminalDrayageHistory).mockResolvedValue([])
|
||||||
vi.mocked(selectTerminalDrayageFleet).mockResolvedValue({ ...drayage, fleetSupplierId: '502', fleetSupplierName: '新车队', version: 4, allowedActions: [...drayage.allowedActions] })
|
vi.mocked(selectTerminalDrayageFleet).mockResolvedValue({ ...drayage, fleetSupplierId: '502', fleetSupplierName: '新车队', version: 4, allowedActions: [...drayage.allowedActions] })
|
||||||
vi.mocked(recordTerminalDrayageEmptyReturn).mockResolvedValue({ ...drayage, status: 'EMPTY_RETURNED', actualEmptyReturnAt: '2026-09-11T13:00:00', version: 4, emptyReturnWarning: { status: 'RESOLVED', deadlineAt: '2026-09-10T10:00:00', overdueHours: 0, overdueDays: 0 }, allowedActions: ['SELECT_FLEET'] })
|
vi.mocked(recordTerminalDrayageEmptyReturn).mockReset().mockResolvedValue({ ...drayage, status: 'EMPTY_RETURNED', actualEmptyReturnAt: '2026-09-11T13:00:00', version: 4, emptyReturnWarning: { status: 'RESOLVED', deadlineAt: '2026-09-10T10:00:00', overdueHours: 0, overdueDays: 0 }, allowedActions: ['SELECT_FLEET'] })
|
||||||
vi.mocked(listContainers).mockResolvedValue({ records: [{ id: '9001', planId: null, bookingNo: 'BK-1', cabinetNo: 'CB-1', containerNo: 'MSCU-1', sourceType: 'SIMULATION', allowedActions: ['EDIT_CONTAINER', 'SELECT_FLEET', 'SCHEDULE_PICKUP', 'RECORD_PICKUP', 'RECORD_EMPTY_RETURN'], terminalDrayage: drayage, oceanBookingId: null, warehouseId: '8', warehouseName: '义乌仓', channelCode: 'ZBYHK', channelName: '美森正班海卡包税', containerType: '40HC', planStatus: 'WAIT_LOADING', version: 0, billOfLadingNo: null, vesselName: null, voyageNo: null, shippingCompanyId: null, shippingCompanyName: null, departurePortCode: null, departurePortName: '宁波', destinationPortCode: null, destinationPortName: '长滩', destinationWarehouseCode: null, destinationWarehouseName: null, capacityCbm: 76, maxWeightKg: 28000, terminalTruckingCompanyId: null, terminalTruckingCompany: null, loadingAt: null, estimatedSailingAt: null, actualSailingAt: null, estimatedPortArrivalAt: null, actualPortArrivalAt: null, estimatedWarehouseArrivalAt: null, actualWarehouseArrivalAt: null }], page: 1, size: 20, total: 1, pages: 1 })
|
vi.mocked(listContainers).mockResolvedValue({ records: [{ id: '9001', planId: null, bookingNo: 'BK-1', cabinetNo: 'CB-1', containerNo: 'MSCU-1', sourceType: 'SIMULATION', allowedActions: ['EDIT_CONTAINER', 'SELECT_FLEET', 'SCHEDULE_PICKUP', 'RECORD_PICKUP', 'RECORD_EMPTY_RETURN'], terminalDrayage: drayage, oceanBookingId: null, warehouseId: '8', warehouseName: '义乌仓', channelCode: 'ZBYHK', channelName: '美森正班海卡包税', containerType: '40HC', planStatus: 'WAIT_LOADING', version: 0, billOfLadingNo: null, vesselName: null, voyageNo: null, shippingCompanyId: null, shippingCompanyName: null, departurePortCode: null, departurePortName: '宁波', destinationPortCode: null, destinationPortName: '长滩', destinationWarehouseCode: null, destinationWarehouseName: null, capacityCbm: 76, maxWeightKg: 28000, terminalTruckingCompanyId: null, terminalTruckingCompany: null, loadingAt: null, estimatedSailingAt: null, actualSailingAt: null, estimatedPortArrivalAt: null, actualPortArrivalAt: null, estimatedWarehouseArrivalAt: null, actualWarehouseArrivalAt: null }], page: 1, size: 20, total: 1, pages: 1 })
|
||||||
vi.mocked(getContainer).mockResolvedValue({ id: '9001', planId: null, bookingNo: 'BK-1', cabinetNo: 'CB-1', containerNo: 'MSCU-1', sourceType: 'SIMULATION', allowedActions: ['EDIT_CONTAINER'], oceanBookingId: null, warehouseId: '8', warehouseName: '义乌仓', channelCode: 'ZBYHK', channelName: '美森正班海卡包税', containerType: '40HC', planStatus: 'WAIT_LOADING', version: 0, billOfLadingNo: null, vesselName: null, voyageNo: null, shippingCompanyId: null, shippingCompanyName: null, departurePortCode: null, departurePortName: '宁波', destinationPortCode: null, destinationPortName: '长滩', destinationWarehouseCode: null, destinationWarehouseName: null, capacityCbm: 76, maxWeightKg: 28000, terminalTruckingCompanyId: null, terminalTruckingCompany: null, loadingAt: null, estimatedSailingAt: null, actualSailingAt: null, estimatedPortArrivalAt: null, actualPortArrivalAt: null, estimatedWarehouseArrivalAt: null, actualWarehouseArrivalAt: null })
|
vi.mocked(getContainer).mockResolvedValue({ id: '9001', planId: null, bookingNo: 'BK-1', cabinetNo: 'CB-1', containerNo: 'MSCU-1', sourceType: 'SIMULATION', allowedActions: ['EDIT_CONTAINER'], oceanBookingId: null, warehouseId: '8', warehouseName: '义乌仓', channelCode: 'ZBYHK', channelName: '美森正班海卡包税', containerType: '40HC', planStatus: 'WAIT_LOADING', version: 0, billOfLadingNo: null, vesselName: null, voyageNo: null, shippingCompanyId: null, shippingCompanyName: null, departurePortCode: null, departurePortName: '宁波', destinationPortCode: null, destinationPortName: '长滩', destinationWarehouseCode: null, destinationWarehouseName: null, capacityCbm: 76, maxWeightKg: 28000, terminalTruckingCompanyId: null, terminalTruckingCompany: null, loadingAt: null, estimatedSailingAt: null, actualSailingAt: null, estimatedPortArrivalAt: null, actualPortArrivalAt: null, estimatedWarehouseArrivalAt: null, actualWarehouseArrivalAt: null })
|
||||||
vi.mocked(getContainerOverview).mockResolvedValue({ container: { id: '9001', bookingNo: 'BK-1', cabinetNo: 'CB-1', containerNo: 'MSCU-1', status: 'WAIT_LOADING', warehouseId: '8', warehouseName: '义乌仓', voyageNo: null, vesselName: null, version: 0 }, loadingPlanId: null, pda: null, documents: [], exception: { active: false, reason: null, markedBy: null, markedByName: null, markedAt: null }, exceptionHistory: [], timeline: [] })
|
vi.mocked(getContainerOverview).mockResolvedValue({ container: { id: '9001', bookingNo: 'BK-1', cabinetNo: 'CB-1', containerNo: 'MSCU-1', status: 'WAIT_LOADING', warehouseId: '8', warehouseName: '义乌仓', voyageNo: null, vesselName: null, version: 0 }, loadingPlanId: null, pda: null, documents: [], exception: { active: false, reason: null, markedBy: null, markedByName: null, markedAt: null }, exceptionHistory: [], timeline: [] })
|
||||||
@@ -84,12 +84,54 @@ describe('ContainersView', () => {
|
|||||||
expect(buttons).toHaveLength(1)
|
expect(buttons).toHaveLength(1)
|
||||||
await buttons[0].trigger('click')
|
await buttons[0].trigger('click')
|
||||||
await flushPromises()
|
await flushPromises()
|
||||||
|
const returnDialog = wrapper.findAllComponents(AppDialog).find((dialog) => dialog.props('title') === '确认还柜')!
|
||||||
|
returnDialog.findComponent({ name: 'ElDatePicker' }).vm.$emit('update:modelValue', '2026-09-11T13:00:00')
|
||||||
|
await returnDialog.find('textarea').setValue('还柜凭证已核对')
|
||||||
|
const callsBefore = vi.mocked(listContainers).mock.calls.length
|
||||||
|
const page: Awaited<ReturnType<typeof listContainers>> = await vi.mocked(listContainers).mock.results.at(-1)!.value
|
||||||
|
vi.mocked(listContainers).mockResolvedValueOnce({ ...page, records: page.records.map((row) => ({ ...row, allowedActions: [], terminalDrayage: { ...drayage, status: 'EMPTY_RETURNED', version: 4, allowedActions: [], emptyReturnWarning: { status: 'RESOLVED', deadlineAt: null, overdueHours: 0, overdueDays: 0 } } })) })
|
||||||
const confirm = [...document.body.querySelectorAll('button')].filter((item) => item.textContent?.includes('确认还柜')).at(-1) as HTMLButtonElement
|
const confirm = [...document.body.querySelectorAll('button')].filter((item) => item.textContent?.includes('确认还柜')).at(-1) as HTMLButtonElement
|
||||||
confirm.click()
|
confirm.click()
|
||||||
await flushPromises()
|
await flushPromises()
|
||||||
expect(recordTerminalDrayageEmptyReturn).toHaveBeenCalledTimes(1)
|
expect(recordTerminalDrayageEmptyReturn).toHaveBeenCalledTimes(1)
|
||||||
expect(recordTerminalDrayageEmptyReturn).toHaveBeenCalledWith('9001', expect.objectContaining({ expectedVersion: 3, actualAt: expect.any(String) }))
|
expect(recordTerminalDrayageEmptyReturn).toHaveBeenCalledWith('9001', { expectedVersion: 3, actualAt: '2026-09-11T13:00:00', remark: '还柜凭证已核对' })
|
||||||
expect(wrapper.text()).toContain('已还柜')
|
expect(wrapper.text()).toContain('已还柜')
|
||||||
|
expect(listContainers).toHaveBeenCalledTimes(callsBefore + 1)
|
||||||
|
expect(wrapper.findAll('.row-actions button').some((button) => button.text() === '确认还柜')).toBe(false)
|
||||||
|
wrapper.unmount()
|
||||||
|
})
|
||||||
|
|
||||||
|
it('cancels empty return without writing and preserves input on failure while preventing duplicate requests', async () => {
|
||||||
|
const router = createTestRouter()
|
||||||
|
await router.push('/'); await router.isReady()
|
||||||
|
const wrapper = mount(ContainersView, { props: { workbench: 'OVERSEAS' }, attachTo: document.body, global: { plugins: [ElementPlus, router] } })
|
||||||
|
await flushPromises()
|
||||||
|
const open = async () => {
|
||||||
|
await wrapper.findAll('.row-actions button').find((button) => button.text() === '确认还柜')!.trigger('click')
|
||||||
|
await flushPromises()
|
||||||
|
return wrapper.findAllComponents(AppDialog).find((dialog) => dialog.props('title') === '确认还柜')!
|
||||||
|
}
|
||||||
|
const first = await open()
|
||||||
|
first.vm.$emit('update:modelValue', false)
|
||||||
|
await flushPromises()
|
||||||
|
expect(recordTerminalDrayageEmptyReturn).not.toHaveBeenCalled()
|
||||||
|
const dialog = await open()
|
||||||
|
const state = wrapper.vm as unknown as { drayageActionAt: string; drayageReason: string; drayageActionOpen: boolean; drayageActionError: { message: string } | null }
|
||||||
|
state.drayageActionAt = '2026-09-11T13:00:00'
|
||||||
|
state.drayageReason = '还柜凭证已核对'
|
||||||
|
let reject!: (reason: Error) => void
|
||||||
|
vi.mocked(recordTerminalDrayageEmptyReturn).mockReturnValueOnce(new Promise((_resolve, rejectRequest) => { reject = rejectRequest }))
|
||||||
|
dialog.vm.$emit('confirm')
|
||||||
|
dialog.vm.$emit('confirm')
|
||||||
|
expect(recordTerminalDrayageEmptyReturn).toHaveBeenCalledTimes(1)
|
||||||
|
expect(recordTerminalDrayageEmptyReturn).toHaveBeenCalledWith('9001', { expectedVersion: 3, actualAt: '2026-09-11T13:00:00', remark: '还柜凭证已核对' })
|
||||||
|
reject(new Error('还柜时间不符合业务要求'))
|
||||||
|
await flushPromises()
|
||||||
|
expect(state.drayageActionOpen).toBe(true)
|
||||||
|
expect(state.drayageActionAt).toBe('2026-09-11T13:00:00')
|
||||||
|
expect(state.drayageReason).toBe('还柜凭证已核对')
|
||||||
|
expect(state.drayageActionError).not.toBeNull()
|
||||||
|
wrapper.unmount()
|
||||||
})
|
})
|
||||||
|
|
||||||
it('hides the direct empty-return action without permission or without the server action', async () => {
|
it('hides the direct empty-return action without permission or without the server action', async () => {
|
||||||
@@ -352,6 +394,8 @@ describe('ContainersView', () => {
|
|||||||
|
|
||||||
vm.openDrayageAction(vm.rows[0], 'RECORD_EMPTY_RETURN')
|
vm.openDrayageAction(vm.rows[0], 'RECORD_EMPTY_RETURN')
|
||||||
vm.drayageActionAt = '2026-09-11T13:00:00'
|
vm.drayageActionAt = '2026-09-11T13:00:00'
|
||||||
|
const page: Awaited<ReturnType<typeof listContainers>> = await vi.mocked(listContainers).mock.results.at(-1)!.value
|
||||||
|
vi.mocked(listContainers).mockResolvedValueOnce({ ...page, records: page.records.map((row) => ({ ...row, allowedActions: [], terminalDrayage: { ...drayage, status: 'EMPTY_RETURNED', version: 5, allowedActions: [], emptyReturnWarning: { status: 'RESOLVED', deadlineAt: null, overdueHours: 0, overdueDays: 0 } } })) })
|
||||||
await vm.saveDrayageAction()
|
await vm.saveDrayageAction()
|
||||||
expect(recordTerminalDrayageEmptyReturn).toHaveBeenCalledWith('9001', { expectedVersion: 4, actualAt: '2026-09-11T13:00:00', remark: undefined })
|
expect(recordTerminalDrayageEmptyReturn).toHaveBeenCalledWith('9001', { expectedVersion: 4, actualAt: '2026-09-11T13:00:00', remark: undefined })
|
||||||
expect(wrapper.text()).toContain('已还柜')
|
expect(wrapper.text()).toContain('已还柜')
|
||||||
|
|||||||
@@ -570,6 +570,7 @@ async function saveDrayageAction() {
|
|||||||
: await recordTerminalDrayageEmptyReturn(row.id, { expectedVersion: current.version, actualAt: drayageActionAt.value, remark: reason })
|
: await recordTerminalDrayageEmptyReturn(row.id, { expectedVersion: current.version, actualAt: drayageActionAt.value, remark: reason })
|
||||||
replaceDrayage(next); drayageActionOpen.value = false
|
replaceDrayage(next); drayageActionOpen.value = false
|
||||||
ElMessage.success(`${drayageActionText(drayageAction.value)}成功`)
|
ElMessage.success(`${drayageActionText(drayageAction.value)}成功`)
|
||||||
|
await loadRows()
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
const details = getApiErrorDetails(error, `${drayageActionText(drayageAction.value)}失败`)
|
const details = getApiErrorDetails(error, `${drayageActionText(drayageAction.value)}失败`)
|
||||||
if (details.code === 'A0409') {
|
if (details.code === 'A0409') {
|
||||||
|
|||||||
Reference in New Issue
Block a user