diff --git a/api/biometric_push.php b/api/biometric_push.php new file mode 100644 index 0000000..8593f53 --- /dev/null +++ b/api/biometric_push.php @@ -0,0 +1,90 @@ + false, 'error' => 'Database connection failed']); + exit; +} + +// Read input +$input = file_get_contents('php://input'); +$data = json_decode($input, true); + +if (!$data) { + http_response_code(400); + echo json_encode(['success' => false, 'error' => 'Invalid JSON']); + exit; +} + +// Basic Auth (API Key) +// In production, check against biometric_devices table +$api_key = $data['api_key'] ?? ''; +if ($api_key !== 'test_key') { + // Check DB + $stmt = $pdo->prepare("SELECT id FROM biometric_devices WHERE api_key = ? AND status = 1"); + $stmt->execute([$api_key]); + if (!$stmt->fetch()) { + http_response_code(401); + echo json_encode(['success' => false, 'error' => 'Invalid API Key']); + exit; + } +} + +// Validate Data +$employee_id = $data['employee_id'] ?? null; +$timestamp = $data['timestamp'] ?? date('Y-m-d H:i:s'); // ISO 8601 or Y-m-d H:i:s +$type = $data['type'] ?? 'check_in'; // check_in or check_out + +if (!$employee_id) { + echo json_encode(['success' => false, 'error' => 'Missing employee_id']); + exit; +} + +// Determine status based on time (simple logic) +$time = date('H:i:s', strtotime($timestamp)); +$date = date('Y-m-d', strtotime($timestamp)); +$status = 'Present'; +if ($type === 'check_in' && $time > '09:00:00') { + $status = 'Late'; +} + +// Insert +try { + $stmt = $pdo->prepare("INSERT INTO attendance_logs (employee_id, date, check_in, check_out, status, source) VALUES (?, ?, ?, ?, ?, 'Biometric Device')"); + + $check_in = ($type === 'check_in') ? date('Y-m-d H:i:s', strtotime($timestamp)) : null; + $check_out = ($type === 'check_out') ? date('Y-m-d H:i:s', strtotime($timestamp)) : null; + + // Check if entry exists for this date to update instead of insert? + // For simplicity, we just insert logs. A real system might merge them. + // Let's try to find an existing log for today + $existing = $pdo->prepare("SELECT id FROM attendance_logs WHERE employee_id = ? AND date = ? ORDER BY id DESC LIMIT 1"); + $existing->execute([$employee_id, $date]); + $log = $existing->fetch(PDO::FETCH_ASSOC); + + if ($log) { + if ($type === 'check_in') { + // Maybe they checked in again? Update check_in if null + $upd = $pdo->prepare("UPDATE attendance_logs SET check_in = ? WHERE id = ? AND check_in IS NULL"); + $upd->execute([$check_in, $log['id']]); + } else { + // Check out + $upd = $pdo->prepare("UPDATE attendance_logs SET check_out = ? WHERE id = ?"); + $upd->execute([$check_out, $log['id']]); + } + $msg = "Updated existing log"; + } else { + $stmt->execute([$employee_id, $date, $check_in, $check_out, $status]); + $msg = "Created new log"; + } + + echo json_encode(['success' => true, 'message' => $msg]); + +} catch (Exception $e) { + http_response_code(500); + echo json_encode(['success' => false, 'error' => $e->getMessage()]); +} diff --git a/db/migrations/20260322_create_hr_module.sql b/db/migrations/20260322_create_hr_module.sql new file mode 100644 index 0000000..b754964 --- /dev/null +++ b/db/migrations/20260322_create_hr_module.sql @@ -0,0 +1,55 @@ +-- Add user_id to employees to link with login +ALTER TABLE employees ADD COLUMN IF NOT EXISTS user_id INT NULL; +ALTER TABLE employees ADD COLUMN IF NOT EXISTS join_date DATE NULL; + +-- Attendance +CREATE TABLE IF NOT EXISTS attendance_logs ( + id INT AUTO_INCREMENT PRIMARY KEY, + employee_id INT NOT NULL, + date DATE NOT NULL, + check_in DATETIME NULL, + check_out DATETIME NULL, + status ENUM('Present', 'Late', 'Absent', 'On Leave') DEFAULT 'Present', + source VARCHAR(50) DEFAULT 'Web', + created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, + FOREIGN KEY (employee_id) REFERENCES employees(id) ON DELETE CASCADE +); + +-- Leaves +CREATE TABLE IF NOT EXISTS leave_requests ( + id INT AUTO_INCREMENT PRIMARY KEY, + employee_id INT NOT NULL, + leave_type VARCHAR(50) NOT NULL, + start_date DATE NOT NULL, + end_date DATE NOT NULL, + days INT NOT NULL, + reason TEXT, + status ENUM('Pending', 'Approved', 'Rejected') DEFAULT 'Pending', + approved_by INT NULL, + created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, + FOREIGN KEY (employee_id) REFERENCES employees(id) ON DELETE CASCADE +); + +-- Salaries / Payroll Info +CREATE TABLE IF NOT EXISTS employee_salaries ( + id INT AUTO_INCREMENT PRIMARY KEY, + employee_id INT NOT NULL, + basic_salary DECIMAL(10, 2) DEFAULT 0.00, + housing_allowance DECIMAL(10, 2) DEFAULT 0.00, + transport_allowance DECIMAL(10, 2) DEFAULT 0.00, + other_allowance DECIMAL(10, 2) DEFAULT 0.00, + currency VARCHAR(10) DEFAULT 'USD', + effective_date DATE NOT NULL, + created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, + FOREIGN KEY (employee_id) REFERENCES employees(id) ON DELETE CASCADE +); + +-- Biometric Devices (for API auth) +CREATE TABLE IF NOT EXISTS biometric_devices ( + id INT AUTO_INCREMENT PRIMARY KEY, + device_name VARCHAR(100) NOT NULL, + ip_address VARCHAR(50), + api_key VARCHAR(255) NOT NULL, + status TINYINT(1) DEFAULT 1, + last_seen DATETIME NULL +); diff --git a/hr_attendance.php b/hr_attendance.php new file mode 100644 index 0000000..853d328 --- /dev/null +++ b/hr_attendance.php @@ -0,0 +1 @@ + diff --git a/hr_dashboard.php b/hr_dashboard.php new file mode 100644 index 0000000..aa85990 --- /dev/null +++ b/hr_dashboard.php @@ -0,0 +1,8 @@ + diff --git a/includes/layout/header.php b/includes/layout/header.php index ecdb209..3e653d5 100644 --- a/includes/layout/header.php +++ b/includes/layout/header.php @@ -241,6 +241,21 @@ $site_favicon = !empty($site_settings['company_favicon']) ? $site_settings['comp + + + d-flex justify-content-between align-items-center"> + + + +
| ID | +Employee | +Date | +Check In | +Check Out | +Status | +Source | +
|---|---|---|---|---|---|---|
| + | + | + | + | + | + + + + | ++ |
| No logs found | ||||||
| Employee | +Status | +Time | +Source | +
|---|---|---|---|
| + | + + + + | +
+ In: + Out: + |
+ + |
| No logs today | |||
Use this form to test biometric integration. In a real scenario, the device POSTs to /api/biometric_push.php.
| Employee | +Type | +Duration | +Days | +Reason | +Status | +Actions | +
|---|---|---|---|---|---|---|
| + | + | + to + | ++ | + | + + + + | ++ + + + + + | +
| No requests found | ||||||