feat: 账户关联根部门 + 员工新增自动带出账户
1. 账户管理:新建/编辑时可勾选关联 level=0 根部门(公司/分公司/子公司) 2. 后端新增 API: - PUT /social/accounts/:id/departments 批量关联根部门 - GET /social/accounts/:id/departments 查询已关联部门 - GET /social/department-account/:departmentId 按部门带出适用账户+标准 3. 部门更新 API 支持 socialAccountId/housingAccountId 字段 4. 员工新增表单:选定部门后自动带出社保公积金账户,可手动调整 5. 参保记录创建时写入 accountId Generated with [Devin](https://devin.ai) Co-Authored-By: Devin <158243242+devin-ai-integration[bot]@users.noreply.github.com>
This commit is contained in:
@@ -16,7 +16,10 @@ const createDeptSchema = z.object({
|
||||
description: z.string().max(200).optional(),
|
||||
})
|
||||
|
||||
const updateDeptSchema = createDeptSchema.partial()
|
||||
const updateDeptSchema = createDeptSchema.partial().extend({
|
||||
socialAccountId: z.string().nullable().optional(),
|
||||
housingAccountId: z.string().nullable().optional(),
|
||||
})
|
||||
|
||||
/** 获取部门树 */
|
||||
router.get('/', authMiddleware, async (req: AuthRequest, res, next) => {
|
||||
@@ -109,6 +112,8 @@ router.put('/:id', authMiddleware, async (req: AuthRequest, res, next) => {
|
||||
...(data.sortOrder !== undefined ? { sortOrder: data.sortOrder } : {}),
|
||||
...(data.description !== undefined ? { description: data.description } : {}),
|
||||
...(level !== undefined ? { level } : {}),
|
||||
...(data.socialAccountId !== undefined ? { socialAccountId: data.socialAccountId } : {}),
|
||||
...(data.housingAccountId !== undefined ? { housingAccountId: data.housingAccountId } : {}),
|
||||
},
|
||||
})
|
||||
res.json({ success: true, data: dept })
|
||||
|
||||
@@ -231,6 +231,103 @@ router.post('/accounts/:accountId/standards', async (req: AuthRequest, res: Resp
|
||||
} catch (err) { next(err) }
|
||||
})
|
||||
|
||||
// 账户关联根部门(level=0)批量设置
|
||||
router.put('/accounts/:id/departments', async (req: AuthRequest, res: Response, next: NextFunction) => {
|
||||
try {
|
||||
const orgId = req.user!.orgId
|
||||
const { id } = req.params
|
||||
const { departmentIds } = req.body as { departmentIds: string[] }
|
||||
const account = await prisma.socialAccount.findFirst({ where: { id, orgId } })
|
||||
if (!account) return res.status(404).json({ success: false, error: { code: 'NOT_FOUND', message: '账户不存在' } })
|
||||
|
||||
// 先清除该账户的所有部门关联
|
||||
const field = account.type === 'SOCIAL' ? 'socialAccountId' : 'housingAccountId'
|
||||
await prisma.department.updateMany({
|
||||
where: { orgId, [field]: id },
|
||||
data: { [field]: null },
|
||||
})
|
||||
// 批量设置新关联(仅 level=0 根部门)
|
||||
if (departmentIds && departmentIds.length > 0) {
|
||||
await prisma.department.updateMany({
|
||||
where: { id: { in: departmentIds }, orgId, level: 0 },
|
||||
data: { [field]: id },
|
||||
})
|
||||
}
|
||||
res.json({ success: true, data: { message: `已关联 ${departmentIds?.length || 0} 个根部门` } })
|
||||
} catch (err) { next(err) }
|
||||
})
|
||||
|
||||
// 获取账户已关联的根部门列表
|
||||
router.get('/accounts/:id/departments', async (req: AuthRequest, res: Response, next: NextFunction) => {
|
||||
try {
|
||||
const orgId = req.user!.orgId
|
||||
const { id } = req.params
|
||||
const account = await prisma.socialAccount.findFirst({ where: { id, orgId } })
|
||||
if (!account) return res.status(404).json({ success: false, error: { code: 'NOT_FOUND', message: '账户不存在' } })
|
||||
|
||||
const field = account.type === 'SOCIAL' ? 'socialAccountId' : 'housingAccountId'
|
||||
const departments = await prisma.department.findMany({
|
||||
where: { orgId, level: 0, [field]: id },
|
||||
select: { id: true, name: true },
|
||||
})
|
||||
res.json({ success: true, data: departments })
|
||||
} catch (err) { next(err) }
|
||||
})
|
||||
|
||||
// 按部门获取适用账户(选部门时自动带出)
|
||||
router.get('/department-account/:departmentId', async (req: AuthRequest, res: Response, next: NextFunction) => {
|
||||
try {
|
||||
const orgId = req.user!.orgId
|
||||
const { departmentId } = req.params
|
||||
// 向上找到 level=0 的根部门
|
||||
let currentDept: any = await prisma.department.findFirst({ where: { id: departmentId, orgId } })
|
||||
if (!currentDept) return res.status(404).json({ success: false, error: { code: 'NOT_FOUND', message: '部门不存在' } })
|
||||
|
||||
while (currentDept && currentDept.level > 0 && currentDept.parentId) {
|
||||
currentDept = await prisma.department.findUnique({ where: { id: currentDept.parentId } })
|
||||
}
|
||||
const rootDeptId = currentDept?.id || null
|
||||
|
||||
let socialAccount: any = null
|
||||
let housingAccount: any = null
|
||||
if (rootDeptId) {
|
||||
const rootDept = await prisma.department.findUnique({
|
||||
where: { id: rootDeptId },
|
||||
include: { socialAccount: true, housingAccount: true },
|
||||
})
|
||||
socialAccount = rootDept?.socialAccount || null
|
||||
housingAccount = rootDept?.housingAccount || null
|
||||
}
|
||||
|
||||
// 回退到公司默认账户
|
||||
if (!socialAccount) {
|
||||
socialAccount = await prisma.socialAccount.findFirst({ where: { orgId, type: 'SOCIAL', isDefault: true } })
|
||||
}
|
||||
if (!housingAccount) {
|
||||
housingAccount = await prisma.socialAccount.findFirst({ where: { orgId, type: 'HOUSING', isDefault: true } })
|
||||
}
|
||||
|
||||
// 获取当前生效标准
|
||||
const currentMonth = new Date().toISOString().slice(0, 7)
|
||||
let socialStandard: any = null
|
||||
let housingStandard: any = null
|
||||
if (socialAccount) {
|
||||
socialStandard = await prisma.socialYearStandard.findFirst({
|
||||
where: { accountId: socialAccount.id, isCurrent: true },
|
||||
orderBy: { effectiveFrom: 'desc' },
|
||||
})
|
||||
}
|
||||
if (housingAccount) {
|
||||
housingStandard = await prisma.socialYearStandard.findFirst({
|
||||
where: { accountId: housingAccount.id, isCurrent: true },
|
||||
orderBy: { effectiveFrom: 'desc' },
|
||||
})
|
||||
}
|
||||
|
||||
res.json({ success: true, data: { socialAccount, housingAccount, socialStandard, housingStandard } })
|
||||
} catch (err) { next(err) }
|
||||
})
|
||||
|
||||
// 按员工获取适用账户(通过根部门 level=0 继承,不向下到普通部门)
|
||||
router.get('/employee-account/:employeeId', async (req: AuthRequest, res: Response, next: NextFunction) => {
|
||||
try {
|
||||
|
||||
@@ -396,6 +396,7 @@ export async function createEmployee(orgId: string, userId: string, data: any) {
|
||||
changeType: 'ONBOARDING',
|
||||
createdBy: userId,
|
||||
city: data.city || '北京',
|
||||
accountId: data.socialAccountId || null,
|
||||
},
|
||||
})
|
||||
|
||||
@@ -409,6 +410,7 @@ export async function createEmployee(orgId: string, userId: string, data: any) {
|
||||
changeType: 'ONBOARDING',
|
||||
createdBy: userId,
|
||||
city: data.city || '北京',
|
||||
accountId: data.housingAccountId || null,
|
||||
},
|
||||
})
|
||||
|
||||
|
||||
Reference in New Issue
Block a user