38808-vm/hr_payroll.php
2026-03-27 04:07:31 +00:00

311 lines
15 KiB
PHP

<?php
require_once 'includes/header.php';
require_once 'includes/accounting_functions.php'; // Include accounting helpers
if (!canView('hr_payroll')) {
echo "<div class='alert alert-danger'>ليس لديك صلاحية للوصول إلى هذه الصفحة.</div>";
require_once 'includes/footer.php';
exit;
}
$month = $_GET['month'] ?? date('m');
$year = $_GET['year'] ?? date('Y');
$error = '';
$success = '';
// Handle Payroll Actions
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
$action = $_POST['action'] ?? '';
if (isset($_POST['generate_payroll'])) {
if (!canAdd('hr_payroll')) {
$error = "لا تملك صلاحية التوليد.";
} else {
$gen_month = $_POST['month'];
$gen_year = $_POST['year'];
// Get all active employees
$employees = db()->query("SELECT id, basic_salary FROM hr_employees WHERE status = 'active'")->fetchAll();
$count = 0;
foreach ($employees as $emp) {
// Check if already exists
$stmt = db()->prepare("SELECT id FROM hr_payroll WHERE employee_id = ? AND month = ? AND year = ?");
$stmt->execute([$emp['id'], $gen_month, $gen_year]);
if ($stmt->fetch()) continue; // Skip if exists
// Calculate Absent Deductions
$stmt = db()->prepare("SELECT COUNT(*) FROM hr_attendance WHERE employee_id = ? AND status = 'absent' AND MONTH(date) = ? AND YEAR(date) = ?");
$stmt->execute([$emp['id'], $gen_month, $gen_year]);
$absent_days = $stmt->fetchColumn();
$daily_rate = $emp['basic_salary'] / 30;
$deductions = round($absent_days * $daily_rate, 2);
$net = $emp['basic_salary'] - $deductions;
$stmt = db()->prepare("INSERT INTO hr_payroll (employee_id, month, year, basic_salary, deductions, net_salary, status) VALUES (?, ?, ?, ?, ?, ?, 'pending')");
$stmt->execute([$emp['id'], $gen_month, $gen_year, $emp['basic_salary'], $deductions, $net]);
$count++;
}
$success = "تم توليد الرواتب لـ $count موظف.";
}
} elseif (isset($_POST['update_payroll'])) {
if (!canEdit('hr_payroll')) {
$error = "لا تملك صلاحية التعديل.";
} else {
$id = $_POST['id'];
$bonuses = floatval($_POST['bonuses']);
$deductions = floatval($_POST['deductions']);
$status = $_POST['status'];
// Recalculate Net
$stmt = db()->prepare("SELECT * FROM hr_payroll WHERE id = ?");
$stmt->execute([$id]);
$current = $stmt->fetch();
if ($current) {
$net = $current['basic_salary'] + $bonuses - $deductions;
$payment_date = ($status == 'paid') ? date('Y-m-d') : null;
$journal_id = $current['journal_id'];
try {
$db = db();
// Update Payroll Record
$stmt = $db->prepare("UPDATE hr_payroll SET bonuses = ?, deductions = ?, net_salary = ?, status = ?, payment_date = ? WHERE id = ?");
$stmt->execute([$bonuses, $deductions, $net, $status, $payment_date, $id]);
// Accounting Integration
if ($status == 'paid') {
// Create or Update Journal Entry
$salary_account = 'مصروفات الرواتب'; // Account ID 13
$cash_account = 'النقدية'; // Account ID 17
$entries = [
['account' => $salary_account, 'debit' => $net, 'credit' => 0],
['account' => $cash_account, 'debit' => 0, 'credit' => $net]
];
// Get Employee Name for Description
$stmt_emp = $db->prepare("SELECT first_name, last_name FROM hr_employees WHERE id = ?");
$stmt_emp->execute([$current['employee_id']]);
$emp_name = $stmt_emp->fetch(PDO::FETCH_ASSOC);
$emp_full_name = $emp_name ? ($emp_name['first_name'] . ' ' . $emp_name['last_name']) : "Employee #{$current['employee_id']}";
$description = "Salary Payment: $emp_full_name ({$current['month']}/{$current['year']})";
$reference = "PAY-{$current['year']}-{$current['month']}-{$current['employee_id']}";
if ($journal_id) {
edit_journal_entry($journal_id, $payment_date, $description, $reference, $entries);
} else {
$jid = add_journal_entry($payment_date, $description, $reference, $entries);
if ($jid) {
$db->prepare("UPDATE hr_payroll SET journal_id = ? WHERE id = ?")->execute([$jid, $id]);
}
}
} elseif ($status == 'pending' && $journal_id) {
// If changing back to pending, delete the journal entry
delete_journal_entry($journal_id);
$db->prepare("UPDATE hr_payroll SET journal_id = NULL WHERE id = ?")->execute([$id]);
}
$success = "تم تحديث الراتب.";
} catch (Exception $e) {
$error = "حدث خطأ: " . $e->getMessage();
}
}
}
}
}
// Fetch Payroll Records
$sql = "SELECT p.*, e.first_name, e.last_name, e.job_title
FROM hr_payroll p
JOIN hr_employees e ON p.employee_id = e.id
WHERE p.month = ? AND p.year = ?
ORDER BY e.first_name";
$stmt = db()->prepare($sql);
$stmt->execute([$month, $year]);
$payrolls = $stmt->fetchAll();
// Calculate Totals
$total_salaries = 0;
foreach ($payrolls as $p) $total_salaries += $p['net_salary'];
?>
<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">
<select name="month" class="form-select form-select-sm">
<?php for($m=1; $m<=12; $m++): ?>
<option value="<?= $m ?>" <?= $m == $month ? 'selected' : '' ?>><?= date('F', mktime(0, 0, 0, $m, 1)) ?></option>
<?php endfor; ?>
</select>
<select name="year" class="form-select form-select-sm">
<?php for($y=date('Y')-1; $y<=date('Y')+1; $y++): ?>
<option value="<?= $y ?>" <?= $y == $year ? 'selected' : '' ?>><?= $y ?></option>
<?php endfor; ?>
</select>
<button type="submit" class="btn btn-sm btn-outline-secondary">عرض</button>
</form>
<?php if (canAdd('hr_payroll')): ?>
<button type="button" class="btn btn-sm btn-primary ms-2" data-bs-toggle="modal" data-bs-target="#generateModal">
<i class="fas fa-cog"></i> توليد الرواتب
</button>
<?php endif; ?>
</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="row mb-3">
<div class="col-md-4">
<div class="card bg-light">
<div class="card-body py-2 text-center">
<h6 class="mb-0 text-muted">إجمالي الرواتب للشهر</h6>
<h4 class="mb-0 text-primary fw-bold"><?= number_format($total_salaries, 2) ?></h4>
</div>
</div>
</div>
</div>
<div class="card shadow-sm">
<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($payrolls)): ?>
<tr><td colspan="7" class="text-center py-4 text-muted">لا توجد بيانات لهذا الشهر. اضغط على "توليد الرواتب" للبدء.</td></tr>
<?php else: ?>
<?php foreach ($payrolls as $row): ?>
<tr>
<td>
<div class="fw-bold"><?= htmlspecialchars($row['first_name'] . ' ' . $row['last_name']) ?></div>
<div class="small text-muted"><?= htmlspecialchars($row['job_title']) ?></div>
</td>
<td><?= number_format($row['basic_salary'], 2) ?></td>
<td class="text-success"><?= number_format($row['bonuses'], 2) ?></td>
<td class="text-danger"><?= number_format($row['deductions'], 2) ?></td>
<td class="fw-bold"><?= number_format($row['net_salary'], 2) ?></td>
<td>
<span class="badge bg-<?= $row['status'] == 'paid' ? 'success' : 'warning' ?>">
<?= $row['status'] == 'paid' ? 'مدفوع' : 'معلق' ?>
</span>
<?php if($row['journal_id']): ?>
<span class="badge bg-info text-dark" title="مرحل للمحاسبة"><i class="fas fa-check-circle"></i></span>
<?php endif; ?>
</td>
<td>
<?php if (canEdit('hr_payroll')): ?>
<button class="btn btn-sm btn-outline-primary"
data-bs-toggle="modal"
data-bs-target="#editPayModal"
data-id="<?= $row['id'] ?>"
data-name="<?= htmlspecialchars($row['first_name']) ?>"
data-bonus="<?= $row['bonuses'] ?>"
data-deduct="<?= $row['deductions'] ?>"
data-status="<?= $row['status'] ?>">
<i class="fas fa-edit"></i>
</button>
<?php endif; ?>
</td>
</tr>
<?php endforeach; ?>
<?php endif; ?>
</tbody>
</table>
</div>
</div>
</div>
<!-- Generate Modal -->
<div class="modal fade" id="generateModal" tabindex="-1" aria-hidden="true">
<div class="modal-dialog">
<div class="modal-content">
<div class="modal-header">
<h5 class="modal-title">توليد الرواتب</h5>
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
</div>
<form method="post">
<div class="modal-body">
<p>سيتم حساب الرواتب لجميع الموظفين النشطين لشهر: <strong><?= $month ?> / <?= $year ?></strong></p>
<p class="text-muted small">سيتم احتساب الخصومات تلقائياً بناءً على أيام الغياب المسجلة.</p>
<input type="hidden" name="month" value="<?= $month ?>">
<input type="hidden" name="year" value="<?= $year ?>">
</div>
<div class="modal-footer">
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal">إلغاء</button>
<button type="submit" name="generate_payroll" class="btn btn-primary">تأكيد التوليد</button>
</div>
</form>
</div>
</div>
</div>
<!-- Edit Payroll Modal -->
<div class="modal fade" id="editPayModal" tabindex="-1" aria-hidden="true">
<div class="modal-dialog">
<div class="modal-content">
<div class="modal-header">
<h5 class="modal-title">تعديل الراتب: <span id="payName"></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="id" id="payId">
<div class="mb-3">
<label class="form-label">مكافآت / إضافي</label>
<input type="number" step="0.01" name="bonuses" id="payBonus" class="form-control">
</div>
<div class="mb-3">
<label class="form-label">خصومات</label>
<input type="number" step="0.01" name="deductions" id="payDeduct" class="form-control">
</div>
<div class="mb-3">
<label class="form-label">الحالة</label>
<select name="status" id="payStatus" class="form-select">
<option value="pending">معلق</option>
<option value="paid">مدفوع</option>
</select>
</div>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal">إلغاء</button>
<button type="submit" name="update_payroll" class="btn btn-primary">حفظ التغييرات</button>
</div>
</form>
</div>
</div>
</div>
<script>
const editPayModal = document.getElementById('editPayModal');
editPayModal.addEventListener('show.bs.modal', event => {
const button = event.relatedTarget;
document.getElementById('payId').value = button.getAttribute('data-id');
document.getElementById('payName').textContent = button.getAttribute('data-name');
document.getElementById('payBonus').value = button.getAttribute('data-bonus');
document.getElementById('payDeduct').value = button.getAttribute('data-deduct');
document.getElementById('payStatus').value = button.getAttribute('data-status');
});
</script>
<?php require_once 'includes/footer.php'; ?>