fix: 个税累计预扣中专项附加扣除改为按月读取实际填报金额

原逻辑用 employee.specialDeduction(便捷字段)× 月数计算累计
专项附加扣除,未按月读取 SpecialDeductionRecord 实际金额。
若员工某月填报/取消专项附加扣除,累计值会不准。

修复:
- 优先按月查询 SpecialDeductionRecord(当年至当月)累加实际金额
- 无按月记录时回退到便捷字段 × 月数(兼容旧数据)
- taxBreakdown 增加 specialDeductionSource 字段标识数据来源

Generated with [Devin](https://devin.ai)

Co-Authored-By: Devin <158243242+devin-ai-integration[bot]@users.noreply.github.com>
This commit is contained in:
selfrelease
2026-08-16 13:36:02 +08:00
parent ce1670d171
commit 454b3d4b05
4 changed files with 695 additions and 3 deletions
+18 -1
View File
@@ -258,7 +258,22 @@ export async function calcBatchEntry(
const ytdIncome = archivedEntries.reduce((s, e) => s + e.totalPay, 0) + totalPay
const ytdSocialEmp = archivedEntries.reduce((s, e) => s + e.socialEmp, 0) + socialEmp
const ytdHousingEmp = archivedEntries.reduce((s, e) => s + e.housingEmp, 0) + housingEmp
const ytdSpecialDeduction = employee.specialDeduction * Number(month.slice(5, 7))
// 专项附加扣除:按月读取 SpecialDeductionRecord 实际填报金额累加
// 优先使用按月记录;若无按月记录,回退到员工便捷字段 × 月数(兼容旧数据)
const deductionRecords = await prisma.specialDeductionRecord.findMany({
where: { orgId, employeeId, month: { startsWith: year, lte: month } },
select: { amount: true, month: true },
})
let ytdSpecialDeduction: number
if (deductionRecords.length > 0) {
// 按月实际填报金额累加
ytdSpecialDeduction = deductionRecords.reduce((s, r) => s + r.amount, 0)
} else {
// 回退:员工便捷字段 × 月数(兼容未填报按月记录的旧数据)
ytdSpecialDeduction = employee.specialDeduction * Number(month.slice(5, 7))
}
const ytdTaxDeducted = archivedEntries.reduce((s, e) => s + e.tax, 0)
const deductionAmount = 5000 * Number(month.slice(5, 7))
const ytdTaxableIncome = Math.max(0, ytdIncome - deductionAmount - ytdSocialEmp - ytdHousingEmp - ytdSpecialDeduction)
@@ -271,6 +286,8 @@ export async function calcBatchEntry(
ytdSocialEmp,
ytdHousingEmp,
ytdSpecialDeduction,
specialDeductionSource: deductionRecords.length > 0 ? '按月记录' : '便捷字段',
specialDeductionRecords: deductionRecords.length,
ytdTaxableIncome,
ytdTaxDeducted,
currentMonthTax: tax,