refactor: 删除 PRE_ONBOARD 显式状态,统一按入职日期判定预入职
预入职不再需要显式设置 PRE_ONBOARD 状态,统一按入职日期自动 判定:入职日期在未来 = 预入职(不进入薪资),入职日期 <= 今天 = 正式在职。 删除内容: - schema: EmployeeStatus 枚举移除 PRE_ONBOARD - roster.routes.ts: 移除 PRE_ONBOARD 筛选和 /activate API - payroll2.routes.ts: 移除 NOT PRE_ONBOARD 排除条件 - modals.tsx: 移除入职状态选择器 保留:按 hireDate > 今天 自动判定预入职的逻辑 Generated with [Devin](https://devin.ai) Co-Authored-By: Devin <158243242+devin-ai-integration[bot]@users.noreply.github.com>
This commit is contained in:
@@ -24,7 +24,6 @@ enum Role {
|
|||||||
}
|
}
|
||||||
|
|
||||||
enum EmployeeStatus {
|
enum EmployeeStatus {
|
||||||
PRE_ONBOARD // 预入职:已录入但未正式入职,不进入薪资批次
|
|
||||||
ACTIVE
|
ACTIVE
|
||||||
RESIGNED
|
RESIGNED
|
||||||
TERMINATED
|
TERMINATED
|
||||||
|
|||||||
@@ -340,8 +340,6 @@ router.post('/batches', async (req: AuthRequest, res: Response, next: NextFuncti
|
|||||||
{ status: 'ACTIVE' },
|
{ status: 'ACTIVE' },
|
||||||
{ status: 'RESIGNED', updatedAt: { gte: monthStart, lte: monthEnd } },
|
{ status: 'RESIGNED', updatedAt: { gte: monthStart, lte: monthEnd } },
|
||||||
],
|
],
|
||||||
// 预入职员工不进入薪资批次
|
|
||||||
NOT: { status: 'PRE_ONBOARD' },
|
|
||||||
},
|
},
|
||||||
include: { contracts: { orderBy: { createdAt: 'desc' }, take: 1 } },
|
include: { contracts: { orderBy: { createdAt: 'desc' }, take: 1 } },
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -101,17 +101,12 @@ router.get('/', authMiddleware, async (req: AuthRequest, res, next) => {
|
|||||||
if (status === 'RESIGNED') {
|
if (status === 'RESIGNED') {
|
||||||
whereBase.status = 'RESIGNED'
|
whereBase.status = 'RESIGNED'
|
||||||
} else if (status === 'PRE_HIRE' || status === 'PRE_ONBOARD') {
|
} else if (status === 'PRE_HIRE' || status === 'PRE_ONBOARD') {
|
||||||
// 预入职:PRE_ONBOARD 状态,或 ACTIVE 状态但入职日期在未来
|
// 预入职:ACTIVE 状态但入职日期在未来(按入职日期自动判定)
|
||||||
whereBase.OR = [
|
whereBase.status = 'ACTIVE'
|
||||||
{ status: 'PRE_ONBOARD' },
|
whereBase.hireDate = { gt: todayEnd }
|
||||||
{ status: 'ACTIVE', hireDate: { gt: todayEnd } },
|
|
||||||
]
|
|
||||||
} else if (status === 'ACTIVE') {
|
} else if (status === 'ACTIVE') {
|
||||||
whereBase.status = 'ACTIVE'
|
whereBase.status = 'ACTIVE'
|
||||||
whereBase.hireDate = { lte: todayEnd }
|
whereBase.hireDate = { lte: todayEnd }
|
||||||
} else if (!status) {
|
|
||||||
// 无状态过滤时,排除 PRE_ONBOARD(默认只看在职和离职)
|
|
||||||
whereBase.NOT = { status: 'PRE_ONBOARD' }
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// unsigned 合同状态可以在 DB 层过滤
|
// unsigned 合同状态可以在 DB 层过滤
|
||||||
@@ -1606,20 +1601,4 @@ router.get('/contract-types', authMiddleware, (_req: AuthRequest, res) => {
|
|||||||
res.json({ success: true, data: types })
|
res.json({ success: true, data: types })
|
||||||
})
|
})
|
||||||
|
|
||||||
// 预入职转正式(PRE_ONBOARD → ACTIVE)
|
|
||||||
router.post('/:id/activate', authMiddleware, async (req: AuthRequest, res, next) => {
|
|
||||||
try {
|
|
||||||
const emp = await prisma.employee.findFirst({ where: { id: req.params.id, orgId: req.user!.orgId! } })
|
|
||||||
if (!emp) return res.status(404).json({ success: false, error: { code: 'NOT_FOUND', message: '员工不存在' } })
|
|
||||||
if (emp.status !== 'PRE_ONBOARD') {
|
|
||||||
return res.status(400).json({ success: false, error: { code: 'BAD_REQUEST', message: '该员工不是预入职状态' } })
|
|
||||||
}
|
|
||||||
const updated = await prisma.employee.update({
|
|
||||||
where: { id: emp.id },
|
|
||||||
data: { status: 'ACTIVE' },
|
|
||||||
})
|
|
||||||
res.json({ success: true, data: { id: updated.id, status: updated.status } })
|
|
||||||
} catch (err) { next(err) }
|
|
||||||
})
|
|
||||||
|
|
||||||
export default router
|
export default router
|
||||||
|
|||||||
@@ -705,7 +705,7 @@ export function AddEmployeeModal({ onClose, onSubmit, loading, error }: {
|
|||||||
return {
|
return {
|
||||||
name: '', department: '', departmentId: '', position: '', hireDate: todayStr, monthlySalary: '',
|
name: '', department: '', departmentId: '', position: '', hireDate: todayStr, monthlySalary: '',
|
||||||
idCardNumber: '', gender: '男' as '男' | '女', femaleWorkerType: '' as '' | 'CADRE' | 'WORKER', phone: '',
|
idCardNumber: '', gender: '男' as '男' | '女', femaleWorkerType: '' as '' | 'CADRE' | 'WORKER', phone: '',
|
||||||
city: '北京', education: '', status: 'ACTIVE' as 'ACTIVE' | 'PRE_ONBOARD',
|
city: '北京', education: '', status: 'ACTIVE',
|
||||||
contractType: 'FIXED' as 'FIXED' | 'UNFIXED' | 'UNSIGNED',
|
contractType: 'FIXED' as 'FIXED' | 'UNFIXED' | 'UNSIGNED',
|
||||||
signDate: '', startDate: todayStr, endDate: defaultEndDate,
|
signDate: '', startDate: todayStr, endDate: defaultEndDate,
|
||||||
contractYears: 3, probationMonths: 0, probationSalary: 0,
|
contractYears: 3, probationMonths: 0, probationSalary: 0,
|
||||||
@@ -1016,7 +1016,6 @@ export function AddEmployeeModal({ onClose, onSubmit, loading, error }: {
|
|||||||
</div>
|
</div>
|
||||||
<div className="grid grid-cols-4 gap-4">
|
<div className="grid grid-cols-4 gap-4">
|
||||||
<div><Label>入职日期 *</Label><Input type="date" value={form.hireDate} onChange={(e) => handleHireDateChange(e.target.value)} /></div>
|
<div><Label>入职日期 *</Label><Input type="date" value={form.hireDate} onChange={(e) => handleHireDateChange(e.target.value)} /></div>
|
||||||
<div><Label>入职状态</Label><Select value={form.status} onChange={(e) => setForm({ ...form, status: e.target.value as 'ACTIVE' | 'PRE_ONBOARD' })}><option value="ACTIVE">正式入职</option><option value="PRE_ONBOARD">预入职(不进入薪资)</option></Select></div>
|
|
||||||
<div><Label>月工资 *</Label><Input type="number" value={form.monthlySalary} onChange={(e) => setForm({ ...form, monthlySalary: e.target.value })} placeholder="元" /></div>
|
<div><Label>月工资 *</Label><Input type="number" value={form.monthlySalary} onChange={(e) => setForm({ ...form, monthlySalary: e.target.value })} placeholder="元" /></div>
|
||||||
<div><Label>手机号</Label><Input value={form.phone} onChange={(e) => {
|
<div><Label>手机号</Label><Input value={form.phone} onChange={(e) => {
|
||||||
const phone = e.target.value.replace(/\D/g, '').slice(0, 11)
|
const phone = e.target.value.replace(/\D/g, '').slice(0, 11)
|
||||||
|
|||||||
Reference in New Issue
Block a user