zibo-dashboard/public/userarea/worksheets.php
2026-03-23 17:49:48 +01:00

708 lines
27 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
ini_set('display_errors', 1);
error_reporting(E_ALL);
include('include/headscript.php');
$db = DBHandlerSelect::getInstance();
$pdo = $db->getConnection();
function h($v)
{
return htmlspecialchars((string)$v, ENT_QUOTES, 'UTF-8');
}
function formatDateIT($d)
{
if (!$d || $d === '0000-00-00') return '—';
return date('d/m/Y', strtotime($d));
}
function formatDateTimeIT($d)
{
if (!$d || $d === '0000-00-00 00:00:00') return '—';
return date('d/m/Y H:i', strtotime($d));
}
function getRevisionLabel($rev)
{
$rev = trim((string)$rev);
return $rev !== '' ? $rev : '0';
}
function getWorksheetLabel($num)
{
$num = (int)$num;
return $num > 0 ? 'FL' . $num : '—';
}
// AJAX HANDLERS
if ($_SERVER['REQUEST_METHOD'] === 'POST' && isset($_POST['ajax']) && $_POST['ajax'] == '1') {
header('Content-Type: application/json; charset=utf-8');
$action = $_POST['action'] ?? '';
try {
if ($action === 'delete') {
$id = (int)($_POST['id'] ?? 0);
if ($id <= 0) {
echo json_encode(['success' => false, 'message' => 'ID non valido']);
exit;
}
$stmt = $pdo->prepare("DELETE FROM work_sheets WHERE id = ?");
$stmt->execute([$id]);
echo json_encode(['success' => true]);
exit;
}
if ($action === 'create_revision') {
$id = (int)($_POST['id'] ?? 0);
if ($id <= 0) {
echo json_encode(['success' => false, 'message' => 'ID non valido']);
exit;
}
$stmt = $pdo->prepare("
SELECT *
FROM work_sheets
WHERE id = ?
LIMIT 1
");
$stmt->execute([$id]);
$source = $stmt->fetch(PDO::FETCH_ASSOC);
if (!$source) {
echo json_encode(['success' => false, 'message' => 'Foglio non trovato']);
exit;
}
if (($source['worksheet_status'] ?? 'active') !== 'active') {
echo json_encode([
'success' => false,
'message' => 'Questo foglio è già inattivo/revisionato e non può essere revisionato di nuovo'
]);
exit;
}
$idmatrice = (int)$source['idmatrice'];
if ($idmatrice <= 0) {
echo json_encode(['success' => false, 'message' => 'ID matrice non valido']);
exit;
}
$worksheetNumber = (int)($source['worksheet_number'] ?? 0);
if ($worksheetNumber <= 0) {
echo json_encode(['success' => false, 'message' => 'Numero foglio non valido o non valorizzato']);
exit;
}
$pdo->beginTransaction();
// Trova la prossima revisione sulla stessa numerazione foglio
$stmtMaxRev = $pdo->prepare("
SELECT revision_code
FROM work_sheets
WHERE worksheet_number = ?
ORDER BY
CASE
WHEN revision_code IS NULL OR revision_code = '' THEN 0
WHEN revision_code REGEXP '^R[0-9]+$' THEN CAST(SUBSTRING(revision_code, 2) AS UNSIGNED)
ELSE 0
END DESC,
id DESC
LIMIT 1
");
$stmtMaxRev->execute([$worksheetNumber]);
$lastRevisionCode = $stmtMaxRev->fetchColumn();
$nextRevisionNumber = 1;
if ($lastRevisionCode && preg_match('/^R(\d+)$/', $lastRevisionCode, $m)) {
$nextRevisionNumber = ((int)$m[1]) + 1;
}
$newRevisionCode = 'R' . $nextRevisionNumber;
// Rendi inattivo il foglio corrente
$stmtDeactivate = $pdo->prepare("
UPDATE work_sheets
SET worksheet_status = 'inactive'
WHERE id = ?
");
$stmtDeactivate->execute([$id]);
// Crea clone del foglio mantenendo lo stesso worksheet_number
$stmtInsert = $pdo->prepare("
INSERT INTO work_sheets (
worksheet_number,
idmatrice,
worksheet_date,
customer_name,
profile_type_code,
revision_code,
worksheet_status,
marking,
prod_control_measure_settings,
control_frequency_cut,
control_frequency_drawing,
control_frequency_jig,
control_mode_jig,
requested_package_code,
meters_per_package,
meters_per_package_tolerance,
meters_per_package_notes,
box_type,
packages_or_pieces_per_box,
meters_per_box,
pallet_type,
boxes_or_packages_per_pallet,
speed_expected_kg_h,
speed_actual_kg_h,
speed_expected_m_h,
speed_actual_m_h,
approved_by,
notes
) VALUES (
?, ?, ?, ?, ?, ?, ?,
?, ?, ?, ?, ?, ?,
?, ?, ?, ?, ?, ?, ?, ?, ?,
?, ?, ?, ?, ?, ?
)
");
$stmtInsert->execute([
$worksheetNumber,
$source['idmatrice'],
$source['worksheet_date'],
$source['customer_name'],
$source['profile_type_code'],
$newRevisionCode,
'active',
$source['marking'],
$source['prod_control_measure_settings'],
$source['control_frequency_cut'],
$source['control_frequency_drawing'],
$source['control_frequency_jig'],
$source['control_mode_jig'],
$source['requested_package_code'],
$source['meters_per_package'],
$source['meters_per_package_tolerance'],
$source['meters_per_package_notes'],
$source['box_type'],
$source['packages_or_pieces_per_box'],
$source['meters_per_box'],
$source['pallet_type'],
$source['boxes_or_packages_per_pallet'],
$source['speed_expected_kg_h'],
$source['speed_actual_kg_h'],
$source['speed_expected_m_h'],
$source['speed_actual_m_h'],
$source['approved_by'],
$source['notes']
]);
$newWorksheetId = (int)$pdo->lastInsertId();
// Duplica le mescole collegate
$stmtMixes = $pdo->prepare("
SELECT
idmescola,
mix_position,
mix_weight_g_m,
required_density,
required_hardness_shore_a,
lubrication_type,
lubrication_notes
FROM work_sheet_mescole
WHERE worksheet_id = ?
ORDER BY mix_position ASC, id ASC
");
$stmtMixes->execute([$id]);
$mixRows = $stmtMixes->fetchAll(PDO::FETCH_ASSOC);
if ($mixRows) {
$stmtInsertMix = $pdo->prepare("
INSERT INTO work_sheet_mescole (
worksheet_id,
idmescola,
mix_position,
mix_weight_g_m,
required_density,
required_hardness_shore_a,
lubrication_type,
lubrication_notes
) VALUES (?, ?, ?, ?, ?, ?, ?, ?)
");
foreach ($mixRows as $mix) {
$stmtInsertMix->execute([
$newWorksheetId,
$mix['idmescola'],
$mix['mix_position'],
$mix['mix_weight_g_m'],
$mix['required_density'],
$mix['required_hardness_shore_a'],
$mix['lubrication_type'],
$mix['lubrication_notes']
]);
}
}
$pdo->commit();
echo json_encode([
'success' => true,
'message' => 'Revisione creata correttamente',
'new_id' => $newWorksheetId,
'new_revision_code' => $newRevisionCode,
'worksheet_number_label' => 'FL' . $worksheetNumber
]);
exit;
}
echo json_encode(['success' => false, 'message' => 'Azione sconosciuta']);
exit;
} catch (Exception $e) {
if ($pdo->inTransaction()) {
$pdo->rollBack();
}
echo json_encode(['success' => false, 'message' => $e->getMessage()]);
exit;
}
}
// Worksheets list + row count
$worksheets = $pdo->query("
SELECT
ws.id,
ws.worksheet_number,
ws.worksheet_date,
ws.customer_name,
ws.profile_type_code,
ws.revision_code,
ws.worksheet_status,
ws.approved_by,
ws.created_at,
ws.updated_at,
m.nome AS matrice_nome,
m.cliente AS matrice_cliente,
(
SELECT COUNT(*)
FROM work_sheet_mescole wsm
WHERE wsm.worksheet_id = ws.id
) AS mixes_count
FROM work_sheets ws
LEFT JOIN matrice m ON ws.idmatrice = m.id
ORDER BY ws.worksheet_number DESC, ws.id DESC
")->fetchAll(PDO::FETCH_ASSOC);
?>
<!doctype html>
<html lang="it">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<?php include('cssinclude.php'); ?>
<title>Fogli di Lavoro</title>
<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>
<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>
<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/@ttskch/select2-bootstrap4-theme@1.5.2/dist/select2-bootstrap4.min.css" rel="stylesheet" />
<script src="https://cdn.jsdelivr.net/npm/select2@4.1.0-rc.0/dist/js/select2.min.js"></script>
<style>
body {
font-size: 0.95rem;
background: #f8fafc;
}
.card {
border-radius: 16px;
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.08);
}
.back-dashboard {
background: #cfe3ff !important;
color: #1f2d3d !important;
border: 1px solid #bcd4f4 !important;
border-radius: 10px;
font-weight: 600;
padding: 10px 18px;
}
.btn-add {
background: #0d6efd;
color: white;
border-radius: 8px;
padding: 10px 20px;
font-weight: 500;
}
.btn-add:hover {
color: #fff;
}
.table thead {
background: #cfe3ff;
color: #1f2d3d;
}
.badge-revision {
display: inline-flex;
align-items: center;
justify-content: center;
min-width: 48px;
padding: 6px 10px;
border-radius: 999px;
background: #e7f1ff;
color: #0d6efd;
font-weight: 700;
font-size: 0.85rem;
}
.badge-fl {
display: inline-flex;
align-items: center;
justify-content: center;
min-width: 68px;
padding: 6px 12px;
border-radius: 999px;
background: #e8f7ee;
color: #198754;
font-weight: 800;
font-size: 0.86rem;
}
.badge-status-active {
display: inline-flex;
align-items: center;
justify-content: center;
min-width: 90px;
padding: 6px 10px;
border-radius: 999px;
background: #d1e7dd;
color: #0f5132;
font-weight: 700;
font-size: 0.85rem;
}
.badge-status-inactive {
display: inline-flex;
align-items: center;
justify-content: center;
min-width: 90px;
padding: 6px 10px;
border-radius: 999px;
background: #f8d7da;
color: #842029;
font-weight: 700;
font-size: 0.85rem;
}
.row-inactive {
opacity: 0.78;
background: #fcfcfd !important;
}
.btn-rev {
border-radius: 8px;
font-weight: 700;
}
.btn-rev[disabled] {
cursor: not-allowed;
opacity: 0.65;
}
.table td,
.table th {
vertical-align: middle;
}
/* Select2 Bootstrap sizing alignment */
.select2-container .select2-selection--single {
height: calc(2.375rem + 2px);
padding: 0.375rem 0.75rem;
border: 1px solid #ced4da;
border-radius: 0.375rem;
display: flex;
align-items: center;
}
.select2-container--default .select2-selection--single .select2-selection__rendered {
line-height: 1.6;
padding-left: 0;
}
.select2-container--default .select2-selection--single .select2-selection__arrow {
height: calc(2.375rem + 2px);
}
.select2-container {
width: 100% !important;
}
</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">Fogli di Lavoro</h5>
<button class="btn back-dashboard" onclick="location.href='production_dashboard.php'">↩️ Dashboard</button>
</div>
<div class="card-body">
<div class="d-flex justify-content-between align-items-center mb-3">
<h6 class="fw-semibold mb-0">Storico</h6>
<button class="btn btn-add" onclick="location.href='manage-worksheet.php'"> Nuovo Foglio</button>
</div>
<div class="table-responsive">
<table id="tabellaWorksheets" class="table table-striped table-bordered align-middle">
<thead>
<tr>
<th>Foglio</th>
<th>Rev</th>
<th>Data</th>
<th>Matrice/Profilo</th>
<th>Cliente</th>
<th>Codice Profilo</th>
<th>Stato</th>
<th>Mescole</th>
<th>Visto</th>
<th>Creato</th>
<th>Azioni</th>
</tr>
</thead>
<tbody>
<?php foreach ($worksheets as $ws): ?>
<?php
$status = trim((string)($ws['worksheet_status'] ?? 'active'));
if ($status === '') $status = 'active';
$isActive = ($status === 'active');
$revisionLabel = getRevisionLabel($ws['revision_code'] ?? '');
$worksheetLabel = getWorksheetLabel($ws['worksheet_number'] ?? 0);
?>
<tr class="<?= !$isActive ? 'row-inactive' : '' ?>">
<td>
<span class="badge-fl"><?= h($worksheetLabel) ?></span>
</td>
<td>
<span class="badge-revision"><?= h($revisionLabel) ?></span>
</td>
<td><?= h(formatDateIT($ws['worksheet_date'] ?? '')) ?></td>
<td><?= h($ws['matrice_nome'] ?? '-') ?></td>
<td><?= h($ws['customer_name'] ?: ($ws['matrice_cliente'] ?? '-')) ?></td>
<td><?= h($ws['profile_type_code'] ?? '-') ?></td>
<td>
<?php if ($isActive): ?>
<span class="badge-status-active">Attivo</span>
<?php else: ?>
<span class="badge-status-inactive">Inattivo</span>
<?php endif; ?>
</td>
<td><?= (int)$ws['mixes_count'] ?></td>
<td><?= h($ws['approved_by'] ?? '-') ?></td>
<td><?= h(formatDateTimeIT($ws['created_at'] ?? '')) ?></td>
<td class="text-nowrap">
<?php if ($isActive): ?>
<button class="btn btn-sm btn-outline-primary"
onclick="location.href='manage-worksheet.php?id=<?= (int)$ws['id'] ?>'">
✏️ Modifica
</button>
<?php else: ?>
<button class="btn btn-sm btn-outline-secondary"
onclick="location.href='manage-worksheet.php?id=<?= (int)$ws['id'] ?>'">
👁️ Vedi
</button>
<?php endif; ?>
<button class="btn btn-sm btn-outline-info btn-rev create-revision"
data-id="<?= (int)$ws['id'] ?>"
data-name="<?= h(($ws['matrice_nome'] ?? 'Foglio') . ' ' . $worksheetLabel) ?>"
data-rev="<?= h($revisionLabel) ?>"
data-fl="<?= h($worksheetLabel) ?>"
<?= !$isActive ? 'disabled title="Foglio già revisionato/inattivo"' : '' ?>>
REV
</button>
<button class="btn btn-sm btn-outline-danger delete-ws"
data-id="<?= (int)$ws['id'] ?>"
data-name="<?= h(($ws['matrice_nome'] ?? 'Foglio') . ' ' . $worksheetLabel . ' rev. ' . $revisionLabel) ?>">
🗑️ Elimina
</button>
</td>
</tr>
<?php endforeach; ?>
</tbody>
</table>
</div>
</div>
</div>
</div>
</div>
<?php include('include/footer.php'); ?>
</div>
<?php include('jsinclude.php'); ?>
<script>
$(document).ready(function() {
$('#tabellaWorksheets').DataTable({
pageLength: 50,
lengthMenu: [10, 25, 50, 100],
order: [
[0, 'desc']
],
language: {
url: 'https://cdn.datatables.net/plug-ins/1.13.6/i18n/it-IT.json'
}
});
$(document).on('click', '.delete-ws', function() {
const id = $(this).data('id');
const name = $(this).data('name');
Swal.fire({
title: 'Confermi eliminazione?',
text: `${name}`,
icon: 'warning',
showCancelButton: true,
confirmButtonColor: '#d33',
cancelButtonColor: '#6c757d',
confirmButtonText: 'Sì, elimina',
cancelButtonText: 'Annulla'
}).then(result => {
if (!result.isConfirmed) return;
fetch('', {
method: 'POST',
headers: {
'Content-Type': 'application/x-www-form-urlencoded'
},
body: `ajax=1&action=delete&id=${id}`
})
.then(r => r.json())
.then(data => {
if (data.success) {
Swal.fire({
icon: 'success',
title: 'Eliminato!',
timer: 900
}).then(() => location.reload());
} else {
Swal.fire({
icon: 'error',
title: 'Errore',
text: data.message
});
}
});
});
});
$(document).on('click', '.create-revision', function() {
if ($(this).is(':disabled')) {
return;
}
const id = $(this).data('id');
const name = $(this).data('name');
const rev = $(this).data('rev');
const fl = $(this).data('fl');
Swal.fire({
title: 'Creare una nuova revisione?',
html: `
<div class="text-start">
<div><strong>Foglio:</strong> ${fl}</div>
<div><strong>Profilo:</strong> ${name}</div>
<div><strong>Revisione attuale:</strong> ${rev}</div>
<hr>
<div>Il sistema:</div>
<ul style="text-align:left; margin-top:8px;">
<li>renderà inattivo il foglio corrente</li>
<li>creerà un clone completo</li>
<li>duplicherà anche le mescole</li>
<li>manterrà lo stesso numero foglio</li>
<li>assegnerà la nuova revisione progressiva</li>
</ul>
</div>
`,
icon: 'question',
showCancelButton: true,
confirmButtonColor: '#0dcaf0',
cancelButtonColor: '#6c757d',
confirmButtonText: 'Sì, crea revisione',
cancelButtonText: 'Annulla'
}).then(result => {
if (!result.isConfirmed) return;
fetch('', {
method: 'POST',
headers: {
'Content-Type': 'application/x-www-form-urlencoded'
},
body: `ajax=1&action=create_revision&id=${id}`
})
.then(r => r.json())
.then(data => {
if (data.success) {
Swal.fire({
icon: 'success',
title: 'Revisione creata',
html: `
<div>Foglio: <strong>${data.worksheet_number_label}</strong></div>
<div class="mt-1">Nuova revisione: <strong>${data.new_revision_code}</strong></div>
<div class="mt-2">Vuoi aprire subito il nuovo foglio?</div>
`,
showCancelButton: true,
confirmButtonText: 'Apri nuovo foglio',
cancelButtonText: 'Resta qui'
}).then(res => {
if (res.isConfirmed) {
window.location.href = 'manage-worksheet.php?id=' + data.new_id;
} else {
location.reload();
}
});
} else {
Swal.fire({
icon: 'error',
title: 'Errore',
text: data.message
});
}
})
.catch(() => {
Swal.fire({
icon: 'error',
title: 'Errore',
text: 'Errore durante la creazione della revisione'
});
});
});
});
});
</script>
</body>
</html>