Files
TurboHR/20260723-优化-1.md
T
selfrelease 0df8aa77d9 feat: AIHR 智能人力资源管理系统初始提交
- 员工花名册管理(加密存储、导入导出)
- 薪酬管理(发薪批次、薪酬模版、加班费计算、工资条)
- 社保公积金(多城市配置、版本管理、基数调整)
- 解聘管理(6步流程、证据链、工作交接)
- AI 助手(合同审查、风险预测、RAG 知识库)
- Dashboard 仪表盘
- 设置与通知
2026-07-24 13:53:11 +08:00

18 KiB
Raw Blame History

社保公积金优化方案

核心原则

  • 社保和公积金完全分离:独立配置、独立调基、独立增减员、独立申报
  • 社保公积金开始/截止年月必填,增减变以此为准
  • 基数缺省等于工资,可修改
  • 调薪后社保公积金基数不自动调整(社保基数通常每年7月统一调基,调薪仅影响发薪基数)
  • 发薪列表和社保/公积金申报列表中,入离职日期与社保公积金年月不一致时提醒
  • 所有变更(入职/重新入职/调基/调薪/调部门/离职/解聘)都按版本记录保存,算薪和月度处理时按月份获取当前有效版本

一、Schema 改动

1.1 拆分配置模型

现有 SocialInsuranceConfig(含社保+公积金比例)拆为:

  • SocialInsuranceConfig(保留,移除公积金字段):养老/医疗/失业/工伤/生育比例 + 社保基数上下限 + 生效月份 + 版本管理 + adjustmentDone 标记
  • HousingFundConfig(新增):公积金企业/个人比例 + 公积金基数上下限 + 生效月份 + 版本管理 + adjustmentDone 标记(字段结构同社保配置)

Organization 和 Employee 需增加反向关联字段:

  • Organization: housingFundConfigs HousingFundConfig[]socialInsRecords EmployeeSocialInsRecord[]housingFundRecords EmployeeHousingFundRecord[]departmentRecords EmployeeDepartmentRecord[]salaryChangeRecords 已存在)
  • Employee: socialInsRecords EmployeeSocialInsRecord[]housingFundRecords EmployeeHousingFundRecord[]departmentRecords EmployeeDepartmentRecord[]salaryChanges 已存在)

1.2 新增模型:社保/公积金缴费记录(按版本保存)

社保和公积金的基数、起止年月不是 Employee 上的简单字段,而是按版本记录保存。每次入职/重新入职/调基/离职/解聘都生成新版本,形成完整变更历史。

EmployeeSocialInsRecord(社保缴费记录)

model EmployeeSocialInsRecord {
  id           String   @id @default(cuid())
  orgId        String
  org          Organization @relation(fields: [orgId], references: [id], onDelete: Cascade)
  employeeId   String
  employee     Employee   @relation(fields: [employeeId], references: [id], onDelete: Cascade)
  startMonth   String   // 开始缴费年月 YYYY-MM
  endMonth     String?  // 截止缴费年月 YYYY-MMnull=至今有效)
  base         Float    // 缴费基数
  // 变更来源
  changeType   String   // ONBOARDING=入职, REHIRE=重新入职, ADJUST=调基, TERMINATION=离职/解聘
  changeRefId  String?  // 关联的 TerminationRecord ID(离职/解聘时)
  remark       String?
  createdBy    String
  createdAt    DateTime @default(now())

  @@index([orgId, employeeId])
  @@index([employeeId, startMonth, endMonth])  // 复合索引:按员工+月份查询有效版本
}

EmployeeHousingFundRecord(公积金缴费记录)

model EmployeeHousingFundRecord {
  id           String   @id @default(cuid())
  orgId        String
  org          Organization @relation(fields: [orgId], references: [id], onDelete: Cascade)
  employeeId   String
  employee     Employee   @relation(fields: [employeeId], references: [id], onDelete: Cascade)
  startMonth   String   // 开始缴费年月 YYYY-MM
  endMonth     String?  // 截止缴费年月 YYYY-MMnull=至今有效)
  base         Float    // 缴费基数
  // 变更来源
  changeType   String   // ONBOARDING=入职, REHIRE=重新入职, ADJUST=调基, TERMINATION=离职/解聘
  changeRefId  String?  // 关联的 TerminationRecord ID(离职/解聘时)
  remark       String?
  createdBy    String
  createdAt    DateTime @default(now())

  @@index([orgId, employeeId])
  @@index([employeeId, startMonth, endMonth])  // 复合索引:按员工+月份查询有效版本
}

Employee 保留便捷字段(当前生效值,由后端同步维护)

