feat: 完善审批退回流程与报销申请关联
后端优化报销单访问策略和常量定义,增强退回原因和审批状态 流转,前端完善退回对话框和审批交互组件,新增报销申请关联 模型,优化文档中心行数据和审批收件箱工具函数,增强引导 流程和会话模型,补充单元测试覆盖。
This commit is contained in:
@@ -7,6 +7,7 @@ export const GUIDED_ACTION_START_APPLICATION = 'start_guided_application'
|
||||
export const GUIDED_ACTION_START_STATUS_QUERY = 'start_guided_status_query'
|
||||
export const GUIDED_ACTION_OPEN_TRAVEL_CALCULATOR = 'open_travel_calculator'
|
||||
export const GUIDED_ACTION_SELECT_EXPENSE_TYPE = 'guided_select_expense_type'
|
||||
export const GUIDED_ACTION_SELECT_REQUIRED_APPLICATION = 'guided_select_required_application'
|
||||
export const GUIDED_ACTION_CONFIRM_REIMBURSEMENT_REVIEW = 'guided_confirm_reimbursement_review'
|
||||
export const GUIDED_ACTION_CONTINUE_FILLING = 'guided_continue_filling'
|
||||
export const GUIDED_ACTION_PROCESS_INTERRUPTION = 'guided_process_interruption'
|
||||
@@ -109,13 +110,36 @@ function normalizeValues(values) {
|
||||
}, {})
|
||||
}
|
||||
|
||||
function normalizeApplicationCandidates(applications) {
|
||||
if (!Array.isArray(applications)) {
|
||||
return []
|
||||
}
|
||||
return applications
|
||||
.map((item) => (item && typeof item === 'object' ? item : null))
|
||||
.filter(Boolean)
|
||||
.map((item) => ({
|
||||
id: normalizeText(item.id || item.application_claim_id),
|
||||
claim_no: normalizeText(item.claim_no || item.application_claim_no),
|
||||
expense_type: normalizeText(item.expense_type || item.application_expense_type),
|
||||
reason: normalizeText(item.reason || item.application_reason),
|
||||
location: normalizeText(item.location || item.application_location),
|
||||
amount: normalizeText(item.amount || item.application_amount),
|
||||
amount_label: normalizeText(item.amount_label || item.application_amount_label),
|
||||
status: normalizeText(item.status || item.application_status),
|
||||
status_label: normalizeText(item.status_label || item.application_status_label),
|
||||
application_date: normalizeText(item.application_date)
|
||||
}))
|
||||
.filter((item) => item.id || item.claim_no)
|
||||
}
|
||||
|
||||
export function createEmptyGuidedFlowState() {
|
||||
return {
|
||||
mode: GUIDED_FLOW_MODE_NONE,
|
||||
stepKey: '',
|
||||
expenseType: '',
|
||||
values: {},
|
||||
pendingInterruptionText: ''
|
||||
pendingInterruptionText: '',
|
||||
applicationCandidates: []
|
||||
}
|
||||
}
|
||||
|
||||
@@ -134,7 +158,8 @@ export function normalizeGuidedFlowState(state) {
|
||||
stepKey: normalizeText(source.stepKey),
|
||||
expenseType: normalizeText(source.expenseType),
|
||||
values: normalizeValues(source.values),
|
||||
pendingInterruptionText: normalizeText(source.pendingInterruptionText)
|
||||
pendingInterruptionText: normalizeText(source.pendingInterruptionText),
|
||||
applicationCandidates: normalizeApplicationCandidates(source.applicationCandidates)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -191,7 +216,44 @@ export function selectGuidedExpenseType(state, expenseType) {
|
||||
mode: GUIDED_FLOW_MODE_REIMBURSEMENT,
|
||||
expenseType: type.key,
|
||||
stepKey: steps[0]?.key || 'summary',
|
||||
pendingInterruptionText: ''
|
||||
pendingInterruptionText: '',
|
||||
applicationCandidates: []
|
||||
}
|
||||
}
|
||||
|
||||
export function waitForGuidedApplicationSelection(state, expenseType, applications = []) {
|
||||
const type = getGuidedExpenseType(expenseType)
|
||||
if (!type) {
|
||||
return normalizeGuidedFlowState(state)
|
||||
}
|
||||
return {
|
||||
...normalizeGuidedFlowState(state),
|
||||
mode: GUIDED_FLOW_MODE_REIMBURSEMENT,
|
||||
expenseType: type.key,
|
||||
stepKey: 'application_selection',
|
||||
pendingInterruptionText: '',
|
||||
applicationCandidates: normalizeApplicationCandidates(applications)
|
||||
}
|
||||
}
|
||||
|
||||
export function selectGuidedRequiredApplication(state, application = {}) {
|
||||
const current = normalizeGuidedFlowState(state)
|
||||
const steps = getGuidedReimbursementSteps(current.expenseType)
|
||||
return {
|
||||
...current,
|
||||
values: normalizeValues({
|
||||
...current.values,
|
||||
application_claim_id: application.application_claim_id || application.id || '',
|
||||
application_claim_no: application.application_claim_no || application.claim_no || '',
|
||||
application_reason: application.application_reason || application.reason || '',
|
||||
application_location: application.application_location || application.location || '',
|
||||
application_amount: application.application_amount || application.amount || '',
|
||||
application_amount_label: application.application_amount_label || application.amount_label || '',
|
||||
application_status_label: application.application_status_label || application.status_label || ''
|
||||
}),
|
||||
stepKey: steps[0]?.key || 'summary',
|
||||
pendingInterruptionText: '',
|
||||
applicationCandidates: []
|
||||
}
|
||||
}
|
||||
|
||||
@@ -290,6 +352,16 @@ export function buildGuidedReimbursementSummaryText(state) {
|
||||
'请核查下面的关键信息:'
|
||||
]
|
||||
|
||||
if (current.values.application_claim_no) {
|
||||
const applicationParts = [
|
||||
current.values.application_claim_no,
|
||||
current.values.application_reason,
|
||||
current.values.application_location,
|
||||
current.values.application_amount_label
|
||||
].filter(Boolean)
|
||||
lines.push(`- 关联申请单:${applicationParts.join(' / ')}`)
|
||||
}
|
||||
|
||||
steps.forEach((step) => {
|
||||
const value = step.key === 'attachments'
|
||||
? (current.values.attachment_names?.length
|
||||
@@ -324,6 +396,9 @@ export function buildGuidedReviewSubmitOptions(state, files = []) {
|
||||
: values[step.key]
|
||||
return `${step.summaryLabel}:${value || '待补充'}`
|
||||
})
|
||||
if (values.application_claim_no) {
|
||||
fieldLines.unshift(`关联申请单:${values.application_claim_no}`)
|
||||
}
|
||||
const rawText = [
|
||||
`报销类型:${typeLabel}`,
|
||||
...fieldLines
|
||||
@@ -340,7 +415,12 @@ export function buildGuidedReviewSubmitOptions(state, files = []) {
|
||||
time_range: values.time_range || '',
|
||||
business_time: values.time_range || '',
|
||||
amount: values.amount || '',
|
||||
attachment_names: Array.isArray(values.attachment_names) ? values.attachment_names : []
|
||||
attachment_names: Array.isArray(values.attachment_names) ? values.attachment_names : [],
|
||||
application_claim_id: values.application_claim_id || '',
|
||||
application_claim_no: values.application_claim_no || '',
|
||||
application_reason: values.application_reason || '',
|
||||
application_location: values.application_location || '',
|
||||
application_amount: values.application_amount || ''
|
||||
}
|
||||
|
||||
return {
|
||||
@@ -355,7 +435,9 @@ export function buildGuidedReviewSubmitOptions(state, files = []) {
|
||||
expense_scene_selection: {
|
||||
expense_type: type?.key || current.expenseType || 'other',
|
||||
expense_type_label: typeLabel,
|
||||
original_message: rawText
|
||||
original_message: rawText,
|
||||
application_claim_id: values.application_claim_id || '',
|
||||
application_claim_no: values.application_claim_no || ''
|
||||
},
|
||||
review_form_values: reviewFormValues
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user