fix: 批量获取绩效/奖金/扣款后重算应发和实发
根因:获取绩效工资/提成奖金/违纪扣款/考勤扣款等批量操作 只更新了单个字段(performanceSalary/bonus/deduction),没有重算 entry 的 totalPay/tax/netPay,导致应发合计不包含新增的绩效工资 修复: - 新增 recalcEntry 辅助函数:更新字段后调用 calcBatchEntry 重算 - 新增 recalcBatchTotals 辅助函数:重算批次汇总 - 四个批量操作接口(获取绩效/奖金/违纪扣款/考勤扣款)都改为 更新后调用 recalcEntry + recalcBatchTotals
This commit is contained in:
@@ -55,6 +55,69 @@ async function getPrevDeferred(orgId: string, employeeId: string, currentMonth:
|
|||||||
return result
|
return result
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 重算单个 BatchEntry 并更新到数据库
|
||||||
|
* 用于批量操作(获取绩效/奖金/扣款等)后重算 totalPay/tax/netPay
|
||||||
|
*/
|
||||||
|
async function recalcEntry(orgId: string, batchId: string, batchMonth: string, batchType: string, entryId: string, employeeId: string) {
|
||||||
|
const entry = await prisma.batchEntry.findUnique({ where: { id: entryId } })
|
||||||
|
if (!entry) return
|
||||||
|
|
||||||
|
const inputs = {
|
||||||
|
baseSalary: entry.baseSalary,
|
||||||
|
performanceSalary: entry.performanceSalary || undefined,
|
||||||
|
overtimePay: entry.overtimePay,
|
||||||
|
allowance: entry.allowance,
|
||||||
|
deduction: entry.deduction,
|
||||||
|
bonus: entry.bonus,
|
||||||
|
positionSalary: entry.positionSalary || undefined,
|
||||||
|
senioritySalary: entry.senioritySalary || undefined,
|
||||||
|
transportAllowance: entry.transportAllowance || undefined,
|
||||||
|
mealAllowance: entry.mealAllowance || undefined,
|
||||||
|
housingAllowance: entry.housingAllowance || undefined,
|
||||||
|
communicationAllowance: entry.communicationAllowance || undefined,
|
||||||
|
otherDeduction: entry.otherDeduction || undefined,
|
||||||
|
}
|
||||||
|
|
||||||
|
const prevDeferred = await getPrevDeferred(orgId, employeeId, batchMonth)
|
||||||
|
const calcResult = await calcBatchEntry(orgId, employeeId, batchMonth, inputs, batchType as any, { prevDeferred })
|
||||||
|
|
||||||
|
const { systemSocialEmp, systemSocialOrg, systemHousingEmp, systemHousingOrg, systemSupplementaryHousingEmp, systemSupplementaryHousingOrg, ...entryData } = calcResult
|
||||||
|
await prisma.batchEntry.update({
|
||||||
|
where: { id: entryId },
|
||||||
|
data: entryData,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 重算批次汇总
|
||||||
|
*/
|
||||||
|
async function recalcBatchTotals(batchId: string) {
|
||||||
|
const allEntries = await prisma.batchEntry.findMany({ where: { batchId } })
|
||||||
|
const totals = allEntries.reduce((acc, e) => ({
|
||||||
|
totalPay: acc.totalPay + e.totalPay,
|
||||||
|
totalNetPay: acc.totalNetPay + e.netPay,
|
||||||
|
totalSocialOrg: acc.totalSocialOrg + e.socialOrg,
|
||||||
|
totalSocialEmp: acc.totalSocialEmp + e.socialEmp,
|
||||||
|
totalHousingOrg: acc.totalHousingOrg + e.housingOrg,
|
||||||
|
totalHousingEmp: acc.totalHousingEmp + e.housingEmp,
|
||||||
|
totalTax: acc.totalTax + e.tax,
|
||||||
|
}), { totalPay: 0, totalNetPay: 0, totalSocialOrg: 0, totalSocialEmp: 0, totalHousingOrg: 0, totalHousingEmp: 0, totalTax: 0 })
|
||||||
|
|
||||||
|
await prisma.payrollBatch.update({
|
||||||
|
where: { id: batchId },
|
||||||
|
data: {
|
||||||
|
totalPay: Math.round(totals.totalPay * 100) / 100,
|
||||||
|
totalNetPay: Math.round(totals.totalNetPay * 100) / 100,
|
||||||
|
totalSocialOrg: Math.round(totals.totalSocialOrg * 100) / 100,
|
||||||
|
totalSocialEmp: Math.round(totals.totalSocialEmp * 100) / 100,
|
||||||
|
totalHousingOrg: Math.round(totals.totalHousingOrg * 100) / 100,
|
||||||
|
totalHousingEmp: Math.round(totals.totalHousingEmp * 100) / 100,
|
||||||
|
totalTax: Math.round(totals.totalTax * 100) / 100,
|
||||||
|
},
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
// RFC 5987 编码中文文件名
|
// RFC 5987 编码中文文件名
|
||||||
function contentDisposition(filename: string): string {
|
function contentDisposition(filename: string): string {
|
||||||
const encoded = encodeURIComponent(filename)
|
const encoded = encodeURIComponent(filename)
|
||||||
@@ -704,35 +767,14 @@ router.post('/batches/:batchId/fetch-bonus', async (req: AuthRequest, res: Respo
|
|||||||
where: { id: entry.id },
|
where: { id: entry.id },
|
||||||
data: { bonus: bonus.amount },
|
data: { bonus: bonus.amount },
|
||||||
})
|
})
|
||||||
|
await recalcEntry(orgId, batchId, batch.month, batch.type, entry.id, entry.employeeId)
|
||||||
filled++
|
filled++
|
||||||
totalAmount += bonus.amount
|
totalAmount += bonus.amount
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// 重算批次汇总
|
// 重算批次汇总
|
||||||
const allEntries = await prisma.batchEntry.findMany({ where: { batchId } })
|
await recalcBatchTotals(batchId)
|
||||||
const totals = allEntries.reduce((acc, e) => ({
|
|
||||||
totalPay: acc.totalPay + e.baseSalary + e.overtimePay + e.allowance + e.bonus - e.deduction,
|
|
||||||
totalNetPay: acc.totalNetPay + e.netPay,
|
|
||||||
totalSocialOrg: acc.totalSocialOrg + e.socialOrg,
|
|
||||||
totalSocialEmp: acc.totalSocialEmp + e.socialEmp,
|
|
||||||
totalHousingOrg: acc.totalHousingOrg + e.housingOrg,
|
|
||||||
totalHousingEmp: acc.totalHousingEmp + e.housingEmp,
|
|
||||||
totalTax: acc.totalTax + e.tax,
|
|
||||||
}), { totalPay: 0, totalNetPay: 0, totalSocialOrg: 0, totalSocialEmp: 0, totalHousingOrg: 0, totalHousingEmp: 0, totalTax: 0 })
|
|
||||||
|
|
||||||
await prisma.payrollBatch.update({
|
|
||||||
where: { id: batchId },
|
|
||||||
data: {
|
|
||||||
totalPay: Math.round(totals.totalPay * 100) / 100,
|
|
||||||
totalNetPay: Math.round(totals.totalNetPay * 100) / 100,
|
|
||||||
totalSocialOrg: Math.round(totals.totalSocialOrg * 100) / 100,
|
|
||||||
totalSocialEmp: Math.round(totals.totalSocialEmp * 100) / 100,
|
|
||||||
totalHousingOrg: Math.round(totals.totalHousingOrg * 100) / 100,
|
|
||||||
totalHousingEmp: Math.round(totals.totalHousingEmp * 100) / 100,
|
|
||||||
totalTax: Math.round(totals.totalTax * 100) / 100,
|
|
||||||
},
|
|
||||||
})
|
|
||||||
|
|
||||||
res.json({
|
res.json({
|
||||||
success: true,
|
success: true,
|
||||||
@@ -810,34 +852,13 @@ router.post('/batches/:batchId/fetch-performance', async (req: AuthRequest, res:
|
|||||||
where: { id: entry.id },
|
where: { id: entry.id },
|
||||||
data: { performanceSalary: calculatedPerfSalary },
|
data: { performanceSalary: calculatedPerfSalary },
|
||||||
})
|
})
|
||||||
|
await recalcEntry(orgId, batchId, batch.month, batch.type, entry.id, entry.employeeId)
|
||||||
filled++
|
filled++
|
||||||
totalAmount += calculatedPerfSalary
|
totalAmount += calculatedPerfSalary
|
||||||
}
|
}
|
||||||
|
|
||||||
// 重算批次汇总
|
// 重算批次汇总
|
||||||
const allEntries = await prisma.batchEntry.findMany({ where: { batchId } })
|
await recalcBatchTotals(batchId)
|
||||||
const totals = allEntries.reduce((acc, e) => ({
|
|
||||||
totalPay: acc.totalPay + e.baseSalary + (e.performanceSalary || 0) + (e.positionSalary || 0) + (e.senioritySalary || 0) + e.overtimePay + (e.transportAllowance || 0) + (e.mealAllowance || 0) + (e.housingAllowance || 0) + (e.communicationAllowance || 0) + e.allowance + e.bonus - e.deduction - (e.otherDeduction || 0),
|
|
||||||
totalNetPay: acc.totalNetPay + e.netPay,
|
|
||||||
totalSocialOrg: acc.totalSocialOrg + e.socialOrg,
|
|
||||||
totalSocialEmp: acc.totalSocialEmp + e.socialEmp,
|
|
||||||
totalHousingOrg: acc.totalHousingOrg + e.housingOrg,
|
|
||||||
totalHousingEmp: acc.totalHousingEmp + e.housingEmp,
|
|
||||||
totalTax: acc.totalTax + e.tax,
|
|
||||||
}), { totalPay: 0, totalNetPay: 0, totalSocialOrg: 0, totalSocialEmp: 0, totalHousingOrg: 0, totalHousingEmp: 0, totalTax: 0 })
|
|
||||||
|
|
||||||
await prisma.payrollBatch.update({
|
|
||||||
where: { id: batchId },
|
|
||||||
data: {
|
|
||||||
totalPay: Math.round(totals.totalPay * 100) / 100,
|
|
||||||
totalNetPay: Math.round(totals.totalNetPay * 100) / 100,
|
|
||||||
totalSocialOrg: Math.round(totals.totalSocialOrg * 100) / 100,
|
|
||||||
totalSocialEmp: Math.round(totals.totalSocialEmp * 100) / 100,
|
|
||||||
totalHousingOrg: Math.round(totals.totalHousingOrg * 100) / 100,
|
|
||||||
totalHousingEmp: Math.round(totals.totalHousingEmp * 100) / 100,
|
|
||||||
totalTax: Math.round(totals.totalTax * 100) / 100,
|
|
||||||
},
|
|
||||||
})
|
|
||||||
|
|
||||||
res.json({
|
res.json({
|
||||||
success: true,
|
success: true,
|
||||||
@@ -905,35 +926,14 @@ router.post('/batches/:batchId/fetch-disciplinary', async (req: AuthRequest, res
|
|||||||
where: { id: entry.id },
|
where: { id: entry.id },
|
||||||
data: { deduction: Math.round(deduction * 100) / 100 },
|
data: { deduction: Math.round(deduction * 100) / 100 },
|
||||||
})
|
})
|
||||||
|
await recalcEntry(orgId, batchId, batch.month, batch.type, entry.id, entry.employeeId)
|
||||||
filled++
|
filled++
|
||||||
totalAmount += deduction
|
totalAmount += deduction
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// 重算批次汇总
|
// 重算批次汇总
|
||||||
const allEntries = await prisma.batchEntry.findMany({ where: { batchId } })
|
await recalcBatchTotals(batchId)
|
||||||
const totals = allEntries.reduce((acc, e) => ({
|
|
||||||
totalPay: acc.totalPay + e.baseSalary + (e.performanceSalary || 0) + (e.positionSalary || 0) + (e.senioritySalary || 0) + e.overtimePay + (e.transportAllowance || 0) + (e.mealAllowance || 0) + (e.housingAllowance || 0) + (e.communicationAllowance || 0) + e.allowance + e.bonus - e.deduction - (e.otherDeduction || 0),
|
|
||||||
totalNetPay: acc.totalNetPay + e.netPay,
|
|
||||||
totalSocialOrg: acc.totalSocialOrg + e.socialOrg,
|
|
||||||
totalSocialEmp: acc.totalSocialEmp + e.socialEmp,
|
|
||||||
totalHousingOrg: acc.totalHousingOrg + e.housingOrg,
|
|
||||||
totalHousingEmp: acc.totalHousingEmp + e.housingEmp,
|
|
||||||
totalTax: acc.totalTax + e.tax,
|
|
||||||
}), { totalPay: 0, totalNetPay: 0, totalSocialOrg: 0, totalSocialEmp: 0, totalHousingOrg: 0, totalHousingEmp: 0, totalTax: 0 })
|
|
||||||
|
|
||||||
await prisma.payrollBatch.update({
|
|
||||||
where: { id: batchId },
|
|
||||||
data: {
|
|
||||||
totalPay: Math.round(totals.totalPay * 100) / 100,
|
|
||||||
totalNetPay: Math.round(totals.totalNetPay * 100) / 100,
|
|
||||||
totalSocialOrg: Math.round(totals.totalSocialOrg * 100) / 100,
|
|
||||||
totalSocialEmp: Math.round(totals.totalSocialEmp * 100) / 100,
|
|
||||||
totalHousingOrg: Math.round(totals.totalHousingOrg * 100) / 100,
|
|
||||||
totalHousingEmp: Math.round(totals.totalHousingEmp * 100) / 100,
|
|
||||||
totalTax: Math.round(totals.totalTax * 100) / 100,
|
|
||||||
},
|
|
||||||
})
|
|
||||||
|
|
||||||
res.json({
|
res.json({
|
||||||
success: true,
|
success: true,
|
||||||
@@ -995,35 +995,14 @@ router.post('/batches/:batchId/fetch-attendance-deduction', async (req: AuthRequ
|
|||||||
where: { id: entry.id },
|
where: { id: entry.id },
|
||||||
data: { deduction: Math.round(deduction * 100) / 100 },
|
data: { deduction: Math.round(deduction * 100) / 100 },
|
||||||
})
|
})
|
||||||
|
await recalcEntry(orgId, batchId, batch.month, batch.type, entry.id, entry.employeeId)
|
||||||
filled++
|
filled++
|
||||||
totalAmount += deduction
|
totalAmount += deduction
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// 重算批次汇总
|
// 重算批次汇总
|
||||||
const allEntries = await prisma.batchEntry.findMany({ where: { batchId } })
|
await recalcBatchTotals(batchId)
|
||||||
const totals = allEntries.reduce((acc, e) => ({
|
|
||||||
totalPay: acc.totalPay + e.baseSalary + (e.performanceSalary || 0) + (e.positionSalary || 0) + (e.senioritySalary || 0) + e.overtimePay + (e.transportAllowance || 0) + (e.mealAllowance || 0) + (e.housingAllowance || 0) + (e.communicationAllowance || 0) + e.allowance + e.bonus - e.deduction - (e.otherDeduction || 0),
|
|
||||||
totalNetPay: acc.totalNetPay + e.netPay,
|
|
||||||
totalSocialOrg: acc.totalSocialOrg + e.socialOrg,
|
|
||||||
totalSocialEmp: acc.totalSocialEmp + e.socialEmp,
|
|
||||||
totalHousingOrg: acc.totalHousingOrg + e.housingOrg,
|
|
||||||
totalHousingEmp: acc.totalHousingEmp + e.housingEmp,
|
|
||||||
totalTax: acc.totalTax + e.tax,
|
|
||||||
}), { totalPay: 0, totalNetPay: 0, totalSocialOrg: 0, totalSocialEmp: 0, totalHousingOrg: 0, totalHousingEmp: 0, totalTax: 0 })
|
|
||||||
|
|
||||||
await prisma.payrollBatch.update({
|
|
||||||
where: { id: batchId },
|
|
||||||
data: {
|
|
||||||
totalPay: Math.round(totals.totalPay * 100) / 100,
|
|
||||||
totalNetPay: Math.round(totals.totalNetPay * 100) / 100,
|
|
||||||
totalSocialOrg: Math.round(totals.totalSocialOrg * 100) / 100,
|
|
||||||
totalSocialEmp: Math.round(totals.totalSocialEmp * 100) / 100,
|
|
||||||
totalHousingOrg: Math.round(totals.totalHousingOrg * 100) / 100,
|
|
||||||
totalHousingEmp: Math.round(totals.totalHousingEmp * 100) / 100,
|
|
||||||
totalTax: Math.round(totals.totalTax * 100) / 100,
|
|
||||||
},
|
|
||||||
})
|
|
||||||
|
|
||||||
res.json({
|
res.json({
|
||||||
success: true,
|
success: true,
|
||||||
|
|||||||
Reference in New Issue
Block a user