updating migration file

This commit is contained in:
Flatlogic Bot 2026-03-27 04:07:31 +00:00
parent 55701e7000
commit e11d717222
10 changed files with 213 additions and 61 deletions

View File

@ -0,0 +1,12 @@
-- Translate default expense categories to Arabic
UPDATE expense_categories SET name = 'مستلزمات مكتبية', description = 'قرطاسية، أحبار، ورق، إلخ' WHERE name = 'Office Supplies';
UPDATE expense_categories SET name = 'سفر وتنقلات', description = 'تذاكر طيران، فنادق، مواصلات' WHERE name = 'Travel';
UPDATE expense_categories SET name = 'وجبات وضيافة', description = 'غداء عمل، فعاليات الفريق' WHERE name = 'Meals & Entertainment';
UPDATE expense_categories SET name = 'مرافق وخدمات', description = 'كهرباء، مياه، انترنت' WHERE name = 'Utilities';
UPDATE expense_categories SET name = 'إيجار', description = 'إيجار المكتب' WHERE name = 'Rent';
UPDATE expense_categories SET name = 'رواتب وأجور', description = 'رواتب الموظفين' WHERE name = 'Salaries';
UPDATE expense_categories SET name = 'صيانة وإصلاحات', description = 'صيانة دورية وإصلاح أعطال' WHERE name = 'Maintenance';
UPDATE expense_categories SET name = 'تسويق وإعلان', description = 'حملات إعلانية وترويج' WHERE name = 'Advertising';
UPDATE expense_categories SET name = 'برمجيات وتراخيص', description = 'اشتراكات برامج وتراخيص' WHERE name = 'Software';
UPDATE expense_categories SET name = 'مصروفات أخرى', description = 'نفقات متنوعة غير مصنفة' WHERE name = 'Other';

View File

@ -0,0 +1,23 @@
-- Add account_id to expense_categories
ALTER TABLE expense_categories ADD COLUMN account_id INT DEFAULT NULL;
ALTER TABLE expense_categories ADD CONSTRAINT fk_expense_category_account FOREIGN KEY (account_id) REFERENCES accounting_accounts(id) ON DELETE SET NULL;
-- Seed default mappings (Best effort based on Arabic account names vs English categories)
-- Accounts:
-- 13: مصروفات الرواتب (Salaries)
-- 16: مصروفات التسويق (Marketing)
-- 30: مصروفات الإيجار (Rent)
-- 31: مصروفات المرافق (Utilities)
-- 12: تكلفة البضاعة المباعة (COGS)
-- Update 'Salaries' category
UPDATE expense_categories SET account_id = 13 WHERE name LIKE '%Salaries%' OR name LIKE '%رواتب%';
-- Update 'Rent' category
UPDATE expense_categories SET account_id = 30 WHERE name LIKE '%Rent%' OR name LIKE '%إيجار%';
-- Update 'Utilities' category
UPDATE expense_categories SET account_id = 31 WHERE name LIKE '%Utilities%' OR name LIKE '%مرافق%';
-- Update 'Advertising' category
UPDATE expense_categories SET account_id = 16 WHERE name LIKE '%Advertising%' OR name LIKE '%Marketing%' OR name LIKE '%تسو%';

View File

@ -0,0 +1,6 @@
-- Add journal_id to expenses and hr_payroll for tracking
ALTER TABLE expenses ADD COLUMN journal_id INT DEFAULT NULL;
ALTER TABLE expenses ADD CONSTRAINT fk_expense_journal FOREIGN KEY (journal_id) REFERENCES accounting_journal(id) ON DELETE SET NULL;
ALTER TABLE hr_payroll ADD COLUMN journal_id INT DEFAULT NULL;
ALTER TABLE hr_payroll ADD CONSTRAINT fk_payroll_journal FOREIGN KEY (journal_id) REFERENCES accounting_journal(id) ON DELETE SET NULL;

View File

@ -0,0 +1,2 @@
-- Fix accounting_accounts type column length
ALTER TABLE accounting_accounts MODIFY type VARCHAR(100) NOT NULL;

View File

