producstion pause

This commit is contained in:
2026-09-15 15:46:32 +02:00
parent 4241d8e62b
commit cc24202937
6 changed files with 1125 additions and 82 deletions
+189 -41
View File
@@ -77,14 +77,33 @@ if ($_SERVER['REQUEST_METHOD'] === 'POST' && !empty($_POST['start_production']))
// 3) Ripresa: da PAUSA (7) → PRODUZIONE (2) **senza toccare start_time**
} elseif ((int)$currentStatus === (int)$statusPausaId) {
// Chiudo l'ultima pausa aperta di questa produzione
// Il motivo è obbligatorio alla ripresa: lo ricevo dal form
$resumeReasonId = isset($_POST['reason_id']) ? (int)$_POST['reason_id'] : 0;
// Verifico se la pausa aperta ha già un motivo
$stmtChk = $pdo->prepare("SELECT reason_id FROM production_pauses
WHERE id_production = :id AND end_pause IS NULL
ORDER BY start_pause DESC LIMIT 1");
$stmtChk->execute(['id' => $id]);
$existingReason = $stmtChk->fetchColumn();
// Se manca il motivo (sia sulla pausa che nel form) → blocco
if (($existingReason === null || $existingReason === false) && $resumeReasonId <= 0) {
echo json_encode(['success' => false, 'msg' => 'Seleziona il motivo della pausa prima di riprendere.']);
exit;
}
// Chiudo l'ultima pausa aperta + scrivo il motivo se fornito ora
$sqlClose = "UPDATE production_pauses
SET end_pause = UTC_TIMESTAMP()
SET end_pause = UTC_TIMESTAMP()"
. ($resumeReasonId > 0 ? ", reason_id = :reason_id" : "") . "
WHERE id_production = :id AND end_pause IS NULL
ORDER BY start_pause DESC
LIMIT 1";
$stmtClose = $pdo->prepare($sqlClose);
$stmtClose->execute(['id' => $id]);
$paramsClose = ['id' => $id];
if ($resumeReasonId > 0) $paramsClose['reason_id'] = $resumeReasonId;
$stmtClose->execute($paramsClose);
$sqlUpdate = "UPDATE productiondata
SET id_status = :status,
@@ -123,23 +142,22 @@ if ($_SERVER['REQUEST_METHOD'] === 'POST' && !empty($_POST['pause_production']))
try {
if ($reasonId <= 0) {
echo json_encode(['success' => false, 'msg' => 'Motivo pausa non valido.']);
exit;
// Motivo pausa ora OPZIONALE: se non lo sanno, salvano NULL e lo mettono al riprendi
if ($reasonId > 0) {
$sql = "SELECT is_problem FROM pause_reasons WHERE id = :id LIMIT 1";
$stmt = $pdo->prepare($sql);
$stmt->execute(['id' => $reasonId]);
$isProblem = $stmt->fetchColumn();
if ($isProblem === false) {
echo json_encode(['success' => false, 'msg' => 'Motivo pausa non trovato.']);
exit;
}
$isProblem = (int)$isProblem;
} else {
$reasonId = null; // nessun motivo scelto ora
$isProblem = 0;
}
// Leggo se il motivo è "problema" oppure pausa normale
$sql = "SELECT is_problem FROM pause_reasons WHERE id = :id LIMIT 1";
$stmt = $pdo->prepare($sql);
$stmt->execute(['id' => $reasonId]);
$isProblem = $stmt->fetchColumn();
if ($isProblem === false) {
echo json_encode(['success' => false, 'msg' => 'Motivo pausa non trovato.']);
exit;
}
$isProblem = (int)$isProblem;
// ❌ PRIMA – sbagliato per la tua logica:
// $nextStatus = $isProblem ? 8 : $statusPausaId; // 8 = Problema in produzione
@@ -294,8 +312,14 @@ if ($_SERVER['REQUEST_METHOD'] === 'POST' && !empty($_POST['save_final_data']))
$scartoPerc = round(($scarto / $totaleKg) * 100, 2);
}
// Stato destinazione
$nextStatus = ($reason === 'problema') ? 8 : $statusQualita;
// "Compila dopo" → stato 11 (dati da inserire), altrimenti flusso normale
$compilaDopo = !empty($_POST['compila_dopo']);
if ($compilaDopo) {
$nextStatus = 11; // Da compilare dati
} else {
$nextStatus = ($reason === 'problema') ? 8 : $statusQualita;
}
// Update finale
$sql = "UPDATE productiondata
@@ -410,6 +434,27 @@ if ($_SERVER['REQUEST_METHOD'] === 'POST' && !empty($_POST['create_quick'])) {
}
// --- AJAX: la pausa aperta di questa produzione ha già un motivo? ---
if (!empty($_GET['check_pause_reason'])) {
header('Content-Type: application/json; charset=utf-8');
$pid = (int)($_GET['id'] ?? 0);
if ($pid <= 0) {
echo json_encode(['success' => false]);
exit;
}
$stmt = $pdo->prepare("SELECT reason_id FROM production_pauses
WHERE id_production = :id AND end_pause IS NULL
ORDER BY start_pause DESC LIMIT 1");
$stmt->execute(['id' => $pid]);
$rid = $stmt->fetchColumn();
// has_reason = true se esiste una pausa aperta con reason_id non NULL
echo json_encode([
'success' => true,
'has_reason' => ($rid !== false && $rid !== null)
]);
exit;
}
// --- AJAX: mescole collegate a una matrice ---
if (!empty($_GET['mescole_by_matrice'])) {
header('Content-Type: application/json; charset=utf-8');
@@ -1497,17 +1542,32 @@ if (!empty($_GET['ajax'])) {
</div>
<!-- Motivo pausa (dropdown, solo per PAUSA) -->
<div id="pauseReasonContainer" style="margin-top:1rem; display:none;">
<label for="pauseReason"><strong>Motivo pausa:</strong></label>
<select id="pauseReason"
style="width:100%;padding:0.5rem;border-radius:0.5rem;border:1px solid #ccc;">
<option value="">Seleziona un motivo</option>
<?php foreach ($pauseReasons as $pr): ?>
<option value="<?= $pr['id'] ?>"
data-problem="<?= (int)$pr['is_problem'] ?>">
<?= htmlspecialchars($pr['name']) ?>
</option>
<?php endforeach; ?>
</select>
<!-- Toggle "motivo dopo" (comodo per tablet) -->
<label id="laterToggleWrap" for="pauseLater"
style="display:flex; align-items:center; gap:0.8rem; padding:0.9rem 1rem;
background:#f1f5f9; border:2px solid #cbd5e1; border-radius:0.8rem;
cursor:pointer; margin-bottom:1rem; user-select:none;">
<input type="checkbox" id="pauseLater"
style="width:1.6rem; height:1.6rem; cursor:pointer; flex:0 0 auto;">
<span style="font-weight:700; font-size:1.1rem; color:#1e3a8a;">
Inserisco il motivo dopo (al riprendi)
</span>
</label>
<div id="pauseReasonSelectWrap">
<label for="pauseReason"><strong>Motivo pausa:</strong></label>
<select id="pauseReason"
style="width:100%;padding:0.5rem;border-radius:0.5rem;border:1px solid #ccc;">
<option value="">Seleziona un motivo</option>
<?php foreach ($pauseReasons as $pr): ?>
<option value="<?= $pr['id'] ?>"
data-problem="<?= (int)$pr['is_problem'] ?>">
<?= htmlspecialchars($pr['name']) ?>
</option>
<?php endforeach; ?>
</select>
</div>
</div>
@@ -1579,8 +1639,10 @@ if (!empty($_GET['ajax'])) {
</div>
<div class="modal-buttons" style="margin-top:1.5rem;">
<div class="modal-buttons" style="margin-top:1.5rem; flex-wrap:wrap;">
<button id="finalCancel" class="modal-btn modal-cancel">Annulla</button>
<button id="finalLater" class="modal-btn modal-confirm"
style="background:#7c3aed;">Compila dopo</button>
<button id="finalConfirm" class="modal-btn modal-confirm">Conferma</button>
</div>
</div>
@@ -2183,7 +2245,20 @@ if (!empty($_GET['ajax'])) {
e.stopPropagation();
playBeep();
const id = $(this).data('id');
showReasonModal('resume', id);
// Chiedo al server se la pausa ha già un motivo
$.getJSON('', {
check_pause_reason: 1,
id: id
}, function(r) {
const hasReason = r && r.success && r.has_reason;
// 'resume' → motivo già presente: solo conferma
// 'resume_ask' → motivo mancante: chiedi il motivo
showReasonModal(hasReason ? 'resume' : 'resume_ask', id);
}).fail(function() {
// In caso di errore, per sicurezza chiedo il motivo
showReasonModal('resume_ask', id);
});
});
// --- PAUSA ---
@@ -2216,6 +2291,17 @@ if (!empty($_GET['ajax'])) {
return `${day} ${month} ${year}`;
}
// Toggle "motivo dopo": nasconde/disabilita la tendina
$(document).off('change', '#pauseLater').on('change', '#pauseLater', function() {
const later = $(this).is(':checked');
if (later) {
$('#pauseReason').val('').prop('disabled', true);
$('#pauseReasonSelectWrap').slideUp(120);
} else {
$('#pauseReason').prop('disabled', false);
$('#pauseReasonSelectWrap').slideDown(120);
}
});
// --- MODALE UNIFICATA PER TUTTE LE AZIONI ---
function showReasonModal(action, id) {
@@ -2226,6 +2312,10 @@ if (!empty($_GET['ajax'])) {
$('#noteContainer').hide();
$('#pauseReasonContainer').hide();
$('#pauseReason').val('');
// reset toggle "motivo dopo"
$('#pauseLater').prop('checked', false);
$('#pauseReasonSelectWrap').show();
$('#pauseReason').prop('disabled', false);
// Titolo e testo dinamici
switch (action) {
@@ -2235,8 +2325,22 @@ if (!empty($_GET['ajax'])) {
break;
case 'resume':
// Motivo già presente in pausa → solo conferma
$('#modalTitle').text('Ripresa produzione');
$('#modalQuestion').text('Vuoi riprendere la produzione?');
$('#pauseReasonContainer').hide();
$('#noteContainer').hide();
break;
case 'resume_ask':
// Pausa senza motivo → lo chiedo ora (obbligatorio)
$('#modalTitle').text('Ripresa produzione');
$('#modalQuestion').text('Indica il motivo della pausa per riprendere:');
$('#pauseReasonContainer').show();
$('#laterToggleWrap').hide(); // niente "motivo dopo" al riprendi
$('#pauseReasonSelectWrap').show();
$('#pauseReason').prop('disabled', false);
$('#noteContainer').show();
break;
case 'pause':
@@ -2244,6 +2348,7 @@ if (!empty($_GET['ajax'])) {
$('#modalQuestion').text('Seleziona il motivo della pausa:');
$('#modalOptions').hide(); // niente radio
$('#pauseReasonContainer').show(); // mostra tendina
$('#laterToggleWrap').show(); // toggle "motivo dopo" visibile
$('#noteContainer').show(); // nota opzionale / obbligatoria se problema
break;
@@ -2266,15 +2371,21 @@ if (!empty($_GET['ajax'])) {
// --- LOGICA PAUSA ---
if (action === 'pause') {
const pauseReasonId = $('#pauseReason').val();
if (!pauseReasonId) {
alert('Seleziona un motivo di pausa.');
const later = $('#pauseLater').is(':checked');
// Se NON ha scelto "motivo dopo", il motivo è obbligatorio ora
if (!later && !$('#pauseReason').val()) {
alert('Seleziona un motivo, oppure attiva "Inserisco il motivo dopo".');
return;
}
// Se "motivo dopo" → NULL (0), altrimenti l'id scelto
const pauseReasonId = later ? 0 : ($('#pauseReason').val() || 0);
const isProblem = $('#pauseReason option:selected').data('problem') == 1;
if (isProblem && note === '') {
// "Problema" richiede la nota (solo se il motivo è stato scelto ora)
if (!later && isProblem && note === '') {
alert('Inserisci una nota per una pausa con problema tecnico.');
return;
}
@@ -2304,10 +2415,24 @@ if (!empty($_GET['ajax'])) {
// Azioni specifiche
switch (action) {
case 'start':
case 'resume':
startProduction(id);
break;
case 'resume':
// Motivo già presente: riprendo senza inviare reason_id
startProduction(id);
break;
case 'resume_ask':
// Motivo mancante: obbligatorio sceglierlo ora
const resumeReasonId = $('#pauseReason').val();
if (!resumeReasonId) {
alert('Seleziona il motivo della pausa prima di riprendere.');
return;
}
startProduction(id, resumeReasonId);
break;
case 'stop':
// Sia "fine" che "problema" aprono il modale dati finali.
// Il reason viene passato e conservato per il salvataggio.
@@ -2375,6 +2500,28 @@ if (!empty($_GET['ajax'])) {
$('#finalCancel').off('click').on('click', function() {
$('#finalDataModal').removeClass('active');
});
// "Compila dopo": chiude la produzione senza kg/metri, va in stato 11
$('#finalLater').off('click').on('click', function() {
if (!confirm('La produzione verrà terminata e i dati (kg, metri, scarto) resteranno da inserire nella pagina dedicata. Confermi?')) {
return;
}
$('#finalDataModal').removeClass('active');
$.post('', {
save_final_data: 1,
id: id,
reason: reason,
compila_dopo: 1, // flag: rimanda la compilazione
kgprod: 0,
mtprod: 0,
scarto: 0,
note: $('#finalNote').val().trim()
}, function(r) {
if (r.success) loadRecords();
else alert(r.msg || 'Errore nel salvataggio.');
}, 'json');
});
}
function recalcScrapPerc() {
@@ -2394,10 +2541,11 @@ if (!empty($_GET['ajax'])) {
// --- START PRODUZIONE ---
function startProduction(id) {
function startProduction(id, reasonId) {
$.post('', {
start_production: 1,
id: id
id: id,
reason_id: reasonId || 0
}, function(r) {
if (r.success) loadRecords();
else alert(r.msg || 'Errore durante l\'avvio.');