feat: Phase 1-3 优化全部完成
Phase 1 紧急修复(8项): - 社保城市选择改为可输入 - 社保上下限拆分(三险/医保独立基数) - 公积金试算结果展示修复 - 花名册合同保存修复(日期ISO格式) - 薪酬批次创建失败修复(城市过滤+错误处理) - 证据链查看修复 - 个税计算修复(blank_employees读取基本工资) - 加班费倍率读取配置 Phase 2 功能完善(3项): - 批量导入per-row异常捕获+导入按钮 - 单人发薪UI入口优化 - 解除协议模板补充(员工提出离职版) Phase 3 后期规划(4项): - 工资表导入功能(POST /import/payroll + 前端入口) - 大病险/长护险附加险种(extraInsurances JSON + 计算适配) - 专项附加扣除按月录入(SpecialDeductionRecord模型 + 前端Tab) - 预置河北省社保政策(seed数据)
This commit is contained in:
@@ -35,10 +35,35 @@ export async function getTemplate(orgId: string) {
|
||||
// ========== 社保计算 ==========
|
||||
|
||||
export function calcSocialInsurance(base: number, config: any) {
|
||||
// 养老/失业/工伤保险基数
|
||||
const actualBase = Math.min(Math.max(base, config.baseMin), config.baseMax)
|
||||
const socialEmp = actualBase * (config.pensionEmp + config.medicalEmp + config.unemploymentEmp) / 100
|
||||
const socialOrg = actualBase * (config.pensionOrg + config.medicalOrg + config.unemploymentOrg + config.injuryOrg + config.maternityOrg) / 100
|
||||
return { actualBase, socialEmp, socialOrg }
|
||||
// 医疗/生育保险基数(独立上下限,为 0 时 fallback 到统一基数)
|
||||
const medMin = config.medicalBaseMin && config.medicalBaseMin > 0 ? config.medicalBaseMin : config.baseMin
|
||||
const medMax = config.medicalBaseMax && config.medicalBaseMax > 0 ? config.medicalBaseMax : config.baseMax
|
||||
const medicalBase = Math.min(Math.max(base, medMin), medMax)
|
||||
let socialEmp = actualBase * (config.pensionEmp + config.unemploymentEmp) / 100 + medicalBase * config.medicalEmp / 100
|
||||
let socialOrg = actualBase * (config.pensionOrg + config.unemploymentOrg + config.injuryOrg) / 100 + medicalBase * (config.medicalOrg + config.maternityOrg) / 100
|
||||
// 附加险种(大病险/长护险等)
|
||||
const extraItems: any[] = []
|
||||
if (config.extraInsurances && Array.isArray(config.extraInsurances)) {
|
||||
for (const ins of config.extraInsurances) {
|
||||
const insBase = ins.baseType === 'medical' ? medicalBase : ins.baseType === 'fixed' ? 1 : actualBase
|
||||
if (ins.baseType === 'fixed' && ins.fixedAmount) {
|
||||
const orgAmt = ins.fixedAmount
|
||||
const empAmt = ins.empFixedAmount || 0
|
||||
socialOrg += orgAmt
|
||||
socialEmp += empAmt
|
||||
extraItems.push({ name: ins.name, orgAmount: orgAmt, empAmount: empAmt })
|
||||
} else {
|
||||
const orgAmt = insBase * (ins.orgRate || 0) / 100
|
||||
const empAmt = insBase * (ins.empRate || 0) / 100
|
||||
socialOrg += orgAmt
|
||||
socialEmp += empAmt
|
||||
extraItems.push({ name: ins.name, orgAmount: orgAmt, empAmount: empAmt })
|
||||
}
|
||||
}
|
||||
}
|
||||
return { actualBase, medicalBase, socialEmp, socialOrg, extraItems }
|
||||
}
|
||||
|
||||
export function calcHousingFund(base: number, config: any) {
|
||||
@@ -106,11 +131,14 @@ export async function calcBatchEntry(
|
||||
batchType: string = 'REGULAR',
|
||||
options?: { skipSocial?: boolean; overrideSocial?: { socialEmp?: number; socialOrg?: number; housingEmp?: number; housingOrg?: number } },
|
||||
) {
|
||||
const [employee, socialConfig, housingConfig] = await Promise.all([
|
||||
prisma.employee.findFirst({ where: { id: employeeId, orgId } }),
|
||||
const employee = await prisma.employee.findFirst({ where: { id: employeeId, orgId } })
|
||||
if (!employee) throw { code: 'NOT_FOUND', message: '员工不存在' }
|
||||
|
||||
const cityWhere = employee.city ? { orgId, city: employee.city } : { orgId }
|
||||
const [socialConfig, housingConfig] = await Promise.all([
|
||||
prisma.socialInsuranceConfig.findFirst({
|
||||
where: {
|
||||
orgId,
|
||||
...cityWhere,
|
||||
effectiveFrom: { lte: month },
|
||||
OR: [{ effectiveTo: null }, { effectiveTo: { gte: month } }],
|
||||
},
|
||||
@@ -118,14 +146,13 @@ export async function calcBatchEntry(
|
||||
}),
|
||||
prisma.housingFundConfig.findFirst({
|
||||
where: {
|
||||
orgId,
|
||||
...cityWhere,
|
||||
effectiveFrom: { lte: month },
|
||||
OR: [{ effectiveTo: null }, { effectiveTo: { gte: month } }],
|
||||
},
|
||||
orderBy: { effectiveFrom: 'desc' },
|
||||
}),
|
||||
])
|
||||
if (!employee) throw { code: 'NOT_FOUND', message: '员工不存在' }
|
||||
|
||||
// 社保基数:优先用员工核定基数,否则用基本工资
|
||||
const socialBase = employee.socialInsBase || inputs.baseSalary
|
||||
|
||||
@@ -90,7 +90,7 @@ export const documentTemplates: DocumentTemplate[] = [
|
||||
id: 'tpl_termination_agreement',
|
||||
name: '解除劳动合同协议书',
|
||||
category: 'AGREEMENT',
|
||||
description: '协商一致解除劳动合同协议书模板',
|
||||
description: '协商一致解除劳动合同协议书模板(用人单位提出)',
|
||||
variables: ['companyName', 'employeeName', 'idCard', 'terminationDate', 'compensation', 'lastWorkDay', 'socialInsEndMonth', 'housingFundEndMonth'],
|
||||
content: `解除劳动合同协议书
|
||||
|
||||
@@ -123,6 +123,53 @@ export const documentTemplates: DocumentTemplate[] = [
|
||||
八、其他
|
||||
本协议一式两份,甲乙双方各执一份,自双方签字盖章之日起生效。
|
||||
|
||||
甲方(盖章):____________ 乙方(签字):____________
|
||||
日期:____年__月__日 日期:____年__月__日`,
|
||||
},
|
||||
{
|
||||
id: 'tpl_termination_agreement_employee',
|
||||
name: '解除劳动合同协议书(员工提出离职)',
|
||||
category: 'AGREEMENT',
|
||||
description: '员工主动提出离职的解除劳动合同协议书模板',
|
||||
variables: ['companyName', 'employeeName', 'idCard', 'terminationDate', 'lastWorkDay', 'socialInsEndMonth', 'housingFundEndMonth', 'resignationReason'],
|
||||
content: `解除劳动合同协议书
|
||||
|
||||
甲方(用人单位):{{companyName}}
|
||||
乙方(劳动者):{{employeeName}},身份证号:{{idCard}}
|
||||
|
||||
乙方因个人原因主动提出离职,经甲乙双方友好协商,就解除劳动合同事宜达成如下协议:
|
||||
|
||||
一、离职原因
|
||||
乙方因{{resignationReason}},自愿提出解除劳动合同。
|
||||
|
||||
二、解除日期
|
||||
双方同意于{{terminationDate}}解除劳动合同,乙方最后工作日为{{lastWorkDay}}。
|
||||
|
||||
三、工资结算
|
||||
甲方结清乙方截至解除日的所有工资、加班费等劳动报酬,于乙方办理完工作交接手续后__个工作日内一次性支付。
|
||||
|
||||
四、社会保险和公积金
|
||||
甲方为乙方缴纳社会保险至{{socialInsEndMonth}}月,住房公积金缴存至{{housingFundEndMonth}}月。
|
||||
|
||||
五、工作交接
|
||||
乙方应在最后工作日前完成工作交接,归还甲方所有财物和资料。
|
||||
|
||||
六、经济补偿
|
||||
因乙方主动提出离职,甲方无需向乙方支付经济补偿金。乙方确认对此无异议。
|
||||
|
||||
七、保密义务
|
||||
乙方解除劳动合同后,仍应遵守保密义务,不得泄露甲方商业秘密。
|
||||
|
||||
八、竞业限制
|
||||
如双方另行签有竞业限制协议,乙方应继续履行竞业限制义务。
|
||||
|
||||
九、争议解决
|
||||
本协议履行过程中如发生争议,双方应协商解决;协商不成的,可向劳动争议仲裁委员会申请仲裁。
|
||||
|
||||
十、其他
|
||||
1. 本协议签订后,双方劳动关系即告终止,双方不再存在任何劳动争议。
|
||||
2. 本协议一式两份,甲乙双方各执一份,自双方签字盖章之日起生效。
|
||||
|
||||
甲方(盖章):____________ 乙方(签字):____________
|
||||
日期:____年__月__日 日期:____年__月__日`,
|
||||
},
|
||||
|
||||
Reference in New Issue
Block a user