Files
betterhuman/backend/dist/routes/leave.js
T

211 lines
8.0 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 prisma_1 = __importDefault(require("../lib/prisma"));
const auth_1 = require("../middleware/auth");
const router = (0, express_1.Router)();
// GET /leave/policies
router.get('/policies', auth_1.requireAuth, async (req, res) => {
const policies = await prisma_1.default.leavePolicy.findMany({
where: { companyId: req.user.companyId },
});
return res.json(policies);
});
// POST /leave/policies
router.post('/policies', auth_1.requireAuth, async (req, res) => {
try {
const policy = await prisma_1.default.leavePolicy.create({
data: { ...req.body, companyId: req.user.companyId },
});
return res.status(201).json(policy);
}
catch (err) {
return res.status(500).json({ error: 'Internal server error' });
}
});
// GET /leave/balances
router.get('/balances', auth_1.requireAuth, async (req, res) => {
try {
const { employeeId } = req.query;
const empId = employeeId || req.user.employeeId;
if (!empId)
return res.status(400).json({ error: 'Employee not found' });
const year = new Date().getFullYear();
const balances = await prisma_1.default.leaveBalance.findMany({
where: { employeeId: empId, year },
include: { policy: true },
});
return res.json(balances);
}
catch (err) {
return res.status(500).json({ error: 'Internal server error' });
}
});
// GET /leave/requests
router.get('/requests', auth_1.requireAuth, async (req, res) => {
try {
const { status, employeeId, page = '1', limit = '20' } = req.query;
const skip = (parseInt(page) - 1) * parseInt(limit);
const where = {};
if (req.user.role === 'EMPLOYEE') {
where.employeeId = req.user.employeeId;
}
else if (req.user.role === 'MANAGER') {
// Get team members
const reports = await prisma_1.default.employee.findMany({
where: { managerId: req.user.employeeId },
select: { id: true },
});
const ids = [req.user.employeeId, ...reports.map(r => r.id)];
where.employeeId = { in: ids };
}
if (status)
where.status = status;
if (employeeId && req.user.role !== 'EMPLOYEE')
where.employeeId = employeeId;
const [requests, total] = await Promise.all([
prisma_1.default.leaveRequest.findMany({
where,
skip,
take: parseInt(limit),
include: {
employee: { select: { firstName: true, lastName: true, employeeCode: true } },
policy: { select: { name: true, type: true } },
},
orderBy: { createdAt: 'desc' },
}),
prisma_1.default.leaveRequest.count({ where }),
]);
return res.json({ data: requests, total, page: parseInt(page), totalPages: Math.ceil(total / parseInt(limit)) });
}
catch (err) {
return res.status(500).json({ error: 'Internal server error' });
}
});
// POST /leave/requests
router.post('/requests', auth_1.requireAuth, async (req, res) => {
try {
const { policyId, startDate, endDate, reason } = req.body;
const employeeId = req.user.employeeId;
if (!employeeId)
return res.status(400).json({ error: 'Employee not found' });
const start = new Date(startDate);
const end = new Date(endDate);
const days = Math.ceil((end.getTime() - start.getTime()) / (1000 * 60 * 60 * 24)) + 1;
// Check balance
const year = start.getFullYear();
const balance = await prisma_1.default.leaveBalance.findUnique({
where: { employeeId_policyId_year: { employeeId, policyId, year } },
});
if (!balance || balance.balance < days) {
return res.status(400).json({ error: 'Insufficient leave balance' });
}
// Check overlap
const overlap = await prisma_1.default.leaveRequest.findFirst({
where: {
employeeId,
status: { in: ['PENDING', 'APPROVED'] },
OR: [
{ startDate: { lte: end }, endDate: { gte: start } },
],
},
});
if (overlap) {
return res.status(409).json({ error: 'Leave request overlaps with existing request' });
}
const request = await prisma_1.default.leaveRequest.create({
data: { employeeId, policyId, startDate: start, endDate: end, days, reason, status: 'PENDING' },
include: { policy: true, employee: { select: { firstName: true, lastName: true } } },
});
return res.status(201).json(request);
}
catch (err) {
return res.status(500).json({ error: 'Internal server error' });
}
});
// PATCH /leave/requests/:id/approve
router.patch('/requests/:id/approve', auth_1.requireAuth, async (req, res) => {
try {
const request = await prisma_1.default.leaveRequest.findUnique({ where: { id: req.params.id } });
if (!request)
return res.status(404).json({ error: 'Request not found' });
if (request.status !== 'PENDING')
return res.status(400).json({ error: 'Request already processed' });
await prisma_1.default.leaveRequest.update({
where: { id: req.params.id },
data: {
status: 'APPROVED',
approverId: req.user.employeeId,
approvedAt: new Date(),
approverNote: req.body.note,
},
});
// Deduct from balance
const year = request.startDate.getFullYear();
await prisma_1.default.leaveBalance.update({
where: {
employeeId_policyId_year: {
employeeId: request.employeeId,
policyId: request.policyId,
year,
},
},
data: {
used: { increment: request.days },
balance: { decrement: request.days },
},
});
return res.json({ message: 'Approved' });
}
catch (err) {
return res.status(500).json({ error: 'Internal server error' });
}
});
// PATCH /leave/requests/:id/reject
router.patch('/requests/:id/reject', auth_1.requireAuth, async (req, res) => {
try {
await prisma_1.default.leaveRequest.update({
where: { id: req.params.id },
data: {
status: 'REJECTED',
approverId: req.user.employeeId,
approverNote: req.body.note,
},
});
return res.json({ message: 'Rejected' });
}
catch (err) {
return res.status(500).json({ error: 'Internal server error' });
}
});
// GET /leave/calendar
router.get('/calendar', auth_1.requireAuth, async (req, res) => {
try {
const { month, year } = req.query;
const y = parseInt(year) || new Date().getFullYear();
const m = parseInt(month) || new Date().getMonth() + 1;
const startOfMonth = new Date(y, m - 1, 1);
const endOfMonth = new Date(y, m, 0);
const requests = await prisma_1.default.leaveRequest.findMany({
where: {
status: 'APPROVED',
employee: { companyId: req.user.companyId },
startDate: { lte: endOfMonth },
endDate: { gte: startOfMonth },
},
include: {
employee: { select: { firstName: true, lastName: true } },
policy: { select: { name: true } },
},
});
return res.json(requests);
}
catch (err) {
return res.status(500).json({ error: 'Internal server error' });
}
});
exports.default = router;
//# sourceMappingURL=leave.js.map