4f125d309b
- Schema: 拆分社保/公积金配置,新增EmployeeSocialInsRecord/EmployeeHousingFundRecord/DepartmentRecord模型,扩展SalaryChangeRecord,增加SEVERANCE批次类型 - 后端: createEmployee/rehireEmployee接收社保公积金字段并创建缴费记录版本;createTermination/createResignation接收截止年月并关闭缴费记录;调薪/调部门API+版本记录;月度增减员API;公积金独立CRUD/计算/调基;SEVERANCE批次calcBatchEntry - 前端: AddEmployeeModal/RehireModal增加社保公积金输入;ResignModal/Termination增加截止年月+日期不一致提醒;花名册增加调薪/调部门弹窗;SocialInsurance.tsx Tab拆分(社保/公积金/月度增减员)+CSV导出;Money.tsx增加补偿金批次类型 - 修复: seed.ts移除housingOrg/housingEmp;risk.service.ts从HousingFundConfig获取公积金费率
151 lines
4.5 KiB
TypeScript
151 lines
4.5 KiB
TypeScript
/**
|
|
* 一次性迁移脚本:为现有员工创建初始版本记录
|
|
* 运行方式:npx tsx scripts/migrate-records.ts
|
|
*/
|
|
import prisma from '../src/lib/prisma.js'
|
|
import { decrypt } from '../src/lib/crypto.js'
|
|
|
|
function dateToMonth(date: Date): string {
|
|
const y = date.getFullYear()
|
|
const m = String(date.getMonth() + 1).padStart(2, '0')
|
|
return `${y}-${m}`
|
|
}
|
|
|
|
function prevMonth(month: string): string {
|
|
const [y, m] = month.split('-').map(Number)
|
|
const d = new Date(y, m - 2, 1)
|
|
return `${d.getFullYear()}-${String(d.getMonth() + 1).padStart(2, '0')}`
|
|
}
|
|
|
|
async function main() {
|
|
const employees = await prisma.employee.findMany({
|
|
include: {
|
|
terminations: { orderBy: { terminationDate: 'desc' }, take: 1 },
|
|
salaryChanges: { orderBy: { createdAt: 'desc' }, take: 1 },
|
|
socialInsRecords: { take: 1 },
|
|
housingFundRecords: { take: 1 },
|
|
departmentRecords: { take: 1 },
|
|
},
|
|
})
|
|
|
|
console.log(`Found ${employees.length} employees to migrate`)
|
|
|
|
for (const emp of employees) {
|
|
const hireMonth = dateToMonth(emp.hireDate)
|
|
const termination = emp.terminations[0]
|
|
const endMonth = termination ? dateToMonth(termination.terminationDate) : null
|
|
|
|
// 解密月薪获取数值
|
|
let salaryNum = 0
|
|
try {
|
|
salaryNum = parseFloat(decrypt(emp.monthlySalary)) || 0
|
|
} catch {
|
|
salaryNum = parseFloat(emp.monthlySalary) || 0
|
|
}
|
|
|
|
const socialInsBase = emp.socialInsBase ?? salaryNum
|
|
const housingFundBase = emp.housingFundBase ?? salaryNum
|
|
|
|
// 1. 社保缴费记录(仅当尚无记录时创建)
|
|
if (emp.socialInsRecords.length === 0) {
|
|
await prisma.employeeSocialInsRecord.create({
|
|
data: {
|
|
orgId: emp.orgId,
|
|
employeeId: emp.id,
|
|
startMonth: emp.socialInsStartMonth || hireMonth,
|
|
endMonth: endMonth || emp.socialInsEndMonth || null,
|
|
base: socialInsBase,
|
|
changeType: 'ONBOARDING',
|
|
createdBy: emp.createdBy,
|
|
},
|
|
})
|
|
}
|
|
|
|
// 2. 公积金缴费记录
|
|
if (emp.housingFundRecords.length === 0) {
|
|
await prisma.employeeHousingFundRecord.create({
|
|
data: {
|
|
orgId: emp.orgId,
|
|
employeeId: emp.id,
|
|
startMonth: emp.housingFundStartMonth || hireMonth,
|
|
endMonth: endMonth || emp.housingFundEndMonth || null,
|
|
base: housingFundBase,
|
|
changeType: 'ONBOARDING',
|
|
createdBy: emp.createdBy,
|
|
},
|
|
})
|
|
}
|
|
|
|
// 3. 薪资变更记录(仅当尚无记录时创建)
|
|
if (emp.salaryChanges.length === 0) {
|
|
await prisma.salaryChangeRecord.create({
|
|
data: {
|
|
orgId: emp.orgId,
|
|
employeeId: emp.id,
|
|
oldSalary: 0,
|
|
newSalary: salaryNum,
|
|
effectiveDate: emp.hireDate,
|
|
effectiveMonth: hireMonth,
|
|
endMonth: null,
|
|
changeType: 'ONBOARDING',
|
|
createdBy: emp.createdBy,
|
|
},
|
|
})
|
|
} else {
|
|
// 已有记录但缺少 effectiveMonth/endMonth/changeType,补充
|
|
const latest = emp.salaryChanges[0]
|
|
if (!latest.effectiveMonth || !latest.changeType) {
|
|
await prisma.salaryChangeRecord.update({
|
|
where: { id: latest.id },
|
|
data: {
|
|
effectiveMonth: dateToMonth(latest.effectiveDate),
|
|
changeType: latest.changeType || 'SALARY_CHANGE',
|
|
},
|
|
})
|
|
}
|
|
}
|
|
|
|
// 4. 部门变更记录
|
|
if (emp.departmentRecords.length === 0) {
|
|
await prisma.employeeDepartmentRecord.create({
|
|
data: {
|
|
orgId: emp.orgId,
|
|
employeeId: emp.id,
|
|
oldDepartment: '',
|
|
newDepartment: emp.department,
|
|
effectiveMonth: hireMonth,
|
|
endMonth: null,
|
|
changeType: 'ONBOARDING',
|
|
createdBy: emp.createdBy,
|
|
},
|
|
})
|
|
}
|
|
|
|
// 5. 同步 Employee 便捷字段
|
|
await prisma.employee.update({
|
|
where: { id: emp.id },
|
|
data: {
|
|
socialInsStartMonth: emp.socialInsStartMonth || hireMonth,
|
|
socialInsEndMonth: endMonth || emp.socialInsEndMonth || null,
|
|
socialInsBase,
|
|
housingFundStartMonth: emp.housingFundStartMonth || hireMonth,
|
|
housingFundEndMonth: endMonth || emp.housingFundEndMonth || null,
|
|
housingFundBase,
|
|
},
|
|
})
|
|
|
|
console.log(` ✓ ${emp.name} (${emp.department}) — records created/synced`)
|
|
}
|
|
|
|
console.log('\nMigration complete!')
|
|
}
|
|
|
|
main()
|
|
.catch((e) => {
|
|
console.error('Migration failed:', e)
|
|
process.exit(1)
|
|
})
|
|
.finally(async () => {
|
|
await prisma.$disconnect()
|
|
})
|