feat: 20260815 系统优化 - 全部31项问题修复(P0×6+P1×14+P2×9+P3×2)
P0紧急修复(6项): - 草稿保存完整恢复所有字段(含socialAvgWage) - 补偿金批次从compensationBreakdown读取 - 违法解除风险确认UI - 合同结束日期前后校验(前后端双保险) P1高优先级(14项): - 离职日期联动社保/公积金截止月(15号规则) - 合规检查+工作交接改为软阻断(生成待办) - 补偿月数(N/N+1/2N/自定义)+计算基数(近12月/合同/自定义) - 解聘并入花名册操作栏(类型选择跳转向导) - 合同续签开始日期自动推导(原合同结束日+1天) - 年龄合规筛查(童工阻断/未成年工/退休警告) - 编辑入职日期后状态联动(待入职↔在职) - 转正移植到花名册操作栏+薪资回写 - 男职工无法选择三期 P2体验优化(9项): - "劳动合同"调整为"用工关系" - 费用结算新增剩余年假折算(300%日工资) - 身份证号全域改为"证件号码"(前后端18个文件) - 手机号查重 - 开具证明+合同续签移植到花名册操作栏 - 批量转正+批量开具证明 - 去掉用工办理模块 P3规划(2项): - 组织架构+审批流(Department/Position/ApprovalFlow/ApprovalInstance) - 客服工作台(Ticket/ChatSession+SUPPORT角色) 新增模型: Department/Position/ApprovalFlow/ApprovalInstance/Ticket/TicketMessage/ChatSession/ChatMessage 新增字段: Employee.departmentId/supervisorId 新增角色: SUPPORT Generated with [Devin](https://devin.ai) Co-Authored-By: Devin <158243242+devin-ai-integration[bot]@users.noreply.github.com>
This commit is contained in:
@@ -283,7 +283,18 @@ router.post('/batches', async (req: AuthRequest, res: Response, next: NextFuncti
|
||||
where: { orgId, terminationDate: { gte: monthStart, lte: monthEnd } },
|
||||
include: { employee: { include: { contracts: { orderBy: { createdAt: 'desc' }, take: 1 } } } },
|
||||
})
|
||||
employees = terminations.map(t => t.employee)
|
||||
// SEVERANCE 批次:仅包含已审批通过(APPROVED/EXECUTING/COMPLETED)且有补偿金的离职记录
|
||||
if (type === 'SEVERANCE') {
|
||||
const eligibleTerms = terminations.filter(t =>
|
||||
(t.status === 'APPROVED' || t.status === 'EXECUTING' || t.status === 'COMPLETED') &&
|
||||
(t.compensation > 0 || (t.compensationBreakdown as any)?.total > 0)
|
||||
)
|
||||
employees = eligibleTerms.map(t => t.employee)
|
||||
// 缓存 terminationRecord 以便后续 entry 创建时读取补偿金
|
||||
;(req as any)._severanceTerms = new Map(eligibleTerms.map(t => [t.employeeId, t]))
|
||||
} else {
|
||||
employees = terminations.map(t => t.employee)
|
||||
}
|
||||
} else {
|
||||
employees = await prisma.employee.findMany({
|
||||
where: {
|
||||
@@ -357,14 +368,33 @@ router.post('/batches', async (req: AuthRequest, res: Response, next: NextFuncti
|
||||
try { baseSalary = Number(decrypt(emp.monthlySalary)) || 0 } catch { baseSalary = Number(emp.monthlySalary) || 0 }
|
||||
}
|
||||
|
||||
// SEVERANCE 批次:从离职记录读取补偿金作为应发金额,不走工资/社保/个税计算
|
||||
let severanceAmount = 0
|
||||
let severanceBreakdown: any = null
|
||||
if (type === 'SEVERANCE') {
|
||||
const severanceTerms: Map<string, any> = (req as any)._severanceTerms || new Map()
|
||||
const termRecord = severanceTerms.get(emp.id)
|
||||
if (termRecord) {
|
||||
severanceBreakdown = termRecord.compensationBreakdown
|
||||
// 优先取 compensationBreakdown.total(含手动调整),否则取 compensation
|
||||
severanceAmount = (severanceBreakdown as any)?.total || termRecord.compensation || 0
|
||||
baseSalary = severanceAmount
|
||||
}
|
||||
}
|
||||
|
||||
// calcBatchEntry 内部会按员工检查当月已归档批次是否已扣社保,已扣则跳过
|
||||
// SEVERANCE 批次:补偿金不走社保/个税计算,直接作为应发和实发金额
|
||||
let calcResult: any
|
||||
try {
|
||||
calcResult = await calcBatchEntry(orgId, emp.id, month, { baseSalary, overtimePay, allowance, deduction, bonus }, type)
|
||||
} 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 }
|
||||
if (type === 'SEVERANCE' && severanceAmount > 0) {
|
||||
calcResult = { socialEmp: 0, socialOrg: 0, housingEmp: 0, housingOrg: 0, tax: 0, totalPay: severanceAmount, netPay: severanceAmount }
|
||||
} else {
|
||||
try {
|
||||
calcResult = await calcBatchEntry(orgId, emp.id, month, { baseSalary, overtimePay, allowance, deduction, bonus }, type)
|
||||
} 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 }
|
||||
}
|
||||
}
|
||||
|
||||
// 风险提示
|
||||
|
||||
Reference in New Issue
Block a user