✏️ 编辑: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 & 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'; ?>
💾 保存
取消
保存为 UTF-8,换行统一为 LF