0df8aa77d9
- 员工花名册管理(加密存储、导入导出) - 薪酬管理(发薪批次、薪酬模版、加班费计算、工资条) - 社保公积金(多城市配置、版本管理、基数调整) - 解聘管理(6步流程、证据链、工作交接) - AI 助手(合同审查、风险预测、RAG 知识库) - Dashboard 仪表盘 - 设置与通知
378 lines
18 KiB
Markdown
378 lines
18 KiB
Markdown
# 社保公积金优化方案
|
||
|
||
## 核心原则
|
||
|
||
- 社保和公积金**完全分离**:独立配置、独立调基、独立增减员、独立申报
|
||
- 社保公积金开始/截止年月**必填**,增减变以此为准
|
||
- 基数缺省等于工资,可修改
|
||
- 调薪后社保公积金基数**不自动调整**(社保基数通常每年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-MM(null=至今有效)
|
||
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-MM(null=至今有效)
|
||
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-MM(null=至今有效)
|
||
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:离职/解聘填写社保公积金截止年月
|
||
|
||
### 前端 ResignModal(Roster.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 推送 | 全部 |
|