Files
2026-05-16 23:00:37 -05:00

338 lines
15 KiB
PHP

<?php
ob_start();
/**
* Tom's Java Jive - Admin Inventory Management
*/
$pageTitle = 'Inventory';
require_once __DIR__ . '/includes/header.php';
// Handle actions
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
$action = $_POST['action'] ?? '';
if ($action === 'adjust' && !empty($_POST['product_id'])) {
$adjustment = intval($_POST['adjustment'] ?? 0);
$reason = trim($_POST['reason'] ?? '');
if ($adjustment != 0) {
$product = db()->fetch("SELECT name, stock FROM products WHERE product_id = :id", ['id' => $_POST['product_id']]);
$newStock = max(0, ($product['stock'] ?? 0) + $adjustment);
db()->update('products', ['stock' => $newStock], 'product_id = :id', ['id' => $_POST['product_id']]);
setFlash('success', $product['name'] . ' stock adjusted by ' . ($adjustment > 0 ? '+' : '') . $adjustment . '. New stock: ' . $newStock);
}
header('Location: /admin/inventory.php');
exit;
}
if ($action === 'update_threshold' && !empty($_POST['product_id'])) {
$threshold = intval($_POST['low_stock_threshold'] ?? 10);
db()->update('products', ['low_stock_threshold' => $threshold], 'product_id = :id', ['id' => $_POST['product_id']]);
setFlash('success', 'Low stock threshold updated');
header('Location: /admin/inventory.php');
exit;
}
if ($action === 'bulk_adjust') {
$adjustments = $_POST['adjustments'] ?? [];
$count = 0;
foreach ($adjustments as $productId => $adj) {
$adj = intval($adj);
if ($adj != 0) {
db()->query(
"UPDATE products SET stock = GREATEST(0, stock + :adj) WHERE product_id = :id",
['adj' => $adj, 'id' => $productId]
);
$count++;
}
}
if ($count > 0) {
setFlash('success', "Adjusted stock for $count products");
}
header('Location: /admin/inventory.php');
exit;
}
}
// Filters
$filter = $_GET['filter'] ?? '';
$search = $_GET['search'] ?? '';
$category = $_GET['category'] ?? '';
$where = ['1=1'];
$params = [];
if ($search) {
$where[] = '(name LIKE :search OR sku LIKE :search OR barcode LIKE :search)';
$params['search'] = '%' . $search . '%';
}
if ($category) {
$where[] = 'category = :category';
$params['category'] = $category;
}
if ($filter === 'low') {
$where[] = 'stock <= low_stock_threshold AND stock > 0';
} elseif ($filter === 'out') {
$where[] = 'stock <= 0';
} elseif ($filter === 'in') {
$where[] = 'stock > low_stock_threshold';
}
$whereClause = implode(' AND ', $where);
$products = db()->fetchAll(
"SELECT product_id, name, sku, barcode, category, stock, low_stock_threshold, price, is_active
FROM products
WHERE {$whereClause}
ORDER BY stock ASC, name ASC",
$params
);
// Get categories for filter
$categories = db()->fetchAll(
"SELECT DISTINCT category FROM products WHERE category IS NOT NULL AND category != '' ORDER BY category"
);
// Stats
$totalProducts = db()->count('products', 'is_active = 1');
$lowStockCount = db()->count('products', 'stock <= low_stock_threshold AND stock > 0 AND is_active = 1');
$outOfStockCount = db()->count('products', 'stock <= 0 AND is_active = 1');
$totalStock = db()->fetch("SELECT SUM(stock) as total FROM products WHERE is_active = 1")['total'] ?? 0;
$inventoryValue = db()->fetch("SELECT SUM(stock * price) as total FROM products WHERE is_active = 1")['total'] ?? 0;
?>
<div class="page-header">
<h1 class="page-title">Inventory Management</h1>
<button class="btn btn-primary" onclick="document.getElementById('bulkForm').style.display = document.getElementById('bulkForm').style.display === 'none' ? 'block' : 'none'">
<i class="fas fa-boxes"></i> Bulk Adjust
</button>
</div>
<?php if (hasFlash('success')): ?>
<div class="alert alert-success"><i class="fas fa-check-circle"></i> <?= getFlash('success') ?></div>
<?php endif; ?>
<!-- Stats -->
<div class="stats-grid">
<div class="stat-card">
<div class="stat-card-icon primary"><i class="fas fa-boxes"></i></div>
<div>
<div class="stat-card-value"><?= number_format($totalStock) ?></div>
<div class="stat-card-label">Total Units</div>
</div>
</div>
<div class="stat-card">
<div class="stat-card-icon success"><i class="fas fa-dollar-sign"></i></div>
<div>
<div class="stat-card-value"><?= formatCurrency($inventoryValue) ?></div>
<div class="stat-card-label">Inventory Value</div>
</div>
</div>
<div class="stat-card">
<div class="stat-card-icon warning"><i class="fas fa-exclamation-triangle"></i></div>
<div>
<div class="stat-card-value"><?= $lowStockCount ?></div>
<div class="stat-card-label">Low Stock</div>
</div>
</div>
<div class="stat-card">
<div class="stat-card-icon error"><i class="fas fa-times-circle"></i></div>
<div>
<div class="stat-card-value"><?= $outOfStockCount ?></div>
<div class="stat-card-label">Out of Stock</div>
</div>
</div>
</div>
<!-- Filters -->
<div class="admin-card">
<div class="admin-card-body">
<form method="GET" style="display: flex; gap: 1rem; flex-wrap: wrap; align-items: end;">
<div class="form-group mb-0" style="flex: 1; min-width: 200px;">
<label class="form-label">Search</label>
<input type="text" name="search" class="form-input" placeholder="Name, SKU, barcode..." value="<?= htmlspecialchars($search) ?>">
</div>
<div class="form-group mb-0">
<label class="form-label">Category</label>
<select name="category" class="form-select">
<option value="">All Categories</option>
<?php foreach ($categories as $cat): ?>
<option value="<?= htmlspecialchars($cat['category']) ?>" <?= $category === $cat['category'] ? 'selected' : '' ?>>
<?= htmlspecialchars(ucfirst($cat['category'])) ?>
</option>
<?php endforeach; ?>
</select>
</div>
<div class="form-group mb-0">
<label class="form-label">Stock Status</label>
<select name="filter" class="form-select">
<option value="">All</option>
<option value="in" <?= $filter === 'in' ? 'selected' : '' ?>>In Stock</option>
<option value="low" <?= $filter === 'low' ? 'selected' : '' ?>>Low Stock</option>
<option value="out" <?= $filter === 'out' ? 'selected' : '' ?>>Out of Stock</option>
</select>
</div>
<button type="submit" class="btn btn-secondary"><i class="fas fa-filter"></i> Filter</button>
<?php if ($filter || $search || $category): ?>
<a href="/admin/inventory.php" class="btn btn-secondary">Clear</a>
<?php endif; ?>
</form>
</div>
</div>
<!-- Bulk Adjustment Form (hidden by default) -->
<form method="POST" id="bulkForm" style="display: none;">
<input type="hidden" name="action" value="bulk_adjust">
<div class="admin-card">
<div class="admin-card-header">
<h3 class="admin-card-title">Bulk Stock Adjustment</h3>
</div>
<div class="admin-card-body">
<p class="text-muted mb-1">Enter adjustment values for each product (positive to add, negative to subtract). Leave blank or 0 to skip.</p>
<button type="submit" class="btn btn-primary mb-1">Apply All Adjustments</button>
</div>
</div>
</form>
<!-- Inventory Table -->
<div class="admin-card">
<div class="admin-card-header">
<span><?= count($products) ?> products</span>
</div>
<div class="admin-card-body" style="padding: 0;">
<table class="admin-table">
<thead>
<tr>
<th>Product</th>
<th>SKU / Barcode</th>
<th>Category</th>
<th>Stock</th>
<th>Threshold</th>
<th>Status</th>
<th>Actions</th>
</tr>
</thead>
<tbody>
<?php if (empty($products)): ?>
<tr><td colspan="7" class="text-muted" style="text-align: center; padding: 2rem;">No products found</td></tr>
<?php else: ?>
<?php foreach ($products as $product): ?>
<tr>
<td>
<strong><?= htmlspecialchars($product['name']) ?></strong>
<?php if (!$product['is_active']): ?>
<span class="badge badge-error">Inactive</span>
<?php endif; ?>
</td>
<td>
<?php if ($product['sku']): ?>
<code><?= htmlspecialchars($product['sku']) ?></code>
<?php endif; ?>
<?php if ($product['barcode']): ?>
<br><small class="text-muted"><?= htmlspecialchars($product['barcode']) ?></small>
<?php endif; ?>
<?php if (!$product['sku'] && !$product['barcode']): ?>
<span class="text-muted">-</span>
<?php endif; ?>
</td>
<td><?= htmlspecialchars($product['category'] ?? '-') ?></td>
<td>
<span style="font-size: 1.5rem; font-weight: 700; color: <?= $product['stock'] <= 0 ? 'var(--admin-error)' : ($product['stock'] <= $product['low_stock_threshold'] ? 'var(--admin-warning)' : 'var(--admin-success)') ?>;">
<?= $product['stock'] ?>
</span>
<input type="number" name="adjustments[<?= $product['product_id'] ?>]" form="bulkForm"
class="form-input" style="width: 80px; margin-left: 0.5rem; display: none;" placeholder="+/-">
</td>
<td>
<span class="text-muted"><?= $product['low_stock_threshold'] ?></span>
</td>
<td>
<?php if ($product['stock'] <= 0): ?>
<span class="badge badge-error">Out of Stock</span>
<?php elseif ($product['stock'] <= $product['low_stock_threshold']): ?>
<span class="badge badge-warning">Low Stock</span>
<?php else: ?>
<span class="badge badge-success">In Stock</span>
<?php endif; ?>
</td>
<td>
<button class="btn btn-sm btn-secondary" onclick="openAdjustModal('<?= $product['product_id'] ?>', '<?= htmlspecialchars(addslashes($product['name'])) ?>', <?= $product['stock'] ?>, <?= $product['low_stock_threshold'] ?>)" title="Adjust Stock">
<i class="fas fa-edit"></i>
</button>
<a href="/admin/product-edit.php?id=<?= $product['product_id'] ?>" class="btn btn-sm btn-secondary" title="Edit Product">
<i class="fas fa-cog"></i>
</a>
</td>
</tr>
<?php endforeach; ?>
<?php endif; ?>
</tbody>
</table>
</div>
</div>
<!-- Adjust Modal -->
<div class="modal-overlay" id="adjustModal">
<div class="modal">
<div class="modal-header">
<h3 class="modal-title">Adjust Stock</h3>
<button type="button" class="modal-close" onclick="Modal.close('adjustModal')">&times;</button>
</div>
<form method="POST">
<div class="modal-body">
<input type="hidden" name="action" value="adjust">
<input type="hidden" name="product_id" id="adjustProductId">
<p><strong id="adjustProductName"></strong></p>
<p>Current Stock: <strong id="adjustCurrentStock"></strong></p>
<div class="form-group">
<label class="form-label">Stock Adjustment</label>
<input type="number" name="adjustment" id="adjustmentInput" class="form-input" required placeholder="e.g., 10 or -5">
<small class="text-muted">Positive to add, negative to subtract</small>
</div>
<div class="form-group">
<label class="form-label">Reason (optional)</label>
<input type="text" name="reason" class="form-input" placeholder="e.g., Received shipment, Damaged goods">
</div>
<hr>
<div class="form-group mb-0">
<label class="form-label">Low Stock Threshold</label>
<input type="number" name="low_stock_threshold" id="adjustThreshold" class="form-input" min="0">
<small class="text-muted">Alert when stock falls to this level</small>
</div>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-secondary" onclick="Modal.close('adjustModal')">Cancel</button>
<button type="submit" class="btn btn-primary">Save Changes</button>
</div>
</form>
</div>
</div>
<script>
function openAdjustModal(productId, name, stock, threshold) {
document.getElementById('adjustProductId').value = productId;
document.getElementById('adjustProductName').textContent = name;
document.getElementById('adjustCurrentStock').textContent = stock;
document.getElementById('adjustThreshold').value = threshold;
document.getElementById('adjustmentInput').value = '';
Modal.open('adjustModal');
}
// Toggle bulk adjustment inputs
document.getElementById('bulkForm').addEventListener('toggle', function() {
document.querySelectorAll('input[name^="adjustments"]').forEach(input => {
input.style.display = this.style.display === 'none' ? 'none' : 'inline-block';
});
});
</script>
<?php require_once __DIR__ . '/includes/footer.php'; ?>