267 lines
10 KiB
PHP
267 lines
10 KiB
PHP
<?php
|
|
session_start();
|
|
require 'db/config.php';
|
|
$db = db();
|
|
|
|
if (!isset($_SESSION['user_id']) || $_SESSION['role'] !== 'admin') {
|
|
header('Location: index.php');
|
|
exit;
|
|
}
|
|
|
|
$userId = $_SESSION['user_id'];
|
|
|
|
// Handle Inventory Update
|
|
if ($_SERVER['REQUEST_METHOD'] === 'POST' && isset($_POST['update_stock'])) {
|
|
$itemId = $_POST['item_id'];
|
|
$qty = $_POST['quantity'];
|
|
$type = $_POST['type']; // in or out
|
|
|
|
try {
|
|
$db->beginTransaction();
|
|
|
|
// Record transaction
|
|
$stmt = $db->prepare("INSERT INTO inventory_transactions (inventory_id, type, quantity, user_id) VALUES (?, ?, ?, ?)");
|
|
$stmt->execute([$itemId, $type, $qty, $userId]);
|
|
|
|
// Update stock
|
|
$mod = ($type === 'in' ? '+' : '-');
|
|
$db->exec("UPDATE inventory SET stock_level = stock_level $mod $qty WHERE id = $itemId");
|
|
|
|
$db->commit();
|
|
header("Location: inventory.php?success=1");
|
|
exit;
|
|
} catch (Exception $e) {
|
|
$db->rollBack();
|
|
$error = "Error: " . $e->getMessage();
|
|
}
|
|
}
|
|
|
|
// Handle New Item
|
|
if ($_SERVER['REQUEST_METHOD'] === 'POST' && isset($_POST['new_item'])) {
|
|
$name = $_POST['name'];
|
|
$cat = $_POST['category'];
|
|
$stock = $_POST['stock_level'];
|
|
$reorder = $_POST['reorder_level'];
|
|
$unit = $_POST['unit'];
|
|
|
|
$stmt = $db->prepare("INSERT INTO inventory (name, category, stock_level, reorder_level, unit) VALUES (?, ?, ?, ?, ?)");
|
|
$stmt->execute([$name, $cat, $stock, $reorder, $unit]);
|
|
header("Location: inventory.php?item_added=1");
|
|
exit;
|
|
}
|
|
|
|
$inventory = $db->query("SELECT * FROM inventory ORDER BY name ASC")->fetchAll();
|
|
$lowStock = array_filter($inventory, fn($i) => $i['stock_level'] <= $i['reorder_level']);
|
|
|
|
?>
|
|
<!doctype html>
|
|
<html lang="en">
|
|
<head>
|
|
<meta charset="utf-8" />
|
|
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
|
<title>M-TRACK | Inventory</title>
|
|
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/css/bootstrap.min.css" rel="stylesheet">
|
|
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap-icons@1.10.0/font/bootstrap-icons.css">
|
|
<link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700&display=swap" rel="stylesheet">
|
|
<style>
|
|
:root {
|
|
--sidebar-width: 240px;
|
|
--bg: #f8fafc;
|
|
--primary: #1e293b;
|
|
--accent: #3b82f6;
|
|
--text: #334155;
|
|
--border: #e2e8f0;
|
|
--danger: #ef4444;
|
|
}
|
|
body {
|
|
font-family: 'Inter', system-ui, -apple-system, sans-serif;
|
|
background-color: var(--bg);
|
|
color: var(--text);
|
|
}
|
|
.sidebar {
|
|
width: var(--sidebar-width);
|
|
position: fixed;
|
|
top: 0;
|
|
bottom: 0;
|
|
left: 0;
|
|
background-color: var(--primary);
|
|
color: white;
|
|
z-index: 1000;
|
|
padding: 1.5rem 1rem;
|
|
}
|
|
.main-content {
|
|
margin-left: var(--sidebar-width);
|
|
padding: 2.5rem;
|
|
}
|
|
.card {
|
|
background: white;
|
|
border: 1px solid var(--border);
|
|
border-radius: 4px;
|
|
box-shadow: 0 1px 3px 0 rgb(0 0 0 / 0.1);
|
|
margin-bottom: 1.5rem;
|
|
}
|
|
.alert-low {
|
|
border-left: 4px solid var(--danger);
|
|
}
|
|
</style>
|
|
</head>
|
|
<body>
|
|
|
|
<div class="sidebar">
|
|
<h2>M-TRACK</h2>
|
|
<nav class="nav nav-pills flex-column">
|
|
<a class="nav-link" href="dashboard.php"><i class="bi bi-grid-fill me-2"></i> Dashboard</a>
|
|
<a class="nav-link" href="jobs.php"><i class="bi bi-briefcase me-2"></i> Jobs</a>
|
|
<a class="nav-link active" href="inventory.php"><i class="bi bi-boxes me-2"></i> Inventory</a>
|
|
<a class="nav-link" href="users.php"><i class="bi bi-people me-2"></i> Users</a>
|
|
<a class="nav-link" href="time_study.php"><i class="bi bi-clock-history me-2"></i> Time Study</a>
|
|
<a class="nav-link" href="scan.php"><i class="bi bi-upc-scan me-2"></i> Scan</a>
|
|
<hr class="my-4 border-secondary opacity-25">
|
|
<a class="nav-link text-danger" href="logout.php"><i class="bi bi-box-arrow-right me-2"></i> Logout</a>
|
|
</nav>
|
|
</div>
|
|
|
|
<div class="main-content">
|
|
<div class="d-flex justify-content-between align-items-center mb-4">
|
|
<h1>Inventory Management</h1>
|
|
<button class="btn btn-primary" data-bs-toggle="modal" data-bs-target="#newItemModal">
|
|
<i class="bi bi-plus-lg me-1"></i> New Item
|
|
</button>
|
|
</div>
|
|
|
|
<?php if (!empty($lowStock)): ?>
|
|
<div class="alert alert-danger d-flex align-items-center mb-4">
|
|
<i class="bi bi-exclamation-triangle-fill me-2"></i>
|
|
<div><strong>Low Stock Warning:</strong> <?= count($lowStock) ?> items are below reorder level.</div>
|
|
</div>
|
|
<?php endif; ?>
|
|
|
|
<div class="card">
|
|
<div class="card-body p-0">
|
|
<div class="table-responsive">
|
|
<table class="table table-hover mb-0 align-middle">
|
|
<thead class="table-light">
|
|
<tr>
|
|
<th>Item Name</th>
|
|
<th>Category</th>
|
|
<th>Current Stock</th>
|
|
<th>Reorder Level</th>
|
|
<th class="text-end">Actions</th>
|
|
</tr>
|
|
</thead>
|
|
<tbody>
|
|
<?php if (empty($inventory)): ?>
|
|
<tr><td colspan="5" class="p-5 text-center text-muted">No inventory items found.</td></tr>
|
|
<?php else: ?>
|
|
<?php foreach ($inventory as $item): ?>
|
|
<tr class="<?= $item['stock_level'] <= $item['reorder_level'] ? 'table-danger alert-low' : '' ?>">
|
|
<td>
|
|
<div class="fw-bold"><?= htmlspecialchars($item['name']) ?></div>
|
|
<div class="small text-muted"><?= $item['unit'] ?></div>
|
|
</td>
|
|
<td><span class="badge bg-light text-dark border"><?= strtoupper($item['category']) ?></span></td>
|
|
<td>
|
|
<span class="fw-bold <?= $item['stock_level'] <= $item['reorder_level'] ? 'text-danger' : '' ?>">
|
|
<?= $item['stock_level'] ?>
|
|
</span>
|
|
</td>
|
|
<td><?= $item['reorder_level'] ?></td>
|
|
<td class="text-end">
|
|
<button class="btn btn-sm btn-outline-primary" onclick="setUpdateItem(<?= $item['id'] ?>, '<?= htmlspecialchars($item['name']) ?>')" data-bs-toggle="modal" data-bs-target="#updateModal">
|
|
Update Stock
|
|
</button>
|
|
</td>
|
|
</tr>
|
|
<?php endforeach; ?>
|
|
<?php endif; ?>
|
|
</tbody>
|
|
</table>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- New Item Modal -->
|
|
<div class="modal fade" id="newItemModal" tabindex="-1">
|
|
<div class="modal-dialog">
|
|
<form method="POST" class="modal-content">
|
|
<div class="modal-header">
|
|
<h5 class="modal-title">Add Inventory Item</h5>
|
|
<button type="button" class="btn-close" data-bs-dismiss="modal"></button>
|
|
</div>
|
|
<div class="modal-body">
|
|
<div class="mb-3">
|
|
<label class="form-label">Item Name</label>
|
|
<input type="text" name="name" class="form-control" required>
|
|
</div>
|
|
<div class="mb-3">
|
|
<label class="form-label">Category</label>
|
|
<select name="category" class="form-select">
|
|
<option value="material">Raw Material</option>
|
|
<option value="consumable">Consumable</option>
|
|
<option value="hardware">Hardware</option>
|
|
</select>
|
|
</div>
|
|
<div class="row">
|
|
<div class="col-md-6 mb-3">
|
|
<label class="form-label">Initial Stock</label>
|
|
<input type="number" step="0.01" name="stock_level" class="form-control" value="0">
|
|
</div>
|
|
<div class="col-md-6 mb-3">
|
|
<label class="form-label">Reorder Level</label>
|
|
<input type="number" step="0.01" name="reorder_level" class="form-control" value="0">
|
|
</div>
|
|
</div>
|
|
<div class="mb-3">
|
|
<label class="form-label">Unit</label>
|
|
<input type="text" name="unit" class="form-control" placeholder="pcs, kg, meters, etc.">
|
|
</div>
|
|
</div>
|
|
<div class="modal-footer">
|
|
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal">Cancel</button>
|
|
<button type="submit" name="new_item" class="btn btn-primary">Add Item</button>
|
|
</div>
|
|
</form>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Update Stock Modal -->
|
|
<div class="modal fade" id="updateModal" tabindex="-1">
|
|
<div class="modal-dialog">
|
|
<form method="POST" class="modal-content">
|
|
<input type="hidden" name="item_id" id="update_item_id">
|
|
<div class="modal-header">
|
|
<h5 class="modal-title">Update Stock: <span id="update_item_name"></span></h5>
|
|
<button type="button" class="btn-close" data-bs-dismiss="modal"></button>
|
|
</div>
|
|
<div class="modal-body">
|
|
<div class="mb-3">
|
|
<label class="form-label">Transaction Type</label>
|
|
<select name="type" class="form-select">
|
|
<option value="in">Stock In (Receive)</option>
|
|
<option value="out">Stock Out (Consume)</option>
|
|
</select>
|
|
</div>
|
|
<div class="mb-3">
|
|
<label class="form-label">Quantity</label>
|
|
<input type="number" step="0.01" name="quantity" class="form-control" required>
|
|
</div>
|
|
</div>
|
|
<div class="modal-footer">
|
|
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal">Cancel</button>
|
|
<button type="submit" name="update_stock" class="btn btn-primary">Save Changes</button>
|
|
</div>
|
|
</form>
|
|
</div>
|
|
</div>
|
|
|
|
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/js/bootstrap.bundle.min.js"></script>
|
|
<script>
|
|
function setUpdateItem(id, name) {
|
|
document.getElementById('update_item_id').value = id;
|
|
document.getElementById('update_item_name').innerText = name;
|
|
}
|
|
</script>
|
|
</body>
|
|
</html>
|