👁 查看:stock_history.php
路径:/home/boguraj1/public_html/ipsms/stock_history.php
大小:14.8 KB · 修改:2026-06-15 23:58:02 · 权限:0644 · 可写
<?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 &amp; Time</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'])) ?>
                            <br>
                            <span><?= date('h:i A', strtotime($t['created_at'])) ?></span>
                        </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'; ?>