")
.text(part.part_description || "")
.html();
const escapedNote = part.note
? $("
").text(part.note).html()
: "";
const newRow = `
|
|
|
|
${partsExtraField ? buildExtraFieldCellHtml() : ``}
|
`;
$("#partsTableBody").append(newRow);
const $row = $(
`#partsTableBody tr[data-part-id="${part.id}"]`,
);
if (
part.extra_value_id !== undefined &&
part.extra_value_id !== null
) {
const vid = String(part.extra_value_id);
$row.data("extra-value-id", vid);
$row.find(".part-extra-value-id").val(vid);
$row.find(".part-extra-select").attr(
"data-selected",
vid,
);
}
initializeExtraFieldSelect2($row);
const $select = $("#partsTableBody").find(
`tr[data-part-id="${part.id}"] .part-matrice`,
);
const selectedMacro =
$("#macro-matrice-filter").val() || "";
initializeSelect2(
$select,
part.part_number,
part.id,
part.idmatrice || null,
selectedMacro,
);
if (part.idmatrice) {
partMatrice[part.part_number] = part.idmatrice;
}
});
} else {
addNewRow(1, false); // Riga iniziale normale
$("#quotationeBtn").removeClass("d-none");
}
updateRowButtons();
if (callback) callback();
},
error: function (xhr, status, error) {
const errorMsg = $(
'
Errore nel caricamento delle parti: ' +
error +
" (" +
xhr.status +
")
",
);
$("#partsModal .modal-body").prepend(errorMsg);
setTimeout(function () {
errorMsg.fadeOut(500, function () {
$(this).remove();
});
}, 5000);
addNewRow(1, false); // Riga iniziale normale
},
});
}
function loadMacroMatrici() {
$.ajax({
url: "get_macro_matrici.php",
method: "GET",
dataType: "json",
success: function (data) {
macroMatrici = data.value || [];
initializeMacroMatriceFilter();
},
error: function (xhr, status, error) {
console.error(
"Errore nel caricamento delle MacroMatrici:",
error,
);
macroMatrici = [];
initializeMacroMatriceFilter();
const errorMsg = $(
'
Errore nel caricamento delle MacroMatrici: ' +
error +
" (" +
xhr.status +
")
",
);
$("#partsModal .modal-body").prepend(errorMsg);
setTimeout(function () {
errorMsg.fadeOut(500, function () {
$(this).remove();
});
}, 5000);
},
});
}
function initializeMacroMatriceFilter() {
const $select = $("#macro-matrice-filter");
if ($select.length === 0) return; // Ignora se il filtro non è presente nell'HTML
if (typeof $.fn.select2 === "undefined") {
$select.replaceWith(
'
',
);
return;
}
// Popola il dropdown MacroMatrice
const options = [
{ id: "", text: "Tutte le MacroMatrici" },
...macroMatrici.map(function (macro) {
return { id: macro, text: macro };
}),
];
// Distrugge Select2 esistente, se presente
if ($select.hasClass("select2-hidden-accessible")) {
$select.select2("destroy");
}
$select.empty().select2({
placeholder: "Seleziona MacroMatrice",
allowClear: true,
data: options,
dropdownParent: $("#partsModal"),
});
$select.on("change", function () {
const selectedMacro = $(this).val() || "";
console.log("Filtro MacroMatrice cambiato:", selectedMacro);
// Aggiorna global-matrice
initializeGlobalSelect2(selectedMacro);
// Aggiorna tutti i part-matrice
$("#partsTableBody .part-matrice").each(function () {
const $partSelect = $(this);
const partNumber = $partSelect
.closest("tr")
.find(".part-number")
.val();
const partId = $partSelect.closest("tr").data("part-id");
const currentValue =
$partSelect.val() || partMatrice[partNumber] || null;
initializeSelect2(
$partSelect,
partNumber,
partId,
currentValue,
selectedMacro,
true,
);
});
});
}
function initializeGlobalSelect2(selectedMacro = null) {
const $select = $("#global-matrice");
if (typeof $.fn.select2 === "undefined") {
$select.replaceWith(
'
',
);
return;
}
// Filtra le matrici in base alla MacroMatrice selezionata
const filteredMatrici = selectedMacro
? matrici.filter(
(matrice) => matrice.MacroMatrice === selectedMacro,
)
: matrici;
// Crea opzioni per Select2
const options = filteredMatrici.map(function (matrice) {
return { id: matrice.IdMatrice, text: matrice.NomeMatrice };
});
// Memorizza il valore corrente
const currentValue = $select.val();
// Distrugge Select2 esistente, se presente
if ($select.hasClass("select2-hidden-accessible")) {
$select.select2("destroy");
}
// Inizializza Select2
$select.empty().select2({
placeholder: filteredMatrici.length
? "Seleziona matrice globale"
: "Nessuna matrice disponibile",
allowClear: true,
data: options,
dropdownParent: $("#partsModal"),
minimumResultsForSearch: 0, // Abilita ricerca senza limite minimo di caratteri
matcher: function (params, data) {
if (!params.term) return data; // Mostra tutte le opzioni se non c'è termine di ricerca
const term = params.term.toUpperCase();
if (data.text.toUpperCase().indexOf(term) >= 0) return data;
return null;
},
});
// Ripristina il valore corrente se valido
if (
currentValue &&
filteredMatrici.some((m) => m.IdMatrice == currentValue)
) {
$select.val(currentValue).trigger("change");
} else if (filteredMatrici.length === 0) {
const errorMsg = $(
'
Nessuna matrice disponibile per la MacroMatrice selezionata.
',
);
$("#partsModal .modal-body").prepend(errorMsg);
setTimeout(function () {
errorMsg.fadeOut(500, function () {
$(this).remove();
});
}, 5000);
}
}
function initializeSelect2(
$select,
partNumber,
partId,
idmatrice,
selectedMacro = null,
fromFilter = false,
) {
if (typeof $.fn.select2 === "undefined") {
$select.replaceWith(
'
',
);
return;
}
// Filtra le matrici in base alla MacroMatrice selezionata
const filteredMatrici = selectedMacro
? matrici.filter((m) => m.MacroMatrice === selectedMacro)
: matrici;
// Crea opzioni per Select2, includendo il valore pre-selezionato se non è nel filtro
let options = filteredMatrici.map(function (matrice) {
return { id: matrice.IdMatrice, text: matrice.NomeMatrice };
});
// Se idmatrice esiste ed è fuori dal filtro, aggiungilo come opzione
if (
idmatrice &&
!filteredMatrici.some((m) => m.IdMatrice == idmatrice)
) {
const selectedMatrice = matrici.find(
(m) => m.IdMatrice == idmatrice,
);
if (selectedMatrice) {
options.push({
id: selectedMatrice.IdMatrice,
text: selectedMatrice.NomeMatrice,
});
}
}
// Memorizza il valore corrente
const currentValue = idmatrice || $select.val();
// Distrugge Select2 esistente, se presente
if ($select.hasClass("select2-hidden-accessible")) {
$select.select2("destroy");
}
// Inizializza Select2
$select.empty().select2({
placeholder: filteredMatrici.length
? "Seleziona matrice"
: "Nessuna matrice disponibile",
allowClear: true,
data: options,
dropdownParent: $("#partsModal"),
minimumResultsForSearch: 0, // Abilita ricerca senza limite minimo di caratteri
matcher: function (params, data) {
if (!params.term) return data; // Mostra tutte le opzioni se non c'è termine di ricerca
const term = params.term.toUpperCase();
if (data.text.toUpperCase().indexOf(term) >= 0) return data;
return null;
},
});
// Ripristina il valore se valido
if (partId && partId !== "new" && currentValue) {
const matrice = matrici.find((m) => m.IdMatrice == currentValue);
if (matrice) {
const option = new Option(
matrice.NomeMatrice,
matrice.IdMatrice,
true,
true,
);
if (!fromFilter) $select.append(option).trigger("change");
else $select.append(option);
partMatrice[partNumber] = matrice.IdMatrice;
} else {
// Aggiusta valore non valido
if (!fromFilter) $select.val(null).trigger("change");
partMatrice[partNumber] = null;
}
} else {
$select.val(null).trigger("change", [{ skipHandler: true }]);
}
$select.on("change", function (event, data) {
if (data && data?.skipHandler) return;
const idmatrice = $(this).val();
const $row = $(this).closest("tr");
const partId = $row.data("part-id");
const partNumber = $row.find(".part-number").val();
const $saveStatus = $row.find(".save-status");
const $saveLoading = $row.find(".save-loading");
partMatrice[partNumber] = idmatrice || null;
if (partId && partId !== "new") {
$saveLoading.show();
$saveStatus.hide();
const iddatadb = $("#partsModal").data("iddatadb");
const idquotations = $("#partsModal").data("idquotations");
const endpoint = idquotations
? "save_matrice_quotation.php"
: "save_matrice.php";
const data = idquotations
? { idquotations: idquotations }
: { iddatadb: iddatadb };
$.ajax({
url: endpoint,
method: "POST",
data: JSON.stringify({
...data,
parts: [{ id: partId, idmatrice: idmatrice || null }],
}),
contentType: "application/json",
success: function (response) {
if (response.success) {
$saveLoading.hide();
$saveStatus.show();
setTimeout(() => $saveStatus.hide(), 2000);
} else {
const errorMsg = $(
'
Errore nel salvataggio della matrice: ' +
response.message +
"
",
);
$("#partsModal .modal-body").prepend(errorMsg);
setTimeout(function () {
errorMsg.fadeOut(500, function () {
$(this).remove();
});
}, 5000);
$saveLoading.hide();
}
},
error: function (xhr, status, error) {
const errorMsg = $(
'
Errore nel salvataggio della matrice: ' +
error +
" (" +
xhr.status +
")
",
);
$("#partsModal .modal-body").prepend(errorMsg);
setTimeout(function () {
errorMsg.fadeOut(500, function () {
$(this).remove();
});
}, 5000);
$saveLoading.hide();
},
});
}
});
// Mostra messaggio se non ci sono matrici
if (filteredMatrici.length === 0 && selectedMacro) {
const errorMsg = $(
'
Nessuna matrice disponibile per la MacroMatrice selezionata.
',
);
$("#partsModal .modal-body").prepend(errorMsg);
setTimeout(function () {
errorMsg.fadeOut(500, function () {
$(this).remove();
});
}, 5000);
}
// Debug per verificare inizializzazione
console.log(
"Inizializzo Select2 per partId:",
partId,
"con idmatrice:",
idmatrice,
"Macro:",
selectedMacro,
);
}
$(document).on("click", ".propagate-matrice-btn", function () {
const $row = $(this).closest("tr");
const globalVal = $("#global-matrice").val();
if (globalVal) {
$row.find(".part-matrice").val(globalVal).trigger("change");
} else {
const errorMsg = $(
'
Seleziona una matrice globale prima di propagare.
',
);
$("#partsModal .modal-body").prepend(errorMsg);
setTimeout(function () {
errorMsg.fadeOut(500, function () {
$(this).remove();
});
}, 5000);
}
});
$(document).on("click", ".propagate-all-btn", function () {
const globalVal = $("#global-matrice").val();
if (globalVal) {
$("#partsTableBody .part-matrice").val(globalVal).trigger("change");
} else {
const errorMsg = $(
'
Seleziona una matrice globale prima di propagare.
',
);
$("#partsModal .modal-body").prepend(errorMsg);
setTimeout(function () {
errorMsg.fadeOut(500, function () {
$(this).remove();
});
}, 5000);
}
});
function renumberParts() {
console.log(
"Inizio rinumera parts, numero righe:",
$("#partsTableBody tr").length,
);
const $rows = $("#partsTableBody tr");
const iddatadb = $("#partsModal").data("iddatadb");
const idquotations = $("#partsModal").data("idquotations");
const endpoint = idquotations
? "renumber_parts_quotation.php"
: "renumber_parts.php";
const data = idquotations
? { idquotations: idquotations }
: { iddatadb: iddatadb };
let newPartMatrice = {};
let partsData = $rows
.map(function (index) {
const $row = $(this);
return {
partNumber: $row.find(".part-number").val(),
partDescription: $row
.find(".part-description")
.val()
.trim(),
partId:
$row.data("part-id") === "new" ||
$row.data("part-id") === ""
? null
: $row.data("part-id"),
note: $row.data("note") || null,
dateexpiry: $row.find(".part-dateexpiry").val() || null,
};
})
.get();
console.log("Parts to save prima di rinumerazione:", partsData);
partsData.forEach((part, index) => {
const newNumber = index + 1;
newPartMatrice[newNumber] = partMatrice[part.partNumber] || null;
part.partNumber = newNumber;
});
$rows.each(function (index) {
$(this)
.find(".part-number")
.val(index + 1);
});
partMatrice = newPartMatrice;
const partsToSave = partsData.map((part, index) => ({
id: part.partId,
part_number: index + 1,
part_description: part.partDescription,
mix: part.partDescription.startsWith("Mix") ? "Y" : "N",
idmatrice: partMatrice[index + 1] || null,
note: part.note,
dateexpiry: part.dateexpiry,
}));
console.log("Parts to save inviati al server:", partsToSave);
$.ajax({
url: endpoint,
method: "POST",
data: JSON.stringify({ ...data, parts: partsToSave }),
contentType: "application/json",
success: function (response) {
console.log("Risposta server:", response);
if (response.success && response.part_ids) {
$rows.each(function (index) {
const $row = $(this);
const newPartId =
response.part_ids[index] || $row.data("part-id");
if (newPartId) {
$row.attr("data-part-id", newPartId).data(
"part-id",
newPartId,
);
}
const $saveStatus = $row.find(".save-status");
const $saveLoading = $row.find(".save-loading");
$saveLoading.hide();
$saveStatus.show();
setTimeout(() => $saveStatus.hide(), 2000);
});
$rows
.find(".part-number, .part-description")
.trigger("blur");
unsavedChanges = false;
} else {
console.error("Errore risposta server:", response.message);
const errorMsg = $(
'
Errore nella rinumerazione delle parti: ' +
(response.message || "Errore sconosciuto") +
"
",
);
$("#partsModal .modal-body").prepend(errorMsg);
setTimeout(function () {
errorMsg.fadeOut(500, function () {
$(this).remove();
});
}, 5000);
}
},
error: function (xhr, status, error) {
console.error(
"Errore AJAX rinumerazione:",
error,
xhr.responseText,
);
const errorMsg = $(
'
Errore nella rinumerazione delle parti: ' +
error +
" (" +
xhr.status +
")
",
);
$("#partsModal .modal-body").prepend(errorMsg);
setTimeout(function () {
errorMsg.fadeOut(500, function () {
$(this).remove();
});
}, 5000);
},
});
}
$(document).on("click", "#renumberPartsBtn", function (e) {
console.log("Clicked Rinumera Parti - Evento triggerato");
e.preventDefault();
renumberParts();
});
function markUnsaved() {
if (!unsavedChanges) {
unsavedChanges = true;
}
}
$(document).on(
"input change",
"#partsTableBody input, #partsTableBody select",
markUnsaved,
);
$(document).on(
"click",
".add-row-global, .add-mix-global, .add-mix-row, .remove-row, .propagate-matrice-btn, .propagate-all-btn, .note-btn",
markUnsaved,
);
// Esporta la funzione loadParts per essere usata da import_Edit2.php
window.loadParts = loadParts;
$(document).on("click", "#quotationeBtn", function () {
$("#addQuotationModal").modal("show");
if (quotations.length > 0) {
reloadQuotations();
} else {
$.ajax({
url: "load_quotations.php",
method: "GET",
success: function (response) {
quotations = response?.quotations || [];
reloadQuotations();
},
error: function (xhr, status, error) {
console.error(
"Errore AJAX caricamento quotations:",
error,
xhr.responseText,
);
let message = `
Errore nel caricamento delle quotations: ${error} (${xhr.status})
`;
$("#partsModal .modal-body").prepend(errorMsg);
setTimeout(function () {
message.fadeOut(500, function () {
$(this).remove();
});
}, 5000);
},
});
}
});
$(document).on("click", "#addQuotationBtn", function () {
const quotationId = $("#addQuotationSelect").val();
if (
quotationId &&
confirm("Confermo di collegare la quotazione al campione?")
) {
$("#addQuotationModal").modal("hide");
loadParts(null, quotationId, () => {
$("#quotationeBtn").addClass("d-none");
setTimeout(() => {
$("#partsTableBody tr").each(function () {
$(this).find(".save-loading").show();
});
let photoList = $("#photoSelector option")
.map(function () {
return this.value?.split("/")?.pop();
})
.get();
if (!photoList.length) {
let path = $("#samplePhoto")
.attr("src")
?.split("/")
?.pop();
if (path) photoList = [path];
}
$.ajax({
url: "save_parts_photo_iddatadb.php",
method: "POST",
data: JSON.stringify({
iddatadb: $("#partsModal").data("iddatadb"),
photoList,
partIds: $("#partsTableBody tr")
.map(function () {
return $(this).data("part-id");
})
.get(),
}),
success: function () {
$("#partsTableBody tr").each(function () {
let $row = $(this);
let $saveStatus = $row.find(".save-status");
let $saveLoading = $row.find(".save-loading");
$saveLoading.hide();
$saveStatus.show();
setTimeout(() => $saveStatus.hide(), 2000);
});
},
error: function (xhr, status, error) {
let message = `
Errore di salvataggio: ${error} (${xhr.status})
`;
$("#partsModal .modal-body").prepend(message);
setTimeout(function () {
message.fadeOut(500, function () {
$(this).remove();
});
}, 5000);
},
});
}, 100);
});
}
});
function reloadQuotations() {
if (quotations.length > 0) {
$("#addQuotationSelect").empty();
$("#addQuotationSelect").append(
"
",
);
quotations.forEach((quotation) => {
if (quotation?.description) {
$("#addQuotationSelect").append(
`
`,
);
}
});
$("#addQuotationSelect").select2();
}
}
});
$(document).on("change", ".propagate-date-input", function () {
const dateexpiry = $(this).val();
const iddatadb = $("#partsModal").data("iddatadb");
const idquotations = $("#partsModal").data("idquotations");
const endpoint = idquotations
? "save_parts_quotation.php"
: "save_parts.php";
const data = idquotations
? { idquotations: idquotations }
: { iddatadb: iddatadb };
const partsToSave = [];
$("#partsTableBody tr").each(function () {
const $row = $(this);
const partId = $row.data("part-id");
const partNumber = $row.find(".part-number").val();
const partDescription = $row.find(".part-description").val().trim();
const mix = partDescription.startsWith("Mix") ? "Y" : "N";
const idmatrice = $row.find(".part-matrice").val() || null;
const note = $row.data("note") || null;
partsToSave.push({
id: partId && partId !== "new" ? partId : null,
part_number: partNumber,
part_description: partDescription,
mix: mix,
idmatrice: idmatrice,
note: note,
dateexpiry: dateexpiry || null,
});
if (partId && partId !== "new") {
$row.find(".save-loading").show();
$row.find(".save-status").hide();
}
});
if (partsToSave.length > 0) {
$.ajax({
url: endpoint,
method: "POST",
data: JSON.stringify({
...data,
parts: partsToSave,
}),
contentType: "application/json",
success: function (response) {
$("#partsTableBody tr").each(function () {
const $row = $(this);
const partId = $row.data("part-id");
if (partId && partId !== "new") {
$row.find(".save-loading").hide();
$row.find(".save-status").show();
setTimeout(
() => $row.find(".save-status").hide(),
2000,
);
}
$row.find(".part-dateexpiry").val(dateexpiry);
});
if (!response.success) {
const errorMsg = $(
'
Errore nel salvataggio della data comune: ' +
response.message +
"
",
);
$("#partsModal .modal-body").prepend(errorMsg);
setTimeout(function () {
errorMsg.fadeOut(500, function () {
$(this).remove();
});
}, 5000);
}
},
error: function (xhr, status, error) {
$("#partsTableBody tr").each(function () {
const $row = $(this);
$row.find(".save-loading").hide();
});
const errorMsg = $(
'
Errore nel salvataggio della data comune: ' +
error +
" (" +
xhr.status +
")
",
);
$("#partsModal .modal-body").prepend(errorMsg);
setTimeout(function () {
errorMsg.fadeOut(500, function () {
$(this).remove();
});
}, 5000);
},
});
} else {
$("#partsTableBody tr").find(".part-dateexpiry").val(dateexpiry);
markUnsaved();
}
});
$(document).on("click", ".propagate-note-btn", function () {
const $commonNoteModal = $("#commonNoteModal");
$commonNoteModal.find(".part-note").val("");
const modalInstance = new bootstrap.Modal($commonNoteModal[0], {
backdrop: "static",
keyboard: false,
});
modalInstance.show();
});
$(document).on("click", ".save-common-note-btn", function () {
const $commonNoteModal = $("#commonNoteModal");
const note = $commonNoteModal.find(".part-note").val().trim();
const iddatadb = $("#partsModal").data("iddatadb");
const idquotations = $("#partsModal").data("idquotations");
const endpoint = idquotations
? "save_parts_quotation.php"
: "save_parts.php";
const data = idquotations
? { idquotations: idquotations }
: { iddatadb: iddatadb };
const partsToSave = [];
$("#partsTableBody tr").each(function () {
const $row = $(this);
const partId = $row.data("part-id");
const partNumber = $row.find(".part-number").val();
const partDescription = $row.find(".part-description").val().trim();
const mix = partDescription.startsWith("Mix") ? "Y" : "N";
const idmatrice = $row.find(".part-matrice").val() || null;
const dateexpiry = $row.find(".part-dateexpiry").val() || null;
partsToSave.push({
id: partId && partId !== "new" ? partId : null,
part_number: partNumber,
part_description: partDescription,
mix: mix,
idmatrice: idmatrice,
note: note || null,
dateexpiry: dateexpiry,
});
if (partId && partId !== "new") {
$row.find(".save-loading").show();
$row.find(".save-status").hide();
}
});
if (partsToSave.length > 0) {
$.ajax({
url: endpoint,
method: "POST",
data: JSON.stringify({
...data,
parts: partsToSave,
}),
contentType: "application/json",
success: function (response) {
$("#partsTableBody tr").each(function () {
const $row = $(this);
const partId = $row.data("part-id");
if (partId && partId !== "new") {
$row.find(".save-loading").hide();
$row.find(".save-status").show();
setTimeout(
() => $row.find(".save-status").hide(),
2000,
);
}
$row.data("note", note);
$row.find(".note-btn").toggleClass("has-note", !!note);
});
bootstrap.Modal.getInstance(
document.getElementById("commonNoteModal"),
).hide();
if (!response.success) {
const errorMsg = $(
'
Errore nel salvataggio della nota comune: ' +
response.message +
"
",
);
$("#partsModal .modal-body").prepend(errorMsg);
setTimeout(function () {
errorMsg.fadeOut(500, function () {
$(this).remove();
});
}, 5000);
}
},
error: function (xhr, status, error) {
$("#partsTableBody tr").each(function () {
const $row = $(this);
$row.find(".save-loading").hide();
});
const errorMsg = $(
'
Errore nel salvataggio della nota comune: ' +
error +
" (" +
xhr.status +
")
",
);
$("#partsModal .modal-body").prepend(errorMsg);
setTimeout(function () {
errorMsg.fadeOut(500, function () {
$(this).remove();
});
}, 5000);
},
});
} else {
$("#partsTableBody tr").each(function () {
const $row = $(this);
$row.data("note", note);
$row.find(".note-btn").toggleClass("has-note", !!note);
});
bootstrap.Modal.getInstance(
document.getElementById("commonNoteModal"),
).hide();
markUnsaved();
}
});
$(document).on("click", "#showHideImageBtn", function () {
let mainRow = $(this).closest(".parts-row");
let photoContainer = mainRow.find(".col-md-3");
let tableContainer = mainRow
.find("#partsTable")
.closest("div[class*='col-md']");
if (photoContainer.hasClass("d-none")) {
photoContainer.removeClass("d-none");
tableContainer.removeClass("col-md-12").addClass("col-md-9");
$(this).html(
"
",
);
} else {
photoContainer.addClass("d-none");
tableContainer.removeClass("col-md-9").addClass("col-md-12");
$(this).html(
"
",
);
}
});