"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)(); function calculateIndiaPayroll(grossSalary, pfApplicable, esiApplicable) { const basic = grossSalary * 0.40; const hra = grossSalary * 0.20; const transport = 1600; const pfBasic = Math.min(basic, 15000); const pfEmployee = pfApplicable ? pfBasic * 0.12 : 0; const pfEmployer = pfApplicable ? pfBasic * 0.12 : 0; const esiEmployee = (esiApplicable && grossSalary <= 21000) ? grossSalary * 0.0075 : 0; const esiEmployer = (esiApplicable && grossSalary <= 21000) ? grossSalary * 0.0325 : 0; const professionalTax = grossSalary > 15000 ? 200 : 0; const annualGross = grossSalary * 12; const tds = annualGross > 750000 ? (annualGross - 750000) * 0.20 / 12 : 0; const totalDeductions = pfEmployee + esiEmployee + tds + professionalTax; const netPay = grossSalary - totalDeductions; return { grossPay: grossSalary, netPay: parseFloat(netPay.toFixed(2)), basicSalary: parseFloat(basic.toFixed(2)), hra: parseFloat(hra.toFixed(2)), transport, pfEmployee: parseFloat(pfEmployee.toFixed(2)), pfEmployer: parseFloat(pfEmployer.toFixed(2)), esiEmployee: parseFloat(esiEmployee.toFixed(2)), esiEmployer: parseFloat(esiEmployer.toFixed(2)), tds: parseFloat(tds.toFixed(2)), professionalTax, totalDeductions: parseFloat(totalDeductions.toFixed(2)), }; } // GET /payroll/runs router.get('/runs', auth_1.requireAuth, async (req, res) => { try { const runs = await prisma_1.default.payrollRun.findMany({ where: { companyId: req.user.companyId }, include: { _count: { select: { payslips: true } } }, orderBy: { createdAt: 'desc' }, }); return res.json(runs); } catch (err) { return res.status(500).json({ error: 'Internal server error' }); } }); // POST /payroll/runs router.post('/runs', auth_1.requireAuth, async (req, res) => { try { const { name, periodStart, periodEnd } = req.body; const run = await prisma_1.default.payrollRun.create({ data: { companyId: req.user.companyId, name, periodStart: new Date(periodStart), periodEnd: new Date(periodEnd), status: 'DRAFT', }, }); return res.status(201).json(run); } catch (err) { return res.status(500).json({ error: 'Internal server error' }); } }); // POST /payroll/runs/:id/process router.post('/runs/:id/process', auth_1.requireAuth, async (req, res) => { try { const run = await prisma_1.default.payrollRun.findUnique({ where: { id: req.params.id } }); if (!run) return res.status(404).json({ error: 'Payroll run not found' }); const employees = await prisma_1.default.employee.findMany({ where: { companyId: req.user.companyId, status: { in: ['ACTIVE', 'PROBATION'] }, }, }); // Delete existing payslips for this run await prisma_1.default.payslip.deleteMany({ where: { payrollRunId: run.id } }); let totalGross = 0; let totalNet = 0; let totalDeductions = 0; const payslipsData = employees.map(emp => { const calc = calculateIndiaPayroll(emp.salary, emp.pfApplicable, emp.esiApplicable); totalGross += calc.grossPay; totalNet += calc.netPay; totalDeductions += calc.totalDeductions; return { payrollRunId: run.id, employeeId: emp.id, ...calc, }; }); await prisma_1.default.payslip.createMany({ data: payslipsData }); await prisma_1.default.payrollRun.update({ where: { id: run.id }, data: { status: 'PROCESSED', totalGross: parseFloat(totalGross.toFixed(2)), totalNet: parseFloat(totalNet.toFixed(2)), totalDeductions: parseFloat(totalDeductions.toFixed(2)), }, }); return res.json({ message: `Processed ${employees.length} payslips`, totalGross, totalNet }); } catch (err) { console.error(err); return res.status(500).json({ error: 'Internal server error' }); } }); // GET /payroll/runs/:id router.get('/runs/:id', auth_1.requireAuth, async (req, res) => { try { const run = await prisma_1.default.payrollRun.findUnique({ where: { id: req.params.id }, include: { payslips: { include: { employee: { select: { firstName: true, lastName: true, employeeCode: true, department: { select: { name: true } } }, }, }, }, }, }); if (!run) return res.status(404).json({ error: 'Not found' }); return res.json(run); } catch (err) { return res.status(500).json({ error: 'Internal server error' }); } }); // GET /payroll/payslips (employee view) router.get('/payslips', auth_1.requireAuth, async (req, res) => { try { const employeeId = req.user.employeeId; if (!employeeId) return res.json([]); const payslips = await prisma_1.default.payslip.findMany({ where: { employeeId }, include: { payrollRun: { select: { name: true, periodStart: true, periodEnd: true, status: true } }, }, orderBy: { generatedAt: 'desc' }, }); return res.json(payslips); } catch (err) { return res.status(500).json({ error: 'Internal server error' }); } }); exports.default = router; //# sourceMappingURL=payroll.js.map