added additional field in parts template e layout

This commit is contained in:
2026-03-02 14:37:20 +01:00
parent 9af0df3cca
commit c9122774b1
15 changed files with 741 additions and 196 deletions
+339 -123
View File
@@ -6,7 +6,9 @@ $(document).ready(function () {
let unsavedChanges = false;
let matrici = [];
let macroMatrici = [];
let quotations = [];
let quotations = [];
let partsExtraField = null; // {field_id, field_label} oppure null
let extraFieldOptions = []; // [{id,label}]
// --- ROW ID helpers: niente più cache impazzita di jQuery .data() ---
function getPartId($row) {
@@ -29,6 +31,148 @@ $(document).ready(function () {
return id === "new" || id === "" || id === null ? null : id;
}
function loadPartsExtraField(iddatadb, done) {
partsExtraField = null;
if (!iddatadb) return done();
$.ajax({
url: "get_parts_extra_field.php",
method: "GET",
dataType: "json",
data: { iddatadb },
success: function (res) {
partsExtraField =
res && res.success && res.field ? res.field : null;
if (
partsExtraField &&
(partsExtraField.data_type || "").toLowerCase() ===
"sceltamultipla"
) {
loadExtraFieldOptions(
String(partsExtraField.field_id),
function () {
applyExtraFieldColumn();
done();
},
);
} else {
applyExtraFieldColumn();
done();
}
},
error: function () {
partsExtraField = null;
applyExtraFieldColumn();
done();
},
});
}
function loadExtraFieldOptions(fieldId, done) {
extraFieldOptions = [];
if (!fieldId) return done();
$.ajax({
url: "get_customfield_values.php",
method: "GET",
dataType: "json",
data: { field_ids: fieldId },
success: function (res) {
const arr = res && res[fieldId] ? res[fieldId] : [];
extraFieldOptions = (arr || []).map((x) => ({
id: x.IdCustomFieldsValue,
label: x.Valore,
}));
done();
},
error: function () {
extraFieldOptions = [];
done();
},
});
}
function buildExtraFieldCellHtml($row = null) {
if (!partsExtraField) return "";
const selectedValueId = $row ? $row.data("extra-value-id") || "" : "";
// SceltaMultipla -> select + hidden value id
if (
(partsExtraField.data_type || "").toLowerCase() === "sceltamultipla"
) {
const opts = [`<option value="">Select…</option>`]
.concat(
extraFieldOptions.map(
(o) => `<option value="${o.id}">${o.label}</option>`,
),
)
.join("");
return `
<td class="extra-field-td" style="width:200px;">
<input type="hidden" class="part-extra-field-id" value="${partsExtraField.field_id}">
<input type="hidden" class="part-extra-value-id" value="">
<select class="form-control form-control-sm part-extra-select" data-selected="${selectedValueId || ""}">${opts}</select>
</td>`;
}
// Testo -> input + hidden field_id
return `
<td class="extra-field-td" style="width:200px;">
<input type="hidden" class="part-extra-field-id" value="${partsExtraField.field_id}">
<input type="text" class="form-control form-control-sm part-extra-field" data-type="${partsExtraField.data_type || ""}">
</td>`;
}
$(document).on("change", ".part-extra-select", function () {
const valId = $(this).val() || "";
const $row = $(this).closest("tr");
// salva nello stato della riga (così sopravvive ai re-render)
$row.data("extra-value-id", valId);
$(this).closest("td").find(".part-extra-value-id").val(valId);
saveRow($row);
});
function applyExtraFieldColumn() {
const $theadRow = $("#partsTable thead tr");
// 1) rimuovi header extra
$theadRow.find("th.extra-field-th").remove();
// 2) rimuovi celle extra esistenti
$("#partsTableBody tr").each(function () {
$(this).find("td.extra-field-td").remove();
});
// 3) se non c'è campo extra -> fine
if (!partsExtraField) return;
// 4) inserisci header prima di "Azioni" (ultima colonna)
$theadRow
.find("th:last")
.before(
`<th class="extra-field-th" style="width:200px;">${partsExtraField.field_label}</th>`,
);
// 5) aggiungi cella a ogni riga già presente (una sola volta)
$("#partsTableBody tr").each(function () {
const $row = $(this);
$row.find("td:last").before(buildExtraFieldCellHtml($row));
// ripristina selezione se già presente nello stato riga
const selected = ($row.data("extra-value-id") || "").toString();
if (selected) {
$row.find(".part-extra-value-id").val(selected);
$row.find(".part-extra-select").val(selected);
}
});
}
function setPartId($row, id) {
if (!id) return;
// Sincronizza TUTTO: attributo + cache jQuery + nostra cache
@@ -153,15 +297,19 @@ $(document).ready(function () {
matrici = data.value || [];
loadMacroMatrici();
initializeGlobalSelect2();
loadPhoto(iddatadb, idquotations);
loadExistingParts(iddatadb, idquotations, callback);
loadPartsExtraField(iddatadb, function () {
loadPhoto(iddatadb, idquotations);
loadExistingParts(iddatadb, idquotations, callback);
});
},
error: function (xhr, status, error) {
matrici = [];
loadMacroMatrici();
initializeGlobalSelect2();
loadPhoto(iddatadb, idquotations);
loadExistingParts(iddatadb, idquotations, callback);
loadPartsExtraField(iddatadb, function () {
loadPhoto(iddatadb, idquotations);
loadExistingParts(iddatadb, idquotations, callback);
});
const errorMsg = $(
'<div class="alert alert-danger temp-alert" role="alert">Errore nel caricamento delle matrici: ' +
error +
@@ -180,12 +328,16 @@ $(document).ready(function () {
} else {
loadMacroMatrici();
initializeGlobalSelect2();
loadPhoto(iddatadb, idquotations);
loadExistingParts(iddatadb, idquotations, callback);
loadPartsExtraField(iddatadb, function () {
loadPhoto(iddatadb, idquotations);
loadExistingParts(iddatadb, idquotations, callback);
});
}
} else {
loadPhoto(iddatadb, idquotations);
loadExistingParts(iddatadb, idquotations, callback);
loadPartsExtraField(iddatadb, function () {
loadPhoto(iddatadb, idquotations);
loadExistingParts(iddatadb, idquotations, callback);
});
}
}
@@ -514,6 +666,11 @@ $(document).ready(function () {
if (response.new_id) return response.new_id;
if (response.partId) return response.partId;
// ✅ RISPOSTA DI save_parts.php: { success:true, results:[{part_id:...}] }
if (Array.isArray(response.results) && response.results[0]) {
if (response.results[0].part_id) return response.results[0].part_id;
if (response.results[0].id) return response.results[0].id;
}
return null;
}
@@ -527,6 +684,13 @@ $(document).ready(function () {
const iddatadb = $("#partsModal").data("iddatadb");
const idquotations = $("#partsModal").data("idquotations");
const isMix = partDescription.startsWith("Mix") ? "Y" : "N";
// EXTRA FIELD (0/1)
const extra_field_id = $row.find(".part-extra-field-id").val() || null;
const extra_value_id = $row.find(".part-extra-value-id").val() || null;
const extra_value_text = $row.find(".part-extra-field").length
? ($row.find(".part-extra-field").val() || "").trim()
: null;
let partId = getPartId($row);
if (partId === "new") partId = null; // difesa extra (non dovrebbe più servire, ma sicura)
const endpoint = idquotations
@@ -559,8 +723,14 @@ $(document).ready(function () {
part_number: partNumber,
part_description: partDescription,
mix: isMix,
idmatrice: $row.find(".part-matrice").val() || null,
dateexpiry: dateexpiry || null,
note: note,
// extra custom field
extra_field_id: extra_field_id,
extra_value_id: extra_value_id,
extra_value_text: extra_value_text,
},
],
}),
@@ -591,7 +761,8 @@ $(document).ready(function () {
$saveStatus.show();
setTimeout(() => $saveStatus.hide(), 2000);
if (!$("#quotationeBtn").hasClass('d-none')) $("#quotationeBtn").addClass("d-none");
if (!$("#quotationeBtn").hasClass("d-none"))
$("#quotationeBtn").addClass("d-none");
} else {
const errorMsg = $(
'<div class="alert alert-danger temp-alert" role="alert">Errore nel salvataggio: ' +
@@ -720,8 +891,9 @@ $(document).ready(function () {
<select class="part-matrice form-control form-control-sm" style="width: 150px;"></select>
</div>
</td>
<td><input type="date" class="form-control form-control-sm part-dateexpiry" style="width: 130px;"></td>
<td>
<td><input type="date" class="form-control form-control-sm part-dateexpiry" style="width: 130px;"></td>
${partsExtraField ? buildExtraFieldCellHtml(null) : ``}
<td>
<button type="button" class="btn btn-light btn-sm note-btn" style="padding: 0.1rem 0.3rem; font-size: 0.8rem;" title="Aggiungi/Modifica nota"><i class="fas fa-file-alt"></i></button>
<button type="button" class="btn btn-warning btn-sm add-mix-row" style="padding: 0.1rem 0.3rem; font-size: 0.8rem;">M+</button>
<button type="button" class="btn btn-danger btn-sm remove-row" style="padding: 0.1rem 0.3rem; font-size: 0.8rem; display: none;"><i class="fas fa-trash fa-xs"></i></button>
@@ -1154,9 +1326,13 @@ $(document).ready(function () {
}
});
$(document).on("blur", ".part-description, .part-number", function () {
saveRow($(this).closest("tr"));
});
$(document).on(
"blur",
".part-description, .part-number, .part-extra-field",
function () {
saveRow($(this).closest("tr"));
},
);
function loadExistingParts(iddatadb, idquotations, callback = null) {
const endpoint = idquotations
@@ -1208,8 +1384,9 @@ $(document).ready(function () {
<select class="part-matrice form-control form-control-sm" style="width: 150px;" ${idquotations && !part.idmatrice ? "disabled" : ""}></select>
</div>
</td>
<td><input type="date" class="form-control form-control-sm part-dateexpiry" value="${part.dateexpiry || ""}" style="width: 130px;"></td>
<td>
<td><input type="date" class="form-control form-control-sm part-dateexpiry" value="${part.dateexpiry || ""}" style="width: 130px;"></td>
${partsExtraField ? buildExtraFieldCellHtml() : ``}
<td>
<button type="button" class="btn btn-light btn-sm note-btn ${part.note ? "has-note" : ""}" style="padding: 0.1rem 0.3rem; font-size: 0.8rem;" title="Aggiungi/Modifica nota"><i class="fas fa-file-alt"></i></button>
<button type="button" class="btn btn-warning btn-sm add-mix-row" style="padding: 0.1rem 0.3rem; font-size: 0.8rem;">M+</button>
<button type="button" class="btn btn-danger btn-sm remove-row" style="padding: 0.1rem 0.3rem; font-size: 0.8rem;"><i class="fas fa-trash fa-xs"></i></button>
@@ -1218,6 +1395,20 @@ $(document).ready(function () {
</td>
</tr>`;
$("#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").val(vid);
}
const $select = $("#partsTableBody").find(
`tr[data-part-id="${part.id}"] .part-matrice`,
);
@@ -1236,11 +1427,11 @@ $(document).ready(function () {
});
} else {
addNewRow(1, false); // Riga iniziale normale
$("#quotationeBtn").removeClass("d-none");
$("#quotationeBtn").removeClass("d-none");
}
updateRowButtons();
if (callback) callback();
if (callback) callback();
},
error: function (xhr, status, error) {
const errorMsg = $(
@@ -1345,7 +1536,7 @@ $(document).ready(function () {
partId,
currentValue,
selectedMacro,
true
true,
);
});
});
@@ -1422,7 +1613,7 @@ $(document).ready(function () {
partId,
idmatrice,
selectedMacro = null,
fromFilter = false
fromFilter = false,
) {
if (typeof $.fn.select2 === "undefined") {
$select.replaceWith(
@@ -1493,23 +1684,23 @@ $(document).ready(function () {
true,
);
if (!fromFilter) $select.append(option).trigger("change");
else $select.append(option);
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");
if (!fromFilter) $select.val(null).trigger("change");
partMatrice[partNumber] = null;
}
} else {
$select.val(null).trigger("change", [{ skipHandler: true }]);
}
$select.val(null).trigger("change", [{ skipHandler: true }]);
}
$select.on("change", function (event, data) {
if (data && data?.skipHandler) return;
if (data && data?.skipHandler) return;
const idmatrice = $(this).val();
const $row = $(this).closest("tr");
const partId = $row.data("part-id");
@@ -1794,121 +1985,140 @@ $(document).ready(function () {
// Esporta la funzione loadParts per essere usata da import_Edit2.php
window.loadParts = loadParts;
$(document).on("click", "#quotationeBtn", function () {
$("#addQuotationModal").modal("show");
$(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 || [];
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 = `
reloadQuotations();
},
error: function (xhr, status, error) {
console.error(
"Errore AJAX caricamento quotations:",
error,
xhr.responseText,
);
let message = `
<div class="alert alert-danger temp-alert" role="alert">
Errore nel caricamento delle quotations: ${error} (${xhr.status})
</div>
`;
$("#partsModal .modal-body").prepend(errorMsg);
$("#partsModal .modal-body").prepend(errorMsg);
setTimeout(function () {
message.fadeOut(500, function () {
$(this).remove();
});
}, 5000);
}
});
}
});
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");
$(document).on("click", "#addQuotationBtn", function () {
const quotationId = $("#addQuotationSelect").val();
loadParts(null, quotationId, () => {
$("#quotationeBtn").addClass("d-none");
if (
quotationId &&
confirm("Confermo di collegare la quotazione al campione?")
) {
$("#addQuotationModal").modal("hide");
setTimeout(() => {
$("#partsTableBody tr").each(function () {
$(this).find(".save-loading").show();
});
loadParts(null, quotationId, () => {
$("#quotationeBtn").addClass("d-none");
let photoList = $('#photoSelector option').map(function () {
return this.value?.split('/')?.pop();
}).get();
setTimeout(() => {
$("#partsTableBody tr").each(function () {
$(this).find(".save-loading").show();
});
if (!photoList.length) {
let path = $('#samplePhoto').attr("src")?.split('/')?.pop();
if (path) photoList = [path];
}
let photoList = $("#photoSelector option")
.map(function () {
return this.value?.split("/")?.pop();
})
.get();
$.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");
if (!photoList.length) {
let path = $("#samplePhoto")
.attr("src")
?.split("/")
?.pop();
$saveLoading.hide();
$saveStatus.show();
if (path) photoList = [path];
}
setTimeout(() => $saveStatus.hide(), 2000);
});
}, error: function (xhr, status, error) {
let message = `
$.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 = `
<div class="alert alert-danger temp-alert" role="alert">
Errore di salvataggio: ${error} (${xhr.status})
</div>
`;
$("#partsModal .modal-body").prepend(message);
$("#partsModal .modal-body").prepend(message);
setTimeout(function () {
message.fadeOut(500, function () {
$(this).remove();
});
}, 5000);
}
});
}, 100);
});
}
});
setTimeout(function () {
message.fadeOut(500, function () {
$(this).remove();
});
}, 5000);
},
});
}, 100);
});
}
});
function reloadQuotations() {
if (quotations.length > 0) {
$("#addQuotationSelect").empty();
function reloadQuotations() {
if (quotations.length > 0) {
$("#addQuotationSelect").empty();
$("#addQuotationSelect").append("<option value=''>Seleziona Quotation</option>");
quotations.forEach(quotation => {
if (quotation?.description) {
$("#addQuotationSelect").append(`<option value='${quotation?.id}'>${quotation?.description}</option>`);
}
});
$("#addQuotationSelect").append(
"<option value=''>Seleziona Quotation</option>",
);
$("#addQuotationSelect").select2();
}
}
quotations.forEach((quotation) => {
if (quotation?.description) {
$("#addQuotationSelect").append(
`<option value='${quotation?.id}'>${quotation?.description}</option>`,
);
}
});
$("#addQuotationSelect").select2();
}
}
});
$(document).on("change", ".propagate-date-input", function () {
@@ -2136,15 +2346,21 @@ $(document).on("click", ".save-common-note-btn", function () {
$(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']");
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("<i class='fas fa-eye-slash' style='font-size: 0.8rem;'></i><i class='fas fa-image ms-1' style='font-size: 0.8rem;'></i>");
$(this).html(
"<i class='fas fa-eye-slash' style='font-size: 0.8rem;'></i><i class='fas fa-image ms-1' style='font-size: 0.8rem;'></i>",
);
} else {
photoContainer.addClass("d-none");
tableContainer.removeClass("col-md-9").addClass("col-md-12");
$(this).html("<i class='fas fa-eye' style='font-size: 0.8rem;'></i><i class='fas fa-image ms-1' style='font-size: 0.8rem;'></i>");
$(this).html(
"<i class='fas fa-eye' style='font-size: 0.8rem;'></i><i class='fas fa-image ms-1' style='font-size: 0.8rem;'></i>",
);
}
});
});