fix: 人力成本分析按员工去重,统一与Dashboard口径;环比/同比无数据时显示暂无数据

This commit is contained in:
selfrelease
2026-07-31 14:24:37 +08:00
parent 9df147ce59
commit c15e11ec22
3 changed files with 577 additions and 31 deletions
+45 -17
View File
@@ -1116,18 +1116,34 @@ export async function getCostAnalysis(orgId: string, month: string) {
const lastYearMonthEnd = new Date(year - 1, monthNum, 0, 23, 59, 59)
const lastYearMonthStr = `${lastYearMonthStart.getFullYear()}-${String(lastYearMonthStart.getMonth() + 1).padStart(2, '0')}`
// 获取各月归档批次汇总
// 获取各月归档批次汇总(按员工去重,与 getDashboardData 口径一致)
const getMonthCost = async (m: string) => {
const batches = await prisma.payrollBatch.findMany({
where: { orgId, month: m, status: 'ARCHIVED' },
const entries = await prisma.batchEntry.findMany({
where: { orgId, batch: { month: m, status: 'ARCHIVED' } },
select: { employeeId: true, totalPay: true, socialOrg: true, housingOrg: true, tax: true },
})
return batches.reduce((acc, b) => ({
totalPay: acc.totalPay + b.totalPay,
totalSocialOrg: acc.totalSocialOrg + b.totalSocialOrg,
totalHousingOrg: acc.totalHousingOrg + b.totalHousingOrg,
totalTax: acc.totalTax + b.totalTax,
employeeCount: acc.employeeCount + b.employeeCount,
}), { totalPay: 0, totalSocialOrg: 0, totalHousingOrg: 0, totalTax: 0, employeeCount: 0 })
// 同一员工多批次只取一条(取最后一条,金额通常已合并)
const empMap = new Map<string, { totalPay: number; socialOrg: number; housingOrg: number; tax: number }>()
for (const e of entries) {
const ex = empMap.get(e.employeeId)
if (!ex) {
empMap.set(e.employeeId, { totalPay: e.totalPay, socialOrg: e.socialOrg, housingOrg: e.housingOrg, tax: e.tax })
} else {
// 同一员工多批次:累加金额
ex.totalPay += e.totalPay
ex.socialOrg += e.socialOrg
ex.housingOrg += e.housingOrg
ex.tax += e.tax
}
}
const deduped = Array.from(empMap.values())
return {
totalPay: deduped.reduce((s, e) => s + e.totalPay, 0),
totalSocialOrg: deduped.reduce((s, e) => s + e.socialOrg, 0),
totalHousingOrg: deduped.reduce((s, e) => s + e.housingOrg, 0),
totalTax: deduped.reduce((s, e) => s + e.tax, 0),
employeeCount: deduped.length,
}
}
const current = await getMonthCost(month)
@@ -1183,7 +1199,7 @@ export async function getCostAnalysis(orgId: string, month: string) {
})
}
// 按部门拆分成本
// 按部门拆分成本(按员工去重)
const deptEntries = await prisma.batchEntry.findMany({
where: {
orgId,
@@ -1191,14 +1207,26 @@ export async function getCostAnalysis(orgId: string, month: string) {
},
include: { employee: { select: { department: true } } },
})
const deptMap: Record<string, { totalPay: number; socialOrg: number; housingOrg: number; headcount: number }> = {}
// 先按员工去重,同一员工多批次累加金额
const deptEmpMap = new Map<string, { dept: string; totalPay: number; socialOrg: number; housingOrg: number }>()
for (const e of deptEntries) {
const dept = e.employee?.department || '未分配'
if (!deptMap[dept]) deptMap[dept] = { totalPay: 0, socialOrg: 0, housingOrg: 0, headcount: 0 }
deptMap[dept].totalPay += e.totalPay
deptMap[dept].socialOrg += e.socialOrg
deptMap[dept].housingOrg += e.housingOrg
deptMap[dept].headcount += 1
const ex = deptEmpMap.get(e.employeeId)
if (!ex) {
deptEmpMap.set(e.employeeId, { dept, totalPay: e.totalPay, socialOrg: e.socialOrg, housingOrg: e.housingOrg })
} else {
ex.totalPay += e.totalPay
ex.socialOrg += e.socialOrg
ex.housingOrg += e.housingOrg
}
}
const deptMap: Record<string, { totalPay: number; socialOrg: number; housingOrg: number; headcount: number }> = {}
for (const [, v] of deptEmpMap) {
if (!deptMap[v.dept]) deptMap[v.dept] = { totalPay: 0, socialOrg: 0, housingOrg: 0, headcount: 0 }
deptMap[v.dept].totalPay += v.totalPay
deptMap[v.dept].socialOrg += v.socialOrg
deptMap[v.dept].housingOrg += v.housingOrg
deptMap[v.dept].headcount += 1
}
const departmentCost = Object.entries(deptMap)
.map(([dept, v]) => ({