zibo-dashboard/public/userarea/packaging_items.php

689 lines
28 KiB
PHP
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<?php include('include/headscript.php'); ?>
<!doctype html>
<html lang="it">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<link rel="icon" href="assets/images/favicon-32x32.png" type="image/png" />
<?php include('cssinclude.php'); ?>
<title>Imballaggi - Anagrafica & Stock - <?= htmlspecialchars($titlewebsite, ENT_QUOTES, 'UTF-8'); ?></title>
<!-- jQuery + Bootstrap -->
<script src="https://code.jquery.com/jquery-3.6.0.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/js/bootstrap.bundle.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/sweetalert2@11"></script>
<!-- DataTables -->
<link rel="stylesheet" href="https://cdn.datatables.net/1.13.6/css/dataTables.bootstrap5.min.css">
<script src="https://cdn.datatables.net/1.13.6/js/jquery.dataTables.min.js"></script>
<script src="https://cdn.datatables.net/1.13.6/js/dataTables.bootstrap5.min.js"></script>
<!-- Select2 -->
<link href="https://cdn.jsdelivr.net/npm/select2@4.1.0-rc.0/dist/css/select2.min.css" rel="stylesheet" />
<link href="https://cdn.jsdelivr.net/npm/select2-bootstrap-5-theme@1.3.0/dist/select2-bootstrap-5-theme.min.css" rel="stylesheet" />
<script src="https://cdn.jsdelivr.net/npm/select2@4.1.0-rc.0/dist/js/select2.full.min.js"></script>
<style>
body {
font-size: 1.05rem;
background: #f8fafc;
}
.card {
border-radius: 16px;
box-shadow: 0 4px 12px rgba(0, 0, 0, .08);
}
.table thead {
background: #cfe3ff;
color: #1f2d3d;
}
.modal-content {
border-radius: 16px;
}
.btn-add {
background-color: #0d6efd;
color: #fff;
border-radius: 8px;
padding: 10px 20px;
font-weight: 500;
}
.btn-add:hover {
background-color: #0b5ed7;
}
#tabPackaging {
table-layout: fixed;
width: 100% !important;
}
#tabPackaging th,
#tabPackaging td {
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
vertical-align: middle;
}
/* Column widths */
#tabPackaging th:nth-child(1),
#tabPackaging td:nth-child(1) {
width: 60px;
max-width: 60px;
}
#tabPackaging th:nth-child(2),
#tabPackaging td:nth-child(2) {
width: 170px;
max-width: 170px;
}
#tabPackaging th:nth-child(4),
#tabPackaging td:nth-child(4) {
width: 140px;
max-width: 140px;
}
#tabPackaging th:nth-child(5),
#tabPackaging td:nth-child(5) {
width: 110px;
max-width: 110px;
}
#tabPackaging th:nth-child(6),
#tabPackaging td:nth-child(6) {
width: 120px;
max-width: 120px;
}
#tabPackaging th:nth-child(7),
#tabPackaging td:nth-child(7) {
width: 420px;
max-width: 420px;
}
.back-dashboard {
background-color: #cfe3ff !important;
color: #1f2d3d !important;
border: 1px solid #bcd4f4 !important;
border-radius: 10px;
font-weight: 600;
font-size: 1rem;
padding: 10px 18px;
box-shadow: 0 3px 8px rgba(0, 0, 0, .1);
}
.back-dashboard:hover {
background-color: #b9d3ff !important;
transform: translateY(-2px);
}
.qty-badge {
font-weight: 700;
}
tr.inactive-item {
opacity: 0.65;
}
</style>
</head>
<body>
<div class="wrapper toggled">
<?php include('include/navbar.php'); ?>
<?php include('include/topbar.php'); ?>
<div class="page-wrapper">
<div class="page-content">
<div class="card p-3">
<div class="card-header d-flex justify-content-between align-items-center">
<h5 class="mb-0">Imballaggi - Anagrafica & Stock</h5>
<div class="d-flex gap-2">
<button class="btn back-dashboard" onclick="location.href='warehouse_dashboard.php'">↩️ Torna a Magazzino</button>
<button class="btn btn-add" data-bs-toggle="modal" data-bs-target="#addItemModal"> Nuovo Imballo</button>
</div>
</div>
<div class="card-body">
<div class="d-flex justify-content-between align-items-center mb-3">
<div class="d-flex align-items-center gap-3">
<h6 class="fw-semibold mb-0">Elenco</h6>
<select id="filterActive" class="form-select form-select-sm" style="width:220px;">
<option value="all">Tutti</option>
<option value="1" selected>Solo Attivi</option>
<option value="0">Solo Inattivi</option>
</select>
<select id="filterCategory" class="form-select form-select-sm" style="width:240px;">
<option value="all">Tutte le categorie</option>
<option value="PACKAGING_TYPE">Tipo Confezione</option>
<option value="BOX">Scatole</option>
<option value="PALLET">Pallet</option>
<option value="OTHER">Altro</option>
</select>
</div>
</div>
<?php
$db = DBHandlerSelect::getInstance();
$pdo = $db->getConnection();
$active = $_GET['active'] ?? '1';
if (!in_array($active, ['all', '0', '1'], true)) $active = '1';
$cat = $_GET['cat'] ?? 'all';
$allowedCats = ['all', 'PACKAGING_TYPE', 'BOX', 'PALLET', 'OTHER'];
if (!in_array($cat, $allowedCats, true)) $cat = 'all';
$where = [];
$params = [];
if ($active !== 'all') {
$where[] = "pi.is_active = ?";
$params[] = (int)$active;
}
if ($cat !== 'all') {
$where[] = "pi.category = ?";
$params[] = $cat;
}
$whereSql = $where ? ("WHERE " . implode(" AND ", $where)) : "";
// Sum qty in subquery to avoid duplicates
$sql = "
SELECT
pi.id,
pi.category,
pi.item_name,
pi.item_code,
pi.is_active,
IFNULL(q.qty_totale, 0) AS qty_totale
FROM packaging_items pi
LEFT JOIN (
SELECT idpackaging_item, SUM(qty) AS qty_totale
FROM packaging_stock_lots
GROUP BY idpackaging_item
) q ON q.idpackaging_item = pi.id
$whereSql
ORDER BY pi.category ASC, pi.item_name ASC, pi.id DESC
";
$stmt = $pdo->prepare($sql);
$stmt->execute($params);
$rows = $stmt->fetchAll(PDO::FETCH_ASSOC);
function catLabel($c)
{
return match ($c) {
'PACKAGING_TYPE' => 'Tipo Confezione',
'BOX' => 'Scatola',
'PALLET' => 'Pallet',
default => $c
};
}
?>
<div class="table-responsive">
<table id="tabPackaging" class="table table-striped align-middle text-center" style="width:100%;">
<thead>
<tr>
<th>ID</th>
<th>Categoria</th>
<th>Nome</th>
<th>Codice</th>
<th>Q.tà Tot</th>
<th>Stato</th>
<th>Azioni</th>
</tr>
</thead>
<tbody>
<?php
if (!$rows) {
echo "<tr>
<td class='text-muted'>-</td>
<td class='text-muted'>Nessun elemento</td>
<td class='text-muted'>-</td>
<td class='text-muted'>-</td>
<td class='text-muted'>-</td>
<td class='text-muted'>-</td>
<td class='text-muted'>-</td>
</tr>";
} else {
foreach ($rows as $r) {
$isActive = ((int)$r['is_active'] === 1);
$badge = $isActive
? "<span class='badge bg-success'>Attivo</span>"
: "<span class='badge bg-secondary'>Inattivo</span>";
$toggleText = $isActive ? "Disattiva" : "Attiva";
$toggleClass = $isActive ? "btn-outline-warning" : "btn-outline-success";
$qtyTot = number_format((float)$r['qty_totale'], 3, ',', '.');
$trClass = $isActive ? "" : " class='inactive-item'";
echo "<tr{$trClass} data-item-id='{$r['id']}'>
<td>{$r['id']}</td>
<td>" . htmlspecialchars(catLabel($r['category'])) . "</td>
<td>" . htmlspecialchars($r['item_name']) . "</td>
<td><span class='fw-semibold'>" . htmlspecialchars($r['item_code']) . "</span></td>
<td><span class='qty-badge'>{$qtyTot}</span></td>
<td>{$badge}</td>
<td>
<button class='btn btn-sm btn-outline-dark manage-stock'
data-id='{$r['id']}'
data-name='" . htmlspecialchars($r['item_name'], ENT_QUOTES) . "'>
📦 Stock
</button>
<button class='btn btn-sm {$toggleClass} toggle-item'
data-id='{$r['id']}'>
🔁 {$toggleText}
</button>
</td>
</tr>";
}
}
?>
</tbody>
</table>
</div>
<div class="text-muted small mt-2">
Q.tà Tot = somma di tutti i lotti/fornitori collegati allimballo.
</div>
</div>
</div>
</div>
</div>
<?php include('include/footer.php'); ?>
</div>
<!-- MODALE: NUOVO IMBALLO -->
<div class="modal fade" id="addItemModal" tabindex="-1">
<div class="modal-dialog modal-dialog-centered">
<div class="modal-content">
<div class="modal-header" style="background-color:#cfe3ff;">
<h5 class="modal-title">Nuovo Imballo</h5>
<button type="button" class="btn-close" data-bs-dismiss="modal"></button>
</div>
<div class="modal-body">
<form id="addItemForm">
<div class="mb-3">
<label class="form-label fw-semibold">Categoria</label>
<select class="form-select" id="newCategory" required>
<option value="PACKAGING_TYPE">Tipo Confezione</option>
<option value="BOX">Scatola</option>
<option value="PALLET">Pallet</option>
<option value="OTHER">Altro</option>
</select>
</div>
<div class="mb-3">
<label class="form-label fw-semibold">Nome</label>
<input type="text" class="form-control" id="newItemName" required>
</div>
<div class="mb-3">
<label class="form-label fw-semibold">Codice</label>
<input type="text" class="form-control" id="newItemCode" required>
</div>
<div class="text-center">
<button type="submit" class="btn btn-add">💾 Salva</button>
</div>
</form>
</div>
</div>
</div>
</div>
<!-- MODALE: STOCK FORNITORI/LOTTI -->
<div class="modal fade" id="stockModal" tabindex="-1">
<div class="modal-dialog modal-xl modal-dialog-centered">
<div class="modal-content">
<div class="modal-header" style="background-color:#cfe3ff;">
<h5 class="modal-title">Stock - <span id="stockItemName"></span></h5>
<button type="button" class="btn-close" data-bs-dismiss="modal"></button>
</div>
<div class="modal-body">
<input type="hidden" id="stockItemId">
<input type="hidden" id="stockEditId">
<div class="row g-2 align-items-end mb-3">
<div class="col-md-3">
<label class="form-label fw-semibold">Fornitore</label>
<select class="form-select" id="stockSupplier" style="width:100%;"></select>
</div>
<div class="col-md-3">
<label class="form-label fw-semibold">Lotto</label>
<input type="text" class="form-control" id="stockLot" placeholder="LOT-...">
</div>
<div class="col-md-2">
<label class="form-label fw-semibold">Scadenza</label>
<input type="date" class="form-control" id="stockExpiry">
</div>
<div class="col-md-2">
<label class="form-label fw-semibold">Q.tà</label>
<input type="number" step="0.001" class="form-control" id="stockQty" value="0">
</div>
<div class="col-md-2 text-end">
<button class="btn btn-add w-100" id="stockSaveBtn"> Aggiungi</button>
<button class="btn btn-outline-secondary w-100 mt-2" id="stockCancelEdit" type="button" style="display:none;">Annulla</button>
</div>
</div>
<div class="table-responsive">
<table class="table table-striped align-middle text-center" id="stockTable">
<thead class="table-light">
<tr>
<th>ID</th>
<th>Fornitore</th>
<th>Lotto</th>
<th>Scadenza</th>
<th>Q.tà</th>
<th>Azioni</th>
</tr>
</thead>
<tbody></tbody>
</table>
</div>
<div class="text-muted small">
Suggerimento: per cambiare quantità clicca ✏️, modifica Q.tà e salva.
</div>
</div>
</div>
</div>
</div>
<?php include('jsinclude.php'); ?>
<script>
let dt;
function refreshUrlParams() {
const url = new URL(window.location.href);
url.searchParams.set('active', $('#filterActive').val());
url.searchParams.set('cat', $('#filterCategory').val());
window.location.href = url.toString();
}
function loadSuppliersSelect($select, dropdownParent) {
return fetch("get_suppliers.php")
.then(r => r.json())
.then(data => {
$select.empty();
$select.append(`<option value="">Seleziona...</option>`);
if (data.success && Array.isArray(data.rows)) {
data.rows.forEach(s => $select.append(`<option value="${s.idsupplier}">${s.supplier_name}</option>`));
}
$select.select2({
theme: "bootstrap-5",
width: "100%",
dropdownParent: dropdownParent
});
});
}
function stockResetForm() {
$("#stockEditId").val("");
$("#stockSupplier").val("").trigger("change");
$("#stockLot").val("");
$("#stockExpiry").val("");
$("#stockQty").val("0");
$("#stockSaveBtn").text(" Aggiungi");
$("#stockCancelEdit").hide();
}
function stockLoadRows(itemId) {
fetch("get_packaging_stock_lots.php?id=" + encodeURIComponent(itemId))
.then(r => r.json())
.then(data => {
const tbody = $("#stockTable tbody");
tbody.empty();
if (!data.success || !Array.isArray(data.rows) || data.rows.length === 0) {
tbody.append(`<tr>
<td class="text-muted">-</td>
<td class="text-muted">Nessuna riga</td>
<td class="text-muted">-</td>
<td class="text-muted">-</td>
<td class="text-muted">-</td>
<td class="text-muted">-</td>
</tr>`);
return;
}
data.rows.forEach(row => {
const exp = row.expiry_date ? row.expiry_date : "";
tbody.append(`
<tr data-row-id="${row.id}">
<td>${row.id}</td>
<td>${row.supplier_name}</td>
<td>${row.lot_code ?? ""}</td>
<td>${exp || "-"}</td>
<td>${row.qty}</td>
<td>
<button class="btn btn-sm btn-outline-secondary stock-edit"
data-id="${row.id}"
data-idsupplier="${row.idsupplier}"
data-lot="${(row.lot_code ?? "").replace(/"/g,'&quot;')}"
data-exp="${exp}"
data-qty="${row.qty}">
✏️
</button>
<button class="btn btn-sm btn-outline-danger stock-del" data-id="${row.id}">🗑️</button>
</td>
</tr>
`);
});
});
}
$(document).ready(function() {
// Init filters from URL
const urlParams = new URLSearchParams(window.location.search);
$('#filterActive').val(urlParams.get('active') || '1');
$('#filterCategory').val(urlParams.get('cat') || 'all');
dt = $('#tabPackaging').DataTable({
order: [
[2, 'asc']
],
pageLength: 50,
language: {
url: 'https://cdn.datatables.net/plug-ins/1.13.6/i18n/it-IT.json'
}
});
$('#filterActive, #filterCategory').on('change', refreshUrlParams);
});
// Create new item
$("#addItemForm").on("submit", function(e) {
e.preventDefault();
const category = $("#newCategory").val();
const item_name = $("#newItemName").val().trim();
const item_code = $("#newItemCode").val().trim();
fetch("save_packaging_item.php", {
method: "POST",
headers: {
"Content-Type": "application/x-www-form-urlencoded"
},
body: `category=${encodeURIComponent(category)}&item_name=${encodeURIComponent(item_name)}&item_code=${encodeURIComponent(item_code)}`
})
.then(r => r.json())
.then(data => {
if (data.success) {
Swal.fire({
icon: "success",
title: "Creato!"
}).then(() => location.reload());
} else {
Swal.fire({
icon: "error",
title: "Errore",
text: data.message || "Non salvato"
});
}
});
});
// Toggle item active
$(document).on("click", ".toggle-item", function() {
const id = $(this).data("id");
fetch("toggle_packaging_item_active.php", {
method: "POST",
headers: {
"Content-Type": "application/x-www-form-urlencoded"
},
body: `id=${encodeURIComponent(id)}`
})
.then(r => r.json())
.then(data => {
if (data.success) location.reload();
else Swal.fire({
icon: "error",
title: "Errore",
text: data.message || "Operazione non riuscita"
});
});
});
// Open stock modal
$(document).on("click", ".manage-stock", function() {
const id = $(this).data("id");
const name = $(this).data("name");
$("#stockItemId").val(id);
$("#stockItemName").text(name);
$("#stockModal").modal("show");
stockResetForm();
loadSuppliersSelect($("#stockSupplier"), $("#stockModal")).then(() => stockLoadRows(id));
});
// Click edit stock row
$(document).on("click", ".stock-edit", function() {
$("#stockEditId").val($(this).data("id"));
$("#stockSupplier").val(String($(this).data("idsupplier"))).trigger("change");
$("#stockLot").val($(this).data("lot"));
$("#stockExpiry").val($(this).data("exp"));
$("#stockQty").val($(this).data("qty"));
$("#stockSaveBtn").text("💾 Salva");
$("#stockCancelEdit").show();
});
// Cancel edit
$("#stockCancelEdit").on("click", function() {
stockResetForm();
});
// Save stock (insert/update)
$("#stockSaveBtn").on("click", function(e) {
e.preventDefault();
const itemId = $("#stockItemId").val();
const editId = $("#stockEditId").val();
const idsupplier = $("#stockSupplier").val();
const lot = $("#stockLot").val().trim();
const expiry = $("#stockExpiry").val();
const qty = $("#stockQty").val();
if (!idsupplier) {
Swal.fire({
icon: "warning",
title: "Attenzione",
text: "Seleziona un fornitore"
});
return;
}
const url = editId ? "update_packaging_stock_lot.php" : "save_packaging_stock_lot.php";
const body =
(editId ? `id=${encodeURIComponent(editId)}&` : ``) +
`idpackaging_item=${encodeURIComponent(itemId)}` +
`&idsupplier=${encodeURIComponent(idsupplier)}` +
`&lot_code=${encodeURIComponent(lot)}` +
`&expiry_date=${encodeURIComponent(expiry)}` +
`&qty=${encodeURIComponent(qty)}`;
fetch(url, {
method: "POST",
headers: {
"Content-Type": "application/x-www-form-urlencoded"
},
body
})
.then(r => r.json())
.then(data => {
if (data.success) {
stockResetForm();
stockLoadRows(itemId);
// Refresh totals by reloading page (simple and safe)
// If you want live update without reload, we can do it.
// location.reload();
} else {
Swal.fire({
icon: "error",
title: "Errore",
text: data.message || "Operazione non riuscita"
});
}
});
});
// Delete stock row
$(document).on("click", ".stock-del", function() {
const id = $(this).data("id");
const itemId = $("#stockItemId").val();
Swal.fire({
title: "Eliminare la riga?",
icon: "warning",
showCancelButton: true,
confirmButtonText: "Sì, elimina",
cancelButtonText: "Annulla",
confirmButtonColor: "#d33"
}).then((res) => {
if (!res.isConfirmed) return;
fetch("delete_packaging_stock_lot.php", {
method: "POST",
headers: {
"Content-Type": "application/x-www-form-urlencoded"
},
body: `id=${encodeURIComponent(id)}`
})
.then(r => r.json())
.then(data => {
if (data.success) {
stockLoadRows(itemId);
// location.reload();
} else {
Swal.fire({
icon: "error",
title: "Errore",
text: data.message || "Cancellazione non riuscita"
});
}
});
});
});
</script>
</body>
</html>