add paginations to all modules
This commit is contained in:
parent
1b3577b917
commit
7828e2ad4e
@ -2,6 +2,7 @@
|
|||||||
require_once 'db/config.php';
|
require_once 'db/config.php';
|
||||||
require_once 'includes/header.php';
|
require_once 'includes/header.php';
|
||||||
require_once 'includes/accounting_functions.php';
|
require_once 'includes/accounting_functions.php';
|
||||||
|
require_once 'includes/pagination.php';
|
||||||
|
|
||||||
// Check permission
|
// Check permission
|
||||||
$user_id = $_SESSION['user_id'] ?? 0;
|
$user_id = $_SESSION['user_id'] ?? 0;
|
||||||
@ -60,18 +61,19 @@ if ($_SERVER['REQUEST_METHOD'] === 'POST') {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Pagination and Filtering setup
|
// Pagination and Filtering setup
|
||||||
$page = isset($_GET['p']) ? (int)$_GET['p'] : 1;
|
$page = isset($_GET['page']) ? (int)$_GET['page'] : 1; // Standardized to 'page'
|
||||||
|
if ($page < 1) $page = 1;
|
||||||
$limit = 10;
|
$limit = 10;
|
||||||
$offset = ($page - 1) * $limit;
|
$offset = ($page - 1) * $limit;
|
||||||
|
|
||||||
$search = $_GET['search'] ?? '';
|
$search = $_GET['search'] ?? '';
|
||||||
$date_from = $_GET['date_from'] ?? '';
|
$date_from = $_GET['date_from'] ?? '';
|
||||||
$date_to = $_GET['date_to'] ?? '';
|
$date_to = $_GET['date_to'] ?? '';
|
||||||
|
|
||||||
// Fetch ledger data with filters
|
// Fetch ledger data with filters using optimized functions
|
||||||
$ledger_all = get_full_ledger_filtered($search, $date_from, $date_to);
|
$totalFiltered = get_ledger_count($search, $date_from, $date_to);
|
||||||
$total_items = count($ledger_all);
|
$ledger = get_ledger_paginated($search, $date_from, $date_to, $limit, $offset);
|
||||||
$total_pages = ceil($total_items / $limit);
|
|
||||||
$ledger = array_slice($ledger_all, $offset, $limit);
|
|
||||||
?>
|
?>
|
||||||
|
|
||||||
<link href="https://cdn.jsdelivr.net/npm/select2@4.1.0-rc.0/dist/css/select2.min.css" rel="stylesheet" />
|
<link href="https://cdn.jsdelivr.net/npm/select2@4.1.0-rc.0/dist/css/select2.min.css" rel="stylesheet" />
|
||||||
@ -237,6 +239,9 @@ $ledger = array_slice($ledger_all, $offset, $limit);
|
|||||||
<table class="table table-hover table-bordered text-right align-middle table-sm">
|
<table class="table table-hover table-bordered text-right align-middle table-sm">
|
||||||
<thead class="table-light"><tr><th>التاريخ</th><th>الوصف</th><th>المرجع</th><th>الحساب</th><th>مدين</th><th>دائن</th><th>الإجراءات</th></tr></thead>
|
<thead class="table-light"><tr><th>التاريخ</th><th>الوصف</th><th>المرجع</th><th>الحساب</th><th>مدين</th><th>دائن</th><th>الإجراءات</th></tr></thead>
|
||||||
<tbody>
|
<tbody>
|
||||||
|
<?php if(empty($ledger)): ?>
|
||||||
|
<tr><td colspan="7" class="text-center">لا توجد قيود.</td></tr>
|
||||||
|
<?php endif; ?>
|
||||||
<?php foreach ($ledger as $row): ?>
|
<?php foreach ($ledger as $row): ?>
|
||||||
<tr>
|
<tr>
|
||||||
<td><?= htmlspecialchars($row['date']) ?></td>
|
<td><?= htmlspecialchars($row['date']) ?></td>
|
||||||
@ -262,15 +267,8 @@ $ledger = array_slice($ledger_all, $offset, $limit);
|
|||||||
</table>
|
</table>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<nav>
|
<!-- Pagination -->
|
||||||
<ul class="pagination pagination-sm justify-content-center">
|
<?php renderPagination($page, $totalFiltered, $limit); ?>
|
||||||
<?php for ($i = 1; $i <= $total_pages; $i++): ?>
|
|
||||||
<li class="page-item <?= $i == $page ? 'active' : '' ?>">
|
|
||||||
<a class="page-link" href="?p=<?= $i ?>&search=<?= urlencode($search) ?>&date_from=<?= urlencode($date_from) ?>&date_to=<?= urlencode($date_to) ?>"><?= $i ?></a>
|
|
||||||
</li>
|
|
||||||
<?php endfor; ?>
|
|
||||||
</ul>
|
|
||||||
</nav>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@ -1,5 +1,6 @@
|
|||||||
<?php
|
<?php
|
||||||
require_once 'includes/header.php';
|
require_once 'includes/header.php';
|
||||||
|
require_once 'includes/pagination.php';
|
||||||
|
|
||||||
if (!canView('hr_attendance')) {
|
if (!canView('hr_attendance')) {
|
||||||
echo "<div class='alert alert-danger'>ليس لديك صلاحية للوصول إلى هذه الصفحة.</div>";
|
echo "<div class='alert alert-danger'>ليس لديك صلاحية للوصول إلى هذه الصفحة.</div>";
|
||||||
@ -44,13 +45,25 @@ if ($_SERVER['REQUEST_METHOD'] === 'POST' && isset($_POST['save_attendance'])) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 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
|
// Fetch Employees and their attendance for the selected date
|
||||||
$sql = "SELECT e.id, e.first_name, e.last_name, e.job_title,
|
$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
|
a.id as att_id, a.status, a.check_in, a.check_out, a.notes
|
||||||
FROM hr_employees e
|
FROM hr_employees e
|
||||||
LEFT JOIN hr_attendance a ON e.id = a.employee_id AND a.date = ?
|
LEFT JOIN hr_attendance a ON e.id = a.employee_id AND a.date = ?
|
||||||
WHERE e.status = 'active'
|
WHERE e.status = 'active'
|
||||||
ORDER BY e.first_name";
|
ORDER BY e.first_name
|
||||||
|
LIMIT $limit OFFSET $offset";
|
||||||
$stmt = db()->prepare($sql);
|
$stmt = db()->prepare($sql);
|
||||||
$stmt->execute([$date]);
|
$stmt->execute([$date]);
|
||||||
$records = $stmt->fetchAll();
|
$records = $stmt->fetchAll();
|
||||||
@ -74,7 +87,7 @@ $records = $stmt->fetchAll();
|
|||||||
<div class="alert alert-success"><?= htmlspecialchars($success) ?></div>
|
<div class="alert alert-success"><?= htmlspecialchars($success) ?></div>
|
||||||
<?php endif; ?>
|
<?php endif; ?>
|
||||||
|
|
||||||
<div class="card shadow-sm">
|
<div class="card shadow-sm border-0">
|
||||||
<div class="card-body">
|
<div class="card-body">
|
||||||
<div class="table-responsive">
|
<div class="table-responsive">
|
||||||
<table class="table table-hover align-middle">
|
<table class="table table-hover align-middle">
|
||||||
@ -90,6 +103,11 @@ $records = $stmt->fetchAll();
|
|||||||
</tr>
|
</tr>
|
||||||
</thead>
|
</thead>
|
||||||
<tbody>
|
<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): ?>
|
<?php foreach ($records as $row): ?>
|
||||||
<tr class="<?= !$row['att_id'] ? 'table-light text-muted' : '' ?>">
|
<tr class="<?= !$row['att_id'] ? 'table-light text-muted' : '' ?>">
|
||||||
<td class="fw-bold"><?= htmlspecialchars($row['first_name'] . ' ' . $row['last_name']) ?></td>
|
<td class="fw-bold"><?= htmlspecialchars($row['first_name'] . ' ' . $row['last_name']) ?></td>
|
||||||
@ -158,6 +176,9 @@ $records = $stmt->fetchAll();
|
|||||||
</tbody>
|
</tbody>
|
||||||
</table>
|
</table>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<!-- Pagination -->
|
||||||
|
<?php renderPagination($page, $totalFiltered, $limit); ?>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
@ -212,16 +233,18 @@ $records = $stmt->fetchAll();
|
|||||||
|
|
||||||
<script>
|
<script>
|
||||||
const attModal = document.getElementById('attModal');
|
const attModal = document.getElementById('attModal');
|
||||||
attModal.addEventListener('show.bs.modal', event => {
|
if (attModal) {
|
||||||
const button = event.relatedTarget;
|
attModal.addEventListener('show.bs.modal', event => {
|
||||||
|
const button = event.relatedTarget;
|
||||||
|
|
||||||
document.getElementById('modalEmpId').value = button.getAttribute('data-id');
|
document.getElementById('modalEmpId').value = button.getAttribute('data-id');
|
||||||
document.getElementById('modalEmpName').textContent = button.getAttribute('data-name');
|
document.getElementById('modalEmpName').textContent = button.getAttribute('data-name');
|
||||||
document.getElementById('modalStatus').value = button.getAttribute('data-status');
|
document.getElementById('modalStatus').value = button.getAttribute('data-status');
|
||||||
document.getElementById('modalIn').value = button.getAttribute('data-in');
|
document.getElementById('modalIn').value = button.getAttribute('data-in');
|
||||||
document.getElementById('modalOut').value = button.getAttribute('data-out');
|
document.getElementById('modalOut').value = button.getAttribute('data-out');
|
||||||
document.getElementById('modalNotes').value = button.getAttribute('data-notes');
|
document.getElementById('modalNotes').value = button.getAttribute('data-notes');
|
||||||
});
|
});
|
||||||
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<?php require_once 'includes/footer.php'; ?>
|
<?php require_once 'includes/footer.php'; ?>
|
||||||
@ -1,5 +1,6 @@
|
|||||||
<?php
|
<?php
|
||||||
require_once 'includes/header.php';
|
require_once 'includes/header.php';
|
||||||
|
require_once 'includes/pagination.php';
|
||||||
|
|
||||||
if (!canView('hr_leaves')) {
|
if (!canView('hr_leaves')) {
|
||||||
echo "<div class='alert alert-danger'>ليس لديك صلاحية للوصول إلى هذه الصفحة.</div>";
|
echo "<div class='alert alert-danger'>ليس لديك صلاحية للوصول إلى هذه الصفحة.</div>";
|
||||||
@ -64,14 +65,27 @@ if ($_SERVER['REQUEST_METHOD'] === 'POST') {
|
|||||||
// Fetch Employees for Dropdown
|
// Fetch Employees for Dropdown
|
||||||
$employees = db()->query("SELECT id, first_name, last_name FROM hr_employees WHERE status = 'active' ORDER BY first_name")->fetchAll();
|
$employees = db()->query("SELECT id, first_name, last_name FROM hr_employees WHERE status = 'active' ORDER BY first_name")->fetchAll();
|
||||||
|
|
||||||
|
// Pagination
|
||||||
|
$page = isset($_GET['page']) ? (int)$_GET['page'] : 1;
|
||||||
|
if ($page < 1) $page = 1;
|
||||||
|
$limit = 10;
|
||||||
|
$offset = ($page - 1) * $limit;
|
||||||
|
|
||||||
// Fetch Leaves based on Tab
|
// Fetch Leaves based on Tab
|
||||||
$where_clause = $tab === 'pending' ? "WHERE l.status = 'pending'" : "WHERE 1=1";
|
$where_clause = $tab === 'pending' ? "WHERE l.status = 'pending'" : "WHERE 1=1";
|
||||||
|
|
||||||
|
// Count Total
|
||||||
|
$countSql = "SELECT COUNT(*) FROM hr_leaves l $where_clause";
|
||||||
|
$countStmt = db()->query($countSql);
|
||||||
|
$totalFiltered = $countStmt->fetchColumn();
|
||||||
|
|
||||||
$sql = "SELECT l.*, e.first_name, e.last_name, u.full_name as approver_name
|
$sql = "SELECT l.*, e.first_name, e.last_name, u.full_name as approver_name
|
||||||
FROM hr_leaves l
|
FROM hr_leaves l
|
||||||
JOIN hr_employees e ON l.employee_id = e.id
|
JOIN hr_employees e ON l.employee_id = e.id
|
||||||
LEFT JOIN users u ON l.approved_by = u.id
|
LEFT JOIN users u ON l.approved_by = u.id
|
||||||
$where_clause
|
$where_clause
|
||||||
ORDER BY l.created_at DESC";
|
ORDER BY l.created_at DESC
|
||||||
|
LIMIT $limit OFFSET $offset";
|
||||||
$requests = db()->query($sql)->fetchAll();
|
$requests = db()->query($sql)->fetchAll();
|
||||||
|
|
||||||
?>
|
?>
|
||||||
@ -196,6 +210,9 @@ $requests = db()->query($sql)->fetchAll();
|
|||||||
</tbody>
|
</tbody>
|
||||||
</table>
|
</table>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<!-- Pagination -->
|
||||||
|
<?php renderPagination($page, $totalFiltered, $limit); ?>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|||||||
@ -1,6 +1,7 @@
|
|||||||
<?php
|
<?php
|
||||||
require_once 'includes/header.php';
|
require_once 'includes/header.php';
|
||||||
require_once 'includes/accounting_functions.php'; // Include accounting helpers
|
require_once 'includes/accounting_functions.php'; // Include accounting helpers
|
||||||
|
require_once 'includes/pagination.php';
|
||||||
|
|
||||||
if (!canView('hr_payroll')) {
|
if (!canView('hr_payroll')) {
|
||||||
echo "<div class='alert alert-danger'>ليس لديك صلاحية للوصول إلى هذه الصفحة.</div>";
|
echo "<div class='alert alert-danger'>ليس لديك صلاحية للوصول إلى هذه الصفحة.</div>";
|
||||||
@ -117,19 +118,34 @@ if ($_SERVER['REQUEST_METHOD'] === 'POST') {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Pagination
|
||||||
|
$page = isset($_GET['page']) ? (int)$_GET['page'] : 1;
|
||||||
|
if ($page < 1) $page = 1;
|
||||||
|
$limit = 10;
|
||||||
|
$offset = ($page - 1) * $limit;
|
||||||
|
|
||||||
// Fetch Payroll Records
|
// Fetch Payroll Records
|
||||||
$sql = "SELECT p.*, e.first_name, e.last_name, e.job_title
|
$sql = "SELECT p.*, e.first_name, e.last_name, e.job_title
|
||||||
FROM hr_payroll p
|
FROM hr_payroll p
|
||||||
JOIN hr_employees e ON p.employee_id = e.id
|
JOIN hr_employees e ON p.employee_id = e.id
|
||||||
WHERE p.month = ? AND p.year = ?
|
WHERE p.month = ? AND p.year = ?
|
||||||
ORDER BY e.first_name";
|
ORDER BY e.first_name
|
||||||
|
LIMIT $limit OFFSET $offset";
|
||||||
$stmt = db()->prepare($sql);
|
$stmt = db()->prepare($sql);
|
||||||
$stmt->execute([$month, $year]);
|
$stmt->execute([$month, $year]);
|
||||||
$payrolls = $stmt->fetchAll();
|
$payrolls = $stmt->fetchAll();
|
||||||
|
|
||||||
// Calculate Totals
|
// Count Total for Pagination
|
||||||
$total_salaries = 0;
|
$countSql = "SELECT COUNT(*) FROM hr_payroll WHERE month = ? AND year = ?";
|
||||||
foreach ($payrolls as $p) $total_salaries += $p['net_salary'];
|
$countStmt = db()->prepare($countSql);
|
||||||
|
$countStmt->execute([$month, $year]);
|
||||||
|
$totalFiltered = $countStmt->fetchColumn();
|
||||||
|
|
||||||
|
// Calculate Grand Total Salaries (for the stats card)
|
||||||
|
$sumSql = "SELECT SUM(net_salary) FROM hr_payroll WHERE month = ? AND year = ?";
|
||||||
|
$sumStmt = db()->prepare($sumSql);
|
||||||
|
$sumStmt->execute([$month, $year]);
|
||||||
|
$total_salaries = $sumStmt->fetchColumn() ?: 0;
|
||||||
|
|
||||||
?>
|
?>
|
||||||
|
|
||||||
@ -233,6 +249,9 @@ foreach ($payrolls as $p) $total_salaries += $p['net_salary'];
|
|||||||
</tbody>
|
</tbody>
|
||||||
</table>
|
</table>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<!-- Pagination -->
|
||||||
|
<?php renderPagination($page, $totalFiltered, $limit); ?>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
@ -298,14 +317,16 @@ foreach ($payrolls as $p) $total_salaries += $p['net_salary'];
|
|||||||
|
|
||||||
<script>
|
<script>
|
||||||
const editPayModal = document.getElementById('editPayModal');
|
const editPayModal = document.getElementById('editPayModal');
|
||||||
editPayModal.addEventListener('show.bs.modal', event => {
|
if (editPayModal) {
|
||||||
const button = event.relatedTarget;
|
editPayModal.addEventListener('show.bs.modal', event => {
|
||||||
document.getElementById('payId').value = button.getAttribute('data-id');
|
const button = event.relatedTarget;
|
||||||
document.getElementById('payName').textContent = button.getAttribute('data-name');
|
document.getElementById('payId').value = button.getAttribute('data-id');
|
||||||
document.getElementById('payBonus').value = button.getAttribute('data-bonus');
|
document.getElementById('payName').textContent = button.getAttribute('data-name');
|
||||||
document.getElementById('payDeduct').value = button.getAttribute('data-deduct');
|
document.getElementById('payBonus').value = button.getAttribute('data-bonus');
|
||||||
document.getElementById('payStatus').value = button.getAttribute('data-status');
|
document.getElementById('payDeduct').value = button.getAttribute('data-deduct');
|
||||||
});
|
document.getElementById('payStatus').value = button.getAttribute('data-status');
|
||||||
|
});
|
||||||
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<?php require_once 'includes/footer.php'; ?>
|
<?php require_once 'includes/footer.php'; ?>
|
||||||
19
inbound.php
19
inbound.php
@ -1,5 +1,6 @@
|
|||||||
<?php
|
<?php
|
||||||
require_once 'includes/header.php';
|
require_once 'includes/header.php';
|
||||||
|
require_once 'includes/pagination.php';
|
||||||
|
|
||||||
$error = '';
|
$error = '';
|
||||||
$success = '';
|
$success = '';
|
||||||
@ -100,13 +101,26 @@ if (isset($_GET['my_tasks'])) {
|
|||||||
$params[] = $_SESSION['user_id'];
|
$params[] = $_SESSION['user_id'];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Pagination
|
||||||
|
$page = isset($_GET['page']) ? (int)$_GET['page'] : 1;
|
||||||
|
if ($page < 1) $page = 1;
|
||||||
|
$limit = 10;
|
||||||
|
$offset = ($page - 1) * $limit;
|
||||||
|
|
||||||
|
// Count filtered
|
||||||
|
$countQuery = "SELECT COUNT(*) FROM inbound_mail m $where";
|
||||||
|
$countStmt = db()->prepare($countQuery);
|
||||||
|
$countStmt->execute($params);
|
||||||
|
$totalFiltered = $countStmt->fetchColumn();
|
||||||
|
|
||||||
$query = "SELECT m.*, s.name as status_name, s.color as status_color, u.full_name as assigned_to_name,
|
$query = "SELECT m.*, s.name as status_name, s.color as status_color, u.full_name as assigned_to_name,
|
||||||
(SELECT GROUP_CONCAT(display_name SEPARATOR '|||') FROM inbound_attachments WHERE mail_id = m.id) as attachment_names
|
(SELECT GROUP_CONCAT(display_name SEPARATOR '|||') FROM inbound_attachments WHERE mail_id = m.id) as attachment_names
|
||||||
FROM inbound_mail m
|
FROM inbound_mail m
|
||||||
LEFT JOIN mailbox_statuses s ON m.status_id = s.id
|
LEFT JOIN mailbox_statuses s ON m.status_id = s.id
|
||||||
LEFT JOIN users u ON m.assigned_to = u.id
|
LEFT JOIN users u ON m.assigned_to = u.id
|
||||||
$where
|
$where
|
||||||
ORDER BY m.date_registered DESC, m.id DESC";
|
ORDER BY m.date_registered DESC, m.id DESC
|
||||||
|
LIMIT $limit OFFSET $offset";
|
||||||
|
|
||||||
$stmt = db()->prepare($query);
|
$stmt = db()->prepare($query);
|
||||||
$stmt->execute($params);
|
$stmt->execute($params);
|
||||||
@ -290,6 +304,9 @@ if (isset($_GET['id'])) {
|
|||||||
</tbody>
|
</tbody>
|
||||||
</table>
|
</table>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<!-- Pagination -->
|
||||||
|
<?php renderPagination($page, $totalFiltered, $limit); ?>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|||||||
@ -49,6 +49,64 @@ function get_full_ledger_filtered($search = '', $date_from = '', $date_to = '')
|
|||||||
return $stmt->fetchAll(PDO::FETCH_ASSOC);
|
return $stmt->fetchAll(PDO::FETCH_ASSOC);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function get_ledger_count($search = '', $date_from = '', $date_to = '') {
|
||||||
|
$db = db();
|
||||||
|
$sql = "SELECT COUNT(*)
|
||||||
|
FROM accounting_journal j
|
||||||
|
JOIN accounting_entries e ON j.id = e.journal_id
|
||||||
|
WHERE 1=1";
|
||||||
|
$params = [];
|
||||||
|
|
||||||
|
if ($search) {
|
||||||
|
$sql .= " AND (j.description LIKE ? OR j.reference LIKE ? OR e.account_name LIKE ?)";
|
||||||
|
$params[] = "%$search%";
|
||||||
|
$params[] = "%$search%";
|
||||||
|
$params[] = "%$search%";
|
||||||
|
}
|
||||||
|
if ($date_from) {
|
||||||
|
$sql .= " AND j.date >= ?";
|
||||||
|
$params[] = $date_from;
|
||||||
|
}
|
||||||
|
if ($date_to) {
|
||||||
|
$sql .= " AND j.date <= ?";
|
||||||
|
$params[] = $date_to;
|
||||||
|
}
|
||||||
|
|
||||||
|
$stmt = $db->prepare($sql);
|
||||||
|
$stmt->execute($params);
|
||||||
|
return $stmt->fetchColumn();
|
||||||
|
}
|
||||||
|
|
||||||
|
function get_ledger_paginated($search = '', $date_from = '', $date_to = '', $limit = 10, $offset = 0) {
|
||||||
|
$db = db();
|
||||||
|
$sql = "SELECT j.id, j.date, j.description, j.reference, e.account_name, e.debit, e.credit
|
||||||
|
FROM accounting_journal j
|
||||||
|
JOIN accounting_entries e ON j.id = e.journal_id
|
||||||
|
WHERE 1=1";
|
||||||
|
$params = [];
|
||||||
|
|
||||||
|
if ($search) {
|
||||||
|
$sql .= " AND (j.description LIKE ? OR j.reference LIKE ? OR e.account_name LIKE ?)";
|
||||||
|
$params[] = "%$search%";
|
||||||
|
$params[] = "%$search%";
|
||||||
|
$params[] = "%$search%";
|
||||||
|
}
|
||||||
|
if ($date_from) {
|
||||||
|
$sql .= " AND j.date >= ?";
|
||||||
|
$params[] = $date_from;
|
||||||
|
}
|
||||||
|
if ($date_to) {
|
||||||
|
$sql .= " AND j.date <= ?";
|
||||||
|
$params[] = $date_to;
|
||||||
|
}
|
||||||
|
|
||||||
|
$sql .= " ORDER BY j.date DESC, j.id DESC LIMIT $limit OFFSET $offset";
|
||||||
|
|
||||||
|
$stmt = $db->prepare($sql);
|
||||||
|
$stmt->execute($params);
|
||||||
|
return $stmt->fetchAll(PDO::FETCH_ASSOC);
|
||||||
|
}
|
||||||
|
|
||||||
function get_trial_balance() {
|
function get_trial_balance() {
|
||||||
$db = db();
|
$db = db();
|
||||||
$stmt = $db->query("SELECT account_name, SUM(debit) as total_debit, SUM(credit) as total_credit
|
$stmt = $db->query("SELECT account_name, SUM(debit) as total_debit, SUM(credit) as total_credit
|
||||||
|
|||||||
20
outbound.php
20
outbound.php
@ -1,5 +1,6 @@
|
|||||||
<?php
|
<?php
|
||||||
require_once 'includes/header.php';
|
require_once 'includes/header.php';
|
||||||
|
require_once 'includes/pagination.php';
|
||||||
|
|
||||||
$error = '';
|
$error = '';
|
||||||
$success = '';
|
$success = '';
|
||||||
@ -95,13 +96,27 @@ if (isset($_GET['status_id']) && !empty($_GET['status_id'])) {
|
|||||||
$params[] = $_GET['status_id'];
|
$params[] = $_GET['status_id'];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Pagination
|
||||||
|
$page = isset($_GET['page']) ? (int)$_GET['page'] : 1;
|
||||||
|
if ($page < 1) $page = 1;
|
||||||
|
$limit = 10;
|
||||||
|
$offset = ($page - 1) * $limit;
|
||||||
|
|
||||||
|
// Count filtered
|
||||||
|
$countQuery = "SELECT COUNT(*) FROM outbound_mail m $where";
|
||||||
|
$countStmt = db()->prepare($countQuery);
|
||||||
|
$countStmt->execute($params);
|
||||||
|
$totalFiltered = $countStmt->fetchColumn();
|
||||||
|
|
||||||
|
|
||||||
$query = "SELECT m.*, s.name as status_name, s.color as status_color, u.full_name as assigned_to_name,
|
$query = "SELECT m.*, s.name as status_name, s.color as status_color, u.full_name as assigned_to_name,
|
||||||
(SELECT GROUP_CONCAT(display_name SEPARATOR '|||') FROM outbound_attachments WHERE mail_id = m.id) as attachment_names
|
(SELECT GROUP_CONCAT(display_name SEPARATOR '|||') FROM outbound_attachments WHERE mail_id = m.id) as attachment_names
|
||||||
FROM outbound_mail m
|
FROM outbound_mail m
|
||||||
LEFT JOIN mailbox_statuses s ON m.status_id = s.id
|
LEFT JOIN mailbox_statuses s ON m.status_id = s.id
|
||||||
LEFT JOIN users u ON m.assigned_to = u.id
|
LEFT JOIN users u ON m.assigned_to = u.id
|
||||||
$where
|
$where
|
||||||
ORDER BY m.date_registered DESC, m.id DESC";
|
ORDER BY m.date_registered DESC, m.id DESC
|
||||||
|
LIMIT $limit OFFSET $offset";
|
||||||
|
|
||||||
$stmt = db()->prepare($query);
|
$stmt = db()->prepare($query);
|
||||||
$stmt->execute($params);
|
$stmt->execute($params);
|
||||||
@ -270,6 +285,9 @@ if (isset($_GET['id'])) {
|
|||||||
</tbody>
|
</tbody>
|
||||||
</table>
|
</table>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<!-- Pagination -->
|
||||||
|
<?php renderPagination($page, $totalFiltered, $limit); ?>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|||||||
80
stock_in.php
80
stock_in.php
@ -1,5 +1,6 @@
|
|||||||
<?php
|
<?php
|
||||||
require_once __DIR__ . '/includes/header.php';
|
require_once __DIR__ . '/includes/header.php';
|
||||||
|
require_once __DIR__ . '/includes/pagination.php';
|
||||||
|
|
||||||
if (!canView('stock_in')) {
|
if (!canView('stock_in')) {
|
||||||
echo '<div class="alert alert-danger">عذراً، ليس لديك صلاحية الوصول لهذه الصفحة.</div>';
|
echo '<div class="alert alert-danger">عذراً، ليس لديك صلاحية الوصول لهذه الصفحة.</div>';
|
||||||
@ -57,6 +58,36 @@ if ($_SERVER['REQUEST_METHOD'] === 'POST') {
|
|||||||
$stores = db()->query("SELECT * FROM stock_stores ORDER BY name ASC")->fetchAll();
|
$stores = db()->query("SELECT * FROM stock_stores ORDER BY name ASC")->fetchAll();
|
||||||
$items = db()->query("SELECT * FROM stock_items ORDER BY name ASC")->fetchAll();
|
$items = db()->query("SELECT * FROM stock_items ORDER BY name ASC")->fetchAll();
|
||||||
|
|
||||||
|
// Pagination for History
|
||||||
|
$page = isset($_GET['page']) ? (int)$_GET['page'] : 1;
|
||||||
|
if ($page < 1) $page = 1;
|
||||||
|
$limit = 10;
|
||||||
|
$offset = ($page - 1) * $limit;
|
||||||
|
|
||||||
|
$where = "WHERE t.transaction_type = 'in'";
|
||||||
|
$params = [];
|
||||||
|
|
||||||
|
// Count Total
|
||||||
|
$countQuery = "SELECT COUNT(*) FROM stock_transactions t $where";
|
||||||
|
$countStmt = db()->prepare($countQuery);
|
||||||
|
$countStmt->execute($params);
|
||||||
|
$totalFiltered = $countStmt->fetchColumn();
|
||||||
|
|
||||||
|
// Fetch History
|
||||||
|
$historyQuery = "
|
||||||
|
SELECT t.*, i.name as item_name, s.name as store_name, u.full_name as user_name
|
||||||
|
FROM stock_transactions t
|
||||||
|
JOIN stock_items i ON t.item_id = i.id
|
||||||
|
JOIN stock_stores s ON t.store_id = s.id
|
||||||
|
LEFT JOIN users u ON t.user_id = u.id
|
||||||
|
$where
|
||||||
|
ORDER BY t.created_at DESC
|
||||||
|
LIMIT $limit OFFSET $offset
|
||||||
|
";
|
||||||
|
$stmt = db()->prepare($historyQuery);
|
||||||
|
$stmt->execute($params);
|
||||||
|
$history = $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">
|
<div class="d-flex justify-content-between flex-wrap flex-md-nowrap align-items-center pt-3 pb-2 mb-3 border-bottom">
|
||||||
@ -80,9 +111,12 @@ $items = db()->query("SELECT * FROM stock_items ORDER BY name ASC")->fetchAll();
|
|||||||
</div>
|
</div>
|
||||||
<?php endif; ?>
|
<?php endif; ?>
|
||||||
|
|
||||||
<div class="row justify-content-center">
|
<div class="row justify-content-center mb-5">
|
||||||
<div class="col-md-8">
|
<div class="col-md-8">
|
||||||
<div class="card shadow-sm border-0">
|
<div class="card shadow-sm border-0">
|
||||||
|
<div class="card-header bg-success text-white">
|
||||||
|
<h5 class="mb-0"><i class="fas fa-plus-circle me-2"></i> تسجيل توريد جديد</h5>
|
||||||
|
</div>
|
||||||
<div class="card-body p-4">
|
<div class="card-body p-4">
|
||||||
<form method="POST">
|
<form method="POST">
|
||||||
<div class="row">
|
<div class="row">
|
||||||
@ -133,4 +167,48 @@ $items = db()->query("SELECT * FROM stock_items ORDER BY name ASC")->fetchAll();
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<!-- History Table -->
|
||||||
|
<div class="card shadow-sm border-0 mb-4">
|
||||||
|
<div class="card-header bg-white py-3">
|
||||||
|
<h5 class="mb-0 fw-bold"><i class="fas fa-history me-2 text-secondary"></i> سجل عمليات التوريد</h5>
|
||||||
|
</div>
|
||||||
|
<div class="table-responsive">
|
||||||
|
<table class="table table-hover align-middle mb-0">
|
||||||
|
<thead class="bg-light">
|
||||||
|
<tr>
|
||||||
|
<th class="ps-4">#</th>
|
||||||
|
<th>الصنف</th>
|
||||||
|
<th>المستودع</th>
|
||||||
|
<th>الكمية</th>
|
||||||
|
<th>بواسطة</th>
|
||||||
|
<th>التاريخ</th>
|
||||||
|
<th>المرجع</th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody>
|
||||||
|
<?php if (empty($history)): ?>
|
||||||
|
<tr>
|
||||||
|
<td colspan="7" class="text-center py-4 text-muted">لا توجد عمليات توريد سابقة.</td>
|
||||||
|
</tr>
|
||||||
|
<?php else: ?>
|
||||||
|
<?php foreach ($history as $h): ?>
|
||||||
|
<tr>
|
||||||
|
<td class="ps-4"><?= $h['id'] ?></td>
|
||||||
|
<td class="fw-bold"><?= htmlspecialchars($h['item_name']) ?></td>
|
||||||
|
<td><?= htmlspecialchars($h['store_name']) ?></td>
|
||||||
|
<td class="text-success fw-bold" dir="ltr">+<?= number_format($h['quantity'], 2) ?></td>
|
||||||
|
<td><?= htmlspecialchars($h['user_name'] ?? '-') ?></td>
|
||||||
|
<td><?= date('Y-m-d H:i', strtotime($h['created_at'])) ?></td>
|
||||||
|
<td><small class="text-muted"><?= htmlspecialchars($h['reference'] ?? '-') ?></small></td>
|
||||||
|
</tr>
|
||||||
|
<?php endforeach; ?>
|
||||||
|
<?php endif; ?>
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Pagination -->
|
||||||
|
<?php renderPagination($page, $totalFiltered, $limit); ?>
|
||||||
|
</div>
|
||||||
|
|
||||||
<?php require_once __DIR__ . '/includes/footer.php'; ?>
|
<?php require_once __DIR__ . '/includes/footer.php'; ?>
|
||||||
@ -1,5 +1,6 @@
|
|||||||
<?php
|
<?php
|
||||||
require_once __DIR__ . '/includes/header.php';
|
require_once __DIR__ . '/includes/header.php';
|
||||||
|
require_once __DIR__ . '/includes/pagination.php';
|
||||||
|
|
||||||
if (!canView('stock_items')) {
|
if (!canView('stock_items')) {
|
||||||
echo '<div class="alert alert-danger">عذراً، ليس لديك صلاحية الوصول لهذه الصفحة.</div>';
|
echo '<div class="alert alert-danger">عذراً، ليس لديك صلاحية الوصول لهذه الصفحة.</div>';
|
||||||
@ -51,15 +52,45 @@ if ($_SERVER['REQUEST_METHOD'] === 'POST') {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Pagination & Search
|
||||||
|
$page = isset($_GET['page']) ? (int)$_GET['page'] : 1;
|
||||||
|
if ($page < 1) $page = 1;
|
||||||
|
$limit = 10;
|
||||||
|
$offset = ($page - 1) * $limit;
|
||||||
|
|
||||||
|
$where = "WHERE 1=1";
|
||||||
|
$params = [];
|
||||||
|
|
||||||
|
if (isset($_GET['search']) && !empty($_GET['search'])) {
|
||||||
|
$where .= " AND (i.name LIKE ? OR i.sku LIKE ?)";
|
||||||
|
$search = "%" . $_GET['search'] . "%";
|
||||||
|
$params = array_merge($params, [$search, $search]);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (isset($_GET['category_id']) && !empty($_GET['category_id'])) {
|
||||||
|
$where .= " AND i.category_id = ?";
|
||||||
|
$params[] = $_GET['category_id'];
|
||||||
|
}
|
||||||
|
|
||||||
|
// Count Total
|
||||||
|
$countQuery = "SELECT COUNT(*) FROM stock_items i $where";
|
||||||
|
$countStmt = db()->prepare($countQuery);
|
||||||
|
$countStmt->execute($params);
|
||||||
|
$totalFiltered = $countStmt->fetchColumn();
|
||||||
|
|
||||||
// Fetch Items with Category Name and Total Quantity
|
// Fetch Items with Category Name and Total Quantity
|
||||||
$query = "
|
$query = "
|
||||||
SELECT i.*, c.name as category_name,
|
SELECT i.*, c.name as category_name,
|
||||||
(SELECT SUM(quantity) FROM stock_quantities q WHERE q.item_id = i.id) as total_quantity
|
(SELECT SUM(quantity) FROM stock_quantities q WHERE q.item_id = i.id) as total_quantity
|
||||||
FROM stock_items i
|
FROM stock_items i
|
||||||
LEFT JOIN stock_categories c ON i.category_id = c.id
|
LEFT JOIN stock_categories c ON i.category_id = c.id
|
||||||
|
$where
|
||||||
ORDER BY i.name ASC
|
ORDER BY i.name ASC
|
||||||
|
LIMIT $limit OFFSET $offset
|
||||||
";
|
";
|
||||||
$items = db()->query($query)->fetchAll();
|
$stmt = db()->prepare($query);
|
||||||
|
$stmt->execute($params);
|
||||||
|
$items = $stmt->fetchAll();
|
||||||
|
|
||||||
// Fetch Categories for Dropdown
|
// Fetch Categories for Dropdown
|
||||||
$categories = db()->query("SELECT * FROM stock_categories ORDER BY name ASC")->fetchAll();
|
$categories = db()->query("SELECT * FROM stock_categories ORDER BY name ASC")->fetchAll();
|
||||||
@ -89,6 +120,31 @@ $categories = db()->query("SELECT * FROM stock_categories ORDER BY name ASC")->f
|
|||||||
</div>
|
</div>
|
||||||
<?php endif; ?>
|
<?php endif; ?>
|
||||||
|
|
||||||
|
<!-- Filter Bar -->
|
||||||
|
<div class="card border-0 shadow-sm mb-4">
|
||||||
|
<div class="card-body p-3">
|
||||||
|
<form method="GET" class="row g-2 align-items-center">
|
||||||
|
<div class="col-md-5">
|
||||||
|
<div class="input-group">
|
||||||
|
<span class="input-group-text bg-white border-end-0"><i class="fas fa-search text-muted"></i></span>
|
||||||
|
<input type="text" name="search" class="form-control border-start-0" placeholder="بحث باسم الصنف أو الرمز (SKU)..." value="<?= htmlspecialchars($_GET['search'] ?? '') ?>">
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="col-md-4">
|
||||||
|
<select name="category_id" class="form-select" onchange="this.form.submit()">
|
||||||
|
<option value="">جميع التصنيفات</option>
|
||||||
|
<?php foreach ($categories as $cat): ?>
|
||||||
|
<option value="<?= $cat['id'] ?>" <?= (isset($_GET['category_id']) && $_GET['category_id'] == $cat['id']) ? 'selected' : '' ?>><?= $cat['name'] ?></option>
|
||||||
|
<?php endforeach; ?>
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
<div class="col-md-3 text-end">
|
||||||
|
<button type="submit" class="btn btn-light w-100">تصفية</button>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
<div class="card shadow-sm border-0">
|
<div class="card shadow-sm border-0">
|
||||||
<div class="card-body p-0">
|
<div class="card-body p-0">
|
||||||
<div class="table-responsive">
|
<div class="table-responsive">
|
||||||
@ -105,6 +161,14 @@ $categories = db()->query("SELECT * FROM stock_categories ORDER BY name ASC")->f
|
|||||||
</tr>
|
</tr>
|
||||||
</thead>
|
</thead>
|
||||||
<tbody>
|
<tbody>
|
||||||
|
<?php if (empty($items)): ?>
|
||||||
|
<tr>
|
||||||
|
<td colspan="7" class="text-center py-5 text-muted">
|
||||||
|
<i class="fas fa-boxes fa-3x mb-3 opacity-20"></i>
|
||||||
|
<p>لا يوجد أصناف مطابقة.</p>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
<?php endif; ?>
|
||||||
<?php foreach ($items as $item): ?>
|
<?php foreach ($items as $item): ?>
|
||||||
<tr>
|
<tr>
|
||||||
<td class="ps-4 fw-bold"><?= htmlspecialchars($item['name']) ?></td>
|
<td class="ps-4 fw-bold"><?= htmlspecialchars($item['name']) ?></td>
|
||||||
@ -136,6 +200,9 @@ $categories = db()->query("SELECT * FROM stock_categories ORDER BY name ASC")->f
|
|||||||
</tbody>
|
</tbody>
|
||||||
</table>
|
</table>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<!-- Pagination -->
|
||||||
|
<?php renderPagination($page, $totalFiltered, $limit); ?>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
@ -201,10 +268,14 @@ $categories = db()->query("SELECT * FROM stock_categories ORDER BY name ASC")->f
|
|||||||
<script>
|
<script>
|
||||||
let itemModal;
|
let itemModal;
|
||||||
document.addEventListener('DOMContentLoaded', function() {
|
document.addEventListener('DOMContentLoaded', function() {
|
||||||
itemModal = new bootstrap.Modal(document.getElementById('itemModal'));
|
var modalEl = document.getElementById('itemModal');
|
||||||
|
if (modalEl) {
|
||||||
|
itemModal = new bootstrap.Modal(modalEl);
|
||||||
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
function openItemModal(action, data = null) {
|
function openItemModal(action, data = null) {
|
||||||
|
if (!itemModal) return;
|
||||||
document.getElementById('modalAction').value = action;
|
document.getElementById('modalAction').value = action;
|
||||||
document.getElementById('itemModalLabel').textContent = action === 'add' ? 'إضافة صنف جديد' : 'تعديل الصنف';
|
document.getElementById('itemModalLabel').textContent = action === 'add' ? 'إضافة صنف جديد' : 'تعديل الصنف';
|
||||||
|
|
||||||
|
|||||||
@ -1,5 +1,6 @@
|
|||||||
<?php
|
<?php
|
||||||
require_once __DIR__ . '/includes/header.php';
|
require_once __DIR__ . '/includes/header.php';
|
||||||
|
require_once __DIR__ . '/includes/pagination.php';
|
||||||
|
|
||||||
if (!canView('stock_out')) {
|
if (!canView('stock_out')) {
|
||||||
echo '<div class="alert alert-danger">عذراً، ليس لديك صلاحية الوصول لهذه الصفحة.</div>';
|
echo '<div class="alert alert-danger">عذراً، ليس لديك صلاحية الوصول لهذه الصفحة.</div>';
|
||||||
@ -57,6 +58,36 @@ if ($_SERVER['REQUEST_METHOD'] === 'POST') {
|
|||||||
$stores = db()->query("SELECT * FROM stock_stores ORDER BY name ASC")->fetchAll();
|
$stores = db()->query("SELECT * FROM stock_stores ORDER BY name ASC")->fetchAll();
|
||||||
$items = db()->query("SELECT * FROM stock_items ORDER BY name ASC")->fetchAll();
|
$items = db()->query("SELECT * FROM stock_items ORDER BY name ASC")->fetchAll();
|
||||||
|
|
||||||
|
// Pagination for History
|
||||||
|
$page = isset($_GET['page']) ? (int)$_GET['page'] : 1;
|
||||||
|
if ($page < 1) $page = 1;
|
||||||
|
$limit = 10;
|
||||||
|
$offset = ($page - 1) * $limit;
|
||||||
|
|
||||||
|
$where = "WHERE t.transaction_type IN ('out', 'damage')";
|
||||||
|
$params = [];
|
||||||
|
|
||||||
|
// Count Total
|
||||||
|
$countQuery = "SELECT COUNT(*) FROM stock_transactions t $where";
|
||||||
|
$countStmt = db()->prepare($countQuery);
|
||||||
|
$countStmt->execute($params);
|
||||||
|
$totalFiltered = $countStmt->fetchColumn();
|
||||||
|
|
||||||
|
// Fetch History
|
||||||
|
$historyQuery = "
|
||||||
|
SELECT t.*, i.name as item_name, s.name as store_name, u.full_name as user_name
|
||||||
|
FROM stock_transactions t
|
||||||
|
JOIN stock_items i ON t.item_id = i.id
|
||||||
|
JOIN stock_stores s ON t.store_id = s.id
|
||||||
|
LEFT JOIN users u ON t.user_id = u.id
|
||||||
|
$where
|
||||||
|
ORDER BY t.created_at DESC
|
||||||
|
LIMIT $limit OFFSET $offset
|
||||||
|
";
|
||||||
|
$stmt = db()->prepare($historyQuery);
|
||||||
|
$stmt->execute($params);
|
||||||
|
$history = $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">
|
<div class="d-flex justify-content-between flex-wrap flex-md-nowrap align-items-center pt-3 pb-2 mb-3 border-bottom">
|
||||||
@ -80,9 +111,12 @@ $items = db()->query("SELECT * FROM stock_items ORDER BY name ASC")->fetchAll();
|
|||||||
</div>
|
</div>
|
||||||
<?php endif; ?>
|
<?php endif; ?>
|
||||||
|
|
||||||
<div class="row justify-content-center">
|
<div class="row justify-content-center mb-5">
|
||||||
<div class="col-md-8">
|
<div class="col-md-8">
|
||||||
<div class="card shadow-sm border-0">
|
<div class="card shadow-sm border-0">
|
||||||
|
<div class="card-header bg-danger text-white">
|
||||||
|
<h5 class="mb-0"><i class="fas fa-minus-circle me-2"></i> تسجيل عملية صرف</h5>
|
||||||
|
</div>
|
||||||
<div class="card-body p-4">
|
<div class="card-body p-4">
|
||||||
<form method="POST">
|
<form method="POST">
|
||||||
<div class="row">
|
<div class="row">
|
||||||
@ -141,4 +175,56 @@ $items = db()->query("SELECT * FROM stock_items ORDER BY name ASC")->fetchAll();
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<!-- History Table -->
|
||||||
|
<div class="card shadow-sm border-0 mb-4">
|
||||||
|
<div class="card-header bg-white py-3">
|
||||||
|
<h5 class="mb-0 fw-bold"><i class="fas fa-history me-2 text-secondary"></i> سجل عمليات الصرف</h5>
|
||||||
|
</div>
|
||||||
|
<div class="table-responsive">
|
||||||
|
<table class="table table-hover align-middle mb-0">
|
||||||
|
<thead class="bg-light">
|
||||||
|
<tr>
|
||||||
|
<th class="ps-4">#</th>
|
||||||
|
<th>النوع</th>
|
||||||
|
<th>الصنف</th>
|
||||||
|
<th>المستودع</th>
|
||||||
|
<th>الكمية</th>
|
||||||
|
<th>بواسطة</th>
|
||||||
|
<th>التاريخ</th>
|
||||||
|
<th>المرجع</th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody>
|
||||||
|
<?php if (empty($history)): ?>
|
||||||
|
<tr>
|
||||||
|
<td colspan="8" class="text-center py-4 text-muted">لا توجد عمليات صرف سابقة.</td>
|
||||||
|
</tr>
|
||||||
|
<?php else: ?>
|
||||||
|
<?php foreach ($history as $h): ?>
|
||||||
|
<tr>
|
||||||
|
<td class="ps-4"><?= $h['id'] ?></td>
|
||||||
|
<td>
|
||||||
|
<?php if ($h['transaction_type'] == 'damage'): ?>
|
||||||
|
<span class="badge bg-dark">تالف</span>
|
||||||
|
<?php else: ?>
|
||||||
|
<span class="badge bg-danger">صرف</span>
|
||||||
|
<?php endif; ?>
|
||||||
|
</td>
|
||||||
|
<td class="fw-bold"><?= htmlspecialchars($h['item_name']) ?></td>
|
||||||
|
<td><?= htmlspecialchars($h['store_name']) ?></td>
|
||||||
|
<td class="text-danger fw-bold" dir="ltr">-<?= number_format($h['quantity'], 2) ?></td>
|
||||||
|
<td><?= htmlspecialchars($h['user_name'] ?? '-') ?></td>
|
||||||
|
<td><?= date('Y-m-d H:i', strtotime($h['created_at'])) ?></td>
|
||||||
|
<td><small class="text-muted"><?= htmlspecialchars($h['reference'] ?? '-') ?></small></td>
|
||||||
|
</tr>
|
||||||
|
<?php endforeach; ?>
|
||||||
|
<?php endif; ?>
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Pagination -->
|
||||||
|
<?php renderPagination($page, $totalFiltered, $limit); ?>
|
||||||
|
</div>
|
||||||
|
|
||||||
<?php require_once __DIR__ . '/includes/footer.php'; ?>
|
<?php require_once __DIR__ . '/includes/footer.php'; ?>
|
||||||
Loading…
x
Reference in New Issue
Block a user