fix
This commit is contained in:
1 parent
3d26b0b259
commit
0f71fff9ac
11 files changed
+873
-205
No files matched your search
+378
-60
@@ -22,7 +22,8 @@ import {
|
||||
AlertDialogTrigger,
|
||||
} from 'reka-ui'
|
||||
import { decodeZwArchive, encodeZwArchive, ZW_FILE_EXTENSION } from '@/lib/zwArchive'
|
||||
import { exportFile, serviceList } from '@/sql'
|
||||
import { addNumbers, roundTo } from '@/lib/decimal'
|
||||
import { exportFile, serviceList, additionalWorkList } from '@/sql'
|
||||
|
||||
interface DataEntry {
|
||||
key: string
|
||||
@@ -59,12 +60,14 @@ type XmInfoLike = {
|
||||
|
||||
interface ScaleRowLike {
|
||||
id: string
|
||||
amount?: unknown
|
||||
landArea?: unknown
|
||||
amount: number | null
|
||||
landArea: number | null
|
||||
}
|
||||
|
||||
interface XmInfoStorageLike extends XmInfoLike {
|
||||
detailRows?: ScaleRowLike[]
|
||||
detailRows?: ScaleRowLike[],
|
||||
totalAmount?: number,
|
||||
roughCalcEnabled?: boolean
|
||||
}
|
||||
interface XmScaleStorageLike {
|
||||
detailRows?: ScaleRowLike[]
|
||||
@@ -78,6 +81,7 @@ interface ContractCardItem {
|
||||
|
||||
interface ZxFwRowLike {
|
||||
id: string
|
||||
process?: unknown
|
||||
subtotal?: unknown
|
||||
investScale?: unknown
|
||||
landScale?: unknown
|
||||
@@ -86,6 +90,8 @@ interface ZxFwRowLike {
|
||||
}
|
||||
|
||||
interface ZxFwStorageLike {
|
||||
selectedIds?: string[]
|
||||
selectedCodes?: string[]
|
||||
detailRows?: ZxFwRowLike[]
|
||||
}
|
||||
|
||||
@@ -97,6 +103,28 @@ interface ScaleMethodRowLike extends ScaleRowLike {
|
||||
budgetFeeOptional?: unknown
|
||||
consultCategoryFactor?: unknown
|
||||
majorFactor?: unknown
|
||||
workStageFactor?: unknown
|
||||
workRatio?: unknown
|
||||
remark?: unknown
|
||||
}
|
||||
|
||||
interface HtFeeMainRowLike {
|
||||
id?: unknown
|
||||
name?: unknown
|
||||
}
|
||||
|
||||
interface RateMethodRowLike {
|
||||
rate?: unknown
|
||||
budgetFee?: unknown
|
||||
}
|
||||
|
||||
interface QuantityMethodRowLike {
|
||||
id?: unknown
|
||||
feeItem?: unknown
|
||||
unit?: unknown
|
||||
quantity?: unknown
|
||||
unitPrice?: unknown
|
||||
budgetFee?: unknown
|
||||
remark?: unknown
|
||||
}
|
||||
|
||||
@@ -120,7 +148,11 @@ interface HourlyMethodRowLike {
|
||||
}
|
||||
|
||||
interface DetailRowsStorageLike<T> {
|
||||
detailRows?: T[]
|
||||
detailRows?: T[],
|
||||
roughCalcEnabled?: boolean,
|
||||
totalAmount?: number,
|
||||
|
||||
|
||||
}
|
||||
|
||||
interface FactorRowLike {
|
||||
@@ -132,8 +164,8 @@ interface FactorRowLike {
|
||||
|
||||
interface ExportScaleRow {
|
||||
major: number
|
||||
cost: number
|
||||
area: number
|
||||
cost: number | null
|
||||
area: number | null
|
||||
}
|
||||
|
||||
interface ExportMethod1Detail {
|
||||
@@ -250,8 +282,63 @@ interface ExportContract {
|
||||
serviceCoes: ExportServiceCoe[]
|
||||
majorCoes: ExportMajorCoe[]
|
||||
services: ExportService[]
|
||||
addtional: []
|
||||
reserve: []
|
||||
addtional: ExportAdditional | null
|
||||
reserve: ExportReserve | null
|
||||
}
|
||||
|
||||
interface ExportRichTextCode {
|
||||
richText: Array<{
|
||||
text: string
|
||||
font?: Record<string, unknown>
|
||||
}>
|
||||
}
|
||||
|
||||
interface ExportMethod0 {
|
||||
coe: number
|
||||
fee: number
|
||||
}
|
||||
|
||||
interface ExportMethod5Detail {
|
||||
name: string
|
||||
unit: string
|
||||
amount: number
|
||||
price: number
|
||||
fee: number
|
||||
remark: string
|
||||
}
|
||||
|
||||
interface ExportMethod5 {
|
||||
fee: number
|
||||
det: ExportMethod5Detail[]
|
||||
}
|
||||
|
||||
interface ExportAdditionalDetail {
|
||||
id: number
|
||||
code: ExportRichTextCode
|
||||
ref?: ExportRichTextCode
|
||||
name: string
|
||||
fee: number
|
||||
m0?: ExportMethod0
|
||||
m4?: ExportMethod4
|
||||
m5?: ExportMethod5
|
||||
}
|
||||
|
||||
interface ExportAdditional {
|
||||
code: ExportRichTextCode
|
||||
ref?: ExportRichTextCode
|
||||
name: string
|
||||
fee: number
|
||||
det: ExportAdditionalDetail[]
|
||||
}
|
||||
|
||||
interface ExportReserve {
|
||||
code: ExportRichTextCode
|
||||
ref?: ExportRichTextCode
|
||||
name: string
|
||||
fee: number
|
||||
m0?: ExportMethod0
|
||||
m4?: ExportMethod4
|
||||
m5?: ExportMethod5
|
||||
}
|
||||
|
||||
interface ExportReportPayload {
|
||||
@@ -872,6 +959,15 @@ const getExpertIdFromRowId = (value: string): number | null => {
|
||||
const hasServiceId = (serviceId: string) =>
|
||||
Object.prototype.hasOwnProperty.call(serviceList as Record<string, unknown>, serviceId)
|
||||
|
||||
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
|
||||
})
|
||||
|
||||
const mapIndustryCodeToExportIndustry = (value: unknown): number => {
|
||||
const raw = typeof value === 'string' ? value.trim().toUpperCase() : ''
|
||||
if (!raw) return 0
|
||||
@@ -886,12 +982,12 @@ const buildProjectServiceCoes = (rows: FactorRowLike[] | undefined): ExportServi
|
||||
return rows
|
||||
.map(row => {
|
||||
const serviceid = toSafeInteger(row.id)
|
||||
if (serviceid == null) return null
|
||||
const coe = toFiniteNumber(row.budgetValue) ?? toFiniteNumber(row.standardFactor) ?? 0
|
||||
if (serviceid == null || row.budgetValue == null) return null
|
||||
const coe = toFiniteNumber(row.budgetValue)
|
||||
return {
|
||||
serviceid,
|
||||
coe,
|
||||
remark: typeof row.remark === 'string' ? row.remark : ''
|
||||
remark: row.remark
|
||||
}
|
||||
})
|
||||
.filter((item): item is ExportServiceCoe => Boolean(item))
|
||||
@@ -902,42 +998,44 @@ const buildProjectMajorCoes = (rows: FactorRowLike[] | undefined): ExportMajorCo
|
||||
return rows
|
||||
.map(row => {
|
||||
const majorid = toSafeInteger(row.id)
|
||||
if (majorid == null) return null
|
||||
const coe = toFiniteNumber(row.budgetValue) ?? toFiniteNumber(row.standardFactor) ?? 0
|
||||
if (majorid == null || row.budgetValue == null) return null
|
||||
const coe = toFiniteNumber(row.budgetValue)
|
||||
return {
|
||||
majorid,
|
||||
coe,
|
||||
remark: typeof row.remark === 'string' ? row.remark : ''
|
||||
remark: row.remark
|
||||
}
|
||||
})
|
||||
.filter((item): item is ExportMajorCoe => Boolean(item))
|
||||
}
|
||||
|
||||
const buildScaleRows = (rows: ScaleRowLike[] | undefined): ExportScaleRow[] => {
|
||||
const toExportScaleRows = (rows: ScaleRowLike[] | undefined): ExportScaleRow[] => {
|
||||
if (!Array.isArray(rows)) return []
|
||||
return rows
|
||||
.map(row => {
|
||||
const major = toSafeInteger(row.id)
|
||||
const cost = toFiniteNumber(row.amount)
|
||||
const area = toFiniteNumber(row.landArea)
|
||||
if (major == null || (cost == null && area == null)) return null
|
||||
|
||||
if (row.id == null || (row.amount == null && row.landArea == null)) return null
|
||||
return {
|
||||
major,
|
||||
cost: cost ?? 0,
|
||||
area: area ?? 0
|
||||
major: toSafeInteger(row.id),
|
||||
cost: row.amount,
|
||||
area: row.landArea
|
||||
}
|
||||
})
|
||||
.filter((item): item is ExportScaleRow => Boolean(item))
|
||||
}
|
||||
|
||||
|
||||
|
||||
const buildMethod1 = (rows: ScaleMethodRowLike[] | undefined): ExportMethod1 | null => {
|
||||
if (!Array.isArray(rows)) return null
|
||||
let hasTotalValue = false
|
||||
const det = rows
|
||||
.map(row => {
|
||||
const major = toSafeInteger(row.id)
|
||||
if (major == null) return null
|
||||
if (major == null || row.budgetFee ==null) return null
|
||||
const cost = toFiniteNumber(row.amount)
|
||||
const basicFee = toFiniteNumber(row.budgetFee)
|
||||
if (basicFee != null) hasTotalValue = true
|
||||
const basicFeeBasic = toFiniteNumber(row.budgetFeeBasic)
|
||||
const basicFeeOptional = toFiniteNumber(row.budgetFeeOptional)
|
||||
const remark = typeof row.remark === 'string' ? row.remark : ''
|
||||
@@ -958,15 +1056,16 @@ const buildMethod1 = (rows: ScaleMethodRowLike[] | undefined): ExportMethod1 | n
|
||||
basicFee_optional: basicFeeOptional ?? 0,
|
||||
serviceCoe: toFiniteNumberOrZero(row.consultCategoryFactor),
|
||||
majorCoe: toFiniteNumberOrZero(row.majorFactor),
|
||||
processCoe: 1,
|
||||
proportion: 1,
|
||||
processCoe: toFiniteNumber(row.workStageFactor) ?? 1,
|
||||
proportion: toFiniteNumber(row.workRatio) ?? 1,
|
||||
fee: basicFee ?? 0,
|
||||
remark
|
||||
}
|
||||
})
|
||||
.filter((item): item is ExportMethod1Detail => Boolean(item))
|
||||
|
||||
if (det.length === 0) return null
|
||||
if (det.length === 0 || !hasTotalValue) return null
|
||||
console.log(det)
|
||||
return {
|
||||
cost: sumNumbers(det.map(item => item.cost)),
|
||||
basicFee: sumNumbers(det.map(item => item.basicFee)),
|
||||
@@ -979,12 +1078,14 @@ const buildMethod1 = (rows: ScaleMethodRowLike[] | undefined): ExportMethod1 | n
|
||||
|
||||
const buildMethod2 = (rows: ScaleMethodRowLike[] | undefined): ExportMethod2 | null => {
|
||||
if (!Array.isArray(rows)) return null
|
||||
let hasTotalValue = false
|
||||
const det = rows
|
||||
.map(row => {
|
||||
const major = toSafeInteger(row.id)
|
||||
if (major == null) return null
|
||||
if (major == null || row.budgetFee ==null) return null
|
||||
const area = toFiniteNumber(row.landArea)
|
||||
const basicFee = toFiniteNumber(row.budgetFee)
|
||||
if (basicFee != null) hasTotalValue = true
|
||||
const basicFeeBasic = toFiniteNumber(row.budgetFeeBasic)
|
||||
const basicFeeOptional = toFiniteNumber(row.budgetFeeOptional)
|
||||
const remark = typeof row.remark === 'string' ? row.remark : ''
|
||||
@@ -1005,15 +1106,15 @@ const buildMethod2 = (rows: ScaleMethodRowLike[] | undefined): ExportMethod2 | n
|
||||
basicFee_optional: basicFeeOptional ?? 0,
|
||||
serviceCoe: toFiniteNumberOrZero(row.consultCategoryFactor),
|
||||
majorCoe: toFiniteNumberOrZero(row.majorFactor),
|
||||
processCoe: 1,
|
||||
proportion: 1,
|
||||
processCoe: toFiniteNumber(row.workStageFactor) ?? 1,
|
||||
proportion: toFiniteNumber(row.workRatio) ?? 1,
|
||||
fee: basicFee ?? 0,
|
||||
remark
|
||||
}
|
||||
})
|
||||
.filter((item): item is ExportMethod2Detail => Boolean(item))
|
||||
|
||||
if (det.length === 0) return null
|
||||
if (det.length === 0 || !hasTotalValue) return null
|
||||
return {
|
||||
area: sumNumbers(det.map(item => item.area)),
|
||||
basicFee: sumNumbers(det.map(item => item.basicFee)),
|
||||
@@ -1026,6 +1127,7 @@ const buildMethod2 = (rows: ScaleMethodRowLike[] | undefined): ExportMethod2 | n
|
||||
|
||||
const buildMethod3 = (rows: WorkloadMethodRowLike[] | undefined): ExportMethod3 | null => {
|
||||
if (!Array.isArray(rows)) return null
|
||||
let hasTotalValue = false
|
||||
const det = rows
|
||||
.map(row => {
|
||||
const task = getTaskIdFromRowId(row.id)
|
||||
@@ -1033,6 +1135,7 @@ const buildMethod3 = (rows: WorkloadMethodRowLike[] | undefined): ExportMethod3
|
||||
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
|
||||
@@ -1048,7 +1151,7 @@ const buildMethod3 = (rows: WorkloadMethodRowLike[] | undefined): ExportMethod3
|
||||
})
|
||||
.filter((item): item is ExportMethod3Detail => Boolean(item))
|
||||
|
||||
if (det.length === 0) return null
|
||||
if (det.length === 0 || !hasTotalValue) return null
|
||||
return {
|
||||
basicFee: sumNumbers(det.map(item => item.basicFee)),
|
||||
fee: sumNumbers(det.map(item => item.fee)),
|
||||
@@ -1058,6 +1161,7 @@ const buildMethod3 = (rows: WorkloadMethodRowLike[] | undefined): ExportMethod3
|
||||
|
||||
const buildMethod4 = (rows: HourlyMethodRowLike[] | undefined): ExportMethod4 | null => {
|
||||
if (!Array.isArray(rows)) return null
|
||||
let hasTotalValue = false
|
||||
const det = rows
|
||||
.map(row => {
|
||||
const expert = getExpertIdFromRowId(row.id)
|
||||
@@ -1065,6 +1169,7 @@ const buildMethod4 = (rows: HourlyMethodRowLike[] | undefined): ExportMethod4 |
|
||||
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
|
||||
@@ -1079,7 +1184,7 @@ const buildMethod4 = (rows: HourlyMethodRowLike[] | undefined): ExportMethod4 |
|
||||
})
|
||||
.filter((item): item is ExportMethod4Detail => Boolean(item))
|
||||
|
||||
if (det.length === 0) return null
|
||||
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)),
|
||||
@@ -1089,26 +1194,182 @@ const buildMethod4 = (rows: HourlyMethodRowLike[] | undefined): ExportMethod4 |
|
||||
}
|
||||
|
||||
const buildServiceFee = (
|
||||
row: ZxFwRowLike,
|
||||
row: ZxFwRowLike | null | undefined,
|
||||
method1: ExportMethod1 | null,
|
||||
method2: ExportMethod2 | null,
|
||||
method3: ExportMethod3 | null,
|
||||
method4: ExportMethod4 | null
|
||||
) => {
|
||||
const subtotal = toFiniteNumber(row.subtotal)
|
||||
const subtotal = toFiniteNumber(row?.subtotal)
|
||||
if (subtotal != null) return subtotal
|
||||
|
||||
const methodSum = sumNumbers([method1?.fee, method2?.fee, method3?.fee, method4?.fee])
|
||||
if (methodSum !== 0) return methodSum
|
||||
|
||||
return sumNumbers([
|
||||
toFiniteNumber(row.investScale),
|
||||
toFiniteNumber(row.landScale),
|
||||
toFiniteNumber(row.workload),
|
||||
toFiniteNumber(row.hourly)
|
||||
toFiniteNumber(row?.investScale),
|
||||
toFiniteNumber(row?.landScale),
|
||||
toFiniteNumber(row?.workload),
|
||||
toFiniteNumber(row?.hourly)
|
||||
])
|
||||
}
|
||||
|
||||
const createRichTextCode = (...parts: string[]): ExportRichTextCode => ({
|
||||
richText: parts
|
||||
.map(item => String(item || '').trim())
|
||||
.filter(Boolean)
|
||||
.map(text => ({ text }))
|
||||
})
|
||||
|
||||
const buildMethod0 = (payload: RateMethodRowLike | null | undefined): ExportMethod0 | null => {
|
||||
const coe = toFiniteNumber(payload?.rate)
|
||||
const fee = toFiniteNumber(payload?.budgetFee)
|
||||
if (fee == null) return null
|
||||
return {
|
||||
coe: coe ?? 0,
|
||||
fee
|
||||
}
|
||||
}
|
||||
|
||||
const buildMethod5 = (rows: QuantityMethodRowLike[] | undefined): ExportMethod5 | null => {
|
||||
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 : ''
|
||||
const hasValue =
|
||||
quantity != null ||
|
||||
unitPrice != null ||
|
||||
fee != null ||
|
||||
isNonEmptyString(name) ||
|
||||
isNonEmptyString(unit) ||
|
||||
isNonEmptyString(remark)
|
||||
if (!hasValue) return null
|
||||
return {
|
||||
name,
|
||||
unit,
|
||||
amount: quantity ?? 0,
|
||||
price: unitPrice ?? 0,
|
||||
fee: fee ?? 0,
|
||||
remark
|
||||
}
|
||||
})
|
||||
.filter((item): item is ExportMethod5Detail => Boolean(item))
|
||||
|
||||
if (det.length === 0) return null
|
||||
return {
|
||||
fee: subtotalFee,
|
||||
det
|
||||
}
|
||||
}
|
||||
|
||||
const normalizeHtFeeMainRows = (rows: HtFeeMainRowLike[] | undefined) => {
|
||||
if (!Array.isArray(rows)) return [] as Array<{ id: string; name: string }>
|
||||
return rows
|
||||
.map(row => {
|
||||
const id = String(row?.id || '').trim()
|
||||
if (!id) return null
|
||||
return {
|
||||
id,
|
||||
name: typeof row?.name === 'string' ? row.name : ''
|
||||
}
|
||||
})
|
||||
.filter((item): item is { id: string; name: string } => Boolean(item))
|
||||
}
|
||||
|
||||
const loadHtFeeMethodsByRow = async (mainStorageKey: string, rowId: string) => {
|
||||
const [rateState, hourlyState, quantityState] = await Promise.all([
|
||||
zxFwPricingStore.loadHtFeeMethodState<RateMethodRowLike>(mainStorageKey, rowId, 'rate-fee'),
|
||||
zxFwPricingStore.loadHtFeeMethodState<DetailRowsStorageLike<HourlyMethodRowLike>>(mainStorageKey, rowId, 'hourly-fee'),
|
||||
zxFwPricingStore.loadHtFeeMethodState<DetailRowsStorageLike<QuantityMethodRowLike>>(mainStorageKey, rowId, 'quantity-unit-price-fee')
|
||||
])
|
||||
const m0 = buildMethod0(rateState)
|
||||
const m4 = buildMethod4(Array.isArray(hourlyState?.detailRows) ? hourlyState?.detailRows : undefined)
|
||||
const m5 = buildMethod5(Array.isArray(quantityState?.detailRows) ? quantityState?.detailRows : undefined)
|
||||
if (!m0 && !m4 && !m5) return null
|
||||
return {
|
||||
fee: sumNumbers([m0?.fee, m4?.fee, m5?.fee]),
|
||||
m0,
|
||||
m4,
|
||||
m5
|
||||
}
|
||||
}
|
||||
|
||||
const buildAdditionalDetailCode = (index: number, name: string): ExportRichTextCode => {
|
||||
const dictionaryIndex = additionalWorkList.findIndex(item => item === name)
|
||||
const useIndex = dictionaryIndex >= 0 ? dictionaryIndex : index
|
||||
const suffix = useIndex === 0 ? 'F' : useIndex === 1 ? 'X' : String(useIndex + 1)
|
||||
return createRichTextCode('C', suffix)
|
||||
}
|
||||
|
||||
const buildAdditionalExport = async (contractId: string): Promise<ExportAdditional | null> => {
|
||||
const storageKey = `htExtraFee-${contractId}-additional-work`
|
||||
const mainState = await zxFwPricingStore.loadHtFeeMainState<HtFeeMainRowLike>(storageKey)
|
||||
const rows = normalizeHtFeeMainRows(mainState?.detailRows)
|
||||
if (rows.length === 0) return null
|
||||
|
||||
const det = (
|
||||
await Promise.all(
|
||||
rows.map(async (row, index) => {
|
||||
const methodPayload = await loadHtFeeMethodsByRow(storageKey, row.id)
|
||||
if (!methodPayload) return null
|
||||
const item: ExportAdditionalDetail = {
|
||||
id: index,
|
||||
code: buildAdditionalDetailCode(index, row.name),
|
||||
name: row.name || `附加工作-${index + 1}`,
|
||||
fee: methodPayload.fee
|
||||
}
|
||||
item.ref = item.code
|
||||
if (methodPayload.m0) item.m0 = methodPayload.m0
|
||||
if (methodPayload.m4) item.m4 = methodPayload.m4
|
||||
if (methodPayload.m5) item.m5 = methodPayload.m5
|
||||
return item
|
||||
})
|
||||
)
|
||||
).filter((item): item is ExportAdditionalDetail => Boolean(item))
|
||||
|
||||
if (det.length === 0) return null
|
||||
const addtionalCode = createRichTextCode('C', 'C')
|
||||
return {
|
||||
code: addtionalCode,
|
||||
ref: addtionalCode,
|
||||
name: '附加工作',
|
||||
fee: sumNumbers(det.map(item => item.fee)),
|
||||
det
|
||||
}
|
||||
}
|
||||
|
||||
const buildReserveExport = async (contractId: string): Promise<ExportReserve | null> => {
|
||||
const storageKey = `htExtraFee-${contractId}-reserve`
|
||||
const mainState = await zxFwPricingStore.loadHtFeeMainState<HtFeeMainRowLike>(storageKey)
|
||||
const rows = normalizeHtFeeMainRows(mainState?.detailRows)
|
||||
if (rows.length === 0) return null
|
||||
|
||||
for (const row of rows) {
|
||||
const methodPayload = await loadHtFeeMethodsByRow(storageKey, row.id)
|
||||
if (!methodPayload) continue
|
||||
const reserve: ExportReserve = {
|
||||
code: createRichTextCode('Y', 'B'),
|
||||
name: row.name || '预备费',
|
||||
fee: methodPayload.fee
|
||||
}
|
||||
reserve.ref = reserve.code
|
||||
if (methodPayload.m0) reserve.m0 = methodPayload.m0
|
||||
if (methodPayload.m4) reserve.m4 = methodPayload.m4
|
||||
if (methodPayload.m5) reserve.m5 = methodPayload.m5
|
||||
return reserve
|
||||
}
|
||||
return null
|
||||
}
|
||||
|
||||
const buildExportReportPayload = async (): Promise<ExportReportPayload> => {
|
||||
const [projectInfoRaw, projectScaleRaw, consultCategoryFactorRaw, majorFactorRaw, contractCardsRaw] = await Promise.all([
|
||||
kvStore.getItem<XmInfoLike>(PROJECT_INFO_DB_KEY),
|
||||
@@ -1120,8 +1381,14 @@ const buildExportReportPayload = async (): Promise<ExportReportPayload> => {
|
||||
|
||||
const projectInfo = projectInfoRaw || {}
|
||||
const projectScaleSource = projectScaleRaw || {}
|
||||
const projectScale = buildScaleRows(projectScaleSource.detailRows)
|
||||
const projectScaleCost = sumNumbers(projectScale.map(item => item.cost))
|
||||
|
||||
const projectScale = projectScaleSource.roughCalcEnabled ? [] : toExportScaleRows(projectScaleSource.detailRows)
|
||||
const projectScaleCost = toFiniteNumber(projectScaleSource.totalAmount) ?? sumNumbers(projectScale.map(item => item.cost))
|
||||
projectScale.push({
|
||||
major: -1, cost: projectScaleCost,
|
||||
area: null
|
||||
})
|
||||
|
||||
const projectServiceCoes = buildProjectServiceCoes(consultCategoryFactorRaw?.detailRows)
|
||||
const projectMajorCoes = buildProjectMajorCoes(majorFactorRaw?.detailRows)
|
||||
const projectName = isNonEmptyString(projectInfo.projectName) ? projectInfo.projectName.trim() : '造价项目'
|
||||
@@ -1139,21 +1406,59 @@ const buildExportReportPayload = async (): Promise<ExportReportPayload> => {
|
||||
for (let index = 0; index < contractCards.length; index++) {
|
||||
const contract = contractCards[index]
|
||||
const contractId = contract.id
|
||||
const [htInfoRaw, zxFwRaw] = await Promise.all([
|
||||
await zxFwPricingStore.loadContract(contractId)
|
||||
const [htInfoRaw, zxFwRaw, htConsultCategoryFactorRaw, htMajorFactorRaw] = await Promise.all([
|
||||
kvStore.getItem<DetailRowsStorageLike<ScaleRowLike>>(`ht-info-v3-${contractId}`),
|
||||
kvStore.getItem<ZxFwStorageLike>(`zxFW-${contractId}`)
|
||||
kvStore.getItem<ZxFwStorageLike>(`zxFW-${contractId}`),
|
||||
kvStore.getItem<DetailRowsStorageLike<FactorRowLike>>(`ht-consult-category-factor-v1-${contractId}`),
|
||||
kvStore.getItem<DetailRowsStorageLike<FactorRowLike>>(`ht-major-factor-v1-${contractId}`)
|
||||
])
|
||||
|
||||
const zxRows = Array.isArray(zxFwRaw?.detailRows) ? zxFwRaw.detailRows : []
|
||||
const fixedRow = zxRows.find(row => row.id === 'fixed-budget-c')
|
||||
const serviceRows = zxRows.filter(row => row.id !== 'fixed-budget-c' && hasServiceId(String(row.id)))
|
||||
const contractState = zxFwPricingStore.getContractState(contractId)
|
||||
const zxRowsFromStore: ZxFwRowLike[] = Array.isArray(contractState?.detailRows)
|
||||
? contractState.detailRows.map(row => ({
|
||||
id: String(row.id || ''),
|
||||
process: row.process,
|
||||
subtotal: row.subtotal,
|
||||
investScale: row.investScale,
|
||||
landScale: row.landScale,
|
||||
workload: row.workload,
|
||||
hourly: row.hourly
|
||||
}))
|
||||
: []
|
||||
const zxRowsFromKv = Array.isArray(zxFwRaw?.detailRows) ? zxFwRaw.detailRows : []
|
||||
const zxRows = zxRowsFromStore.length > 0 ? zxRowsFromStore : zxRowsFromKv
|
||||
const selectedIdsFromStore = Array.isArray(contractState?.selectedIds)
|
||||
? contractState.selectedIds.map(id => String(id || '').trim()).filter(Boolean)
|
||||
: []
|
||||
const selectedIdsFromKv = Array.isArray(zxFwRaw?.selectedIds)
|
||||
? zxFwRaw.selectedIds.map(id => String(id || '').trim()).filter(Boolean)
|
||||
: []
|
||||
const selectedIds = Array.from(new Set([...selectedIdsFromStore, ...selectedIdsFromKv])).filter(hasServiceId)
|
||||
|
||||
let fixedRow: ZxFwRowLike | undefined
|
||||
const serviceRowMap = new Map<string, ZxFwRowLike>()
|
||||
for (const row of zxRows) {
|
||||
const rowId = String(row?.id || '').trim()
|
||||
if (!rowId) continue
|
||||
if (rowId === 'fixed-budget-c') {
|
||||
fixedRow = row
|
||||
continue
|
||||
}
|
||||
if (!hasServiceId(rowId)) continue
|
||||
serviceRowMap.set(rowId, row)
|
||||
}
|
||||
const fallbackServiceIds = Array.from(serviceRowMap.keys())
|
||||
const serviceIdTexts = sortServiceIdsByDict(
|
||||
(selectedIds.length > 0 ? selectedIds : fallbackServiceIds).filter(hasServiceId)
|
||||
)
|
||||
|
||||
const services = (
|
||||
await Promise.all(
|
||||
serviceRows.map(async row => {
|
||||
const serviceIdText = String(row.id)
|
||||
serviceIdTexts.map(async serviceIdText => {
|
||||
const serviceId = toSafeInteger(serviceIdText)
|
||||
if (serviceId == null) return null
|
||||
const sourceRow = serviceRowMap.get(serviceIdText)
|
||||
|
||||
const [method1State, method2State, method3State, method4State] = await Promise.all([
|
||||
zxFwPricingStore.loadServicePricingMethodState<ScaleMethodRowLike>(contractId, serviceIdText, 'investScale'),
|
||||
@@ -1170,11 +1475,11 @@ const buildExportReportPayload = async (): Promise<ExportReportPayload> => {
|
||||
const method2 = buildMethod2(method2Raw?.detailRows)
|
||||
const method3 = buildMethod3(method3Raw?.detailRows)
|
||||
const method4 = buildMethod4(method4Raw?.detailRows)
|
||||
const fee = buildServiceFee(row, method1, method2, method3, method4)
|
||||
|
||||
const fee = buildServiceFee(sourceRow, method1, method2, method3, method4)
|
||||
const process = Number(sourceRow?.process) === 1 ? 1 : 0
|
||||
const service: ExportService = {
|
||||
id: serviceId,
|
||||
process: 0,
|
||||
process,
|
||||
fee
|
||||
}
|
||||
if (method1) service.method1 = method1
|
||||
@@ -1195,9 +1500,21 @@ const buildExportReportPayload = async (): Promise<ExportReportPayload> => {
|
||||
toFiniteNumber(fixedRow?.hourly)
|
||||
])
|
||||
const serviceFee = fixedSubtotal ?? (serviceFeeSum !== 0 ? serviceFeeSum : fixedMethodSum)
|
||||
const addtionalFee = 0
|
||||
const reserveFee = 0
|
||||
const contractFee = serviceFee + addtionalFee + reserveFee
|
||||
const [addtional, reserve] = await Promise.all([
|
||||
buildAdditionalExport(contractId),
|
||||
buildReserveExport(contractId)
|
||||
])
|
||||
const addtionalFee = addtional ? addtional.fee : 0
|
||||
const reserveFee = reserve ? reserve.fee : 0
|
||||
const contractFee = roundTo(addNumbers(serviceFee, addtionalFee, reserveFee), 3)
|
||||
const contractScale = htInfoRaw?.roughCalcEnabled ? [] : toExportScaleRows(htInfoRaw?.detailRows)
|
||||
contractScale.push({
|
||||
major: -1, cost: contractFee,
|
||||
area: null
|
||||
})
|
||||
const contractServiceCoesRaw = buildProjectServiceCoes(htConsultCategoryFactorRaw?.detailRows)
|
||||
const contractMajorCoesRaw = buildProjectMajorCoes(htMajorFactorRaw?.detailRows)
|
||||
|
||||
|
||||
contracts.push({
|
||||
name: isNonEmptyString(contract.name) ? contract.name : `合同段-${index + 1}`,
|
||||
@@ -1205,12 +1522,12 @@ const buildExportReportPayload = async (): Promise<ExportReportPayload> => {
|
||||
addtionalFee,
|
||||
reserveFee,
|
||||
fee: contractFee,
|
||||
scale: buildScaleRows(htInfoRaw?.detailRows),
|
||||
serviceCoes: projectServiceCoes.map(item => ({ ...item })),
|
||||
majorCoes: projectMajorCoes.map(item => ({ ...item })),
|
||||
scale: contractScale,
|
||||
serviceCoes: contractServiceCoesRaw,
|
||||
majorCoes: contractMajorCoesRaw,
|
||||
services,
|
||||
addtional: [],
|
||||
reserve: []
|
||||
addtional,
|
||||
reserve
|
||||
})
|
||||
}
|
||||
|
||||
@@ -1273,11 +1590,12 @@ const exportReport = async () => {
|
||||
try {
|
||||
const now = new Date()
|
||||
const payload = await buildExportReportPayload()
|
||||
console.log(payload)
|
||||
const fileName = `${sanitizeFileNamePart(payload.name)}-报表-${formatExportTimestamp(now)}`
|
||||
await exportFile(fileName, payload)
|
||||
} catch (error) {
|
||||
console.error('export report failed:', error)
|
||||
window.alert('导出报表失败,请重试。')
|
||||
// window.alert('导出报表失败,请重试。')
|
||||
} finally {
|
||||
dataMenuOpen.value = false
|
||||
}
|
||||
|
||||
Reference in new issue
Block a user