fixed multiple parts mix and single line
This commit is contained in:
parent
dbc66723a6
commit
8d6fe92481
@ -7,6 +7,35 @@ $(document).ready(function () {
|
||||
let matrici = [];
|
||||
let macroMatrici = [];
|
||||
|
||||
// --- ROW ID helpers: niente più cache impazzita di jQuery .data() ---
|
||||
function getPartId($row) {
|
||||
// Legge in ordine: cache jQuery, nostra cache, attributo
|
||||
const d = $row.data("part-id");
|
||||
const ours = $row.data("__pid");
|
||||
const attr = $row.attr("data-part-id");
|
||||
|
||||
// Scegli il primo definito
|
||||
const id =
|
||||
d !== undefined
|
||||
? d
|
||||
: ours !== undefined
|
||||
? ours
|
||||
: attr !== undefined
|
||||
? attr
|
||||
: null;
|
||||
|
||||
// Normalizza: "new" o "" NON sono ID validi
|
||||
return id === "new" || id === "" || id === null ? null : id;
|
||||
}
|
||||
|
||||
function setPartId($row, id) {
|
||||
if (!id) return;
|
||||
// Sincronizza TUTTO: attributo + cache jQuery + nostra cache
|
||||
$row.attr("data-part-id", id);
|
||||
$row.data("part-id", id); // <<< fondamentale per invalidare la cache di jQuery
|
||||
$row.data("__pid", id);
|
||||
}
|
||||
|
||||
// ===================
|
||||
// VOICE RECOGNITION SETUP
|
||||
// ===================
|
||||
@ -439,6 +468,7 @@ $(document).ready(function () {
|
||||
|
||||
$(document).on("click", ".add-mix-global", function (e) {
|
||||
e.preventDefault();
|
||||
|
||||
const maxPartNumber = Math.max(
|
||||
...$("#partsTableBody tr")
|
||||
.map(function () {
|
||||
@ -446,36 +476,177 @@ $(document).ready(function () {
|
||||
})
|
||||
.get(),
|
||||
);
|
||||
addNewRow(maxPartNumber + 1, true); // Riga Mix
|
||||
|
||||
// Crea la riga Mix
|
||||
addNewRow(maxPartNumber + 1, true);
|
||||
const $mixRow = $("#partsTableBody tr:last");
|
||||
|
||||
// Consenti SOLO ora la creazione (INSERT) della riga Mix
|
||||
$mixRow.data("allowCreateMix", true);
|
||||
|
||||
// esegue SUBITO l'INSERT così ottieni part-id
|
||||
saveRow($mixRow);
|
||||
});
|
||||
|
||||
function extractPartId(response) {
|
||||
// prova i campi più comuni
|
||||
if (response == null) return null;
|
||||
|
||||
if (response.part_id) return response.part_id;
|
||||
if (response.id) return response.id;
|
||||
if (response.insert_id) return response.insert_id;
|
||||
if (response.lastId) return response.lastId;
|
||||
|
||||
// array di id
|
||||
if (Array.isArray(response.part_ids) && response.part_ids[0]) {
|
||||
return response.part_ids[0];
|
||||
}
|
||||
|
||||
// oggetti annidati
|
||||
if (response.parts && response.parts[0]) {
|
||||
if (response.parts[0].id) return response.parts[0].id;
|
||||
if (response.parts[0].part_id) return response.parts[0].part_id;
|
||||
if (response.parts[0].insert_id) return response.parts[0].insert_id;
|
||||
}
|
||||
|
||||
// altri possibili nomi dal backend
|
||||
if (response.new_id) return response.new_id;
|
||||
if (response.partId) return response.partId;
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
function saveRow($row) {
|
||||
const partNumber = $row.find(".part-number").val();
|
||||
const partDescription = $row.find(".part-description").val().trim();
|
||||
const dateexpiry = $row.find(".part-dateexpiry").val();
|
||||
const note = $row.data("note") || null;
|
||||
const $saveStatus = $row.find(".save-status");
|
||||
const $saveLoading = $row.find(".save-loading");
|
||||
const iddatadb = $("#partsModal").data("iddatadb");
|
||||
const idquotations = $("#partsModal").data("idquotations");
|
||||
const isMix = partDescription.startsWith("Mix") ? "Y" : "N";
|
||||
let partId = getPartId($row);
|
||||
if (partId === "new") partId = null; // difesa extra (non dovrebbe più servire, ma sicura)
|
||||
const endpoint = idquotations
|
||||
? "save_parts_quotation.php"
|
||||
: "save_parts.php";
|
||||
const data = idquotations ? { idquotations } : { iddatadb };
|
||||
|
||||
// Evita salvataggi concorrenti
|
||||
if ($row.data("saving") === true) return;
|
||||
|
||||
// Blocca INSERT del Mix se non esplicitamente richiesta
|
||||
if (isMix === "Y" && !partId && $row.data("allowCreateMix") !== true) {
|
||||
return;
|
||||
}
|
||||
|
||||
$row.data("saving", true);
|
||||
|
||||
if (partDescription && (iddatadb || idquotations)) {
|
||||
$saveLoading.show();
|
||||
$saveStatus.hide();
|
||||
|
||||
$.ajax({
|
||||
url: endpoint,
|
||||
method: "POST",
|
||||
data: JSON.stringify({
|
||||
...data,
|
||||
parts: [
|
||||
{
|
||||
id: partId,
|
||||
part_number: partNumber,
|
||||
part_description: partDescription,
|
||||
mix: isMix,
|
||||
dateexpiry: dateexpiry || null,
|
||||
note: note,
|
||||
},
|
||||
],
|
||||
}),
|
||||
contentType: "application/json",
|
||||
|
||||
success: function (response) {
|
||||
// assegna ID appena arriva (robusto)
|
||||
if (response.success) {
|
||||
const newId = extractPartId(response);
|
||||
if (newId) {
|
||||
setPartId($row, newId);
|
||||
} else {
|
||||
console.warn(
|
||||
"Parte salvata ma ID non presente nella risposta. Ricarico parti per sincronizzare gli ID.",
|
||||
);
|
||||
loadExistingParts(iddatadb, idquotations); // <<< ORA ANCHE PER RIGHE NORMALI
|
||||
}
|
||||
}
|
||||
|
||||
$row.data("saving", false);
|
||||
$row.removeData("allowCreateMix");
|
||||
|
||||
// ora che l'ID c'è di sicuro, sblocco gli update pendenti (es. M+)
|
||||
$row.trigger("row:saved");
|
||||
|
||||
$saveLoading.hide();
|
||||
if (response.success) {
|
||||
$saveStatus.show();
|
||||
setTimeout(() => $saveStatus.hide(), 2000);
|
||||
} else {
|
||||
const errorMsg = $(
|
||||
'<div class="alert alert-danger temp-alert" role="alert">Errore nel salvataggio: ' +
|
||||
response.message +
|
||||
"</div>",
|
||||
);
|
||||
$("#partsModal .modal-body").prepend(errorMsg);
|
||||
setTimeout(() => {
|
||||
errorMsg.fadeOut(500, function () {
|
||||
$(this).remove();
|
||||
});
|
||||
}, 5000);
|
||||
}
|
||||
},
|
||||
|
||||
error: function (xhr, status, error) {
|
||||
$row.data("saving", false);
|
||||
$row.removeData("allowCreateMix");
|
||||
$saveLoading.hide();
|
||||
const errorMsg = $(
|
||||
'<div class="alert alert-danger temp-alert" role="alert">Errore nel salvataggio delle parti: ' +
|
||||
error +
|
||||
" (" +
|
||||
xhr.status +
|
||||
")</div>",
|
||||
);
|
||||
$("#partsModal .modal-body").prepend(errorMsg);
|
||||
setTimeout(() => {
|
||||
errorMsg.fadeOut(500, function () {
|
||||
$(this).remove();
|
||||
});
|
||||
}, 5000);
|
||||
},
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
$(document).on("click", ".add-mix-row", function (e) {
|
||||
e.preventDefault();
|
||||
const $row = $(this).closest("tr");
|
||||
const partDescription = $row.find(".part-description").val().trim();
|
||||
|
||||
const $srcRow = $(this).closest("tr");
|
||||
const partDescription = $srcRow.find(".part-description").val().trim();
|
||||
if (!partDescription) {
|
||||
const errorMsg = $(
|
||||
'<div class="alert alert-danger temp-alert" role="alert">Inserisci una descrizione valida prima di aggiungerla al Mix.</div>',
|
||||
);
|
||||
$("#partsModal .modal-body").prepend(errorMsg);
|
||||
setTimeout(function () {
|
||||
errorMsg.fadeOut(500, function () {
|
||||
$(this).remove();
|
||||
});
|
||||
}, 5000);
|
||||
setTimeout(
|
||||
() =>
|
||||
errorMsg.fadeOut(500, function () {
|
||||
$(this).remove();
|
||||
}),
|
||||
5000,
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
const maxPartNumber = Math.max(
|
||||
...$("#partsTableBody tr")
|
||||
.map(function () {
|
||||
return parseInt($(this).find(".part-number").val()) || 0;
|
||||
})
|
||||
.get(),
|
||||
);
|
||||
|
||||
let mixDescription = `Mix ${partDescription}`;
|
||||
const $mixRow = $("#partsTableBody tr")
|
||||
let $mixRow = $("#partsTableBody tr")
|
||||
.filter(function () {
|
||||
return $(this)
|
||||
.find(".part-description")
|
||||
@ -485,31 +656,59 @@ $(document).ready(function () {
|
||||
})
|
||||
.last();
|
||||
|
||||
if ($mixRow.length > 0) {
|
||||
let currentMix = $mixRow.find(".part-description").val().trim();
|
||||
if (currentMix === "Mix") {
|
||||
mixDescription = currentMix + " " + partDescription;
|
||||
} else if (!currentMix.includes(partDescription)) {
|
||||
mixDescription = currentMix + " + " + partDescription;
|
||||
// Se non esiste una riga Mix, ne creo una e la INSERISCO SUBITO (come fa il bottone in header)
|
||||
if ($mixRow.length === 0) {
|
||||
const maxPartNumber = Math.max(
|
||||
...$("#partsTableBody tr")
|
||||
.map(function () {
|
||||
return (
|
||||
parseInt($(this).find(".part-number").val()) || 0
|
||||
);
|
||||
})
|
||||
.get(),
|
||||
);
|
||||
|
||||
addNewRow(maxPartNumber + 1, true);
|
||||
$mixRow = $("#partsTableBody tr:last");
|
||||
$mixRow.find(".part-description").val(`Mix ${partDescription}`);
|
||||
|
||||
// Consenti la creazione (INSERT) della riga Mix e salvala subito
|
||||
$mixRow.data("allowCreateMix", true);
|
||||
|
||||
saveRow($mixRow); // -> INSERT
|
||||
return; // la descrizione include già l'elemento appena aggiunto
|
||||
}
|
||||
|
||||
// Aggiorna la descrizione del Mix esistente
|
||||
const currentMix = $mixRow.find(".part-description").val().trim();
|
||||
let newDesc = currentMix;
|
||||
if (currentMix === "Mix") newDesc = currentMix + " " + partDescription;
|
||||
else if (!currentMix.includes(partDescription))
|
||||
newDesc = currentMix + " + " + partDescription;
|
||||
|
||||
$mixRow.find(".part-description").val(newDesc);
|
||||
|
||||
// Se il Mix è già in salvataggio (INSERT o UPDATE in corso), accodiamo un solo UPDATE
|
||||
if ($mixRow.data("saving") === true) {
|
||||
// evita più code accumulate
|
||||
if (!$mixRow.data("pendingUpdate")) {
|
||||
$mixRow.data("pendingUpdate", true);
|
||||
$mixRow.one("row:saved", function () {
|
||||
$mixRow.removeData("pendingUpdate");
|
||||
// ora che saving è false, salviamo l'ultima descrizione impostata
|
||||
saveRow($mixRow);
|
||||
});
|
||||
}
|
||||
$mixRow
|
||||
.find(".part-description")
|
||||
.val(mixDescription)
|
||||
.trigger("blur");
|
||||
} else {
|
||||
addNewRow(maxPartNumber + 1, true); // Crea nuova riga Mix
|
||||
const $newMixRow = $("#partsTableBody tr:last");
|
||||
$newMixRow
|
||||
.find(".part-description")
|
||||
.val(mixDescription)
|
||||
.trigger("blur");
|
||||
// libero: salva subito
|
||||
saveRow($mixRow);
|
||||
}
|
||||
});
|
||||
|
||||
function addNewRow(nextPartNumber, isMix = false) {
|
||||
const description = isMix ? "Mix" : "";
|
||||
const newRow = `
|
||||
<tr data-part-id="">
|
||||
<tr data-part-id="new">
|
||||
<td><input type="number" class="form-control form-control-sm part-number" value="${nextPartNumber || 1}" style="width: 80px;"></td>
|
||||
<td><input type="text" class="form-control form-control-sm part-description" value="${description}" placeholder="Inserisci descrizione"></td>
|
||||
<td>
|
||||
@ -540,7 +739,7 @@ $(document).ready(function () {
|
||||
// ===================
|
||||
$(document).on("click", ".note-btn", function () {
|
||||
const $row = $(this).closest("tr");
|
||||
const partId = $row.data("part-id");
|
||||
const partId = getPartId($row);
|
||||
const note = $row.data("note") || "";
|
||||
const $noteModal = $("#noteModal");
|
||||
$noteModal.find(".part-note").val(note);
|
||||
@ -655,7 +854,7 @@ $(document).ready(function () {
|
||||
$(document).on("change", ".part-dateexpiry", function () {
|
||||
const $input = $(this);
|
||||
const $row = $input.closest("tr");
|
||||
const partId = $row.data("part-id");
|
||||
const partId = getPartId($row);
|
||||
const dateexpiry = $input.val();
|
||||
const iddatadb = $("#partsModal").data("iddatadb");
|
||||
const idquotations = $("#partsModal").data("idquotations");
|
||||
@ -953,88 +1152,7 @@ $(document).ready(function () {
|
||||
});
|
||||
|
||||
$(document).on("blur", ".part-description, .part-number", function () {
|
||||
const $input = $(this);
|
||||
const $row = $input.closest("tr");
|
||||
const partNumber = $row.find(".part-number").val();
|
||||
const partDescription = $row.find(".part-description").val().trim();
|
||||
const dateexpiry = $row.find(".part-dateexpiry").val();
|
||||
const note = $row.data("note") || null;
|
||||
const $saveStatus = $row.find(".save-status");
|
||||
const $saveLoading = $row.find(".save-loading");
|
||||
const iddatadb = $("#partsModal").data("iddatadb");
|
||||
const idquotations = $("#partsModal").data("idquotations");
|
||||
const isMix = partDescription.startsWith("Mix") ? "Y" : "N";
|
||||
const partId = $row.data("part-id") || null;
|
||||
const endpoint = idquotations
|
||||
? "save_parts_quotation.php"
|
||||
: "save_parts.php";
|
||||
const data = idquotations
|
||||
? { idquotations: idquotations }
|
||||
: { iddatadb: iddatadb };
|
||||
|
||||
if (partDescription && (iddatadb || idquotations)) {
|
||||
$saveLoading.show();
|
||||
$saveStatus.hide();
|
||||
$.ajax({
|
||||
url: endpoint,
|
||||
method: "POST",
|
||||
data: JSON.stringify({
|
||||
...data,
|
||||
parts: [
|
||||
{
|
||||
id: partId,
|
||||
part_number: partNumber,
|
||||
part_description: partDescription,
|
||||
mix: isMix,
|
||||
dateexpiry: dateexpiry || null,
|
||||
note: note,
|
||||
},
|
||||
],
|
||||
}),
|
||||
contentType: "application/json",
|
||||
success: function (response) {
|
||||
$saveLoading.hide();
|
||||
if (response.success) {
|
||||
$saveStatus.show();
|
||||
if (response.part_id) {
|
||||
$row.attr("data-part-id", response.part_id).data(
|
||||
"part-id",
|
||||
response.part_id,
|
||||
);
|
||||
}
|
||||
setTimeout(() => $saveStatus.hide(), 2000);
|
||||
} else {
|
||||
const errorMsg = $(
|
||||
'<div class="alert alert-danger temp-alert" role="alert">Errore nel salvataggio: ' +
|
||||
response.message +
|
||||
"</div>",
|
||||
);
|
||||
$("#partsModal .modal-body").prepend(errorMsg);
|
||||
setTimeout(function () {
|
||||
errorMsg.fadeOut(500, function () {
|
||||
$(this).remove();
|
||||
});
|
||||
}, 5000);
|
||||
}
|
||||
},
|
||||
error: function (xhr, status, error) {
|
||||
$saveLoading.hide();
|
||||
const errorMsg = $(
|
||||
'<div class="alert alert-danger temp-alert" role="alert">Errore nel salvataggio delle parti: ' +
|
||||
error +
|
||||
" (" +
|
||||
xhr.status +
|
||||
")</div>",
|
||||
);
|
||||
$("#partsModal .modal-body").prepend(errorMsg);
|
||||
setTimeout(function () {
|
||||
errorMsg.fadeOut(500, function () {
|
||||
$(this).remove();
|
||||
});
|
||||
}, 5000);
|
||||
},
|
||||
});
|
||||
}
|
||||
saveRow($(this).closest("tr"));
|
||||
});
|
||||
|
||||
function loadExistingParts(iddatadb, idquotations) {
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user