diff --git a/20260816-薪税管理逻辑.md b/20260816-薪税管理逻辑.md index 0ea574d..f166182 100644 --- a/20260816-薪税管理逻辑.md +++ b/20260816-薪税管理逻辑.md @@ -470,10 +470,10 @@ POST /batches/:batchId/unarchive --- -## 十四、4 步工作流 +## 十四、3 步工作流 ``` -① 编辑薪资 → ② 核对汇总 → ③ 归档锁定 → ④ 发布工资条 +① 编辑薪资(含核对) → ② 归档锁定 → ③ 发布工资条 ``` ### 步骤 1:编辑薪资(DRAFT 状态) @@ -487,24 +487,26 @@ POST /batches/:batchId/unarchive - **添加/删除人员**:动态调整批次人员 - **查看个税明细**:点击个税金额查看累计预扣计算过程 -质量门禁(草稿状态自动检查): -- 实发为负的员工 → 红色警告 -- 全零记录 → 黄色警告 - -### 步骤 2:核对汇总 - -批次列表展示汇总数据: -- 应发合计、社保合计、公积金合计、个税合计、实发合计 +核对(编辑页面内直接展示,非独立步骤): +- 批次汇总卡片:应发合计、社保合计、公积金合计、个税合计、实发合计 +- 质量门禁自动检查:实发为负、全零记录、最低工资、递延扣款等 - 可导出 **薪资汇总表** / **薪资明细表**(CSV) -### 步骤 3:归档锁定(ARCHIVED) +质量门禁(草稿状态自动检查): +- 实发为负的员工 → 红色警告 +- 实发低于最低工资 → 红色警告 +- 最低工资保护已触发(递延扣款)→ 黄色警告 +- 本月补扣上月递延 → 黄色警告 +- 全零记录 → 黄色警告 + +### 步骤 2:归档锁定(ARCHIVED) **归档时后端自动执行**: 1. **重算所有条目**:调用 `calcBatchEntry` 重新计算社保公积金和个税 2. **更新批次汇总**:重算 totalPay/totalNetPay/各项合计 3. **标记为 ARCHIVED**:设置 `archivedAt`,不可再编辑 -### 步骤 4:发布工资条 +### 步骤 3:发布工资条 - 调用 `POST /batches/:id/publish` → 更新 `Payslip.publishStatus = PUBLISHED` - 或调用 `POST /batches/:id/schedule` → 设置 `publishStatus = SCHEDULED` + `scheduledAt` diff --git a/frontend/src/pages/money/BatchTab.tsx b/frontend/src/pages/money/BatchTab.tsx index aed08f9..8da375f 100644 --- a/frontend/src/pages/money/BatchTab.tsx +++ b/frontend/src/pages/money/BatchTab.tsx @@ -948,8 +948,7 @@ function BatchDetail({ batchId, onBack }: { batchId: string; onBack: () => void void 公积金(个人) 个税 实发 + 递延扣款 风险 {!isArchived && 操作} @@ -1110,12 +1110,34 @@ function BatchDetail({ batchId, onBack }: { batchId: string; onBack: () => void {renderCell(entry, 'socialEmp')} {renderCell(entry, 'housingEmp')} handleTaxDetailClick(entry.employeeId, entry.employee.name)} title="点击查看个税计算明细">{fmt(entry.tax)} - {fmt(entry.netPay)} + {/* 实发:最低工资保护触发时高亮显示 */} + 0 ? 'text-amber-600' : 'text-safe'}`} title={(entry.minWageApplied || 0) > 0 ? `最低工资保护:补齐 ¥${entry.minWageApplied},递延扣款 ¥${(entry.deferredSocialEmp || 0) + (entry.deferredHousingEmp || 0) + (entry.deferredMinWage || 0)} 将在次月补扣` : ''}> + {fmt(entry.netPay)} + {(entry.minWageApplied || 0) > 0 && } + + {/* 递延扣款明细 */} + + {(() => { + const deferred = (entry.deferredSocialEmp || 0) + (entry.deferredHousingEmp || 0) + (entry.deferredMinWage || 0) + const prevDeferred = (entry.prevDeferredSocialEmp || 0) + (entry.prevDeferredHousingEmp || 0) + (entry.prevDeferredMinWage || 0) + if (deferred > 0) { + return 递延 ¥{fmt(deferred)} + } + if (prevDeferred > 0) { + return 补扣 ¥{fmt(prevDeferred)} + } + return + })()} + {entry.riskWarnings && entry.riskWarnings.length > 0 ? ( + ) : (entry.minWageApplied || 0) > 0 ? ( + + + ) : ( )}