feat: 增加差旅报销标准测算和财务终审流程
新增差旅报销测算接口及 Spreadsheet 规则解析,审批流程拆分 直属领导审批与财务终审两阶段并细分权限,修复 PDF 文本层 缺失时自动回退 OCR,提交后清理关联会话,前端适配审批流 交互并补充单元测试。
This commit is contained in:
@@ -17,7 +17,12 @@ import {
|
||||
uploadExpenseClaimItemAttachment,
|
||||
updateExpenseClaimItem
|
||||
} from '../../services/reimbursements.js'
|
||||
import { canManageExpenseClaims, canReturnExpenseClaims } from '../../utils/accessControl.js'
|
||||
import {
|
||||
canApproveLeaderExpenseClaims,
|
||||
canManageExpenseClaims,
|
||||
canReturnExpenseClaims,
|
||||
isFinanceUser
|
||||
} from '../../utils/accessControl.js'
|
||||
import { normalizeRequestForUi } from '../../utils/requestViewModel.js'
|
||||
import {
|
||||
buildAiAdviceViewModel,
|
||||
@@ -82,7 +87,7 @@ function resolveLocationDisplay(value, expenseType) {
|
||||
|
||||
function buildFallbackProgressSteps() {
|
||||
return [
|
||||
{ index: 1, label: '保存草稿', time: '已完成', done: true, active: true },
|
||||
{ index: 1, label: '创建单据', time: '已完成', done: true, active: true },
|
||||
{ index: 2, label: '待提交', time: '进行中', active: true, current: true },
|
||||
{ index: 3, label: 'AI预审', time: '待处理' },
|
||||
{ index: 4, label: '直属领导审批', time: '待处理' },
|
||||
@@ -486,20 +491,51 @@ export default {
|
||||
const node = String(request.value.node || request.value.approvalStage || '').trim()
|
||||
return node === '直属领导审批'
|
||||
})
|
||||
const showLeaderApprovalPanel = computed(() =>
|
||||
Boolean(props.approvalMode)
|
||||
&& request.value.approvalKey === 'in_progress'
|
||||
&& isDirectManagerApprovalStage.value
|
||||
&& Boolean(request.value.claimId)
|
||||
)
|
||||
const isFinanceApprovalStage = computed(() => {
|
||||
const node = String(request.value.node || request.value.approvalStage || '').trim()
|
||||
return node === '财务审批'
|
||||
})
|
||||
const canReturnRequest = computed(() =>
|
||||
canReturnExpenseClaims(currentUser.value)
|
||||
&& request.value.approvalKey === 'in_progress'
|
||||
&& Boolean(request.value.claimId)
|
||||
)
|
||||
const canApproveRequest = computed(() =>
|
||||
showLeaderApprovalPanel.value
|
||||
&& canReturnExpenseClaims(currentUser.value)
|
||||
Boolean(props.approvalMode)
|
||||
&& request.value.approvalKey === 'in_progress'
|
||||
&& Boolean(request.value.claimId)
|
||||
&& (
|
||||
(
|
||||
isDirectManagerApprovalStage.value
|
||||
&& canApproveLeaderExpenseClaims(currentUser.value)
|
||||
)
|
||||
|| (
|
||||
isFinanceApprovalStage.value
|
||||
&& isFinanceUser(currentUser.value)
|
||||
)
|
||||
)
|
||||
)
|
||||
const showLeaderApprovalPanel = computed(() => canApproveRequest.value)
|
||||
const approvalOpinionTitle = computed(() => (isFinanceApprovalStage.value ? '财务意见' : '领导意见'))
|
||||
const approvalOpinionPlaceholder = computed(() =>
|
||||
isFinanceApprovalStage.value
|
||||
? '请输入财务终审意见,可补充票据核验、金额一致性或入账关注点。'
|
||||
: '请输入审批意见,可补充核实情况、费用合理性或后续财务关注点。'
|
||||
)
|
||||
const approvalOpinionHint = computed(() =>
|
||||
isFinanceApprovalStage.value ? '审核通过后将进入归档入账。' : '审批通过后将流转至财务审批。'
|
||||
)
|
||||
const approvalConfirmBadge = computed(() => (isFinanceApprovalStage.value ? '财务终审' : '领导审批'))
|
||||
const approvalConfirmDescription = computed(() =>
|
||||
isFinanceApprovalStage.value
|
||||
? '确认后该报销单会完成财务终审并进入归档入账,请确认票据、金额与财务意见无误。'
|
||||
: '确认后该报销单会从直属领导审批流转到财务审批,请确认申请信息与领导意见无误。'
|
||||
)
|
||||
const approvalNextStage = computed(() => (isFinanceApprovalStage.value ? '归档入账' : '财务审批'))
|
||||
const approvalSuccessToast = computed(() =>
|
||||
isFinanceApprovalStage.value
|
||||
? `${request.value.id} 已完成财务终审,进入归档入账。`
|
||||
: `${request.value.id} 已审批通过,流转至财务审批。`
|
||||
)
|
||||
const deleteActionLabel = computed(() => (isDraftRequest.value ? '删除草稿' : '删除单据'))
|
||||
const deleteDialogTitle = computed(() => `确认${deleteActionLabel.value} ${request.value.id} 吗?`)
|
||||
@@ -564,7 +600,7 @@ export default {
|
||||
},
|
||||
{
|
||||
key: 'date',
|
||||
label: '日期',
|
||||
label: '单据申请日期',
|
||||
value: request.value.applyTime || request.value.occurredDisplay,
|
||||
icon: 'mdi mdi-calendar-month-outline',
|
||||
valueClass: ''
|
||||
@@ -1011,12 +1047,23 @@ export default {
|
||||
try {
|
||||
const payload = await uploadExpenseClaimItemAttachment(request.value.claimId, item.id, file)
|
||||
expenseAttachmentMeta[item.id] = payload?.attachment || null
|
||||
applyLocalExpenseItemPatch(item.id, {
|
||||
const recognizedItemAmount = Number(payload?.item_amount ?? payload?.itemAmount)
|
||||
const itemPatch = {
|
||||
invoiceId: String(payload?.invoice_id || '').trim(),
|
||||
attachmentHint: String(payload?.attachment?.file_name || file.name || '').trim()
|
||||
}
|
||||
if (Number.isFinite(recognizedItemAmount) && recognizedItemAmount > 0) {
|
||||
itemPatch.itemAmount = recognizedItemAmount
|
||||
itemPatch.amount = formatCurrency(recognizedItemAmount)
|
||||
}
|
||||
applyLocalExpenseItemPatch(item.id, {
|
||||
...itemPatch
|
||||
})
|
||||
if (editingExpenseId.value === item.id) {
|
||||
expenseEditor.invoiceId = String(payload?.invoice_id || '').trim()
|
||||
if (Number.isFinite(recognizedItemAmount) && recognizedItemAmount > 0) {
|
||||
expenseEditor.itemAmount = String(recognizedItemAmount)
|
||||
}
|
||||
}
|
||||
|
||||
emit('request-updated', { claimId: request.value.claimId })
|
||||
@@ -1322,7 +1369,7 @@ export default {
|
||||
}
|
||||
|
||||
if (!canApproveRequest.value) {
|
||||
toast('当前节点不支持领导审批通过。')
|
||||
toast('当前节点暂不支持审批通过。')
|
||||
return
|
||||
}
|
||||
|
||||
@@ -1345,7 +1392,7 @@ export default {
|
||||
}
|
||||
|
||||
if (!canApproveRequest.value) {
|
||||
toast('当前节点不支持领导审批通过。')
|
||||
toast('当前节点暂不支持审批通过。')
|
||||
approveConfirmDialogOpen.value = false
|
||||
return
|
||||
}
|
||||
@@ -1357,7 +1404,7 @@ export default {
|
||||
})
|
||||
approveConfirmDialogOpen.value = false
|
||||
leaderOpinion.value = ''
|
||||
toast(`${request.value.id} 已审批通过,流转至财务审批。`)
|
||||
toast(approvalSuccessToast.value)
|
||||
emit('request-updated', { claimId: request.value.claimId })
|
||||
} catch (error) {
|
||||
toast(error?.message || '审批通过失败,请稍后重试。')
|
||||
@@ -1396,6 +1443,12 @@ export default {
|
||||
attachmentPreviewUrl,
|
||||
approveBusy,
|
||||
approveConfirmDialogOpen,
|
||||
approvalConfirmBadge,
|
||||
approvalConfirmDescription,
|
||||
approvalNextStage,
|
||||
approvalOpinionHint,
|
||||
approvalOpinionPlaceholder,
|
||||
approvalOpinionTitle,
|
||||
canDeleteRequest,
|
||||
canManageCurrentClaim,
|
||||
canNavigateAttachmentPreview,
|
||||
|
||||
Reference in New Issue
Block a user