<?php
session_start();
require_once 'includes/auth.php';
requireAdmin();
require_once 'config/db.php';
date_default_timezone_set('Asia/Dhaka');
// ---- Pagination config ----
const PER_PAGE = 5;
// ---- Filter inputs ----
$filter_product = (int) ($_GET['product_id'] ?? 0);
$filter_type = in_array($_GET['type'] ?? '', ['added', 'deducted']) ? $_GET['type'] : '';
$filter_date = $_GET['date'] ?? '';
$filter_date = preg_match('/^\d{4}-\d{2}-\d{2}$/', $filter_date) ? $filter_date : '';
$page = max(1, (int) ($_GET['page'] ?? 1));
// ---- Build WHERE clause ----
$conditions = [];
$params = [];
if ($filter_product > 0) {
$conditions[] = 'st.product_id = ?';
$params[] = $filter_product;
}
if ($filter_type !== '') {
$conditions[] = 'st.type = ?';
$params[] = $filter_type;
}
if ($filter_date !== '') {
$conditions[] = 'DATE(st.created_at) = ?';
$params[] = $filter_date;
}
$where = $conditions ? 'WHERE ' . implode(' AND ', $conditions) : '';
// ---- Total record count (for pagination) ----
$count_stmt = $pdo->prepare("
SELECT COUNT(*)
FROM stock_transactions st
JOIN products p ON p.id = st.product_id
LEFT JOIN users u ON u.id = st.user_id
{$where}
");
$count_stmt->execute($params);
$total_records = (int) $count_stmt->fetchColumn();
$total_pages = max(1, (int) ceil($total_records / PER_PAGE));
// Clamp page to valid range
$page = min($page, $total_pages);
$offset = ($page - 1) * PER_PAGE;
// ---- Summary totals (all filtered records, no LIMIT) ----
$totals_stmt = $pdo->prepare("
SELECT
COALESCE(SUM(CASE WHEN st.type = 'added' THEN st.quantity ELSE 0 END), 0) AS total_added,
COALESCE(SUM(CASE WHEN st.type = 'deducted' THEN st.quantity ELSE 0 END), 0) AS total_deducted
FROM stock_transactions st
JOIN products p ON p.id = st.product_id
LEFT JOIN users u ON u.id = st.user_id
{$where}
");
$totals_stmt->execute($params);
$totals = $totals_stmt->fetch();
$total_added = (int) $totals['total_added'];
$total_deducted = (int) $totals['total_deducted'];
// ---- Fetch current page transactions ----
$stmt = $pdo->prepare("
SELECT st.id,
st.quantity,
st.type,
st.notes,
st.is_edited,
st.edited_at,
st.created_at,
p.name AS product_name,
p.unit,
COALESCE(u.full_name, u.username, 'System') AS submitted_by
FROM stock_transactions st
JOIN products p ON p.id = st.product_id
LEFT JOIN users u ON u.id = st.user_id
{$where}
ORDER BY st.created_at DESC
LIMIT " . PER_PAGE . " OFFSET {$offset}
");
$stmt->execute($params);
$transactions = $stmt->fetchAll();
// ---- Products list for filter dropdown ----
$products_list = $pdo->query("SELECT id, name FROM products ORDER BY name ASC")->fetchAll();
// ---- Helper: build pagination URL preserving active filters ----
function pageUrl(int $p): string
{
global $filter_product, $filter_type, $filter_date;
$q = array_filter([
'product_id' => $filter_product ?: null,
'type' => $filter_type ?: null,
'date' => $filter_date ?: null,
'page' => $p,
]);
return 'stock_history.php?' . http_build_query($q);
}
include 'includes/header.php';
?>
<div class="row mb-3">
<div class="col">
<h4 class="page-title"><i class="bi bi-clock-history me-2 text-primary"></i>Stock History</h4>
<nav aria-label="breadcrumb">
<ol class="breadcrumb">
<li class="breadcrumb-item"><a href="dashboard.php">Dashboard</a></li>
<li class="breadcrumb-item active">Stock History</li>
</ol>
</nav>
</div>
</div>
<!-- ===== Filter Card ===== -->
<div class="card mb-4">
<div class="card-header bg-light fw-semibold">
<i class="bi bi-funnel me-1"></i> Filter Transactions
</div>
<div class="card-body">
<!-- Note: no hidden "page" field — filter always resets to page 1 -->
<form method="GET" action="" class="row g-3 align-items-end">
<div class="col-sm-6 col-md-3">
<label class="form-label fw-semibold small">Product</label>
<select class="form-select form-select-sm" name="product_id">
<option value="">All Products</option>
<?php foreach ($products_list as $p): ?>
<option value="<?= $p['id'] ?>"
<?= $filter_product === (int) $p['id'] ? 'selected' : '' ?>>
<?= htmlspecialchars($p['name']) ?>
</option>
<?php endforeach; ?>
</select>
</div>
<div class="col-sm-6 col-md-2">
<label class="form-label fw-semibold small">Type</label>
<select class="form-select form-select-sm" name="type">
<option value="">All Types</option>
<option value="added" <?= $filter_type === 'added' ? 'selected' : '' ?>>Added</option>
<option value="deducted" <?= $filter_type === 'deducted' ? 'selected' : '' ?>>Used / Deducted</option>
</select>
</div>
<div class="col-sm-6 col-md-3">
<label class="form-label fw-semibold small">Date</label>
<input type="date" class="form-control form-control-sm"
name="date" value="<?= htmlspecialchars($filter_date) ?>">
</div>
<div class="col-sm-6 col-md-4 d-flex gap-2">
<button type="submit" class="btn btn-primary btn-sm">
<i class="bi bi-search me-1"></i>Filter
</button>
<a href="stock_history.php" class="btn btn-outline-secondary btn-sm">
<i class="bi bi-x-circle me-1"></i>Reset
</a>
</div>
</form>
</div>
</div>
<!-- ===== Summary Totals (always reflects ALL filtered records) ===== -->
<?php if ($total_records > 0): ?>
<div class="row g-3 mb-4">
<div class="col-sm-4">
<div class="card border-0 bg-white shadow-sm text-center py-3">
<div class="text-muted small">Total Records</div>
<div class="fw-bold fs-4"><?= number_format($total_records) ?></div>
</div>
</div>
<div class="col-sm-4">
<div class="card border-0 bg-white shadow-sm text-center py-3">
<div class="text-muted small">Total Added</div>
<div class="fw-bold fs-4 text-success">+<?= number_format($total_added) ?></div>
</div>
</div>
<div class="col-sm-4">
<div class="card border-0 bg-white shadow-sm text-center py-3">
<div class="text-muted small">Total Deducted</div>
<div class="fw-bold fs-4 text-danger">-<?= number_format($total_deducted) ?></div>
</div>
</div>
</div>
<?php endif; ?>
<!-- ===== Transaction Table ===== -->
<div class="card">
<div class="card-header bg-secondary text-white d-flex justify-content-between align-items-center">
<span><i class="bi bi-table me-1"></i>Transaction Log</span>
<span class="badge bg-light text-dark">
<?php
$from = $total_records > 0 ? $offset + 1 : 0;
$to = min($offset + PER_PAGE, $total_records);
echo "{$from}–{$to} of {$total_records} record(s)";
?>
</span>
</div>
<div class="card-body p-0">
<div class="table-responsive">
<table class="table table-hover mb-0 align-middle">
<thead>
<tr>
<th style="width:50px;">#</th>
<th>Product</th>
<th class="text-center" style="width:100px;">Quantity</th>
<th class="text-center" style="width:120px;">Type</th>
<th>Notes</th>
<th style="width:130px;">Submitted By</th>
<th style="width:160px;">Date</th>
</tr>
</thead>
<tbody>
<?php if (empty($transactions)): ?>
<tr>
<td colspan="7" class="text-center py-5 text-muted">
<i class="bi bi-inbox fs-1 d-block mb-2"></i>
No transactions found for the selected filters.
</td>
</tr>
<?php else: ?>
<?php foreach ($transactions as $i => $t): ?>
<tr>
<!-- Absolute serial number across pages -->
<td class="text-muted small"><?= $offset + $i + 1 ?></td>
<td>
<span class="fw-semibold"><?= htmlspecialchars($t['product_name']) ?></span>
<span class="text-muted small ms-1">(<?= htmlspecialchars($t['unit']) ?>)</span>
</td>
<td class="text-center fw-bold <?= $t['type'] === 'added' ? 'text-success' : 'text-danger' ?>">
<?= $t['type'] === 'added' ? '+' : '-' ?><?= number_format($t['quantity']) ?>
</td>
<td class="text-center">
<?php if ($t['type'] === 'added'): ?>
<span class="badge bg-success">
<i class="bi bi-arrow-up me-1"></i>Added
</span>
<?php else: ?>
<span class="badge bg-danger">
<i class="bi bi-arrow-down me-1"></i>Used
</span>
<?php endif; ?>
</td>
<td class="text-muted small"><?= htmlspecialchars($t['notes'] ?: '—') ?></td>
<td class="small">
<?= htmlspecialchars($t['submitted_by']) ?>
<?php if ($t['is_edited']): ?>
<br>
<span class="badge bg-secondary" style="font-size:0.68em;"
title="Edited on <?= date('d M Y h:i A', strtotime($t['edited_at'])) ?>">
<i class="bi bi-pencil"></i> Edited
</span>
<?php endif; ?>
</td>
<td class="small text-muted">
<?= date('d M Y', strtotime($t['created_at'])) ?>
</td>
</tr>
<?php endforeach; ?>
<?php endif; ?>
</tbody>
</table>
</div>
</div>
<!-- ===== Footer: info + pagination ===== -->
<div class="card-footer d-flex justify-content-between align-items-center flex-wrap gap-2">
<!-- Left: record range info -->
<div class="text-muted small">
<?php if ($total_records > 0): ?>
Showing <strong><?= $from ?></strong>–<strong><?= $to ?></strong>
of <strong><?= $total_records ?></strong> record(s)
(Page <?= $page ?> of <?= $total_pages ?>)
<?php if ($filter_product || $filter_type || $filter_date): ?>
— <a href="stock_history.php">Clear filters</a>
<?php endif; ?>
<?php else: ?>
No records found.
<?php endif; ?>
</div>
<!-- Right: pagination controls -->
<?php if ($total_pages > 1): ?>
<nav aria-label="Stock history pagination">
<ul class="pagination pagination-sm mb-0">
<!-- First page -->
<li class="page-item <?= $page <= 1 ? 'disabled' : '' ?>">
<a class="page-link" href="<?= htmlspecialchars(pageUrl(1)) ?>" title="First">
<i class="bi bi-chevron-double-left"></i>
</a>
</li>
<!-- Previous -->
<li class="page-item <?= $page <= 1 ? 'disabled' : '' ?>">
<a class="page-link" href="<?= htmlspecialchars(pageUrl($page - 1)) ?>" title="Previous">
<i class="bi bi-chevron-left"></i>
</a>
</li>
<?php
// Show a sliding window of page numbers around the current page
$window = 2; // pages on each side of current
$p_start = max(1, $page - $window);
$p_end = min($total_pages, $page + $window);
// Always show first page
if ($p_start > 1): ?>
<li class="page-item">
<a class="page-link" href="<?= htmlspecialchars(pageUrl(1)) ?>">1</a>
</li>
<?php if ($p_start > 2): ?>
<li class="page-item disabled"><span class="page-link">…</span></li>
<?php endif;
endif; ?>
<?php for ($p = $p_start; $p <= $p_end; $p++): ?>
<li class="page-item <?= $p === $page ? 'active' : '' ?>">
<a class="page-link <?= $p === $page ? 'fw-bold' : '' ?>"
href="<?= htmlspecialchars(pageUrl($p)) ?>"><?= $p ?></a>
</li>
<?php endfor; ?>
<?php
// Always show last page
if ($p_end < $total_pages):
if ($p_end < $total_pages - 1): ?>
<li class="page-item disabled"><span class="page-link">…</span></li>
<?php endif; ?>
<li class="page-item">
<a class="page-link" href="<?= htmlspecialchars(pageUrl($total_pages)) ?>">
<?= $total_pages ?>
</a>
</li>
<?php endif; ?>
<!-- Next -->
<li class="page-item <?= $page >= $total_pages ? 'disabled' : '' ?>">
<a class="page-link" href="<?= htmlspecialchars(pageUrl($page + 1)) ?>" title="Next">
<i class="bi bi-chevron-right"></i>
</a>
</li>
<!-- Last page -->
<li class="page-item <?= $page >= $total_pages ? 'disabled' : '' ?>">
<a class="page-link" href="<?= htmlspecialchars(pageUrl($total_pages)) ?>" title="Last">
<i class="bi bi-chevron-double-right"></i>
</a>
</li>
</ul>
</nav>
<?php endif; ?>
</div>
</div>
<?php include 'includes/footer.php'; ?>