feat: Phase 1-3 优化全部完成
Phase 1 紧急修复(8项): - 社保城市选择改为可输入 - 社保上下限拆分(三险/医保独立基数) - 公积金试算结果展示修复 - 花名册合同保存修复(日期ISO格式) - 薪酬批次创建失败修复(城市过滤+错误处理) - 证据链查看修复 - 个税计算修复(blank_employees读取基本工资) - 加班费倍率读取配置 Phase 2 功能完善(3项): - 批量导入per-row异常捕获+导入按钮 - 单人发薪UI入口优化 - 解除协议模板补充(员工提出离职版) Phase 3 后期规划(4项): - 工资表导入功能(POST /import/payroll + 前端入口) - 大病险/长护险附加险种(extraInsurances JSON + 计算适配) - 专项附加扣除按月录入(SpecialDeductionRecord模型 + 前端Tab) - 预置河北省社保政策(seed数据)
This commit is contained in:
@@ -297,6 +297,7 @@ router.post('/batches', async (req: AuthRequest, res: Response, next: NextFuncti
|
||||
|
||||
// 创建批次条目
|
||||
const entries: any[] = []
|
||||
const failedEmployees: { employeeId: string; name: string; error: string }[] = []
|
||||
for (const emp of employees) {
|
||||
let baseSalary = 0
|
||||
let overtimePay = 0
|
||||
@@ -334,6 +335,10 @@ router.post('/batches', async (req: AuthRequest, res: Response, next: NextFuncti
|
||||
deduction = prevPayslip?.deduction || 0
|
||||
}
|
||||
// blank_employees 和 blank_all: 所有金额默认 0
|
||||
// blank_employees 模式下尝试从员工记录获取基本工资
|
||||
if (mode === 'blank_employees' && emp.monthlySalary) {
|
||||
try { baseSalary = Number(decrypt(emp.monthlySalary)) || 0 } catch { baseSalary = Number(emp.monthlySalary) || 0 }
|
||||
}
|
||||
|
||||
// 判断同月是否已有归档的常规批次(用于决定是否跳过社保)
|
||||
const hasArchivedRegularBatch = await prisma.payrollBatch.count({
|
||||
@@ -343,7 +348,15 @@ router.post('/batches', async (req: AuthRequest, res: Response, next: NextFuncti
|
||||
// 计算社保、个税等
|
||||
// 同月已有归档常规批次时,新批次跳过社保(避免重复扣缴),但用户可手动编辑覆盖
|
||||
const skipSocial = type !== 'BONUS' && type !== 'SEVERANCE' && hasArchivedRegularBatch > 0
|
||||
const calcResult = await calcBatchEntry(orgId, emp.id, month, { baseSalary, overtimePay, allowance, deduction, bonus }, type, { skipSocial })
|
||||
|
||||
let calcResult: any
|
||||
try {
|
||||
calcResult = await calcBatchEntry(orgId, emp.id, month, { baseSalary, overtimePay, allowance, deduction, bonus }, type, { skipSocial })
|
||||
} catch (calcErr: any) {
|
||||
// 单个员工计算失败不阻塞整个批次,记录错误并使用零值
|
||||
failedEmployees.push({ employeeId: emp.id, name: emp.name, error: calcErr?.message || '计算失败' })
|
||||
calcResult = { socialEmp: 0, socialOrg: 0, housingEmp: 0, housingOrg: 0, tax: 0, totalPay: baseSalary + overtimePay + allowance + bonus - deduction, netPay: baseSalary + overtimePay + allowance + bonus - deduction }
|
||||
}
|
||||
|
||||
// 风险提示
|
||||
const riskWarnings = await getPayrollRiskWarnings(orgId, emp.id)
|
||||
@@ -396,7 +409,7 @@ router.post('/batches', async (req: AuthRequest, res: Response, next: NextFuncti
|
||||
include: { entries: { include: { employee: { select: { id: true, name: true, department: true, status: true } } } } },
|
||||
})
|
||||
|
||||
res.json({ success: true, data: updatedBatch })
|
||||
res.json({ success: true, data: updatedBatch, failedEmployees: failedEmployees.length > 0 ? failedEmployees : undefined })
|
||||
} catch (err) {
|
||||
next(err)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user