feat(hr): Complete HR module with Employee Portal, Loans, Leave, Purchase Requests, Contracts
- Database: Add Loan, LoanInstallment, PurchaseRequest, LeaveEntitlement, EmployeeContract models - Database: Extend Attendance with ZK Tico fields (sourceDeviceId, externalId, rawData) - Database: Add Employee.attendancePin for device mapping - Backend: HR admin - Loans, Purchase Requests, Leave entitlements, Employee contracts CRUD - Backend: Leave reject, bulk attendance sync (ZK Tico ready) - Backend: Employee Portal API - scoped by employeeId (loans, leaves, purchase-requests, attendance, salaries) - Frontend: Employee Portal - dashboard, loans, leave, purchase-requests, attendance, salaries - Frontend: HR Admin - new tabs for Leaves, Loans, Purchase Requests, Contracts (approve/reject) - Dashboard: Add My Portal link - No destructive schema changes; additive migrations only Made-with: Cursor
This commit is contained in:
@@ -1,12 +1,24 @@
|
||||
import { Router } from 'express';
|
||||
import { body, param } from 'express-validator';
|
||||
import { hrController } from './hr.controller';
|
||||
import { portalController } from './portal.controller';
|
||||
import { authenticate, authorize } from '../../shared/middleware/auth';
|
||||
import { validate } from '../../shared/middleware/validation';
|
||||
|
||||
const router = Router();
|
||||
router.use(authenticate);
|
||||
|
||||
// ========== EMPLOYEE PORTAL (authenticate only, scoped by employeeId) ==========
|
||||
|
||||
router.get('/portal/me', portalController.getMe);
|
||||
router.get('/portal/loans', portalController.getMyLoans);
|
||||
router.post('/portal/loans', portalController.submitLoanRequest);
|
||||
router.get('/portal/leave-balance', portalController.getMyLeaveBalance);
|
||||
router.get('/portal/leaves', portalController.getMyLeaves);
|
||||
router.post('/portal/leaves', portalController.submitLeaveRequest);
|
||||
router.get('/portal/purchase-requests', portalController.getMyPurchaseRequests);
|
||||
router.post('/portal/purchase-requests', portalController.submitPurchaseRequest);
|
||||
router.get('/portal/attendance', portalController.getMyAttendance);
|
||||
router.get('/portal/salaries', portalController.getMySalaries);
|
||||
|
||||
// ========== EMPLOYEES ==========
|
||||
|
||||
router.get('/employees', authorize('hr', 'employees', 'read'), hrController.findAllEmployees);
|
||||
@@ -19,11 +31,14 @@ router.post('/employees/:id/terminate', authorize('hr', 'employees', 'terminate'
|
||||
|
||||
router.post('/attendance', authorize('hr', 'attendance', 'create'), hrController.recordAttendance);
|
||||
router.get('/attendance/:employeeId', authorize('hr', 'attendance', 'read'), hrController.getAttendance);
|
||||
router.post('/attendance/sync', authorize('hr', 'attendance', 'create'), hrController.bulkSyncAttendance);
|
||||
|
||||
// ========== LEAVES ==========
|
||||
|
||||
router.get('/leaves', authorize('hr', 'leaves', 'read'), hrController.findAllLeaves);
|
||||
router.post('/leaves', authorize('hr', 'leaves', 'create'), hrController.createLeaveRequest);
|
||||
router.post('/leaves/:id/approve', authorize('hr', 'leaves', 'approve'), hrController.approveLeave);
|
||||
router.post('/leaves/:id/reject', authorize('hr', 'leaves', 'approve'), hrController.rejectLeave);
|
||||
|
||||
// ========== SALARIES ==========
|
||||
|
||||
@@ -41,5 +56,35 @@ router.delete('/departments/:id', authorize('hr', 'all', 'delete'), hrController
|
||||
|
||||
router.get('/positions', authorize('hr', 'all', 'read'), hrController.findAllPositions);
|
||||
|
||||
// ========== LOANS ==========
|
||||
|
||||
router.get('/loans', authorize('hr', 'all', 'read'), hrController.findAllLoans);
|
||||
router.get('/loans/:id', authorize('hr', 'all', 'read'), hrController.findLoanById);
|
||||
router.post('/loans', authorize('hr', 'all', 'create'), hrController.createLoan);
|
||||
router.post('/loans/:id/approve', authorize('hr', 'all', 'approve'), hrController.approveLoan);
|
||||
router.post('/loans/:id/reject', authorize('hr', 'all', 'approve'), hrController.rejectLoan);
|
||||
router.post('/loans/:id/pay-installment', authorize('hr', 'all', 'update'), hrController.recordLoanInstallmentPayment);
|
||||
|
||||
// ========== PURCHASE REQUESTS ==========
|
||||
|
||||
router.get('/purchase-requests', authorize('hr', 'all', 'read'), hrController.findAllPurchaseRequests);
|
||||
router.get('/purchase-requests/:id', authorize('hr', 'all', 'read'), hrController.findPurchaseRequestById);
|
||||
router.post('/purchase-requests', authorize('hr', 'all', 'create'), hrController.createPurchaseRequest);
|
||||
router.post('/purchase-requests/:id/approve', authorize('hr', 'all', 'approve'), hrController.approvePurchaseRequest);
|
||||
router.post('/purchase-requests/:id/reject', authorize('hr', 'all', 'approve'), hrController.rejectPurchaseRequest);
|
||||
|
||||
// ========== LEAVE ENTITLEMENTS ==========
|
||||
|
||||
router.get('/leave-balance/:employeeId', authorize('hr', 'all', 'read'), hrController.getLeaveBalance);
|
||||
router.get('/leave-entitlements', authorize('hr', 'all', 'read'), hrController.findAllLeaveEntitlements);
|
||||
router.post('/leave-entitlements', authorize('hr', 'all', 'create'), hrController.upsertLeaveEntitlement);
|
||||
|
||||
// ========== EMPLOYEE CONTRACTS ==========
|
||||
|
||||
router.get('/contracts', authorize('hr', 'all', 'read'), hrController.findAllEmployeeContracts);
|
||||
router.get('/contracts/:id', authorize('hr', 'all', 'read'), hrController.findEmployeeContractById);
|
||||
router.post('/contracts', authorize('hr', 'all', 'create'), hrController.createEmployeeContract);
|
||||
router.put('/contracts/:id', authorize('hr', 'all', 'update'), hrController.updateEmployeeContract);
|
||||
|
||||
export default router;
|
||||
|
||||
|
||||
Reference in New Issue
Block a user