feat(fleet): select versioned sales quotes and isolate cost drafts

This commit is contained in:
2026-10-03 13:20:35 +08:00
parent 3440f9dd11
commit 5641ea0f7e
15 changed files with 288 additions and 31 deletions
@@ -2,6 +2,7 @@ import { afterEach, beforeEach, expect, it, vi } from 'vitest'
import { shallowMount, flushPromises, type VueWrapper } from '@vue/test-utils'
import VehicleCareFinanceLedger from './VehicleCareFinanceLedger.vue'
import * as api from '../api/companyVehicleCosts'
import { ApiResponseError } from '@/shared/api/http'
const access = vi.hoisted(() => ({ permissions: new Set<string>() }))
vi.mock('@/core/access/accessPreview', () => ({ useAccessPreviewStore: () => ({ can: (permission: string) => access.permissions.has(permission) }) }))
vi.mock('element-plus', () => ({ ElMessage: { success: vi.fn(), warning: vi.fn() } }))
@@ -98,3 +99,40 @@ it('cannot void or pay again when the server says paid, and booking is an indepe
expect(api.commandCompanyCost).toHaveBeenCalledWith('VC-ONE', 'approve', expect.any(String), { expectedVersion: '0', reason: null })
expect(api.payCompanyCost).not.toHaveBeenCalled()
})
it('does not carry another cost payment draft into the next record', async () => {
const w = mount(); await flushPromises(); await button(w, '登记付款').trigger('click'); await flushPromises()
w.findComponent({ name: 'ElInputNumber' }).vm.$emit('update:modelValue', 20)
await w.get('input[placeholder="填写银行流水号或支付凭据号"]').setValue('previous-cost-reference')
await files(w)
vi.mocked(api.getCompanyCost).mockResolvedValueOnce({ ...detail, costNo: 'VC-TWO' })
await w.setProps({ costNo: 'VC-TWO' }); await flushPromises()
await button(w, '登记付款').trigger('click'); await flushPromises()
expect(w.findComponent({ name: 'ElInputNumber' }).props('modelValue')).toBeNull()
expect((w.get('input[placeholder="填写银行流水号或支付凭据号"]').element as HTMLInputElement).value).toBe('')
expect(w.text()).not.toContain('已上传:payment.png')
})
it.each(['A0422', 'FIN_409'])('unlocks a definite HTTP200 %s rejection while retaining entered payment and vouchers', async code => {
vi.mocked(api.payCompanyCost).mockRejectedValueOnce(new ApiResponseError('明确拒绝', code, 200))
const w = mount(); await flushPromises(); await button(w, '登记付款').trigger('click'); await flushPromises()
w.findComponent({ name: 'ElInputNumber' }).vm.$emit('update:modelValue', 20); await files(w)
await button(w, '确认操作').trigger('click'); await flushPromises()
const first = vi.mocked(api.payCompanyCost).mock.calls[0]!
expect(w.text()).not.toContain('上次操作结果尚未确认'); expect(w.text()).toContain('已上传:payment.png')
w.findComponent({ name: 'ElInputNumber' }).vm.$emit('update:modelValue', 10)
await button(w, '确认操作').trigger('click'); await flushPromises()
const second = vi.mocked(api.payCompanyCost).mock.calls[1]!
expect(second[1]).not.toBe(first[1]); expect(second[2].amount).toBe(10)
expect(api.uploadCompanyCostVoucher).toHaveBeenCalledTimes(1)
})
it('ignores an old detail response after another cost is opened', async () => {
let finish!: (value: api.CompanyCost) => void
vi.mocked(api.getCompanyCost).mockImplementationOnce(() => new Promise(resolve => { finish = resolve }))
const w = mount()
vi.mocked(api.getCompanyCost).mockResolvedValueOnce({ ...detail, costNo: 'VC-TWO' })
await w.setProps({ costNo: 'VC-TWO' }); await flushPromises()
finish(detail); await flushPromises()
expect(w.text()).toContain('VC-TWO'); expect(w.text()).not.toContain('VC-ONE')
})