|
|
|
@@ -10,9 +10,9 @@ import { useUnsavedWorkStore } from '@/core/unsaved-work/unsavedWork'
|
|
|
|
|
import * as api from '../api/capacityOffers'
|
|
|
|
|
import type { CapacityOffer, CapacityInquiry } from '../capacityOfferTypes'
|
|
|
|
|
import type { FleetTrip } from '../tripTypes'
|
|
|
|
|
const state = vi.hoisted(() => ({ manage: false, write: true, quote: false, route: { name: 'overseas-master-data-capacity-offer-detail', params: { offerNo: 'CO-1' } }, push: vi.fn() }))
|
|
|
|
|
const state = vi.hoisted(() => ({ manage: false, write: true, quote: false, links: new Set<string>(), route: { name: 'overseas-master-data-capacity-offer-detail', params: { offerNo: 'CO-1' } }, push: vi.fn() }))
|
|
|
|
|
vi.mock('vue-router', () => ({ useRoute: () => state.route, useRouter: () => ({ push: state.push }) }))
|
|
|
|
|
vi.mock('@/core/access/accessPreview', () => ({ useAccessPreviewStore: () => ({ can: (p: string) => p === 'platform:fleet:capacity-offer:manage' ? state.manage : p === 'platform:fleet:quote:list' ? state.quote : p === 'platform:fleet:capacity-offer:list' || p.includes(':inquiry-') && state.write }) }))
|
|
|
|
|
vi.mock('@/core/access/accessPreview', () => ({ useAccessPreviewStore: () => ({ can: (p: string) => state.links.has(p) || (p === 'platform:fleet:capacity-offer:manage' ? state.manage : p === 'platform:fleet:quote:list' ? state.quote : p === 'platform:fleet:capacity-offer:list' || p.includes(':inquiry-') && state.write) }) }))
|
|
|
|
|
vi.mock('element-plus', () => ({ ElMessage: { success: vi.fn(), warning: vi.fn() }, ElMessageBox: { confirm: vi.fn().mockResolvedValue(undefined) } }))
|
|
|
|
|
vi.mock('../api/capacityOffers', () => ({ listCapacityOffers: vi.fn(), listCapacityInquiries: vi.fn(), getCapacityOffer: vi.fn(), getCapacityInquiry: vi.fn(), saveCapacityInquiry: vi.fn(), saveAndConfirmInquiry: vi.fn(), commandCapacityInquiry: vi.fn(), listOfferCustomers: vi.fn(), listOfferQuotes: vi.fn(), saveCapacityOffer: vi.fn(), commandCapacityOffer: vi.fn() }))
|
|
|
|
|
export const offer: CapacityOffer = { offerNo: 'CO-1', tripNo: 'TR-1', status: 'OPEN', recordVersion: 2, tripRecordVersion: 5, warehouseId: '92', warehouseName: '测试起运仓', plannedDepartureAt: '2026-10-10T12:00:00Z', cutoffAt: '2026-10-10T10:00:00Z', contactName: '调度', contactPhone: '测试联系信息', remark: null, capacityKnown: false, canCreateInquiry: true, canConfirm: false, blockReason: '待调度核对', destinations: [{ destinationKey: 'FBA:ONT8', name: 'ONT8', deliveryIndex: 1, remaining: null }], segments: [], actions: { canEdit: true, canPublish: false, canPause: true, canResume: false, canClose: true } }
|
|
|
|
@@ -35,7 +35,7 @@ function mount(component: object, props: Record<string, unknown> = {}) {
|
|
|
|
|
} } }); wrappers.push(wrapper); return { wrapper, pinia }
|
|
|
|
|
}
|
|
|
|
|
const button = (w: VueWrapper, label: string) => w.findAll('button').find(b => b.text() === label)!
|
|
|
|
|
beforeEach(() => { vi.clearAllMocks(); state.manage = false; state.write = true; state.quote = false; vi.mocked(ElMessageBox.confirm).mockImplementation(vi.fn().mockResolvedValue(undefined)); vi.mocked(api.listCapacityOffers).mockResolvedValue(pageOf([structuredClone(offer)], 41)); vi.mocked(api.listCapacityInquiries).mockResolvedValue(pageOf([structuredClone(inquiry)])); vi.mocked(api.getCapacityOffer).mockResolvedValue({ offer: structuredClone(offer), inquiries: pageOf([structuredClone(inquiry)]) }) })
|
|
|
|
|
beforeEach(() => { vi.clearAllMocks(); state.manage = false; state.write = true; state.quote = false; state.links.clear(); vi.mocked(ElMessageBox.confirm).mockImplementation(vi.fn().mockResolvedValue(undefined)); vi.mocked(api.listCapacityOffers).mockResolvedValue(pageOf([structuredClone(offer)], 41)); vi.mocked(api.listCapacityInquiries).mockResolvedValue(pageOf([structuredClone(inquiry)])); vi.mocked(api.getCapacityOffer).mockResolvedValue({ offer: structuredClone(offer), inquiries: pageOf([structuredClone(inquiry)]) }) })
|
|
|
|
|
afterEach(() => { wrappers.splice(0).forEach(w => w.unmount()); vi.useRealTimers() })
|
|
|
|
|
it('loads only the active tab and sends exactly one query after resetting from a later page', async () => {
|
|
|
|
|
const { wrapper } = mount(CapacityOffersView); await flushPromises()
|
|
|
|
@@ -161,3 +161,33 @@ it('requires an explicit choice for multiple destinations', async () => {
|
|
|
|
|
wrapper.findComponent({ name: 'AppDialog' }).vm.$emit('confirm'); await flushPromises()
|
|
|
|
|
expect(api.saveCapacityInquiry).not.toHaveBeenCalled()
|
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
// Navigation must retain the exact bound business references and independent read permissions.
|
|
|
|
|
it.each([CapacityOffersView, CapacityOfferDetailView])('opens the bound trip separately from read-only dispatch detail', async component => {
|
|
|
|
|
const row = { ...inquiry, dispatchNo: 'DS-BOUND', status: 'BOUND' as const }
|
|
|
|
|
state.links = new Set(['platform:fleet:trip:list', 'platform:overseas-warehouse:dispatch:list'])
|
|
|
|
|
const { wrapper } = mount(component); await flushPromises()
|
|
|
|
|
const dialog = wrapper.findComponent(CapacityInquiryDialog)
|
|
|
|
|
dialog.vm.$emit('action', 'dispatch', row); await flushPromises()
|
|
|
|
|
expect(state.push).toHaveBeenLastCalledWith({ name: 'overseas-dispatch-detail', params: { dispatchNo: 'DS-BOUND' } })
|
|
|
|
|
dialog.vm.$emit('action', 'trip', row); await flushPromises()
|
|
|
|
|
expect(state.push).toHaveBeenLastCalledWith({ name: 'overseas-master-data-trip-detail', params: { tripNo: row.tripNo } })
|
|
|
|
|
expect(api.commandCapacityInquiry).not.toHaveBeenCalled()
|
|
|
|
|
state.links.clear(); state.push.mockClear()
|
|
|
|
|
dialog.vm.$emit('action', 'trip', row); dialog.vm.$emit('action', 'dispatch', row); await flushPromises()
|
|
|
|
|
expect(state.push).not.toHaveBeenCalled()
|
|
|
|
|
})
|
|
|
|
|
it('shows follow-up destinations only with the matching read permission', async () => {
|
|
|
|
|
const row = { ...inquiry, dispatchNo: 'DS-BOUND', status: 'BOUND' as const }
|
|
|
|
|
state.links.add('platform:overseas-warehouse:dispatch:list')
|
|
|
|
|
const { wrapper } = mount(CapacityInquiryDialog, { modelValue: true, offer, inquiry: row, readonly: true }); await flushPromises()
|
|
|
|
|
expect(button(wrapper, '查看排车详情')).toBeDefined()
|
|
|
|
|
expect(button(wrapper, '前往车次审核')).toBeUndefined()
|
|
|
|
|
wrapper.unmount()
|
|
|
|
|
state.links = new Set(['platform:fleet:trip:list'])
|
|
|
|
|
const second = mount(CapacityInquiryDialog, { modelValue: true, offer, inquiry: row, readonly: true }); await flushPromises()
|
|
|
|
|
expect(button(second.wrapper, '查看排车详情')).toBeUndefined()
|
|
|
|
|
await button(second.wrapper, '前往车次审核').trigger('click')
|
|
|
|
|
expect(second.wrapper.emitted('action')).toEqual([['trip', row]])
|
|
|
|
|
expect(api.commandCapacityInquiry).not.toHaveBeenCalled()
|
|
|
|
|
})
|
|
|
|
|