@ -1,12 +0,0 @@
<?php
require_once 'db/config.php';
try {
$db = db();
// Allow for longer Arabic type names
$db->exec("ALTER TABLE accounting_accounts MODIFY type VARCHAR(100) NOT NULL");
echo "Successfully updated accounting_accounts table structure.";
} catch (PDOException $e) {
echo "Error: " . $e->getMessage();
}
?>

View File

@ -15,17 +15,18 @@ if ($_SERVER['REQUEST_METHOD'] === 'POST') {
$id = $_POST['id'] ?? 0; $id = $_POST['id'] ?? 0;
$name = trim($_POST['name'] ?? ''); $name = trim($_POST['name'] ?? '');
$description = trim($_POST['description'] ?? ''); $description = trim($_POST['description'] ?? '');
$account_id = !empty($_POST['account_id']) ? $_POST['account_id'] : null;
if ($name) { if ($name) {
try { try {
$db = db(); $db = db();
if ($action === 'add') { if ($action === 'add') {
$stmt = $db->prepare("INSERT INTO expense_categories (name, description) VALUES (?, ?)"); $stmt = $db->prepare("INSERT INTO expense_categories (name, description, account_id) VALUES (?, ?, ?)");
$stmt->execute([$name, $description]); $stmt->execute([$name, $description, $account_id]);
$_SESSION['success'] = 'تم إضافة التصنيف بنجاح'; $_SESSION['success'] = 'تم إضافة التصنيف بنجاح';
} elseif ($action === 'edit' && $id) { } elseif ($action === 'edit' && $id) {
$stmt = $db->prepare("UPDATE expense_categories SET name = ?, description = ? WHERE id = ?"); $stmt = $db->prepare("UPDATE expense_categories SET name = ?, description = ?, account_id = ? WHERE id = ?");
$stmt->execute([$name, $description, $id]); $stmt->execute([$name, $description, $account_id, $id]);
$_SESSION['success'] = 'تم تحديث التصنيف بنجاح'; $_SESSION['success'] = 'تم تحديث التصنيف بنجاح';
} }
redirect('expense_categories.php'); redirect('expense_categories.php');
@ -55,7 +56,8 @@ if (isset($_GET['action']) && $_GET['action'] === 'delete' && isset($_GET['id'])
redirect('expense_categories.php'); redirect('expense_categories.php');
} }
$categories = db()->query("SELECT * FROM expense_categories ORDER BY name")->fetchAll(PDO::FETCH_ASSOC); $categories = db()->query("SELECT c.*, a.name as account_name FROM expense_categories c LEFT JOIN accounting_accounts a ON c.account_id = a.id ORDER BY c.name")->fetchAll(PDO::FETCH_ASSOC);
$accounts = db()->query("SELECT * FROM accounting_accounts ORDER BY type, name")->fetchAll(PDO::FETCH_ASSOC);
if (isset($_SESSION['success'])) { if (isset($_SESSION['success'])) {
$success = $_SESSION['success']; $success = $_SESSION['success'];
@ -97,6 +99,7 @@ if (isset($_SESSION['error'])) {
<thead class="bg-light"> <thead class="bg-light">
<tr> <tr>
<th class="ps-4">اسم التصنيف</th> <th class="ps-4">اسم التصنيف</th>
<th>الحساب المحاسبي المرتبط</th>
<th>الوصف</th> <th>الوصف</th>
<th class="text-center">الإجراءات</th> <th class="text-center">الإجراءات</th>
</tr> </tr>
@ -105,6 +108,13 @@ if (isset($_SESSION['error'])) {
<?php foreach ($categories as $cat): ?> <?php foreach ($categories as $cat): ?>
<tr> <tr>
<td class="ps-4 fw-bold"><?= htmlspecialchars($cat['name']) ?></td> <td class="ps-4 fw-bold"><?= htmlspecialchars($cat['name']) ?></td>
<td>
<?php if($cat['account_name']): ?>
<span class="badge bg-info text-dark"><?= htmlspecialchars($cat['account_name']) ?></span>
<?php else: ?>
<span class="text-muted text-small">غير مرتبط</span>
<?php endif; ?>
</td>
<td><?= htmlspecialchars($cat['description']) ?></td> <td><?= htmlspecialchars($cat['description']) ?></td>
<td class="text-center"> <td class="text-center">
<?php if (canEdit('expense_settings')): ?> <?php if (canEdit('expense_settings')): ?>
@ -143,6 +153,19 @@ if (isset($_SESSION['error'])) {
<label class="form-label fw-bold">اسم التصنيف</label> <label class="form-label fw-bold">اسم التصنيف</label>
<input type="text" name="name" id="modalName" class="form-control" required> <input type="text" name="name" id="modalName" class="form-control" required>
</div> </div>
<div class="mb-3">
<label class="form-label fw-bold">الحساب المحاسبي (للمزامنة التلقائية)</label>
<select name="account_id" id="modalAccountId" class="form-select">
<option value="">-- اختر الحساب --</option>
<?php foreach($accounts as $acc): ?>
<option value="<?= $acc['id'] ?>">
<?= htmlspecialchars($acc['name']) ?> (<?= $acc['type'] ?>)
</option>
<?php endforeach; ?>
</select>
<div class="form-text">عند تسجيل مصروف بهذا التصنيف، سيتم إنشاء قيد محاسبي تلقائياً على هذا الحساب.</div>
</div>
<div class="mb-3"> <div class="mb-3">
<label class="form-label fw-bold">الوصف</label> <label class="form-label fw-bold">الوصف</label>
@ -174,11 +197,13 @@ function openModal(action, data = null) {
document.getElementById('modalId').value = 0; document.getElementById('modalId').value = 0;
document.getElementById('modalName').value = ''; document.getElementById('modalName').value = '';
document.getElementById('modalDescription').value = ''; document.getElementById('modalDescription').value = '';
document.getElementById('modalAccountId').value = '';
} else { } else {
title.textContent = 'تعديل التصنيف'; title.textContent = 'تعديل التصنيف';
document.getElementById('modalId').value = data.id; document.getElementById('modalId').value = data.id;
document.getElementById('modalName').value = data.name; document.getElementById('modalName').value = data.name;
document.getElementById('modalDescription').value = data.description; document.getElementById('modalDescription').value = data.description;
document.getElementById('modalAccountId').value = data.account_id || '';
} }
categoryModal.show(); categoryModal.show();

View File

@ -1,5 +1,6 @@
<?php <?php
require_once __DIR__ . '/includes/header.php'; require_once __DIR__ . '/includes/header.php';
require_once __DIR__ . '/includes/accounting_functions.php'; // Include accounting helpers
if (!canView('expenses')) { if (!canView('expenses')) {
redirect('index.php'); redirect('index.php');
@ -42,20 +43,73 @@ if ($_SERVER['REQUEST_METHOD'] === 'POST') {
try { try {
$db = db(); $db = db();
$journal_id = null;
// --- Accounting Logic: Prepare Data ---
// 1. Get Category Account
$stmt_cat = $db->prepare("SELECT a.name as account_name FROM expense_categories c LEFT JOIN accounting_accounts a ON c.account_id = a.id WHERE c.id = ?");
$stmt_cat->execute([$category_id]);
$cat_account = $stmt_cat->fetchColumn();
// 2. Get Payment Account (Default: Cash / النقدية)
// Ideally, map Payment Method to Account. For now, defaulting to 'النقدية'.
$pay_account = 'النقدية';
// Could be improved: if ($payment_method == 'Bank Transfer') $pay_account = 'Bank'; etc.
if ($action === 'add') { if ($action === 'add') {
// Create Expense
$stmt = $db->prepare("INSERT INTO expenses (date, category_id, amount, description, reference, vendor, payment_method, receipt_file, user_id) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)"); $stmt = $db->prepare("INSERT INTO expenses (date, category_id, amount, description, reference, vendor, payment_method, receipt_file, user_id) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)");
$stmt->execute([$date, $category_id, $amount, $description, $reference, $vendor, $payment_method, $receipt_path, $_SESSION['user_id']]); $stmt->execute([$date, $category_id, $amount, $description, $reference, $vendor, $payment_method, $receipt_path, $_SESSION['user_id']]);
$_SESSION['success'] = 'تم إضافة المصروف بنجاح'; $expense_id = $db->lastInsertId();
} else {
// Get old file if not replaced // Create Journal Entry
if (!$receipt_path) { if ($cat_account) {
$stmt = $db->prepare("SELECT receipt_file FROM expenses WHERE id = ?"); $entries = [
$stmt->execute([$id]); ['account' => $cat_account, 'debit' => $amount, 'credit' => 0],
$receipt_path = $stmt->fetchColumn(); ['account' => $pay_account, 'debit' => 0, 'credit' => $amount]
];
$journal_desc = "Expense #$expense_id: " . ($vendor ? "$vendor - " : "") . $description;
$jid = add_journal_entry($date, $journal_desc, $reference, $entries);
if ($jid) {
$db->prepare("UPDATE expenses SET journal_id = ? WHERE id = ?")->execute([$jid, $expense_id]);
}
} }
$_SESSION['success'] = 'تم إضافة المصروف بنجاح' . ($cat_account ? ' وتم إنشاء قيد محاسبي.' : '');
} else {
// Update Expense
// Get existing journal_id and file
$stmt = $db->prepare("SELECT journal_id, receipt_file FROM expenses WHERE id = ?");
$stmt->execute([$id]);
$row = $stmt->fetch(PDO::FETCH_ASSOC);
$journal_id = $row['journal_id'] ?? null;
$old_file = $row['receipt_file'] ?? null;
if (!$receipt_path) $receipt_path = $old_file;
$stmt = $db->prepare("UPDATE expenses SET date=?, category_id=?, amount=?, description=?, reference=?, vendor=?, payment_method=?, receipt_file=? WHERE id=?"); $stmt = $db->prepare("UPDATE expenses SET date=?, category_id=?, amount=?, description=?, reference=?, vendor=?, payment_method=?, receipt_file=? WHERE id=?");
$stmt->execute([$date, $category_id, $amount, $description, $reference, $vendor, $payment_method, $receipt_path, $id]); $stmt->execute([$date, $category_id, $amount, $description, $reference, $vendor, $payment_method, $receipt_path, $id]);
// Update Journal Entry
if ($cat_account) {
$entries = [
['account' => $cat_account, 'debit' => $amount, 'credit' => 0],
['account' => $pay_account, 'debit' => 0, 'credit' => $amount]
];
$journal_desc = "Expense #$id: " . ($vendor ? "$vendor - " : "") . $description;
if ($journal_id) {
edit_journal_entry($journal_id, $date, $journal_desc, $reference, $entries);
} else {
// Create new if missing
$jid = add_journal_entry($date, $journal_desc, $reference, $entries);
if ($jid) {
$db->prepare("UPDATE expenses SET journal_id = ? WHERE id = ?")->execute([$jid, $id]);
}
}
}
$_SESSION['success'] = 'تم تحديث المصروف بنجاح'; $_SESSION['success'] = 'تم تحديث المصروف بنجاح';
} }
redirect('expenses.php'); redirect('expenses.php');
@ -70,14 +124,24 @@ if (isset($_GET['action']) && $_GET['action'] === 'delete' && isset($_GET['id'])
if (!canDelete('expenses')) redirect('expenses.php'); if (!canDelete('expenses')) redirect('expenses.php');
$id = $_GET['id']; $id = $_GET['id'];
$db = db(); $db = db();
// Get file to delete
$stmt = $db->prepare("SELECT receipt_file FROM expenses WHERE id = ?");
$stmt->execute([$id]);
$file = $stmt->fetchColumn();
if ($file && file_exists($file)) unlink($file);
// Get file and journal_id
$stmt = $db->prepare("SELECT receipt_file, journal_id FROM expenses WHERE id = ?");
$stmt->execute([$id]);
$row = $stmt->fetch(PDO::FETCH_ASSOC);
// Delete file
if ($row && $row['receipt_file'] && file_exists($row['receipt_file'])) unlink($row['receipt_file']);
// Delete Journal Entry
if ($row && $row['journal_id']) {
delete_journal_entry($row['journal_id']);
}
// Delete Expense
$stmt = $db->prepare("DELETE FROM expenses WHERE id = ?"); $stmt = $db->prepare("DELETE FROM expenses WHERE id = ?");
$stmt->execute([$id]); $stmt->execute([$id]);
$_SESSION['success'] = 'تم حذف المصروف بنجاح'; $_SESSION['success'] = 'تم حذف المصروف بنجاح';
redirect('expenses.php'); redirect('expenses.php');
} }

View File

@ -1,5 +1,6 @@
<?php <?php
require_once 'includes/header.php'; require_once 'includes/header.php';
require_once 'includes/accounting_functions.php'; // Include accounting helpers
if (!canView('hr_payroll')) { if (!canView('hr_payroll')) {
echo "<div class='alert alert-danger'>ليس لديك صلاحية للوصول إلى هذه الصفحة.</div>"; echo "<div class='alert alert-danger'>ليس لديك صلاحية للوصول إلى هذه الصفحة.</div>";
@ -14,6 +15,8 @@ $success = '';
// Handle Payroll Actions // Handle Payroll Actions
if ($_SERVER['REQUEST_METHOD'] === 'POST') { if ($_SERVER['REQUEST_METHOD'] === 'POST') {
$action = $_POST['action'] ?? '';
if (isset($_POST['generate_payroll'])) { if (isset($_POST['generate_payroll'])) {
if (!canAdd('hr_payroll')) { if (!canAdd('hr_payroll')) {
$error = "لا تملك صلاحية التوليد."; $error = "لا تملك صلاحية التوليد.";
@ -56,17 +59,59 @@ if ($_SERVER['REQUEST_METHOD'] === 'POST') {
$status = $_POST['status']; $status = $_POST['status'];
// Recalculate Net // Recalculate Net
$stmt = db()->prepare("SELECT basic_salary FROM hr_payroll WHERE id = ?"); $stmt = db()->prepare("SELECT * FROM hr_payroll WHERE id = ?");
$stmt->execute([$id]); $stmt->execute([$id]);
$current = $stmt->fetch(); $current = $stmt->fetch();
if ($current) { if ($current) {
$net = $current['basic_salary'] + $bonuses - $deductions; $net = $current['basic_salary'] + $bonuses - $deductions;
$payment_date = ($status == 'paid') ? date('Y-m-d') : null; $payment_date = ($status == 'paid') ? date('Y-m-d') : null;
$journal_id = $current['journal_id'];
$stmt = db()->prepare("UPDATE hr_payroll SET bonuses = ?, deductions = ?, net_salary = ?, status = ?, payment_date = ? WHERE id = ?"); try {
$stmt->execute([$bonuses, $deductions, $net, $status, $payment_date, $id]); $db = db();
$success = "تم تحديث الراتب."; // 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();
}
} }
} }
} }
@ -164,6 +209,9 @@ foreach ($payrolls as $p) $total_salaries += $p['net_salary'];
<span class="badge bg-<?= $row['status'] == 'paid' ? 'success' : 'warning' ?>"> <span class="badge bg-<?= $row['status'] == 'paid' ? 'success' : 'warning' ?>">
<?= $row['status'] == 'paid' ? 'مدفوع' : 'معلق' ?> <?= $row['status'] == 'paid' ? 'مدفوع' : 'معلق' ?>
</span> </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>
<td> <td>
<?php if (canEdit('hr_payroll')): ?> <?php if (canEdit('hr_payroll')): ?>
@ -260,4 +308,4 @@ foreach ($payrolls as $p) $total_salaries += $p['net_salary'];
}); });
</script> </script>
<?php require_once 'includes/footer.php'; ?> <?php require_once 'includes/footer.php'; ?>

View File

@ -90,7 +90,7 @@ function add_journal_entry($date, $description, $reference, $entries) {
$stmt->execute([$journal_id, $entry['account'], $entry['debit'], $entry['credit']]); $stmt->execute([$journal_id, $entry['account'], $entry['debit'], $entry['credit']]);
} }
$db->commit(); $db->commit();
return true; return $journal_id;
} catch (Exception $e) { } catch (Exception $e) {
$db->rollBack(); $db->rollBack();
return false; return false;

View File

@ -118,7 +118,7 @@ if (!isLoggedIn() && basename($_SERVER['PHP_SELF']) !== 'login.php' && basename(
// Determine active groups // Determine active groups
$cp = basename($_SERVER['PHP_SELF']); $cp = basename($_SERVER['PHP_SELF']);
$mail_pages = ['inbound.php', 'outbound.php', 'internal_inbox.php', 'internal_outbox.php']; $mail_pages = ['inbound.php', 'outbound.php', 'internal_inbox.php', 'internal_outbox.php', 'overdue_report.php'];
$is_mail_open = in_array($cp, $mail_pages); $is_mail_open = in_array($cp, $mail_pages);
$acct_pages = ['accounting.php', 'trial_balance.php', 'balance_sheet.php', 'accounts.php']; $acct_pages = ['accounting.php', 'trial_balance.php', 'balance_sheet.php', 'accounts.php'];
@ -133,9 +133,6 @@ $is_stock_open = in_array($cp, $stock_pages);
$expenses_pages = ['expenses.php', 'expense_categories.php', 'expense_reports.php']; $expenses_pages = ['expenses.php', 'expense_categories.php', 'expense_reports.php'];
$is_expenses_open = in_array($cp, $expenses_pages); $is_expenses_open = in_array($cp, $expenses_pages);
$report_pages = ['overdue_report.php'];
$is_report_open = in_array($cp, $report_pages);
$admin_pages = ['index.php', 'users.php', 'charity-settings.php']; $admin_pages = ['index.php', 'users.php', 'charity-settings.php'];
$is_admin_open = in_array($cp, $admin_pages); $is_admin_open = in_array($cp, $admin_pages);
?> ?>
@ -276,7 +273,7 @@ $is_admin_open = in_array($cp, $admin_pages);
</li> </li>
<!-- Mail Group --> <!-- Mail Group -->
<?php if (canView('inbound') || canView('outbound') || canView('internal')): ?> <?php if (canView('inbound') || canView('outbound') || canView('internal') || canView('reports')): ?>
<li class="nav-item"> <li class="nav-item">
<button class="sidebar-group-btn <?= $is_mail_open ? '' : 'collapsed' ?>" type="button" data-bs-toggle="collapse" data-bs-target="#menu-mail" aria-expanded="<?= $is_mail_open ? 'true' : 'false' ?>"> <button class="sidebar-group-btn <?= $is_mail_open ? '' : 'collapsed' ?>" type="button" data-bs-toggle="collapse" data-bs-target="#menu-mail" aria-expanded="<?= $is_mail_open ? 'true' : 'false' ?>">
<span class="group-content group-mail"> <span class="group-content group-mail">
@ -314,6 +311,14 @@ $is_admin_open = in_array($cp, $admin_pages);
</a> </a>
</li> </li>
<?php endif; ?> <?php endif; ?>
<?php if (canView('reports')): ?>
<li class="nav-item">
<a class="nav-link <?= $cp == 'overdue_report.php' ? 'active' : '' ?>" href="overdue_report.php">
تقرير التأخير
</a>
</li>
<?php endif; ?>
</ul> </ul>
</div> </div>
</li> </li>
@ -521,27 +526,6 @@ $is_admin_open = in_array($cp, $admin_pages);
</div> </div>
</li> </li>
<?php endif; ?> <?php endif; ?>
<!-- Reports Group -->
<?php if (canView('reports')): ?>
<li class="nav-item">
<button class="sidebar-group-btn <?= $is_report_open ? '' : 'collapsed' ?>" type="button" data-bs-toggle="collapse" data-bs-target="#menu-reports" aria-expanded="<?= $is_report_open ? 'true' : 'false' ?>">
<span class="group-content group-reports">
<i class="fas fa-chart-pie"></i> التقارير
</span>
<i class="fas fa-chevron-down arrow-icon"></i>
</button>
<div class="collapse <?= $is_report_open ? 'show' : '' ?>" id="menu-reports">
<ul class="nav flex-column">
<li class="nav-item">
<a class="nav-link <?= $cp == 'overdue_report.php' ? 'active' : '' ?>" href="overdue_report.php">
تقرير التأخير
</a>
</li>
</ul>
</div>
</li>
<?php endif; ?>
<!-- Admin Group --> <!-- Admin Group -->
<?php if (canView('users') || canView('settings') || isAdmin()): ?> <?php if (canView('users') || canView('settings') || isAdmin()): ?>