fix: 专项附加扣除改为5项明细分类(子女教育、赡养老人、住房、继续教育、婴幼儿照护)

- 后端 GET /profile 返回最新月份的专项附加扣除明细记录
- 后端 PUT /profile 保存明细到 SpecialDeductionRecord 表(当前月份 upsert)
- 前端5项明细输入框 + 合计实时计算 + 备注字段
- 与管理端社保>专项附加扣除 Tab 数据结构一致
This commit is contained in:
freedakgmail
2026-08-17 19:16:26 +08:00
parent 88b0fcdc7d
commit 76a49985f1
2 changed files with 138 additions and 16 deletions
+49 -4
View File
@@ -903,6 +903,21 @@ router.get('/profile', portalAuth, async (req: any, res, next) => {
} }
} }
// 查询最新月份的专项附加扣除明细
const latestDeductionRecord = await (prisma as any).specialDeductionRecord.findFirst({
where: { employeeId, orgId },
orderBy: { month: 'desc' },
})
const latestDeduction = latestDeductionRecord ? {
month: latestDeductionRecord.month,
children: latestDeductionRecord.children || 0,
elderly: latestDeductionRecord.elderly || 0,
housing: latestDeductionRecord.housing || 0,
education: latestDeductionRecord.education || 0,
infant: latestDeductionRecord.infant || 0,
remark: latestDeductionRecord.remark || '',
} : null
res.json({ res.json({
success: true, success: true,
data: { data: {
@@ -925,6 +940,7 @@ router.get('/profile', portalAuth, async (req: any, res, next) => {
employeeNo: emp.employeeNo || '', employeeNo: emp.employeeNo || '',
femaleWorkerType: emp.femaleWorkerType || '', femaleWorkerType: emp.femaleWorkerType || '',
specialDeduction: emp.specialDeduction ?? 0, specialDeduction: emp.specialDeduction ?? 0,
specialDeductionDetail: latestDeduction,
}, },
}) })
} catch (err) { next(err) } } catch (err) { next(err) }
@@ -940,7 +956,7 @@ router.put('/profile', portalAuth, async (req: any, res, next) => {
const { const {
emergencyContact, emergencyPhone, address, emergencyContact, emergencyPhone, address,
bankAccount, bankName, education, city, bankAccount, bankName, education, city,
specialDeduction, specialDeductionDetail,
} = req.body } = req.body
// 构建更新数据(仅允许员工自行编辑的字段) // 构建更新数据(仅允许员工自行编辑的字段)
@@ -955,9 +971,38 @@ router.put('/profile', portalAuth, async (req: any, res, next) => {
if (bankAccount !== undefined && bankAccount) { if (bankAccount !== undefined && bankAccount) {
updateData.bankAccount = encrypt(bankAccount) updateData.bankAccount = encrypt(bankAccount)
} }
// 专项附加扣除(子女教育、赡养老人等,员工自行填报)
if (specialDeduction !== undefined) { // 专项附加扣除明细保存到 SpecialDeductionRecord 表(当前月份)
updateData.specialDeduction = Number(specialDeduction) || 0 if (specialDeductionDetail && typeof specialDeductionDetail === 'object') {
const now = new Date()
const currentMonth = `${now.getFullYear()}-${String(now.getMonth() + 1).padStart(2, '0')}`
const { children, elderly, housing, education: dedEducation, infant, remark } = specialDeductionDetail
const total = (Number(children) || 0) + (Number(elderly) || 0) + (Number(housing) || 0) + (Number(dedEducation) || 0) + (Number(infant) || 0)
await (prisma as any).specialDeductionRecord.upsert({
where: { employeeId_month: { employeeId, month: currentMonth } },
create: {
orgId, employeeId, month: currentMonth,
amount: total,
children: Number(children) || 0,
elderly: Number(elderly) || 0,
housing: Number(housing) || 0,
education: Number(dedEducation) || 0,
infant: Number(infant) || 0,
remark: remark || null,
createdBy: employeeId,
},
update: {
amount: total,
children: Number(children) || 0,
elderly: Number(elderly) || 0,
housing: Number(housing) || 0,
education: Number(dedEducation) || 0,
infant: Number(infant) || 0,
remark: remark || null,
},
})
// 同步便捷字段
updateData.specialDeduction = total
} }
await prisma.employee.update({ where: { id: employeeId }, data: updateData }) await prisma.employee.update({ where: { id: employeeId }, data: updateData })
+89 -12
View File
@@ -23,7 +23,14 @@ export default function MyProfile() {
bankName: '', bankName: '',
education: '', education: '',
city: '', city: '',
specialDeduction: 0, })
const [deductionForm, setDeductionForm] = useState({
children: 0,
elderly: 0,
housing: 0,
education: 0,
infant: 0,
remark: '',
}) })
const { data: profile, isLoading } = useQuery<any>({ const { data: profile, isLoading } = useQuery<any>({
@@ -41,7 +48,15 @@ export default function MyProfile() {
bankName: profile.bankName || '', bankName: profile.bankName || '',
education: profile.education || '', education: profile.education || '',
city: profile.city || '', city: profile.city || '',
specialDeduction: profile.specialDeduction ?? 0, })
const d = profile.specialDeductionDetail
setDeductionForm({
children: d?.children || 0,
elderly: d?.elderly || 0,
housing: d?.housing || 0,
education: d?.education || 0,
infant: d?.infant || 0,
remark: d?.remark || '',
}) })
} }
}, [profile]) }, [profile])
@@ -59,7 +74,7 @@ export default function MyProfile() {
}) })
const handleSave = () => { const handleSave = () => {
updateMutation.mutate(form) updateMutation.mutate({ ...form, specialDeductionDetail: deductionForm })
} }
if (isLoading) { if (isLoading) {
@@ -211,15 +226,77 @@ export default function MyProfile() {
disabled={!editing} disabled={!editing}
/> />
</div> </div>
<div> </div>
<Label>/</Label>
<Input {/* 专项附加扣除明细 */}
type="number" <div className="mt-4 pt-4 border-t">
value={form.specialDeduction} <div className="flex items-center justify-between mb-3">
onChange={(e) => setForm({ ...form, specialDeduction: Number(e.target.value) || 0 })} <h3 className="text-xs font-medium text-gray-700">/</h3>
placeholder="子女教育、赡养老人等,0表示无" <span className="text-xs text-gray-400">
disabled={!editing} ¥{((deductionForm.children || 0) + (deductionForm.elderly || 0) + (deductionForm.housing || 0) + (deductionForm.education || 0) + (deductionForm.infant || 0)).toLocaleString('zh-CN', { minimumFractionDigits: 2 })}
/> {profile.specialDeductionDetail?.month && `${profile.specialDeductionDetail.month}月记录)`}
</span>
</div>
<div className="grid grid-cols-2 md:grid-cols-3 gap-3">
<div>
<Label></Label>
<Input
type="number"
value={deductionForm.children}
onChange={(e) => setDeductionForm({ ...deductionForm, children: Number(e.target.value) || 0 })}
placeholder="0"
disabled={!editing}
/>
</div>
<div>
<Label></Label>
<Input
type="number"
value={deductionForm.elderly}
onChange={(e) => setDeductionForm({ ...deductionForm, elderly: Number(e.target.value) || 0 })}
placeholder="0"
disabled={!editing}
/>
</div>
<div>
<Label>/</Label>
<Input
type="number"
value={deductionForm.housing}
onChange={(e) => setDeductionForm({ ...deductionForm, housing: Number(e.target.value) || 0 })}
placeholder="0"
disabled={!editing}
/>
</div>
<div>
<Label></Label>
<Input
type="number"
value={deductionForm.education}
onChange={(e) => setDeductionForm({ ...deductionForm, education: Number(e.target.value) || 0 })}
placeholder="0"
disabled={!editing}
/>
</div>
<div>
<Label></Label>
<Input
type="number"
value={deductionForm.infant}
onChange={(e) => setDeductionForm({ ...deductionForm, infant: Number(e.target.value) || 0 })}
placeholder="0"
disabled={!editing}
/>
</div>
<div className="md:col-span-3">
<Label></Label>
<Input
value={deductionForm.remark}
onChange={(e) => setDeductionForm({ ...deductionForm, remark: e.target.value })}
placeholder="可选备注信息"
disabled={!editing}
/>
</div>
</div> </div>
</div> </div>