socialInsStartMonth   String?  // 当前社保开始年月(=最新记录的startMonth
socialInsBase         Float?   // 当前社保基数(=最新记录的base
socialInsEndMonth     String?  // 当前社保截止年月(=最新记录的endMonthnull=在保)
housingFundStartMonth String?  // 当前公积金开始年月
housingFundBase       Float?   // 当前公积金基数
housingFundEndMonth   String?  // 当前公积金截止年月

这些字段是冗余的便捷查询字段,由后端在创建/更新缴费记录时自动同步。增减员和在职申报查询主要使用 Record 表,发薪计算使用 Employee 便捷字段。

1.3 TerminationRecord 增加字段

socialInsEndMonth     String   // 社保截止缴费年月 YYYY-MM(必填)
housingFundEndMonth   String   // 公积金截止缴费年月 YYYY-MM(必填)

TerminationRecord 保存截止年月的同时,后端自动创建一条 EmployeeSocialInsRecord / EmployeeHousingFundRecord,将上一条有效记录的 endMonth 设为此值,并同步 Employee 便捷字段。

1.4 扩展模型:调薪/调部门按版本保存

调薪和调部门也按版本记录保存,与社保公积金缴费记录同理。每次变更生成新版本,算薪和社保公积金月度处理时获取当前最新版。

扩展现有 SalaryChangeRecord(增加版本字段)

现有 SalaryChangeRecord 已有 oldSalary/newSalary/effectiveDate/reason,与其新建模型,直接扩展:

// 在现有 SalaryChangeRecord 增加字段:
  effectiveMonth String   // 生效年月 YYYY-MM(从 effectiveDate 转换)
  endMonth       String?  // 失效年月 YYYY-MM(null=至今有效,被新版本覆盖时设置)
  changeType     String   @default("SALARY_CHANGE") // ONBOARDING=入职, REHIRE=重新入职, SALARY_CHANGE=调薪

  @@index([employeeId, effectiveMonth, endMonth])  // 复合索引

不新建 EmployeeSalaryRecord,直接复用 SalaryChangeRecord,避免数据分散。入职时也创建一条(oldSalary=0, newSalary=月薪, changeType=ONBOARDING)。

EmployeeDepartmentRecord(部门变更记录,新增模型)

model EmployeeDepartmentRecord {
  id           String   @id @default(cuid())
  orgId        String
  org          Organization @relation(fields: [orgId], references: [id], onDelete: Cascade)
  employeeId   String
  employee     Employee   @relation(fields: [employeeId], references: [id], onDelete: Cascade)
  oldDepartment String  // 调整前部门
  newDepartment String  // 调整后部门
  effectiveMonth String // 生效年月 YYYY-MM
  endMonth     String?  // 失效年月 YYYY-MMnull=至今有效)
  reason       String?  // 调部门原因
  changeType   String   // ONBOARDING=入职, REHIRE=重新入职, TRANSFER=调部门
  createdBy    String
  createdAt    DateTime @default(now())

  @@index([orgId, employeeId])
  @@index([employeeId, effectiveMonth, endMonth])  // 复合索引
}

Employee 上的 monthlySalarydepartment 作为便捷字段由后端同步维护。

1.5 PayrollBatchType 增加枚举

SEVERANCE  // 补偿金按月发放(无社保,个税按政策处理)

1.6 数据迁移策略

Schema 改动后,需要为现有员工创建初始 Record:

  • EmployeeSocialInsRecord:为每个现有员工创建一条,startMonth = 入职日期年月,endMonth = 已离职员工的离职日期年月(如有),base = 现有 socialInsBase 或月薪,changeType = 'ONBOARDING'
  • EmployeeHousingFundRecord:同上,base = 现有 housingFundBase 或月薪
  • SalaryChangeRecord:为每个现有员工创建一条初始记录,oldSalary = 0, newSalary = 当前月薪, effectiveMonth = 入职日期年月, endMonth = null
  • EmployeeDepartmentRecord:为每个现有员工创建一条,oldDepartment = '', newDepartment = 当前部门, effectiveMonth = 入职日期年月, endMonth = null
  • 迁移脚本npx prisma db push 后执行一次性迁移脚本 scripts/migrate-records.ts

二、需求1:新增/重新入职填写社保公积金开始年月+基数

前端 AddEmployeeModal

  • 新增4个必填字段(2列布局):
    • 社保开始年月(type=month,缺省=入职日期年月,可修改)
    • 社保基数(type=number,缺省=月薪,可修改)
    • 公积金开始年月(type=month,缺省=入职日期年月,可修改)
    • 公积金基数(type=number,缺省=月薪,可修改)
  • 当入职日期变更时(handleHireDateChange),自动同步4个缺省值
  • canSubmit 增加这4个字段的必填校验

前端 RehireModal

  • 同 AddEmployeeModal,缺省=新入职日期年月

