feat: AIHR 智能人力资源管理系统初始提交
- 员工花名册管理(加密存储、导入导出) - 薪酬管理(发薪批次、薪酬模版、加班费计算、工资条) - 社保公积金(多城市配置、版本管理、基数调整) - 解聘管理(6步流程、证据链、工作交接) - AI 助手(合同审查、风险预测、RAG 知识库) - Dashboard 仪表盘 - 设置与通知
This commit is contained in:
@@ -0,0 +1,577 @@
|
||||
import { Router, Response, NextFunction } from 'express'
|
||||
import prisma from '../lib/prisma'
|
||||
import { decrypt } from '../lib/crypto'
|
||||
import { authMiddleware, AuthRequest } from '../middleware/auth'
|
||||
import { z } from 'zod'
|
||||
|
||||
const router = Router()
|
||||
router.use(authMiddleware)
|
||||
|
||||
// ========== 加班费记录 ==========
|
||||
|
||||
const overtimeSchema = z.object({
|
||||
employeeId: z.string().min(1),
|
||||
month: z.string().regex(/^\d{4}-\d{2}$/),
|
||||
monthlyWage: z.number().positive(),
|
||||
weekdayHours: z.number().min(0).default(0),
|
||||
weekendHours: z.number().min(0).default(0),
|
||||
holidayHours: z.number().min(0).default(0),
|
||||
})
|
||||
|
||||
// 获取加班费记录列表
|
||||
router.get('/overtime', async (req: AuthRequest, res: Response, next: NextFunction) => {
|
||||
try {
|
||||
const { employeeId, month } = req.query
|
||||
const records = await prisma.overtimeRecord.findMany({
|
||||
where: {
|
||||
orgId: req.user!.orgId,
|
||||
...(employeeId ? { employeeId: String(employeeId) } : {}),
|
||||
...(month ? { month: String(month) } : {}),
|
||||
},
|
||||
include: { employee: { select: { id: true, name: true, department: true } } },
|
||||
orderBy: { createdAt: 'desc' },
|
||||
})
|
||||
res.json({ success: true, data: records })
|
||||
} catch (err) {
|
||||
next(err)
|
||||
}
|
||||
})
|
||||
|
||||
// 保存加班费记录
|
||||
router.post('/overtime', async (req: AuthRequest, res: Response, next: NextFunction) => {
|
||||
try {
|
||||
const data = overtimeSchema.parse(req.body)
|
||||
const hourlyWage = data.monthlyWage / 21.75 / 8
|
||||
const weekdayPay = hourlyWage * 1.5 * data.weekdayHours
|
||||
const weekendPay = hourlyWage * 2.0 * data.weekendHours
|
||||
const holidayPay = hourlyWage * 3.0 * data.holidayHours
|
||||
const totalPay = weekdayPay + weekendPay + holidayPay
|
||||
|
||||
const record = await prisma.overtimeRecord.upsert({
|
||||
where: {
|
||||
employeeId_month: { employeeId: data.employeeId, month: data.month },
|
||||
},
|
||||
update: {
|
||||
weekdayHours: data.weekdayHours,
|
||||
weekendHours: data.weekendHours,
|
||||
holidayHours: data.holidayHours,
|
||||
weekdayPay,
|
||||
weekendPay,
|
||||
holidayPay,
|
||||
totalPay,
|
||||
},
|
||||
create: {
|
||||
orgId: req.user!.orgId,
|
||||
employeeId: data.employeeId,
|
||||
month: data.month,
|
||||
weekdayHours: data.weekdayHours,
|
||||
weekendHours: data.weekendHours,
|
||||
holidayHours: data.holidayHours,
|
||||
weekdayPay,
|
||||
weekendPay,
|
||||
holidayPay,
|
||||
totalPay,
|
||||
},
|
||||
})
|
||||
res.json({ success: true, data: record })
|
||||
} catch (err) {
|
||||
next(err)
|
||||
}
|
||||
})
|
||||
|
||||
// 更新加班记录(按ID)
|
||||
const overtimeUpdateSchema = z.object({
|
||||
weekdayHours: z.number().min(0).optional(),
|
||||
weekendHours: z.number().min(0).optional(),
|
||||
holidayHours: z.number().min(0).optional(),
|
||||
monthlyWage: z.number().positive().optional(),
|
||||
})
|
||||
|
||||
router.put('/overtime/:id', async (req: AuthRequest, res: Response, next: NextFunction) => {
|
||||
try {
|
||||
const { id } = req.params
|
||||
const data = overtimeUpdateSchema.parse(req.body)
|
||||
|
||||
const existing = await prisma.overtimeRecord.findUnique({ where: { id } })
|
||||
if (!existing) {
|
||||
res.status(404).json({ success: false, message: '记录不存在' })
|
||||
return
|
||||
}
|
||||
|
||||
const monthlyWage = data.monthlyWage ?? 0
|
||||
const weekdayHours = data.weekdayHours ?? existing.weekdayHours
|
||||
const weekendHours = data.weekendHours ?? existing.weekendHours
|
||||
const holidayHours = data.holidayHours ?? existing.holidayHours
|
||||
|
||||
const hourlyWage = monthlyWage / 21.75 / 8
|
||||
const weekdayPay = hourlyWage * 1.5 * weekdayHours
|
||||
const weekendPay = hourlyWage * 2.0 * weekendHours
|
||||
const holidayPay = hourlyWage * 3.0 * holidayHours
|
||||
const totalPay = weekdayPay + weekendPay + holidayPay
|
||||
|
||||
const record = await prisma.overtimeRecord.update({
|
||||
where: { id },
|
||||
data: {
|
||||
weekdayHours,
|
||||
weekendHours,
|
||||
holidayHours,
|
||||
weekdayPay,
|
||||
weekendPay,
|
||||
holidayPay,
|
||||
totalPay,
|
||||
},
|
||||
})
|
||||
res.json({ success: true, data: record })
|
||||
} catch (err) {
|
||||
next(err)
|
||||
}
|
||||
})
|
||||
|
||||
// ========== 工资条管理 ==========
|
||||
|
||||
const payslipSchema = z.object({
|
||||
employeeId: z.string().min(1),
|
||||
month: z.string().regex(/^\d{4}-\d{2}$/),
|
||||
baseSalary: z.number().min(0).default(0),
|
||||
overtimePay: z.number().min(0).default(0),
|
||||
weekdayOvertimePay: z.number().min(0).default(0),
|
||||
weekendOvertimePay: z.number().min(0).default(0),
|
||||
holidayOvertimePay: z.number().min(0).default(0),
|
||||
allowance: z.number().min(0).default(0),
|
||||
deduction: z.number().min(0).default(0),
|
||||
})
|
||||
|
||||
// 获取工资条列表
|
||||
router.get('/payslip', async (req: AuthRequest, res: Response, next: NextFunction) => {
|
||||
try {
|
||||
const { month, employeeId } = req.query
|
||||
const payslips = await prisma.payslip.findMany({
|
||||
where: {
|
||||
orgId: req.user!.orgId,
|
||||
...(month ? { month: String(month) } : {}),
|
||||
...(employeeId ? { employeeId: String(employeeId) } : {}),
|
||||
},
|
||||
include: { employee: { select: { id: true, name: true, department: true } } },
|
||||
orderBy: [{ month: 'desc' }, { employee: { name: 'asc' } }],
|
||||
})
|
||||
res.json({ success: true, data: payslips })
|
||||
} catch (err) {
|
||||
next(err)
|
||||
}
|
||||
})
|
||||
|
||||
// 创建/更新工资条
|
||||
router.post('/payslip', async (req: AuthRequest, res: Response, next: NextFunction) => {
|
||||
try {
|
||||
const data = payslipSchema.parse(req.body)
|
||||
const totalPay = data.baseSalary + data.overtimePay + data.allowance - data.deduction
|
||||
|
||||
const payslip = await prisma.payslip.upsert({
|
||||
where: {
|
||||
employeeId_month: { employeeId: data.employeeId, month: data.month },
|
||||
},
|
||||
update: {
|
||||
baseSalary: data.baseSalary,
|
||||
overtimePay: data.overtimePay,
|
||||
weekdayOvertimePay: data.weekdayOvertimePay,
|
||||
weekendOvertimePay: data.weekendOvertimePay,
|
||||
holidayOvertimePay: data.holidayOvertimePay,
|
||||
allowance: data.allowance,
|
||||
deduction: data.deduction,
|
||||
totalPay,
|
||||
},
|
||||
create: {
|
||||
orgId: req.user!.orgId,
|
||||
employeeId: data.employeeId,
|
||||
month: data.month,
|
||||
baseSalary: data.baseSalary,
|
||||
overtimePay: data.overtimePay,
|
||||
weekdayOvertimePay: data.weekdayOvertimePay,
|
||||
weekendOvertimePay: data.weekendOvertimePay,
|
||||
holidayOvertimePay: data.holidayOvertimePay,
|
||||
allowance: data.allowance,
|
||||
deduction: data.deduction,
|
||||
totalPay,
|
||||
},
|
||||
})
|
||||
res.json({ success: true, data: payslip })
|
||||
} catch (err) {
|
||||
next(err)
|
||||
}
|
||||
})
|
||||
|
||||
// 从加班费记录自动生成工资条
|
||||
router.post('/payslip/generate', async (req: AuthRequest, res: Response, next: NextFunction) => {
|
||||
try {
|
||||
const { month, employeeId, baseSalary, allowance, deduction } = req.body as {
|
||||
month: string
|
||||
employeeId: string
|
||||
baseSalary: number
|
||||
allowance?: number
|
||||
deduction?: number
|
||||
}
|
||||
|
||||
const overtime = await prisma.overtimeRecord.findUnique({
|
||||
where: { employeeId_month: { employeeId, month } },
|
||||
})
|
||||
|
||||
const overtimePay = overtime?.totalPay || 0
|
||||
const totalPay = baseSalary + overtimePay + (allowance || 0) - (deduction || 0)
|
||||
|
||||
const payslip = await prisma.payslip.upsert({
|
||||
where: { employeeId_month: { employeeId, month } },
|
||||
update: {
|
||||
baseSalary,
|
||||
overtimePay,
|
||||
weekdayOvertimePay: overtime?.weekdayPay || 0,
|
||||
weekendOvertimePay: overtime?.weekendPay || 0,
|
||||
holidayOvertimePay: overtime?.holidayPay || 0,
|
||||
allowance: allowance || 0,
|
||||
deduction: deduction || 0,
|
||||
totalPay,
|
||||
},
|
||||
create: {
|
||||
orgId: req.user!.orgId,
|
||||
employeeId,
|
||||
month,
|
||||
baseSalary,
|
||||
overtimePay,
|
||||
weekdayOvertimePay: overtime?.weekdayPay || 0,
|
||||
weekendOvertimePay: overtime?.weekendPay || 0,
|
||||
holidayOvertimePay: overtime?.holidayPay || 0,
|
||||
allowance: allowance || 0,
|
||||
deduction: deduction || 0,
|
||||
totalPay,
|
||||
},
|
||||
})
|
||||
res.json({ success: true, data: payslip })
|
||||
} catch (err) {
|
||||
next(err)
|
||||
}
|
||||
})
|
||||
|
||||
// 删除工资条
|
||||
router.delete('/payslip/:id', async (req: AuthRequest, res: Response, next: NextFunction) => {
|
||||
try {
|
||||
await prisma.payslip.delete({
|
||||
where: { id: req.params.id, orgId: req.user!.orgId },
|
||||
})
|
||||
res.json({ success: true })
|
||||
} catch (err) {
|
||||
next(err)
|
||||
}
|
||||
})
|
||||
|
||||
// ========== 批量生成工资条 ==========
|
||||
|
||||
const batchGenerateSchema = z.object({
|
||||
month: z.string().regex(/^\d{4}-\d{2}$/),
|
||||
allowances: z.record(z.string(), z.number().default(0)).optional(),
|
||||
deductions: z.record(z.string(), z.number().default(0)).optional(),
|
||||
})
|
||||
|
||||
// 批量生成全员工资条
|
||||
router.post('/payslip/batch-generate', async (req: AuthRequest, res: Response, next: NextFunction) => {
|
||||
try {
|
||||
const { month, allowances = {}, deductions = {} } = batchGenerateSchema.parse(req.body)
|
||||
|
||||
const employees = await prisma.employee.findMany({
|
||||
where: { orgId: req.user!.orgId, status: 'ACTIVE' },
|
||||
include: {
|
||||
contracts: { orderBy: { createdAt: 'desc' }, take: 1 },
|
||||
},
|
||||
})
|
||||
|
||||
const results: any[] = []
|
||||
for (const emp of employees) {
|
||||
const overtime = await prisma.overtimeRecord.findUnique({
|
||||
where: { employeeId_month: { employeeId: emp.id, month } },
|
||||
})
|
||||
|
||||
const overtimePay = overtime?.totalPay || 0
|
||||
const allowance = allowances[emp.id] || 0
|
||||
const deduction = deductions[emp.id] || 0
|
||||
|
||||
let baseSalary = 0
|
||||
if (emp.contracts[0]?.probationSalary && new Date(emp.contracts[0].startDate) > new Date(Date.now() - 365 * 24 * 60 * 60 * 1000)) {
|
||||
baseSalary = emp.contracts[0].probationSalary
|
||||
} else if (emp.monthlySalary) {
|
||||
try {
|
||||
baseSalary = Number(decrypt(emp.monthlySalary)) || 0
|
||||
} catch {
|
||||
baseSalary = Number(emp.monthlySalary) || 0
|
||||
}
|
||||
}
|
||||
|
||||
const totalPay = baseSalary + overtimePay + allowance - deduction
|
||||
|
||||
const payslip = await prisma.payslip.upsert({
|
||||
where: { employeeId_month: { employeeId: emp.id, month } },
|
||||
update: { baseSalary, overtimePay, allowance, deduction, totalPay },
|
||||
create: {
|
||||
orgId: req.user!.orgId,
|
||||
employeeId: emp.id,
|
||||
month,
|
||||
baseSalary,
|
||||
overtimePay,
|
||||
weekdayOvertimePay: overtime?.weekdayPay || 0,
|
||||
weekendOvertimePay: overtime?.weekendPay || 0,
|
||||
holidayOvertimePay: overtime?.holidayPay || 0,
|
||||
allowance,
|
||||
deduction,
|
||||
totalPay,
|
||||
},
|
||||
})
|
||||
results.push(payslip)
|
||||
}
|
||||
|
||||
res.json({ success: true, data: { generated: results.length, payslips: results } })
|
||||
} catch (err) {
|
||||
next(err)
|
||||
}
|
||||
})
|
||||
|
||||
// ========== 加班费计算规则配置 ==========
|
||||
|
||||
const overtimeConfigSchema = z.object({
|
||||
weekdayRate: z.number().min(1).default(1.5),
|
||||
weekendRate: z.number().min(1).default(2.0),
|
||||
holidayRate: z.number().min(1).default(3.0),
|
||||
monthlyDays: z.number().min(1).default(21.75),
|
||||
dailyHours: z.number().min(1).default(8),
|
||||
})
|
||||
|
||||
// 获取加班费计算规则
|
||||
router.get('/overtime/config', async (req: AuthRequest, res: Response, next: NextFunction) => {
|
||||
try {
|
||||
let config = await prisma.overtimeConfig.findUnique({ where: { orgId: req.user!.orgId } })
|
||||
if (!config) {
|
||||
config = await prisma.overtimeConfig.create({ data: { orgId: req.user!.orgId } })
|
||||
}
|
||||
res.json({ success: true, data: config })
|
||||
} catch (err) {
|
||||
next(err)
|
||||
}
|
||||
})
|
||||
|
||||
// 保存加班费计算规则
|
||||
router.post('/overtime/config', async (req: AuthRequest, res: Response, next: NextFunction) => {
|
||||
try {
|
||||
const data = overtimeConfigSchema.parse(req.body)
|
||||
const config = await prisma.overtimeConfig.upsert({
|
||||
where: { orgId: req.user!.orgId },
|
||||
update: data,
|
||||
create: { orgId: req.user!.orgId, ...data },
|
||||
})
|
||||
res.json({ success: true, data: config })
|
||||
} catch (err) {
|
||||
next(err)
|
||||
}
|
||||
})
|
||||
|
||||
// ========== 批量导入加班工时 ==========
|
||||
|
||||
const batchOvertimeSchema = z.array(
|
||||
z.object({
|
||||
employeeId: z.string().min(1),
|
||||
month: z.string().regex(/^\d{4}-\d{2}$/),
|
||||
weekdayHours: z.number().min(0).default(0),
|
||||
weekendHours: z.number().min(0).default(0),
|
||||
holidayHours: z.number().min(0).default(0),
|
||||
}),
|
||||
)
|
||||
|
||||
router.post('/overtime/batch', async (req: AuthRequest, res: Response, next: NextFunction) => {
|
||||
try {
|
||||
const items = batchOvertimeSchema.parse(req.body)
|
||||
const results: any[] = []
|
||||
|
||||
for (const data of items) {
|
||||
const record = await prisma.overtimeRecord.upsert({
|
||||
where: { employeeId_month: { employeeId: data.employeeId, month: data.month } },
|
||||
update: {
|
||||
weekdayHours: data.weekdayHours,
|
||||
weekendHours: data.weekendHours,
|
||||
holidayHours: data.holidayHours,
|
||||
weekdayPay: 0, weekendPay: 0, holidayPay: 0, totalPay: 0,
|
||||
},
|
||||
create: {
|
||||
orgId: req.user!.orgId,
|
||||
employeeId: data.employeeId,
|
||||
month: data.month,
|
||||
weekdayHours: data.weekdayHours,
|
||||
weekendHours: data.weekendHours,
|
||||
holidayHours: data.holidayHours,
|
||||
},
|
||||
})
|
||||
results.push(record)
|
||||
}
|
||||
|
||||
res.json({ success: true, data: { imported: results.length } })
|
||||
} catch (err) {
|
||||
next(err)
|
||||
}
|
||||
})
|
||||
|
||||
// ========== 批次导入加班费 ==========
|
||||
|
||||
router.post('/overtime/import-to-batch/:batchId', async (req: AuthRequest, res: Response, next: NextFunction) => {
|
||||
try {
|
||||
const { batchId } = req.params
|
||||
const orgId = req.user!.orgId
|
||||
|
||||
const batch = await prisma.payrollBatch.findFirst({ where: { id: batchId, orgId } })
|
||||
if (!batch) return res.status(404).json({ success: false, message: '批次不存在' })
|
||||
if (batch.status === 'ARCHIVED') return res.status(400).json({ success: false, message: '已归档批次不可操作' })
|
||||
|
||||
// 获取加班费计算规则
|
||||
let config = await prisma.overtimeConfig.findUnique({ where: { orgId } })
|
||||
if (!config) config = await prisma.overtimeConfig.create({ data: { orgId } })
|
||||
|
||||
// 获取该月未关联批次的加班记录
|
||||
const overtimeRecords = await prisma.overtimeRecord.findMany({
|
||||
where: { orgId, month: batch.month, batchId: null },
|
||||
include: { employee: { select: { id: true, name: true, monthlySalary: true } } },
|
||||
})
|
||||
|
||||
if (overtimeRecords.length === 0) {
|
||||
return res.json({ success: false, message: '没有可导入的加班记录(所有记录已关联批次或无数据)' })
|
||||
}
|
||||
|
||||
const results: any[] = []
|
||||
for (const ot of overtimeRecords) {
|
||||
// 获取员工月工资
|
||||
let monthlyWage = 0
|
||||
try {
|
||||
monthlyWage = ot.employee.monthlySalary ? Number(decrypt(ot.employee.monthlySalary)) : 0
|
||||
} catch {
|
||||
monthlyWage = Number(ot.employee.monthlySalary) || 0
|
||||
}
|
||||
if (!monthlyWage) continue
|
||||
|
||||
// 根据规则计算加班费
|
||||
const hourlyWage = monthlyWage / config.monthlyDays / config.dailyHours
|
||||
const weekdayPay = hourlyWage * config.weekdayRate * ot.weekdayHours
|
||||
const weekendPay = hourlyWage * config.weekendRate * ot.weekendHours
|
||||
const holidayPay = hourlyWage * config.holidayRate * ot.holidayHours
|
||||
const totalPay = weekdayPay + weekendPay + holidayPay
|
||||
|
||||
// 更新加班记录:计算金额并锁定到批次
|
||||
await prisma.overtimeRecord.update({
|
||||
where: { id: ot.id },
|
||||
data: { weekdayPay, weekendPay, holidayPay, totalPay, batchId },
|
||||
})
|
||||
|
||||
// 更新批次条目的加班费
|
||||
const entry = await prisma.batchEntry.findUnique({
|
||||
where: { batchId_employeeId: { batchId, employeeId: ot.employeeId } },
|
||||
})
|
||||
if (entry) {
|
||||
await prisma.batchEntry.update({
|
||||
where: { id: entry.id },
|
||||
data: { overtimePay: totalPay },
|
||||
})
|
||||
// 重新计算条目
|
||||
const newTotalPay = entry.baseSalary + totalPay + entry.allowance + entry.bonus - entry.deduction
|
||||
await prisma.batchEntry.update({
|
||||
where: { id: entry.id },
|
||||
data: { totalPay: newTotalPay },
|
||||
})
|
||||
}
|
||||
|
||||
results.push({ employeeId: ot.employeeId, employeeName: ot.employee.name, totalPay })
|
||||
}
|
||||
|
||||
res.json({ success: true, data: { imported: results.length, details: results } })
|
||||
} catch (err) {
|
||||
next(err)
|
||||
}
|
||||
})
|
||||
|
||||
// ========== 税率试算 ==========
|
||||
router.post('/tax-preview', async (req: AuthRequest, res: Response, next: NextFunction) => {
|
||||
try {
|
||||
const { employeeId, month, baseSalary, overtimePay, allowance, deduction, bonus, specialDeduction } = req.body
|
||||
const orgId = req.user!.orgId
|
||||
|
||||
// 获取员工和配置
|
||||
const [employee, socialConfig, housingConfig] = await Promise.all([
|
||||
employeeId ? prisma.employee.findFirst({ where: { id: employeeId, orgId } }) : null,
|
||||
prisma.socialInsuranceConfig.findFirst({
|
||||
where: { orgId, effectiveFrom: { lte: month }, OR: [{ effectiveTo: null }, { effectiveTo: { gte: month } }] },
|
||||
orderBy: { effectiveFrom: 'desc' },
|
||||
}),
|
||||
prisma.housingFundConfig.findFirst({
|
||||
where: { orgId, effectiveFrom: { lte: month }, OR: [{ effectiveTo: null }, { effectiveTo: { gte: month } }] },
|
||||
orderBy: { effectiveFrom: 'desc' },
|
||||
}),
|
||||
])
|
||||
|
||||
const emp = employee || { socialInsBase: baseSalary, housingFundBase: baseSalary }
|
||||
const socialBase = emp.socialInsBase || baseSalary
|
||||
const housingBase = emp.housingFundBase || baseSalary
|
||||
|
||||
// 计算社保公积金
|
||||
let socialEmp = 0, housingEmp = 0
|
||||
if (socialConfig) {
|
||||
const { calcSocialInsurance } = await import('../services/payroll.service')
|
||||
const social = calcSocialInsurance(socialBase, socialConfig)
|
||||
socialEmp = social.socialEmp
|
||||
}
|
||||
if (housingConfig) {
|
||||
const { calcHousingFund } = await import('../services/payroll.service')
|
||||
const housing = calcHousingFund(housingBase, housingConfig)
|
||||
housingEmp = housing.housingEmp
|
||||
}
|
||||
|
||||
// 获取 YTD 数据计算累计个税
|
||||
const year = month.slice(0, 4)
|
||||
const ytdPayslips = employeeId
|
||||
? await prisma.payslip.findMany({
|
||||
where: { employeeId, month: { startsWith: year }, status: 'PUBLISHED' },
|
||||
orderBy: { month: 'asc' },
|
||||
})
|
||||
: []
|
||||
|
||||
const ytdTaxableIncome = ytdPayslips.reduce((sum, p) => sum + (p.totalPay - p.deduction - socialEmp - housingEmp - (specialDeduction || 0)), 0)
|
||||
const ytdTaxDeducted = ytdPayslips.reduce((sum, p) => sum + (p.tax || 0), 0)
|
||||
|
||||
const { calcCumulativeTax } = await import('../services/payroll.service')
|
||||
const totalPay = (baseSalary || 0) + (overtimePay || 0) + (allowance || 0) - (deduction || 0) + (bonus || 0)
|
||||
const taxableIncome = totalPay - socialEmp - housingEmp - (specialDeduction || 0)
|
||||
const tax = calcCumulativeTax(ytdTaxableIncome + taxableIncome, ytdTaxDeducted)
|
||||
const netPay = totalPay - socialEmp - housingEmp - tax
|
||||
|
||||
res.json({
|
||||
success: true,
|
||||
data: {
|
||||
baseSalary: baseSalary || 0,
|
||||
overtimePay: overtimePay || 0,
|
||||
allowance: allowance || 0,
|
||||
deduction: deduction || 0,
|
||||
bonus: bonus || 0,
|
||||
totalPay,
|
||||
socialEmp,
|
||||
housingEmp,
|
||||
specialDeduction: specialDeduction || 0,
|
||||
taxableIncome,
|
||||
estimatedTax: tax,
|
||||
netPay,
|
||||
ytdPayslipCount: ytdPayslips.length,
|
||||
breakdown: [
|
||||
{ label: '应发合计', value: totalPay },
|
||||
{ label: '个人社保', value: -socialEmp },
|
||||
{ label: '个人公积金', value: -housingEmp },
|
||||
{ label: '专项附加扣除', value: -(specialDeduction || 0) },
|
||||
{ label: '应纳税所得额', value: taxableIncome },
|
||||
{ label: '当月个税', value: -tax },
|
||||
{ label: '实发工资', value: netPay },
|
||||
],
|
||||
},
|
||||
})
|
||||
} catch (err) {
|
||||
next(err)
|
||||
}
|
||||
})
|
||||
|
||||
export default router
|
||||
Reference in New Issue
Block a user