(Feat): Initial Commit
This commit is contained in:
259
backend/routes/attendance.js
Normal file
259
backend/routes/attendance.js
Normal file
@@ -0,0 +1,259 @@
|
||||
import express from 'express';
|
||||
import db from '../config/database.js';
|
||||
import { authenticateToken, authorize } from '../middleware/auth.js';
|
||||
|
||||
const router = express.Router();
|
||||
|
||||
// Get all attendance records
|
||||
router.get('/', authenticateToken, async (req, res) => {
|
||||
try {
|
||||
const { employeeId, startDate, endDate, status } = req.query;
|
||||
|
||||
let query = `
|
||||
SELECT a.*,
|
||||
e.name as employee_name, e.username as employee_username,
|
||||
s.name as supervisor_name,
|
||||
d.name as department_name,
|
||||
c.name as contractor_name
|
||||
FROM attendance a
|
||||
JOIN users e ON a.employee_id = e.id
|
||||
JOIN users s ON a.supervisor_id = s.id
|
||||
LEFT JOIN departments d ON e.department_id = d.id
|
||||
LEFT JOIN users c ON e.contractor_id = c.id
|
||||
WHERE 1=1
|
||||
`;
|
||||
const params = [];
|
||||
|
||||
// Role-based filtering
|
||||
if (req.user.role === 'Supervisor') {
|
||||
query += ' AND a.supervisor_id = ?';
|
||||
params.push(req.user.id);
|
||||
} else if (req.user.role === 'Employee') {
|
||||
query += ' AND a.employee_id = ?';
|
||||
params.push(req.user.id);
|
||||
}
|
||||
|
||||
if (employeeId) {
|
||||
query += ' AND a.employee_id = ?';
|
||||
params.push(employeeId);
|
||||
}
|
||||
|
||||
if (startDate) {
|
||||
query += ' AND a.work_date >= ?';
|
||||
params.push(startDate);
|
||||
}
|
||||
|
||||
if (endDate) {
|
||||
query += ' AND a.work_date <= ?';
|
||||
params.push(endDate);
|
||||
}
|
||||
|
||||
if (status) {
|
||||
query += ' AND a.status = ?';
|
||||
params.push(status);
|
||||
}
|
||||
|
||||
query += ' ORDER BY a.work_date DESC, a.check_in_time DESC';
|
||||
|
||||
const [records] = await db.query(query, params);
|
||||
res.json(records);
|
||||
} catch (error) {
|
||||
console.error('Get attendance error:', error);
|
||||
res.status(500).json({ error: 'Internal server error' });
|
||||
}
|
||||
});
|
||||
|
||||
// Get attendance by ID
|
||||
router.get('/:id', authenticateToken, async (req, res) => {
|
||||
try {
|
||||
const [records] = await db.query(
|
||||
`SELECT a.*,
|
||||
e.name as employee_name, e.username as employee_username,
|
||||
s.name as supervisor_name,
|
||||
d.name as department_name,
|
||||
c.name as contractor_name
|
||||
FROM attendance a
|
||||
JOIN users e ON a.employee_id = e.id
|
||||
JOIN users s ON a.supervisor_id = s.id
|
||||
LEFT JOIN departments d ON e.department_id = d.id
|
||||
LEFT JOIN users c ON e.contractor_id = c.id
|
||||
WHERE a.id = ?`,
|
||||
[req.params.id]
|
||||
);
|
||||
|
||||
if (records.length === 0) {
|
||||
return res.status(404).json({ error: 'Attendance record not found' });
|
||||
}
|
||||
|
||||
res.json(records[0]);
|
||||
} catch (error) {
|
||||
console.error('Get attendance error:', error);
|
||||
res.status(500).json({ error: 'Internal server error' });
|
||||
}
|
||||
});
|
||||
|
||||
// Check in employee (Supervisor or SuperAdmin)
|
||||
router.post('/check-in', authenticateToken, authorize('Supervisor', 'SuperAdmin'), async (req, res) => {
|
||||
try {
|
||||
const { employeeId, workDate } = req.body;
|
||||
|
||||
if (!employeeId || !workDate) {
|
||||
return res.status(400).json({ error: 'Employee ID and work date required' });
|
||||
}
|
||||
|
||||
// Verify employee exists (SuperAdmin can check in any employee, Supervisor only their department)
|
||||
let employeeQuery = 'SELECT * FROM users WHERE id = ? AND role = ?';
|
||||
let employeeParams = [employeeId, 'Employee'];
|
||||
|
||||
if (req.user.role === 'Supervisor') {
|
||||
employeeQuery += ' AND department_id = ?';
|
||||
employeeParams.push(req.user.departmentId);
|
||||
}
|
||||
|
||||
const [employees] = await db.query(employeeQuery, employeeParams);
|
||||
|
||||
if (employees.length === 0) {
|
||||
return res.status(403).json({ error: 'Employee not found or not in your department' });
|
||||
}
|
||||
|
||||
// Check if already checked in today
|
||||
const [existing] = await db.query(
|
||||
'SELECT * FROM attendance WHERE employee_id = ? AND work_date = ? AND status = ?',
|
||||
[employeeId, workDate, 'CheckedIn']
|
||||
);
|
||||
|
||||
if (existing.length > 0) {
|
||||
return res.status(400).json({ error: 'Employee already checked in today' });
|
||||
}
|
||||
|
||||
const checkInTime = new Date();
|
||||
|
||||
const [result] = await db.query(
|
||||
'INSERT INTO attendance (employee_id, supervisor_id, check_in_time, work_date, status) VALUES (?, ?, ?, ?, ?)',
|
||||
[employeeId, req.user.id, checkInTime, workDate, 'CheckedIn']
|
||||
);
|
||||
|
||||
const [newRecord] = await db.query(
|
||||
`SELECT a.*,
|
||||
e.name as employee_name, e.username as employee_username,
|
||||
s.name as supervisor_name,
|
||||
d.name as department_name,
|
||||
c.name as contractor_name
|
||||
FROM attendance a
|
||||
JOIN users e ON a.employee_id = e.id
|
||||
JOIN users s ON a.supervisor_id = s.id
|
||||
LEFT JOIN departments d ON e.department_id = d.id
|
||||
LEFT JOIN users c ON e.contractor_id = c.id
|
||||
WHERE a.id = ?`,
|
||||
[result.insertId]
|
||||
);
|
||||
|
||||
res.status(201).json(newRecord[0]);
|
||||
} catch (error) {
|
||||
console.error('Check in error:', error);
|
||||
res.status(500).json({ error: 'Internal server error' });
|
||||
}
|
||||
});
|
||||
|
||||
// Check out employee (Supervisor or SuperAdmin)
|
||||
router.post('/check-out', authenticateToken, authorize('Supervisor', 'SuperAdmin'), async (req, res) => {
|
||||
try {
|
||||
const { employeeId, workDate } = req.body;
|
||||
|
||||
if (!employeeId || !workDate) {
|
||||
return res.status(400).json({ error: 'Employee ID and work date required' });
|
||||
}
|
||||
|
||||
// Find the check-in record (SuperAdmin can check out any, Supervisor only their own)
|
||||
let query = 'SELECT * FROM attendance WHERE employee_id = ? AND work_date = ? AND status = ?';
|
||||
let params = [employeeId, workDate, 'CheckedIn'];
|
||||
|
||||
if (req.user.role === 'Supervisor') {
|
||||
query += ' AND supervisor_id = ?';
|
||||
params.push(req.user.id);
|
||||
}
|
||||
|
||||
const [records] = await db.query(query, params);
|
||||
|
||||
if (records.length === 0) {
|
||||
return res.status(404).json({ error: 'No check-in record found for today' });
|
||||
}
|
||||
|
||||
const checkOutTime = new Date();
|
||||
|
||||
await db.query(
|
||||
'UPDATE attendance SET check_out_time = ?, status = ? WHERE id = ?',
|
||||
[checkOutTime, 'CheckedOut', records[0].id]
|
||||
);
|
||||
|
||||
const [updatedRecord] = await db.query(
|
||||
`SELECT a.*,
|
||||
e.name as employee_name, e.username as employee_username,
|
||||
s.name as supervisor_name,
|
||||
d.name as department_name,
|
||||
c.name as contractor_name
|
||||
FROM attendance a
|
||||
JOIN users e ON a.employee_id = e.id
|
||||
JOIN users s ON a.supervisor_id = s.id
|
||||
LEFT JOIN departments d ON e.department_id = d.id
|
||||
LEFT JOIN users c ON e.contractor_id = c.id
|
||||
WHERE a.id = ?`,
|
||||
[records[0].id]
|
||||
);
|
||||
|
||||
res.json(updatedRecord[0]);
|
||||
} catch (error) {
|
||||
console.error('Check out error:', error);
|
||||
res.status(500).json({ error: 'Internal server error' });
|
||||
}
|
||||
});
|
||||
|
||||
// Get attendance summary
|
||||
router.get('/summary/stats', authenticateToken, async (req, res) => {
|
||||
try {
|
||||
const { startDate, endDate, departmentId } = req.query;
|
||||
|
||||
let query = `
|
||||
SELECT
|
||||
COUNT(DISTINCT a.employee_id) as total_employees,
|
||||
COUNT(DISTINCT CASE WHEN a.status = 'CheckedIn' THEN a.employee_id END) as checked_in,
|
||||
COUNT(DISTINCT CASE WHEN a.status = 'CheckedOut' THEN a.employee_id END) as checked_out,
|
||||
d.name as department_name
|
||||
FROM attendance a
|
||||
JOIN users e ON a.employee_id = e.id
|
||||
LEFT JOIN departments d ON e.department_id = d.id
|
||||
WHERE 1=1
|
||||
`;
|
||||
const params = [];
|
||||
|
||||
if (req.user.role === 'Supervisor') {
|
||||
query += ' AND a.supervisor_id = ?';
|
||||
params.push(req.user.id);
|
||||
}
|
||||
|
||||
if (startDate) {
|
||||
query += ' AND a.work_date >= ?';
|
||||
params.push(startDate);
|
||||
}
|
||||
|
||||
if (endDate) {
|
||||
query += ' AND a.work_date <= ?';
|
||||
params.push(endDate);
|
||||
}
|
||||
|
||||
if (departmentId) {
|
||||
query += ' AND e.department_id = ?';
|
||||
params.push(departmentId);
|
||||
}
|
||||
|
||||
query += ' GROUP BY d.id, d.name';
|
||||
|
||||
const [summary] = await db.query(query, params);
|
||||
res.json(summary);
|
||||
} catch (error) {
|
||||
console.error('Get attendance summary error:', error);
|
||||
res.status(500).json({ error: 'Internal server error' });
|
||||
}
|
||||
});
|
||||
|
||||
export default router;
|
||||
Reference in New Issue
Block a user