fix(dispatch): align dispatch actions and native return workflows

This commit is contained in:
2026-10-18 16:37:37 +08:00
parent 4806f88d7c
commit a5a1976924
29 changed files with 854 additions and 150 deletions
@@ -3,6 +3,7 @@ import { ElInput } from 'element-plus/es/components/input/index'
import { afterEach, beforeEach, expect, it, vi } from 'vitest'
import { shallowMount, flushPromises, type VueWrapper } from '@vue/test-utils'
import TripBookingPanel from './TripBookingPanel.vue'
import TripBookingFulfillmentDialog from './TripBookingFulfillmentDialog.vue'
import * as api from '../api/tripBookings'
import type { FleetTrip } from '../tripTypes'
import type { TripBooking } from '../bookingTypes'
@@ -147,3 +148,30 @@ it('keeps manual entry available when candidate lookup fails and hides lookup wi
const denied = mount(); await flushPromises(); await button(denied, '编辑').trigger('click'); await flushPromises()
expect(denied.findAll('button').some(node => node.text() === '查询报价库')).toBe(false)
})
it.each(['CONFIRMED', 'RECEIVED'] as const)('opens the existing receipt confirmation for an unbound %s booking', async status => {
const current = { ...row, status, incomingNo: 'WT-1', recordVersion: 7 }
vi.mocked(api.listTripBookings).mockResolvedValue({ tripRecordVersion: 9, records: [current] })
const wrapper = mount(); await flushPromises()
await button(wrapper, '核对实收').trigger('click'); await flushPromises()
expect(wrapper.findComponent(TripBookingFulfillmentDialog).props()).toMatchObject({ modelValue: true, mode: 'receipt', booking: current })
})
it.each([
{ status: 'BOUND' as const, dispatchNo: 'DS-1', planned: true, permitted: true },
{ status: 'RECEIVED' as const, dispatchNo: 'DS-1', planned: true, permitted: true },
{ status: 'RECEIVED' as const, dispatchNo: null, planned: false, permitted: true },
{ status: 'RECEIVED' as const, dispatchNo: null, planned: true, permitted: false },
{ status: 'CANCELLED' as const, dispatchNo: null, planned: true, permitted: true },
])('blocks receipt rechecking for $status / bound=$dispatchNo / planned=$planned / permitted=$permitted', async scenario => {
access.writable = scenario.permitted
const current = { ...row, status: scenario.status, incomingNo: 'WT-1', dispatchNo: scenario.dispatchNo }
vi.mocked(api.listTripBookings).mockResolvedValue({ tripRecordVersion: 4, records: [current] })
const wrapper = mount()
if (!scenario.planned) await wrapper.setProps({ trip: { ...trip, status: 'IN_TRANSIT' } })
await flushPromises()
expect(wrapper.findAll('button').some(node => node.text() === '核对实收')).toBe(false)
const state = wrapper.vm as unknown as { fulfill: (booking: TripBooking, mode: 'receipt') => void }
state.fulfill(current, 'receipt'); await flushPromises()
expect(wrapper.findComponent(TripBookingFulfillmentDialog).props('modelValue')).toBe(false)
})