后端

  • createEmployeeSchema 增加 socialInsStartMonthsocialInsBasehousingFundStartMonthhousingFundBase(必填)
  • createEmployee 存储这些字段到 Employee 便捷字段,同时创建一条 EmployeeSocialInsRecordchangeType=ONBOARDING)和一条 EmployeeHousingFundRecordchangeType=ONBOARDING
  • rehireEmployee 接收并更新这些字段,同时创建新版本缴费记录(changeType=REHIRE,并将之前有效记录的 endMonth 设为重新入职前一个月

二.5 需求补充:花名册增加调薪/调部门操作

前端花名册列表

  • 每行操作区增加「调薪」「调部门」按钮(与「离职」并列)

前端调薪弹窗(SalaryChangeModal

  • 显示:员工姓名、当前月薪、当前部门
  • 输入:
    • 新月薪(必填,缺省=当前月薪)
    • 生效年月(type=month,必填,缺省=当月)
    • 调薪原因(选填)
  • 提交后:
    • 后端创建 SalaryChangeRecordoldSalary=当前月薪,newSalary=新月薪,effectiveMonth=生效年月, changeType=SALARY_CHANGE
    • 将之前有效记录的 endMonth 设为生效月前一个月
    • 同步 Employee.monthlySalary = 新月薪

前端调部门弹窗(DepartmentChangeModal

  • 显示:员工姓名、当前部门
  • 输入:
    • 新部门(必填,缺省=当前部门)
    • 生效年月(type=month,必填,缺省=当月)
    • 调部门原因(选填)
  • 提交后:
    • 后端创建 EmployeeDepartmentRecordoldDepartment=当前部门,newDepartment=新部门,effectiveMonth=生效年月)
    • 将之前有效记录的 endMonth 设为生效月前一个月
    • 同步 Employee.department = 新部门

后端

  • POST /roster/:id/salary-change — 调薪,创建版本记录 + 同步 Employee
  • POST /roster/:id/department-change — 调部门,创建版本记录 + 同步 Employee
  • GET /roster/:id/salary-records — 调薪历史
  • GET /roster/:id/department-records — 调部门历史

算薪和社保公积金月度处理

  • 算薪时:根据发薪月份获取该月有效的 SalaryChangeRecordeffectiveMonth <= monthendMonth == null 或 >= month),使用该记录的 newSalary 作为发薪基数
  • 社保公积金月度处理时:根据月份获取该月有效的 EmployeeSocialInsRecord / EmployeeHousingFundRecord,使用该记录的 base 作为缴费基数
  • 部门信息:根据月份获取该月有效的 EmployeeDepartmentRecord,用于月度报表中的部门归属
  • 调薪与社保基数关系:调薪仅影响发薪基数,不自动调整社保公积金基数。社保公积金基数仅在每年7月统一调基时调整

三、需求2:离职/解聘填写社保公积金截止年月

前端 ResignModalRoster.tsx

  • 新增2个必填字段:
    • 社保截止年月(type=month,缺省=离职日期年月,可修改)
    • 公积金截止年月(type=month,缺省=离职日期年月,可修改)
  • 当离职日期变更时,自动同步缺省值
  • canSubmit 增加必填校验

前端 Termination.tsx(解聘向导 Step 1

  • 在解聘日期下方增加社保截止年月、公积金截止年月输入
  • 缺省=解聘日期年月,可修改

后端

  • terminationChecklistSchema 增加 socialInsEndMonthhousingFundEndMonth(必填)
  • createTerminationcreateResignation 存储这些字段到 TerminationRecord
  • 同时更新 Employee 便捷字段socialInsEndMonthhousingFundEndMonth
  • 同时创建/更新缴费记录:将当前有效记录的 endMonth 设为截止年月,同步 Employee 便捷字段

四、需求3:社保公积金Tab增加月度增减员+在职申报+导出

4.1 前端 SocialInsurance.tsx 改造

增加顶层 Tab 切换:

  • 「社保」Tab:社保配置管理 + 社保调基 + 社保月度增减员 + 社保在职申报
  • 「公积金」Tab:公积金配置管理 + 公积金调基 + 公积金月度增减员 + 公积金在职申报

每个 Tab 内再分子 Tab

  • 配置管理(现有功能,社保/公积金各自独立)
  • 月度增减员
  • 在职申报

4.2 月度增减员

后端 API

  • GET /social/monthly-changes?month=YYYY-MM — 社保增减员
  • GET /housing/monthly-changes?month=YYYY-MM — 公积金增减员

逻辑(统一使用 Record 表查询,确保历史月份也能查到已离职员工):

  • 增员:查 EmployeeSocialInsRecord.startMonth == month(姓名、部门、基数、开始年月、changeType)
  • 减员:查 EmployeeSocialInsRecord.endMonth == monthchangeType == 'TERMINATION'(姓名、部门、基数、截止年月、离职类型)
  • 支持导出 CSV(前端生成,无需后端依赖)

前端:选择月份 → 显示增员表和减员表(两个表格或折叠分区)→ 导出按钮

4.3 在职申报

后端 API

  • GET /social/active-declaration?month=YYYY-MM — 社保在保人员
  • GET /housing/active-declaration?month=YYYY-MM — 公积金在保人员

逻辑(使用 Record 表查询):

  • 筛选条件:EmployeeSocialInsRecord.startMonth <= monthendMonth == null 或 >= month
  • 返回:姓名、身份证号、部门、社保基数、开始年月、截止年月
  • 支持导出 CSV(前端生成,无需后端依赖)

前端:选择月份 → 显示在保人员表格 → 导出按钮

4.4 调基拆分

现有调基操作同时调整社保和公积金基数。改为:

  • 社保调基:只调整社保基数,使用 SocialInsuranceConfig 的上下限
    • 将当前有效记录的 endMonth 设为调基月前一个月
    • 创建新 EmployeeSocialInsRecordchangeType=ADJUST),startMonth=调基月,base=新基数
    • 同步 Employee.socialInsBase / socialInsStartMonth
  • 公积金调基:只调整公积金基数,使用 HousingFundConfig 的上下限
    • 将当前有效记录的 endMonth 设为调基月前一个月
    • 创建新 EmployeeHousingFundRecordchangeType=ADJUST),startMonth=调基月,base=新基数
    • 同步 Employee.housingFundBase / housingFundStartMonth
  • 两个调基操作独立执行,各自有 adjustmentDone 标记

