优化
This commit is contained in:
@@ -18,7 +18,7 @@ vi.mock('@/core/unsaved-work/useUnsavedWorkRegistration', () => ({ useUnsavedWor
|
||||
enableAutoUnmount(afterEach)
|
||||
const workflow = (status = 'RECEIVING'): ProcurementWorkflow => ({ orderNo: 'OP-1', version: 4, status, receivedQuantity: 5, stockQuantity: 5, reservedQuantity: 0, availableQuantity: 5, receipts: [], outbounds: [] })
|
||||
const detail = { customerView: { order: { purchaseQuantity: 20 } }, files: [] } as unknown as PlatformProcurement
|
||||
const beforeOutbound = vi.fn<() => Promise<void>>()
|
||||
const beforeOutbound = vi.fn<() => Promise<boolean | null>>()
|
||||
function render(status = 'RECEIVING', permissions = ['receive-overseas', 'ship-customer']) {
|
||||
const pinia = createPinia(); setActivePinia(pinia)
|
||||
const auth = useAuthStore(); auth.token = 'test'; auth.permissions = permissions.map(value => `platform:overseas-business:purchase:${value}`); useAccessPreviewStore().initializeAfterLogin()
|
||||
@@ -26,18 +26,15 @@ function render(status = 'RECEIVING', permissions = ['receive-overseas', 'ship-c
|
||||
}
|
||||
beforeEach(() => { vi.clearAllMocks(); beforeOutbound.mockResolvedValue(undefined); vi.mocked(submitProcurementWorkflow).mockResolvedValue(workflow()); vi.mocked(listOverseasPurchaseWarehouseOptions).mockResolvedValue([]) })
|
||||
describe('simplified procurement operations', () => {
|
||||
it('blocks the outbound dialog when fees are missing or not approved and permits retry after approval', async () => {
|
||||
it('allows outbound when the fee check reports missing or unreviewed details', async () => {
|
||||
const wrapper = render('CONFIRMED')
|
||||
const button = wrapper.findAll('button').find(button => button.text() === '创建出库单')!
|
||||
beforeOutbound.mockRejectedValueOnce(new Error('请先添加费用明细并审核通过后再出库'))
|
||||
await button.trigger('click'); await flushPromises()
|
||||
expect(wrapper.findComponent(AppDialog).props('modelValue')).toBe(false)
|
||||
expect(submitProcurementWorkflow).not.toHaveBeenCalled()
|
||||
beforeOutbound.mockRejectedValueOnce(new Error('费用明细尚未全部审核通过'))
|
||||
await button.trigger('click'); await flushPromises()
|
||||
expect(wrapper.findComponent(AppDialog).props('modelValue')).toBe(false)
|
||||
beforeOutbound.mockResolvedValueOnce(true)
|
||||
await button.trigger('click'); await flushPromises()
|
||||
expect(wrapper.findComponent(AppDialog).props('modelValue')).toBe(true)
|
||||
await button.trigger('click'); await flushPromises()
|
||||
expect(wrapper.findComponent(AppDialog).props('modelValue')).toBe(true)
|
||||
expect(beforeOutbound).toHaveBeenCalledTimes(2)
|
||||
})
|
||||
it('gates receipt and outbound actions with the existing operation permissions', () => {
|
||||
const receipt = render('RECEIVING', [])
|
||||
|
||||
@@ -14,7 +14,7 @@ import { dateTime, statusLabel } from '../procurementPresentation'
|
||||
import type { PlatformProcurement } from '../procurementTypes'
|
||||
import type { OverseasPurchaseWarehouseOption } from '../types'
|
||||
|
||||
const props = defineProps<{ detail: PlatformProcurement; workflow: ProcurementWorkflow; customerId: string; downloading?: string; beforeOutbound: () => Promise<void> }>()
|
||||
const props = defineProps<{ detail: PlatformProcurement; workflow: ProcurementWorkflow; customerId: string; downloading?: string; beforeOutbound: () => Promise<boolean | null> }>()
|
||||
const emit = defineEmits<{ changed: []; download: [id: string] }>()
|
||||
const access = useAccessPreviewStore()
|
||||
const open = ref(false), saving = ref(false), uploading = ref(false), checkingFees = ref(false), error = ref<ApiErrorDetails | null>(null)
|
||||
@@ -40,10 +40,12 @@ async function begin(name: string, batch?: ProcurementOutbound) {
|
||||
const current = ++dialogGeneration
|
||||
if (name === 'outbounds') {
|
||||
checkingFees.value = true
|
||||
try { await props.beforeOutbound() }
|
||||
catch (failure) { if (current === dialogGeneration) ElMessage.warning(getApiErrorDetails(failure, '费用校验失败').message); return }
|
||||
let noFeeDetails = false
|
||||
try { noFeeDetails = await props.beforeOutbound() === true }
|
||||
catch { /* 费用查询仅用于提示,不能阻断采购出库。 */ }
|
||||
finally { checkingFees.value = false }
|
||||
if (current !== dialogGeneration) return
|
||||
if (noFeeDetails) ElMessage.info('没有新增费用明细,仍可继续创建出库单')
|
||||
}
|
||||
action.value = name; batchId.value = batch?.id || ''; error.value = null; warehouseLoading.value = false
|
||||
Object.assign(form, { quantity: 1, trackingNo: '', fileIds: [], warehouseId: '', decision: 'APPROVE', reason: '' }); uploadedNames.value = []
|
||||
|
||||
@@ -22,7 +22,7 @@ import { overseasBusinessPermissions } from '../permissions'
|
||||
import { statusLabel, money, dateTime, text } from '../procurementPresentation'
|
||||
import type { PlatformProcurement } from '../procurementTypes'
|
||||
import { directionLabel } from '../procurementDirection'
|
||||
import { requireReviewedProcurementFees } from '../procurementOutboundFees'
|
||||
import { hasProcurementFeeDetails } from '../procurementOutboundFees'
|
||||
const route = useRoute(); const router = useRouter(); const auth = useAuthStore(); const access = useAccessPreviewStore()
|
||||
const purchaseId = computed(() => typeof route.params.purchaseId === 'string' ? route.params.purchaseId : '')
|
||||
const customerId = computed(() => typeof route.query.customerId === 'string' ? route.query.customerId : '')
|
||||
@@ -30,9 +30,10 @@ const detail = ref<PlatformProcurement | null>(null), workflow = ref<Procurement
|
||||
const loadError = ref<ApiErrorDetails | null>(null), operationError = ref<ApiErrorDetails | null>(null), downloading = ref('')
|
||||
const order = computed(() => detail.value?.customerView.order)
|
||||
const feePanel = ref<InstanceType<typeof BusinessDocumentFees>>()
|
||||
async function beforeOutbound() {
|
||||
if (!feePanel.value) throw new Error('费用明细尚未加载完成,请稍后重试')
|
||||
requireReviewedProcurementFees(await feePanel.value.getFees())
|
||||
async function beforeOutbound(): Promise<boolean | null> {
|
||||
if (!feePanel.value) return null
|
||||
try { return !hasProcurementFeeDetails(await feePanel.value.getFees()) }
|
||||
catch { return null }
|
||||
}
|
||||
const fields = computed(() => [
|
||||
['客户', order.value?.customerName], ['采购方向', directionLabel(order.value?.procurementDirection)], ['收货仓', order.value?.actualWarehouseName || order.value?.plannedWarehouseName],
|
||||
|
||||
@@ -1,18 +1,11 @@
|
||||
import { describe, expect, it } from 'vitest'
|
||||
import { requireReviewedProcurementFees } from './procurementOutboundFees'
|
||||
import { hasProcurementFeeDetails } from './procurementOutboundFees'
|
||||
|
||||
describe('procurement outbound fee gate', () => {
|
||||
const approved = { direction: 'RECEIVABLE' as const, sourceStatus: 'ACTIVE', reviewStatus: 'APPROVED' }
|
||||
it('requires a current receivable, excluding voided fees and payables', () => {
|
||||
for (const rows of [[], [{ ...approved, sourceStatus: 'VOIDED' }], [{ ...approved, direction: 'PAYABLE' as const }]]) {
|
||||
expect(() => requireReviewedProcurementFees(rows)).toThrow('请先添加费用明细')
|
||||
}
|
||||
})
|
||||
it('requires every current fee to be active and approved', () => {
|
||||
for (const reviewStatus of ['PENDING', 'REJECTED']) {
|
||||
expect(() => requireReviewedProcurementFees([approved, { ...approved, reviewStatus }])).toThrow('尚未全部审核通过')
|
||||
}
|
||||
expect(() => requireReviewedProcurementFees([approved, { ...approved, sourceStatus: 'VOIDING' }])).toThrow('尚未全部审核通过')
|
||||
expect(() => requireReviewedProcurementFees([approved, { ...approved, sourceStatus: 'VOIDED', reviewStatus: 'REJECTED' }])).not.toThrow()
|
||||
describe('procurement outbound fee notice', () => {
|
||||
it('only reports when there is no current receivable fee detail', () => {
|
||||
expect(hasProcurementFeeDetails([])).toBe(false)
|
||||
expect(hasProcurementFeeDetails([{ direction: 'RECEIVABLE', sourceStatus: 'VOIDED' }])).toBe(false)
|
||||
expect(hasProcurementFeeDetails([{ direction: 'PAYABLE', sourceStatus: 'ACTIVE' }])).toBe(false)
|
||||
expect(hasProcurementFeeDetails([{ direction: 'RECEIVABLE', sourceStatus: 'ACTIVE' }])).toBe(true)
|
||||
})
|
||||
})
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
import type { OverseasFee } from './api/overseasFees'
|
||||
|
||||
export function requireReviewedProcurementFees(rows: Pick<OverseasFee, 'direction' | 'sourceStatus' | 'reviewStatus'>[]) {
|
||||
const fees = rows.filter(fee => fee.direction === 'RECEIVABLE' && fee.sourceStatus !== 'VOIDED')
|
||||
if (!fees.length) throw new Error('请先添加费用明细并审核通过后再出库')
|
||||
if (fees.some(fee => fee.sourceStatus !== 'ACTIVE' || fee.reviewStatus !== 'APPROVED')) throw new Error('费用明细尚未全部审核通过,请先完成费用审核后再出库')
|
||||
/** 缺少未作废应收时仅供页面提示,不作为创建出库单的门禁。 */
|
||||
export function hasProcurementFeeDetails(rows: Pick<OverseasFee, 'direction' | 'sourceStatus'>[]) {
|
||||
return rows.some(fee => fee.direction === 'RECEIVABLE' && fee.sourceStatus !== 'VOIDED')
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user