627 lines
19 KiB
TypeScript
627 lines
19 KiB
TypeScript
import { serviceList } from '@/sql'
|
|
import { roundTo, toFiniteNumber, toFiniteNumberOrZero } from '@/lib/decimal'
|
|
import { getBenchmarkBudgetSplitByScale, getScaleBudgetFeeSplit } from '@/lib/pricingScaleFee'
|
|
export { toFiniteNumber, toFiniteNumberOrZero }
|
|
|
|
interface ScaleMethodRowLike {
|
|
id: string
|
|
majorDictId?: unknown
|
|
amount: number | null
|
|
landArea: number | null
|
|
benchmarkBudgetBasicChecked?: unknown
|
|
benchmarkBudgetOptionalChecked?: unknown
|
|
basicFormula?: unknown
|
|
optionalFormula?: unknown
|
|
budgetFee?: unknown
|
|
budgetFeeBasic?: unknown
|
|
budgetFeeOptional?: unknown
|
|
consultCategoryFactor?: unknown
|
|
majorFactor?: unknown
|
|
workStageFactor?: unknown
|
|
workRatio?: unknown
|
|
remark?: unknown
|
|
}
|
|
|
|
interface WorkContentRowLike {
|
|
id?: unknown
|
|
content?: unknown
|
|
checked?: unknown
|
|
custom?: unknown
|
|
serviceid?: unknown
|
|
isAddTrigger?: unknown
|
|
}
|
|
|
|
interface FactorRowLike {
|
|
id: string
|
|
budgetValue?: unknown
|
|
remark?: unknown
|
|
}
|
|
|
|
interface ScaleRowLike {
|
|
id: string
|
|
amount: number | null
|
|
landArea: number | null
|
|
}
|
|
|
|
interface WorkloadMethodRowLike {
|
|
id: string
|
|
budgetAdoptedUnitPrice?: unknown
|
|
workload?: unknown
|
|
basicFee?: unknown
|
|
consultCategoryFactor?: unknown
|
|
serviceFee?: unknown
|
|
remark?: unknown
|
|
}
|
|
|
|
interface HourlyMethodRowLike {
|
|
id: string
|
|
adoptedBudgetUnitPrice?: unknown
|
|
personnelCount?: unknown
|
|
workdayCount?: unknown
|
|
serviceBudget?: unknown
|
|
remark?: unknown
|
|
}
|
|
|
|
interface ZxFwRowLike {
|
|
id: string
|
|
process?: unknown
|
|
subtotal?: unknown
|
|
finalFee?: unknown
|
|
investScale?: unknown
|
|
landScale?: unknown
|
|
workload?: unknown
|
|
hourly?: unknown
|
|
}
|
|
|
|
interface RateMethodRowLike {
|
|
rate?: unknown
|
|
budgetFee?: unknown
|
|
}
|
|
|
|
interface QuantityMethodRowLike {
|
|
id?: unknown
|
|
feeItem?: unknown
|
|
unit?: unknown
|
|
quantity?: unknown
|
|
unitPrice?: unknown
|
|
budgetFee?: number | null
|
|
remark?: unknown
|
|
}
|
|
|
|
interface ExportScaleRowLike {
|
|
majorid: number
|
|
major: number
|
|
cost: number | null
|
|
area: number | null
|
|
}
|
|
|
|
interface ExportServiceCoeLike {
|
|
serviceid: number
|
|
coe: number
|
|
remark: string
|
|
}
|
|
|
|
interface ExportMajorCoeLike {
|
|
majorid: number
|
|
coe: number
|
|
remark: string
|
|
}
|
|
|
|
interface ExportMethod1DetailLike {
|
|
proNum: number
|
|
major: number
|
|
cost: number
|
|
basicFee: number
|
|
basicFormula: string
|
|
basicFee_basic: number
|
|
optionalFormula: string
|
|
basicFee_optional: number
|
|
serviceCoe: number
|
|
majorCoe: number
|
|
processCoe: number
|
|
proportion: number
|
|
fee: number
|
|
remark: string
|
|
}
|
|
|
|
interface ExportMethod2DetailLike {
|
|
proNum: number
|
|
major: number
|
|
area: number
|
|
basicFee: number
|
|
basicFormula: string
|
|
basicFee_basic: number
|
|
optionalFormula: string
|
|
basicFee_optional: number
|
|
serviceCoe: number
|
|
majorCoe: number
|
|
processCoe: number
|
|
proportion: number
|
|
fee: number
|
|
remark: string
|
|
}
|
|
|
|
interface ExportMethod3DetailLike {
|
|
task: number
|
|
price: number
|
|
amount: number
|
|
basicFee: number
|
|
serviceCoe: number
|
|
fee: number
|
|
remark: string
|
|
}
|
|
|
|
interface ExportMethod4DetailLike {
|
|
expert: number
|
|
price: number
|
|
person_num: number
|
|
work_day: number
|
|
fee: number
|
|
remark: string
|
|
}
|
|
|
|
interface ExportMethod5DetailLike {
|
|
name: string
|
|
unit: string
|
|
amount: number
|
|
price: number
|
|
fee: number
|
|
remark: string
|
|
}
|
|
|
|
export const toSafeInteger = (value: unknown): number | null => {
|
|
const num = Number(value)
|
|
if (!Number.isInteger(num)) return null
|
|
if (!Number.isSafeInteger(num)) return null
|
|
return num
|
|
}
|
|
|
|
export const sumNumbers = (values: Array<number | null | undefined>): number =>
|
|
values.reduce<number>(
|
|
(sum, value) => sum + (typeof value === 'number' && Number.isFinite(value) ? value : 0),
|
|
0
|
|
)
|
|
|
|
export const toMoney = (value: unknown): number => roundTo(toFiniteNumber(value) ?? 0, 2)
|
|
|
|
export const isNonEmptyString = (value: unknown): value is string =>
|
|
typeof value === 'string' && value.trim().length > 0
|
|
|
|
export const getTaskIdFromRowId = (value: string): number | null => {
|
|
const match = /^task-(\d+)-\d+$/.exec(value)
|
|
return match ? toSafeInteger(match[1]) : null
|
|
}
|
|
|
|
export const getExpertIdFromRowId = (value: string): number | null => {
|
|
const match = /^expert-(\d+)$/.exec(value)
|
|
return match ? toSafeInteger(match[1]) : null
|
|
}
|
|
|
|
export const hasServiceId = (serviceId: string) =>
|
|
Object.prototype.hasOwnProperty.call(serviceList as Record<string, unknown>, serviceId)
|
|
|
|
export const sortServiceIdsByDict = (ids: string[]) =>
|
|
[...ids].sort((left, right) => {
|
|
const leftOrder = Number((serviceList as Record<string, any>)[left]?.order)
|
|
const rightOrder = Number((serviceList as Record<string, any>)[right]?.order)
|
|
const safeLeft = Number.isFinite(leftOrder) ? leftOrder : Number.MAX_SAFE_INTEGER
|
|
const safeRight = Number.isFinite(rightOrder) ? rightOrder : Number.MAX_SAFE_INTEGER
|
|
return safeLeft - safeRight
|
|
})
|
|
|
|
export const mapIndustryCodeToExportIndustry = (value: unknown): number => {
|
|
const raw = typeof value === 'string' ? value.trim().toUpperCase() : ''
|
|
if (!raw) return 0
|
|
if (raw === '0' || raw === 'E2') return 0
|
|
if (raw === '1' || raw === 'E3') return 1
|
|
if (raw === '2' || raw === 'E4') return 2
|
|
return 0
|
|
}
|
|
|
|
export const parseScaleScopedRowId = (rowId: unknown) => {
|
|
const raw = String(rowId || '').trim()
|
|
const scopedMatch = /^(\d+)::(.+)$/.exec(raw)
|
|
if (scopedMatch) {
|
|
return {
|
|
proNum: toSafeInteger(scopedMatch[1]) ?? 1,
|
|
majorPart: String(scopedMatch[2] || '').trim()
|
|
}
|
|
}
|
|
return {
|
|
proNum: 1,
|
|
majorPart: raw
|
|
}
|
|
}
|
|
|
|
export const toScaleMajorId = (row: ScaleMethodRowLike): number | null => {
|
|
const direct = toSafeInteger((row as { majorDictId?: unknown }).majorDictId)
|
|
if (direct != null) return direct
|
|
const parsed = parseScaleScopedRowId(row.id)
|
|
return toSafeInteger(parsed.majorPart)
|
|
}
|
|
|
|
export const toScaleProNum = (row: ScaleMethodRowLike): number => {
|
|
const parsed = parseScaleScopedRowId(row.id)
|
|
return parsed.proNum > 0 ? parsed.proNum : 1
|
|
}
|
|
|
|
export const normalizeTaskText = (value: unknown): string => String(value || '').trim()
|
|
|
|
export const resolveTaskRowServiceId = (row: WorkContentRowLike): number | null =>
|
|
toSafeInteger((row as { serviceid?: unknown })?.serviceid)
|
|
|
|
export const resolveScaleMethodFee = (row: ScaleMethodRowLike, mode: 'cost' | 'area') => {
|
|
const scaleValue = mode === 'cost' ? toFiniteNumber(row.amount) : toFiniteNumber(row.landArea)
|
|
const benchmarkSplit = getBenchmarkBudgetSplitByScale(scaleValue, mode)
|
|
const basicChecked = row.benchmarkBudgetBasicChecked !== false
|
|
const optionalChecked = row.benchmarkBudgetOptionalChecked !== false
|
|
const allUnchecked = !basicChecked && !optionalChecked
|
|
const benchmarkBudgetBasic = benchmarkSplit ? (basicChecked ? benchmarkSplit.basic : 0) : null
|
|
const benchmarkBudgetOptional = benchmarkSplit ? (optionalChecked ? benchmarkSplit.optional : 0) : null
|
|
const computedSplit = benchmarkSplit
|
|
? getScaleBudgetFeeSplit({
|
|
benchmarkBudgetBasic,
|
|
benchmarkBudgetOptional,
|
|
majorFactor: row.majorFactor,
|
|
consultCategoryFactor: row.consultCategoryFactor,
|
|
workStageFactor: row.workStageFactor,
|
|
workRatio: row.workRatio
|
|
})
|
|
: null
|
|
const basicFee = allUnchecked ? null : (toFiniteNumber(row.budgetFee) ?? computedSplit?.total ?? null)
|
|
const basicFeeBasic = allUnchecked ? null : (toFiniteNumber(row.budgetFeeBasic) ?? computedSplit?.basic ?? null)
|
|
const basicFeeOptional = allUnchecked ? null : (toFiniteNumber(row.budgetFeeOptional) ?? computedSplit?.optional ?? null)
|
|
const basicFormula = typeof row.basicFormula === 'string' && row.basicFormula.trim()
|
|
? row.basicFormula
|
|
: (basicChecked ? (benchmarkSplit?.basicFormula ?? '') : '')
|
|
const optionalFormula = typeof row.optionalFormula === 'string' && row.optionalFormula.trim()
|
|
? row.optionalFormula
|
|
: (optionalChecked ? (benchmarkSplit?.optionalFormula ?? '') : '')
|
|
return {
|
|
basicFee,
|
|
basicFeeBasic,
|
|
basicFeeOptional,
|
|
basicFormula,
|
|
optionalFormula
|
|
}
|
|
}
|
|
|
|
export interface ExportTaskGroupLike {
|
|
serviceid?: number
|
|
text: string[]
|
|
}
|
|
|
|
export const groupWorkContentTasks = (
|
|
rows: WorkContentRowLike[] | undefined,
|
|
options?: { forceUngroup?: boolean }
|
|
): ExportTaskGroupLike[] => {
|
|
const source = Array.isArray(rows) ? rows : []
|
|
const selected = source.filter(item => {
|
|
if (item && item.isAddTrigger === true) return false
|
|
const isCustom = Boolean(item?.custom)
|
|
const isChecked = Boolean(item?.checked)
|
|
return isCustom || isChecked
|
|
})
|
|
if (selected.length === 0) return []
|
|
|
|
const hasGroup = !options?.forceUngroup && selected.some(item => resolveTaskRowServiceId(item) != null)
|
|
if (!hasGroup) {
|
|
const text = selected
|
|
.map(item => normalizeTaskText(item?.content))
|
|
.filter(Boolean)
|
|
return text.length > 0 ? [{ text }] : []
|
|
}
|
|
|
|
const grouped = new Map<number, string[]>()
|
|
const orderedServiceIds: number[] = []
|
|
const ungroupedText: string[] = []
|
|
for (const item of selected) {
|
|
const content = normalizeTaskText(item?.content)
|
|
if (!content) continue
|
|
const serviceid = resolveTaskRowServiceId(item)
|
|
if (serviceid == null) {
|
|
ungroupedText.push(content)
|
|
continue
|
|
}
|
|
if (!grouped.has(serviceid)) {
|
|
grouped.set(serviceid, [])
|
|
orderedServiceIds.push(serviceid)
|
|
}
|
|
grouped.get(serviceid)?.push(content)
|
|
}
|
|
|
|
const groupedTasks: ExportTaskGroupLike[] = []
|
|
for (const serviceid of orderedServiceIds) {
|
|
const text = grouped.get(serviceid) || []
|
|
if (text.length === 0) continue
|
|
groupedTasks.push({ serviceid, text })
|
|
}
|
|
|
|
if (ungroupedText.length > 0) {
|
|
groupedTasks.push({ text: ungroupedText })
|
|
}
|
|
return groupedTasks
|
|
}
|
|
|
|
export const buildProjectServiceCoes = (rows: FactorRowLike[] | undefined) => {
|
|
if (!Array.isArray(rows)) return []
|
|
return rows
|
|
.map(row => {
|
|
const serviceid = toSafeInteger(row.id)
|
|
if (serviceid == null || row.budgetValue == null) return null
|
|
const coe = toFiniteNumber(row.budgetValue)
|
|
return {
|
|
serviceid,
|
|
coe: coe ?? 0,
|
|
remark: typeof row.remark === 'string' ? row.remark : ''
|
|
}
|
|
})
|
|
.filter((item): item is ExportServiceCoeLike => Boolean(item))
|
|
}
|
|
|
|
export const buildProjectMajorCoes = (rows: FactorRowLike[] | undefined) => {
|
|
if (!Array.isArray(rows)) return []
|
|
return rows
|
|
.map(row => {
|
|
const majorid = toSafeInteger(row.id)
|
|
if (majorid == null || row.budgetValue == null) return null
|
|
const coe = toFiniteNumber(row.budgetValue)
|
|
return {
|
|
majorid,
|
|
coe: coe ?? 0,
|
|
remark: typeof row.remark === 'string' ? row.remark : ''
|
|
}
|
|
})
|
|
.filter((item): item is ExportMajorCoeLike => Boolean(item))
|
|
}
|
|
|
|
export const toExportScaleRows = (rows: ScaleRowLike[] | undefined) => {
|
|
if (!Array.isArray(rows)) return []
|
|
return rows
|
|
.map(row => {
|
|
const majorid = toSafeInteger(row.id)
|
|
if (majorid == null || (row.amount == null && row.landArea == null)) return null
|
|
return {
|
|
majorid,
|
|
major: majorid,
|
|
cost: row.amount,
|
|
area: row.landArea
|
|
}
|
|
})
|
|
.filter((item): item is ExportScaleRowLike => Boolean(item))
|
|
}
|
|
|
|
export const buildMethod1 = (rows: ScaleMethodRowLike[] | undefined) => {
|
|
if (!Array.isArray(rows)) return null
|
|
let hasTotalValue = false
|
|
const proSet = new Set<number>()
|
|
const det = rows
|
|
.map(row => {
|
|
const major = toScaleMajorId(row)
|
|
if (major == null) return null
|
|
const proNum = toScaleProNum(row)
|
|
proSet.add(proNum)
|
|
const cost = toFiniteNumber(row.amount)
|
|
const feeResolved = resolveScaleMethodFee(row, 'cost')
|
|
const basicFee = feeResolved.basicFee
|
|
if (basicFee != null) hasTotalValue = true
|
|
const basicFeeBasic = feeResolved.basicFeeBasic
|
|
const basicFeeOptional = feeResolved.basicFeeOptional
|
|
const remark = typeof row.remark === 'string' ? row.remark : ''
|
|
if (basicFee == null) return null
|
|
return {
|
|
proNum,
|
|
major,
|
|
cost: cost ?? 0,
|
|
basicFee: toMoney(basicFee),
|
|
basicFormula: feeResolved.basicFormula,
|
|
basicFee_basic: toMoney(basicFeeBasic),
|
|
optionalFormula: feeResolved.optionalFormula,
|
|
basicFee_optional: toMoney(basicFeeOptional),
|
|
serviceCoe: toFiniteNumberOrZero(row.consultCategoryFactor),
|
|
majorCoe: toFiniteNumberOrZero(row.majorFactor),
|
|
processCoe: toFiniteNumber(row.workStageFactor) ?? 1,
|
|
proportion: toFiniteNumber(row.workRatio) ?? 1,
|
|
fee: toMoney(basicFee),
|
|
remark
|
|
}
|
|
})
|
|
.filter((item): item is ExportMethod1DetailLike => Boolean(item))
|
|
|
|
if (det.length === 0 || !hasTotalValue) return null
|
|
return {
|
|
proAmount: proSet.size > 0 ? proSet.size : 1,
|
|
cost: sumNumbers(det.map(item => item.cost)),
|
|
basicFee: toMoney(sumNumbers(det.map(item => item.basicFee))),
|
|
basicFee_basic: toMoney(sumNumbers(det.map(item => item.basicFee_basic))),
|
|
basicFee_optional: toMoney(sumNumbers(det.map(item => item.basicFee_optional))),
|
|
fee: toMoney(sumNumbers(det.map(item => item.fee))),
|
|
det
|
|
}
|
|
}
|
|
|
|
export const buildMethod2 = (rows: ScaleMethodRowLike[] | undefined) => {
|
|
if (!Array.isArray(rows)) return null
|
|
let hasTotalValue = false
|
|
const proSet = new Set<number>()
|
|
const det = rows
|
|
.map(row => {
|
|
const major = toScaleMajorId(row)
|
|
if (major == null) return null
|
|
const proNum = toScaleProNum(row)
|
|
proSet.add(proNum)
|
|
const area = toFiniteNumber(row.landArea)
|
|
const feeResolved = resolveScaleMethodFee(row, 'area')
|
|
const basicFee = feeResolved.basicFee
|
|
if (basicFee != null) hasTotalValue = true
|
|
const basicFeeBasic = feeResolved.basicFeeBasic
|
|
const basicFeeOptional = feeResolved.basicFeeOptional
|
|
const remark = typeof row.remark === 'string' ? row.remark : ''
|
|
if (basicFee == null) return null
|
|
return {
|
|
proNum,
|
|
major,
|
|
area: area ?? 0,
|
|
basicFee: toMoney(basicFee),
|
|
basicFormula: feeResolved.basicFormula,
|
|
basicFee_basic: toMoney(basicFeeBasic),
|
|
optionalFormula: feeResolved.optionalFormula,
|
|
basicFee_optional: toMoney(basicFeeOptional),
|
|
serviceCoe: toFiniteNumberOrZero(row.consultCategoryFactor),
|
|
majorCoe: toFiniteNumberOrZero(row.majorFactor),
|
|
processCoe: toFiniteNumber(row.workStageFactor) ?? 1,
|
|
proportion: toFiniteNumber(row.workRatio) ?? 1,
|
|
fee: toMoney(basicFee),
|
|
remark
|
|
}
|
|
})
|
|
.filter((item): item is ExportMethod2DetailLike => Boolean(item))
|
|
|
|
if (det.length === 0 || !hasTotalValue) return null
|
|
return {
|
|
proAmount: proSet.size > 0 ? proSet.size : 1,
|
|
area: sumNumbers(det.map(item => item.area)),
|
|
basicFee: toMoney(sumNumbers(det.map(item => item.basicFee))),
|
|
basicFee_basic: toMoney(sumNumbers(det.map(item => item.basicFee_basic))),
|
|
basicFee_optional: toMoney(sumNumbers(det.map(item => item.basicFee_optional))),
|
|
fee: toMoney(sumNumbers(det.map(item => item.fee))),
|
|
det
|
|
}
|
|
}
|
|
|
|
export const buildMethod3 = (rows: WorkloadMethodRowLike[] | undefined) => {
|
|
if (!Array.isArray(rows)) return null
|
|
let hasTotalValue = false
|
|
const det = rows
|
|
.map(row => {
|
|
const task = getTaskIdFromRowId(row.id)
|
|
if (task == null || row.basicFee == null) return null
|
|
const amount = toFiniteNumber(row.workload)
|
|
const basicFee = toFiniteNumber(row.basicFee)
|
|
const fee = toFiniteNumber(row.serviceFee)
|
|
if (fee != null) hasTotalValue = true
|
|
const remark = typeof row.remark === 'string' ? row.remark : ''
|
|
const hasValue = amount != null || basicFee != null || fee != null || isNonEmptyString(remark)
|
|
if (!hasValue) return null
|
|
return {
|
|
task,
|
|
price: toFiniteNumberOrZero(row.budgetAdoptedUnitPrice),
|
|
amount: amount ?? 0,
|
|
basicFee: basicFee ?? 0,
|
|
serviceCoe: toFiniteNumberOrZero(row.consultCategoryFactor),
|
|
fee: fee ?? 0,
|
|
remark
|
|
}
|
|
})
|
|
.filter((item): item is ExportMethod3DetailLike => Boolean(item))
|
|
|
|
if (det.length === 0 || !hasTotalValue) return null
|
|
return {
|
|
basicFee: sumNumbers(det.map(item => item.basicFee)),
|
|
fee: sumNumbers(det.map(item => item.fee)),
|
|
det
|
|
}
|
|
}
|
|
|
|
export const buildMethod4 = (rows: HourlyMethodRowLike[] | undefined) => {
|
|
if (!Array.isArray(rows)) return null
|
|
let hasTotalValue = false
|
|
const det = rows
|
|
.map(row => {
|
|
const expert = getExpertIdFromRowId(row.id)
|
|
if (expert == null || row.serviceBudget == null) return null
|
|
const personNum = toFiniteNumber(row.personnelCount)
|
|
const workDay = toFiniteNumber(row.workdayCount)
|
|
const fee = toFiniteNumber(row.serviceBudget)
|
|
if (fee != null) hasTotalValue = true
|
|
const remark = typeof row.remark === 'string' ? row.remark : ''
|
|
const hasValue = personNum != null || workDay != null || fee != null || isNonEmptyString(remark)
|
|
if (!hasValue) return null
|
|
return {
|
|
expert,
|
|
price: toFiniteNumberOrZero(row.adoptedBudgetUnitPrice),
|
|
person_num: personNum ?? 0,
|
|
work_day: workDay ?? 0,
|
|
fee: fee ?? 0,
|
|
remark
|
|
}
|
|
})
|
|
.filter((item): item is ExportMethod4DetailLike => Boolean(item))
|
|
|
|
if (det.length === 0 || !hasTotalValue) return null
|
|
return {
|
|
person_num: sumNumbers(det.map(item => item.person_num)),
|
|
work_day: sumNumbers(det.map(item => item.work_day)),
|
|
fee: sumNumbers(det.map(item => item.fee)),
|
|
det
|
|
}
|
|
}
|
|
|
|
export const buildServiceFee = (
|
|
row: ZxFwRowLike | null | undefined,
|
|
method1: { fee: number } | null,
|
|
method2: { fee: number } | null,
|
|
method3: { fee: number } | null,
|
|
method4: { fee: number } | null
|
|
) => {
|
|
const subtotal = toFiniteNumber(row?.subtotal)
|
|
return subtotal != null ? toMoney(subtotal) : 0
|
|
}
|
|
|
|
export const buildMethod0 = (payload: RateMethodRowLike | null | undefined) => {
|
|
const coe = toFiniteNumber(payload?.rate)
|
|
const fee = toFiniteNumber(payload?.budgetFee)
|
|
if (fee == null) return null
|
|
return {
|
|
coe: coe ?? 0,
|
|
fee
|
|
}
|
|
}
|
|
|
|
export const buildMethod5 = (rows: QuantityMethodRowLike[] | undefined) => {
|
|
if (!Array.isArray(rows) || rows.length === 0) return null
|
|
const subtotalRow = rows.find(row => String(row?.id || '') === 'fee-subtotal-fixed')
|
|
const subtotalFee = toFiniteNumber(subtotalRow?.budgetFee)
|
|
if (subtotalFee == null) return null
|
|
const det = rows
|
|
.filter(row => String(row?.id || '') !== 'fee-subtotal-fixed')
|
|
.map(row => {
|
|
const quantity = toFiniteNumber(row.quantity)
|
|
const unitPrice = toFiniteNumber(row.unitPrice)
|
|
const fee = toFiniteNumber(row.budgetFee)
|
|
const name = typeof row.feeItem === 'string' ? row.feeItem : ''
|
|
const unit = typeof row.unit === 'string' ? row.unit : ''
|
|
const remark = typeof row.remark === 'string' ? row.remark : ''
|
|
|
|
if (row.budgetFee == null) return null
|
|
return {
|
|
name,
|
|
unit,
|
|
amount: quantity ?? 0,
|
|
price: unitPrice ?? 0,
|
|
fee: fee ?? 0,
|
|
remark
|
|
}
|
|
})
|
|
.filter((item): item is ExportMethod5DetailLike => Boolean(item))
|
|
|
|
if (det.length === 0) return null
|
|
return {
|
|
fee: subtotalFee,
|
|
det
|
|
}
|
|
}
|
|
|
|
export const buildServiceFinalFee = (
|
|
row: ZxFwRowLike | null | undefined,
|
|
method1: { fee: number } | null,
|
|
method2: { fee: number } | null,
|
|
method3: { fee: number } | null,
|
|
method4: { fee: number } | null
|
|
) => {
|
|
const finalFee = toFiniteNumber(row?.finalFee)
|
|
if (finalFee != null) return toMoney(finalFee)
|
|
const subtotal = toFiniteNumber(row?.subtotal)
|
|
return subtotal != null ? toMoney(subtotal) : 0
|
|
}
|