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

1566 lines
65 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
ob_start(); // buffer any accidental output
ini_set('display_errors', 1);
error_reporting(E_ALL);
include('include/headscript.php');
$db = DBHandlerSelect::getInstance();
$pdo = $db->getConnection();
$worksheet_id = isset($_GET['id']) && is_numeric($_GET['id']) ? (int)$_GET['id'] : 0;
// AJAX HANDLERS
if ($_SERVER['REQUEST_METHOD'] === 'POST' && isset($_POST['ajax']) && $_POST['ajax'] == '1') {
// Ensure clean JSON output
while (ob_get_level()) {
ob_end_clean();
}
header('Content-Type: application/json; charset=utf-8');
$action = $_POST['action'] ?? '';
try {
// Save header (insert/update)
if ($action === 'save_header') {
$idmatrice = (int)($_POST['idmatrice'] ?? 0);
if ($idmatrice <= 0) {
echo json_encode(['success' => false, 'message' => 'Seleziona una matrice/profilo']);
exit;
}
$id = (int)($_POST['id'] ?? 0);
$worksheet_date = $_POST['worksheet_date'] !== '' ? $_POST['worksheet_date'] : null;
$customer_name = trim($_POST['customer_name'] ?? '');
$profile_code = trim($_POST['profile_type_code'] ?? '');
$marking = ($_POST['marking'] ?? '') !== '' ? $_POST['marking'] : null;
$prod_control = trim($_POST['prod_control_measure_settings'] ?? '');
$freq_cut = trim($_POST['control_frequency_cut'] ?? '');
$freq_draw = trim($_POST['control_frequency_drawing'] ?? '');
$freq_jig = trim($_POST['control_frequency_jig'] ?? '');
$mode_jig = trim($_POST['control_mode_jig'] ?? '');
// Package data moved to worksheet header
$requested_package_code = trim($_POST['requested_package_code'] ?? '');
$meters_per_package = $_POST['meters_per_package'] !== '' ? (int)$_POST['meters_per_package'] : null;
$meters_per_package_tolerance = trim($_POST['meters_per_package_tolerance'] ?? '');
$meters_per_package_notes = trim($_POST['meters_per_package_notes'] ?? '');
$box_type = trim($_POST['box_type'] ?? '');
$pkg_box = $_POST['packages_or_pieces_per_box'] !== '' ? (int)$_POST['packages_or_pieces_per_box'] : null;
$m_box = $_POST['meters_per_box'] !== '' ? (int)$_POST['meters_per_box'] : null;
$pallet_type = trim($_POST['pallet_type'] ?? '');
$per_pallet = $_POST['boxes_or_packages_per_pallet'] !== '' ? (int)$_POST['boxes_or_packages_per_pallet'] : null;
$sp_exp_kg = $_POST['speed_expected_kg_h'] !== '' ? (float)$_POST['speed_expected_kg_h'] : null;
$sp_act_kg = $_POST['speed_actual_kg_h'] !== '' ? (float)$_POST['speed_actual_kg_h'] : null;
$sp_exp_m = $_POST['speed_expected_m_h'] !== '' ? (float)$_POST['speed_expected_m_h'] : null;
$sp_act_m = $_POST['speed_actual_m_h'] !== '' ? (float)$_POST['speed_actual_m_h'] : null;
$approved_by = trim($_POST['approved_by'] ?? '');
$notes = trim($_POST['notes'] ?? '');
$worksheet_number = null;
if ($id > 0) {
$stmt = $pdo->prepare("
UPDATE work_sheets SET
idmatrice = ?,
worksheet_date = ?,
customer_name = ?,
profile_type_code = ?,
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 = ?
WHERE id = ?
");
$stmt->execute([
$idmatrice,
$worksheet_date,
$customer_name !== '' ? $customer_name : null,
$profile_code !== '' ? $profile_code : null,
$marking,
$prod_control !== '' ? $prod_control : null,
$freq_cut !== '' ? $freq_cut : null,
$freq_draw !== '' ? $freq_draw : null,
$freq_jig !== '' ? $freq_jig : null,
$mode_jig !== '' ? $mode_jig : null,
$requested_package_code !== '' ? $requested_package_code : null,
$meters_per_package,
$meters_per_package_tolerance !== '' ? $meters_per_package_tolerance : null,
$meters_per_package_notes !== '' ? $meters_per_package_notes : null,
$box_type !== '' ? $box_type : null,
$pkg_box,
$m_box,
$pallet_type !== '' ? $pallet_type : null,
$per_pallet,
$sp_exp_kg,
$sp_act_kg,
$sp_exp_m,
$sp_act_m,
$approved_by !== '' ? $approved_by : null,
$notes !== '' ? $notes : null,
$id
]);
echo json_encode(['success' => true, 'id' => $id]);
exit;
}
$pdo->beginTransaction();
$stmtNum = $pdo->query("
SELECT IFNULL(MAX(worksheet_number), 0) + 1 AS next_number
FROM work_sheets
");
$worksheet_number = (int)$stmtNum->fetchColumn();
if ($worksheet_number <= 0) {
$worksheet_number = 1;
}
$stmt = $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
(
?, ?, ?, ?, ?, ?, ?,
?, ?, ?, ?, ?, ?,
?, ?, ?, ?, ?, ?, ?, ?, ?,
?, ?, ?, ?, ?, ?
)
");
$stmt->execute([
$worksheet_number,
$idmatrice,
$worksheet_date,
$customer_name !== '' ? $customer_name : null,
$profile_code !== '' ? $profile_code : null,
null, // revision_code = vuoto => revisione 0
'active', // worksheet_status
$marking,
$prod_control !== '' ? $prod_control : null,
$freq_cut !== '' ? $freq_cut : null,
$freq_draw !== '' ? $freq_draw : null,
$freq_jig !== '' ? $freq_jig : null,
$mode_jig !== '' ? $mode_jig : null,
$requested_package_code !== '' ? $requested_package_code : null,
$meters_per_package,
$meters_per_package_tolerance !== '' ? $meters_per_package_tolerance : null,
$meters_per_package_notes !== '' ? $meters_per_package_notes : null,
$box_type !== '' ? $box_type : null,
$pkg_box,
$m_box,
$pallet_type !== '' ? $pallet_type : null,
$per_pallet,
$sp_exp_kg,
$sp_act_kg,
$sp_exp_m,
$sp_act_m,
$approved_by !== '' ? $approved_by : null,
$notes !== '' ? $notes : null
]);
$newId = (int)$pdo->lastInsertId();
$pdo->commit();
echo json_encode([
'success' => true,
'id' => $newId,
'worksheet_number' => $worksheet_number,
'worksheet_number_label' => worksheet_number_label($worksheet_number)
]);
exit;
}
// Add or edit mix row
if ($action === 'save_mix_row') {
$worksheet_id = (int)($_POST['worksheet_id'] ?? 0);
if ($worksheet_id <= 0) {
echo json_encode(['success' => false, 'message' => 'Salva prima il foglio (testata)']);
exit;
}
$row_id = (int)($_POST['row_id'] ?? 0);
$idmescola = (int)($_POST['idmescola'] ?? 0);
$pos = 0; // auto-assign
if ($idmescola <= 0) {
echo json_encode(['success' => false, 'message' => 'Seleziona una mescola']);
exit;
}
if ($pos <= 0) $pos = 1;
$mix_weight = $_POST['mix_weight_g_m'] !== '' ? (float)$_POST['mix_weight_g_m'] : null;
$density = trim($_POST['required_density'] ?? '');
$hardness = trim($_POST['required_hardness_shore_a'] ?? '');
$lub_type = ($_POST['lubrication_type'] ?? '') !== '' ? $_POST['lubrication_type'] : null;
$lub_note = trim($_POST['lubrication_notes'] ?? '');
$pdo->beginTransaction();
if ($row_id > 0) {
$stmt = $pdo->prepare("
UPDATE work_sheet_mescole SET
idmescola = ?,
mix_weight_g_m = ?,
required_density = ?,
required_hardness_shore_a = ?,
lubrication_type = ?,
lubrication_notes = ?
WHERE id = ? AND worksheet_id = ?
");
$stmt->execute([
$idmescola,
$mix_weight,
$density !== '' ? $density : null,
$hardness !== '' ? $hardness : null,
$lub_type,
$lub_note !== '' ? $lub_note : null,
$row_id,
$worksheet_id
]);
$pdo->commit();
echo json_encode(['success' => true]);
exit;
}
$stmtPos = $pdo->prepare("
SELECT IFNULL(MAX(mix_position), 0) + 1
FROM work_sheet_mescole
WHERE worksheet_id = ?
FOR UPDATE
");
$stmtPos->execute([$worksheet_id]);
$pos = (int)$stmtPos->fetchColumn();
if ($pos <= 0) $pos = 1;
$stmt = $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 (?, ?, ?, ?, ?, ?, ?, ?)
");
$stmt->execute([
$worksheet_id,
$idmescola,
$pos,
$mix_weight,
$density !== '' ? $density : null,
$hardness !== '' ? $hardness : null,
$lub_type,
$lub_note !== '' ? $lub_note : null
]);
$pdo->commit();
echo json_encode(['success' => true]);
exit;
}
// Delete mix row
if ($action === 'delete_mix_row') {
$row_id = (int)($_POST['row_id'] ?? 0);
if ($row_id <= 0) {
echo json_encode(['success' => false, 'message' => 'ID riga non valido']);
exit;
}
$stmt = $pdo->prepare("DELETE FROM work_sheet_mescole WHERE id = ?");
$stmt->execute([$row_id]);
echo json_encode(['success' => true]);
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;
}
}
// Load matrices dropdown
$matrici = $pdo->query("
SELECT id, nome, cliente
FROM matrice
ORDER BY nome ASC
")->fetchAll(PDO::FETCH_ASSOC);
// Load generic worksheet lookup options
$lookup = [];
$lookupDefault = [];
$stmt = $pdo->prepare("
SELECT category, value, label, is_default
FROM ws_lookup_options
WHERE is_active = 1
ORDER BY category ASC, sort_order ASC, label ASC
");
$stmt->execute();
foreach ($stmt->fetchAll(PDO::FETCH_ASSOC) as $r) {
$lookup[$r['category']][] = $r;
if ((int)$r['is_default'] === 1 && !isset($lookupDefault[$r['category']])) {
$lookupDefault[$r['category']] = $r['value'];
}
}
// Load packaging items from dedicated table
$packagingLookup = [
'PACKAGING_TYPE' => [],
'BOX' => [],
'PALLET' => []
];
$stmt = $pdo->prepare("
SELECT category, item_name, item_code
FROM packaging_items
WHERE is_active = 1
AND category IN ('PACKAGING_TYPE', 'BOX', 'PALLET')
ORDER BY category ASC, item_name ASC
");
$stmt->execute();
foreach ($stmt->fetchAll(PDO::FETCH_ASSOC) as $r) {
$label = $r['item_name'];
if (!empty($r['item_code'])) {
$label .= ' — ' . $r['item_code'];
}
$packagingLookup[$r['category']][] = [
'value' => $r['item_code'],
'label' => $label
];
}
// Load mescole dropdown for modal
$mescole = $pdo->query("
SELECT id, nome, nomeuscita
FROM mescole
ORDER BY nome ASC
")->fetchAll(PDO::FETCH_ASSOC);
// Load worksheet (edit)
$worksheet = null;
$mixRows = [];
if ($worksheet_id > 0) {
$stmt = $pdo->prepare("
SELECT ws.*, m.nome AS matrice_nome
FROM work_sheets ws
LEFT JOIN matrice m ON ws.idmatrice = m.id
WHERE ws.id = ?
");
$stmt->execute([$worksheet_id]);
$worksheet = $stmt->fetch(PDO::FETCH_ASSOC);
if ($worksheet) {
$stmt = $pdo->prepare("
SELECT
wsm.*,
me.nome AS mescola_nome,
me.nomeuscita AS mescola_uscita
FROM work_sheet_mescole wsm
LEFT JOIN mescole me ON me.id = wsm.idmescola
WHERE wsm.worksheet_id = ?
ORDER BY wsm.mix_position ASC, wsm.id ASC
");
$stmt->execute([$worksheet_id]);
$mixRows = $stmt->fetchAll(PDO::FETCH_ASSOC);
} else {
$worksheet_id = 0;
}
}
// Helpers
function h($v)
{
return htmlspecialchars((string)$v, ENT_QUOTES);
}
function worksheet_number_label($n)
{
$n = (int)$n;
return $n > 0 ? 'FL' . $n : 'Non assegnato';
}
function ws_options($rows, $selectedValue, $defaultValue = '')
{
$selectedValue = (string)$selectedValue;
if ($selectedValue === '' && $defaultValue !== '') {
$selectedValue = (string)$defaultValue;
}
$html = '<option value="">--</option>';
if (!$rows) return $html;
foreach ($rows as $r) {
$val = h($r['value']);
$lab = h($r['label']);
$sel = ((string)$r['value'] === $selectedValue) ? ' selected' : '';
$html .= "<option value=\"{$val}\"{$sel}>{$lab}</option>";
}
return $html;
}
function packaging_options($rows, $selectedValue = '')
{
$selectedValue = (string)$selectedValue;
$html = '<option value="">--</option>';
if (!$rows) return $html;
foreach ($rows as $r) {
$val = h($r['value']);
$lab = h($r['label']);
$sel = ((string)$r['value'] === $selectedValue) ? ' selected' : '';
$html .= "<option value=\"{$val}\"{$sel}>{$lab}</option>";
}
return $html;
}
$isEdit = ($worksheet_id > 0);
?>
<!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><?= $isEdit ? 'Modifica Foglio di Lavoro' : 'Nuovo Foglio di Lavoro' ?></title>
<script src="https://code.jquery.com/jquery-3.6.0.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.96rem;
background:
radial-gradient(circle at top right, rgba(59, 130, 246, 0.08), transparent 24%),
radial-gradient(circle at bottom left, rgba(16, 185, 129, 0.07), transparent 28%),
#f4f7fb;
}
.page-title-wrap {
display: flex;
align-items: flex-start;
justify-content: space-between;
gap: 15px;
flex-wrap: wrap;
margin-bottom: 20px;
}
.page-title-box {
background: linear-gradient(135deg, #1d4ed8, #2563eb);
color: #fff;
border-radius: 20px;
padding: 22px 24px;
box-shadow: 0 14px 35px rgba(37, 99, 235, 0.18);
flex: 1 1 auto;
}
.page-title-box h4 {
color: #fff;
margin-bottom: 6px;
font-weight: 800;
}
.page-title-box .subtitle {
margin: 0;
color: rgba(255, 255, 255, 0.92);
font-size: 0.93rem;
}
.top-actions {
display: flex;
gap: 10px;
align-items: center;
flex-wrap: wrap;
}
.btn-history {
background: #fff !important;
color: #1f2937 !important;
border: 1px solid #dbe3ef !important;
border-radius: 12px;
font-weight: 700;
padding: 11px 18px;
box-shadow: 0 6px 18px rgba(15, 23, 42, 0.06);
}
.main-card {
border: 0;
border-radius: 22px;
overflow: hidden;
box-shadow: 0 12px 34px rgba(15, 23, 42, 0.08);
background: #fff;
}
.section-block {
border: 1px solid #e7edf5;
border-radius: 18px;
background: #fff;
box-shadow: 0 4px 12px rgba(15, 23, 42, 0.03);
overflow: hidden;
margin-bottom: 18px;
}
.section-header {
display: flex;
align-items: center;
justify-content: space-between;
gap: 12px;
padding: 14px 18px;
border-bottom: 1px solid rgba(255, 255, 255, 0.25);
}
.section-header h6 {
margin: 0;
font-size: 1rem;
font-weight: 800;
}
.section-header .section-hint {
font-size: 0.84rem;
opacity: 0.92;
margin: 0;
}
.section-body {
padding: 18px;
}
.theme-blue .section-header {
background: linear-gradient(135deg, #dbeafe, #bfdbfe);
color: #123b7a;
}
.theme-green .section-header {
background: linear-gradient(135deg, #dcfce7, #bbf7d0);
color: #14532d;
}
.theme-orange .section-header {
background: linear-gradient(135deg, #ffedd5, #fed7aa);
color: #9a3412;
}
.theme-purple .section-header {
background: linear-gradient(135deg, #ede9fe, #ddd6fe);
color: #5b21b6;
}
.theme-gray .section-header {
background: linear-gradient(135deg, #f3f4f6, #e5e7eb);
color: #374151;
}
.theme-cyan .section-header {
background: linear-gradient(135deg, #cffafe, #a5f3fc);
color: #155e75;
}
.section-grid-note {
font-size: 0.84rem;
color: #6b7280;
margin-top: -4px;
margin-bottom: 10px;
}
.form-label {
font-weight: 700;
color: #1f2937;
margin-bottom: 6px;
}
.form-control,
.form-select {
border-radius: 12px;
min-height: 46px;
border: 1px solid #d7e0ea;
box-shadow: none !important;
}
textarea.form-control {
min-height: auto;
}
.form-control:focus,
.form-select:focus {
border-color: #60a5fa;
}
.input-tip {
font-size: 0.79rem;
color: #6b7280;
margin-top: 4px;
}
.required-badge {
display: inline-flex;
align-items: center;
gap: 6px;
background: #fee2e2;
color: #991b1b;
border-radius: 999px;
padding: 5px 10px;
font-size: 0.77rem;
font-weight: 700;
}
.soft-badge {
display: inline-flex;
align-items: center;
gap: 6px;
background: #eef2ff;
color: #3730a3;
border-radius: 999px;
padding: 5px 10px;
font-size: 0.77rem;
font-weight: 700;
}
.action-bar {
position: sticky;
bottom: 12px;
z-index: 20;
background: rgba(255, 255, 255, 0.92);
backdrop-filter: blur(8px);
border: 1px solid #e5edf6;
border-radius: 18px;
padding: 14px;
box-shadow: 0 10px 28px rgba(15, 23, 42, 0.10);
margin-top: 14px;
}
.btn-main-save {
background: linear-gradient(135deg, #16a34a, #22c55e);
color: #fff;
border: 0;
border-radius: 12px;
padding: 12px 22px;
font-weight: 800;
box-shadow: 0 10px 24px rgba(34, 197, 94, 0.18);
}
.btn-main-save:hover {
color: #fff;
opacity: 0.96;
}
.btn-mix {
background: linear-gradient(135deg, #2563eb, #3b82f6);
color: #fff;
border: 0;
border-radius: 12px;
padding: 12px 20px;
font-weight: 800;
box-shadow: 0 10px 24px rgba(59, 130, 246, 0.18);
}
.btn-mix:disabled {
background: #cbd5e1;
box-shadow: none;
color: #fff;
}
.summary-strip {
display: grid;
grid-template-columns: repeat(4, minmax(0, 1fr));
gap: 12px;
margin-bottom: 18px;
}
.summary-item {
background: #fff;
border: 1px solid #e5edf6;
border-radius: 16px;
padding: 14px 16px;
box-shadow: 0 6px 18px rgba(15, 23, 42, 0.04);
}
.summary-item .label {
font-size: 0.80rem;
color: #6b7280;
margin-bottom: 4px;
font-weight: 700;
}
.summary-item .value {
font-size: 1rem;
color: #111827;
font-weight: 800;
}
.table-wrap {
border: 1px solid #e5edf6;
border-radius: 18px;
overflow: hidden;
background: #fff;
}
.table thead th {
background: #e0efff !important;
color: #163a63;
font-weight: 800;
border-bottom: 1px solid #cde0f6 !important;
vertical-align: middle;
}
.table td {
vertical-align: middle;
}
.mix-table-title {
display: flex;
align-items: center;
justify-content: space-between;
gap: 12px;
flex-wrap: wrap;
margin-bottom: 14px;
}
.mix-table-title h5 {
margin: 0;
font-weight: 800;
color: #111827;
}
.mix-helper {
color: #6b7280;
font-size: 0.86rem;
}
.mini-pill {
display: inline-flex;
align-items: center;
border-radius: 999px;
padding: 4px 9px;
font-size: 0.75rem;
font-weight: 700;
background: #eef6ff;
color: #1d4ed8;
}
.modal-content {
border-radius: 20px;
overflow: hidden;
border: 0;
box-shadow: 0 18px 44px rgba(15, 23, 42, 0.18);
}
.modal-header {
border-bottom: 0;
padding: 16px 20px;
}
.modal-body {
padding: 20px;
}
.mix-modal-head {
background: linear-gradient(135deg, #dbeafe, #bfdbfe);
color: #123b7a;
}
.select2-container--bootstrap4 .select2-selection {
min-height: 46px !important;
border-radius: 12px !important;
border-color: #d7e0ea !important;
}
.select2-container--bootstrap4 .select2-selection--single .select2-selection__rendered {
line-height: 44px !important;
}
.select2-container--bootstrap4 .select2-selection--single .select2-selection__arrow {
height: 44px !important;
}
@media (max-width: 992px) {
.summary-strip {
grid-template-columns: repeat(2, minmax(0, 1fr));
}
}
@media (max-width: 576px) {
.summary-strip {
grid-template-columns: 1fr;
}
.page-title-box {
padding: 18px;
}
.section-body {
padding: 14px;
}
.action-bar {
padding: 12px;
}
}
</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="page-title-wrap">
<div class="page-title-box">
<h4><?= $isEdit ? 'Modifica Foglio di Lavoro' : 'Nuovo Foglio di Lavoro' ?></h4>
<p class="subtitle">
Pagina pensata per uso operativo in produzione: prima salva la testata, poi inserisci le mescole
</p>
</div>
<div class="top-actions">
<button class="btn btn-history" onclick="location.href='worksheets.php'">↩️ Storico fogli</button>
</div>
</div>
<div class="summary-strip">
<div class="summary-item">
<div class="label">Stato foglio</div>
<div class="value"><?= $isEdit ? 'In modifica' : 'Nuovo foglio' ?></div>
</div>
<div class="summary-item">
<div class="label">Foglio / Revisione</div>
<div class="value" id="summaryWsId">
<?php
$wsNumberLabel = ($worksheet && !empty($worksheet['worksheet_number']))
? worksheet_number_label($worksheet['worksheet_number'])
: 'Non assegnato';
$wsRevisionLabel = ($worksheet && isset($worksheet['revision_code']) && trim((string)$worksheet['revision_code']) !== '')
? trim((string)$worksheet['revision_code'])
: '0';
echo h($wsNumberLabel . ' / Rev. ' . $wsRevisionLabel);
?>
</div>
</div>
<div class="summary-item">
<div class="label">Mescole inserite</div>
<div class="value"><?= count($mixRows) ?></div>
</div>
<div class="summary-item">
<div class="label">Passaggio operativo</div>
<div class="value"><?= $worksheet_id > 0 ? 'Puoi aggiungere mescole' : 'Salva prima la testata' ?></div>
</div>
</div>
<div class="card main-card">
<div class="card-body p-4 p-lg-4">
<form id="worksheetHeaderForm">
<input type="hidden" name="id" id="wsId" value="<?= (int)$worksheet_id ?>">
<div class="section-block theme-blue">
<div class="section-header">
<div>
<h6>1. Dati principali del foglio</h6>
<p class="section-hint mb-0">Campi iniziali per identificare il lavoro</p>
</div>
<div class="required-badge">Obbligatorio: matrice / profilo</div>
</div>
<div class="section-body">
<div class="row g-3">
<div class="col-lg-6">
<label class="form-label">Matrice / Profilo *</label>
<select class="form-select" name="idmatrice" id="idmatrice" required>
<option value="">-- Seleziona --</option>
<?php foreach ($matrici as $m): ?>
<option value="<?= (int)$m['id'] ?>"
<?= $worksheet && (int)$worksheet['idmatrice'] === (int)$m['id'] ? 'selected' : '' ?>>
<?= h($m['nome']) ?><?= $m['cliente'] ? ' — ' . h($m['cliente']) : '' ?>
</option>
<?php endforeach; ?>
</select>
<div class="input-tip">Seleziona il profilo da produrre</div>
</div>
<div class="col-lg-3 col-md-6">
<label class="form-label">Data foglio</label>
<input type="date" class="form-control" name="worksheet_date"
value="<?= $worksheet ? h($worksheet['worksheet_date']) : '' ?>">
</div>
<div class="col-lg-3 col-md-6">
<label class="form-label">Codice profilo</label>
<input type="text" class="form-control" name="profile_type_code"
value="<?= $worksheet ? h($worksheet['profile_type_code']) : '' ?>">
</div>
<div class="col-lg-6">
<label class="form-label">Cliente (override)</label>
<input type="text" class="form-control" name="customer_name"
value="<?= $worksheet ? h($worksheet['customer_name']) : '' ?>">
<div class="input-tip">Compila solo se vuoi specificare un nome cliente manuale</div>
</div>
<div class="col-lg-3 col-md-6">
<label class="form-label">Marchiatura</label>
<select class="form-select" name="marking" id="marking">
<?= ws_options($lookup['marking'] ?? [], $worksheet['marking'] ?? '', $lookupDefault['marking'] ?? '') ?>
</select>
</div>
<div class="col-lg-3 col-md-6">
<label class="form-label">Visto / Approvazione</label>
<input type="text" class="form-control" name="approved_by"
value="<?= $worksheet ? h($worksheet['approved_by']) : '' ?>">
</div>
</div>
</div>
</div>
<div class="section-block theme-green">
<div class="section-header">
<div>
<h6>2. Controlli di produzione</h6>
<p class="section-hint mb-0">Istruzioni e frequenze di controllo per loperatore</p>
</div>
<div class="soft-badge">Sezione operativa</div>
</div>
<div class="section-body">
<div class="row g-3">
<div class="col-12">
<label class="form-label">Impostazione misure controllo produzione</label>
<textarea class="form-control" name="prod_control_measure_settings" rows="3"><?= $worksheet ? h($worksheet['prod_control_measure_settings']) : '' ?></textarea>
</div>
<div class="col-lg-3 col-md-6">
<label class="form-label">Controlli - Taglio</label>
<select class="form-select" name="control_frequency_cut" id="control_frequency_cut">
<?= ws_options($lookup['control_frequency_cut'] ?? [], $worksheet['control_frequency_cut'] ?? '', $lookupDefault['control_frequency_cut'] ?? '') ?>
</select>
</div>
<div class="col-lg-3 col-md-6">
<label class="form-label">Controlli - Disegno</label>
<select class="form-select" name="control_frequency_drawing" id="control_frequency_drawing">
<?= ws_options($lookup['control_frequency_drawing'] ?? [], $worksheet['control_frequency_drawing'] ?? '', $lookupDefault['control_frequency_drawing'] ?? '') ?>
</select>
</div>
<div class="col-lg-3 col-md-6">
<label class="form-label">Controlli - Dima</label>
<select class="form-select" name="control_frequency_jig" id="control_frequency_jig">
<?= ws_options($lookup['control_frequency_jig'] ?? [], $worksheet['control_frequency_jig'] ?? '', $lookupDefault['control_frequency_jig'] ?? '') ?>
</select>
</div>
<div class="col-lg-3 col-md-6">
<label class="form-label">Modalità dima</label>
<select class="form-select" name="control_mode_jig" id="control_mode_jig">
<?= ws_options($lookup['control_mode_jig'] ?? [], $worksheet['control_mode_jig'] ?? '', $lookupDefault['control_mode_jig'] ?? '') ?>
</select>
</div>
</div>
</div>
</div>
<div class="section-block theme-orange">
<div class="section-header">
<div>
<h6>3. Imballo e confezionamento</h6>
<p class="section-hint mb-0">Dati per confezione, scatola e bancale</p>
</div>
<div class="soft-badge">Packaging</div>
</div>
<div class="section-body">
<div class="section-grid-note">Compila prima la confezione, poi eventuali dettagli di scatola e bancale</div>
<div class="row g-3">
<div class="col-lg-4">
<label class="form-label">Codice confezione</label>
<select class="form-select" name="requested_package_code" id="requested_package_code">
<?= packaging_options($packagingLookup['PACKAGING_TYPE'] ?? [], $worksheet['requested_package_code'] ?? '') ?>
</select>
</div>
<div class="col-lg-4 col-md-6">
<label class="form-label">Metri per confezione</label>
<input type="number" class="form-control" name="meters_per_package"
value="<?= $worksheet ? h($worksheet['meters_per_package']) : '' ?>">
</div>
<div class="col-lg-4 col-md-6">
<label class="form-label">Tolleranza metri/confezione</label>
<input type="text" class="form-control" name="meters_per_package_tolerance"
value="<?= $worksheet ? h($worksheet['meters_per_package_tolerance']) : '' ?>"
placeholder="es. +/- 4m">
</div>
<div class="col-12">
<label class="form-label">Note metri/confezione</label>
<input type="text" class="form-control" name="meters_per_package_notes"
value="<?= $worksheet ? h($worksheet['meters_per_package_notes']) : '' ?>">
</div>
<div class="col-lg-3 col-md-6">
<label class="form-label">Scatola (tipo)</label>
<select class="form-select" name="box_type" id="box_type">
<?= packaging_options($packagingLookup['BOX'] ?? [], $worksheet['box_type'] ?? '') ?>
</select>
</div>
<div class="col-lg-3 col-md-6">
<label class="form-label">N° conf./pezzi per scatola</label>
<input type="number" class="form-control" name="packages_or_pieces_per_box"
value="<?= $worksheet ? h($worksheet['packages_or_pieces_per_box']) : '' ?>">
</div>
<div class="col-lg-3 col-md-6">
<label class="form-label">Metri per scatola</label>
<input type="number" class="form-control" name="meters_per_box"
value="<?= $worksheet ? h($worksheet['meters_per_box']) : '' ?>">
</div>
<div class="col-lg-3 col-md-6">
<label class="form-label">Bancale (tipo)</label>
<select class="form-select" name="pallet_type" id="pallet_type">
<?= packaging_options($packagingLookup['PALLET'] ?? [], $worksheet['pallet_type'] ?? '') ?>
</select>
</div>
<div class="col-lg-3 col-md-6">
<label class="form-label">Scatole/conf. per bancale</label>
<input type="number" class="form-control" name="boxes_or_packages_per_pallet"
value="<?= $worksheet ? h($worksheet['boxes_or_packages_per_pallet']) : '' ?>">
</div>
</div>
</div>
</div>
<div class="section-block theme-purple">
<div class="section-header">
<div>
<h6>4. Velocità di produzione</h6>
<p class="section-hint mb-0">Confronto tra velocità prevista ed effettiva</p>
</div>
<div class="soft-badge">Prestazioni linea</div>
</div>
<div class="section-body">
<div class="row g-3">
<div class="col-lg-3 col-md-6">
<label class="form-label">Vel. prevista (kg/h)</label>
<input type="number" step="0.01" class="form-control" name="speed_expected_kg_h"
value="<?= $worksheet ? h($worksheet['speed_expected_kg_h']) : '' ?>">
</div>
<div class="col-lg-3 col-md-6">
<label class="form-label">Vel. effettiva (kg/h)</label>
<input type="number" step="0.01" class="form-control" name="speed_actual_kg_h"
value="<?= $worksheet ? h($worksheet['speed_actual_kg_h']) : '' ?>">
</div>
<div class="col-lg-3 col-md-6">
<label class="form-label">Vel. prevista (m/h)</label>
<input type="number" step="0.01" class="form-control" name="speed_expected_m_h"
value="<?= $worksheet ? h($worksheet['speed_expected_m_h']) : '' ?>">
</div>
<div class="col-lg-3 col-md-6">
<label class="form-label">Vel. effettiva (m/h)</label>
<input type="number" step="0.01" class="form-control" name="speed_actual_m_h"
value="<?= $worksheet ? h($worksheet['speed_actual_m_h']) : '' ?>">
</div>
</div>
</div>
</div>
<div class="section-block theme-gray">
<div class="section-header">
<div>
<h6>5. Note finali</h6>
<p class="section-hint mb-0">Annotazioni operative o indicazioni aggiuntive</p>
</div>
<div class="soft-badge">Facoltativo</div>
</div>
<div class="section-body">
<div class="row g-3">
<div class="col-12">
<label class="form-label">Note</label>
<textarea class="form-control" name="notes" rows="3"><?= $worksheet ? h($worksheet['notes']) : '' ?></textarea>
</div>
</div>
</div>
</div>
<div class="action-bar">
<div class="d-flex flex-column flex-md-row justify-content-between align-items-center gap-3">
<div>
<div class="fw-bold text-dark">Flusso consigliato</div>
<div class="text-muted small">1. Compila la testata → 2. Salva → 3. Aggiungi una o più mescole</div>
</div>
<div class="d-flex gap-2 flex-wrap">
<button type="submit" class="btn btn-main-save">💾 Salva testata</button>
<button type="button" class="btn btn-mix"
id="btnAddMix" data-bs-toggle="modal" data-bs-target="#mixModal" disabled>
Aggiungi mescola
</button>
</div>
</div>
</div>
</form>
<div class="section-block theme-cyan mt-4 mb-0">
<div class="section-header">
<div>
<h6>6. Mescole associate al foglio</h6>
<p class="section-hint mb-0">Inserisci una o più righe mescola dopo il salvataggio della testata</p>
</div>
<div class="mini-pill"><?= count($mixRows) ?> righe</div>
</div>
<div class="section-body">
<div class="mix-table-title">
<div>
<h5>Elenco mescole</h5>
<div class="mix-helper">Puoi modificare o eliminare ogni riga direttamente dalla tabella</div>
</div>
</div>
<div class="table-wrap">
<div class="table-responsive">
<table id="tabellaMixRows" class="table table-striped table-bordered mb-0">
<thead>
<tr>
<th style="width:70px;">Pos</th>
<th>Mescola</th>
<th style="width:120px;">Peso (g/m)</th>
<th style="width:140px;">Densità</th>
<th style="width:150px;">Durezza</th>
<th style="width:120px;">Lubr.</th>
<th style="width:170px;">Azioni</th>
</tr>
</thead>
<tbody>
<?php foreach ($mixRows as $r): ?>
<tr>
<td><?= (int)$r['mix_position'] ?></td>
<td>
<div class="fw-bold"><?= h($r['mescola_nome'] ?? '-') ?></div>
<?php if (!empty($r['mescola_uscita'])): ?>
<div class="text-muted small"><?= h($r['mescola_uscita']) ?></div>
<?php endif; ?>
</td>
<td><?= h($r['mix_weight_g_m'] ?? '-') ?></td>
<td><?= h($r['required_density'] ?? '-') ?></td>
<td><?= h($r['required_hardness_shore_a'] ?? '-') ?></td>
<td><?= h($r['lubrication_type'] ?? '-') ?></td>
<td class="text-nowrap">
<button class="btn btn-sm btn-outline-primary edit-mix"
data-row='<?= h(json_encode($r, JSON_UNESCAPED_UNICODE)) ?>'>
✏️ Modifica
</button>
<button class="btn btn-sm btn-outline-danger delete-mix"
data-id="<?= (int)$r['id'] ?>">
🗑️
</button>
</td>
</tr>
<?php endforeach; ?>
</tbody>
</table>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
<?php include('include/footer.php'); ?>
</div>
<!-- MODAL MIX -->
<div class="modal fade" id="mixModal" tabindex="-1">
<div class="modal-dialog modal-lg modal-dialog-centered">
<div class="modal-content">
<div class="modal-header mix-modal-head">
<div>
<h5 class="modal-title mb-1" id="mixModalTitle">Aggiungi Mescola</h5>
<div class="small">Compila i dati della mescola da associare al foglio</div>
</div>
<button type="button" class="btn-close" data-bs-dismiss="modal"></button>
</div>
<div class="modal-body">
<form id="mixForm">
<input type="hidden" name="worksheet_id" id="mixWorksheetId" value="<?= (int)$worksheet_id ?>">
<input type="hidden" name="row_id" id="mixRowId" value="0">
<input type="hidden" name="mix_position" id="mix_position" value="">
<div class="row g-3">
<div class="col-lg-8">
<label class="form-label">Mescola *</label>
<select class="form-select" name="idmescola" id="idmescola" required>
<option value="">-- Seleziona --</option>
<?php foreach ($mescole as $me): ?>
<option value="<?= (int)$me['id'] ?>">
<?= h($me['nome']) ?><?= $me['nomeuscita'] ? ' — ' . h($me['nomeuscita']) : '' ?>
</option>
<?php endforeach; ?>
</select>
</div>
<div class="col-lg-4">
<label class="form-label">Peso metro mescola (g/m)</label>
<input type="number" step="0.01" class="form-control" name="mix_weight_g_m" id="mix_weight_g_m">
</div>
<div class="col-lg-4">
<label class="form-label">Densità richiesta</label>
<input type="text" class="form-control" name="required_density" id="required_density" placeholder="es. 1,22 +/- 0,03">
</div>
<div class="col-lg-4">
<label class="form-label">Durezza richiesta (shore A)</label>
<input type="text" class="form-control" name="required_hardness_shore_a" id="required_hardness_shore_a" placeholder="es. 70 +/- 5">
</div>
<div class="col-lg-4">
<label class="form-label">Lubrificazione</label>
<select class="form-select" name="lubrication_type" id="lubrication_type">
<?= ws_options($lookup['lubrication_type'] ?? [], '', $lookupDefault['lubrication_type'] ?? '') ?>
</select>
</div>
<div class="col-12">
<label class="form-label">Note lubrificazione</label>
<input type="text" class="form-control" name="lubrication_notes" id="lubrication_notes">
</div>
<div class="col-12 text-center mt-2">
<button type="submit" class="btn btn-main-save">💾 Salva mescola</button>
</div>
</div>
</form>
</div>
</div>
</div>
</div>
<?php include('jsinclude.php'); ?>
<script>
$(document).ready(function() {
const wsIdInput = $('#wsId');
const btnAddMix = $('#btnAddMix');
const summaryWsId = $('#summaryWsId');
if ($('#idmatrice').length) {
$('#idmatrice').select2({
theme: 'bootstrap4',
width: '100%',
placeholder: '-- Seleziona --',
allowClear: true
});
}
$('#mixModal').on('shown.bs.modal', function() {
if (!$('#idmescola').data('select2')) {
$('#idmescola').select2({
theme: 'bootstrap4',
width: '100%',
dropdownParent: $('#mixModal'),
placeholder: '-- Seleziona --',
allowClear: true
});
}
});
function enableMixButtonIfSaved() {
const id = parseInt(wsIdInput.val() || '0', 10);
if (id > 0) {
btnAddMix.prop('disabled', false);
$('#mixWorksheetId').val(id);
if (!summaryWsId.text().trim()) {
summaryWsId.text('Assegnato');
}
} else {
btnAddMix.prop('disabled', true);
$('#mixWorksheetId').val('0');
summaryWsId.text('Non assegnato');
}
}
enableMixButtonIfSaved();
$('#tabellaMixRows').DataTable({
pageLength: 25,
lengthMenu: [10, 25, 50, 100],
order: [
[0, 'asc']
],
language: {
url: 'https://cdn.datatables.net/plug-ins/1.13.6/i18n/it-IT.json'
}
});
// Save header
$('#worksheetHeaderForm').on('submit', function(e) {
e.preventDefault();
const formData = new FormData(this);
formData.append('ajax', '1');
formData.append('action', 'save_header');
fetch(window.location.href, {
method: 'POST',
body: new URLSearchParams(formData)
})
.then(async (r) => {
const text = await r.text();
try {
return JSON.parse(text);
} catch (err) {
console.error('Response is not valid JSON:', text);
throw new Error('Risposta non JSON (vedi console)');
}
})
.then(data => {
if (!data.success) {
Swal.fire({
icon: 'error',
title: 'Errore',
text: data.message
});
return;
}
wsIdInput.val(data.id);
if (data.worksheet_number_label) {
summaryWsId.text(data.worksheet_number_label);
}
enableMixButtonIfSaved();
if (!window.location.search.includes('id=')) {
const newUrl = 'manage-worksheet.php?id=' + data.id;
window.history.replaceState({}, '', newUrl);
}
Swal.fire({
icon: 'success',
title: 'Testata salvata',
text: (data.worksheet_number_label ? ('Foglio ' + data.worksheet_number_label + ' creato correttamente. Vuoi aggiungere subito le mescole?') : 'Vuoi aggiungere subito le mescole?'),
showCancelButton: true,
confirmButtonText: 'Sì, aggiungi mescole',
cancelButtonText: 'No'
}).then(res => {
if (res.isConfirmed) {
const modal = new bootstrap.Modal(document.getElementById('mixModal'));
modal.show();
}
});
})
.catch(err => {
Swal.fire({
icon: 'error',
title: 'Errore AJAX',
text: err.message
});
});
});
// Prepare modal for add
$('#mixModal').on('show.bs.modal', function() {
const wsId = parseInt(wsIdInput.val() || '0', 10);
if (wsId <= 0) {
Swal.fire({
icon: 'warning',
title: 'Salva prima la testata',
timer: 1200
});
return false;
}
$('#mixModalTitle').text('Aggiungi Mescola');
$('#mixForm')[0].reset();
$('#mixRowId').val('0');
$('#mixWorksheetId').val(wsId);
$('#idmescola').prop('disabled', false).val('').trigger('change');
$('#lubrication_type').val('');
});
// Edit mix row
$(document).on('click', '.edit-mix', function() {
let row = $(this).attr('data-row');
row = JSON.parse(row);
$('#mixModalTitle').text('Modifica Mescola');
$('#mixForm')[0].reset();
$('#mixRowId').val(row.id);
$('#mixWorksheetId').val(row.worksheet_id);
$('#idmescola').prop('disabled', false);
$('#idmescola').val(row.idmescola).trigger('change');
$('#mix_weight_g_m').val(row.mix_weight_g_m ?? '');
$('#required_density').val(row.required_density ?? '');
$('#required_hardness_shore_a').val(row.required_hardness_shore_a ?? '');
$('#lubrication_type').val(row.lubrication_type ?? '');
$('#lubrication_notes').val(row.lubrication_notes ?? '');
const modal = new bootstrap.Modal(document.getElementById('mixModal'));
modal.show();
});
// Save mix row
$('#mixForm').on('submit', function(e) {
e.preventDefault();
const formData = new FormData(this);
formData.append('ajax', '1');
formData.append('action', 'save_mix_row');
fetch('', {
method: 'POST',
body: new URLSearchParams(formData)
})
.then(r => r.json())
.then(data => {
if (data.success) {
Swal.fire({
icon: 'success',
title: 'Salvata!',
timer: 800
}).then(() => location.reload());
} else {
Swal.fire({
icon: 'error',
title: 'Errore',
text: data.message
});
}
});
});
// Delete mix row
$(document).on('click', '.delete-mix', function() {
const rowId = $(this).data('id');
Swal.fire({
title: 'Eliminare questa mescola dal foglio?',
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_mix_row&row_id=${rowId}`
})
.then(r => r.json())
.then(data => {
if (data.success) {
Swal.fire({
icon: 'success',
title: 'Eliminata!',
timer: 800
}).then(() => location.reload());
} else {
Swal.fire({
icon: 'error',
title: 'Errore',
text: data.message
});
}
});
});
});
});
</script>
</body>
</html>