diff --git a/backend/src/routes/payroll2.routes.ts b/backend/src/routes/payroll2.routes.ts index e478101..ac1408d 100644 --- a/backend/src/routes/payroll2.routes.ts +++ b/backend/src/routes/payroll2.routes.ts @@ -55,6 +55,69 @@ async function getPrevDeferred(orgId: string, employeeId: string, currentMonth: 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 编码中文文件名 function contentDisposition(filename: string): string { const encoded = encodeURIComponent(filename) @@ -704,35 +767,14 @@ router.post('/batches/:batchId/fetch-bonus', async (req: AuthRequest, res: Respo where: { id: entry.id }, data: { bonus: bonus.amount }, }) + await recalcEntry(orgId, batchId, batch.month, batch.type, entry.id, entry.employeeId) filled++ totalAmount += bonus.amount } } // 重算批次汇总 - const allEntries = await prisma.batchEntry.findMany({ where: { 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, - }, - }) + await recalcBatchTotals(batchId) res.json({ success: true, @@ -810,34 +852,13 @@ router.post('/batches/:batchId/fetch-performance', async (req: AuthRequest, res: where: { id: entry.id }, data: { performanceSalary: calculatedPerfSalary }, }) + await recalcEntry(orgId, batchId, batch.month, batch.type, entry.id, entry.employeeId) filled++ totalAmount += calculatedPerfSalary } // 重算批次汇总 - const allEntries = await prisma.batchEntry.findMany({ where: { 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, - }, - }) + await recalcBatchTotals(batchId) res.json({ success: true, @@ -905,35 +926,14 @@ router.post('/batches/:batchId/fetch-disciplinary', async (req: AuthRequest, res where: { id: entry.id }, data: { deduction: Math.round(deduction * 100) / 100 }, }) + await recalcEntry(orgId, batchId, batch.month, batch.type, entry.id, entry.employeeId) filled++ totalAmount += deduction } } // 重算批次汇总 - const allEntries = await prisma.batchEntry.findMany({ where: { 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, - }, - }) + await recalcBatchTotals(batchId) res.json({ success: true, @@ -995,35 +995,14 @@ router.post('/batches/:batchId/fetch-attendance-deduction', async (req: AuthRequ where: { id: entry.id }, data: { deduction: Math.round(deduction * 100) / 100 }, }) + await recalcEntry(orgId, batchId, batch.month, batch.type, entry.id, entry.employeeId) filled++ totalAmount += deduction } } // 重算批次汇总 - const allEntries = await prisma.batchEntry.findMany({ where: { 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, - }, - }) + await recalcBatchTotals(batchId) res.json({ success: true,