fixed manage worksheet

This commit is contained in:
2026-10-26 14:05:31 +01:00
parent 0b9d5f5b40
commit 2f0f1135ac
+180 -40
View File
@@ -1462,6 +1462,7 @@ $isEdit = ($worksheet_id > 0);
<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="">
<input type="hidden" id="mixStagedIdx" value="">
<div class="row g-3">
<div class="col-lg-6">
@@ -1684,7 +1685,7 @@ $isEdit = ($worksheet_id > 0);
// Prepare modal for add
$('#mixModal').on('show.bs.modal', function() {
const wsId = parseInt(wsIdInput.val() || '0', 10);
if (wsId <= 0) {
if (wsId <= 0 && !window.importStaged) {
Swal.fire({
icon: 'warning',
title: 'Salva prima la testata',
@@ -1693,12 +1694,12 @@ $isEdit = ($worksheet_id > 0);
return false;
}
// Se stiamo modificando una riga esistente, non resettare il form
if (parseInt($('#mixRowId').val() || '0', 10) > 0) {
// Se stiamo modificando una riga esistente o provvisoria, non resettare il form
if (parseInt($('#mixRowId').val() || '0', 10) > 0 || $('#mixStagedIdx').val() !== '') {
return;
}
$('#mixModalTitle').text('Aggiungi Mescola');
$('#mixModalTitle').text(wsId > 0 ? 'Aggiungi Mescola' : 'Aggiungi Mescola (verrà salvata con la testata)');
$('#mixForm')[0].reset();
$('#mixRowId').val('0');
$('#mixWorksheetId').val(wsId);
@@ -1706,6 +1707,11 @@ $isEdit = ($worksheet_id > 0);
$('#lubrication_type').val('');
});
$('#mixModal').on('hidden.bs.modal', function() {
$('#mixStagedIdx').val('');
$('#mixRowId').val('0');
});
// Edit mix row
$(document).on('click', '.edit-mix', function() {
let row = $(this).attr('data-row');
@@ -1735,6 +1741,11 @@ $isEdit = ($worksheet_id > 0);
$('#mixForm').on('submit', function(e) {
e.preventDefault();
if (parseInt(wsIdInput.val() || '0', 10) <= 0 && window.importStaged) {
window.stageMixFromForm();
return;
}
const formData = new FormData(this);
formData.append('ajax', '1');
formData.append('action', 'save_mix_row');
@@ -1823,6 +1834,19 @@ $isEdit = ($worksheet_id > 0);
// Click: aggiungi imballo
btnAddPackage.on('click', function() {
const wsId = parseInt(wsIdInput.val() || '0', 10);
if (wsId <= 0 && window.importStaged) {
const code = $('#requested_package_code').val();
if (!code) {
Swal.fire({
icon: 'warning',
title: 'Seleziona un imballo',
timer: 1500
});
return;
}
window.stagePackage(code, $('#requested_package_code option:selected').text(), $('#packaging_category').val());
return;
}
if (wsId <= 0) {
Swal.fire({
icon: 'warning',
@@ -1922,40 +1946,53 @@ $isEdit = ($worksheet_id > 0);
$(function() {
window.importStaged = null;
function escH(v) {
return $('<div>').text(v == null ? '' : String(v)).html();
}
function stagedButtons() {
// con import in corso si può lavorare anche prima del salvataggio
if (window.importStaged) $('#btnAddMix, #btnAddPackage').prop('disabled', false);
}
function renderPendingPackages(packages) {
$('#packageRowsBody tr.import-pending').remove();
$('#packageImportNote').remove();
const $tb = $('#packageRowsBody');
const nonAbbinati = [];
const rows = [];
(packages || []).forEach(function(p) {
// righe solo per gli imballi ABBINATI (sicuri o proposti)
(packages || []).forEach(function(p, i) {
// righe solo per gli imballi ABBINATI (sicuri, proposti o aggiunti a mano)
if (!p.package_code) {
nonAbbinati.push('“' + (p.raw || '') + '” (' + (p.source || '') + ')');
nonAbbinati.push('“' + escH(p.raw) + '” (' + escH(p.source) + ')');
return;
}
const stato = p.needs_review ?
const stato = p.manual ?
'<span class="pending-badge">➕ aggiunto, verrà salvato</span>' :
(p.needs_review ?
'<span class="pending-badge">❓ proposto — verifica</span>' :
'<span class="pending-badge">⏳ verrà aggiunto al salvataggio</span>';
const nome = p.matched_name || '—';
const $tr = $(
'<span class="pending-badge">⏳ verrà salvato</span>');
const origine = p.manual ? 'aggiunto a mano' : ('dal file: ' + escH(p.raw) + ' (' + escH(p.source) + ')');
rows.push(
'<tr class="import-pending">' +
'<td>—</td>' +
'<td>' + (p.category || '—') + '</td>' +
'<td><div class="fw-bold">' + nome + '</div>' +
'<div class="text-muted small">dal file: ' + (p.raw || '') + ' (' + (p.source || '') + ')</div></td>' +
'<td>' + stato + '</td>' +
'<td>' + escH(p.category || '—') + '</td>' +
'<td><div class="fw-bold">' + escH(p.matched_name || '—') + '</div>' +
'<div class="text-muted small">' + origine + '</div></td>' +
'<td class="text-nowrap">' + stato +
' <button type="button" class="btn btn-sm btn-outline-danger staged-pkg-del ms-1" data-i="' + i + '" title="Togli">🗑️</button></td>' +
'</tr>'
);
$tb.prepend($tr);
});
$tb.prepend(rows.join(''));
// nota per gli imballi NON abbinati (niente righe vuote)
if (nonAbbinati.length) {
const $note = $(
'<div id="packageImportNote" class="import-hint" style="margin-bottom:10px;">' +
'⚠ Dal file non abbinati automaticamente: ' + nonAbbinati.join(', ') +
'. Aggiungili a mano qui sotto se servono.' +
'. Se servono, sceglili qui sopra e premi “Aggiungi imballo”.' +
'</div>'
);
$('#packageRowsBody').closest('.table-wrap').before($note);
@@ -1963,29 +2000,137 @@ $isEdit = ($worksheet_id > 0);
}
function renderPendingMescole(mescole) {
// rimuove eventuali righe provvisorie precedenti
// rimuove eventuali righe provvisorie precedenti e la riga "nessun dato" di DataTables
$('#tabellaMixRows tbody tr.import-pending').remove();
$('#tabellaMixRows tbody td.dataTables_empty').closest('tr').remove();
const $tb = $('#tabellaMixRows tbody');
(mescole || []).forEach(function(m) {
if (!m.idmescola) return; // le non riconosciute passano dal modal, non qui
const nome = m.matched_name || m.raw || '-';
const $tr = $(
const rows = [];
(mescole || []).forEach(function(m, i) {
const nome = m.idmescola ?
escH(m.matched_name || m.raw || '-') :
'<span style="color:#b45309;">⚠ da abbinare</span>';
const origine = m.manual ? 'aggiunta a mano' : ('dal file: ' + escH(m.raw));
const stato = m.idmescola ?
'<span class="pending-badge">⏳ verrà salvata</span>' :
'<span class="pending-badge">❓ scegli la mescola</span>';
rows.push(
'<tr class="import-pending">' +
'<td>—</td>' +
'<td><div class="fw-bold">' + nome + '</div>' +
'<div class="text-muted small">dal file: ' + (m.raw || '') + '</div></td>' +
'<td>' + (m.mix_weight_g_m ?? '-') + '</td>' +
'<td>' + (m.support_weight_g_m ?? '-') + '</td>' +
'<td>' + (m.required_density ?? '-') + '</td>' +
'<td>' + (m.required_hardness_shore_a ?? '-') + '</td>' +
'<td>' + (m.lubrication_type ?? '-') + '</td>' +
'<td><span class="pending-badge">⏳ verrà aggiunta al salvataggio</span></td>' +
'<div class="text-muted small">' + origine + '</div></td>' +
'<td>' + escH(m.mix_weight_g_m ?? '-') + '</td>' +
'<td>' + escH(m.support_weight_g_m ?? '-') + '</td>' +
'<td>' + escH(m.required_density || '-') + '</td>' +
'<td>' + escH(m.required_hardness_shore_a || '-') + '</td>' +
'<td>' + escH(m.lubrication_type || '-') + '</td>' +
'<td class="text-nowrap">' + stato + '<br>' +
'<button type="button" class="btn btn-sm btn-outline-primary staged-mix-edit mt-1" data-i="' + i + '">✏️ Modifica</button> ' +
'<button type="button" class="btn btn-sm btn-outline-danger staged-mix-del mt-1" data-i="' + i + '" title="Togli">🗑️</button>' +
'</td>' +
'</tr>'
);
$tb.prepend($tr);
});
$tb.prepend(rows.join(''));
}
// ---- modifica della lista provvisoria (prima del salvataggio) ----
window.stagePackage = function(code, label, category) {
const st = window.importStaged;
if (!st) return;
if (st.packages.some(p => String(p.package_code) === String(code))) {
Swal.fire({
icon: 'warning',
title: 'Imballo già presente',
timer: 1500
});
return;
}
st.packages.push({
package_code: code,
matched_name: label,
category: category || '',
raw: '',
source: 'manuale',
needs_review: false,
manual: true
});
renderPendingPackages(st.packages);
$('#requested_package_code').val('');
};
$(document).on('click', '.staged-pkg-del', function() {
const st = window.importStaged;
if (!st) return;
st.packages.splice(parseInt($(this).data('i'), 10), 1);
renderPendingPackages(st.packages);
});
$(document).on('click', '.staged-mix-del', function() {
const st = window.importStaged;
if (!st) return;
st.mescole.splice(parseInt($(this).data('i'), 10), 1);
renderPendingMescole(st.mescole);
});
$(document).on('click', '.staged-mix-edit', function() {
const st = window.importStaged;
if (!st) return;
const i = parseInt($(this).data('i'), 10);
const m = st.mescole[i];
if (!m) return;
$('#mixForm')[0].reset();
$('#mixRowId').val('0');
$('#mixStagedIdx').val(i);
$('#mixModalTitle').text(m.idmescola ? 'Modifica mescola (non ancora salvata)' : 'Mescola dal file: ' + (m.raw || '') + ' — seleziona la corrispondenza');
$('#idmescola').prop('disabled', false).val(m.idmescola ? String(m.idmescola) : '').trigger('change');
$('#mix_weight_g_m').val(m.mix_weight_g_m ?? '');
$('#support_weight_g_m').val(m.support_weight_g_m ?? '');
$('#required_density').val(m.required_density ?? '');
$('#required_hardness_shore_a').val(m.required_hardness_shore_a ?? '');
$('#lubrication_type').val(m.lubrication_type ?? '');
$('#lubrication_notes').val(m.lubrication_notes ?? '');
bootstrap.Modal.getOrCreateInstance(document.getElementById('mixModal')).show();
});
// salvataggio del modal mescola quando il foglio non esiste ancora
window.stageMixFromForm = function() {
const st = window.importStaged;
if (!st) return;
const idm = $('#idmescola').val();
if (!idm) {
Swal.fire({
icon: 'warning',
title: 'Seleziona una mescola',
timer: 1500
});
return;
}
const idxTxt = $('#mixStagedIdx').val();
const old = idxTxt !== '' ? (st.mescole[parseInt(idxTxt, 10)] || {}) : {};
const m = Object.assign({}, old, {
idmescola: idm,
matched_name: $.trim($('#idmescola option:selected').text()),
needs_review: false,
mix_weight_g_m: $('#mix_weight_g_m').val(),
support_weight_g_m: $('#support_weight_g_m').val(),
required_density: $('#required_density').val(),
required_hardness_shore_a: $('#required_hardness_shore_a').val(),
lubrication_type: $('#lubrication_type').val(),
lubrication_notes: $('#lubrication_notes').val()
});
if (idxTxt !== '') {
st.mescole[parseInt(idxTxt, 10)] = m;
} else {
m.raw = '';
m.manual = true;
st.mescole.push(m);
}
renderPendingMescole(st.mescole);
bootstrap.Modal.getOrCreateInstance(document.getElementById('mixModal')).hide();
};
function postForm(obj) {
return fetch('', {
method: 'POST',
@@ -2027,7 +2172,7 @@ $isEdit = ($worksheet_id > 0);
bits.push('Mescola “' + m.raw + '” ' + (m.idmescola ? '✓' : '⚠ da abbinare a mano')));
(data.packages || []).forEach(p =>
bits.push('Imballo “' + p.raw + '” (' + p.source + ') ' + (p.package_code ? '✓' : '⚠ da aggiungere a mano')));
let html = '<div class="import-banner"><strong>Import completato.</strong> Controlla i campi evidenziati in giallo, poi premi “Salva testata”.';
let html = '<div class="import-banner"><strong>Import completato.</strong> Controlla i campi evidenziati in giallo. Imballi e mescole in giallo si possono togliere (🗑️), correggere o aggiungere: al “Salva testata” viene salvato quello che vedi in tabella.';
if (bits.length) html += '<ul style="margin:8px 0 0 18px;">' + bits.map(b => '<li>' + b + '</li>').join('') + '</ul>';
if ((data.warnings || []).length) html += '<div style="color:#b45309;margin-top:6px;">' + data.warnings.join('<br>') + '</div>';
html += '</div>';
@@ -2048,17 +2193,15 @@ $isEdit = ($worksheet_id > 0);
const matchedMescole = [];
const unmatchedMescole = [];
// imballi: salva solo quelli SICURI; i proposti li segnaliamo da confermare
// imballi: salva quelli rimasti in tabella (sicuri, proposti non tolti, aggiunti a mano)
for (const p of (staged.packages || [])) {
if (p.package_code && !p.needs_review) {
if (p.package_code) {
await postForm({
ajax: 1,
action: 'save_package_row',
worksheet_id: newId,
package_code: p.package_code
});
} else if (p.package_code && p.needs_review) {
unresolvedPkgs.push('Imballo “' + p.raw + '” (' + p.source + ') → proposto: ' + (p.matched_name || '') + ' — verifica e aggiungi a mano');
} else {
unresolvedPkgs.push('Imballo “' + p.raw + '” (' + p.source + ') → nessun abbinamento, aggiungi a mano');
}
@@ -2082,7 +2225,7 @@ $isEdit = ($worksheet_id > 0);
required_density: m.required_density ?? '',
required_hardness_shore_a: m.required_hardness_shore_a ?? '',
lubrication_type: m.lubrication_type ?? '',
lubrication_notes: ''
lubrication_notes: m.lubrication_notes ?? ''
});
}
@@ -2211,10 +2354,6 @@ $isEdit = ($worksheet_id > 0);
setVal('meters_per_package_tolerance', h.meters_per_package_tolerance);
setVal('meters_per_package_notes', h.meters_per_package_notes);
setVal('meters_per_box', h.meters_per_box);
setVal('meters_per_package', h.meters_per_package);
setVal('meters_per_package_tolerance', h.meters_per_package_tolerance);
setVal('meters_per_package_notes', h.meters_per_package_notes);
setVal('meters_per_box', h.meters_per_box);
setVal('packages_or_pieces_per_box', h.packages_or_pieces_per_box);
setVal('boxes_or_packages_per_pallet', h.boxes_or_packages_per_pallet);
setVal('speed_expected_kg_h', h.speed_expected_kg_h);
@@ -2234,6 +2373,7 @@ $isEdit = ($worksheet_id > 0);
renderBanner(data);
renderPendingMescole(data.mescole || []);
renderPendingPackages(data.packages || []);
stagedButtons();
Swal.fire({
icon: 'success',