ux: 新建账户按钮移到社保/公积金Tab内部,按类型显示
按钮从页面顶部移到Tab内容区顶部,文案改为"新建社保账户"/"新建公积金账户", 明确表示新建的是当前Tab对应类型的账户。 Generated with [Devin](https://devin.ai) Co-Authored-By: Devin <158243242+devin-ai-integration[bot]@users.noreply.github.com>
This commit is contained in:
+102
-77
@@ -3,44 +3,33 @@
|
||||
> 目标:将社保公积金从"按城市直接关联版本"改为"账户 + 年度标准"两层实体。
|
||||
> 账户代表用户开设的社保/公积金账户(对应不同子公司/分公司/地区),年度标准是账户下每个年度的缴费比例和基数标准。
|
||||
|
||||
> **状态:已完成实施**(2026-08-16)
|
||||
> 账户+年度标准模型已上线,前端已重构为账户卡片列表+展开年度标准管理。
|
||||
|
||||
---
|
||||
|
||||
## 一、现状分析
|
||||
|
||||
### 当前数据模型
|
||||
### 当前数据模型(已重构)
|
||||
|
||||
| 表 | 说明 | 唯一约束 |
|
||||
|----|------|---------|
|
||||
| `SocialInsuranceConfig` | 社保版本(比例+基数+生效月份),按 orgId+city+effectiveFrom | `@@unique([orgId, city, effectiveFrom])` |
|
||||
| `HousingFundConfig` | 公积金版本,按 orgId+city+accountType+effectiveFrom | `@@unique([orgId, city, accountType, effectiveFrom])` |
|
||||
| `EmployeeSocialInsRecord` | 员工社保参保记录,字段含 city | `@@index([orgId, city])` |
|
||||
| `EmployeeHousingFundRecord` | 员工公积金参保记录,字段含 city | 无 city 索引 |
|
||||
| `SocialAccount` | 社保/公积金账户实体(type=SOCIAL/HOUSING) | `@@unique([orgId, type, name])` |
|
||||
| `SocialYearStandard` | 年度标准(比例+基数+最低工资+生效月份),关联到账户 | `@@unique([accountId, effectiveFrom])` |
|
||||
| `SocialInsuranceConfig` | 旧社保版本表(保留兼容,回退用) | `@@unique([orgId, city, effectiveFrom])` |
|
||||
| `HousingFundConfig` | 旧公积金版本表(保留兼容) | — |
|
||||
| `EmployeeSocialInsRecord` | 员工社保参保记录,已加 accountId | — |
|
||||
| `EmployeeHousingFundRecord` | 员工公积金参保记录,已加 accountId | — |
|
||||
| `SocialMonthlyProcess` | 月度办理记录 | `@@unique([orgId, month, type])` |
|
||||
|
||||
### 当前问题
|
||||
### 已解决的问题
|
||||
|
||||
1. **"城市"只是字符串**:无实体化管理,无法记录账户编号、开户行、缴费主体等
|
||||
2. **多子公司/分公司场景缺失**:同一城市可能有多个社保账户(不同主体分别开户),当前按 city 唯一无法支持
|
||||
3. **版本直接挂在 orgId+city 上**:缺少"账户"这一层抽象,无法区分同一城市不同主体的账户
|
||||
4. **员工参保记录只有 city**:无法精确关联到具体哪个社保账户
|
||||
5. **薪资计算中社保配置查询**:按 orgId+city 查 SocialInsuranceConfig,无法按账户区分
|
||||
|
||||
### 涉及"关联城市"的代码位置
|
||||
|
||||
| 文件 | 位置 | 说明 |
|
||||
|------|------|------|
|
||||
| `backend/prisma/schema.prisma` | `SocialInsuranceConfig.city` | 社保配置按城市区分 |
|
||||
| `backend/prisma/schema.prisma` | `HousingFundConfig.city` | 公积金配置按城市区分 |
|
||||
| `backend/prisma/schema.prisma` | `EmployeeSocialInsRecord.city` | 员工社保参保城市 |
|
||||
| `backend/prisma/schema.prisma` | `EmployeeHousingFundRecord.city` | 员工公积金参保城市 |
|
||||
| `backend/src/routes/social.routes.ts` | 全文 | 社保配置 CRUD/版本/城市列表/月度办理 均按 city |
|
||||
| `backend/src/services/payroll.service.ts` | 社保计算逻辑 | 按 city 查 SocialInsuranceConfig |
|
||||
| `backend/src/routes/payroll2.routes.ts` | 批次计算 | 社保配置查询 |
|
||||
| `backend/src/routes/roster.routes.ts` | socialInsuranceStatus 派生 | 按 city 判定 |
|
||||
| `backend/src/services/contract.service.ts` | createEmployee | 社保记录创建时关联 city |
|
||||
| `frontend/src/pages/SocialInsurance.tsx` | 全文 | 城市选择器、版本管理、月度办理 |
|
||||
| `frontend/src/pages/roster/modals.tsx` | 社保公积金区 | 员工参保城市 |
|
||||
| `frontend/src/pages/Settings.tsx` | 社保配置 | 需新增账户管理入口 |
|
||||
1. ✅ **"城市"只是字符串** → 已改为 SocialAccount 实体化管理
|
||||
2. ✅ **多子公司/分公司场景缺失** → 同一城市可有多个账户,按根部门关联
|
||||
3. ✅ **版本直接挂在 orgId+city 上** → 改为 SocialYearStandard 关联到 accountId
|
||||
4. ✅ **员工参保记录只有 city** → 已加 accountId 字段
|
||||
5. ✅ **薪资计算中社保配置查询** → 改为按 accountId 查 SocialYearStandard,旧表回退
|
||||
6. ✅ **最低工资标准** → SocialYearStandard 新增 minWage 字段,支持最低工资保护和递延扣款
|
||||
|
||||
---
|
||||
|
||||
@@ -73,6 +62,10 @@ model SocialAccount {
|
||||
socialRecords EmployeeSocialInsRecord[]
|
||||
housingRecords EmployeeHousingFundRecord[]
|
||||
|
||||
// 部门关联(根部门 level=0 代表分公司/子公司)
|
||||
deptSocialAccounts Department[] @relation("SocialAccountDepartments")
|
||||
deptHousingAccounts Department[] @relation("HousingAccountDepartments")
|
||||
|
||||
@@unique([orgId, type, name])
|
||||
@@index([orgId, type, city])
|
||||
@@index([orgId, type, isDefault])
|
||||
@@ -104,6 +97,9 @@ model SocialYearStandard {
|
||||
medicalBaseMax Float @default(0)
|
||||
extraInsurances Json?
|
||||
|
||||
// 最低工资标准(仅社保,20260816新增)
|
||||
minWage Float @default(0) // 当地月最低工资标准,0=不检查
|
||||
|
||||
// 公积金比例(type=HOUSING 时使用)
|
||||
housingOrg Float @default(12)
|
||||
housingEmp Float @default(12)
|
||||
@@ -248,7 +244,7 @@ JOIN "SocialAccount" a ON a."orgId" = c."orgId" AND a.city = c.city AND a.type =
|
||||
|
||||
## 四、后端 API 变更
|
||||
|
||||
### 新增:账户管理 API
|
||||
### 新增:账户管理 API(已实施)
|
||||
|
||||
| 方法 | 路径 | 说明 |
|
||||
|------|------|------|
|
||||
@@ -257,15 +253,24 @@ JOIN "SocialAccount" a ON a."orgId" = c."orgId" AND a.city = c.city AND a.type =
|
||||
| PUT | `/social/accounts/:id` | 编辑账户 |
|
||||
| DELETE | `/social/accounts/:id` | 删除账户(无关联记录时可删) |
|
||||
| PUT | `/social/accounts/:id/default` | 设为默认账户 |
|
||||
| PUT | `/social/accounts/:id/departments` | 账户关联根部门(批量) |
|
||||
| GET | `/social/accounts/:id/departments` | 获取账户已关联的根部门 |
|
||||
|
||||
### 改造:年度标准 API(原版本 API)
|
||||
### 新增:年度标准 API(已实施)
|
||||
|
||||
| 方法 | 路径 | 说明 | 变更 |
|
||||
|------|------|------|------|
|
||||
| GET | `/social/config` | 获取当前标准 | 改为按 accountId 查询 |
|
||||
| GET | `/social/config/versions` | 版本列表 | 改为按 accountId 查询 |
|
||||
| POST | `/social/config/versions` | 新建版本 | 改为按 accountId 创建 |
|
||||
| GET | `/social/config/by-month/:month` | 按月查标准 | 改为按 accountId+month 查询 |
|
||||
| 方法 | 路径 | 说明 |
|
||||
|------|------|------|
|
||||
| GET | `/social/accounts/:accountId/standards` | 账户的年度标准列表 |
|
||||
| GET | `/social/accounts/:accountId/current-standard` | 账户的当前生效标准 |
|
||||
| GET | `/social/accounts/:accountId/standard-by-month/:month` | 按账户+月份获取适用标准 |
|
||||
| POST | `/social/accounts/:accountId/standards` | 新建年度标准(旧版本自动归档) |
|
||||
| PUT | `/social/accounts/:accountId/min-wage` | 快速更新当前标准的最低工资 |
|
||||
|
||||
### 改造:薪资计算(已实施)
|
||||
|
||||
- `payroll.service.ts` 中社保配置查询:从 `orgId + city` 改为通过员工→根部门→账户→年度标准
|
||||
- 员工参保记录查询:从 `employeeId + city` 改为 `employeeId + accountId`
|
||||
- 新增最低工资保护和递延扣款逻辑(详见 20260816-薪税管理逻辑.md 第十五章)
|
||||
|
||||
### 废弃:城市相关 API
|
||||
|
||||
@@ -285,30 +290,36 @@ JOIN "SocialAccount" a ON a."orgId" = c."orgId" AND a.city = c.city AND a.type =
|
||||
|
||||
---
|
||||
|
||||
## 五、前端 UI 变更
|
||||
## 五、前端 UI 变更(已实施)
|
||||
|
||||
### 1. 设置页新增"社保公积金账户管理"
|
||||
### 1. 社保公积金菜单(SocialInsurance.tsx)— 已重构
|
||||
|
||||
- 账户列表(按 type 分社保/公积金 Tab)
|
||||
- 每个账户卡片:名称、城市、账户编号、缴费主体、关联范围(公司/分公司/子公司)、是否默认
|
||||
- 新增/编辑/停用账户弹窗
|
||||
- **关联级别**:公司(Organization)或根部门(Department level=0,代表分公司/子公司),不向下到普通部门
|
||||
- 员工通过所属根部门自动继承账户,未关联根部门的员工使用公司默认账户
|
||||
**社保/公积金 Tab**(20260816 重构):
|
||||
- ~~原城市选择器 → 改为账户选择器~~ → 已改为**账户卡片列表 + 展开年度标准管理**
|
||||
- 每个账户卡片:名称、城市、账号、关联部门数、参保记录数
|
||||
- 卡片可展开/折叠(ChevronDown/ChevronRight 图标)
|
||||
- 展开后显示:
|
||||
- 当前年度标准(比例/基数/最低工资)
|
||||
- 最低工资快速编辑(仅社保,Input + 保存按钮)
|
||||
- 操作按钮:新建年度标准 | 批量调基 | 查看历史
|
||||
- 新建年度标准弹窗(原"新建版本"改名,保留 AI 建议、附加险种配置)
|
||||
- 版本历史展示
|
||||
- 调基预览(保留原有展示和编辑逻辑)
|
||||
- 试算工具(使用该账户的城市配置)
|
||||
- 顶部"新建账户"按钮(账户新建/编辑/删除/设默认已从设置页面迁移到此)
|
||||
- 新增组件:`AccountCard.tsx`(账户卡片+展开内容)、`AccountFormModal.tsx`(账户新建/编辑弹窗)
|
||||
|
||||
### 2. SocialInsurance.tsx 改造
|
||||
**月度办理 Tab**:保留不变
|
||||
|
||||
**社保/公积金 Tab**:
|
||||
- 原城市选择器 → 改为**账户选择器**(下拉,显示"账户名称 - 城市")
|
||||
- 选中账户后展示该账户下的年度标准(当前版本 + 历史版本)
|
||||
- 年度标准展示比例和基数(与现在版本展示一致)
|
||||
**员工参保 Tab**:保留不变
|
||||
|
||||
**月度办理 Tab**:
|
||||
- 按账户分别办理
|
||||
- 选择账户 → 获取该账户的增减员名单 → 确认办理
|
||||
**专项附加扣除 Tab**:保留不变
|
||||
|
||||
**员工参保 Tab**:
|
||||
- 员工参保记录展示关联的账户名称
|
||||
- 新增参保时选择账户(而非仅选城市)
|
||||
### 2. 设置页面(Settings.tsx)— 已简化
|
||||
|
||||
- ~~新增"社保公积金账户管理"Tab~~ → 已去掉该 Tab
|
||||
- 账户管理全部移到社保公积金菜单中完成
|
||||
- SocialAccountSettings 和 AccountFormModal 组件定义保留在文件中(不再显示)
|
||||
|
||||
### 3. 员工表单(roster/modals.tsx)
|
||||
|
||||
@@ -354,34 +365,48 @@ JOIN "SocialAccount" a ON a."orgId" = c."orgId" AND a.city = c.city AND a.type =
|
||||
|
||||
---
|
||||
|
||||
## 七、实施计划
|
||||
## 七、实施计划与完成状态
|
||||
|
||||
### 第一步:DB schema + 迁移脚本
|
||||
- 新增 SocialAccount、SocialYearStandard 表
|
||||
- 员工记录表加 accountId 字段(可空)
|
||||
- 编写并执行迁移脚本
|
||||
- 验证数据完整性
|
||||
### 第一步:DB schema + 迁移脚本 ✅ 已完成
|
||||
- ✅ 新增 SocialAccount、SocialYearStandard 表
|
||||
- ✅ 员工记录表加 accountId 字段(可空)
|
||||
- ✅ SocialYearStandard 新增 minWage 字段(最低工资标准)
|
||||
- ✅ Department 表新增 socialAccountId / housingAccountId(根部门关联账户)
|
||||
- ✅ 编写并执行迁移脚本
|
||||
- ✅ 验证数据完整性
|
||||
|
||||
### 第二步:后端 API 重构
|
||||
- 新增账户 CRUD API
|
||||
- 年度标准 API 改为按 accountId
|
||||
- 薪资计算适配
|
||||
- 员工参保记录适配
|
||||
- 保留旧 API 兼容(过渡期)
|
||||
### 第二步:后端 API 重构 ✅ 已完成
|
||||
- ✅ 新增账户 CRUD API(`/social/accounts`)
|
||||
- ✅ 年度标准 API 改为按 accountId(`/social/accounts/:accountId/standards`)
|
||||
- ✅ 新增按账户获取当前标准 API(`/social/accounts/:accountId/current-standard`)
|
||||
- ✅ 新增快速更新最低工资 API(`/social/accounts/:accountId/min-wage`)
|
||||
- ✅ 薪资计算适配(payroll.service.ts 按 accountId 查标准,旧表回退)
|
||||
- ✅ 员工参保记录适配
|
||||
- ✅ 保留旧 API 兼容(过渡期)
|
||||
|
||||
### 第三步:前端账户管理
|
||||
- 设置页新增账户管理 UI
|
||||
- SocialInsurance.tsx 改为账户选择器 + 年度标准
|
||||
### 第三步:前端账户管理 ✅ 已完成
|
||||
- ✅ ~~设置页新增账户管理 UI~~ → 已移到社保公积金菜单
|
||||
- ✅ SocialInsurance.tsx 改为账户卡片列表 + 展开年度标准管理
|
||||
- ✅ 新建版本改名为"新建年度标准"
|
||||
- ✅ 最低工资快速编辑(当前配置区域可直接编辑)
|
||||
- ✅ 新增 AccountCard.tsx 组件(账户卡片+展开内容)
|
||||
- ✅ 新增 AccountFormModal.tsx 组件(账户新建/编辑弹窗)
|
||||
|
||||
### 第四步:前端参保流程适配
|
||||
- 员工表单社保公积金区改为账户选择
|
||||
- 月度办理按账户分别办理
|
||||
- 员工参保记录展示账户名称
|
||||
### 第四步:前端参保流程适配 ✅ 已完成
|
||||
- ✅ 员工表单社保公积金区改为账户选择
|
||||
- ✅ 月度办理保留不变
|
||||
- ✅ 员工参保记录展示账户名称
|
||||
|
||||
### 第五步:清理
|
||||
- 删除旧 SocialInsuranceConfig / HousingFundConfig 表
|
||||
- 删除旧 API
|
||||
- 删除前端城市选择器残留代码
|
||||
### 第五步:清理 ⏳ 待执行
|
||||
- ⏳ 删除旧 SocialInsuranceConfig / HousingFundConfig 表
|
||||
- ⏳ 删除旧 API
|
||||
- ⏳ 删除前端城市选择器残留代码
|
||||
|
||||
### 额外完成的功能(20260816)
|
||||
- ✅ 最低工资保护与递延扣款机制(详见 20260816-薪税管理逻辑.md 第十五章)
|
||||
- ✅ 最低工资保护支持当月累计实发判断(多批次场景)
|
||||
- ✅ 预入职状态(PRE_ONBOARD),预入职员工不进入薪资批次
|
||||
- ✅ 薪资批次新增 payMonth(发薪年月)字段,支持提前发薪场景
|
||||
|
||||
---
|
||||
|
||||
|
||||
@@ -175,11 +175,6 @@ export default function SocialInsurance() {
|
||||
<p className="mt-1 text-sm text-gray-500">维护社保、公积金、商业保险缴费基数、版本及月度记录</p>
|
||||
</div>
|
||||
</div>
|
||||
{(tab === 'social' || tab === 'housing') && (
|
||||
<Button size="sm" onClick={() => { setEditAccount(null); setShowAccountForm(true) }}>
|
||||
<Plus className="w-4 h-4 mr-1" />新建账户
|
||||
</Button>
|
||||
)}
|
||||
</div>
|
||||
|
||||
{/* Tab 切换 */}
|
||||
@@ -200,10 +195,16 @@ export default function SocialInsurance() {
|
||||
{/* ========== 社保 / 公积金 Tab:账户卡片列表 ========== */}
|
||||
{(tab === 'social' || tab === 'housing') && (
|
||||
<div className="space-y-3">
|
||||
{/* 新建对应类型账户按钮 */}
|
||||
<div className="flex justify-end">
|
||||
<Button size="sm" onClick={() => { setEditAccount(null); setShowAccountForm(true) }}>
|
||||
<Plus className="w-4 h-4 mr-1" />新建{tab === 'housing' ? '公积金' : '社保'}账户
|
||||
</Button>
|
||||
</div>
|
||||
{accounts.length === 0 ? (
|
||||
<Card>
|
||||
<div className="text-center py-8 text-gray-500">
|
||||
暂无{tab === 'housing' ? '公积金' : '社保'}账户,点击右上角「新建账户」创建
|
||||
暂无{tab === 'housing' ? '公积金' : '社保'}账户,点击上方「新建{tab === 'housing' ? '公积金' : '社保'}账户」创建
|
||||
</div>
|
||||
</Card>
|
||||
) : (
|
||||
|
||||
Reference in New Issue
Block a user