feat: 工作台概览优化 - 人力成本展示、解聘记录完善、数据准确性修复

- 解聘列表显示所有状态记录(含已撤销/已完成)
- 月度解聘人数排除CANCELLED和DRAFT状态
- 工作台新增当月人力成本和年度累计成本卡片
- 年度累计成本包含当前月未归档数据
- 企业总成本公式加入加班费
- 统计卡片月加班费改为工资条数量,避免与动态区重复
- 本月工作动态与风险分布改为左右两列布局
- 人力成本区域优化:2列网格+突出总成本
This commit is contained in:
selfrelease
2026-07-25 23:11:38 +08:00
parent f74b2808a3
commit 9cb0d1f63b
4 changed files with 127 additions and 9 deletions
+46 -4
View File
@@ -278,12 +278,15 @@ export async function getDashboardData(orgId: string) {
const currentMonth = `${now.getFullYear()}-${String(now.getMonth() + 1).padStart(2, '0')}`
const monthStart = new Date(now.getFullYear(), now.getMonth(), 1)
const monthEnd = new Date(now.getFullYear(), now.getMonth() + 1, 0, 23, 59, 59)
const yearStart = new Date(now.getFullYear(), 0, 1)
const yearEnd = new Date(now.getFullYear(), 11, 31, 23, 59, 59)
const [
employeeCount, highRisks, pendingRisks, riskItems, resolvedItems,
overtimeRecords, payslips, batchEntries, socialConfig, housingConfig,
monthContracts, monthTerminations, monthDisciplinary, monthAttendance,
monthSeverancePay,
yearBatchEntries, yearOvertimeRecords, yearSeverancePay,
] = await Promise.all([
prisma.employee.count({ where: { orgId, status: 'ACTIVE' } }),
prisma.riskItem.count({ where: { orgId, status: 'PENDING', level: 'HIGH', type: { in: ['CONTRACT', 'TERMINATION'] } } }),
@@ -319,7 +322,7 @@ export async function getDashboardData(orgId: string) {
where: { orgId, createdAt: { gte: monthStart, lte: monthEnd } },
}),
prisma.terminationRecord.count({
where: { orgId, createdAt: { gte: monthStart, lte: monthEnd } },
where: { orgId, createdAt: { gte: monthStart, lte: monthEnd }, status: { notIn: ['CANCELLED', 'DRAFT'] } },
}),
prisma.disciplinaryRecord.count({
where: { orgId, violationDate: { gte: monthStart, lte: monthEnd } },
@@ -328,7 +331,22 @@ export async function getDashboardData(orgId: string) {
where: { orgId, date: { gte: monthStart, lte: monthEnd } },
}),
prisma.terminationRecord.aggregate({
where: { orgId, createdAt: { gte: monthStart, lte: monthEnd } },
where: { orgId, createdAt: { gte: monthStart, lte: monthEnd }, status: { notIn: ['CANCELLED', 'DRAFT'] } },
_sum: { compensation: true },
}),
// 年度累计:已归档批次条目
prisma.batchEntry.findMany({
where: { orgId, batch: { month: { startsWith: `${now.getFullYear()}-` }, status: 'ARCHIVED' } },
select: { baseSalary: true, overtimePay: true, allowance: true, deduction: true, bonus: true, totalPay: true, socialEmp: true, socialOrg: true, housingEmp: true, housingOrg: true, tax: true, netPay: true, employeeId: true },
}),
// 年度累计:加班记录
prisma.overtimeRecord.findMany({
where: { orgId, month: { startsWith: `${now.getFullYear()}-` } },
select: { totalPay: true, weekdayHours: true, weekendHours: true, holidayHours: true },
}),
// 年度累计:补偿金
prisma.terminationRecord.aggregate({
where: { orgId, createdAt: { gte: yearStart, lte: yearEnd }, status: { notIn: ['CANCELLED', 'DRAFT'] } },
_sum: { compensation: true },
}),
])
@@ -444,8 +462,8 @@ export async function getDashboardData(orgId: string) {
housingEmp: housingEmpTotal,
estimatedTax,
severancePay: monthSeverancePay._sum.compensation || 0,
// 企业总成本 = 工资总额 + 企业社保 + 企业公积金 + 经济补偿金
orgTotalCost: totalPay + socialOrgTotal + housingOrgTotal + (monthSeverancePay._sum.compensation || 0),
// 企业总成本 = 工资总额 + 企业社保 + 企业公积金 + 加班费 + 经济补偿金
orgTotalCost: totalPay + socialOrgTotal + housingOrgTotal + monthlyOvertimePay + (monthSeverancePay._sum.compensation || 0),
// 员工实发 = 工资总额 - 个人社保 - 个人公积金 - 个税
empNetPay: useArchivedData ? totalNetPay : totalPay - socialEmpTotal - housingEmpTotal - estimatedTax,
}
@@ -461,6 +479,29 @@ export async function getDashboardData(orgId: string) {
overtimePay: monthlyOvertimePay,
}
// 年度累计人力资源成本 = 已归档批次汇总 + 当前月数据(当前月可能未归档)
const yearArchivedPay = yearBatchEntries.reduce((s: number, e: typeof yearBatchEntries[number]) => s + e.totalPay, 0)
const yearArchivedSocialOrg = yearBatchEntries.reduce((s: number, e: typeof yearBatchEntries[number]) => s + e.socialOrg, 0)
const yearArchivedHousingOrg = yearBatchEntries.reduce((s: number, e: typeof yearBatchEntries[number]) => s + e.housingOrg, 0)
const yearOvertimePay = yearOvertimeRecords.reduce((s: number, r: typeof yearOvertimeRecords[number]) => s + r.totalPay, 0)
const yearSeverance = yearSeverancePay._sum.compensation || 0
// 当前月是否已包含在归档批次中
const currentMonthArchived = batchEntries.length > 0
const yearTotalPay = yearArchivedPay + (currentMonthArchived ? 0 : totalPay)
const yearSocialOrg = yearArchivedSocialOrg + (currentMonthArchived ? 0 : socialOrgTotal)
const yearHousingOrg = yearArchivedHousingOrg + (currentMonthArchived ? 0 : housingOrgTotal)
const yearCostSummary = {
year: now.getFullYear().toString(),
totalPay: yearTotalPay,
socialOrg: yearSocialOrg,
housingOrg: yearHousingOrg,
overtimePay: yearOvertimePay,
severancePay: yearSeverance,
orgTotalCost: yearTotalPay + yearSocialOrg + yearHousingOrg + yearOvertimePay + yearSeverance,
}
const riskDistribution = {
contract: riskItems.filter((r: typeof riskItems[number]) => r.type === 'CONTRACT').length,
salary: riskItems.filter((r: typeof riskItems[number]) => r.type === 'SALARY').length,
@@ -521,5 +562,6 @@ export async function getDashboardData(orgId: string) {
aiPrediction: null,
payrollSummary,
monthlyActivities,
yearCostSummary,
}
}
+1 -1
View File
@@ -743,7 +743,7 @@ export async function getDrafts(orgId: string, status?: string) {
if (status) {
where.status = status
} else {
where.status = { in: ['DRAFT', 'PENDING_APPROVAL', 'APPROVED', 'REJECTED'] }
where.status = { in: ['DRAFT', 'PENDING_APPROVAL', 'APPROVED', 'REJECTED', 'EXECUTING', 'COMPLETED', 'CANCELLED'] }
}
const records = await prisma.terminationRecord.findMany({