250 lines
12 KiB
PHP
250 lines
12 KiB
PHP
<?php
|
|
require_once 'includes/header.php';
|
|
require_once 'includes/pagination.php';
|
|
|
|
if (!canView('hr_attendance')) {
|
|
echo "<div class='alert alert-danger'>ليس لديك صلاحية للوصول إلى هذه الصفحة.</div>";
|
|
require_once 'includes/footer.php';
|
|
exit;
|
|
}
|
|
|
|
$date = $_GET['date'] ?? date('Y-m-d');
|
|
$error = '';
|
|
$success = '';
|
|
|
|
// Handle Attendance Submission
|
|
if ($_SERVER['REQUEST_METHOD'] === 'POST' && isset($_POST['save_attendance'])) {
|
|
if (!canAdd('hr_attendance') && !canEdit('hr_attendance')) {
|
|
$error = "لا تملك صلاحية التعديل.";
|
|
} else {
|
|
$emp_id = $_POST['employee_id'];
|
|
$att_date = $_POST['date'];
|
|
$status = $_POST['status'];
|
|
$check_in = !empty($_POST['check_in']) ? $_POST['check_in'] : null;
|
|
$check_out = !empty($_POST['check_out']) ? $_POST['check_out'] : null;
|
|
$notes = $_POST['notes'];
|
|
|
|
try {
|
|
// Check if exists
|
|
$stmt = db()->prepare("SELECT id FROM hr_attendance WHERE employee_id = ? AND date = ?");
|
|
$stmt->execute([$emp_id, $att_date]);
|
|
$exists = $stmt->fetch();
|
|
|
|
if ($exists) {
|
|
$stmt = db()->prepare("UPDATE hr_attendance SET status = ?, check_in = ?, check_out = ?, notes = ? WHERE id = ?");
|
|
$stmt->execute([$status, $check_in, $check_out, $notes, $exists['id']]);
|
|
$success = "تم تحديث الحضور بنجاح.";
|
|
} else {
|
|
$stmt = db()->prepare("INSERT INTO hr_attendance (employee_id, date, status, check_in, check_out, notes) VALUES (?, ?, ?, ?, ?, ?)");
|
|
$stmt->execute([$emp_id, $att_date, $status, $check_in, $check_out, $notes]);
|
|
$success = "تم تسجيل الحضور بنجاح.";
|
|
}
|
|
} catch (PDOException $e) {
|
|
$error = "خطأ: " . $e->getMessage();
|
|
}
|
|
}
|
|
}
|
|
|
|
// Pagination
|
|
$page = isset($_GET['page']) ? (int)$_GET['page'] : 1;
|
|
if ($page < 1) $page = 1;
|
|
$limit = 10;
|
|
$offset = ($page - 1) * $limit;
|
|
|
|
// Count Total Employees
|
|
$countStmt = db()->prepare("SELECT COUNT(*) FROM hr_employees WHERE status = 'active'");
|
|
$countStmt->execute();
|
|
$totalFiltered = $countStmt->fetchColumn();
|
|
|
|
// Fetch Employees and their attendance for the selected date
|
|
$sql = "SELECT e.id, e.first_name, e.last_name, e.job_title,
|
|
a.id as att_id, a.status, a.check_in, a.check_out, a.notes
|
|
FROM hr_employees e
|
|
LEFT JOIN hr_attendance a ON e.id = a.employee_id AND a.date = ?
|
|
WHERE e.status = 'active'
|
|
ORDER BY e.first_name
|
|
LIMIT $limit OFFSET $offset";
|
|
$stmt = db()->prepare($sql);
|
|
$stmt->execute([$date]);
|
|
$records = $stmt->fetchAll();
|
|
|
|
?>
|
|
|
|
<div class="d-flex justify-content-between flex-wrap flex-md-nowrap align-items-center pt-3 pb-2 mb-3 border-bottom">
|
|
<h1 class="h2">سجل الحضور والانصراف</h1>
|
|
<div class="btn-toolbar mb-2 mb-md-0">
|
|
<form class="d-flex gap-2 align-items-center" method="get">
|
|
<label class="col-form-label">التاريخ:</label>
|
|
<input type="date" name="date" class="form-control" value="<?= $date ?>" onchange="this.form.submit()">
|
|
</form>
|
|
</div>
|
|
</div>
|
|
|
|
<?php if ($error): ?>
|
|
<div class="alert alert-danger"><?= htmlspecialchars($error) ?></div>
|
|
<?php endif; ?>
|
|
<?php if ($success): ?>
|
|
<div class="alert alert-success"><?= htmlspecialchars($success) ?></div>
|
|
<?php endif; ?>
|
|
|
|
<div class="card shadow-sm border-0">
|
|
<div class="card-body">
|
|
<div class="table-responsive">
|
|
<table class="table table-hover align-middle">
|
|
<thead class="table-light">
|
|
<tr>
|
|
<th>الموظف</th>
|
|
<th>الوظيفة</th>
|
|
<th>الحالة</th>
|
|
<th>وقت الحضور</th>
|
|
<th>وقت الانصراف</th>
|
|
<th>ملاحظات</th>
|
|
<th>إجراء</th>
|
|
</tr>
|
|
</thead>
|
|
<tbody>
|
|
<?php if (empty($records)): ?>
|
|
<tr>
|
|
<td colspan="7" class="text-center py-4 text-muted">لا يوجد موظفين نشطين.</td>
|
|
</tr>
|
|
<?php endif; ?>
|
|
<?php foreach ($records as $row): ?>
|
|
<tr class="<?= !$row['att_id'] ? 'table-light text-muted' : '' ?>">
|
|
<td class="fw-bold"><?= htmlspecialchars($row['first_name'] . ' ' . $row['last_name']) ?></td>
|
|
<td class="small"><?= htmlspecialchars($row['job_title']) ?></td>
|
|
<td>
|
|
<?php if ($row['att_id']): ?>
|
|
<?php
|
|
$badge = match($row['status']) {
|
|
'present' => 'success',
|
|
'absent' => 'danger',
|
|
'late' => 'warning',
|
|
'excused' => 'info',
|
|
'holiday' => 'primary',
|
|
default => 'secondary'
|
|
};
|
|
$status_text = match($row['status']) {
|
|
'present' => 'حاضر',
|
|
'absent' => 'غائب',
|
|
'late' => 'تأخير',
|
|
'excused' => 'مأذون',
|
|
'holiday' => 'عطلة',
|
|
default => $row['status']
|
|
};
|
|
?>
|
|
<span class="badge bg-<?= $badge ?>"><?= $status_text ?></span>
|
|
<?php else: ?>
|
|
<span class="badge bg-light text-dark border">غير مسجل</span>
|
|
<?php endif; ?>
|
|
</td>
|
|
<td><?= $row['check_in'] ? date('h:i A', strtotime($row['check_in'])) : '-' ?></td>
|
|
<td><?= $row['check_out'] ? date('h:i A', strtotime($row['check_out'])) : '-' ?></td>
|
|
<td class="text-truncate" style="max-width: 150px;"><?= htmlspecialchars($row['notes'] ?? '') ?></td>
|
|
<td>
|
|
<?php if (canEdit('hr_attendance')): ?>
|
|
<?php if ($row['att_id']): ?>
|
|
<button class="btn btn-sm btn-outline-primary"
|
|
title="تعديل"
|
|
data-bs-toggle="modal"
|
|
data-bs-target="#attModal"
|
|
data-id="<?= $row['id'] ?>"
|
|
data-name="<?= htmlspecialchars($row['first_name'] . ' ' . $row['last_name']) ?>"
|
|
data-status="<?= $row['status'] ?? 'present' ?>"
|
|
data-in="<?= $row['check_in'] ?? '' ?>"
|
|
data-out="<?= $row['check_out'] ?? '' ?>"
|
|
data-notes="<?= htmlspecialchars($row['notes'] ?? '') ?>">
|
|
<i class="fas fa-edit"></i>
|
|
</button>
|
|
<?php else: ?>
|
|
<button class="btn btn-sm btn-success"
|
|
title="تسجيل حضور"
|
|
data-bs-toggle="modal"
|
|
data-bs-target="#attModal"
|
|
data-id="<?= $row['id'] ?>"
|
|
data-name="<?= htmlspecialchars($row['first_name'] . ' ' . $row['last_name']) ?>"
|
|
data-status="present"
|
|
data-in="<?= date('09:00') ?>"
|
|
data-out=""
|
|
data-notes="">
|
|
<i class="fas fa-check"></i> تسجيل
|
|
</button>
|
|
<?php endif; ?>
|
|
<?php endif; ?>
|
|
</td>
|
|
</tr>
|
|
<?php endforeach; ?>
|
|
</tbody>
|
|
</table>
|
|
</div>
|
|
|
|
<!-- Pagination -->
|
|
<?php renderPagination($page, $totalFiltered, $limit); ?>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Attendance Modal -->
|
|
<div class="modal fade" id="attModal" tabindex="-1" aria-hidden="true">
|
|
<div class="modal-dialog">
|
|
<div class="modal-content">
|
|
<div class="modal-header">
|
|
<h5 class="modal-title">تسجيل/تعديل الحضور: <span id="modalEmpName" class="text-primary"></span></h5>
|
|
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
|
|
</div>
|
|
<form method="post">
|
|
<div class="modal-body">
|
|
<input type="hidden" name="employee_id" id="modalEmpId">
|
|
<input type="hidden" name="date" value="<?= $date ?>">
|
|
|
|
<div class="mb-3">
|
|
<label class="form-label">الحالة</label>
|
|
<select name="status" id="modalStatus" class="form-select" required>
|
|
<option value="present">حاضر</option>
|
|
<option value="late">تأخير</option>
|
|
<option value="excused">مأذون/إذن</option>
|
|
<option value="absent">غائب</option>
|
|
<option value="holiday">عطلة</option>
|
|
</select>
|
|
</div>
|
|
|
|
<div class="row g-2 mb-3">
|
|
<div class="col">
|
|
<label class="form-label">وقت الحضور</label>
|
|
<input type="time" name="check_in" id="modalIn" class="form-control">
|
|
</div>
|
|
<div class="col">
|
|
<label class="form-label">وقت الانصراف</label>
|
|
<input type="time" name="check_out" id="modalOut" class="form-control">
|
|
</div>
|
|
</div>
|
|
|
|
<div class="mb-3">
|
|
<label class="form-label">ملاحظات</label>
|
|
<textarea name="notes" id="modalNotes" class="form-control" rows="2"></textarea>
|
|
</div>
|
|
</div>
|
|
<div class="modal-footer">
|
|
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal">إلغاء</button>
|
|
<button type="submit" name="save_attendance" class="btn btn-primary">حفظ</button>
|
|
</div>
|
|
</form>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<script>
|
|
const attModal = document.getElementById('attModal');
|
|
if (attModal) {
|
|
attModal.addEventListener('show.bs.modal', event => {
|
|
const button = event.relatedTarget;
|
|
|
|
document.getElementById('modalEmpId').value = button.getAttribute('data-id');
|
|
document.getElementById('modalEmpName').textContent = button.getAttribute('data-name');
|
|
document.getElementById('modalStatus').value = button.getAttribute('data-status');
|
|
document.getElementById('modalIn').value = button.getAttribute('data-in');
|
|
document.getElementById('modalOut').value = button.getAttribute('data-out');
|
|
document.getElementById('modalNotes').value = button.getAttribute('data-notes');
|
|
});
|
|
}
|
|
</script>
|
|
|
|
<?php require_once 'includes/footer.php'; ?>
|