fix: handle risk explanation standard adjustment
This commit is contained in:
@@ -32,11 +32,24 @@ export const LONG_DISTANCE_TRAVEL_EXPENSE_TYPES = new Set(['train_ticket', 'flig
|
||||
export const ROUTE_DESCRIPTION_EXPENSE_TYPES = new Set(['train_ticket', 'flight_ticket', 'ship_ticket', 'ferry_ticket', 'ride_ticket'])
|
||||
export const HOTEL_DESCRIPTION_EXPENSE_TYPES = new Set(['hotel_ticket'])
|
||||
export const ROUTE_DESCRIPTION_PATTERN = /^[A-Za-z0-9\u4e00-\u9fa5()()·]{2,40}\s*-\s*[A-Za-z0-9\u4e00-\u9fa5()()·]{2,40}$/
|
||||
export const STANDARD_ADJUSTMENT_RISK_SOURCE = 'reimbursement_standard_adjustment'
|
||||
|
||||
export function parseCurrency(value) {
|
||||
return Number.parseFloat(String(value).replace(/[^\d.]/g, '')) || 0
|
||||
}
|
||||
|
||||
function parseOptionalCurrency(value) {
|
||||
if (value === null || value === undefined || String(value).trim() === '') {
|
||||
return null
|
||||
}
|
||||
const normalized = String(value).replace(/[^\d.]/g, '')
|
||||
if (!normalized) {
|
||||
return null
|
||||
}
|
||||
const amount = Number.parseFloat(normalized)
|
||||
return Number.isFinite(amount) && amount >= 0 ? amount : null
|
||||
}
|
||||
|
||||
export function formatCurrency(value) {
|
||||
return new Intl.NumberFormat('zh-CN', {
|
||||
style: 'currency',
|
||||
@@ -395,6 +408,60 @@ export function resolveExpenseTimeLabel({ id, itemType, isSystemGenerated, reque
|
||||
return requestModel?.detailVariant === 'travel' ? '出行时间' : '业务发生时间'
|
||||
}
|
||||
|
||||
export function buildStandardAdjustmentMap(requestModel = {}) {
|
||||
const flags = Array.isArray(requestModel?.riskFlags)
|
||||
? requestModel.riskFlags
|
||||
: Array.isArray(requestModel?.risk_flags_json)
|
||||
? requestModel.risk_flags_json
|
||||
: []
|
||||
const adjustmentMap = new Map()
|
||||
|
||||
flags.forEach((flag) => {
|
||||
if (!flag || typeof flag !== 'object') {
|
||||
return
|
||||
}
|
||||
if (String(flag.source || '').trim() !== STANDARD_ADJUSTMENT_RISK_SOURCE) {
|
||||
return
|
||||
}
|
||||
const itemId = String(flag.item_id || flag.itemId || '').trim()
|
||||
if (!itemId) {
|
||||
return
|
||||
}
|
||||
const originalAmount = parseOptionalCurrency(flag.original_amount ?? flag.originalAmount)
|
||||
const reimbursableAmount = parseOptionalCurrency(flag.reimbursable_amount ?? flag.reimbursableAmount)
|
||||
if (reimbursableAmount === null) {
|
||||
return
|
||||
}
|
||||
const employeeAbsorbedAmount = parseOptionalCurrency(flag.employee_absorbed_amount ?? flag.employeeAbsorbedAmount) || 0
|
||||
adjustmentMap.set(itemId, {
|
||||
originalAmount,
|
||||
reimbursableAmount,
|
||||
employeeAbsorbedAmount,
|
||||
message: String(flag.message || flag.summary || '').trim()
|
||||
})
|
||||
})
|
||||
|
||||
return adjustmentMap
|
||||
}
|
||||
|
||||
function resolveSourceStandardAdjustment(source, id, requestModel) {
|
||||
const requestAdjustment = buildStandardAdjustmentMap(requestModel).get(id)
|
||||
if (requestAdjustment) {
|
||||
return requestAdjustment
|
||||
}
|
||||
|
||||
const reimbursableAmount = parseOptionalCurrency(source?.reimbursableAmount ?? source?.reimbursable_amount)
|
||||
if (reimbursableAmount === null) {
|
||||
return null
|
||||
}
|
||||
return {
|
||||
originalAmount: parseOptionalCurrency(source?.originalItemAmount ?? source?.original_item_amount ?? source?.originalAmount ?? source?.original_amount),
|
||||
reimbursableAmount,
|
||||
employeeAbsorbedAmount: parseOptionalCurrency(source?.employeeAbsorbedAmount ?? source?.employee_absorbed_amount) || 0,
|
||||
message: String(source?.standardAdjustmentMessage || source?.standard_adjustment_message || '').trim()
|
||||
}
|
||||
}
|
||||
|
||||
export function buildExpenseItemViewModel(source, index, requestModel, travelTimeLabelMap = new Map()) {
|
||||
const itemType = normalizeExpenseType(source?.itemType || source?.item_type || requestModel?.typeCode || 'other')
|
||||
const isSystemGenerated = isSystemGeneratedExpenseItemSource({ ...source, itemType })
|
||||
@@ -407,7 +474,13 @@ export function buildExpenseItemViewModel(source, index, requestModel, travelTim
|
||||
const invoiceId = String(source?.invoiceId ?? source?.invoice_id ?? '').trim()
|
||||
const attachmentName = String(source?.attachmentName || source?.attachment_name || extractAttachmentDisplayName(invoiceId)).trim()
|
||||
const attachments = invoiceId ? [attachmentName || invoiceId] : []
|
||||
const standardAdjustment = resolveSourceStandardAdjustment(source, id, requestModel)
|
||||
const originalItemAmount = standardAdjustment?.originalAmount ?? itemAmount
|
||||
const reimbursableAmount = standardAdjustment?.reimbursableAmount ?? itemAmount
|
||||
const employeeAbsorbedAmount = standardAdjustment?.employeeAbsorbedAmount || Math.max(originalItemAmount - reimbursableAmount, 0)
|
||||
const hasStandardAdjustment = reimbursableAmount >= 0 && reimbursableAmount < originalItemAmount
|
||||
const amountDisplay = itemAmount > 0 ? formatCurrency(itemAmount) : '待补充'
|
||||
const reimbursableAmountDisplay = reimbursableAmount > 0 ? formatCurrency(reimbursableAmount) : '待补充'
|
||||
const riskText = String(source?.riskText || '').trim()
|
||||
const filledAt = formatExpenseFilledTime(
|
||||
source?.filledAt
|
||||
@@ -424,6 +497,15 @@ export function buildExpenseItemViewModel(source, index, requestModel, travelTim
|
||||
itemLocation,
|
||||
itemNote,
|
||||
itemAmount,
|
||||
originalItemAmount,
|
||||
originalAmountDisplay: originalItemAmount > 0 ? formatCurrency(originalItemAmount) : amountDisplay,
|
||||
reimbursableAmount,
|
||||
reimbursableAmountDisplay,
|
||||
employeeAbsorbedAmount,
|
||||
employeeAbsorbedAmountDisplay: employeeAbsorbedAmount > 0 ? formatCurrency(employeeAbsorbedAmount) : '',
|
||||
hasStandardAdjustment,
|
||||
standardAdjustmentAccepted: Boolean(standardAdjustment),
|
||||
standardAdjustmentMessage: standardAdjustment?.message || '',
|
||||
invoiceId,
|
||||
isSystemGenerated,
|
||||
time: itemDate || '待补充',
|
||||
|
||||
Reference in New Issue
Block a user