feat: 增加差旅报销标准测算和财务终审流程
新增差旅报销测算接口及 Spreadsheet 规则解析,审批流程拆分 直属领导审批与财务终审两阶段并细分权限,修复 PDF 文本层 缺失时自动回退 OCR,提交后清理关联会话,前端适配审批流 交互并补充单元测试。
This commit is contained in:
@@ -19,8 +19,9 @@ const VIEW_ROLE_RULES = {
|
||||
employees: ['manager'],
|
||||
settings: ['manager']
|
||||
}
|
||||
const CLAIM_MANAGER_ROLE_CODES = new Set(['finance', 'executive'])
|
||||
const CLAIM_MANAGER_ROLE_CODES = new Set(['executive'])
|
||||
const CLAIM_RETURN_ROLE_CODES = new Set(['finance', 'executive', 'manager', 'approver'])
|
||||
const CLAIM_LEADER_APPROVAL_ROLE_CODES = new Set(['manager', 'approver'])
|
||||
|
||||
function normalizedRoleCodes(user) {
|
||||
if (!user) {
|
||||
@@ -60,6 +61,14 @@ export function canReturnExpenseClaims(user) {
|
||||
return normalizedRoleCodes(user).some((roleCode) => CLAIM_RETURN_ROLE_CODES.has(roleCode))
|
||||
}
|
||||
|
||||
export function canApproveLeaderExpenseClaims(user) {
|
||||
if (Boolean(user?.isAdmin)) {
|
||||
return true
|
||||
}
|
||||
|
||||
return normalizedRoleCodes(user).some((roleCode) => CLAIM_LEADER_APPROVAL_ROLE_CODES.has(roleCode))
|
||||
}
|
||||
|
||||
export function canAccessAppView(user, viewId) {
|
||||
if (!viewId || !user) {
|
||||
return false
|
||||
|
||||
@@ -1,5 +1,9 @@
|
||||
import { mapExpenseClaimToRequest } from '../composables/useRequests.js'
|
||||
import { canManageExpenseClaims } from './accessControl.js'
|
||||
import {
|
||||
canApproveLeaderExpenseClaims,
|
||||
canManageExpenseClaims,
|
||||
isFinanceUser
|
||||
} from './accessControl.js'
|
||||
|
||||
export function canProcessApprovalRequest(request, currentUser) {
|
||||
const node = String(request?.workflowNode || '').trim()
|
||||
@@ -14,12 +18,18 @@ export function canProcessApprovalRequest(request, currentUser) {
|
||||
return true
|
||||
}
|
||||
|
||||
return (
|
||||
if (isFinanceUser(currentUser) && node.includes('财务')) {
|
||||
return true
|
||||
}
|
||||
|
||||
const isLeaderApprovalNode = (
|
||||
node.includes('直属领导')
|
||||
|| node.includes('领导审批')
|
||||
|| node.includes('部门负责人')
|
||||
|| node.includes('负责人审批')
|
||||
)
|
||||
|
||||
return canApproveLeaderExpenseClaims(currentUser) && isLeaderApprovalNode
|
||||
}
|
||||
|
||||
export function listPendingApprovalRequests(claimsPayload, currentUser) {
|
||||
|
||||
@@ -181,6 +181,21 @@ function normalizeRoleLabels(value) {
|
||||
return text ? [text] : []
|
||||
}
|
||||
|
||||
function isEmailLike(value) {
|
||||
return /^[^@\s]+@[^@\s]+\.[^@\s]+$/.test(String(value || '').trim())
|
||||
}
|
||||
|
||||
function resolveDisplayName(...values) {
|
||||
for (const value of values) {
|
||||
const normalized = String(value || '').trim()
|
||||
if (normalized && !isEmailLike(normalized)) {
|
||||
return normalized
|
||||
}
|
||||
}
|
||||
|
||||
return ''
|
||||
}
|
||||
|
||||
export function normalizeRequestForUi(request) {
|
||||
if (!request) {
|
||||
return null
|
||||
@@ -255,7 +270,12 @@ export function normalizeRequestForUi(request) {
|
||||
String(request.profilePosition || request.employeePosition || request.employee_position || request.position || '').trim()
|
||||
|| '待补充',
|
||||
profileGrade: String(request.profileGrade || request.employeeGrade || request.employee_grade || request.grade || '').trim() || '待补充',
|
||||
profileManager: String(request.profileManager || request.managerName || request.manager_name || request.manager || '').trim() || '待补充',
|
||||
profileManager: resolveDisplayName(
|
||||
request.profileManager,
|
||||
request.managerName,
|
||||
request.manager_name,
|
||||
request.manager
|
||||
) || '待补充',
|
||||
roleLabels,
|
||||
profileAvatar:
|
||||
String(request.person || request.applicant || request.employeeName || '申').trim().slice(0, 1) || '申'
|
||||
|
||||
Reference in New Issue
Block a user