优化
This commit is contained in:
@@ -55,7 +55,7 @@ describe('MeasurementReviewPanel', () => {
|
||||
expect(wrapper.text()).toContain('调整原因:去除托盘重量后采用净值')
|
||||
expect(wrapper.findComponent(DataTable).exists()).toBe(true)
|
||||
expect(wrapper.text()).not.toContain('租户')
|
||||
expect(wrapper.text()).toContain('通过并完成入库')
|
||||
expect(wrapper.text()).toContain('通过并进入待分柜')
|
||||
expect(wrapper.text()).not.toContain('确认费用')
|
||||
expect(getMeasurementReviewDetail).toHaveBeenCalledWith('10001', '90001')
|
||||
const source = await import('./MeasurementReviewPanel.vue?raw').then((module) => module.default)
|
||||
@@ -77,25 +77,37 @@ describe('MeasurementReviewPanel', () => {
|
||||
})
|
||||
|
||||
it('emits changed after a successful review write', async () => {
|
||||
vi.mocked(approveMeasurement).mockResolvedValue({ feeId: '1', feeNo: 'FEE-1', feeVersion: 1, feeStatus: 'SENT', inboundId: '90001', totalAmount: 100, currency: 'CNY', nextProcessStatus: 'WAIT_SPLIT' })
|
||||
vi.mocked(approveMeasurement).mockResolvedValue({ inboundId: '90001', measurementId: '70001', processStatus: 'WAIT_SPLIT', feeStatus: 'CALCULATED', initialPricingSucceeded: true, initialPricingMessage: null, initialFee: null })
|
||||
vi.spyOn(ElMessageBox, 'confirm').mockResolvedValue(undefined as never)
|
||||
const wrapper = mount(MeasurementReviewPanel, { props: { customerId: '10001', inboundId: '90001' }, global: { plugins: [ElementPlus] } })
|
||||
await flushPromises()
|
||||
const buttons = wrapper.findAll('button')
|
||||
const approve = buttons.find((button) => button.text().includes('通过并完成入库'))
|
||||
const approve = buttons.find((button) => button.text().includes('通过并进入待分柜'))
|
||||
expect(approve).toBeDefined()
|
||||
await approve?.trigger('click')
|
||||
await flushPromises()
|
||||
|
||||
expect(ElMessageBox.confirm).toHaveBeenCalledWith(
|
||||
expect.stringContaining('自动试算并发布费用'),
|
||||
expect.stringContaining('初算失败不影响审核通过'),
|
||||
'确认测量审核通过',
|
||||
expect.objectContaining({ confirmButtonText: '通过并完成入库' }),
|
||||
expect.objectContaining({ confirmButtonText: '通过并进入待分柜' }),
|
||||
)
|
||||
expect(approveMeasurement).toHaveBeenCalledWith('10001', '90001', { measurementId: '70001', expectedVersion: 7 })
|
||||
expect(wrapper.emitted('changed')).toHaveLength(1)
|
||||
})
|
||||
|
||||
it('keeps approval successful when initial pricing fails', async () => {
|
||||
vi.mocked(approveMeasurement).mockResolvedValue({ inboundId: '90001', measurementId: '70001', processStatus: 'WAIT_SPLIT', feeStatus: 'UNPRICED', initialPricingSucceeded: false, initialPricingMessage: '缺少有效价格', initialFee: null })
|
||||
vi.spyOn(ElMessageBox, 'confirm').mockResolvedValue(undefined as never)
|
||||
const wrapper = mount(MeasurementReviewPanel, { props: { customerId: '10001', inboundId: '90001' }, global: { plugins: [ElementPlus] } })
|
||||
await flushPromises()
|
||||
await wrapper.findAll('button').find((button) => button.text().includes('通过并进入待分柜'))?.trigger('click')
|
||||
await flushPromises()
|
||||
|
||||
expect(wrapper.emitted('changed')).toHaveLength(1)
|
||||
expect(wrapper.text()).toContain('初算失败不阻断审核')
|
||||
})
|
||||
|
||||
it('rejects with the inbound optimistic-lock version instead of the measurement version', async () => {
|
||||
vi.mocked(rejectMeasurement).mockResolvedValue(undefined)
|
||||
const wrapper = mount(MeasurementReviewPanel, { props: { customerId: '10001', inboundId: '90001' }, global: { plugins: [ElementPlus] } })
|
||||
|
||||
@@ -145,20 +145,24 @@ async function confirmApprove() {
|
||||
if (!detail.value || !isPending.value || !canReview.value) return
|
||||
try {
|
||||
await ElMessageBox.confirm(
|
||||
'审核通过后,系统会自动试算并发布费用,同时完成托盘库位占用和入库。客户无需再次确认费用。是否继续?',
|
||||
'审核通过后,入库单将进入待分柜。系统会尝试生成初算费用,但初算失败不影响审核通过,后续可由平台人员补充调整。是否继续?',
|
||||
'确认测量审核通过',
|
||||
{ confirmButtonText: '通过并完成入库', cancelButtonText: '返回', type: 'warning' },
|
||||
{ confirmButtonText: '通过并进入待分柜', cancelButtonText: '返回', type: 'warning' },
|
||||
)
|
||||
} catch {
|
||||
return
|
||||
}
|
||||
actionLoading.value = 'approve'
|
||||
try {
|
||||
await approveMeasurement(customerId.value, props.inboundId, {
|
||||
const result = await approveMeasurement(customerId.value, props.inboundId, {
|
||||
measurementId: detail.value.measurementId,
|
||||
expectedVersion: detail.value.inboundVersion,
|
||||
})
|
||||
ElMessage.success('审核已通过,费用发布和入库正在完成')
|
||||
if (result.initialPricingSucceeded) {
|
||||
ElMessage.success('审核已通过,入库单已进入待分柜,费用已完成初算')
|
||||
} else {
|
||||
ElMessage.warning(`审核已通过,入库单已进入待分柜;费用初算未完成${result.initialPricingMessage ? `:${result.initialPricingMessage}` : ''}`)
|
||||
}
|
||||
await reload()
|
||||
emit('changed')
|
||||
} catch (error) {
|
||||
@@ -295,10 +299,10 @@ defineExpose({ confirmApprove, openReasonDialog, isPending, isRejected, canRevie
|
||||
</div>
|
||||
|
||||
<div class="review-actions" :class="{ 'review-actions--disabled': !isPending && !isRejected }">
|
||||
<div><strong>审核结论</strong><span v-if="isPending">通过后将自动完成计费、费用发布和入库。</span><span v-else-if="isRejected">当前测量已驳回,可修正后重新提交,或按权限申请异常放行。</span><span v-else>当前记录不在可操作的测量审核状态。</span></div>
|
||||
<div><strong>审核结论</strong><span v-if="isPending">通过后进入待分柜;费用仅做初算,初算失败不阻断审核。</span><span v-else-if="isRejected">当前测量已驳回,可修正后重新提交,或按权限申请异常放行。</span><span v-else>当前记录不在可操作的测量审核状态。</span></div>
|
||||
<div class="action-buttons">
|
||||
<el-button v-if="isPending && canReview" type="danger" plain :loading="actionLoading === 'reject'" @click="openReasonDialog('reject')"><X :size="16" />驳回</el-button>
|
||||
<el-button v-if="isPending && canReview" type="primary" :loading="actionLoading === 'approve'" @click="confirmApprove"><Check :size="16" />通过并完成入库</el-button>
|
||||
<el-button v-if="isPending && canReview" type="primary" :loading="actionLoading === 'approve'" @click="confirmApprove"><Check :size="16" />通过并进入待分柜</el-button>
|
||||
<el-button v-if="isRejected && canRelease" type="warning" plain :loading="actionLoading === 'release'" @click="openReasonDialog('release')"><ShieldAlert :size="16" />异常放行</el-button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import { http, unwrapApiResponse, type ApiEnvelope } from '@/shared/api/http'
|
||||
import type {
|
||||
ExceptionReleaseRequest,
|
||||
FeeSnapshotAfterApprove,
|
||||
MeasurementApprovalResponse,
|
||||
MeasurementReviewDetail,
|
||||
MeasurementReviewHistoryPage,
|
||||
MeasurementReviewHistoryPayload,
|
||||
@@ -63,7 +63,7 @@ export function approveMeasurement(
|
||||
request: MeasurementReviewRequest,
|
||||
key?: string,
|
||||
) {
|
||||
return postReview<FeeSnapshotAfterApprove>(
|
||||
return postReview<MeasurementApprovalResponse>(
|
||||
`/platform/v1/inbounds/${inboundId}/measurements/approve`,
|
||||
tenantId,
|
||||
request,
|
||||
|
||||
@@ -116,6 +116,16 @@ export interface ExceptionReleaseRequest {
|
||||
reason: string
|
||||
}
|
||||
|
||||
export interface MeasurementApprovalResponse {
|
||||
inboundId: string | null
|
||||
measurementId: string | null
|
||||
processStatus: 'WAIT_SPLIT' | string
|
||||
feeStatus: 'CALCULATED' | 'UNPRICED' | string
|
||||
initialPricingSucceeded: boolean
|
||||
initialPricingMessage: string | null
|
||||
initialFee: FeeSnapshotAfterApprove | null
|
||||
}
|
||||
|
||||
export interface FeeSnapshotAfterApprove {
|
||||
feeId: string | null
|
||||
feeNo: string | null
|
||||
|
||||
Reference in New Issue
Block a user