feat(appointment): integrate work calendar and original business actions
This commit is contained in:
@@ -0,0 +1,88 @@
|
||||
import { flushPromises, mount } from '@vue/test-utils'
|
||||
import { beforeEach, expect, it, vi } from 'vitest'
|
||||
import WorkDetailPanel from './WorkDetailPanel.vue'
|
||||
import { getWorkDetail, type WorkAction, type WorkDetail } from './workApi'
|
||||
import { followAction, workDetail, workItem } from './work.test-support'
|
||||
vi.mock('./workApi', async original => ({ ...await original<typeof import('./workApi')>(), getWorkDetail: vi.fn() }))
|
||||
function render(detail: WorkDetail, initialAction?: WorkAction) {
|
||||
vi.mocked(getWorkDetail).mockResolvedValue(detail)
|
||||
return mount(WorkDetailPanel, { props: { itemKey: detail.item.workItemKey, initialAction, canLeave: async () => true }, global: { stubs: {
|
||||
ElButton: { template: '<button><slot /></button>' },
|
||||
CargoVerificationForm: { name: 'CargoVerificationForm', props: ['source'], template: '<div data-cargo />' },
|
||||
WorkCommandForm: { name: 'WorkCommandForm', props: ['detail', 'action'], template: '<div data-command />' },
|
||||
WorkAppointmentForm: { name: 'WorkAppointmentForm', props: ['appointmentNo', 'canWrite'], template: '<div data-appointment />' },
|
||||
WorkCreateAppointmentForm: { name: 'WorkCreateAppointmentForm', props: ['sources', 'canWrite'], template: '<div data-create />' },
|
||||
WorkDispatchForm: { name: 'WorkDispatchForm', props: ['batchKeys', 'canWrite'], template: '<div data-dispatch />' },
|
||||
WorkExistingDispatchForm: { name: 'WorkExistingDispatchForm', props: ['dispatches', 'canWrite'], template: '<div data-existing-dispatch />' },
|
||||
WorkReceiptPlans: { name: 'WorkReceiptPlans', props: ['containerBookingId'], template: '<div data-receiving />' },
|
||||
LoadErrorState: { props: ['message'], template: '<p>{{ message }}</p>' }, ApiErrorMeta: true,
|
||||
} } })
|
||||
}
|
||||
beforeEach(() => vi.clearAllMocks())
|
||||
it('never sends customer-warehouse dispatch batches to the FBA candidate endpoint', async () => {
|
||||
const action: WorkAction = { code: 'ARRANGE_TRANSPORT', label: '安排运输', targetWorkItemKey: null, target: { kind: 'FLEET_TRIP', tripId: null, batchKeys: ['DISPATCH:9007199254740999'] } }
|
||||
const wrapper = render(workDetail({ item: workItem({ allowedActions: [action], nextAction: action }) }), action); await flushPromises()
|
||||
expect(wrapper.findComponent({ name: 'WorkDispatchForm' }).exists()).toBe(false); wrapper.unmount()
|
||||
})
|
||||
it('resolves a customer-warehouse batch using the frozen work item batch number', async () => {
|
||||
const action: WorkAction = { code: 'ARRANGE_TRANSPORT', label: '安排运输', targetWorkItemKey: null, target: { kind: 'FLEET_TRIP', tripId: null, batchKeys: ['DISPATCH:9007199254740999'] } }
|
||||
const wrapper = render(workDetail({ item: workItem({ batchKey: 'DISPATCH:9007199254740999', batchNo: 'DS-ORIGINAL', allowedActions: [action], nextAction: action }) }), action); await flushPromises()
|
||||
expect(wrapper.getComponent({ name: 'WorkExistingDispatchForm' }).props('dispatches')).toEqual([{ batchKey: 'DISPATCH:9007199254740999', dispatchNo: 'DS-ORIGINAL' }])
|
||||
expect(wrapper.findComponent({ name: 'WorkDispatchForm' }).exists()).toBe(false); wrapper.unmount()
|
||||
})
|
||||
it('opens real batch dispatch when the server has not bound a trip yet', async () => {
|
||||
const action: WorkAction = { code: 'ARRANGE_TRANSPORT', label: '安排运输', targetWorkItemKey: null, target: { kind: 'FLEET_TRIP', tripId: null, batchKeys: ['FBA_APPOINTMENT:1'] } }
|
||||
const wrapper = render(workDetail({ item: workItem({ allowedActions: [action], nextAction: action }) }), action); await flushPromises()
|
||||
expect(wrapper.getComponent({ name: 'WorkDispatchForm' }).props('batchKeys')).toEqual(['FBA_APPOINTMENT:1']); wrapper.unmount()
|
||||
})
|
||||
it('loads the exact container plan list for a multiple-plan receiving target', async () => {
|
||||
const action: WorkAction = { code: 'RECORD_EXECUTION', label: '查看接仓', targetWorkItemKey: null, target: { kind: 'WAREHOUSE_RECEIPT', inboundPlanId: null, inboundPlanNo: null, customerId: null, containerBookingId: '9007199254740999' } }
|
||||
const wrapper = render(workDetail({ item: workItem({ allowedActions: [action], nextAction: action }) }), action); await flushPromises()
|
||||
expect(wrapper.getComponent({ name: 'WorkReceiptPlans' }).props('containerBookingId')).toBe('9007199254740999'); wrapper.unmount()
|
||||
})
|
||||
it('opens a real create form for unappointed sources with their exact customer scope', async () => {
|
||||
const action: WorkAction = { code: 'SAVE_PLAN', label: '安排预约', targetWorkItemKey: null, target: { kind: 'FBA_APPOINTMENT', appointmentId: null, batchKey: null, sources: [{ sourceKey: 'BOOKING:1', customerId: 'customer-a', destinationKey: 'FBA:ONT8' }] } }
|
||||
const wrapper = render(workDetail({ item: workItem({ allowedActions: [action], nextAction: action }) }), action); await flushPromises()
|
||||
expect(wrapper.getComponent({ name: 'WorkCreateAppointmentForm' }).props('sources')).toEqual(action.target.kind === 'FBA_APPOINTMENT' ? action.target.sources : [])
|
||||
expect(wrapper.text()).not.toContain('尚未关联可操作的预约记录'); wrapper.unmount()
|
||||
})
|
||||
it('opens the server-selected prerequisite instead of issuing the current item command', async () => {
|
||||
const action = { ...followAction, targetWorkItemKey: 'work-pickup-1' }
|
||||
const wrapper = render(workDetail({ item: workItem({ allowedActions: [action], nextAction: action }) }), action); await flushPromises()
|
||||
expect(wrapper.emitted('open')).toEqual([['work-pickup-1', action]])
|
||||
expect(wrapper.findComponent({ name: 'WorkCommandForm' }).exists()).toBe(false); wrapper.unmount()
|
||||
})
|
||||
it('uses explicit cargo context and preserves large customer IDs without inspecting a business key', async () => {
|
||||
const action: WorkAction = { code: 'VERIFY_CARGO', label: '核实货物资料', targetWorkItemKey: null, target: { kind: 'CARGO_VERIFICATION', source: { customerId: '9007199254740999', sourceKey: 'BOOKING:B-1', destinationKey: 'FBA:ONT8' } } }
|
||||
const wrapper = render(workDetail({ item: workItem({ allowedActions: [action], nextAction: action }) }), action); await flushPromises()
|
||||
expect(wrapper.getComponent({ name: 'CargoVerificationForm' }).props('source')).toEqual(action.target.kind === 'CARGO_VERIFICATION' ? action.target.source : null)
|
||||
expect(wrapper.text()).not.toContain('9007199254740999'); wrapper.unmount()
|
||||
})
|
||||
it('rechecks current qualification before opening a previously offered write action', async () => {
|
||||
const wrapper = render(workDetail({ item: workItem({ allowedActions: [], nextAction: null, blockReasons: ['预约已由其他人修改'] }) }), followAction); await flushPromises()
|
||||
expect(wrapper.findComponent({ name: 'WorkCommandForm' }).exists()).toBe(false)
|
||||
expect(wrapper.text()).toContain('预约已由其他人修改'); wrapper.unmount()
|
||||
})
|
||||
it('continues the real unbatched cargo action into original appointment creation with exact source scope', async () => {
|
||||
const source = { customerId: 'customer-a', sourceKey: 'BOOKING:9007199254740999', destinationKey: 'FBA:ONT8' }
|
||||
const action: WorkAction = { code: 'VERIFY_CARGO', label: '核实货物并建立预约', targetWorkItemKey: null, target: { kind: 'CARGO_VERIFICATION', source } }
|
||||
const wrapper = render(workDetail({ item: workItem({ allowedActions: [action], nextAction: action }) }), action); await flushPromises()
|
||||
wrapper.getComponent({ name: 'CargoVerificationForm' }).vm.$emit('saved'); await flushPromises()
|
||||
expect(wrapper.emitted('saved')).toBeUndefined()
|
||||
await wrapper.findAll('button').find(button => button.text() === '核对货物并新建原预约')!.trigger('click'); await flushPromises()
|
||||
expect(wrapper.getComponent({ name: 'WorkCreateAppointmentForm' }).props('sources')).toEqual([source]); wrapper.unmount()
|
||||
})
|
||||
it('uses the matched appointment business number and never passes the internal appointment ID to the old API', async () => {
|
||||
const action: WorkAction = { code: 'CHANGE_BOOKING', label: '调整预约', targetWorkItemKey: null, target: { kind: 'FBA_APPOINTMENT', appointmentId: 'internal-id', batchKey: 'batch-1', sources: [] } }
|
||||
const detail = workDetail({ item: workItem({ allowedActions: [action], nextAction: action }), references: [{ kind: 'FBA_APPOINTMENT', key: 'batch-1', businessNo: 'AP-TEST', label: '预约' }] })
|
||||
const wrapper = render(detail, action); await flushPromises()
|
||||
expect(wrapper.getComponent({ name: 'WorkAppointmentForm' }).props('appointmentNo')).toBe('AP-TEST'); wrapper.unmount()
|
||||
const missing = render({ ...detail, references: [] }, action); await flushPromises()
|
||||
expect(missing.findComponent({ name: 'WorkAppointmentForm' }).exists()).toBe(false)
|
||||
expect(missing.text()).toContain('尚未关联可操作的预约记录'); missing.unmount()
|
||||
})
|
||||
it('displays all associated containers of a shared trip without creating duplicate transport work', async () => {
|
||||
const wrapper = render(workDetail({ item: workItem({ type: 'TRANSPORT', containers: [{ containerBookingId: '1', bookingNo: 'D-1', containerNo: 'TEST-CN-A' }, { containerBookingId: '2', bookingNo: 'D-2', containerNo: 'TEST-CN-B' }] }) })); await flushPromises()
|
||||
expect(wrapper.findAll('.container-links button')).toHaveLength(2)
|
||||
await wrapper.findAll('.container-links button')[1]!.trigger('click'); expect(wrapper.emitted('container')).toEqual([['2', 'TEST-CN-B']]); wrapper.unmount()
|
||||
})
|
||||
Reference in New Issue
Block a user