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

378 lines
18 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 社保公积金优化方案
## 核心原则
- 社保和公积金**完全分离**:独立配置、独立调基、独立增减员、独立申报
- 社保公积金开始/截止年月**必填**,增减变以此为准
- 基数缺省等于工资,可修改
- 调薪后社保公积金基数**不自动调整**(社保基数通常每年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(社保缴费记录)
```prisma
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(公积金缴费记录)
```prisma
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? // 当前社保截止年月(=最新记录的endMonth,null=在保)
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`,与其新建模型,直接扩展:
```prisma
// 在现有 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(部门变更记录,新增模型)
```prisma
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 上的 `monthlySalary` 和 `department` 作为便捷字段由后端同步维护。
### 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` 增加 `socialInsStartMonth``socialInsBase``housingFundStartMonth``housingFundBase`(必填)
- `createEmployee` 存储这些字段到 Employee 便捷字段,**同时创建一条 `EmployeeSocialInsRecord`changeType=ONBOARDING)和一条 `EmployeeHousingFundRecord`changeType=ONBOARDING**
- `rehireEmployee` 接收并更新这些字段,**同时创建新版本缴费记录(changeType=REHIRE**,并将之前有效记录的 endMonth 设为重新入职前一个月
---
## 二.5 需求补充:花名册增加调薪/调部门操作
### 前端花名册列表
- 每行操作区增加「调薪」「调部门」按钮(与「离职」并列)
### 前端调薪弹窗(SalaryChangeModal
- 显示:员工姓名、当前月薪、当前部门
- 输入:
- 新月薪(必填,缺省=当前月薪)
- 生效年月(type=month,必填,缺省=当月)
- 调薪原因(选填)
- 提交后:
- 后端创建 `SalaryChangeRecord`oldSalary=当前月薪,newSalary=新月薪,effectiveMonth=生效年月, changeType=SALARY_CHANGE
- 将之前有效记录的 `endMonth` 设为生效月前一个月
- 同步 `Employee.monthlySalary` = 新月薪
### 前端调部门弹窗(DepartmentChangeModal
- 显示:员工姓名、当前部门
- 输入:
- 新部门(必填,缺省=当前部门)
- 生效年月(type=month,必填,缺省=当月)
- 调部门原因(选填)
- 提交后:
- 后端创建 `EmployeeDepartmentRecord`oldDepartment=当前部门,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` — 调部门历史
### 算薪和社保公积金月度处理
- 算薪时:根据发薪月份获取该月有效的 `SalaryChangeRecord``effectiveMonth <= month``endMonth == null 或 >= month`),使用该记录的 `newSalary` 作为发薪基数
- 社保公积金月度处理时:根据月份获取该月有效的 `EmployeeSocialInsRecord` / `EmployeeHousingFundRecord`,使用该记录的 `base` 作为缴费基数
- 部门信息:根据月份获取该月有效的 `EmployeeDepartmentRecord`,用于月度报表中的部门归属
- **调薪与社保基数关系**:调薪仅影响发薪基数,**不自动调整**社保公积金基数。社保公积金基数仅在每年7月统一调基时调整
---
## 三、需求2:离职/解聘填写社保公积金截止年月
### 前端 ResignModalRoster.tsx
- 新增2个必填字段:
- 社保截止年月(type=month,缺省=离职日期年月,可修改)
- 公积金截止年月(type=month,缺省=离职日期年月,可修改)
- 当离职日期变更时,自动同步缺省值
- `canSubmit` 增加必填校验
### 前端 Termination.tsx(解聘向导 Step 1
- 在解聘日期下方增加社保截止年月、公积金截止年月输入
- 缺省=解聘日期年月,可修改
### 后端
- `terminationChecklistSchema` 增加 `socialInsEndMonth``housingFundEndMonth`(必填)
- `createTermination``createResignation` 存储这些字段到 TerminationRecord
- **同时更新 Employee 便捷字段**`socialInsEndMonth``housingFundEndMonth`
- **同时创建/更新缴费记录**:将当前有效记录的 `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 == month``changeType == 'TERMINATION'`(姓名、部门、基数、截止年月、离职类型)
- 支持导出 CSV(前端生成,无需后端依赖)
**前端**:选择月份 → 显示增员表和减员表(两个表格或折叠分区)→ 导出按钮
### 4.3 在职申报
**后端 API**
- `GET /social/active-declaration?month=YYYY-MM` — 社保在保人员
- `GET /housing/active-declaration?month=YYYY-MM` — 公积金在保人员
**逻辑**(使用 Record 表查询):
- 筛选条件:`EmployeeSocialInsRecord.startMonth <= month``endMonth == null 或 >= month`
- 返回:姓名、身份证号、部门、社保基数、开始年月、截止年月
- 支持导出 CSV(前端生成,无需后端依赖)
**前端**:选择月份 → 显示在保人员表格 → 导出按钮
### 4.4 调基拆分
现有调基操作同时调整社保和公积金基数。改为:
- 社保调基:只调整社保基数,使用 `SocialInsuranceConfig` 的上下限
- 将当前有效记录的 `endMonth` 设为调基月前一个月
- 创建新 `EmployeeSocialInsRecord`changeType=ADJUST),startMonth=调基月,base=新基数
- 同步 Employee.socialInsBase / socialInsStartMonth
- 公积金调基:只调整公积金基数,使用 `HousingFundConfig` 的上下限
- 将当前有效记录的 `endMonth` 设为调基月前一个月
- 创建新 `EmployeeHousingFundRecord`changeType=ADJUST),startMonth=调基月,base=新基数
- 同步 Employee.housingFundBase / housingFundStartMonth
- 两个调基操作独立执行,各自有 `adjustmentDone` 标记
---
## 五、需求4:已离职员工按月发放补偿金
### 后端
- `PayrollBatchType` 增加 `SEVERANCE`
- `calcBatchEntry`:当 `batchType === 'SEVERANCE'` 时:
- `socialEmp=0``housingEmp=0``socialOrg=0``housingOrg=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已离职"
- 同时也检查社保公积金年月范围,如有不一致也提醒
### 社保/公积金申报列表提醒
- **增员**`socialInsStartMonth``hireDate` 年月不一致 → ⚠️ "社保开始年月与入职日期不一致"
- **减员**`socialInsEndMonth``terminationDate` 年月不一致 → ⚠️ "社保截止年月与离职日期不一致"
- **在职申报**`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 推送 | 全部 |