Files
admin-web/src/modules/overseas-business/components/BusinessOrderFees.test.ts
T
2026-10-26 13:43:08 +08:00

329 lines
23 KiB
TypeScript

import ElementPlus, { ElDialog } from 'element-plus'
import { createPinia } from 'pinia'
import { enableAutoUnmount, flushPromises, mount, type VueWrapper } from '@vue/test-utils'
import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest'
import { ApiResponseError } from '@/shared/api/http'
import OverseasFeeEditor from './OverseasFeeEditor.vue'
import AppDialog from '@/shared/components/AppDialog.vue'
import * as api from '../api/overseasFees'
import type { OverseasFee } from '../api/overseasFees'
import BusinessOrderFees from './BusinessOrderFees.vue'
import { adjustStorageRentFee } from '../api/storageRent'
vi.mock('../api/storageRent', () => ({ adjustStorageRentFee: vi.fn() }))
const can = vi.hoisted(() => vi.fn((_permission: string) => true))
vi.mock('@/core/access/accessPreview', () => ({ useAccessPreviewStore: () => ({ can }) }))
vi.mock('../api/overseasFees', () => ({
actOnOverseasFee: vi.fn(), getOverseasFee: vi.fn(), listOverseasFeeCustomers: vi.fn(), listOverseasFeeItems: vi.fn(),
listOverseasFeeRetries: vi.fn(),
listOverseasFees: vi.fn(), listBusinessOverseasFees: vi.fn(), listBusinessFeeOptions: vi.fn(), createOverseasFee: vi.fn(),
}))
const fee: OverseasFee = {
direction: 'RECEIVABLE',
id: '9223372036854775001', sourceFeeId: '9223372036854775001', sourceVersion: 3,
customerId: '9223372036854775002', customerName: '测试客户', sourceType: 'OVERSEAS_WAREHOUSE_FEE', businessType: 'WAREHOUSE_TRANSFER',
businessId: '9223372036854775003', businessNo: 'WT202610220001', orderId: null, warehouseId: '9223372036854775004',
feeItemCode: 'HANDLING', feeItemName: '操作费', businessDate: '2026-10-22', currency: 'CNY', amount: 70,
originalCurrency: 'USD', originalAmount: 10, chargeableQuantity: null, quantityUnit: null, unitPrice: null,
periodStart: null, periodEnd: null, reviewStatus: 'PENDING', sourceStatus: 'ACTIVE', publicationStatus: 'UNPUBLISHED',
collectionMode: 'WALLET', walletStatus: 'FROZEN', walletErrorMessage: null, reviewReason: null, reviewerId: null, reviewedAt: null,
createdAt: '2026-10-22T10:00:00', updatedAt: '', allowedActions: ['APPROVE', 'REJECT', 'CANCEL'],
}
const business = { businessId: fee.businessId!, businessType: fee.businessType, businessNo: fee.businessNo, customerId: fee.customerId, customerName: fee.customerName, warehouseId: fee.warehouseId, orderId: fee.orderId }
function deferred<T>() {
let resolve!: (value: T) => void, reject!: (reason: unknown) => void
const promise = new Promise<T>((yes, no) => { resolve = yes; reject = no })
return { promise, resolve, reject }
}
async function mountPage() {
const wrapper = mount(BusinessOrderFees, { props: { business }, attachTo: document.body, global: { plugins: [ElementPlus, createPinia()] } })
await flushPromises()
return wrapper
}
async function click(wrapper: VueWrapper, label: string, selector = 'button') {
const button = wrapper.findAll(selector).find(node => node.text().trim() === label)
expect(button, label).toBeDefined()
await button!.trigger('click'); await flushPromises()
}
function activeDialog(wrapper: VueWrapper) {
const dialog = wrapper.findAllComponents(AppDialog).find(component => component.props('modelValue'))
expect(dialog).toBeDefined()
return dialog!.getComponent({ name: 'ElDialogContent' })
}
enableAutoUnmount(afterEach)
beforeEach(() => {
vi.resetAllMocks(); can.mockReturnValue(true)
vi.mocked(api.listOverseasFeeRetries).mockResolvedValue([])
vi.stubGlobal('ResizeObserver', class { observe() {} unobserve() {} disconnect() {} })
vi.mocked(api.listOverseasFeeCustomers).mockResolvedValue([{ customerId: fee.customerId, customerName: fee.customerName }])
vi.mocked(api.listOverseasFeeItems).mockResolvedValue([{ feeCode: 'HANDLING', feeTypeName: '操作费', defaultPrice: 20 }])
vi.mocked(api.listBusinessOverseasFees).mockResolvedValue({ records: [fee], total: 1, page: 1, size: 20, pages: 1 })
vi.mocked(api.getOverseasFee).mockResolvedValue(fee)
vi.mocked(api.actOnOverseasFee).mockResolvedValue(fee)
vi.mocked(api.createOverseasFee).mockResolvedValue(fee)
vi.mocked(api.listBusinessFeeOptions).mockResolvedValue([{ businessId: fee.businessId!, businessType: fee.businessType, businessNo: fee.businessNo,
customerId: fee.customerId, customerName: fee.customerName, warehouseId: fee.warehouseId, orderId: null }])
})
afterEach(() => { vi.restoreAllMocks(); vi.unstubAllGlobals(); document.body.innerHTML = '' })
describe('单据费用操作', () => {
it.each(['RECEIVABLE', 'PAYABLE'] as const)('hides an empty %s block and reveals it when fees are added', async direction => {
vi.mocked(api.listBusinessOverseasFees).mockResolvedValue({ records: [], total: 0, page: 1, size: 20, pages: 0 })
const wrapper = mount(BusinessOrderFees, { props: { business, direction, hideWhenEmpty: true, canCreate: false, heading: direction === 'RECEIVABLE' ? '应收明细' : '应付明细' }, attachTo: document.body, global: { plugins: [ElementPlus, createPinia()] } })
await flushPromises()
expect(wrapper.isVisible()).toBe(false)
vi.mocked(api.listBusinessOverseasFees).mockResolvedValue({ records: [{ ...fee, direction }], total: 1, page: 1, size: 20, pages: 1 })
await wrapper.vm.refresh(); await flushPromises()
expect(wrapper.isVisible()).toBe(true)
expect(wrapper.get('h3').text()).toBe(direction === 'RECEIVABLE' ? '应收明细' : '应付明细')
expect(wrapper.text()).toContain('操作费')
expect(wrapper.findAll('button').some(button => button.text().trim() === '新增费用')).toBe(false)
vi.mocked(api.listBusinessOverseasFees).mockResolvedValue({ records: [], total: 0, page: 1, size: 20, pages: 0 })
await wrapper.vm.refresh(); await flushPromises()
expect(wrapper.isVisible()).toBe(false)
})
it('queries the exact document and creates without a second order selector or catalog default amount', async () => {
const wrapper = await mountPage()
expect(api.listBusinessOverseasFees).toHaveBeenCalledWith([business], { page: 1, size: 20 }, 'RECEIVABLE')
await click(wrapper, '新增费用')
expect(api.listBusinessFeeOptions).not.toHaveBeenCalled()
const dialog = activeDialog(wrapper)
expect(dialog.text()).not.toContain('业务类型')
expect(dialog.text()).not.toContain('业务订单')
expect(dialog.findComponent({ name: 'ElDatePicker' }).exists()).toBe(false)
const selects = dialog.findAllComponents({ name: 'ElSelect' })
selects[1]!.vm.$emit('update:modelValue', 'CNY')
dialog.getComponent({ name: 'ElInputNumber' }).vm.$emit('update:modelValue', 12.34)
selects[0]!.vm.$emit('update:modelValue', 'HANDLING')
selects[0]!.vm.$emit('change', 'HANDLING')
await flushPromises()
expect(dialog.getComponent({ name: 'ElInputNumber' }).props('modelValue')).toBe(12.34)
expect(dialog.text()).toContain('USD 10.00 · 待审核')
await click(dialog, '保存')
expect(api.createOverseasFee).not.toHaveBeenCalled()
dialog.findAllComponents({ name: 'ElInput' }).find(input => input.props('type') === 'textarea')!.vm.$emit('update:modelValue', '补收本次额外操作费用')
await flushPromises()
await click(dialog, '保存')
expect(api.createOverseasFee).toHaveBeenCalledWith(expect.objectContaining({ ...business, feeRemark: '补收本次额外操作费用', amount: 12.34, currency: 'CNY', businessDate: new Date().toLocaleDateString('sv-SE') }), expect.any(String))
expect(wrapper.emitted('changed')).toHaveLength(1)
})
it('checks refreshed eligibility and rejects a fee belonging to another order', async () => {
const wrapper = await mountPage()
vi.mocked(api.getOverseasFee).mockResolvedValueOnce({ ...fee, allowedActions: [] })
await click(wrapper, '审核', '.el-table__body-wrapper button')
expect(wrapper.findAllComponents(ElDialog).some(dialog => dialog.props('modelValue'))).toBe(false)
vi.mocked(api.getOverseasFee).mockResolvedValueOnce({ ...fee, businessId: 'different-order' })
await click(wrapper, '审核', '.el-table__body-wrapper button')
expect(wrapper.findAllComponents(ElDialog).some(dialog => dialog.props('modelValue'))).toBe(false)
expect(api.actOnOverseasFee).not.toHaveBeenCalled()
})
it('keeps supplier payables separate and reviews them for settlement without customer billing hints', async () => {
const payable: OverseasFee = { ...fee, businessType: 'LASTLEG_ORDER', direction: 'PAYABLE', supplierId: 'supplier-1', supplierName: '测试供应商', settlementMode: 'ON_ACCOUNT', collectionMode: 'BILL', walletStatus: 'NOT_REQUIRED' }
vi.mocked(api.listBusinessOverseasFees).mockResolvedValue({ records: [payable], total: 1, page: 1, size: 20, pages: 1 })
vi.mocked(api.getOverseasFee).mockResolvedValue(payable)
const wrapper = mount(BusinessOrderFees, { props: { business: { ...business, businessType: 'LASTLEG_ORDER' }, direction: 'PAYABLE' }, attachTo: document.body, global: { plugins: [ElementPlus, createPinia()] } })
await flushPromises()
expect(api.listBusinessOverseasFees).toHaveBeenLastCalledWith([expect.objectContaining({ businessType: 'LASTLEG_ORDER' })], { page: 1, size: 20 }, 'PAYABLE')
expect(wrapper.text()).toContain('测试供应商'); expect(wrapper.text()).toContain('推送结算')
expect(wrapper.findAll('button').map(button => button.text())).not.toContain('新增费用')
await click(wrapper, '审核', '.el-table__body-wrapper button')
const dialog = activeDialog(wrapper)
expect(dialog.text()).not.toContain('请核对原报价')
await click(dialog, '审核通过')
expect(api.actOnOverseasFee).toHaveBeenCalledWith(fee.sourceFeeId, 'APPROVE', expect.objectContaining({ decision: 'APPROVE' }))
})
it('cannot review the opposite direction returned by a refreshed fee', async () => {
const wrapper = await mountPage()
vi.mocked(api.getOverseasFee).mockResolvedValueOnce({ ...fee, direction: 'PAYABLE' })
await click(wrapper, '审核', '.el-table__body-wrapper button')
expect(wrapper.findAllComponents(AppDialog).some(dialog => dialog.props('modelValue'))).toBe(false)
expect(api.actOnOverseasFee).not.toHaveBeenCalled()
})
it('keeps read-only documents read-only and closes an editor when the order changes', async () => {
const wrapper = await mountPage()
await click(wrapper, '新增费用')
await wrapper.setProps({ business: { ...business, businessId: 'other' } })
await flushPromises()
expect(wrapper.getComponent(OverseasFeeEditor).props('modelValue')).toBe(false)
await wrapper.setProps({ readonly: true })
const visibleButtons = wrapper.findAll('button').filter(button => button.isVisible()).map(button => button.text())
expect(visibleButtons).not.toContain('新增费用')
expect(visibleButtons).not.toContain('审核')
expect(visibleButtons).not.toContain('驳回')
expect(visibleButtons).not.toContain('取消费用')
})
it('does not create against an order or customer changed after the editor was opened', async () => {
const wrapper = mount(OverseasFeeEditor, { props: { modelValue: false, fee: null, business, items: [{ feeCode: 'HANDLING', feeTypeName: '操作费', defaultPrice: 20 }] }, attachTo: document.body, global: { plugins: [ElementPlus, createPinia()] } })
await wrapper.setProps({ modelValue: true }); await flushPromises()
const dialog = activeDialog(wrapper)
dialog.findAllComponents({ name: 'ElSelect' })[0]!.vm.$emit('update:modelValue', 'HANDLING')
dialog.getComponent({ name: 'ElInputNumber' }).vm.$emit('update:modelValue', 10)
await wrapper.setProps({ business: { ...business, customerId: 'different-customer' } })
await flushPromises(); await click(dialog, '保存')
expect(api.createOverseasFee).not.toHaveBeenCalled()
})
it('blocks automatic storage rent from manual addition', async () => {
vi.mocked(api.listOverseasFeeItems).mockResolvedValue([{ feeCode: 'FEE_TYPE_077', feeTypeName: '仓储费', defaultPrice: null }])
const wrapper = await mountPage()
await click(wrapper, '新增费用')
const dialog = activeDialog(wrapper)
const select = dialog.findAllComponents({ name: 'ElSelect' })[0]!
expect(select.findAllComponents({ name: 'ElOption' })).toHaveLength(0)
select.vm.$emit('update:modelValue', 'FEE_TYPE_077')
dialog.getComponent({ name: 'ElInputNumber' }).vm.$emit('update:modelValue', 10)
await flushPromises(); await click(dialog, '保存')
expect(api.createOverseasFee).not.toHaveBeenCalled()
})
it('includes verified related orders but rejects another customer and keeps adjustment on the original order', async () => {
const related = { ...business, businessId: 'return-1', businessType: 'RETURN_ORDER' }
const relatedFee = { ...fee, ...related, allowedActions: ['ADJUST'] }
vi.mocked(api.listBusinessOverseasFees).mockResolvedValue({ records: [relatedFee], total: 1, page: 1, size: 20, pages: 1 })
vi.mocked(api.getOverseasFee).mockResolvedValue(relatedFee)
const wrapper = await mountPage()
await wrapper.setProps({ relatedBusinesses: [related, { ...business, customerId: 'other-customer' }] })
await flushPromises()
expect(api.listBusinessOverseasFees).toHaveBeenLastCalledWith([business, related], { page: 1, size: 20 }, 'RECEIVABLE')
await click(wrapper, '调整', '.el-table__body-wrapper button')
expect(wrapper.getComponent(OverseasFeeEditor).props('business')).toEqual(related)
const dialog = activeDialog(wrapper)
dialog.getComponent({ name: 'ElInputNumber' }).vm.$emit('update:modelValue', 15)
await flushPromises(); await click(dialog, '保存')
expect(api.actOnOverseasFee).toHaveBeenCalledWith(fee.sourceFeeId, 'ADJUST', expect.objectContaining({
expectedVersion: fee.sourceVersion, fee: expect.objectContaining({ businessId: related.businessId, businessType: 'RETURN_ORDER', amount: 15 }),
}))
})
it('shows a voided freeze-failure fixture as closed instead of pending review', async () => {
const closed = { ...fee, sourceStatus: 'VOIDED', reviewStatus: 'PENDING', walletStatus: 'FREEZE_FAILED', walletErrorMessage: '可用余额和授信额度不足', allowedActions: [] }
vi.mocked(api.listBusinessOverseasFees).mockResolvedValue({ records: [closed], total: 1, page: 1, size: 20, pages: 1 })
vi.mocked(api.getOverseasFee).mockResolvedValue(closed)
const wrapper = await mountPage()
expect(wrapper.text()).toContain('已作废')
expect(wrapper.findAll('.el-table__body-wrapper button').map(button => button.text().trim())).toEqual(['详情'])
await click(wrapper, '详情', '.el-table__body-wrapper button')
expect(activeDialog(wrapper).text()).toContain('冻结失败')
expect(activeDialog(wrapper).text()).toContain('此费用已作废,无需继续审核')
expect(activeDialog(wrapper).text()).not.toContain('待审核')
})
it('retries a failed debit only when the refreshed server action is allowed', async () => {
const failed = { ...fee, walletStatus: 'SETTLE_FAILED', allowedActions: ['RETRY_SETTLE'] }
vi.mocked(api.listBusinessOverseasFees).mockResolvedValue({ records: [failed], total: 1, page: 1, size: 20, pages: 1 })
vi.mocked(api.getOverseasFee).mockResolvedValue(failed)
const wrapper = await mountPage()
await click(wrapper, '重试扣费', '.el-table__body-wrapper button')
await click(activeDialog(wrapper), '重试扣费')
expect(api.actOnOverseasFee).toHaveBeenCalledWith(fee.sourceFeeId, 'RETRY_SETTLE', { idempotencyKey: expect.any(String) })
expect(wrapper.emitted('changed')).toHaveLength(1)
})
it('requires a rejection reason and submits the review contract', async () => {
const wrapper = await mountPage()
vi.mocked(api.getOverseasFee).mockResolvedValueOnce({ ...fee, sourceVersion: 5 })
await click(wrapper, '审核', '.el-table__body-wrapper button')
const dialog = activeDialog(wrapper)
const reject = dialog.findAll('button').find(button => button.text().trim() === '驳回')
expect(reject?.attributes('disabled')).toBeDefined()
expect(api.actOnOverseasFee).not.toHaveBeenCalled()
await dialog.get('textarea').setValue(' 金额不符 ')
await click(dialog, '驳回')
expect(api.actOnOverseasFee).toHaveBeenCalledWith(fee.sourceFeeId, 'REJECT', { decision: 'REJECT', reason: '金额不符', expectedVersion: 5, idempotencyKey: expect.any(String) })
})
it('keeps rejected fees read-only while allowing failed frozen funds to be released', async () => {
vi.mocked(api.listBusinessOverseasFees).mockResolvedValue({ records: [{ ...fee, reviewStatus: 'REJECTED', allowedActions: ['SUBMIT', 'RETRY_RELEASE', 'CANCEL'] }], total: 1, page: 1, size: 20, pages: 1 })
vi.mocked(api.getOverseasFee).mockResolvedValue({ ...fee, reviewStatus: 'REJECTED', allowedActions: ['SUBMIT', 'RETRY_RELEASE', 'CANCEL'] })
const wrapper = await mountPage()
const visibleButtons = wrapper.findAll('.el-table__body-wrapper button').map(button => button.text().trim())
expect(visibleButtons).toContain('详情')
expect(visibleButtons).not.toContain('审核')
expect(visibleButtons).not.toContain('驳回')
expect(visibleButtons).not.toContain('提交审核')
expect(visibleButtons).toContain('重试释放')
await click(wrapper, '重试释放', '.el-table__body-wrapper button')
await click(activeDialog(wrapper), '重试释放')
expect(api.actOnOverseasFee).toHaveBeenCalledWith(fee.sourceFeeId, 'RETRY_RELEASE', expect.objectContaining({ idempotencyKey: expect.any(String) }))
await click(wrapper, '详情', '.el-table__body-wrapper button')
const dialog = activeDialog(wrapper)
expect(dialog.text()).toContain('审核状态')
expect(dialog.findAll('button').some(button => button.text().trim() === '审核')).toBe(false)
})
it('voids with the freshly loaded version and a required reason', async () => {
vi.mocked(api.listBusinessOverseasFees).mockResolvedValue({ records: [{ ...fee, allowedActions: ['VOID'] }], total: 1, page: 1, size: 20, pages: 1 })
vi.mocked(api.getOverseasFee).mockResolvedValue({ ...fee, sourceVersion: 5, allowedActions: ['VOID'] })
const wrapper = await mountPage()
await click(wrapper, '作废', '.el-table__body-wrapper button')
const dialog = activeDialog(wrapper)
await click(dialog, '作废')
expect(api.actOnOverseasFee).not.toHaveBeenCalled()
await dialog.get('textarea').setValue(' 重复费用 ')
await click(dialog, '作废')
expect(api.actOnOverseasFee).toHaveBeenCalledWith(fee.sourceFeeId, 'VOID', { expectedVersion: 5, reason: '重复费用', idempotencyKey: expect.any(String) })
})
it('locks in-flight approvals and preserves diagnostics and idempotency when retried', async () => {
const pending = deferred<OverseasFee>()
vi.mocked(api.actOnOverseasFee).mockReturnValueOnce(pending.promise)
const wrapper = await mountPage()
await click(wrapper, '审核', '.el-table__body-wrapper button')
const dialog = activeDialog(wrapper)
await click(dialog, '审核通过')
expect(wrapper.findAllComponents(ElDialog).find(component => component.props('modelValue'))!.props()).toMatchObject({ showClose: false, closeOnPressEscape: false })
expect(dialog.get('textarea').attributes('disabled')).toBeDefined()
pending.reject(new ApiResponseError('余额服务暂不可用', 'FUNDING_ERROR', 409, null, 'test-request-1'))
await flushPromises()
expect(wrapper.findAllComponents(AppDialog).some(component => component.props('modelValue'))).toBe(true)
expect(dialog.text()).toContain('余额服务暂不可用')
expect((dialog.get('textarea').element as HTMLTextAreaElement).value).toBe('')
const firstKey = vi.mocked(api.actOnOverseasFee).mock.calls[0]![2].idempotencyKey
await click(dialog, '审核通过')
expect(vi.mocked(api.actOnOverseasFee).mock.calls[1]![2].idempotencyKey).toBe(firstKey)
})
})
describe('仓租费用审核与调价', () => {
const rent: OverseasFee = { ...fee, feeItemCode: 'FEE_TYPE_077', feeItemName: '仓储费', allowedActions: ['APPROVE', 'REJECT', 'ADJUST_RENT'],
storageRent: { outboundNo: 'OUT-1', outboundAt: '2026-10-22T10:00:00Z', calculatedAmount: 10, chargedAmount: null,
lines: [
{ lotId: '1', inboundAt: '2026-10-01T10:00:00Z', quantity: 2, days: 21, calculatedAmount: 5, sourceBusinessType: 'LASTLEG_ORDER', sourceBusinessNo: 'WC-SOURCE-1', batchNo: 'B-SOURCE-1' },
{ lotId: '2', inboundAt: '2026-10-01T10:00:00Z', quantity: 2, days: 21, calculatedAmount: 5, sourceBusinessType: 'RETURN_ORDER', sourceBusinessNo: 'TH-SOURCE-2', batchNo: 'B-SOURCE-2' },
] } }
async function setupRent() {
vi.mocked(api.listBusinessOverseasFees).mockResolvedValue({ records: [rent], total: 1, page: 1, size: 20, pages: 1 })
vi.mocked(api.getOverseasFee).mockResolvedValue(rent)
return mountPage()
}
it('审核显示实际出入库与原计算额,批准以前不请求扣款', async () => {
const wrapper = await setupRent()
await click(wrapper, '审核', '.el-table__body-wrapper button')
const dialog = activeDialog(wrapper)
expect(dialog.text()).toContain('入库时间'); expect(dialog.text()).toContain('出库时间')
expect(dialog.text()).toContain('原计算金额'); expect(dialog.text()).toContain('实扣金额')
expect(dialog.text()).toContain('来源单号'); expect(dialog.text()).toContain('头程尾程订单')
expect(dialog.text()).toContain('WC-SOURCE-1'); expect(dialog.text()).toContain('TH-SOURCE-2')
expect(dialog.text()).toContain('退货单'); expect(dialog.text()).toContain('B-SOURCE-2')
expect(dialog.text()).toContain('2026-10-01'); expect(api.actOnOverseasFee).not.toHaveBeenCalled()
await click(dialog, '审核通过')
expect(api.actOnOverseasFee).toHaveBeenCalledWith(rent.sourceFeeId, 'APPROVE', expect.objectContaining({ decision: 'APPROVE' }))
})
it('调价说明必填,失败保留金额说明且同参数重试复用请求编号', async () => {
const wrapper = await setupRent()
vi.mocked(adjustStorageRentFee).mockRejectedValueOnce(new Error('暂时不可用')).mockResolvedValue(rent)
await click(wrapper, '调整仓租', '.el-table__body-wrapper button')
const dialog = activeDialog(wrapper)
expect(dialog.getComponent({ name: 'ElInputNumber' }).props('modelValue')).toBe(10)
expect(dialog.findAll('button').find(button => button.text() === '保存调整')!.attributes('disabled')).toBeDefined()
dialog.getComponent({ name: 'ElInputNumber' }).vm.$emit('update:modelValue', 12)
dialog.findAllComponents({ name: 'ElInput' }).find(input => input.props('type') === 'textarea')!.vm.$emit('update:modelValue', '人工核对调整')
await flushPromises(); await click(dialog, '保存调整')
expect(dialog.text()).toContain('仓租操作失败')
await click(dialog, '保存调整')
const calls = vi.mocked(adjustStorageRentFee).mock.calls
expect(calls).toHaveLength(2); expect(calls[0]).toEqual(calls[1])
expect(calls[0]).toEqual([rent.sourceFeeId, expect.objectContaining({ amount: 12, reason: '人工核对调整', expectedVersion: 3 })])
})
it('只读单据在仓租弹框已打开时同样禁止修改', async () => {
const wrapper = await setupRent(); await click(wrapper, '调整仓租', '.el-table__body-wrapper button')
await wrapper.setProps({ readonly: true }); await flushPromises()
expect(activeDialog(wrapper).findAll('button').some(button => button.text() === '保存调整')).toBe(false)
expect(adjustStorageRentFee).not.toHaveBeenCalled()
})
})