f74b2808a3
- 城市变更使用CITY_CHANGE类型替代ADJUST,月度办理显示减员/新增(城市变更) - 在保人员查询排除本月已关闭记录(gte→gt)和本月新增记录(lte→lt) - 社保/公积金减员查询包含CITY_CHANGE类型 - 缴纳记录表格添加城市列显示 - 后端profile API从快照提取city字段 - 修复旧快照缺失city字段的数据 - 修复旧记录changeType为CITY_CHANGE,endMonth与新记录startMonth一致 - 城市变更必填原因,写入备注和审计日志 - 员工详情页添加变更历史Tab - 移除薪酬社保Tab下重复的参保城市变更子Tab - 全局修复跨页面mutation缓存刷新:调薪/调部门/离职/重新入职/批量续签/批量解聘/社保公积金调基/月度办理/撤销解聘均刷新roster-profile
184 lines
13 KiB
TypeScript
184 lines
13 KiB
TypeScript
import prisma from '../src/lib/prisma'
|
|
|
|
const EID = 'cmry97xbv001qtrrpuo12ozy7'
|
|
const ORGID = 'cmry97x7l0000trrp5f9jgng5'
|
|
const UID = 'cmry97xa10002trrp7peorkvh'
|
|
|
|
async function main() {
|
|
// 加班记录(入职后按季度分布)
|
|
const otMonths = [
|
|
{ month: '2025-03', wh: 8, weh: 4, hh: 0, wp: 600, wep: 600, hp: 0, pay: 1200 },
|
|
{ month: '2025-06', wh: 12, weh: 8, hh: 0, wp: 1200, wep: 1200, hp: 0, pay: 2400 },
|
|
{ month: '2025-09', wh: 6, weh: 0, hh: 8, wp: 600, wep: 0, hp: 1200, pay: 1800 },
|
|
{ month: '2025-12', wh: 10, weh: 4, hh: 0, wp: 900, wep: 600, hp: 0, pay: 1500 },
|
|
{ month: '2026-03', wh: 8, weh: 8, hh: 0, wp: 600, wep: 1200, hp: 0, pay: 1800 },
|
|
{ month: '2026-06', wh: 6, weh: 0, hh: 0, wp: 450, wep: 0, hp: 0, pay: 450 },
|
|
]
|
|
for (const o of otMonths) {
|
|
const existing = await prisma.overtimeRecord.findUnique({ where: { employeeId_month: { employeeId: EID, month: o.month } } })
|
|
if (!existing) {
|
|
await prisma.overtimeRecord.create({ data: { orgId: ORGID, employeeId: EID, month: o.month, weekdayHours: o.wh, weekendHours: o.weh, holidayHours: o.hh, weekdayPay: o.wp, weekendPay: o.wep, holidayPay: o.hp, totalPay: o.pay } })
|
|
}
|
|
}
|
|
console.log('加班记录: 完成')
|
|
|
|
// 违纪记录(补充历史记录,保留已有的 2026-07-24 记录)
|
|
const discRecords = [
|
|
{ violationDate: new Date('2025-05-12'), violationType: 'LATE', description: '月度迟到超过5次,影响团队考勤', severity: 'WARNING', action: 'ORAL_WARNING', actionDetail: '口头警告并谈话', employeeAck: true, ackDate: new Date('2025-05-13'), ackMethod: 'SIGN', witness: '王强' },
|
|
{ violationDate: new Date('2025-09-20'), violationType: 'ABSENT', description: '未经请假擅自旷工1天', severity: 'SERIOUS', action: 'DEDUCTION', actionDetail: '扣款200元', employeeAck: true, ackDate: new Date('2025-09-21'), ackMethod: 'SIGN', witness: '王强' },
|
|
{ violationDate: new Date('2026-03-10'), violationType: 'INSUBORDINATION', description: '不服从主管工作安排,拒绝参加客户会议', severity: 'SERIOUS', action: 'WRITTEN_WARNING', actionDetail: '书面警告并记入档案', employeeAck: true, ackDate: new Date('2026-03-11'), ackMethod: 'SIGN', witness: '赵敏' },
|
|
]
|
|
for (const d of discRecords) {
|
|
const existing = await prisma.disciplinaryRecord.findFirst({ where: { employeeId: EID, violationDate: d.violationDate } })
|
|
if (!existing) {
|
|
await prisma.disciplinaryRecord.create({ data: { orgId: ORGID, employeeId: EID, createdBy: UID, ...d } })
|
|
}
|
|
}
|
|
console.log('违纪记录: 完成')
|
|
|
|
// 考勤记录 - 2026年6-7月最近20个工作日
|
|
const attendance = [
|
|
{ date: '2026-06-01', status: 'NORMAL', late: 0, early: 0, ot: 0 },
|
|
{ date: '2026-06-02', status: 'NORMAL', late: 0, early: 0, ot: 0 },
|
|
{ date: '2026-06-03', status: 'LATE', late: 15, early: 0, ot: 0 },
|
|
{ date: '2026-06-04', status: 'NORMAL', late: 0, early: 0, ot: 0 },
|
|
{ date: '2026-06-05', status: 'NORMAL', late: 0, early: 0, ot: 2 },
|
|
{ date: '2026-06-08', status: 'NORMAL', late: 0, early: 0, ot: 0 },
|
|
{ date: '2026-06-09', status: 'NORMAL', late: 0, early: 0, ot: 0 },
|
|
{ date: '2026-06-10', status: 'LEAVE', late: 0, early: 0, ot: 0, remark: '事假' },
|
|
{ date: '2026-06-11', status: 'NORMAL', late: 0, early: 0, ot: 0 },
|
|
{ date: '2026-06-12', status: 'NORMAL', late: 0, early: 0, ot: 3 },
|
|
{ date: '2026-06-15', status: 'NORMAL', late: 0, early: 0, ot: 0 },
|
|
{ date: '2026-06-16', status: 'LATE', late: 30, early: 0, ot: 0 },
|
|
{ date: '2026-06-17', status: 'NORMAL', late: 0, early: 0, ot: 0 },
|
|
{ date: '2026-06-18', status: 'NORMAL', late: 0, early: 0, ot: 0 },
|
|
{ date: '2026-06-19', status: 'EARLY_LEAVE', late: 0, early: 45, ot: 0 },
|
|
{ date: '2026-06-22', status: 'NORMAL', late: 0, early: 0, ot: 0 },
|
|
{ date: '2026-06-23', status: 'NORMAL', late: 0, early: 0, ot: 0 },
|
|
{ date: '2026-06-24', status: 'BUSINESS_TRIP', late: 0, early: 0, ot: 0, remark: '上海客户拜访' },
|
|
{ date: '2026-06-25', status: 'BUSINESS_TRIP', late: 0, early: 0, ot: 0, remark: '上海客户拜访' },
|
|
{ date: '2026-06-26', status: 'NORMAL', late: 0, early: 0, ot: 0 },
|
|
{ date: '2026-06-29', status: 'NORMAL', late: 0, early: 0, ot: 0 },
|
|
{ date: '2026-06-30', status: 'NORMAL', late: 0, early: 0, ot: 0 },
|
|
{ date: '2026-07-01', status: 'NORMAL', late: 0, early: 0, ot: 0 },
|
|
{ date: '2026-07-02', status: 'NORMAL', late: 0, early: 0, ot: 0 },
|
|
{ date: '2026-07-03', status: 'NORMAL', late: 0, early: 0, ot: 0 },
|
|
{ date: '2026-07-06', status: 'NORMAL', late: 0, early: 0, ot: 0 },
|
|
{ date: '2026-07-07', status: 'NORMAL', late: 0, early: 0, ot: 0 },
|
|
{ date: '2026-07-08', status: 'LATE', late: 20, early: 0, ot: 0 },
|
|
{ date: '2026-07-09', status: 'NORMAL', late: 0, early: 0, ot: 0 },
|
|
{ date: '2026-07-10', status: 'NORMAL', late: 0, early: 0, ot: 0 },
|
|
{ date: '2026-07-13', status: 'NORMAL', late: 0, early: 0, ot: 0 },
|
|
{ date: '2026-07-14', status: 'NORMAL', late: 0, early: 0, ot: 0 },
|
|
{ date: '2026-07-15', status: 'NORMAL', late: 0, early: 0, ot: 0 },
|
|
{ date: '2026-07-16', status: 'LATE', late: 25, early: 0, ot: 0 },
|
|
{ date: '2026-07-17', status: 'NORMAL', late: 0, early: 0, ot: 0 },
|
|
{ date: '2026-07-20', status: 'NORMAL', late: 0, early: 0, ot: 0 },
|
|
{ date: '2026-07-21', status: 'NORMAL', late: 0, early: 0, ot: 0 },
|
|
{ date: '2026-07-22', status: 'EARLY_LEAVE', late: 0, early: 30, ot: 0 },
|
|
{ date: '2026-07-23', status: 'NORMAL', late: 0, early: 0, ot: 0 },
|
|
]
|
|
for (const a of attendance) {
|
|
const existing = await prisma.attendanceRecord.findUnique({ where: { employeeId_date: { employeeId: EID, date: new Date(a.date) } } })
|
|
if (!existing) {
|
|
await prisma.attendanceRecord.create({ data: { orgId: ORGID, employeeId: EID, createdBy: UID, date: new Date(a.date), checkInTime: a.status === 'LEAVE' ? null : '09:00', checkOutTime: a.status === 'LEAVE' ? null : '18:00', status: a.status, lateMinutes: a.late, earlyMinutes: a.early, workHours: a.status === 'LEAVE' ? 0 : 8, overtimeHours: a.ot || 0, remark: a.remark || null } })
|
|
}
|
|
}
|
|
console.log('考勤记录: 完成')
|
|
|
|
// 培训签收记录
|
|
const trainings = [
|
|
{ trainingDate: new Date('2025-03-15'), topic: '《员工手册》培训', content: '公司规章制度、考勤制度、奖惩条例', trainer: '赵敏', duration: 2, ackStatus: 'SIGNED', ackDate: new Date('2025-03-15'), remark: '新员工入职培训' },
|
|
{ trainingDate: new Date('2025-06-20'), topic: '销售技巧与合规培训', content: '销售话术规范、客户信息保护、合同签订注意事项', trainer: '王强', duration: 4, ackStatus: 'SIGNED', ackDate: new Date('2025-06-20') },
|
|
{ trainingDate: new Date('2025-09-10'), topic: '《数据安全管理制度》培训', content: '客户数据保护规范、信息安全操作规程、违规处罚条例', trainer: '赵敏', duration: 2, ackStatus: 'SIGNED', ackDate: new Date('2025-09-10') },
|
|
{ trainingDate: new Date('2026-01-10'), topic: '2026年度规章制度更新培训', content: '新版考勤制度、绩效考核办法、安全生产规范', trainer: '赵敏', duration: 3, ackStatus: 'SIGNED', ackDate: new Date('2026-01-10') },
|
|
{ trainingDate: new Date('2026-04-15'), topic: '销售合规与反商业贿赂培训', content: '反商业贿赂法规、客户招待标准、合规销售流程', trainer: '王强', duration: 3, ackStatus: 'PENDING', remark: '待员工签收确认' },
|
|
]
|
|
for (const t of trainings) {
|
|
const existing = await prisma.trainingRecord.findFirst({ where: { employeeId: EID, trainingDate: t.trainingDate } })
|
|
if (!existing) {
|
|
await prisma.trainingRecord.create({ data: { orgId: ORGID, employeeId: EID, createdBy: UID, ...t } })
|
|
}
|
|
}
|
|
console.log('培训记录: 完成')
|
|
|
|
// 绩效记录(从入职后按季度考核)
|
|
const performances = [
|
|
{ period: '2025-Q1', score: 82, grade: 'B', result: 'QUALIFIED', summary: '入职适应良好,销售业绩达标,客户维护良好,需提升新客户开发能力', improvementPlan: '', employeeAck: true, ackDate: new Date('2025-04-10'), reviewer: '王强' },
|
|
{ period: '2025-Q2', score: 75, grade: 'B', result: 'QUALIFIED', summary: '业绩略有下滑,新客户开发不足,团队协作有待加强', improvementPlan: '', employeeAck: true, ackDate: new Date('2025-07-08'), reviewer: '王强' },
|
|
{ period: '2025-Q3', score: 68, grade: 'C', result: 'NEED_IMPROVE', summary: '连续3个月未完成销售目标,客户投诉1次,工作态度需改善', improvementPlan: '调岗至客户维护岗,加强销售技巧培训1个月', employeeAck: true, ackDate: new Date('2025-10-15'), reviewer: '王强' },
|
|
{ period: '2025-Q4', score: 78, grade: 'B', result: 'QUALIFIED', summary: '改进后业绩回升,客户满意度提升,团队配合度改善', improvementPlan: '', employeeAck: true, ackDate: new Date('2026-01-12'), reviewer: '王强' },
|
|
{ period: '2026-Q1', score: 72, grade: 'B', result: 'QUALIFIED', summary: '一季度业绩基本达标,大客户维护稳定,新签客户2家', improvementPlan: '', employeeAck: true, ackDate: new Date('2026-04-08'), reviewer: '王强' },
|
|
{ period: '2026-Q2', score: 65, grade: 'C', result: 'NEED_IMPROVE', summary: '二季度业绩下滑明显,客户流失1家,不服从管理记录1次', improvementPlan: '加强客户维护培训,调整销售目标考核方式', employeeAck: false, reviewer: '王强' },
|
|
]
|
|
for (const p of performances) {
|
|
const existing = await prisma.performanceRecord.findUnique({ where: { employeeId_period: { employeeId: EID, period: p.period } } })
|
|
if (!existing) {
|
|
await prisma.performanceRecord.create({ data: { orgId: ORGID, employeeId: EID, createdBy: UID, ...p } })
|
|
}
|
|
}
|
|
console.log('绩效记录: 完成')
|
|
|
|
// 附件
|
|
const attachments = [
|
|
{ fileName: '吴芳身份证扫描件.pdf', fileType: 'ID_CARD', fileUrl: 'data:application/pdf;base64,placeholder', fileSize: 102400 },
|
|
{ fileName: '吴芳银行卡复印件.jpg', fileType: 'BANK_CARD', fileUrl: 'data:image/jpeg;base64,placeholder', fileSize: 51200 },
|
|
{ fileName: '吴芳劳动合同扫描件.pdf', fileType: 'CONTRACT_SCAN', fileUrl: 'data:application/pdf;base64,placeholder', fileSize: 204800 },
|
|
{ fileName: '吴芳学历证书.jpg', fileType: 'EDUCATION', fileUrl: 'data:image/jpeg;base64,placeholder', fileSize: 81920 },
|
|
{ fileName: '吴芳学位证书.jpg', fileType: 'EDUCATION', fileUrl: 'data:image/jpeg;base64,placeholder', fileSize: 76800 },
|
|
{ fileName: '吴芳离职证明(前单位).pdf', fileType: 'OTHER', fileUrl: 'data:application/pdf;base64,placeholder', fileSize: 153600 },
|
|
]
|
|
for (const a of attachments) {
|
|
const existing = await prisma.employeeAttachment.findFirst({ where: { employeeId: EID, fileName: a.fileName } })
|
|
if (!existing) {
|
|
await prisma.employeeAttachment.create({ data: { ...a, orgId: ORGID, employeeId: EID, uploadedBy: UID } })
|
|
}
|
|
}
|
|
console.log('附件: 完成')
|
|
|
|
// 社保缴费记录(入职)
|
|
const hireMonth = '2025-02'
|
|
const existingSocial = await prisma.employeeSocialInsRecord.findFirst({ where: { employeeId: EID, startMonth: hireMonth } })
|
|
if (!existingSocial) {
|
|
await prisma.employeeSocialInsRecord.create({
|
|
data: { orgId: ORGID, employeeId: EID, startMonth: hireMonth, endMonth: null, base: 9000, changeType: 'ONBOARDING', createdBy: UID, city: '北京' },
|
|
})
|
|
}
|
|
// 公积金缴费记录(入职)
|
|
const existingHousing = await prisma.employeeHousingFundRecord.findFirst({ where: { employeeId: EID, startMonth: hireMonth } })
|
|
if (!existingHousing) {
|
|
await prisma.employeeHousingFundRecord.create({
|
|
data: { orgId: ORGID, employeeId: EID, startMonth: hireMonth, endMonth: null, base: 9000, changeType: 'ONBOARDING', createdBy: UID, city: '北京' },
|
|
})
|
|
}
|
|
// 更新 Employee 便捷字段
|
|
await prisma.employee.update({
|
|
where: { id: EID },
|
|
data: { city: '北京', socialInsStartMonth: hireMonth, housingFundStartMonth: hireMonth },
|
|
})
|
|
console.log('社保/公积金: 完成')
|
|
|
|
// 验证
|
|
const emp = await prisma.employee.findFirst({
|
|
where: { id: EID },
|
|
include: { contracts: true, payslips: true, overtimeRecords: true, disciplinaryRecords: true, attendanceRecords: true, trainingRecords: true, performanceRecords: true, terminations: true, attachments: true, socialInsRecords: true, housingFundRecords: true }
|
|
})
|
|
if (emp) {
|
|
console.log('--- 吴芳完整档案数据统计 ---')
|
|
console.log('contracts:', emp.contracts.length)
|
|
console.log('payslips:', emp.payslips.length)
|
|
console.log('overtimeRecords:', emp.overtimeRecords.length)
|
|
console.log('disciplinaryRecords:', emp.disciplinaryRecords.length)
|
|
console.log('attendanceRecords:', emp.attendanceRecords.length)
|
|
console.log('trainingRecords:', emp.trainingRecords.length)
|
|
console.log('performanceRecords:', emp.performanceRecords.length)
|
|
console.log('terminations:', emp.terminations.length, JSON.stringify(emp.terminations.map(t => ({ status: t.status, type: t.type }))))
|
|
console.log('attachments:', emp.attachments.length)
|
|
console.log('socialInsRecords:', (emp as any).socialInsRecords?.length)
|
|
console.log('housingFundRecords:', (emp as any).housingFundRecords?.length)
|
|
}
|
|
await prisma.$disconnect()
|
|
}
|
|
|
|
main().catch(console.error)
|