feat: 城市变更功能完善及跨页面缓存刷新修复
- 城市变更使用CITY_CHANGE类型替代ADJUST,月度办理显示减员/新增(城市变更) - 在保人员查询排除本月已关闭记录(gte→gt)和本月新增记录(lte→lt) - 社保/公积金减员查询包含CITY_CHANGE类型 - 缴纳记录表格添加城市列显示 - 后端profile API从快照提取city字段 - 修复旧快照缺失city字段的数据 - 修复旧记录changeType为CITY_CHANGE,endMonth与新记录startMonth一致 - 城市变更必填原因,写入备注和审计日志 - 员工详情页添加变更历史Tab - 移除薪酬社保Tab下重复的参保城市变更子Tab - 全局修复跨页面mutation缓存刷新:调薪/调部门/离职/重新入职/批量续签/批量解聘/社保公积金调基/月度办理/撤销解聘均刷新roster-profile
This commit is contained in:
@@ -172,11 +172,46 @@ router.get('/:id/profile', authMiddleware, async (req: AuthRequest, res, next) =
|
||||
performanceRecords: { orderBy: { period: 'desc' } },
|
||||
terminations: { orderBy: { createdAt: 'desc' } },
|
||||
attachments: true,
|
||||
socialInsRecords: { orderBy: { startMonth: 'desc' } },
|
||||
housingFundRecords: { orderBy: { startMonth: 'desc' } },
|
||||
salaryChanges: { orderBy: { effectiveDate: 'desc' } },
|
||||
departmentRecords: { orderBy: { effectiveMonth: 'desc' } },
|
||||
},
|
||||
})
|
||||
if (!employee) {
|
||||
return res.status(404).json({ success: false, error: { code: 'NOT_FOUND', message: '员工不存在' } })
|
||||
}
|
||||
|
||||
// 查询该员工相关的月度办理记录(从快照中筛选该员工)
|
||||
const allProcesses = await prisma.socialMonthlyProcess.findMany({
|
||||
where: { orgId: req.user!.orgId },
|
||||
orderBy: { month: 'desc' },
|
||||
})
|
||||
const employeeId = req.params.id
|
||||
const monthlyProcessRecords: any[] = []
|
||||
for (const p of allProcesses) {
|
||||
const snap = p.snapshot as any
|
||||
// 从增减员快照中筛选
|
||||
const changes = snap.changes
|
||||
const active = snap.active
|
||||
const type = p.type
|
||||
let found = false
|
||||
let recordData: any = { month: p.month, type, processedAt: p.processedAt, status: p.status }
|
||||
if (changes?.additions) {
|
||||
const item = changes.additions.find((a: any) => a.employeeId === employeeId)
|
||||
if (item) { recordData.changeType = '新增'; recordData.detail = item.detail; recordData.base = item.base; recordData.city = item.city; found = true }
|
||||
}
|
||||
if (!found && changes?.reductions) {
|
||||
const item = changes.reductions.find((a: any) => a.employeeId === employeeId)
|
||||
if (item) { recordData.changeType = '减少'; recordData.detail = item.detail; recordData.base = item.base; recordData.city = item.city; found = true }
|
||||
}
|
||||
if (!found && active?.items) {
|
||||
const item = active.items.find((a: any) => a.employeeId === employeeId)
|
||||
if (item) { recordData.changeType = '正常在保'; recordData.detail = item.detail; recordData.base = item.base; recordData.city = item.city; found = true }
|
||||
}
|
||||
if (found) monthlyProcessRecords.push(recordData)
|
||||
}
|
||||
|
||||
const { monthlySalary, bankAccount, idCardNumber, ...rest } = employee
|
||||
const today = new Date()
|
||||
today.setHours(0, 0, 0, 0)
|
||||
@@ -189,6 +224,7 @@ router.get('/:id/profile', authMiddleware, async (req: AuthRequest, res, next) =
|
||||
monthlySalary: safeDecrypt(monthlySalary),
|
||||
bankAccount: bankAccount ? safeDecrypt(bankAccount).toString() : null,
|
||||
idCardNumber: idCardNumber ? safeDecrypt(idCardNumber).toString() : null,
|
||||
monthlyProcessRecords,
|
||||
},
|
||||
})
|
||||
} catch (err) {
|
||||
|
||||
@@ -787,6 +787,57 @@ router.post('/housing-config/:id/reset-adjustment', async (req: AuthRequest, res
|
||||
|
||||
// ========== 月度增减员 ==========
|
||||
|
||||
/** 根据基数和社保配置计算各项企业/个人缴费明细 */
|
||||
function calcSocialDetail(base: number, config: any) {
|
||||
const actualBase = Math.min(Math.max(base, config.baseMin), config.baseMax)
|
||||
const items = [
|
||||
{ name: '养老', orgRate: config.pensionOrg, empRate: config.pensionEmp, orgAmount: actualBase * config.pensionOrg / 100, empAmount: actualBase * config.pensionEmp / 100 },
|
||||
{ name: '医疗', orgRate: config.medicalOrg, empRate: config.medicalEmp, orgAmount: actualBase * config.medicalOrg / 100, empAmount: actualBase * config.medicalEmp / 100 },
|
||||
{ name: '失业', orgRate: config.unemploymentOrg, empRate: config.unemploymentEmp, orgAmount: actualBase * config.unemploymentOrg / 100, empAmount: actualBase * config.unemploymentEmp / 100 },
|
||||
{ name: '工伤', orgRate: config.injuryOrg, empRate: 0, orgAmount: actualBase * config.injuryOrg / 100, empAmount: 0 },
|
||||
{ name: '生育', orgRate: config.maternityOrg, empRate: 0, orgAmount: actualBase * config.maternityOrg / 100, empAmount: 0 },
|
||||
]
|
||||
const totalOrg = items.reduce((s, i) => s + i.orgAmount, 0)
|
||||
const totalEmp = items.reduce((s, i) => s + i.empAmount, 0)
|
||||
return { actualBase, items, totalOrg, totalEmp }
|
||||
}
|
||||
|
||||
/** 根据基数和公积金配置计算企业/个人缴费明细 */
|
||||
function calcHousingDetail(base: number, config: any) {
|
||||
const actualBase = Math.min(Math.max(base, config.baseMin), config.baseMax)
|
||||
const orgAmount = actualBase * config.housingOrg / 100
|
||||
const empAmount = actualBase * config.housingEmp / 100
|
||||
return { actualBase, orgAmount, empAmount, total: orgAmount + empAmount }
|
||||
}
|
||||
|
||||
/** 按月份匹配社保配置版本 */
|
||||
async function getSocialConfigByMonth(orgId: string, month: string, city?: string) {
|
||||
const where: any = { orgId }
|
||||
if (city) where.city = city
|
||||
let config = await prisma.socialInsuranceConfig.findFirst({
|
||||
where: { ...where, effectiveFrom: { lte: month }, OR: [{ effectiveTo: null }, { effectiveTo: { gte: month } }] },
|
||||
orderBy: { effectiveFrom: 'desc' },
|
||||
})
|
||||
if (!config) {
|
||||
config = await prisma.socialInsuranceConfig.findFirst({ where: { ...where, isCurrent: true } })
|
||||
}
|
||||
return config
|
||||
}
|
||||
|
||||
/** 按月份匹配公积金配置版本 */
|
||||
async function getHousingConfigByMonth(orgId: string, month: string, city?: string) {
|
||||
const where: any = { orgId }
|
||||
if (city) where.city = city
|
||||
let config = await prisma.housingFundConfig.findFirst({
|
||||
where: { ...where, effectiveFrom: { lte: month }, OR: [{ effectiveTo: null }, { effectiveTo: { gte: month } }] },
|
||||
orderBy: { effectiveFrom: 'desc' },
|
||||
})
|
||||
if (!config) {
|
||||
config = await prisma.housingFundConfig.findFirst({ where: { ...where, isCurrent: true } })
|
||||
}
|
||||
return config
|
||||
}
|
||||
|
||||
// 社保月度增减员
|
||||
router.get('/monthly-changes', async (req: AuthRequest, res: Response, next: NextFunction) => {
|
||||
try {
|
||||
@@ -800,33 +851,59 @@ router.get('/monthly-changes', async (req: AuthRequest, res: Response, next: Nex
|
||||
orderBy: { createdAt: 'asc' },
|
||||
})
|
||||
|
||||
// 减员:endMonth == month 且 changeType == TERMINATION
|
||||
// 减员:endMonth == month 且 changeType 为 TERMINATION 或 CITY_CHANGE
|
||||
const reductions = await prisma.employeeSocialInsRecord.findMany({
|
||||
where: { orgId, endMonth: month, changeType: 'TERMINATION' },
|
||||
where: { orgId, endMonth: month, changeType: { in: ['TERMINATION', 'CITY_CHANGE'] } },
|
||||
include: { employee: { select: { name: true, department: true, idCardNumber: true } } },
|
||||
orderBy: { createdAt: 'asc' },
|
||||
})
|
||||
|
||||
// 按城市缓存配置
|
||||
const configCache = new Map<string, any>()
|
||||
const getConfigForCity = async (city: string) => {
|
||||
if (!configCache.has(city)) {
|
||||
configCache.set(city, await getSocialConfigByMonth(orgId, month, city))
|
||||
}
|
||||
return configCache.get(city)
|
||||
}
|
||||
|
||||
const mapRecord = async (r: any) => {
|
||||
const config = await getConfigForCity(r.city)
|
||||
const detail = config ? calcSocialDetail(r.base, config) : null
|
||||
return {
|
||||
employeeId: r.employeeId,
|
||||
name: r.employee.name,
|
||||
department: r.employee.department,
|
||||
city: r.city,
|
||||
base: r.base,
|
||||
startMonth: r.startMonth,
|
||||
endMonth: r.endMonth,
|
||||
changeType: r.changeType,
|
||||
detail: detail ? {
|
||||
items: detail.items,
|
||||
totalOrg: detail.totalOrg,
|
||||
totalEmp: detail.totalEmp,
|
||||
total: detail.totalOrg + detail.totalEmp,
|
||||
} : null,
|
||||
}
|
||||
}
|
||||
|
||||
// 按城市分组
|
||||
const allRecords = [...additions, ...reductions]
|
||||
const cities = [...new Set(allRecords.map((r) => r.city))]
|
||||
const configs: Record<string, any> = {}
|
||||
for (const c of cities) {
|
||||
const cfg = await getConfigForCity(c)
|
||||
if (cfg) configs[c] = { city: cfg.city, effectiveFrom: cfg.effectiveFrom, baseMin: cfg.baseMin, baseMax: cfg.baseMax }
|
||||
}
|
||||
|
||||
res.json({
|
||||
success: true,
|
||||
data: {
|
||||
month,
|
||||
additions: additions.map((r) => ({
|
||||
employeeId: r.employeeId,
|
||||
name: r.employee.name,
|
||||
department: r.employee.department,
|
||||
base: r.base,
|
||||
startMonth: r.startMonth,
|
||||
changeType: r.changeType,
|
||||
})),
|
||||
reductions: reductions.map((r) => ({
|
||||
employeeId: r.employeeId,
|
||||
name: r.employee.name,
|
||||
department: r.employee.department,
|
||||
base: r.base,
|
||||
endMonth: r.endMonth,
|
||||
changeType: r.changeType,
|
||||
})),
|
||||
configs,
|
||||
additions: await Promise.all(additions.map(mapRecord)),
|
||||
reductions: await Promise.all(reductions.map(mapRecord)),
|
||||
},
|
||||
})
|
||||
} catch (err) {
|
||||
@@ -847,31 +924,50 @@ router.get('/housing/monthly-changes', async (req: AuthRequest, res: Response, n
|
||||
})
|
||||
|
||||
const reductions = await prisma.employeeHousingFundRecord.findMany({
|
||||
where: { orgId, endMonth: month, changeType: 'TERMINATION' },
|
||||
where: { orgId, endMonth: month, changeType: { in: ['TERMINATION', 'CITY_CHANGE'] } },
|
||||
include: { employee: { select: { name: true, department: true, idCardNumber: true } } },
|
||||
orderBy: { createdAt: 'asc' },
|
||||
})
|
||||
|
||||
const configCache = new Map<string, any>()
|
||||
const getConfigForCity = async (city: string) => {
|
||||
if (!configCache.has(city)) {
|
||||
configCache.set(city, await getHousingConfigByMonth(orgId, month, city))
|
||||
}
|
||||
return configCache.get(city)
|
||||
}
|
||||
|
||||
const mapRecord = async (r: any) => {
|
||||
const config = await getConfigForCity(r.city)
|
||||
const detail = config ? calcHousingDetail(r.base, config) : null
|
||||
return {
|
||||
employeeId: r.employeeId,
|
||||
name: r.employee.name,
|
||||
department: r.employee.department,
|
||||
city: r.city,
|
||||
base: r.base,
|
||||
startMonth: r.startMonth,
|
||||
endMonth: r.endMonth,
|
||||
changeType: r.changeType,
|
||||
detail: detail ? { orgAmount: detail.orgAmount, empAmount: detail.empAmount, total: detail.total } : null,
|
||||
}
|
||||
}
|
||||
|
||||
const allRecords = [...additions, ...reductions]
|
||||
const cities = [...new Set(allRecords.map((r) => r.city))]
|
||||
const configs: Record<string, any> = {}
|
||||
for (const c of cities) {
|
||||
const cfg = await getConfigForCity(c)
|
||||
if (cfg) configs[c] = { city: cfg.city, effectiveFrom: cfg.effectiveFrom, baseMin: cfg.baseMin, baseMax: cfg.baseMax, housingOrg: cfg.housingOrg, housingEmp: cfg.housingEmp }
|
||||
}
|
||||
|
||||
res.json({
|
||||
success: true,
|
||||
data: {
|
||||
month,
|
||||
additions: additions.map((r) => ({
|
||||
employeeId: r.employeeId,
|
||||
name: r.employee.name,
|
||||
department: r.employee.department,
|
||||
base: r.base,
|
||||
startMonth: r.startMonth,
|
||||
changeType: r.changeType,
|
||||
})),
|
||||
reductions: reductions.map((r) => ({
|
||||
employeeId: r.employeeId,
|
||||
name: r.employee.name,
|
||||
department: r.employee.department,
|
||||
base: r.base,
|
||||
endMonth: r.endMonth,
|
||||
changeType: r.changeType,
|
||||
})),
|
||||
configs,
|
||||
additions: await Promise.all(additions.map(mapRecord)),
|
||||
reductions: await Promise.all(reductions.map(mapRecord)),
|
||||
},
|
||||
})
|
||||
} catch (err) {
|
||||
@@ -890,27 +986,52 @@ router.get('/active-declaration', async (req: AuthRequest, res: Response, next:
|
||||
const records = await prisma.employeeSocialInsRecord.findMany({
|
||||
where: {
|
||||
orgId,
|
||||
startMonth: { lte: month },
|
||||
OR: [{ endMonth: null }, { endMonth: { gte: month } }],
|
||||
startMonth: { lt: month },
|
||||
OR: [{ endMonth: null }, { endMonth: { gt: month } }],
|
||||
},
|
||||
include: { employee: { select: { name: true, department: true, idCardNumber: true, hireDate: true } } },
|
||||
orderBy: { createdAt: 'asc' },
|
||||
})
|
||||
|
||||
const configCache = new Map<string, any>()
|
||||
const getConfigForCity = async (city: string) => {
|
||||
if (!configCache.has(city)) {
|
||||
configCache.set(city, await getSocialConfigByMonth(orgId, month, city))
|
||||
}
|
||||
return configCache.get(city)
|
||||
}
|
||||
|
||||
const items = await Promise.all(records.map(async (r) => {
|
||||
const config = await getConfigForCity(r.city)
|
||||
const detail = config ? calcSocialDetail(r.base, config) : null
|
||||
return {
|
||||
employeeId: r.employeeId,
|
||||
name: r.employee.name,
|
||||
department: r.employee.department,
|
||||
city: r.city,
|
||||
base: r.base,
|
||||
startMonth: r.startMonth,
|
||||
endMonth: r.endMonth,
|
||||
changeType: r.changeType,
|
||||
detail: detail ? {
|
||||
items: detail.items,
|
||||
totalOrg: detail.totalOrg,
|
||||
totalEmp: detail.totalEmp,
|
||||
total: detail.totalOrg + detail.totalEmp,
|
||||
} : null,
|
||||
}
|
||||
}))
|
||||
|
||||
const cities = [...new Set(records.map((r) => r.city))]
|
||||
const configs: Record<string, any> = {}
|
||||
for (const c of cities) {
|
||||
const cfg = await getConfigForCity(c)
|
||||
if (cfg) configs[c] = { city: cfg.city, effectiveFrom: cfg.effectiveFrom, baseMin: cfg.baseMin, baseMax: cfg.baseMax }
|
||||
}
|
||||
|
||||
res.json({
|
||||
success: true,
|
||||
data: {
|
||||
month,
|
||||
items: records.map((r) => ({
|
||||
employeeId: r.employeeId,
|
||||
name: r.employee.name,
|
||||
department: r.employee.department,
|
||||
base: r.base,
|
||||
startMonth: r.startMonth,
|
||||
endMonth: r.endMonth,
|
||||
changeType: r.changeType,
|
||||
})),
|
||||
},
|
||||
data: { month, configs, items },
|
||||
})
|
||||
} catch (err) {
|
||||
next(err)
|
||||
@@ -926,26 +1047,85 @@ router.get('/housing/active-declaration', async (req: AuthRequest, res: Response
|
||||
const records = await prisma.employeeHousingFundRecord.findMany({
|
||||
where: {
|
||||
orgId,
|
||||
startMonth: { lte: month },
|
||||
OR: [{ endMonth: null }, { endMonth: { gte: month } }],
|
||||
startMonth: { lt: month },
|
||||
OR: [{ endMonth: null }, { endMonth: { gt: month } }],
|
||||
},
|
||||
include: { employee: { select: { name: true, department: true, idCardNumber: true, hireDate: true } } },
|
||||
orderBy: { createdAt: 'asc' },
|
||||
})
|
||||
|
||||
const configCache = new Map<string, any>()
|
||||
const getConfigForCity = async (city: string) => {
|
||||
if (!configCache.has(city)) {
|
||||
configCache.set(city, await getHousingConfigByMonth(orgId, month, city))
|
||||
}
|
||||
return configCache.get(city)
|
||||
}
|
||||
|
||||
const items = await Promise.all(records.map(async (r) => {
|
||||
const config = await getConfigForCity(r.city)
|
||||
const detail = config ? calcHousingDetail(r.base, config) : null
|
||||
return {
|
||||
employeeId: r.employeeId,
|
||||
name: r.employee.name,
|
||||
department: r.employee.department,
|
||||
city: r.city,
|
||||
base: r.base,
|
||||
startMonth: r.startMonth,
|
||||
endMonth: r.endMonth,
|
||||
changeType: r.changeType,
|
||||
detail: detail ? { orgAmount: detail.orgAmount, empAmount: detail.empAmount, total: detail.total } : null,
|
||||
}
|
||||
}))
|
||||
|
||||
const cities = [...new Set(records.map((r) => r.city))]
|
||||
const configs: Record<string, any> = {}
|
||||
for (const c of cities) {
|
||||
const cfg = await getConfigForCity(c)
|
||||
if (cfg) configs[c] = { city: cfg.city, effectiveFrom: cfg.effectiveFrom, baseMin: cfg.baseMin, baseMax: cfg.baseMax, housingOrg: cfg.housingOrg, housingEmp: cfg.housingEmp }
|
||||
}
|
||||
|
||||
res.json({
|
||||
success: true,
|
||||
data: { month, configs, items },
|
||||
})
|
||||
} catch (err) {
|
||||
next(err)
|
||||
}
|
||||
})
|
||||
|
||||
// ========== 月度办理完成(保存快照) ==========
|
||||
|
||||
// 列出所有已办理月份(用于办理总览)
|
||||
router.get('/monthly-process/list', async (req: AuthRequest, res: Response, next: NextFunction) => {
|
||||
try {
|
||||
const orgId = req.user!.orgId
|
||||
const records = await prisma.socialMonthlyProcess.findMany({
|
||||
where: { orgId },
|
||||
orderBy: { month: 'desc' },
|
||||
select: { id: true, month: true, type: true, status: true, processedAt: true, processedBy: true },
|
||||
})
|
||||
res.json({ success: true, data: records })
|
||||
} catch (err) {
|
||||
next(err)
|
||||
}
|
||||
})
|
||||
|
||||
// 查询某月办理状态
|
||||
router.get('/monthly-process/status', async (req: AuthRequest, res: Response, next: NextFunction) => {
|
||||
try {
|
||||
const month = (req.query.month as string) || new Date().toISOString().slice(0, 7)
|
||||
const orgId = req.user!.orgId
|
||||
|
||||
const records = await prisma.socialMonthlyProcess.findMany({
|
||||
where: { orgId, month },
|
||||
})
|
||||
res.json({
|
||||
success: true,
|
||||
data: {
|
||||
month,
|
||||
items: records.map((r) => ({
|
||||
employeeId: r.employeeId,
|
||||
name: r.employee.name,
|
||||
department: r.employee.department,
|
||||
base: r.base,
|
||||
startMonth: r.startMonth,
|
||||
endMonth: r.endMonth,
|
||||
changeType: r.changeType,
|
||||
})),
|
||||
social: records.find((r) => r.type === 'SOCIAL') || null,
|
||||
housing: records.find((r) => r.type === 'HOUSING') || null,
|
||||
},
|
||||
})
|
||||
} catch (err) {
|
||||
@@ -953,4 +1133,145 @@ router.get('/housing/active-declaration', async (req: AuthRequest, res: Response
|
||||
}
|
||||
})
|
||||
|
||||
// 办理完成(保存快照)
|
||||
router.post('/monthly-process/complete', async (req: AuthRequest, res: Response, next: NextFunction) => {
|
||||
try {
|
||||
const { month, type, snapshot } = req.body as { month: string; type: 'SOCIAL' | 'HOUSING'; snapshot: any }
|
||||
const orgId = req.user!.orgId
|
||||
|
||||
if (!month || !type || !snapshot) {
|
||||
return res.status(400).json({ success: false, message: '缺少必要参数' })
|
||||
}
|
||||
|
||||
const existing = await prisma.socialMonthlyProcess.findUnique({
|
||||
where: { orgId_month_type: { orgId, month, type } },
|
||||
})
|
||||
|
||||
if (existing) {
|
||||
// 已存在则更新快照
|
||||
const updated = await prisma.socialMonthlyProcess.update({
|
||||
where: { id: existing.id },
|
||||
data: { snapshot, processedBy: req.user!.id, processedAt: new Date() },
|
||||
})
|
||||
return res.json({ success: true, data: updated })
|
||||
}
|
||||
|
||||
const record = await prisma.socialMonthlyProcess.create({
|
||||
data: {
|
||||
orgId,
|
||||
month,
|
||||
type,
|
||||
snapshot,
|
||||
processedBy: req.user!.id,
|
||||
createdBy: req.user!.id,
|
||||
},
|
||||
})
|
||||
res.json({ success: true, data: record })
|
||||
} catch (err) {
|
||||
next(err)
|
||||
}
|
||||
})
|
||||
|
||||
// ========== 记录修正(直接更新 + 审计日志) ==========
|
||||
|
||||
// 修正社保记录
|
||||
router.put('/records/social/:id/correct', async (req: AuthRequest, res: Response, next: NextFunction) => {
|
||||
try {
|
||||
const orgId = req.user!.orgId
|
||||
const { city, base, startMonth, endMonth, changeType, remark } = req.body as { city?: string; base?: number; startMonth?: string; endMonth?: string; changeType?: string; remark?: string }
|
||||
|
||||
const record = await prisma.employeeSocialInsRecord.findFirst({ where: { id: req.params.id, orgId } })
|
||||
if (!record) return res.status(404).json({ success: false, message: '记录不存在' })
|
||||
|
||||
const oldData = { city: record.city, base: record.base, startMonth: record.startMonth, endMonth: record.endMonth, changeType: record.changeType, remark: record.remark }
|
||||
const updateData: any = {}
|
||||
if (city !== undefined) updateData.city = city
|
||||
if (base !== undefined) updateData.base = base
|
||||
if (startMonth !== undefined) updateData.startMonth = startMonth
|
||||
if (endMonth !== undefined) updateData.endMonth = endMonth || null
|
||||
if (changeType !== undefined) updateData.changeType = changeType
|
||||
if (remark !== undefined) updateData.remark = remark
|
||||
|
||||
const updated = await prisma.employeeSocialInsRecord.update({ where: { id: req.params.id }, data: updateData })
|
||||
|
||||
// 同步员工便捷字段(如果修正的是当前在保记录)
|
||||
if (!updated.endMonth) {
|
||||
await prisma.employee.update({
|
||||
where: { id: record.employeeId },
|
||||
data: {
|
||||
...(city !== undefined ? { city } : {}),
|
||||
...(base !== undefined ? { socialInsBase: base } : {}),
|
||||
...(startMonth !== undefined ? { socialInsStartMonth: startMonth } : {}),
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
// 写审计日志
|
||||
await prisma.auditLog.create({
|
||||
data: {
|
||||
orgId,
|
||||
userId: req.user!.id,
|
||||
action: 'CORRECT',
|
||||
entity: 'EmployeeSocialInsRecord',
|
||||
entityId: req.params.id,
|
||||
detail: { old: oldData, new: updateData, reason: req.body.reason || '数据修正' },
|
||||
},
|
||||
})
|
||||
|
||||
res.json({ success: true, data: updated })
|
||||
} catch (err) {
|
||||
next(err)
|
||||
}
|
||||
})
|
||||
|
||||
// 修正公积金记录
|
||||
router.put('/records/housing/:id/correct', async (req: AuthRequest, res: Response, next: NextFunction) => {
|
||||
try {
|
||||
const orgId = req.user!.orgId
|
||||
const { city, base, startMonth, endMonth, changeType, remark } = req.body as { city?: string; base?: number; startMonth?: string; endMonth?: string; changeType?: string; remark?: string }
|
||||
|
||||
const record = await prisma.employeeHousingFundRecord.findFirst({ where: { id: req.params.id, orgId } })
|
||||
if (!record) return res.status(404).json({ success: false, message: '记录不存在' })
|
||||
|
||||
const oldData = { city: record.city, base: record.base, startMonth: record.startMonth, endMonth: record.endMonth, changeType: record.changeType, remark: record.remark }
|
||||
const updateData: any = {}
|
||||
if (city !== undefined) updateData.city = city
|
||||
if (base !== undefined) updateData.base = base
|
||||
if (startMonth !== undefined) updateData.startMonth = startMonth
|
||||
if (endMonth !== undefined) updateData.endMonth = endMonth || null
|
||||
if (changeType !== undefined) updateData.changeType = changeType
|
||||
if (remark !== undefined) updateData.remark = remark
|
||||
|
||||
const updated = await prisma.employeeHousingFundRecord.update({ where: { id: req.params.id }, data: updateData })
|
||||
|
||||
// 同步员工便捷字段(如果修正的是当前在保记录)
|
||||
if (!updated.endMonth) {
|
||||
await prisma.employee.update({
|
||||
where: { id: record.employeeId },
|
||||
data: {
|
||||
...(city !== undefined ? { city } : {}),
|
||||
...(base !== undefined ? { housingFundBase: base } : {}),
|
||||
...(startMonth !== undefined ? { housingFundStartMonth: startMonth } : {}),
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
// 写审计日志
|
||||
await prisma.auditLog.create({
|
||||
data: {
|
||||
orgId,
|
||||
userId: req.user!.id,
|
||||
action: 'CORRECT',
|
||||
entity: 'EmployeeHousingFundRecord',
|
||||
entityId: req.params.id,
|
||||
detail: { old: oldData, new: updateData, reason: req.body.reason || '数据修正' },
|
||||
},
|
||||
})
|
||||
|
||||
res.json({ success: true, data: updated })
|
||||
} catch (err) {
|
||||
next(err)
|
||||
}
|
||||
})
|
||||
|
||||
export default router
|
||||
|
||||
Reference in New Issue
Block a user