五、需求4:已离职员工按月发放补偿金

后端

  • PayrollBatchType 增加 SEVERANCE
  • calcBatchEntry:当 batchType === 'SEVERANCE' 时:
    • socialEmp=0housingEmp=0socialOrg=0housingOrg=0(无社保公积金)
    • tax:经济补偿金在当地社平工资3倍以内免征个税,超过部分按单独税率计税。简化处理:tax=0,备注注明「补偿金免征个税(社平3倍以内)」,如超过3倍需手动计算
  • 允许 status === 'RESIGNED' 的员工加入 SEVERANCE 批次
  • 补偿金发放可设置发放月数(如约定发放6个月),到期后自动标记为已完成
  • 也可手动停止发放

前端 Money.tsx

  • 批次类型下拉增加「补偿金发放」选项
  • SEVERANCE 批次:员工选择列表包含已离职员工
  • 输入项简化:只有补偿金金额(baseSalary),无加班/津贴/扣款
  • 可设置发放月数
  • 工资条显示:社保=0、公积金=0、个税=0(备注:补偿金免征)

六、需求5:日期不一致提醒

发薪列表提醒

在发薪批次详情中,对每个员工检查发薪月份与入离职日期的一致性:

  • 发薪月份 < 入职日期年月 → ⚠️ "该员工2025-07入职,当前发薪月份2025-06尚未入职"
  • 发薪月份 > 离职日期年月 → ⚠️ "该员工已于2025-06离职,当前发薪月份2025-07已离职"
  • 同时也检查社保公积金年月范围,如有不一致也提醒

社保/公积金申报列表提醒

  • 增员socialInsStartMonthhireDate 年月不一致 → ⚠️ "社保开始年月与入职日期不一致"
  • 减员socialInsEndMonthterminationDate 年月不一致 → ⚠️ "社保截止年月与离职日期不一致"
  • 在职申报hireDate 年月与 socialInsStartMonth 不一致、terminationDate 年月与 socialInsEndMonth 不一致 → ⚠️ 提醒

七、实施顺序

步骤 内容 涉及
1 Schema 改动(拆分配置、新增缴费/部门记录模型、扩展SalaryChangeRecord、增加字段、增加枚举)+ prisma db push 后端
1.5 数据迁移脚本:为现有员工创建初始 Record 后端
2 后端:createEmployee/rehireEmployee 接收社保公积金字段 + 创建缴费记录版本 后端
3 后端:createTermination/createResignation 接收截止年月 + 更新缴费记录版本 后端
3.5 后端:调薪/调部门 API + 创建/扩展版本记录 + 同步 Employee 后端
4 前端:AddEmployeeModal 增加社保公积金输入 前端
5 前端:RehireModal 同步 前端
6 前端:ResignModal 增加截止年月 前端
6.5 前端:花名册增加调薪/调部门弹窗 前端
7 前端:Termination.tsx 解聘向导增加截止年月 前端
8 后端:月度增减员 + 在职申报 API 后端
9 后端:SEVERANCE 批次类型 + calcBatchEntry 修改 后端
10 前端:SocialInsurance.tsx 改造(Tab拆分+增减员+申报+导出) 前端
11 前端:Money.tsx 增加补偿金批次 前端
12 前端:日期不一致提醒 前端
13 编译验证 + git 推送 全部