This commit is contained in:
zwgczx committed 2026-03-11 17:41:08 +08:00
1 parent 3d26b0b259
commit 0f71fff9ac
11 files changed
+873 -205

No files matched your search

+51 -27
View File
@@ -25,6 +25,17 @@ interface StoredFactorState {
type MaybeNumber = number | null | undefined
const sumByNumberNullable = <T>(list: T[], pick: (item: T) => MaybeNumber): number | null => {
let hasValid = false
const total = sumByNumber(list, item => {
const value = toFiniteNumberOrNull(pick(item))
if (value == null) return null
hasValid = true
return value
})
return hasValid ? total : null
}
interface ScaleRow {
id: string
amount: number | null
@@ -153,6 +164,17 @@ const toStoredDetailRowsState = <TRow = unknown>(state: { detailRows?: TRow[] }
const hasOwn = (obj: unknown, key: string) =>
Object.prototype.hasOwnProperty.call(obj || {}, key)
const getRowNumberOrFallback = (
row: Record<string, unknown> | undefined,
key: string,
fallback: number | null
) => {
if (!row) return fallback
const value = toFiniteNumberOrNull(row[key])
if (value != null) return value
return hasOwn(row, key) ? null : fallback
}
const toRowMap = <TRow extends { id: string }>(rows?: TRow[]) => {
const map = new Map<string, TRow>()
for (const row of rows || []) {
@@ -363,32 +385,31 @@ const getOnlyCostScaleBudgetFee = (
return /^\d+::/.test(id)
})
if (usePerRowCalculation) {
return sumByNumber(sourceRows, row => {
return sumByNumberNullable(sourceRows, row => {
const amount = toFiniteNumberOrNull(row?.amount)
if (amount == null) return null
return getScaleBudgetFee({
benchmarkBudget: getBenchmarkBudgetByAmount(amount),
majorFactor: toFiniteNumberOrNull(row?.majorFactor) ?? defaultMajorFactor,
consultCategoryFactor: toFiniteNumberOrNull(row?.consultCategoryFactor) ?? defaultConsultCategoryFactor,
workStageFactor: toFiniteNumberOrNull(row?.workStageFactor) ?? 1,
workRatio: toFiniteNumberOrNull(row?.workRatio) ?? 100
majorFactor: getRowNumberOrFallback(row, 'majorFactor', defaultMajorFactor),
consultCategoryFactor: getRowNumberOrFallback(row, 'consultCategoryFactor', defaultConsultCategoryFactor),
workStageFactor: getRowNumberOrFallback(row, 'workStageFactor', 1),
workRatio: getRowNumberOrFallback(row, 'workRatio', 100)
})
})
}
const totalAmount = sumByNumber(sourceRows, row =>
const totalAmount = sumByNumberNullable(sourceRows, row =>
typeof row?.amount === 'number' && Number.isFinite(row.amount) ? row.amount : null
)
if (totalAmount == null) return null
const onlyRow =
sourceRows.find(row => String(row?.id || '') === ONLY_COST_SCALE_ROW_ID) ||
sourceRows.find(row => hasOwn(row, 'consultCategoryFactor') || hasOwn(row, 'majorFactor')) ||
sourceRows[0]
const consultCategoryFactor =
toFiniteNumberOrNull(onlyRow?.consultCategoryFactor) ?? defaultConsultCategoryFactor
const majorFactor =
toFiniteNumberOrNull(onlyRow?.majorFactor) ?? defaultMajorFactor
const workStageFactor = toFiniteNumberOrNull(onlyRow?.workStageFactor) ?? 1
const workRatio = toFiniteNumberOrNull(onlyRow?.workRatio) ?? 100
const consultCategoryFactor = getRowNumberOrFallback(onlyRow, 'consultCategoryFactor', defaultConsultCategoryFactor)
const majorFactor = getRowNumberOrFallback(onlyRow, 'majorFactor', defaultMajorFactor)
const workStageFactor = getRowNumberOrFallback(onlyRow, 'workStageFactor', 1)
const workRatio = getRowNumberOrFallback(onlyRow, 'workRatio', 100)
return getScaleBudgetFee({
benchmarkBudget: getBenchmarkBudgetByAmount(totalAmount),
majorFactor,
@@ -405,7 +426,7 @@ const buildOnlyCostScaleDetailRows = (
majorFactorMap?: Map<string, number | null>,
industryId?: string | null
) => {
const totalAmount = sumByNumber(rowsFromDb || [], row =>
const totalAmount = sumByNumberNullable(rowsFromDb || [], row =>
typeof row?.amount === 'number' && Number.isFinite(row.amount) ? row.amount : null
)
const industryMajorEntry = getIndustryMajorEntryByIndustryId(industryId)
@@ -413,17 +434,20 @@ const buildOnlyCostScaleDetailRows = (
const onlyRow =
(rowsFromDb || []).find(row => String(row?.id || '') === ONLY_COST_SCALE_ROW_ID) ||
(rowsFromDb || []).find(row => String(row?.id || '') === onlyCostRowId)
const consultCategoryFactor =
toFiniteNumberOrNull(onlyRow?.consultCategoryFactor) ??
consultCategoryFactorMap?.get(String(serviceId)) ??
getDefaultConsultCategoryFactor(serviceId)
const majorFactor =
toFiniteNumberOrNull(onlyRow?.majorFactor) ??
const consultCategoryFactor = getRowNumberOrFallback(
onlyRow,
'consultCategoryFactor',
consultCategoryFactorMap?.get(String(serviceId)) ?? getDefaultConsultCategoryFactor(serviceId)
)
const majorFactor = getRowNumberOrFallback(
onlyRow,
'majorFactor',
(industryMajorEntry ? majorFactorMap?.get(industryMajorEntry.id) ?? null : null) ??
toFiniteNumberOrNull(industryMajorEntry?.item?.defCoe) ??
1
const workStageFactor = toFiniteNumberOrNull(onlyRow?.workStageFactor) ?? 1
const workRatio = toFiniteNumberOrNull(onlyRow?.workRatio) ?? 100
toFiniteNumberOrNull(industryMajorEntry?.item?.defCoe) ??
1
)
const workStageFactor = getRowNumberOrFallback(onlyRow, 'workStageFactor', 1)
const workRatio = getRowNumberOrFallback(onlyRow, 'workRatio', 100)
return [
{
@@ -757,7 +781,7 @@ export const getPricingMethodTotalsForService = async (params: {
consultCategoryFactorMap,
majorFactorMap
)
return sumByNumber(investRows, row => {
return sumByNumberNullable(investRows, row => {
if (!isCostMajorById(row.id)) return null
if (excludeInvestmentCostAndAreaRows && isDualScaleMajorById(row.id)) return null
return getInvestmentBudgetFee(row)
@@ -771,13 +795,13 @@ export const getPricingMethodTotalsForService = async (params: {
consultCategoryFactorMap,
majorFactorMap
)
const landScale = sumByNumber(landRows, row => (isAreaMajorById(row.id) ? getLandBudgetFee(row) : null))
const landScale = sumByNumberNullable(landRows, row => (isAreaMajorById(row.id) ? getLandBudgetFee(row) : null))
const defaultWorkloadRows = buildDefaultWorkloadRows(serviceId, consultCategoryFactorMap)
const workload =
defaultWorkloadRows.length === 0
? null
: sumByNumber(
: sumByNumberNullable(
workloadData?.detailRows != null
? mergeWorkloadRows(serviceId, workloadData.detailRows as any, consultCategoryFactorMap)
: defaultWorkloadRows,
@@ -788,7 +812,7 @@ export const getPricingMethodTotalsForService = async (params: {
hourlyData?.detailRows != null
? mergeHourlyRows(hourlyData.detailRows as any)
: buildDefaultHourlyRows()
const hourly = sumByNumber(hourlyRows, row => calcHourlyServiceBudget(row))
const hourly = sumByNumberNullable(hourlyRows, row => calcHourlyServiceBudget(row))
return {
investScale,