211 lines
8.0 KiB
JavaScript
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
|