Files
zibo-dashboard/public/userarea/production_da_compilare.php
T
2026-09-15 15:46:32 +02:00

620 lines
19 KiB
PHP

<?php
ini_set('display_errors', 1);
error_reporting(E_ALL);
include('include/headscript.php');
$db = DBHandlerSelect::getInstance();
$pdo = $db->getConnection();
// Stato Qualità di destinazione dopo la compilazione
$statusQualita = (int)$pdo->query("SELECT id FROM production_status WHERE id = 3 LIMIT 1")->fetchColumn();
if (!$statusQualita) {
die("Errore: stato Controllo qualità (3) non trovato in production_status");
}
// --- SALVATAGGIO DATI DI PRODUZIONE (da stato 11 → Qualità 3) ---
if ($_SERVER['REQUEST_METHOD'] === 'POST' && !empty($_POST['save_compile'])) {
header('Content-Type: application/json; charset=utf-8');
$id = (int)($_POST['id'] ?? 0);
$kgprod = (float)($_POST['kgprod'] ?? 0);
$mtprod = (float)($_POST['mtprod'] ?? 0);
$scarto = (float)($_POST['scarto'] ?? 0);
$note = trim($_POST['note'] ?? '');
try {
if ($id <= 0) {
echo json_encode(['success' => false, 'msg' => 'ID non valido.']);
exit;
}
if ($kgprod <= 0 && $mtprod <= 0) {
echo json_encode(['success' => false, 'msg' => 'Inserisci almeno uno tra Kg o Metri prodotti.']);
exit;
}
// Verifico che la riga sia davvero in stato 11 (da compilare)
$stmtChk = $pdo->prepare("SELECT id_status FROM productiondata WHERE id = :id LIMIT 1");
$stmtChk->execute(['id' => $id]);
$cur = $stmtChk->fetchColumn();
if ($cur === false) {
echo json_encode(['success' => false, 'msg' => 'Produzione non trovata.']);
exit;
}
if ((int)$cur !== 11) {
echo json_encode(['success' => false, 'msg' => 'Questa produzione non è più in stato "Da compilare".']);
exit;
}
// Calcolo scarto %
$totaleKg = $kgprod + $scarto;
$scartoPerc = $totaleKg > 0 ? round(($scarto / $totaleKg) * 100, 2) : 0.00;
// Update: riempio i dati e passo a Qualità (3)
$sql = "UPDATE productiondata
SET kgprod = :kgprod,
mtprod = :mtprod,
scarto = :scarto,
scarto_perc = :scarto_perc,
note_operatore = :note,
id_status = :status
WHERE id = :id AND id_status = 11";
$stmt = $pdo->prepare($sql);
$stmt->execute([
'kgprod' => $kgprod,
'mtprod' => $mtprod,
'scarto' => $scarto,
'scarto_perc' => $scartoPerc,
'note' => $note ?: null,
'status' => $statusQualita,
'id' => $id
]);
echo json_encode(['success' => true]);
} catch (Exception $e) {
echo json_encode(['success' => false, 'msg' => $e->getMessage()]);
}
exit;
}
// --- AJAX: lista produzioni in stato 11 ---
if (!empty($_GET['ajax'])) {
$sql = "SELECT
p.*,
m.nome AS matrice,
l.name AS linea,
c.nome AS cliente,
(
SELECT GROUP_CONCAT(mes.nome ORDER BY mes.nome SEPARATOR ' | ')
FROM productiondata_mescole pm
JOIN mescole mes ON mes.id = pm.id_mescola
WHERE pm.id_productiondata = p.id
) AS mescole_list
FROM productiondata p
LEFT JOIN matrice m ON p.idmatrice = m.id
LEFT JOIN production_lines l ON p.id_linea = l.id
LEFT JOIN clients c ON p.id_cliente = c.id
WHERE p.id_status = 11
ORDER BY p.end_time ASC, p.id ASC";
$records = $pdo->query($sql)->fetchAll(PDO::FETCH_ASSOC);
if (empty($records)) {
echo '<div class="no-records"><i class="bi bi-check2-circle"></i><br>Nessuna produzione da compilare.</div>';
exit;
}
// Helper: secondi -> HH:MM:SS
$fmt = function ($sec) {
$sec = (int)$sec;
$h = floor($sec / 3600);
$m = floor(($sec % 3600) / 60);
$s = $sec % 60;
return sprintf('%02d:%02d:%02d', $h, $m, $s);
};
foreach ($records as $r) {
$occupato = $fmt($r['tempo_totale_produzione'] ?? 0);
$pause = $fmt($r['seconds_pause'] ?? 0);
$netto = $fmt($r['seconds_produzione'] ?? 0);
$endTime = $r['end_time'] ? date('d/m/Y H:i', strtotime($r['end_time'])) : '—';
$teoKg = number_format((float)($r['kg_sp'] ?? 0), 2, '.', '');
$teoMt = number_format((float)($r['metri'] ?? 0), 2, '.', '');
echo '<div class="compile-card" data-id="' . (int)$r['id'] . '"
data-teokg="' . htmlspecialchars($teoKg) . '"
data-teomt="' . htmlspecialchars($teoMt) . '">';
echo '<div class="compile-head">';
echo '<div class="compile-title">' . htmlspecialchars($r['matrice'] ?? '—') . '</div>';
echo '<div class="compile-badge">Da compilare</div>';
echo '</div>';
echo '<div class="compile-grid">';
echo '<div><strong>Linea</strong><span>' . htmlspecialchars($r['linea'] ?? '—') . '</span></div>';
echo '<div><strong>Cliente</strong><span>' . htmlspecialchars($r['cliente'] ?? '—') . '</span></div>';
echo '<div><strong>Fine produzione</strong><span>' . $endTime . '</span></div>';
echo '<div><strong>Tempo occupato</strong><span>' . $occupato . '</span></div>';
echo '<div><strong>Tempo pause</strong><span>' . $pause . '</span></div>';
echo '<div><strong>Tempo netto</strong><span>' . $netto . '</span></div>';
echo '</div>';
if (!empty($r['mescole_list'])) {
echo '<div class="compile-mescole"><strong>Mescole:</strong> '
. htmlspecialchars($r['mescole_list']) . '</div>';
}
if (!empty($r['note_extra'])) {
echo '<div class="compile-mescole"><strong>Descrizione:</strong> '
. htmlspecialchars($r['note_extra']) . '</div>';
}
echo '<div class="compile-actions">';
echo '<button class="compile-btn btn-fill" data-id="' . (int)$r['id'] . '">Compila dati</button>';
echo '</div>';
echo '</div>'; // .compile-card
}
exit;
}
?>
<!doctype html>
<html lang="it">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1">
<title>Dati da compilare - Produzione</title>
<?php include('cssinclude.php'); ?>
<link href="https://cdn.jsdelivr.net/npm/bootstrap-icons@1.11.3/font/bootstrap-icons.css" rel="stylesheet">
<style>
* {
box-sizing: border-box;
}
html,
body {
height: 100%;
margin: 0;
padding: 0;
overflow: hidden;
font-family: 'Segoe UI', sans-serif;
}
body {
background: #f1f5f9;
-webkit-tap-highlight-color: transparent;
}
.wrapper,
.page-wrapper,
.page-content {
height: 100%;
display: flex;
flex-direction: column;
}
.page-content {
padding: 0;
}
.main-card {
flex: 1;
display: flex;
flex-direction: column;
margin: 0.8rem;
border-radius: 1.2rem;
overflow: hidden;
box-shadow: 0 10px 30px rgba(0, 0, 0, 0.08);
background: #fff;
}
.card-header {
background: linear-gradient(135deg, #6d28d9, #7c3aed);
color: white;
padding: 1.2rem 1.5rem;
flex-shrink: 0;
display: flex;
justify-content: space-between;
align-items: center;
}
.card-header h5 {
margin: 0;
font-weight: 600;
font-size: 1.35rem;
color: #fff;
}
.back-btn {
background: rgba(255, 255, 255, 0.25);
border: 1px solid rgba(255, 255, 255, 0.4);
color: white;
padding: 0.5rem 1rem;
border-radius: 0.8rem;
font-weight: 500;
cursor: pointer;
}
.back-btn:hover {
background: rgba(255, 255, 255, 0.35);
}
#recordsContainer {
flex: 1;
overflow-y: auto;
padding: 1.2rem 1.5rem 5rem 1.5rem;
background: #f8fafc;
}
.compile-card {
background: #fff;
border-radius: 1rem;
border-left: 6px solid #7c3aed;
box-shadow: 0 4px 15px rgba(0, 0, 0, 0.06);
padding: 1.1rem 1.3rem;
margin-bottom: 1rem;
}
.compile-head {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 0.8rem;
}
.compile-title {
font-size: 1.3rem;
font-weight: 800;
color: #1e293b;
}
.compile-badge {
background: #ede9fe;
color: #6d28d9;
padding: 0.35rem 0.85rem;
border-radius: 2rem;
font-size: 0.75rem;
font-weight: 700;
text-transform: uppercase;
letter-spacing: 0.5px;
}
.compile-grid {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(140px, 1fr));
gap: 0.7rem 1rem;
margin-bottom: 0.6rem;
}
.compile-grid strong {
display: block;
color: #64748b;
font-size: 0.7rem;
text-transform: uppercase;
letter-spacing: 0.5px;
margin-bottom: 0.15rem;
}
.compile-grid span {
color: #1e293b;
font-weight: 600;
font-size: 1.05rem;
}
.compile-mescole {
color: #475569;
font-size: 0.95rem;
margin-top: 0.3rem;
}
.compile-actions {
margin-top: 1rem;
display: flex;
justify-content: flex-end;
}
.compile-btn {
padding: 0.9rem 1.6rem;
font-size: 1.05rem;
font-weight: 700;
border: none;
border-radius: 0.8rem;
cursor: pointer;
color: white;
text-transform: uppercase;
letter-spacing: 0.8px;
}
.btn-fill {
background: linear-gradient(135deg, #7c3aed, #6d28d9);
}
.btn-fill:hover {
background: linear-gradient(135deg, #6d28d9, #5b21b6);
}
.no-records {
text-align: center;
padding: 4rem 1.5rem;
color: #64748b;
font-size: 1.15rem;
}
.no-records i {
font-size: 3.5rem;
color: #a78bfa;
}
/* --- MODALE --- */
.custom-modal {
display: none;
position: fixed;
z-index: 1000;
left: 0;
top: 0;
width: 100%;
height: 100%;
background: rgba(0, 0, 0, 0.6);
justify-content: center;
align-items: center;
}
.custom-modal.active {
display: flex !important;
}
.custom-modal .modal-content {
background: white;
padding: 2rem;
border-radius: 1.2rem;
width: 95%;
max-width: 780px;
max-height: 92vh;
overflow-y: auto;
text-align: left;
box-shadow: 0 20px 50px rgba(0, 0, 0, 0.2);
}
.final-flex {
display: flex;
gap: 2rem;
margin-top: 1.2rem;
flex-wrap: wrap;
}
.final-col {
flex: 1;
min-width: 260px;
}
.teo-box {
background: #eef6ff;
padding: 1rem;
border-radius: 0.8rem;
}
.teo-row {
margin-bottom: 1rem;
font-size: 1.1rem;
}
.teo-row label {
font-weight: 600;
color: #1e3a8a;
}
.teo-row span {
font-weight: 700;
color: #0f172a;
font-size: 1.25rem;
display: block;
}
.real-box label {
margin-top: 0.6rem;
font-weight: 600;
display: block;
}
.input-big {
width: 100%;
padding: 0.9rem;
font-size: 1.25rem;
border-radius: 0.6rem;
border: 1px solid #cbd5e1;
margin-bottom: 1rem;
}
.modal-buttons {
display: flex;
gap: 1rem;
margin-top: 1.5rem;
}
.modal-btn {
flex: 1;
padding: 0.9rem;
font-weight: 600;
border: none;
border-radius: 0.8rem;
cursor: pointer;
font-size: 1.05rem;
}
.modal-confirm {
background: #7c3aed;
color: white;
}
.modal-cancel {
background: #e2e8f0;
color: #475569;
}
</style>
</head>
<body>
<div class="wrapper" id="appWrapper">
<?php include('include/navbar.php'); ?>
<?php include('include/topbar.php'); ?>
<div class="page-wrapper">
<div class="page-content">
<div class="main-card">
<div class="card-header">
<h5>Dati di produzione da compilare</h5>
<button class="back-btn" onclick="location.href='production_line.php'">Torna</button>
</div>
<div id="recordsContainer">
<!-- Caricato via JS -->
</div>
</div>
</div>
</div>
<?php include('include/footer.php'); ?>
</div>
<!-- MODALE COMPILAZIONE DATI -->
<div id="compileModal" class="custom-modal">
<div class="modal-content">
<h3>Dati di produzione</h3>
<p>Confronta i valori teorici e inserisci quelli reali:</p>
<div class="final-flex">
<div class="final-col teo-box">
<h4>Teorici</h4>
<div class="teo-row">
<label>Kg teorici:</label>
<span id="teoKg">0.00</span>
</div>
<div class="teo-row">
<label>Metri teorici:</label>
<span id="teoMt">0.00</span>
</div>
</div>
<div class="final-col real-box">
<h4>Reali</h4>
<label>Kg prodotti:</label>
<input type="number" id="kgprod" step="0.01" min="0" placeholder="0.00" class="input-big">
<label>Metri prodotti:</label>
<input type="number" id="mtprod" step="0.01" min="0" placeholder="0.00" class="input-big">
<label>Scarto (kg):</label>
<input type="number" id="scarto" step="0.01" min="0" placeholder="0.00" class="input-big">
<label>Scarto (%):</label>
<input type="text" id="scartoPerc" class="input-big" readonly>
<label>Note operatore:</label>
<textarea id="compileNote" rows="3" class="input-big"></textarea>
</div>
</div>
<div class="modal-buttons">
<button id="compileCancel" class="modal-btn modal-cancel">Annulla</button>
<button id="compileConfirm" class="modal-btn modal-confirm">Conferma e invia a Qualità</button>
</div>
</div>
</div>
<script src="https://code.jquery.com/jquery-3.6.0.min.js"></script>
<?php include('jsinclude.php'); ?>
<script>
$(function() {
function loadRecords() {
$('#recordsContainer').html(
'<div class="text-center p-5"><i class="bi bi-hourglass-split" style="font-size:2.5rem;color:#a78bfa;"></i></div>'
);
$.get('', {
ajax: 1
}, function(html) {
$('#recordsContainer').html(html);
}).fail(function() {
$('#recordsContainer').html('<div class="no-records">Errore di connessione</div>');
});
}
function recalcScrapPerc() {
const kg = parseFloat($('#kgprod').val()) || 0;
const sc = parseFloat($('#scarto').val()) || 0;
const tot = kg + sc;
const perc = tot > 0 ? (sc / tot) * 100 : 0;
$('#scartoPerc').val(perc.toFixed(2) + ' %');
}
// Apri modale compilazione
$(document).on('click', '.compile-btn', function(e) {
e.stopPropagation();
const card = $(this).closest('.compile-card');
const id = card.data('id');
$('#kgprod, #mtprod, #scarto').val('');
$('#scartoPerc').val('');
$('#compileNote').val('');
$('#teoKg').text(card.data('teokg') || '0.00');
$('#teoMt').text(card.data('teomt') || '0.00');
$('#kgprod, #scarto').off('input').on('input', recalcScrapPerc);
// Conferma
$('#compileConfirm').off('click').on('click', function() {
const kg = parseFloat($('#kgprod').val()) || 0;
const mt = parseFloat($('#mtprod').val()) || 0;
const scarto = parseFloat($('#scarto').val()) || 0;
const note = $('#compileNote').val().trim();
if (kg <= 0 && mt <= 0) {
alert('Inserisci almeno uno tra Kg o Metri prodotti.');
return;
}
$('#compileConfirm').prop('disabled', true);
$.post('', {
save_compile: 1,
id: id,
kgprod: kg,
mtprod: mt,
scarto: scarto,
note: note
}, function(r) {
$('#compileConfirm').prop('disabled', false);
if (r.success) {
$('#compileModal').removeClass('active');
loadRecords();
} else {
alert(r.msg || 'Errore nel salvataggio.');
}
}, 'json').fail(function() {
$('#compileConfirm').prop('disabled', false);
alert('Errore di comunicazione con il server.');
});
});
$('#compileModal').addClass('active');
});
// Annulla → non salva, resta in stato 11
$('#compileCancel').on('click', function() {
$('#compileModal').removeClass('active');
});
$(document).on('click', '#compileModal', function(e) {
if ($(e.target).is('#compileModal')) {
$('#compileModal').removeClass('active');
}
});
loadRecords();
});
</script>
</body>
</html>