getConnection(); /* ========================================== PERMISSIONS ========================================== */ $isHrManager = Auth::user()->hasRole('Admin') || Auth::user()->hasRole('Superuser') || Auth::user()->hasRole('employee-hr') || Auth::user()->hasRole('manager'); if (!$isHrManager) { header('Location: employee-profile.php'); exit; } /* ========================================== FILTERS (from GET) ========================================== */ $fEmployeeId = isset($_GET['employee_id']) && $_GET['employee_id'] !== '' ? (int)$_GET['employee_id'] : 0; $fTopicId = isset($_GET['topic_id']) && $_GET['topic_id'] !== '' ? (int)$_GET['topic_id'] : 0; $fStatus = isset($_GET['status']) ? trim($_GET['status']) : ''; $fType = isset($_GET['type']) ? trim($_GET['type']) : ''; $fDepartmentId = isset($_GET['department_id'])&& $_GET['department_id']!== '' ? (int)$_GET['department_id']: 0; /* ========================================== LOAD DATA ========================================== */ $where = []; $params = []; // Only the most recent record per (employee, topic) β€” older initial/refresher // rows stay as history on the employee profile, not in this overview. $where[] = "NOT EXISTS ( SELECT 1 FROM employee_trainings et2 WHERE et2.employee_id = et.employee_id AND et2.training_topic_id = et.training_topic_id AND (et2.completed_date > et.completed_date OR (et2.completed_date = et.completed_date AND et2.id > et.id)) )"; if ($fEmployeeId > 0) { $where[] = 'et.employee_id = :eid'; $params['eid'] = $fEmployeeId; } if ($fTopicId > 0) { $where[] = 'et.training_topic_id = :tid'; $params['tid'] = $fTopicId; } if ($fType !== '' && in_array($fType, ['initial', 'refresher'], true)) { $where[] = 'et.training_type = :ty'; $params['ty'] = $fType; } if ($fDepartmentId > 0) { $where[] = 'e.department_id = :did'; $params['did'] = $fDepartmentId; } $whereSql = $where ? ('WHERE ' . implode(' AND ', $where)) : ''; $stmt = $pdo->prepare(" SELECT et.*, tt.name AS topic_name, tt.default_reminder_days AS topic_default_rem, e.first_name, e.last_name, e.employee_code, d.name AS department_name, d.color AS department_color, (SELECT COUNT(*) FROM employee_training_attachments a WHERE a.training_id = et.id) AS attachments_count FROM employee_trainings et JOIN training_topics tt ON tt.id = et.training_topic_id JOIN employees e ON e.id = et.employee_id LEFT JOIN departments d ON d.id = e.department_id $whereSql ORDER BY et.next_due_date IS NULL, et.next_due_date ASC, e.last_name, e.first_name "); $stmt->execute($params); $rows = $stmt->fetchAll(PDO::FETCH_ASSOC); /* Filter by computed status */ function trainingStatus(?string $nextDue, ?int $reminderDays, ?int $topicDefaultRem): array { if (!$nextDue) { return ['code' => 'compliant', 'label' => 'Conforme', 'class' => 'success']; } $rem = $reminderDays !== null ? $reminderDays : ($topicDefaultRem !== null ? $topicDefaultRem : 30); $today = new DateTime('today'); $due = DateTime::createFromFormat('Y-m-d', $nextDue); if (!$due) return ['code' => 'compliant', 'label' => 'Conforme', 'class' => 'success']; $daysLeft = (int)$today->diff($due)->format('%r%a'); if ($daysLeft < 0) return ['code' => 'expired', 'label' => 'Scaduto', 'class' => 'danger', 'days' => $daysLeft]; if ($daysLeft <= $rem) return ['code' => 'due_soon', 'label' => 'Da aggiornare', 'class' => 'warning', 'days' => $daysLeft]; return ['code' => 'compliant', 'label' => 'Conforme', 'class' => 'success', 'days' => $daysLeft]; } $filtered = []; $counters = ['compliant' => 0, 'due_soon' => 0, 'expired' => 0, 'not_present' => 0, 'all' => 0]; foreach ($rows as $r) { $s = trainingStatus($r['next_due_date'] ?: null, $r['reminder_days'] !== null ? (int)$r['reminder_days'] : null, $r['topic_default_rem'] !== null ? (int)$r['topic_default_rem'] : null); $r['_status'] = $s; $counters['all']++; $counters[$s['code']] = ($counters[$s['code']] ?? 0) + 1; if ($fStatus !== '' && $fStatus !== $s['code']) continue; $filtered[] = $r; } /* ========================================== "NOT PRESENT" β€” mandatory topics without any record for an employee. Apply the same filters (employee_id / topic_id / department_id / type=initial). ========================================== */ if ($fType === '' || $fType === 'initial') { $missingWhere = []; $missingParams = []; if ($fEmployeeId > 0) { $missingWhere[] = 'e.id = :eid'; $missingParams['eid'] = $fEmployeeId; } if ($fTopicId > 0) { $missingWhere[] = 'tt.id = :tid'; $missingParams['tid'] = $fTopicId; } if ($fDepartmentId > 0) { $missingWhere[] = 'e.department_id = :did'; $missingParams['did'] = $fDepartmentId; } $missingWhereSql = $missingWhere ? ('AND ' . implode(' AND ', $missingWhere)) : ''; $missingStmt = $pdo->prepare(" SELECT e.id AS employee_id, e.first_name, e.last_name, e.employee_code, d.name AS department_name, d.color AS department_color, tt.id AS topic_id, tt.name AS topic_name FROM employees e CROSS JOIN training_topics tt LEFT JOIN departments d ON d.id = e.department_id WHERE tt.is_active = 1 AND tt.is_mandatory = 1 AND NOT EXISTS ( SELECT 1 FROM employee_trainings et WHERE et.employee_id = e.id AND et.training_topic_id = tt.id ) $missingWhereSql ORDER BY e.last_name, e.first_name, tt.name "); $missingStmt->execute($missingParams); $missingRows = $missingStmt->fetchAll(PDO::FETCH_ASSOC); foreach ($missingRows as $m) { $counters['all']++; $counters['not_present']++; if ($fStatus !== '' && $fStatus !== 'not_present') continue; $filtered[] = [ 'id' => null, '_virtual' => true, 'employee_id' => $m['employee_id'], 'first_name' => $m['first_name'], 'last_name' => $m['last_name'], 'employee_code' => $m['employee_code'], 'department_name' => $m['department_name'], 'department_color' => $m['department_color'], 'training_topic_id' => $m['topic_id'], 'topic_name' => $m['topic_name'], 'training_type' => null, 'completed_date' => null, 'next_due_date' => null, 'attachments_count' => 0, '_status' => ['code' => 'not_present', 'label' => 'Non presente', 'class' => 'secondary', 'days' => null], ]; } } /* Dropdown data */ $employees = $pdo->query(" SELECT id, first_name, last_name, employee_code, department_id FROM employees ORDER BY last_name, first_name ")->fetchAll(PDO::FETCH_ASSOC); $topics = $pdo->query(" SELECT id, name, default_frequency_months, default_reminder_days FROM training_topics WHERE is_active = 1 ORDER BY sort_order, name ")->fetchAll(PDO::FETCH_ASSOC); $departments = $pdo->query(" SELECT id, name, color FROM departments WHERE is_active = 1 ORDER BY sort_order, name ")->fetchAll(PDO::FETCH_ASSOC); function fmtDate(?string $d): string { if (!$d || $d === '0000-00-00') return 'β€”'; $ts = strtotime($d); return $ts ? date('d/m/Y', $ts) : 'β€”'; } ?> Storico Formazione - <?= htmlspecialchars($titlewebsite, ENT_QUOTES, 'UTF-8'); ?>
πŸ“š Storico Formazione
0 selezionati
Nessuna formazione corrispondente ai filtri.
Dipendente Reparto Corso Tipo Completato Prossimo agg. Stato Giorni
β€” β€” +
πŸ“–
Tipo: Completato: Prossimo: (g) (+g) Reparto: