feat: 补充公积金支持(员工级别可选)

上海等城市存在补充公积金账户,同一企业内高管可能有但普通员工
没有。新增员工级别补充公积金账户关联:

- Employee 表加 supplementaryHousingAccountId 字段
- BatchEntry 表加 supplementaryHousingEmp/Org 字段
- 薪资计算 calcBatchEntry 支持补充公积金(额外算一笔,纳入
  个税扣除、最低工资保护递延逻辑)
- 新增员工弹窗加补充公积金账户选择器(可选,仅显示
  accountType=SUPPLEMENTARY 的公积金账户)
- 员工档案编辑支持补充公积金账户

Generated with [Devin](https://devin.ai)

Co-Authored-By: Devin <158243242+devin-ai-integration[bot]@users.noreply.github.com>
This commit is contained in:
selfrelease
2026-08-17 10:20:59 +08:00
parent 1e7932c36c
commit 16f38f3c36
7 changed files with 82 additions and 14 deletions
+9 -3
View File
@@ -467,6 +467,8 @@ router.post('/batches', async (req: AuthRequest, res: Response, next: NextFuncti
socialOrg: calcResult.socialOrg,
housingEmp: calcResult.housingEmp,
housingOrg: calcResult.housingOrg,
supplementaryHousingEmp: calcResult.supplementaryHousingEmp || 0,
supplementaryHousingOrg: calcResult.supplementaryHousingOrg || 0,
tax: calcResult.tax,
totalPay: calcResult.totalPay,
netPay: calcResult.netPay,
@@ -578,7 +580,7 @@ router.put('/batches/:batchId/entries/:employeeId', async (req: AuthRequest, res
// 重新计算
const calcResult = await calcBatchEntry(orgId, employeeId, batch.month, inputs, batch.type, options)
const { systemSocialEmp, systemSocialOrg, systemHousingEmp, systemHousingOrg, taxBreakdown, ...entryData } = calcResult
const { systemSocialEmp, systemSocialOrg, systemHousingEmp, systemHousingOrg, systemSupplementaryHousingEmp, systemSupplementaryHousingOrg, taxBreakdown, ...entryData } = calcResult
const updated = await prisma.batchEntry.update({
where: { id: entry.id },
data: { ...inputs, ...entryData },
@@ -718,10 +720,14 @@ router.get('/batches/:batchId/entries/:employeeId/tax-detail', async (req: AuthR
taxBreakdown.systemSocialOrg = calcResult.systemSocialOrg
taxBreakdown.systemHousingEmp = calcResult.systemHousingEmp
taxBreakdown.systemHousingOrg = calcResult.systemHousingOrg
taxBreakdown.systemSupplementaryHousingEmp = calcResult.systemSupplementaryHousingEmp
taxBreakdown.systemSupplementaryHousingOrg = calcResult.systemSupplementaryHousingOrg
taxBreakdown.actualSocialEmp = entry.socialEmp
taxBreakdown.actualSocialOrg = entry.socialOrg
taxBreakdown.actualHousingEmp = entry.housingEmp
taxBreakdown.actualHousingOrg = entry.housingOrg
taxBreakdown.actualSupplementaryHousingEmp = entry.supplementaryHousingEmp || 0
taxBreakdown.actualSupplementaryHousingOrg = entry.supplementaryHousingOrg || 0
res.json({ success: true, data: taxBreakdown })
} catch (err) {
next(err)
@@ -772,7 +778,7 @@ router.post('/batches/:batchId/employees', async (req: AuthRequest, res: Respons
const calcResult = await calcBatchEntry(orgId, employeeId, batch.month, { baseSalary, overtimePay, allowance: 0, deduction: 0, bonus: 0 }, batch.type)
const riskWarnings = await getPayrollRiskWarnings(orgId, employeeId)
const { systemSocialEmp: _sse, systemSocialOrg: _sso, systemHousingEmp: _she, systemHousingOrg: _sho, taxBreakdown: _tb, ...entryData } = calcResult
const { systemSocialEmp: _sse, systemSocialOrg: _sso, systemHousingEmp: _she, systemHousingOrg: _sho, systemSupplementaryHousingEmp: _sshe, systemSupplementaryHousingOrg: _ssho, taxBreakdown: _tb, ...entryData } = calcResult
const entry = await prisma.batchEntry.create({
data: {
batchId, orgId, employeeId,
@@ -915,7 +921,7 @@ router.post('/batches/:batchId/archive', async (req: AuthRequest, res: Response,
const options: any = { prevDeferred }
const calcResult = await calcBatchEntry(orgId, entry.employeeId, batch.month, inputs, batch.type, options)
const { systemSocialEmp: _sse, systemSocialOrg: _sso, systemHousingEmp: _she, systemHousingOrg: _sho, taxBreakdown: _tb, ...entryData } = calcResult
const { systemSocialEmp: _sse, systemSocialOrg: _sso, systemHousingEmp: _she, systemHousingOrg: _sho, systemSupplementaryHousingEmp: _sshe, systemSupplementaryHousingOrg: _ssho, taxBreakdown: _tb, ...entryData } = calcResult
await prisma.batchEntry.update({
where: { id: entry.id },
data: { ...entryData },