feat: 社保年度标准完善 — 医疗附加 + 继承旧配置 + 公积金独立上下限

1. 医疗附加金额:SocialYearStandard 和 SocialInsuranceConfig 新增
   medicalOrgExtra/medicalEmpExtra 字段(如北京3元大病医疗)
   - calcSocialInsurance 计算时加上医疗附加
   - 前端年度标准表单加医疗企业/个人附加输入

2. 新建年度标准继承:新增 /inherit-config API
   - 优先用当前年度标准
   - 回退到旧 SocialInsuranceConfig/HousingFundConfig
   - 前端点击"新建年度标准"时自动继承填充

3. 公积金独立上下限:SocialYearStandard 新增 housingBaseMin/housingBaseMax
   - calcHousingFund 优先用公积金专用上下限,为0时回退到社保
   - 前端公积金年度标准表单加公积金基数上下限输入

4. blank_employees/blank_all 模式跳过试用期工资覆盖

5. 劳务协议/实习协议人员 calcBatchEntry 强制社保公积金为0

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-16 15:31:56 +08:00
parent 3ac333891c
commit 38075420f4
5 changed files with 98 additions and 8 deletions
@@ -42,7 +42,7 @@ export function AccountCard({
effectiveFrom: new Date().toISOString().slice(0, 7),
city: account.city,
pensionOrg: 16, pensionEmp: 8,
medicalOrg: 9.8, medicalEmp: 2,
medicalOrg: 9.8, medicalEmp: 2, medicalOrgExtra: 0, medicalEmpExtra: 0,
unemploymentOrg: 0.5, unemploymentEmp: 0.5,
injuryOrg: 0.2, maternityOrg: 0.8,
baseMin: 6326, baseMax: 33891,
@@ -349,7 +349,7 @@ export function AccountCard({
<div className="flex justify-between border-b pb-1.5"><span className="text-gray-500"></span><span className="font-medium">¥{fmt(config.medicalBaseMax)}</span></div>
)}
<div className="flex justify-between border-b pb-1.5"><span className="text-gray-500">(/)</span><span className="font-medium">{config.pensionOrg}% / {config.pensionEmp}%</span></div>
<div className="flex justify-between border-b pb-1.5"><span className="text-gray-500">(/)</span><span className="font-medium">{config.medicalOrg}% / {config.medicalEmp}%</span></div>
<div className="flex justify-between border-b pb-1.5"><span className="text-gray-500">(/)</span><span className="font-medium">{config.medicalOrg}% / {config.medicalEmp}%{(config.medicalOrgExtra > 0 || config.medicalEmpExtra > 0) && ` + ¥${fmt(config.medicalOrgExtra)}/¥${fmt(config.medicalEmpExtra)}`}</span></div>
<div className="flex justify-between border-b pb-1.5"><span className="text-gray-500">失业(企业/个人)</span><span className="font-medium">{config.unemploymentOrg}% / {config.unemploymentEmp}%</span></div>
<div className="flex justify-between border-b pb-1.5"><span className="text-gray-500">工伤(企业)</span><span className="font-medium">{config.injuryOrg}%</span></div>
<div className="flex justify-between border-b pb-1.5"><span className="text-gray-500">生育(企业)</span><span className="font-medium">{config.maternityOrg}%</span></div>
@@ -397,7 +397,43 @@ export function AccountCard({
{/* 操作按钮 */}
<div className="flex items-center gap-2">
<Button size="sm" onClick={() => setShowNewVersion(!showNewVersion)}>
<Button size="sm" onClick={async () => {
if (!showNewVersion) {
// 打开新建表单时,自动继承当前标准或旧配置
try {
const inherited = await socialAccountApi.inheritConfig(account.id)
if (inherited) {
setNewVersion({
...newVersion,
effectiveFrom: new Date().toISOString().slice(0, 7),
city: account.city,
pensionOrg: inherited.pensionOrg ?? newVersion.pensionOrg,
pensionEmp: inherited.pensionEmp ?? newVersion.pensionEmp,
medicalOrg: inherited.medicalOrg ?? newVersion.medicalOrg,
medicalEmp: inherited.medicalEmp ?? newVersion.medicalEmp,
medicalOrgExtra: inherited.medicalOrgExtra ?? 0,
medicalEmpExtra: inherited.medicalEmpExtra ?? 0,
unemploymentOrg: inherited.unemploymentOrg ?? newVersion.unemploymentOrg,
unemploymentEmp: inherited.unemploymentEmp ?? newVersion.unemploymentEmp,
injuryOrg: inherited.injuryOrg ?? newVersion.injuryOrg,
maternityOrg: inherited.maternityOrg ?? newVersion.maternityOrg,
baseMin: inherited.baseMin ?? newVersion.baseMin,
baseMax: inherited.baseMax ?? newVersion.baseMax,
medicalBaseMin: inherited.medicalBaseMin ?? 0,
medicalBaseMax: inherited.medicalBaseMax ?? 0,
minWage: inherited.minWage ?? 0,
extraInsurances: inherited.extraInsurances ?? [],
housingOrg: inherited.housingOrg ?? newVersion.housingOrg,
housingEmp: inherited.housingEmp ?? newVersion.housingEmp,
housingBaseMin: inherited.housingBaseMin ?? 0,
housingBaseMax: inherited.housingBaseMax ?? 0,
accountType: account.accountType || 'BASIC',
})
}
} catch {}
}
setShowNewVersion(!showNewVersion)
}}>
<Plus className="w-4 h-4 mr-1" />
</Button>
<Button variant="secondary" size="sm" onClick={() => setShowVersions(!showVersions)}>
@@ -621,6 +657,8 @@ export function AccountCard({
<div><Label>(%)</Label><Input type="number" step="0.1" value={newVersion.pensionEmp} onChange={(e) => setNewVersion({ ...newVersion, pensionEmp: Number(e.target.value) })} /></div>
<div><Label>(%)</Label><Input type="number" step="0.1" value={newVersion.medicalOrg} onChange={(e) => setNewVersion({ ...newVersion, medicalOrg: Number(e.target.value) })} /></div>
<div><Label>(%)</Label><Input type="number" step="0.1" value={newVersion.medicalEmp} onChange={(e) => setNewVersion({ ...newVersion, medicalEmp: Number(e.target.value) })} /></div>
<div><Label>()</Label><Input type="number" step="0.01" value={newVersion.medicalOrgExtra} onChange={(e) => setNewVersion({ ...newVersion, medicalOrgExtra: Number(e.target.value) })} placeholder="0" /><p className="text-xs text-gray-400 mt-1"></p></div>
<div><Label>()</Label><Input type="number" step="0.01" value={newVersion.medicalEmpExtra} onChange={(e) => setNewVersion({ ...newVersion, medicalEmpExtra: Number(e.target.value) })} placeholder="如 北京3元" /><p className="text-xs text-gray-400 mt-1">3</p></div>
<div><Label>(%)</Label><Input type="number" step="0.1" value={newVersion.unemploymentOrg} onChange={(e) => setNewVersion({ ...newVersion, unemploymentOrg: Number(e.target.value) })} /></div>
<div><Label>(%)</Label><Input type="number" step="0.1" value={newVersion.unemploymentEmp} onChange={(e) => setNewVersion({ ...newVersion, unemploymentEmp: Number(e.target.value) })} /></div>
<div><Label>(%)</Label><Input type="number" step="0.1" value={newVersion.injuryOrg} onChange={(e) => setNewVersion({ ...newVersion, injuryOrg: Number(e.target.value) })} /></div>