241 lines
8.9 KiB
JavaScript
241 lines
8.9 KiB
JavaScript
"use strict";
|
|
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
};
|
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
const express_1 = require("express");
|
|
const bcryptjs_1 = __importDefault(require("bcryptjs"));
|
|
const zod_1 = require("zod");
|
|
const prisma_1 = __importDefault(require("../lib/prisma"));
|
|
const auth_1 = require("../middleware/auth");
|
|
const validate_1 = require("../middleware/validate");
|
|
const router = (0, express_1.Router)();
|
|
const createEmployeeSchema = zod_1.z.object({
|
|
firstName: zod_1.z.string().min(1),
|
|
lastName: zod_1.z.string().min(1),
|
|
workEmail: zod_1.z.string().email(),
|
|
departmentId: zod_1.z.string().optional(),
|
|
positionId: zod_1.z.string().optional(),
|
|
locationId: zod_1.z.string().optional(),
|
|
managerId: zod_1.z.string().optional(),
|
|
employmentType: zod_1.z.enum(['FULL_TIME', 'PART_TIME', 'CONTRACT', 'INTERN']).default('FULL_TIME'),
|
|
salary: zod_1.z.number().min(0).default(0),
|
|
phone: zod_1.z.string().optional(),
|
|
gender: zod_1.z.string().optional(),
|
|
startDate: zod_1.z.string().optional(),
|
|
pfApplicable: zod_1.z.boolean().default(true),
|
|
esiApplicable: zod_1.z.boolean().default(true),
|
|
});
|
|
// GET /employees
|
|
router.get('/', auth_1.requireAuth, async (req, res) => {
|
|
try {
|
|
const { search, departmentId, status, page = '1', limit = '20' } = req.query;
|
|
const skip = (parseInt(page) - 1) * parseInt(limit);
|
|
const where = {
|
|
companyId: req.user.companyId,
|
|
};
|
|
if (search) {
|
|
where.OR = [
|
|
{ firstName: { contains: search, mode: 'insensitive' } },
|
|
{ lastName: { contains: search, mode: 'insensitive' } },
|
|
{ workEmail: { contains: search, mode: 'insensitive' } },
|
|
{ employeeCode: { contains: search, mode: 'insensitive' } },
|
|
];
|
|
}
|
|
if (departmentId)
|
|
where.departmentId = departmentId;
|
|
if (status)
|
|
where.status = status;
|
|
const [employees, total] = await Promise.all([
|
|
prisma_1.default.employee.findMany({
|
|
where,
|
|
skip,
|
|
take: parseInt(limit),
|
|
include: {
|
|
department: { select: { name: true } },
|
|
position: { select: { name: true } },
|
|
location: { select: { name: true } },
|
|
manager: { select: { firstName: true, lastName: true } },
|
|
},
|
|
orderBy: { createdAt: 'desc' },
|
|
}),
|
|
prisma_1.default.employee.count({ where }),
|
|
]);
|
|
return res.json({
|
|
data: employees,
|
|
total,
|
|
page: parseInt(page),
|
|
limit: parseInt(limit),
|
|
totalPages: Math.ceil(total / parseInt(limit)),
|
|
});
|
|
}
|
|
catch (err) {
|
|
console.error(err);
|
|
return res.status(500).json({ error: 'Internal server error' });
|
|
}
|
|
});
|
|
// GET /employees/org-chart
|
|
router.get('/org-chart', auth_1.requireAuth, async (req, res) => {
|
|
try {
|
|
const employees = await prisma_1.default.employee.findMany({
|
|
where: { companyId: req.user.companyId, status: { not: 'TERMINATED' } },
|
|
select: {
|
|
id: true,
|
|
firstName: true,
|
|
lastName: true,
|
|
managerId: true,
|
|
position: { select: { name: true } },
|
|
department: { select: { name: true } },
|
|
},
|
|
});
|
|
const buildTree = (employees, parentId = null) => {
|
|
return employees
|
|
.filter(e => e.managerId === parentId)
|
|
.map(e => ({
|
|
...e,
|
|
children: buildTree(employees, e.id),
|
|
}));
|
|
};
|
|
const tree = buildTree(employees);
|
|
return res.json(tree);
|
|
}
|
|
catch (err) {
|
|
return res.status(500).json({ error: 'Internal server error' });
|
|
}
|
|
});
|
|
// GET /employees/:id
|
|
router.get('/:id', auth_1.requireAuth, async (req, res) => {
|
|
try {
|
|
const employee = await prisma_1.default.employee.findFirst({
|
|
where: { id: req.params.id, companyId: req.user.companyId },
|
|
include: {
|
|
department: true,
|
|
position: true,
|
|
location: true,
|
|
manager: { select: { id: true, firstName: true, lastName: true } },
|
|
reports: { select: { id: true, firstName: true, lastName: true, position: { select: { name: true } } } },
|
|
leaveBalances: { include: { policy: true } },
|
|
},
|
|
});
|
|
if (!employee) {
|
|
return res.status(404).json({ error: 'Employee not found' });
|
|
}
|
|
return res.json(employee);
|
|
}
|
|
catch (err) {
|
|
return res.status(500).json({ error: 'Internal server error' });
|
|
}
|
|
});
|
|
// POST /employees
|
|
router.post('/', auth_1.requireAuth, (0, validate_1.validate)(createEmployeeSchema), async (req, res) => {
|
|
try {
|
|
const companyId = req.user.companyId;
|
|
const data = req.body;
|
|
// Generate employee code
|
|
const count = await prisma_1.default.employee.count({ where: { companyId } });
|
|
const employeeCode = `EMP${String(count + 1).padStart(3, '0')}`;
|
|
// Create user account
|
|
const tempPassword = Math.random().toString(36).slice(-8);
|
|
const passwordHash = await bcryptjs_1.default.hash(tempPassword, 12);
|
|
const user = await prisma_1.default.user.create({
|
|
data: {
|
|
companyId,
|
|
email: data.workEmail,
|
|
passwordHash,
|
|
role: 'EMPLOYEE',
|
|
},
|
|
});
|
|
const employee = await prisma_1.default.employee.create({
|
|
data: {
|
|
userId: user.id,
|
|
companyId,
|
|
employeeCode,
|
|
firstName: data.firstName,
|
|
lastName: data.lastName,
|
|
workEmail: data.workEmail,
|
|
departmentId: data.departmentId || null,
|
|
positionId: data.positionId || null,
|
|
locationId: data.locationId || null,
|
|
managerId: data.managerId || null,
|
|
employmentType: data.employmentType,
|
|
salary: data.salary,
|
|
phone: data.phone || null,
|
|
gender: data.gender || null,
|
|
startDate: data.startDate ? new Date(data.startDate) : new Date(),
|
|
pfApplicable: data.pfApplicable,
|
|
esiApplicable: data.esiApplicable,
|
|
},
|
|
include: {
|
|
department: true,
|
|
position: true,
|
|
location: true,
|
|
},
|
|
});
|
|
// Initialize leave balances for current year
|
|
const policies = await prisma_1.default.leavePolicy.findMany({ where: { companyId, isActive: true } });
|
|
const year = new Date().getFullYear();
|
|
if (policies.length > 0) {
|
|
await prisma_1.default.leaveBalance.createMany({
|
|
data: policies.map(p => ({
|
|
employeeId: employee.id,
|
|
policyId: p.id,
|
|
year,
|
|
allocated: p.accrualDays * 12,
|
|
used: 0,
|
|
balance: p.accrualDays * 12,
|
|
})),
|
|
skipDuplicates: true,
|
|
});
|
|
}
|
|
return res.status(201).json({ ...employee, tempPassword });
|
|
}
|
|
catch (err) {
|
|
if (err.code === 'P2002') {
|
|
return res.status(409).json({ error: 'Email already exists' });
|
|
}
|
|
console.error(err);
|
|
return res.status(500).json({ error: 'Internal server error' });
|
|
}
|
|
});
|
|
// PATCH /employees/:id
|
|
router.patch('/:id', auth_1.requireAuth, async (req, res) => {
|
|
try {
|
|
const { id } = req.params;
|
|
const existing = await prisma_1.default.employee.findFirst({
|
|
where: { id, companyId: req.user.companyId },
|
|
});
|
|
if (!existing)
|
|
return res.status(404).json({ error: 'Employee not found' });
|
|
const employee = await prisma_1.default.employee.update({
|
|
where: { id },
|
|
data: {
|
|
...req.body,
|
|
updatedAt: new Date(),
|
|
},
|
|
include: {
|
|
department: true,
|
|
position: true,
|
|
location: true,
|
|
},
|
|
});
|
|
return res.json(employee);
|
|
}
|
|
catch (err) {
|
|
return res.status(500).json({ error: 'Internal server error' });
|
|
}
|
|
});
|
|
// DELETE /employees/:id (soft delete)
|
|
router.delete('/:id', auth_1.requireAuth, async (req, res) => {
|
|
try {
|
|
await prisma_1.default.employee.update({
|
|
where: { id: req.params.id },
|
|
data: { status: 'TERMINATED' },
|
|
});
|
|
return res.json({ message: 'Employee terminated' });
|
|
}
|
|
catch (err) {
|
|
return res.status(500).json({ error: 'Internal server error' });
|
|
}
|
|
});
|
|
exports.default = router;
|
|
//# sourceMappingURL=employees.js.map
|