Compare commits

..

1 Commits

Author SHA1 Message Date
solocla 9969cc9edc partstable fixed matrici dropdown 2025-10-28 09:31:13 +01:00
21 changed files with 239 additions and 3396 deletions
+119 -244
View File
@@ -12,16 +12,12 @@ $(document).ready(function () {
let photoAnnotations = {};
let partColors = {};
let partSizes = {}; // memorizza la dimensione specifica per parte
let selectedPartNumber = null;
let unsavedChanges = false;
let fabricCanvas = null;
let descriptionTextbox = null;
let markerObjects = {};
let nextMarkerId = 1;
let partsListData = [];
// DIMENSIONE GLOBALE MARKER
let globalMarkerSize = 16;
// ===================
// MODAL INITIALIZATION
@@ -33,8 +29,6 @@ $(document).ready(function () {
trfHeader,
});
$("#annotationsModal").attr("data-iddatadb", iddatadb);
if (!iddatadb && !idquotations) {
const errorMsg = $(
'<div class="alert alert-danger temp-alert" role="alert">Errore: ID TRF mancante. Impossibile inizializzare il modale delle annotazioni.</div>',
@@ -73,9 +67,6 @@ $(document).ready(function () {
});
}
modal.show();
// Inizializza slider dimensione marker
$("#markerSizeSlider").val(globalMarkerSize);
$("#markerSizeValue").text(globalMarkerSize + "px");
// Debug: Verifica presenza elementi DOM
console.log(
@@ -137,7 +128,6 @@ $(document).ready(function () {
}
descriptionTextbox = null;
markerObjects = {};
globalMarkerSize = 16;
$("#photoSelectorContainerAnnotations").empty().hide();
$("#samplePhotoAnnotations").attr("src", "");
$("#partsListAnnotations").empty();
@@ -154,30 +144,6 @@ $(document).ready(function () {
$(":focus").blur();
});
// SLIDER DIMENSIONE MARKER
$(document)
.off("input", "#markerSizeSlider")
.on("input", "#markerSizeSlider", function () {
globalMarkerSize = parseInt($(this).val());
$("#markerSizeValue").text(globalMarkerSize + "px");
partsListData.forEach(function (part) {
if (part && part.part_number != null) {
partSizes[part.part_number] = globalMarkerSize;
}
});
$("#partsListAnnotations .marker-size-slider").val(
globalMarkerSize,
);
$("#partsListAnnotations .marker-size-value").text(
globalMarkerSize + "px",
);
updateMarkers();
markUnsaved();
});
// ===================
// PHOTO LOADERS
// ===================
@@ -381,13 +347,22 @@ $(document).ready(function () {
const partColor =
partColors[selectedPartNumber] || "#ff0000";
photoAnnotations[currentPhoto].markers.push({
id: nextMarkerId++,
partNumber: selectedPartNumber,
x,
y,
color: partColor,
});
const existingMarker = photoAnnotations[
currentPhoto
].markers.find((m) => m.partNumber == selectedPartNumber);
if (existingMarker) {
existingMarker.x = x;
existingMarker.y = y;
existingMarker.color = partColor;
} else {
photoAnnotations[currentPhoto].markers.push({
partNumber: selectedPartNumber,
x,
y,
color: partColor,
});
}
console.log("Marker aggiunto/spostato:", {
partNumber: selectedPartNumber,
@@ -397,6 +372,8 @@ $(document).ready(function () {
});
updateMarkers();
markUnsaved();
selectedPartNumber = null;
$("#partsListAnnotations li").removeClass("active");
});
fabricCanvas.upperCanvasEl.focus();
@@ -488,89 +465,113 @@ $(document).ready(function () {
);
// ===================
// TORNA AL MODALE PARTI (modal_partsTable.php)
// BACK TO PARTS MODAL
// ===================
$(document)
.off("click.backToParts", "#backToPartsBtnAnnotations")
.on("click.backToParts", "#backToPartsBtnAnnotations", function (e) {
e.preventDefault();
e.stopPropagation();
console.log(
"Evento click su #backToPartsBtnAnnotations, ID elemento:",
$(this).attr("id"),
);
if (!$("#backToPartsBtnAnnotations").length) {
console.error(
"Pulsante #backToPartsBtnAnnotations non trovato nel DOM.",
);
const errorMsg = $(
'<div class="alert alert-danger temp-alert" role="alert">Errore: Pulsante #backToPartsBtnAnnotations non trovato nel DOM.</div>',
);
$("#annotationsModal .modal-body").prepend(errorMsg);
setTimeout(function () {
errorMsg.fadeOut(500, function () {
$(this).remove();
});
}, 5000);
return;
}
// Controlla modifiche non salvate
if (
unsavedChanges &&
!confirm(
"Hai modifiche non salvate. Vuoi davvero tornare al modale delle parti?",
)
) {
console.log(
"Tornare al modale delle parti annullato a causa di modifiche non salvate.",
);
return;
}
// Chiudi annotationsModal
const annotationsModalElement =
document.getElementById("annotationsModal");
const annotationsModal = bootstrap.Modal.getInstance(
annotationsModalElement,
);
if (annotationsModal) {
annotationsModal.hide();
} else {
console.error(
"Impossibile trovare l'istanza del modale #annotationsModal.",
);
}
// Apri partsModal
const iddatadb = $("#annotationsModal").data("iddatadb");
const idquotations = $("#annotationsModal").data("idquotations");
const trfHeader = $("#trfHeaderAnnotations").text();
// CHIUDI MODALE ANNOTAZIONI IN MODO SICURO
const annotationsModalElement =
document.getElementById("annotationsModal");
if (annotationsModalElement) {
const modalInstance = bootstrap.Modal.getInstance(
annotationsModalElement,
);
if (modalInstance) {
modalInstance.hide();
} else {
$(annotationsModalElement).modal("hide"); // fallback jQuery
}
}
console.log("Torno a modal_partsTable.php con:", {
console.log("Apertura partsModal con:", {
iddatadb,
idquotations,
trfHeader,
});
// CARICA E APRI MODALE PARTI
$.get(
"modal_partsTable.php",
{
iddatadb: iddatadb || "",
idquotations: idquotations || "",
trfHeader: trfHeader,
},
function (data) {
// Rimuovi vecchio modale (con ID corretto)
$("#partsTableModal").remove();
$(".modal-backdrop").remove();
// Aggiungi nuovo modale
$("body").append(data);
// Apri con Bootstrap 5
const partsModalElement =
document.getElementById("partsTableModal");
if (partsModalElement) {
const modal = new bootstrap.Modal(partsModalElement, {
backdrop: true,
keyboard: true,
focus: true,
});
modal.show();
} else {
let iddatadb =
$("#annotationsModal").attr("data-iddatadb");
$(
"button.parts-btn[data-iddatadb='" +
iddatadb +
"']",
).trigger("click");
}
},
).fail(function (xhr) {
console.error("Errore caricamento modale parti:", xhr);
alert(
"Errore 404: modal_partsTable.php non trovato o errore server.",
const partsModalElement = document.getElementById("partsModal");
if (!partsModalElement) {
console.error("Elemento #partsModal non trovato nel DOM.");
const errorMsg = $(
'<div class="alert alert-danger temp-alert" role="alert">Errore: Il modale delle parti non è presente nel DOM.</div>',
);
});
$("#annotationsModal .modal-body").prepend(errorMsg);
setTimeout(function () {
errorMsg.fadeOut(500, function () {
$(this).remove();
});
}, 5000);
return;
}
let partsModal = bootstrap.Modal.getInstance(partsModalElement);
if (!partsModal) {
partsModal = new bootstrap.Modal(partsModalElement, {
backdrop: true,
keyboard: true,
focus: true,
});
}
// Inizializza il modale delle parti
if (typeof window.initPartsModal === "function") {
window.initPartsModal(iddatadb, idquotations, trfHeader);
partsModal.show();
console.log("partsModal aperto con successo.");
} else {
console.error("Funzione initPartsModal non definita.");
const errorMsg = $(
'<div class="alert alert-danger temp-alert" role="alert">Errore: Funzione initPartsModal non trovata.</div>',
);
$("#annotationsModal .modal-body").prepend(errorMsg);
setTimeout(function () {
errorMsg.fadeOut(500, function () {
$(this).remove();
});
}, 5000);
}
});
// ===================
// PARTS LIST
// ===================
@@ -632,19 +633,12 @@ $(document).ready(function () {
)
.join("");
const listItem = `
<li class="list-group-item" data-part-number="${partNumber}">
<span class="part-info" style="cursor: pointer;">${partNumber} - ${partDescription}</span>
<div class="color-picker-container" style="display:inline-block; position: relative; overflow: visible; z-index: 2000; margin-left:5px;">
<div class="color-option selected-color" style="background-color: ${partColor}; width: 20px; height: 20px; display: inline-block; cursor: pointer; pointer-events: auto;"></div>
<div class="color-picker" style="display: none; position: absolute; right: 0; z-index: 2000; background: #fff; border: 1px solid #ccc; padding: 5px;">${colorOptions}</div>
</div>
<div style="display:inline-flex; align-items:center; gap:4px; margin-left:5px;">
<input type="range" class="marker-size-slider"
min="12" max="48" step="2"
value="${partSizes[partNumber] || globalMarkerSize}"
style="width:70px;">
<span class="marker-size-value" style="font-size:0.8rem;">${partSizes[partNumber] || globalMarkerSize}px</span>
</div>
<li class="list-group-item" data-part-number="${partNumber}">
<span class="part-info" style="cursor: pointer;">${partNumber} - ${partDescription}</span>
<div class="color-picker-container" style="display: inline-block; position: relative; overflow: visible; z-index: 2000;">
<div class="color-option selected-color" style="background-color: ${partColor}; width: 20px; height: 20px; display: inline-block; margin-left: 5px; cursor: pointer; pointer-events: auto;"></div>
<div class="color-picker" style="display: none; position: absolute; right: 0; z-index: 2000; background: #fff; border: 1px solid #ccc; padding: 5px;">${colorOptions}</div>
</div>
</li>`;
partsListElement.append(listItem);
}
@@ -669,21 +663,8 @@ $(document).ready(function () {
);
return;
}
const $listItem = $(this);
const partNumber = $listItem.data("part-number");
if (
selectedPartNumber == partNumber &&
$listItem.hasClass("active")
) {
selectedPartNumber = null;
$listItem.removeClass("active");
return;
}
selectedPartNumber = partNumber;
selectedPartNumber = $listItem.data("part-number");
$listItem.addClass("active").siblings().removeClass("active");
console.log(
"Parte selezionata tramite riga:",
@@ -706,88 +687,30 @@ $(document).ready(function () {
$picker.toggle();
});
// === Gestione cambio colore ===
partsListElement
.off("click.colorOption")
.on("click.colorOption", ".color-option", function (e) {
e.stopPropagation();
e.preventDefault();
const $this = $(this);
const color = $this.data("color");
const $listItem = $this.closest("li");
const partNumber = $listItem.data("part-number");
console.log(
"Cliccato .color-option, colore:",
color,
"per parte:",
partNumber,
);
// Salva il nuovo colore
partColors[partNumber] = color;
// Aggiorna il colore visivo nel selettore
$listItem
.find(".selected-color")
.css("background-color", color);
let currentPhoto = $("#samplePhotoAnnotations").attr("src");
let annotations = photoAnnotations[currentPhoto];
if (annotations) {
annotations.markers.forEach(function (m) {
if (m.partNumber == partNumber) {
m.color = color;
let group = markerObjects[m.id];
if (group) {
let circle = group.item(0);
circle.set("fill", color);
circle.set("stroke", color);
}
}
});
fabricCanvas.renderAll();
}
// Chiudi la palette e aggiorna canvas
$this.closest(".color-picker").hide();
updateMarkers();
markUnsaved();
});
// === Slider locale per dimensione marker ===
partsListElement
.off("input.localMarkerSize")
.on("input.localMarkerSize", ".marker-size-slider", function (e) {
e.stopPropagation();
e.preventDefault();
// Riferimenti alla parte e valore scelto
const $slider = $(this);
const partNumber = $slider.closest("li").data("part-number");
const newSize = parseInt($slider.val());
// Aggiorna il valore visivo accanto allo slider
$slider.siblings(".marker-size-value").text(newSize + "px");
// Memorizza la nuova dimensione per quella parte
partSizes[partNumber] = newSize;
// Aggiorna i marker sul canvas
updateMarkers();
// Segnala modifiche non salvate
markUnsaved();
console.log(
`Dimensione marker aggiornata per parte ${partNumber}: ${newSize}px`,
);
});
$(document)
.off("click.colorPicker")
.on("click.colorPicker", function (e) {
@@ -798,48 +721,6 @@ $(document).ready(function () {
});
}
function enableDragDropPartsList() {
const list = $("#partsListAnnotations");
if (!list.length || typeof $.ui === "undefined" || !$.ui.sortable) {
console.warn("jQuery UI o .sortable non disponibile. Ritento...");
setTimeout(enableDragDropPartsList, 100);
return;
}
if (list.hasClass("ui-sortable")) {
list.sortable("destroy"); // evita duplicati
}
list.sortable({
items: "li.list-group-item",
placeholder: "list-group-item placeholder",
axis: "y",
containment: "parent",
tolerance: "pointer",
start: function (e, ui) {
ui.item.addClass("dragging");
},
stop: function (e, ui) {
ui.item.removeClass("dragging");
const newOrder = [];
list.find("li").each(function () {
const partNumber = $(this).data("part-number");
const part = partsListData.find(
(p) => p.part_number == partNumber,
);
if (part) newOrder.push(part);
});
partsListData = newOrder;
console.log(
"Ordine parti aggiornato:",
partsListData.map((p) => p.part_number),
);
markUnsaved();
updateMarkers();
},
});
}
// Delegazione evento per il checkbox
$(document)
.off("change.showMix", "#showMixPartsAnnotations")
@@ -872,7 +753,6 @@ $(document).ready(function () {
}
updatePartsList();
updateMarkers();
setTimeout(enableDragDropPartsList, 50);
if (
photoAnnotations[$("#samplePhotoAnnotations").attr("src")]
?.hasDescriptions
@@ -980,9 +860,9 @@ $(document).ready(function () {
"updateMarkers chiamato, markerObjects:",
Object.keys(markerObjects),
);
for (let markerId in markerObjects) {
fabricCanvas.remove(markerObjects[markerId]);
delete markerObjects[markerId];
for (let partNumber in markerObjects) {
fabricCanvas.remove(markerObjects[partNumber]);
delete markerObjects[partNumber];
}
markerObjects = {};
@@ -1012,10 +892,8 @@ $(document).ready(function () {
return;
}
const size = partSizes[marker.partNumber] || globalMarkerSize;
const radius = size / 2;
const fontSize = Math.max(10, Math.round(size * 0.65));
const radius = 12;
const fontSize = 16;
const markerColor =
marker.color || partColors[marker.partNumber] || "#ff0000";
@@ -1063,9 +941,6 @@ $(document).ready(function () {
lockRotation: true,
});
group.markerId = marker.id;
group.partNumber = marker.partNumber;
group.on("moving", function () {
marker.x = this.left / photoData.scale;
marker.y = this.top / photoData.scale;
@@ -1078,7 +953,7 @@ $(document).ready(function () {
});
fabricCanvas.add(group);
markerObjects[marker.id] = group;
markerObjects[marker.partNumber] = group;
});
fabricCanvas.renderAll();
@@ -1138,7 +1013,7 @@ $(document).ready(function () {
scaleY: 1,
backgroundColor: "transparent",
fontFamily: "Arial",
fontSize: Math.max(16, Math.round(globalMarkerSize * 0.8)),
fontSize: 24,
fill: "#000000",
padding: 10,
editable: false,
@@ -1232,9 +1107,9 @@ $(document).ready(function () {
}
}
for (let markerId in markerObjects) {
fabricCanvas.remove(markerObjects[markerId]);
delete markerObjects[markerId];
for (let partNumber in markerObjects) {
fabricCanvas.remove(markerObjects[partNumber]);
delete markerObjects[partNumber];
}
markerObjects = {};
@@ -1269,9 +1144,9 @@ $(document).ready(function () {
photoAnnotations[currentPhoto].markers.length > 0
) {
const lastMarker = photoAnnotations[currentPhoto].markers.pop();
if (markerObjects[lastMarker.id]) {
fabricCanvas.remove(markerObjects[lastMarker.id]);
delete markerObjects[lastMarker.id];
if (markerObjects[lastMarker.partNumber]) {
fabricCanvas.remove(markerObjects[lastMarker.partNumber]);
delete markerObjects[lastMarker.partNumber];
fabricCanvas.renderAll();
}
console.log("Ultimo marker rimosso:", lastMarker);
-77
View File
@@ -1,77 +0,0 @@
<?php
header('Content-Type: application/json');
require_once(__DIR__ . '/include/headscript.php');
$db = DBHandlerSelect::getInstance();
$pdo = $db->getConnection();
$input = json_decode(file_get_contents('php://input'), true);
$templateId = (int)($input['template_id'] ?? 0);
if ($templateId <= 0) {
echo json_encode(['success' => false, 'message' => 'Invalid template_id']);
exit;
}
// If already exists, do nothing
$stmt = $pdo->prepare("SELECT COUNT(*) FROM template_fixed_mapping WHERE template_id = ?");
$stmt->execute([$templateId]);
if ((int)$stmt->fetchColumn() > 0) {
echo json_encode(['success' => true, 'created' => 0, 'message' => 'Fixed fields already exist']);
exit;
}
/**
* FIXED FIELDS STANDARD (no UI selection)
* is_manual always 1
* is_visible_import default 1
*/
$fixedFields = [
// fixed_field_key, data_type
['ClienteResponsabile', 'INT'],
['MoltiplicatorePrezzo', 'INT'],
['AnagraficaCertestObject', 'INT'],
['AnagraficaCertestService', 'INT'],
['ConsegnaRichiesta', 'DATE'],
];
try {
$pdo->beginTransaction();
$ins = $pdo->prepare("
INSERT INTO template_fixed_mapping
(template_id, fixed_field_key, is_manual, data_type, is_required, default_value, is_visible_import)
VALUES
(:template_id, :fixed_field_key, 1, :data_type, 1, NULL, 1)
");
foreach ($fixedFields as $f) {
$ins->execute([
':template_id' => $templateId,
':fixed_field_key' => $f[0],
':data_type' => $f[1],
]);
}
$pdo->commit();
// Return rows (for client render)
$stmt = $pdo->prepare("
SELECT id, template_id, fixed_field_key, is_manual, data_type, is_required, default_value, is_visible_import
FROM template_fixed_mapping
WHERE template_id = ?
ORDER BY id ASC
");
$stmt->execute([$templateId]);
$rows = $stmt->fetchAll(PDO::FETCH_ASSOC);
echo json_encode(['success' => true, 'created' => count($rows), 'rows' => $rows]);
} catch (Exception $e) {
if ($pdo->inTransaction()) $pdo->rollBack();
echo json_encode(['success' => false, 'message' => $e->getMessage()]);
}
File diff suppressed because one or more lines are too long
-15
View File
@@ -12,18 +12,3 @@
2025-08-26 16:49:24 - Risposta non JSON valida: <?xml version="1.0" encoding="utf-8"?><edmx:Edmx Version="4.0" xmlns:edmx="http://docs.oasis-open.org/odata/ns/edmx"><edmx:DataServices><Schema Namespace="DevExpress.ExpressApp.SystemModule" xmlns="http://docs.oasis-open.org/odata/ns/edm"><EntityType Name="DashboardViewItemDescriptor"><Key><PropertyRef Name="ViewId" /></Key><Property Name="ViewId" Type="Edm.String" Nullable="false" /></EntityType><EntityType Name="DashboardOrganizationItem" BaseType="DevExpress.ExpressApp.NonPersistentLiteObject" Abstract="true"><Property Name="Visibility" Type="DevExpress.ExpressApp.Editors.ViewItemVisibility" Nullable="false" /></EntityType><EntityType Name="DashboardOrganizationItem_1OfIModelDashboardViewItem" BaseType="DevExpress.ExpressApp.SystemModule.DashboardOrganizationItem" Abstract="true" /><EntityType Name="ViewDashboardOrganizationItem" BaseType="DevExpress.ExpressApp.SystemModule.DashboardOrganizationItem_1OfIModelDashboardViewItem"><Property Name="ObjectType" Type="System.Type" /><Proper
2025-08-26 16:50:23 - Risposta non JSON valida: <?xml version="1.0" encoding="utf-8"?><edmx:Edmx Version="4.0" xmlns:edmx="http://docs.oasis-open.org/odata/ns/edmx"><edmx:DataServices><Schema Namespace="DevExpress.ExpressApp.SystemModule" xmlns="http://docs.oasis-open.org/odata/ns/edm"><EntityType Name="DashboardViewItemDescriptor"><Key><PropertyRef Name="ViewId" /></Key><Property Name="ViewId" Type="Edm.String" Nullable="false" /></EntityType><EntityType Name="DashboardOrganizationItem" BaseType="DevExpress.ExpressApp.NonPersistentLiteObject" Abstract="true"><Property Name="Visibility" Type="DevExpress.ExpressApp.Editors.ViewItemVisibility" Nullable="false" /></EntityType><EntityType Name="DashboardOrganizationItem_1OfIModelDashboardViewItem" BaseType="DevExpress.ExpressApp.SystemModule.DashboardOrganizationItem" Abstract="true" /><EntityType Name="ViewDashboardOrganizationItem" BaseType="DevExpress.ExpressApp.SystemModule.DashboardOrganizationItem_1OfIModelDashboardViewItem"><Property Name="ObjectType" Type="System.Type" /><Proper
2025-09-08 08:39:17 - Risposta non JSON valida: <?xml version="1.0" encoding="utf-8"?><edmx:Edmx Version="4.0" xmlns:edmx="http://docs.oasis-open.org/odata/ns/edmx"><edmx:DataServices><Schema Namespace="DevExpress.ExpressApp.SystemModule" xmlns="http://docs.oasis-open.org/odata/ns/edm"><EntityType Name="DashboardViewItemDescriptor"><Key><PropertyRef Name="ViewId" /></Key><Property Name="ViewId" Type="Edm.String" Nullable="false" /></EntityType><EntityType Name="DashboardOrganizationItem" BaseType="DevExpress.ExpressApp.NonPersistentLiteObject" Abstract="true"><Property Name="Visibility" Type="DevExpress.ExpressApp.Editors.ViewItemVisibility" Nullable="false" /></EntityType><EntityType Name="DashboardOrganizationItem_1OfIModelDashboardViewItem" BaseType="DevExpress.ExpressApp.SystemModule.DashboardOrganizationItem" Abstract="true" /><EntityType Name="ViewDashboardOrganizationItem" BaseType="DevExpress.ExpressApp.SystemModule.DashboardOrganizationItem_1OfIModelDashboardViewItem"><Property Name="ObjectType" Type="System.Type" /><Proper
2026-01-27 15:33:53 - Errore nel recupero dati: HTTP 404, Risposta:
2026-01-27 15:34:06 - Errore nel recupero dati: HTTP 404, Risposta:
2026-01-27 15:34:10 - Errore nel recupero dati: HTTP 404, Risposta:
2026-01-27 15:35:13 - Errore nel recupero dati: HTTP 404, Risposta:
2026-01-29 14:33:38 [ClienteResponsabile] Errore nel recupero dati: HTTP 404, Risposta:
2026-01-29 14:33:39 [MoltiplicatorePrezzo] Autenticazione fallita: HTTP 400, Errore cURL: , Risposta: {"title":"Bad Request","status":400,"detail":"Cannot persist the object. It was modified or deleted (purged) by another application.","instance":"POST /api/authentication/authenticate","errorCode":"96bfc1252b"}
2026-01-29 14:34:04 [ClienteResponsabile] Errore nel recupero dati: HTTP 404, Risposta:
2026-01-29 14:37:29 [ClienteResponsabile] Errore nel recupero dati: HTTP 404, Risposta:
2026-01-29 14:41:55 [ClienteResponsabile] Errore nel recupero dati: HTTP 404, Risposta:
2026-01-29 14:42:03 [ClienteResponsabile] Errore nel recupero dati: HTTP 404, Risposta:
2026-01-29 14:42:52 [ClienteResponsabile] Errore nel recupero dati: HTTP 404, Risposta:
2026-01-29 14:43:00 [ClienteResponsabile] Errore nel recupero dati: HTTP 404, Risposta:
2026-01-30 10:50:43 [AnagraficaCertestService] Autenticazione fallita: HTTP 400, Errore cURL: , Risposta: {"title":"Bad Request","status":400,"detail":"Cannot persist the object. It was modified or deleted (purged) by another application.","instance":"POST /api/authentication/authenticate","errorCode":"96bfc1252b"}
2026-01-30 10:50:43 [MoltiplicatorePrezzo] Autenticazione fallita: HTTP 400, Errore cURL: , Risposta: {"title":"Bad Request","status":400,"detail":"Cannot persist the object. It was modified or deleted (purged) by another application.","instance":"POST /api/authentication/authenticate","errorCode":"96bfc1252b"}
2026-01-30 11:09:22 [MoltiplicatorePrezzo] Autenticazione fallita: HTTP 400, Errore cURL: , Risposta: {"title":"Bad Request","status":400,"detail":"Cannot persist the object. It was modified or deleted (purged) by another application.","instance":"POST /api/authentication/authenticate","errorCode":"96bfc1252b"}
@@ -1,40 +0,0 @@
<?php
require_once dirname(__DIR__, 2) . '/vendor/autoload.php';
require_once dirname(__FILE__) . '/class/VisualLimsApiClient.class.php';
header('Content-Type: application/json');
ini_set('display_errors', '0');
error_reporting(E_ALL);
try {
$api = VisualLimsApiClient::getInstance();
// Endpoint: GET api/odata/AnagraficaCertestObject
$endpoint = 'AnagraficaCertestObject';
// Opzionale: parametri OData ($top, $filter, $orderby, ecc.)
$options = []; // es: ['$top' => 100]
// Debug: salva URL usata
$base_url = 'https://93.43.5.102/limsapi/api/odata/';
$query = http_build_query($options);
$full_url = $base_url . $endpoint . ($query ? '?' . $query : '');
file_put_contents(__DIR__ . '/last_url.txt', $full_url . PHP_EOL, FILE_APPEND);
// Chiamata API
$data = $api->get($endpoint, $options);
// Salva il JSON in locale
file_put_contents(__DIR__ . '/anagrafica_certest_object_response.json', json_encode($data, JSON_PRETTY_PRINT));
echo json_encode($data);
} catch (Exception $e) {
file_put_contents(
__DIR__ . '/error_log.txt',
date('Y-m-d H:i:s') . ' - ' . $e->getMessage() . PHP_EOL,
FILE_APPEND
);
http_response_code(500);
echo json_encode(['error' => $e->getMessage()]);
}
@@ -1,40 +0,0 @@
<?php
require_once dirname(__DIR__, 2) . '/vendor/autoload.php';
require_once dirname(__FILE__) . '/class/VisualLimsApiClient.class.php';
header('Content-Type: application/json');
ini_set('display_errors', '0');
error_reporting(E_ALL);
try {
$api = VisualLimsApiClient::getInstance();
// Endpoint: GET api/odata/AnagraficaCertestService
$endpoint = 'AnagraficaCertestService';
// Opzionale: parametri OData ($top, $filter, $orderby, ecc.)
$options = []; // es: ['$top' => 100]
// Debug: salva URL usata
$base_url = 'https://93.43.5.102/limsapi/api/odata/';
$query = http_build_query($options);
$full_url = $base_url . $endpoint . ($query ? '?' . $query : '');
file_put_contents(__DIR__ . '/last_url.txt', $full_url . PHP_EOL, FILE_APPEND);
// Chiamata API
$data = $api->get($endpoint, $options);
// Salva il JSON in locale
file_put_contents(__DIR__ . '/anagrafica_certest_service_response.json', json_encode($data, JSON_PRETTY_PRINT));
echo json_encode($data);
} catch (Exception $e) {
file_put_contents(
__DIR__ . '/error_log.txt',
date('Y-m-d H:i:s') . ' - ' . $e->getMessage() . PHP_EOL,
FILE_APPEND
);
http_response_code(500);
echo json_encode(['error' => $e->getMessage()]);
}
@@ -1,40 +0,0 @@
<?php
require_once dirname(__DIR__, 2) . '/vendor/autoload.php';
require_once dirname(__FILE__) . '/class/VisualLimsApiClient.class.php';
header('Content-Type: application/json');
ini_set('display_errors', '0');
error_reporting(E_ALL);
try {
$idCliente = isset($_GET['id_cliente']) ? (int)$_GET['id_cliente'] : 0;
if ($idCliente <= 0) {
http_response_code(400);
echo json_encode(['error' => 'Missing or invalid id_cliente']);
exit;
}
$api = VisualLimsApiClient::getInstance();
// Build endpoint with OData $expand WITHOUT encoding '$' as %24
$endpoint = "Cliente($idCliente)?\$expand=Responsabili";
// Debug URL (real final URL)
$base_url = 'https://93.43.5.102/limsapi/api/odata/';
$full_url = $base_url . $endpoint;
file_put_contents(__DIR__ . '/last_url.txt', $full_url . PHP_EOL, FILE_APPEND);
// Call API: options must be empty because expand is already in endpoint
$data = $api->get($endpoint, []);
file_put_contents(__DIR__ . '/cliente_responsabili_response.json', json_encode($data, JSON_PRETTY_PRINT));
// Return only the list (standard shape used by the frontend)
$responsabili = $data['Responsabili'] ?? [];
echo json_encode(['value' => $responsabili]);
} catch (Exception $e) {
file_put_contents(__DIR__ . '/error_log.txt', date('Y-m-d H:i:s') . ' - ' . $e->getMessage() . PHP_EOL, FILE_APPEND);
http_response_code(500);
echo json_encode(['error' => $e->getMessage()]);
}
-89
View File
@@ -1,89 +0,0 @@
<?php
// get_fixed_field_data.php
require_once dirname(__DIR__, 2) . '/vendor/autoload.php';
require_once dirname(__FILE__) . '/class/VisualLimsApiClient.class.php';
header('Content-Type: application/json');
ini_set('display_errors', '0');
error_reporting(E_ALL);
$field = $_GET['field'] ?? ''; // es: MoltiplicatorePrezzo, AnagraficaCertestObject, ...
if (!$field) {
http_response_code(400);
echo json_encode(['error' => 'Parametro "field" mancante']);
exit;
}
$api = VisualLimsApiClient::getInstance();
$base_url = 'https://93.43.5.102/limsapi/api/odata/';
$data = null;
$endpoint = null;
$cache_file = null;
$options = []; // qui puoi aggiungere filtri/ordering per campo se serve
try {
switch ($field) {
case 'MoltiplicatorePrezzo':
$endpoint = 'MoltiplicatorePrezzi';
$cache_file = __DIR__ . '/cache/moltiplicatori_prezzo.json';
break;
case 'AnagraficaCertestObject':
$endpoint = 'AnagraficaCertestObject';
$cache_file = __DIR__ . '/cache/anagrafica_certest_object.json';
break;
case 'AnagraficaCertestService':
$endpoint = 'AnagraficaCertestService';
$cache_file = __DIR__ . '/cache/anagrafica_certest_service.json';
break;
case 'ClienteResponsabile':
$id_cliente = (int)($_GET['id_cliente'] ?? 0);
if ($id_cliente <= 0) {
http_response_code(400);
echo json_encode(['error' => 'Manca o invalido id_cliente']);
exit;
}
$endpoint = "Cliente($id_cliente)?\$expand=Responsabili";
$cache_file = __DIR__ . '/cache/cliente_responsabili_' . $id_cliente . '.json';
break;
// case 'FuturoCampo5':
// $endpoint = 'QualcosaElse';
// break;
default:
http_response_code(400);
echo json_encode(['error' => "Campo non supportato: $field"]);
exit;
}
// Opzionale: caching semplice (molto utile se i dati cambiano poco)
if ($cache_file && file_exists($cache_file) && (time() - filemtime($cache_file) < 3600)) { // 1 ora
$data = json_decode(file_get_contents($cache_file), true);
} else {
$data = $api->get($endpoint, $options);
if ($cache_file) {
file_put_contents($cache_file, json_encode($data, JSON_PRETTY_PRINT));
}
}
// Log ultimo URL (opzionale, per debug)
$full_url = $base_url . $endpoint . ($options ? '?' . http_build_query($options) : '');
file_put_contents(__DIR__ . '/last_urls.log', date('c') . " - $field - $full_url\n", FILE_APPEND);
echo json_encode($data);
} catch (Exception $e) {
file_put_contents(
__DIR__ . '/error_log.txt',
date('Y-m-d H:i:s') . " [$field] " . $e->getMessage() . PHP_EOL,
FILE_APPEND
);
http_response_code(500);
echo json_encode(['error' => $e->getMessage()]);
}
@@ -1,41 +0,0 @@
<?php
require_once dirname(__DIR__, 2) . '/vendor/autoload.php';
require_once dirname(__FILE__) . '/class/VisualLimsApiClient.class.php';
header('Content-Type: application/json');
ini_set('display_errors', '0');
error_reporting(E_ALL);
try {
$api = VisualLimsApiClient::getInstance();
// Endpoint per recuperare i Moltiplicatori Prezzo
// (dal documento: GET api/odata/MoltiplicatorePrezzi)
$endpoint = 'MoltiplicatorePrezzi';
// Opzionale: parametri OData ($top, $filter, $orderby, ecc.)
$options = []; // es: ['$top' => 100]
// Debug: salva URL usata
$base_url = 'https://93.43.5.102/limsapi/api/odata/';
$query = http_build_query($options);
$full_url = $base_url . $endpoint . ($query ? '?' . $query : '');
file_put_contents(__DIR__ . '/last_url.txt', $full_url . PHP_EOL, FILE_APPEND);
// Chiamata API
$data = $api->get($endpoint, $options);
// Salva il JSON in locale
file_put_contents(__DIR__ . '/moltiplicatori_prezzo_response.json', json_encode($data, JSON_PRETTY_PRINT));
echo json_encode($data);
} catch (Exception $e) {
file_put_contents(
__DIR__ . '/error_log.txt',
date('Y-m-d H:i:s') . ' - ' . $e->getMessage() . PHP_EOL,
FILE_APPEND
);
http_response_code(500);
echo json_encode(['error' => $e->getMessage()]);
}
File diff suppressed because it is too large Load Diff
-32
View File
@@ -1,32 +0,0 @@
<?php
header('Content-Type: application/json');
include('include/headscript.php');
$dbHandler = DBHandlerSelect::getInstance();
$pdo = $dbHandler->getConnection();
// Recupera l'ID dell'utente loggato
$user_id = $iduserlogin ?? 1;
if (!$user_id) {
echo json_encode(['success' => false, 'message' => "ID dell'utente autenticato mancante"]);
exit;
}
try {
$stmt = $pdo->prepare(
"SELECT DISTINCT q.*
FROM quotations q
INNER JOIN identification_parts ip
ON ip.idquotations = q.id
AND ip.iddatadb IS NULL
WHERE q.iduser = :iduser"
);
$stmt->execute([':iduser' => $user_id]);
$quotations = $stmt->fetchAll(PDO::FETCH_ASSOC);
echo json_encode(['success' => true, 'quotations' => $quotations]);
} catch (PDOException $e) {
echo json_encode(['success' => false, 'message' => 'Errore nel caricamento delle quotations: ' . $e->getMessage()]);
}
+1 -117
View File
@@ -97,31 +97,6 @@ if (!$template) {
<ul id="associationsList" class="list-group border p-3"></ul>
</div>
<!-- Default Values (Fixed fields) -->
<div class="mt-4">
<h5>Default Values (Fixed fields)</h5>
<div class="row g-3">
<div class="col-md-6">
<label class="form-label">MoltiplicatorePrezzo (default)</label>
<select id="defaultMoltiplicatorePrezzo" class="form-select">
<option value="">Loading...</option>
</select>
<small class="text-muted">Loaded from API: get_moltiplicatoreprezzo.php</small>
</div>
<div class="col-md-6">
<label class="form-label">ConsegnaRichiesta (default)</label>
<input type="date" id="defaultConsegnaRichiesta" class="form-control">
</div>
</div>
<div class="mt-3">
<button class="btn btn-success" id="saveDefaultsBtn">💾 Save defaults</button>
<span id="defaultsStatus" class="ms-2 text-muted"></span>
</div>
</div>
<!-- Save Button -->
<div class="mt-4 text-end">
<a href="templates_dashboard.php" class="btn btn-primary"> Back to Template Dashboard</a>
@@ -298,7 +273,7 @@ if (!$template) {
}
})
.catch(error => console.error('Error loading table columns:', error));
*/
*/
/** =======================
* CARICAMENTO MAPPATURE GIÀ ESISTENTI
* ======================= */
@@ -466,97 +441,6 @@ if (!$template) {
})
.catch(error => console.error("❌ Fetch error:", error));
}
/** =======================
* DEFAULTS (Fixed fields)
* - stored in localStorage for now
* ======================= */
const defaultsKey = "tpl_defaults_" + templateId;
function loadDefaultsFromLocal() {
try {
const raw = localStorage.getItem(defaultsKey);
if (!raw) return;
const saved = JSON.parse(raw);
if (saved && typeof saved === "object") {
if (saved.moltiplicatore_prezzo_id !== undefined) {
document.getElementById('defaultMoltiplicatorePrezzo').value = saved.moltiplicatore_prezzo_id || "";
}
if (saved.consegna_richiesta !== undefined) {
document.getElementById('defaultConsegnaRichiesta').value = saved.consegna_richiesta || "";
}
}
} catch (e) {
console.warn("⚠️ Cannot parse saved defaults:", e);
}
}
function saveDefaultsToLocal() {
const moltiplicatoreId = document.getElementById('defaultMoltiplicatorePrezzo').value || "";
const consegna = document.getElementById('defaultConsegnaRichiesta').value || "";
const payload = {
moltiplicatore_prezzo_id: moltiplicatoreId,
consegna_richiesta: consegna
};
localStorage.setItem(defaultsKey, JSON.stringify(payload));
const status = document.getElementById('defaultsStatus');
status.textContent = "✅ Defaults saved for this template (local)";
setTimeout(() => status.textContent = "", 2500);
}
function loadMoltiplicatoriPrezzo() {
const select = document.getElementById('defaultMoltiplicatorePrezzo');
select.innerHTML = `<option value="">-- Select --</option>`;
fetch('get_moltiplicatoreprezzo.php')
.then(r => r.json())
.then(data => {
// OData tipico: { value: [...] }
const rows = Array.isArray(data?.value) ? data.value : (Array.isArray(data) ? data : []);
if (!rows.length) {
select.innerHTML = `<option value="">(No data)</option>`;
return;
}
rows.forEach(item => {
// ⚠️ Qui i nomi campi dipendono dal JSON reale:
// - id: item.Id / item.ID / item.idMoltiplicatorePrezzo
// - label: item.Descrizione / item.Nome / item.Codice
const id = item.IdMoltiplicatorePrezzo;
const label = `${item.Descrizione} (x${item.Fattore})`;
if (id === undefined || id === null) return;
const opt = document.createElement('option');
opt.value = String(id);
opt.textContent = String(label);
select.appendChild(opt);
});
// dopo aver popolato, prova a rimettere il valore salvato
loadDefaultsFromLocal();
})
.catch(err => {
console.error("❌ Error loading MoltiplicatorePrezzo:", err);
select.innerHTML = `<option value="">(Load error)</option>`;
});
}
// Events
document.getElementById('saveDefaultsBtn').addEventListener('click', function() {
saveDefaultsToLocal();
});
// Init
loadMoltiplicatoriPrezzo();
loadDefaultsFromLocal();
});
</script>
</body>
+20 -608
View File
@@ -25,18 +25,6 @@ $stmt = $pdo->prepare("SELECT id, field_id, excel_column, is_manual, manual_defa
$stmt->execute([$id]);
$mappings = $stmt->fetchAll(PDO::FETCH_ASSOC);
// Recupera i fixed fields dalla tabella template_fixed_mapping
$stmt = $pdo->prepare("
SELECT id, fixed_field_key, is_manual, data_type, is_required, default_value, is_visible_import
FROM template_fixed_mapping
WHERE template_id = ?
ORDER BY id ASC
");
$stmt->execute([$id]);
$fixedMappings = $stmt->fetchAll(PDO::FETCH_ASSOC);
$hasFixedMappings = !empty($fixedMappings);
// Recupera le colonne già associate nel database
$usedColumnsFromDB = array_filter(array_column($mappings, 'excel_column'));
@@ -54,8 +42,6 @@ $xlsHeaders = $template['xls_headers'] ? json_decode($template['xls_headers'], t
<?php include('cssinclude.php'); ?>
<title>Configure Template <?= htmlspecialchars($template['name'], ENT_QUOTES, 'UTF-8'); ?></title>
<script src="https://cdnjs.cloudflare.com/ajax/libs/xlsx/0.18.5/xlsx.full.min.js"></script>
<link href="https://cdn.jsdelivr.net/npm/select2@4.1.0-rc.0/dist/css/select2.min.css" rel="stylesheet" />
<style>
.dropdown-select {
width: 100%;
@@ -103,11 +89,6 @@ $xlsHeaders = $template['xls_headers'] ? json_decode($template['xls_headers'], t
width: 100px;
text-align: center;
}
/* fix bootstrap/select2 width */
.select2-container {
width: 100% !important;
}
</style>
</head>
@@ -157,215 +138,67 @@ $xlsHeaders = $template['xls_headers'] ? json_decode($template['xls_headers'], t
<table id="schemaFieldsTable" class="table table-striped">
<thead>
<tr>
<th style="width:100px; text-align:center;">Main</th>
<th style="width:120px; text-align:center;">Visible on Import</th>
<th>Main</th>
<th>Visible on Import</th>
<th>Title</th>
<th style="width:140px;">Type</th>
<th>Type</th>
<th>Mapping</th>
<th>Default Value</th>
</tr>
</thead>
<tbody id="schemaFieldsBody">
<?php foreach ($mappings as $mapping): ?>
<tr>
<td class="text-center">
<input type="checkbox"
class="main-field-checkbox"
data-mapping-id="<?php echo (int)$mapping['id']; ?>"
<?php echo ((int)$mapping['main_field'] === 1) ? 'checked' : ''; ?>>
<td>
<input type="checkbox" class="main-field-checkbox" data-mapping-id="<?php echo $mapping['id']; ?>" <?php echo $mapping['main_field'] == 1 ? 'checked' : ''; ?>>
</td>
<td class="text-center">
<input type="checkbox"
class="visible-import-checkbox"
data-mapping-id="<?php echo (int)$mapping['id']; ?>"
<?php echo ((int)($mapping['is_visible_import'] ?? 1) === 1) ? 'checked' : ''; ?>>
<td>
<input type="checkbox" class="visible-import-checkbox" data-mapping-id="<?php echo $mapping['id']; ?>" <?php echo (isset($mapping['is_visible_import']) ? $mapping['is_visible_import'] : 1) == 1 ? 'checked' : ''; ?>>
</td>
<td>
<?php echo htmlspecialchars($mapping['field_label'] ?? 'N/A'); ?>
<?php if ((int)$mapping['is_required'] === 1): ?>
<?php if ($mapping['is_required'] == 1): ?>
<span class="badge bg-danger ms-2">Required</span>
<?php endif; ?>
</td>
<td><?php echo htmlspecialchars($mapping['data_type'] ?? 'N/A'); ?></td>
<td>
<?php
$isSceltaMultipla = ($mapping['data_type'] === 'SceltaMultipla');
$mappingValue = $isSceltaMultipla
? 'manual'
: ($mapping['excel_column'] ? 'xls' : ((int)$mapping['is_manual'] === 1 ? 'manual' : ''));
$isSceltaMultipla = $mapping['data_type'] === 'SceltaMultipla';
$mappingValue = $isSceltaMultipla ? 'manual' : ($mapping['excel_column'] ? 'xls' : ($mapping['is_manual'] ? 'manual' : ''));
?>
<select class="form-select mapping-select"
data-id="<?php echo (int)$mapping['id']; ?>"
data-field-id="<?php echo (int)$mapping['field_id']; ?>"
<?php echo $isSceltaMultipla ? 'disabled' : ''; ?>>
<select class="form-select mapping-select" data-id="<?php echo $mapping['id']; ?>" data-field-id="<?php echo $mapping['field_id']; ?>" <?php echo $isSceltaMultipla ? 'disabled' : ''; ?>>
<?php if (!$isSceltaMultipla): ?>
<option value="">Select Option</option>
<option value="xls" <?php echo ($mappingValue === 'xls') ? 'selected' : ''; ?>>Map to XLS Column</option>
<option value="xls" <?php echo $mappingValue === 'xls' ? 'selected' : ''; ?>>Map to XLS Column</option>
<?php endif; ?>
<option value="manual" <?php echo ($mappingValue === 'manual') ? 'selected' : ''; ?>>Manual Entry</option>
<option value="manual" <?php echo $mappingValue === 'manual' ? 'selected' : ''; ?>>Manual Entry</option>
</select>
<select class="form-select xls-columns"
style="display:<?php echo ($mappingValue === 'xls') ? 'block' : 'none'; ?>"
data-id="<?php echo (int)$mapping['id']; ?>"
<?php echo $mapping['excel_column'] ? 'data-current-xls="' . htmlspecialchars($mapping['excel_column']) . '"' : ''; ?>>
</select>
<?php if (!empty($mapping['excel_column'])): ?>
<span class="mapped-column" style="margin-left:5px;">
(<?php echo htmlspecialchars($mapping['excel_column']); ?>)
</span>
<button class="btn btn-danger btn-sm remove-xls"
data-id="<?php echo (int)$mapping['id']; ?>"
style="margin-left:5px;">
X
</button>
<select class="form-select xls-columns" style="display:<?php echo $mappingValue === 'xls' ? 'block' : 'none'; ?>" data-id="<?php echo $mapping['id']; ?>" <?php echo $mapping['excel_column'] ? 'data-current-xls="' . htmlspecialchars($mapping['excel_column']) . '"' : ''; ?>></select>
<?php if ($mapping['excel_column']): ?>
<span class="mapped-column" style="margin-left: 5px;"><?php echo htmlspecialchars($mapping['excel_column']); ?></span>
<button class="btn btn-danger btn-sm remove-xls" data-id="<?php echo $mapping['id']; ?>" style="margin-left: 5px;">X</button>
<?php endif; ?>
</td>
<td>
<?php if ($mapping['data_type'] === 'SceltaMultipla'): ?>
<select class="form-select dropdown-select manual-default"
data-id="<?php echo (int)$mapping['id']; ?>"
data-field-id="<?php echo (int)$mapping['field_id']; ?>"
data-manual-default="<?php echo htmlspecialchars($mapping['manual_default'] ?? ''); ?>"
style="display:block;">
<select class="form-select dropdown-select manual-default" data-id="<?php echo $mapping['id']; ?>" data-field-id="<?php echo $mapping['field_id']; ?>" data-manual-default="<?php echo htmlspecialchars($mapping['manual_default'] ?? ''); ?>" style="display:block;">
<option value="">Seleziona...</option>
</select>
<?php else: ?>
<input type="text"
class="form-control manual-default"
placeholder="Default value"
value="<?php echo htmlspecialchars($mapping['manual_default'] ?? ''); ?>"
style="display:<?php echo ((int)$mapping['is_manual'] === 1) ? 'block' : 'none'; ?>"
data-field-id="<?php echo (int)$mapping['field_id']; ?>">
<input type="text" class="form-control manual-default" placeholder="Default value" value="<?php echo htmlspecialchars($mapping['manual_default'] ?? ''); ?>" style="display:<?php echo $mapping['is_manual'] ? 'block' : 'none'; ?>" data-field-id="<?php echo $mapping['field_id']; ?>">
<?php endif; ?>
</td>
</tr>
<?php endforeach; ?>
</tbody>
</table>
</div>
</div>
<!-- Defaults for fixed fields (not part of XLS mapping) -->
<!-- Fixed fields defaults (saved in DB, autosave) -->
<div class="row mt-4">
<div class="col-12 d-flex align-items-center justify-content-between">
<h5 class="mb-0">Fixed Fields Defaults</h5>
<?php if (!$hasFixedMappings): ?>
<button id="btnAddFixedFields" class="btn btn-outline-primary">
Add Fixed Fields
</button>
<?php endif; ?>
</div>
<div class="col-12">
<small class="text-muted">Defaults are saved automatically when changed</small>
</div>
</div>
<div class="row mt-3" id="fixedFieldsSection" style="display: <?php echo $hasFixedMappings ? 'block' : 'none'; ?>;">
<div class="col-12">
<table class="table table-striped" id="fixedFieldsTable">
<thead>
<tr>
<th style="width:120px; text-align:center;">Visible on Import</th>
<th style="width:120px; text-align:center;">Required</th>
<th>Field</th>
<th style="width:140px;">Type</th>
<th>Default Value</th>
</tr>
</thead>
<tbody>
<?php foreach ($fixedMappings as $fm): ?>
<tr>
<td class="text-center">
<input type="checkbox"
class="fixed-visible-checkbox"
data-fixed-id="<?php echo (int)$fm['id']; ?>"
<?php echo ((int)$fm['is_visible_import'] === 1) ? 'checked' : ''; ?>>
</td>
<td class="text-center">
<input type="checkbox"
class="fixed-required-checkbox"
data-fixed-id="<?php echo (int)$fm['id']; ?>"
<?php echo ((int)$fm['is_required'] === 1) ? 'checked' : ''; ?>>
</td>
<td>
<strong><?php echo htmlspecialchars($fm['fixed_field_key']); ?></strong>
</td>
<td><?php echo htmlspecialchars($fm['data_type']); ?></td>
<td>
<?php if ($fm['data_type'] === 'DATE'): ?>
<input type="date"
class="form-control fixed-default-input"
data-fixed-id="<?php echo (int)$fm['id']; ?>"
value="<?php echo htmlspecialchars($fm['default_value'] ?? ''); ?>">
<?php elseif (in_array($fm['fixed_field_key'], [
'ClienteResponsabile',
'MoltiplicatorePrezzo',
'AnagraficaCertestObject',
'AnagraficaCertestService'
])): ?>
<select class="form-select fixed-default-select"
data-fixed-id="<?php echo (int)$fm['id']; ?>"
data-fixed-key="<?php echo htmlspecialchars($fm['fixed_field_key']); ?>"
data-current-value="<?php echo htmlspecialchars($fm['default_value'] ?? ''); ?>">
<option value="">Loading...</option>
</select>
<?php else: ?>
<input type="text"
class="form-control fixed-default-input"
data-fixed-id="<?php echo (int)$fm['id']; ?>"
value="<?php echo htmlspecialchars($fm['default_value'] ?? ''); ?>">
<?php endif; ?>
</td>
</tr>
<?php endforeach; ?>
</tbody>
</table>
<div id="fixedSaveStatus" class="small mt-2 text-muted"></div>
</div>
</div>
<div class="mt-4 text-end">
<a href="templates_dashboard.php" class="btn btn-primary"> Back to Template Dashboard</a>
</div>
</div>
</div>
</div>
@@ -376,7 +209,7 @@ $xlsHeaders = $template['xls_headers'] ? json_decode($template['xls_headers'], t
</div>
<?php include('jsinclude.php'); ?>
<script src="https://cdn.jsdelivr.net/npm/select2@4.1.0-rc.0/dist/js/select2.min.js"></script>
<script src="https://code.jquery.com/jquery-3.6.0.min.js"></script>
<script>
let availableXlsColumns = <?php echo json_encode($xlsHeaders); ?> || [];
let usedColumnsFromDB = <?php echo json_encode($usedColumnsFromDB); ?> || [];
@@ -483,80 +316,6 @@ $xlsHeaders = $template['xls_headers'] ? json_decode($template['xls_headers'], t
});
}
function initSelect2ForSchemaDropdowns() {
// dropdown-select = SceltaMultipla (manual default)
if (window.jQuery && $.fn.select2) {
$('.dropdown-select').each(function() {
if ($(this).hasClass('select2-hidden-accessible')) return; // già inizializzato
$(this).select2({
width: '100%',
placeholder: 'Seleziona...',
allowClear: true
});
});
}
}
function initSelect2ForFixedDropdowns() {
if (!(window.jQuery && $.fn.select2)) return;
$('.fixed-default-select').each(function() {
const $el = $(this);
// se era già Select2, lo resettiamo (utile quando renderizzi righe nuove)
if ($el.hasClass('select2-hidden-accessible')) {
$el.select2('destroy');
}
$el.select2({
width: '100%',
placeholder: 'Seleziona...',
allowClear: true
});
});
}
function saveFixedDefaultSelect(selectEl) {
const id = selectEl.dataset.fixedId;
const value = selectEl.value;
fetch('update_fixed_field.php', {
method: 'POST',
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify({
id,
field: 'default_value',
value
})
})
.then(r => r.json())
.then(d => {
if (!d.success) throw new Error(d.message || 'Update failed');
// aggiorno anche il dataset così rimane coerente
selectEl.dataset.currentValue = value;
fixedStatus('✅ Saved');
})
.catch(err => {
console.error(err);
fixedStatus('❌ Save error', true);
});
}
// Eventi Select2 (jQuery)
if (window.jQuery) {
$(document).on('select2:select select2:clear', '.fixed-default-select', function() {
saveFixedDefaultSelect(this);
});
// fallback (nel caso cambi senza select2)
$(document).on('change', '.fixed-default-select', function() {
saveFixedDefaultSelect(this);
});
}
document.addEventListener("DOMContentLoaded", function() {
let templateId = <?php echo $id; ?>;
let schemaId = <?php echo json_encode($template['idschema'] ?? 0); ?>;
@@ -644,13 +403,9 @@ $xlsHeaders = $template['xls_headers'] ? json_decode($template['xls_headers'], t
dropdown.disabled = true;
});
} finally {
// attiva Select2 dopo aver messo le options
initSelect2ForSchemaDropdowns();
console.log('Nascondo overlay di caricamento.');
loadingOverlay.style.display = 'none';
}
}
// Carica i dropdown con overlay
@@ -958,352 +713,9 @@ $xlsHeaders = $template['xls_headers'] ? json_decode($template['xls_headers'], t
.catch(error => console.error("❌ Fetch error:", error));
}
// =======================
// FIXED FIELDS (DB autosave)
// =======================
function escapeHtml(str) {
return String(str ?? '')
.replaceAll('&', '&amp;')
.replaceAll('<', '&lt;')
.replaceAll('>', '&gt;')
.replaceAll('"', '&quot;')
.replaceAll("'", '&#039;');
}
function fixedStatus(msg, isError = false) {
const el = document.getElementById('fixedSaveStatus');
if (!el) return;
el.textContent = msg;
el.classList.toggle('text-danger', isError);
el.classList.toggle('text-muted', !isError);
setTimeout(() => {
el.textContent = '';
}, 2000);
}
function renderFixedRows(rows) {
const tbody = document.querySelector('#fixedFieldsTable tbody');
if (!tbody) return;
const keysWithDropdown = [
'ClienteResponsabile',
'MoltiplicatorePrezzo',
'AnagraficaCertestObject',
'AnagraficaCertestService'
];
tbody.innerHTML = rows.map(r => {
const type = String(r.data_type || '').toUpperCase();
const dv = r.default_value ?? '';
const isDate = type === 'DATE';
const isDropdown = keysWithDropdown.includes(String(r.fixed_field_key || ''));
return `
<tr>
<td class="text-center">
<input type="checkbox"
class="fixed-visible-checkbox"
data-fixed-id="${r.id}"
${parseInt(r.is_visible_import) === 1 ? 'checked' : ''}>
</td>
<td class="text-center">
<input type="checkbox"
class="fixed-required-checkbox"
data-fixed-id="${r.id}"
${parseInt(r.is_required) === 1 ? 'checked' : ''}>
</td>
<td><strong>${escapeHtml(r.fixed_field_key)}</strong></td>
<td>${escapeHtml(r.data_type || '')}</td>
<td>
${
isDate
? `<input type="date"
class="form-control fixed-default-input"
data-fixed-id="${r.id}"
value="${escapeHtml(dv)}">`
: isDropdown
? `<select class="form-select fixed-default-select"
data-fixed-id="${r.id}"
data-fixed-key="${escapeHtml(r.fixed_field_key)}"
data-current-value="${escapeHtml(dv)}">
<option value="">Loading...</option>
</select>`
: `<input type="text"
class="form-control fixed-default-input"
data-fixed-id="${r.id}"
value="${escapeHtml(dv)}"
placeholder="Default value">`
}
</td>
</tr>
`;
}).join('');
}
// Button: Add Fixed Fields
const btnAddFixedFields = document.getElementById('btnAddFixedFields');
if (btnAddFixedFields) {
btnAddFixedFields.addEventListener('click', async () => {
btnAddFixedFields.disabled = true;
btnAddFixedFields.textContent = 'Creating...';
try {
const res = await fetch('create_fixed_fields.php', {
method: 'POST',
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify({
template_id: templateId
})
});
const data = await res.json();
if (!data.success) throw new Error(data.message || 'Create failed');
document.getElementById('fixedFieldsSection').style.display = 'block';
btnAddFixedFields.style.display = 'none';
if (Array.isArray(data.rows)) {
renderFixedRows(data.rows);
await fillFixedDropdowns();
initSelect2ForFixedDropdowns();
}
fixedStatus('✅ Fixed fields created');
} catch (e) {
console.error(e);
fixedStatus('❌ ' + e.message, true);
btnAddFixedFields.disabled = false;
btnAddFixedFields.textContent = 'Add Fixed Fields';
}
});
}
// Autosave: Visible on import
document.addEventListener('change', (e) => {
if (!e.target.classList.contains('fixed-visible-checkbox')) return;
const id = e.target.dataset.fixedId;
const value = e.target.checked ? 1 : 0;
fetch('update_fixed_field.php', {
method: 'POST',
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify({
id,
field: 'is_visible_import',
value
})
})
.then(r => r.json())
.then(d => {
if (!d.success) throw new Error(d.message || 'Update failed');
fixedStatus('✅ Saved');
})
.catch(err => {
console.error(err);
e.target.checked = !e.target.checked;
fixedStatus('❌ Save error', true);
});
});
document.addEventListener('change', (e) => {
if (!e.target.classList.contains('fixed-required-checkbox')) return;
const id = e.target.dataset.fixedId;
const value = e.target.checked ? 1 : 0;
fetch('update_fixed_field.php', {
method: 'POST',
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify({
id,
field: 'is_required',
value
})
})
.then(r => r.json())
.then(d => {
if (!d.success) throw new Error(d.message || 'Update failed');
fixedStatus('✅ Saved');
})
.catch(err => {
console.error(err);
e.target.checked = !e.target.checked;
fixedStatus('❌ Save error', true);
});
});
// Autosave: manual_default (debounce)
let fixedTimer = null;
document.addEventListener('input', (e) => {
if (!e.target.classList.contains('fixed-default-input')) return;
clearTimeout(fixedTimer);
fixedTimer = setTimeout(() => {
const id = e.target.dataset.fixedId;
const value = e.target.value;
fetch('update_fixed_field.php', {
method: 'POST',
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify({
id,
field: 'default_value',
value
})
})
.then(r => r.json())
.then(d => {
if (!d.success) throw new Error(d.message || 'Update failed');
fixedStatus('✅ Saved');
})
.catch(err => {
console.error(err);
fixedStatus('❌ Save error', true);
});
}, 350);
});
if (availableXlsColumns.length) updateXlsDropdowns();
fillFixedDropdowns().then(() => {
initSelect2ForFixedDropdowns();
});
});
async function fillFixedDropdowns() {
const selects = document.querySelectorAll('.fixed-default-select');
console.log('[fillFixedDropdowns] found selects:', selects.length);
if (!selects.length) return;
const clientId = <?php echo (int)($template['idclient'] ?? 0); ?>;
async function fetchJson(url) {
const r = await fetch(url);
const text = await r.text();
if (!r.ok) throw new Error(`HTTP ${r.status} on ${url}: ${text.slice(0, 200)}`);
try {
return JSON.parse(text);
} catch (e) {
throw new Error(`Invalid JSON from ${url}: ${text.slice(0, 200)}`);
}
}
function normalizeList(j, key) {
// Accetta vari formati: {value:[]}, {Responsabili:[]}, {results:[]}, array diretto
if (Array.isArray(j)) return j;
if (j && Array.isArray(j.value)) return j.value;
if (j && Array.isArray(j.Responsabili)) return j.Responsabili;
if (j && Array.isArray(j.results)) return j.results;
console.warn('[fillFixedDropdowns] unknown JSON shape for', key, j);
return [];
}
async function loadData(key) {
if (key === 'MoltiplicatorePrezzo') {
const j = await fetchJson('get_moltiplicatoreprezzo.php');
const list = normalizeList(j, key);
return list.map(x => ({
id: x.IdMoltiplicatorePrezzo,
label: `${x.Descrizione} (x${x.Fattore})`
}));
}
if (key === 'AnagraficaCertestObject') {
const j = await fetchJson('get_anagrafica_certest_object.php');
const list = normalizeList(j, key);
return list.map(x => ({
id: x.IdAnagrafica,
label: `${x.Codice} - ${x.NomeAnagrafica}`
}));
}
if (key === 'AnagraficaCertestService') {
const j = await fetchJson('get_anagrafica_certest_service.php');
const list = normalizeList(j, key);
return list.map(x => ({
id: x.IdAnagrafica,
label: `${x.Codice} - ${x.NomeAnagrafica}`
}));
}
if (key === 'ClienteResponsabile') {
if (!clientId) return [];
const j = await fetchJson('get_cliente_responsabile.php?id_cliente=' + clientId);
const list = normalizeList(j, key);
// supporto due possibili campi:
// - {IdClienteResponsabile, Nominativo}
// - {Id, Nome} o simili
return list.map(x => ({
id: x.IdClienteResponsabile ?? x.Id ?? x.id,
label: x.Nominativo ?? x.Nome ?? x.name ?? ''
})).filter(x => x.id != null && x.label);
}
return [];
}
for (const select of selects) {
const key = select.dataset.fixedKey;
const current = select.dataset.currentValue;
// IMPORTANT: togli "Loading..." subito (così se crasha non resta bloccato)
select.innerHTML = '<option value="">Loading...</option>';
try {
console.log('[fillFixedDropdowns] loading', key);
const items = await loadData(key);
if (!items.length) {
select.innerHTML = '<option value="">No data</option>';
continue;
}
select.innerHTML = '<option value=""></option>';
for (const it of items) {
const opt = document.createElement('option');
opt.value = it.id;
opt.textContent = it.label;
select.appendChild(opt);
}
if (current) select.value = current;
} catch (err) {
console.error('[fillFixedDropdowns] error for', key, err);
select.innerHTML = '<option value="">Error loading data</option>';
}
}
}
</script>
</body>
</html>
+4 -59
View File
@@ -1,22 +1,13 @@
<!-- Modal per la gestione delle annotazioni -->
<div class="modal fade" id="annotationsModal" tabindex="-1" aria-labelledby="annotationsModalLabel" aria-hidden="true">
<div class="modal-dialog modal-xl" style="max-width: 90% !important; width: 90% !important;">
<div class="modal-dialog modal-xl" style="max-width: 80% !important; width: 80% !important;">
<div class="modal-content">
<div class="modal-header">
<h5 class="modal-title" id="annotationsModalLabel">Annotazioni per TRF: <span id="trfHeaderAnnotations"></span></h5>
<!-- SLIDER PER DIMENSIONE MARKER -->
<div style="display: flex; align-items: center; gap: 10px; margin-left: 20px;">
<label for="markerSizeSlider" style="margin: 0; font-size: 0.9rem; white-space: nowrap;">Dimensione marker:</label>
<input type="range" id="markerSizeSlider" min="16" max="48" value="16" step="2" style="width: 120px;">
<span id="markerSizeValue" style="font-weight: bold; min-width: 30px;">24px</span>
</div>
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
</div>
<div class="modal-body">
<div class="row">
<!-- COLONNA SINISTRA RIDOTTA -->
<div class="col-md-6">
<div style="display: flex; justify-content: space-between; align-items: center; margin-bottom: 10px;">
<h6 style="margin: 0;">Elenco Parti</h6>
@@ -25,17 +16,15 @@
<label for="showMixPartsAnnotations" style="font-size: 0.9rem;">Mix</label>
</div>
</div>
<ul id="partsListAnnotations" class="list-group" style="max-height: 500px; overflow-y: auto;"></ul>
<ul id="partsListAnnotations" class="list-group"></ul>
</div>
<!-- COLONNA DESTRA PIÙ GRANDE -->
<div class="col-md-6">
<h6>Foto del Campione</h6>
<div style="display: flex; align-items: center; margin-bottom: 10px;">
<button type="button" class="btn btn-primary btn-sm" id="downloadPhotoBtnAnnotations" style="padding: 0.1rem 0.5rem; font-size: 0.8rem; margin-right: 10px;"><i class="fas fa-download"></i></button>
<div id="photoSelectorContainerAnnotations" style="display: none;"></div>
</div>
<div style="position: relative; width: 100%; min-height: 500px; border: 1px solid #ddd; border-radius: 4px; overflow: hidden;">
<div style="position: relative; width: 100%; min-height: 400px;">
<img id="samplePhotoAnnotations" src="" alt="Foto del campione" style="max-width: 100%; max-height: 100%; object-fit: contain; position: absolute; top: 0; left: 0;">
<canvas id="photoCanvasAnnotations" style="position: absolute; top: 0; left: 0; width: 100%; height: 100%;"></canvas>
<canvas id="overlayCanvasAnnotations" style="position: absolute; top: 0; left: 0; width: 100%; height: 100%; z-index: 1000;"></canvas>
@@ -227,48 +216,4 @@
transform: translateX(-50%);
z-index: 3000;
}
/* Stile per lo slider */
#markerSizeSlider {
-webkit-appearance: none;
height: 6px;
border-radius: 3px;
background: #ddd;
outline: none;
}
#markerSizeSlider::-webkit-slider-thumb {
-webkit-appearance: none;
appearance: none;
width: 16px;
height: 16px;
border-radius: 50%;
background: #007bff;
cursor: pointer;
}
#markerSizeSlider::-moz-range-thumb {
width: 16px;
height: 16px;
border-radius: 50%;
background: #007bff;
cursor: pointer;
border: none;
}
#partsListAnnotations .list-group-item.dragging {
opacity: 0.6;
background-color: #d1e7dd !important;
transform: rotate(3deg);
box-shadow: 0 4px 8px rgba(0, 0, 0, 0.2);
}
#partsListAnnotations .placeholder {
background-color: #f8d7da !important;
border: 2px dashed #dc3545;
height: 40px;
margin: 2px 0;
border-radius: 4px;
}
</style>
<script src="https://code.jquery.com/ui/1.13.2/jquery-ui.min.js"></script>
</style>
+1 -32
View File
@@ -6,7 +6,7 @@
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
</div>
<div class="modal-body">
<div class="row parts-row">
<div class="row">
<div class="col-md-9">
<!-- Prima riga: Elenco Parti, Rinumera, Voce -->
<div style="display: flex; justify-content: space-between; align-items: center; margin-bottom: 10px;">
@@ -14,11 +14,6 @@
<div style="display: flex; align-items: center;">
<button type="button" class="btn btn-info btn-sm" id="renumberPartsBtn" style="padding: 0.1rem 0.5rem; font-size: 0.8rem;">Rinumera Parti</button>
<button type="button" class="btn btn-secondary btn-sm ms-2" id="toggleVoiceBtn" style="padding: 0.1rem 0.5rem; font-size: 0.8rem;"><i class="fas fa-microphone"></i> Voce</button>
<button type="button" class="btn btn-info btn-sm ms-2 d-none" id="quotationeBtn" style="padding: 0.1rem 0.5rem; font-size: 0.8rem;">Add Quotation</button>
<button type="button" class="btn btn-primary btn-sm ms-2" id="showHideImageBtn" style="padding: 0.1rem 0.5rem; font-size: 0.8rem;">
<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>
</button>
</div>
</div>
<!-- Seconda riga: +, M, MacroMatrice, Matrice globale, Propaga -->
@@ -146,24 +141,6 @@
</div>
</div>
</div>
<!-- Finestra modale "Aggiungi preventivo" -->
<div class="modal fade" id="addQuotationModal" tabindex="-1" aria-labelledby="addQuotationModalLabel" aria-hidden="true">
<div class="modal-dialog modal-md">
<div class="modal-content">
<div class="modal-header">
<h5 class="modal-title" id="addQuotationModalLabel">Choose a quotation</h5>
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
</div>
<div class="modal-body">
<select id="addQuotationSelect" class="form-control form-control-sm" style="width: 100% !important; min-width: 100% !important"></select>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-secondary btn-sm" data-bs-dismiss="modal">Annulla</button>
<button type="button" class="btn btn-primary btn-sm" id="addQuotationBtn">Confirm</button>
</div>
</div>
</div>
</div>
<style>
/* --- Base --- */
@@ -180,14 +157,6 @@
max-width: 100% !important
}
#addQuotationModal {
z-index: 1060 !important
}
#addQuotationModal .modal-backdrop {
z-index: 1055 !important
}
/* Tabelle */
#partsTable tr {
display: table-row !important
+16 -160
View File
@@ -6,7 +6,6 @@ $(document).ready(function () {
let unsavedChanges = false;
let matrici = [];
let macroMatrici = [];
let quotations = [];
// --- ROW ID helpers: niente più cache impazzita di jQuery .data() ---
function getPartId($row) {
@@ -142,7 +141,7 @@ $(document).ready(function () {
// ===================
// MODAL HANDLING
// ===================
function loadParts(iddatadb, idquotations, callback = null) {
function loadParts(iddatadb, idquotations) {
if (iddatadb) {
if (matrici.length === 0) {
$.ajax({
@@ -154,14 +153,14 @@ $(document).ready(function () {
loadMacroMatrici();
initializeGlobalSelect2();
loadPhoto(iddatadb, idquotations);
loadExistingParts(iddatadb, idquotations, callback);
loadExistingParts(iddatadb, idquotations);
},
error: function (xhr, status, error) {
matrici = [];
loadMacroMatrici();
initializeGlobalSelect2();
loadPhoto(iddatadb, idquotations);
loadExistingParts(iddatadb, idquotations, callback);
loadExistingParts(iddatadb, idquotations);
const errorMsg = $(
'<div class="alert alert-danger temp-alert" role="alert">Errore nel caricamento delle matrici: ' +
error +
@@ -181,11 +180,11 @@ $(document).ready(function () {
loadMacroMatrici();
initializeGlobalSelect2();
loadPhoto(iddatadb, idquotations);
loadExistingParts(iddatadb, idquotations, callback);
loadExistingParts(iddatadb, idquotations);
}
} else {
loadPhoto(iddatadb, idquotations);
loadExistingParts(iddatadb, idquotations, callback);
loadExistingParts(iddatadb, idquotations);
}
}
@@ -590,8 +589,6 @@ $(document).ready(function () {
if (response.success) {
$saveStatus.show();
setTimeout(() => $saveStatus.hide(), 2000);
if (!$("#quotationeBtn").hasClass('d-none')) $("#quotationeBtn").addClass("d-none");
} else {
const errorMsg = $(
'<div class="alert alert-danger temp-alert" role="alert">Errore nel salvataggio: ' +
@@ -1158,7 +1155,7 @@ $(document).ready(function () {
saveRow($(this).closest("tr"));
});
function loadExistingParts(iddatadb, idquotations, callback = null) {
function loadExistingParts(iddatadb, idquotations) {
const endpoint = idquotations
? "load_parts_quotation.php"
: "load_parts.php";
@@ -1236,11 +1233,8 @@ $(document).ready(function () {
});
} else {
addNewRow(1, false); // Riga iniziale normale
$("#quotationeBtn").removeClass("d-none");
}
updateRowButtons();
if (callback) callback();
},
error: function (xhr, status, error) {
const errorMsg = $(
@@ -1345,7 +1339,6 @@ $(document).ready(function () {
partId,
currentValue,
selectedMacro,
true
);
});
});
@@ -1422,7 +1415,6 @@ $(document).ready(function () {
partId,
idmatrice,
selectedMacro = null,
fromFilter = false
) {
if (typeof $.fn.select2 === "undefined") {
$select.replaceWith(
@@ -1482,8 +1474,8 @@ $(document).ready(function () {
},
});
// Ripristina il valore se valido
if (partId && partId !== "new" && currentValue) {
// === MODIFICA CHIRURGICA: solo qui ===
if (currentValue && currentValue !== "new") {
const matrice = matrici.find((m) => m.IdMatrice == currentValue);
if (matrice) {
const option = new Option(
@@ -1492,24 +1484,20 @@ $(document).ready(function () {
true,
true,
);
if (!fromFilter) $select.append(option).trigger("change");
else $select.append(option);
$select.append(option).trigger("change");
partMatrice[partNumber] = matrice.IdMatrice;
} else {
// Aggiusta valore non valido
if (!fromFilter) $select.val(null).trigger("change");
$select.val(null).trigger("change");
partMatrice[partNumber] = null;
}
} else {
$select.val(null).trigger("change", [{ skipHandler: true }]);
}
// Nessun valore: assicurati che sia vuoto
$select.val(null).trigger("change");
partMatrice[partNumber] = null;
}
// === FINE MODIFICA ===
$select.on("change", function (event, data) {
if (data && data?.skipHandler) return;
$select.on("change", function () {
const idmatrice = $(this).val();
const $row = $(this).closest("tr");
const partId = $row.data("part-id");
@@ -1793,122 +1781,6 @@ $(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");
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 = `
<div class="alert alert-danger temp-alert" role="alert">
Errore nel caricamento delle quotations: ${error} (${xhr.status})
</div>
`;
$("#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 = `
<div class="alert alert-danger temp-alert" role="alert">
Errore di salvataggio: ${error} (${xhr.status})
</div>
`;
$("#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("<option value=''>Seleziona Quotation</option>");
quotations.forEach(quotation => {
if (quotation?.description) {
$("#addQuotationSelect").append(`<option value='${quotation?.id}'>${quotation?.description}</option>`);
}
});
$("#addQuotationSelect").select2();
}
}
});
$(document).on("change", ".propagate-date-input", function () {
@@ -2132,19 +2004,3 @@ $(document).on("click", ".save-common-note-btn", function () {
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("<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>");
}
});
+9 -47
View File
@@ -399,18 +399,14 @@ if (isset($_GET['edit_id'])) {
<a href="javaScript:;" class="back-to-top"><i class='bx bxs-up-arrow-alt'></i></a>
<?php include('include/footer.php'); ?>
</div>
<div id="partsModalContainer"></div>
<div id="annotationsModalContainer"></div>
<?php include 'photos_functions.php'; ?>
<?php include('modal_parts.php'); ?>
<?php include('photos_functions.php'); ?>
<?php include('jsinclude.php'); ?>
<script src="https://code.jquery.com/jquery-3.6.0.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/js/bootstrap.bundle.min.js"></script>
<script src="https://cdn.datatables.net/1.13.4/js/jquery.dataTables.min.js"></script>
<script src="photos.js"></script>
<script src="annotationsModal.js"></script>
<script src="partsTable.js"></script>
<script src="parts.js"></script>
<script>
document.addEventListener("DOMContentLoaded", function() {
// Mostra messaggi di stato se presenti
@@ -427,45 +423,6 @@ if (isset($_GET['edit_id'])) {
}, 5000);
}
$(document).on('click', '.parts-btn', function() {
const idquotations = $(this).data('idquotations');
$.ajax({
url: 'modal_partsTable.php',
method: 'GET',
data: {
idquotations: idquotations
},
success: function(response) {
$('#partsModalContainer').html(response);
const modalElement = document.getElementById('partsModal');
if (!modalElement) return;
$("#trfHeader").text(`Quotation #${idquotations}`);
$("#partsModal").data("idquotations", idquotations);
let modal = bootstrap.Modal.getInstance(modalElement) || new bootstrap.Modal(modalElement, {
backdrop: true
});
modal.show();
if (typeof window.loadParts === 'function') window.loadParts(null, idquotations);
},
error: function(xhr, status, error) {
alert('Errore nel caricamento del modale: ' + error);
}
});
});
$(document).on('hidden.bs.modal', '#partsModal', function() {
$('#partsModalContainer').empty();
$('.modal-backdrop').remove();
$('body').removeClass('modal-open').css('padding-right', '');
});
$(document).on('hidden.bs.modal', '#annotationsModal', function() {
$('#annotationsModalContainer').empty();
$('.modal-backdrop').remove();
$('body').removeClass('modal-open').css('padding-right', '');
});
// Inizializza DataTables se non siamo in modalità modifica
if (!document.querySelector('#editForm')) {
$('#quotationsTable').DataTable({
@@ -567,7 +524,12 @@ if (isset($_GET['edit_id'])) {
</script>
<!-- Modale per le foto in quotations.php -->
<div class="modal" id="photosModal">
<div class="modal-content">
<span class="close-btn">&times;</span>
<div class="popup-content"></div>
</div>
</div>
</body>
+12 -85
View File
@@ -15,44 +15,6 @@ try {
$db = DBHandlerSelect::getInstance();
$pdo = $db->getConnection();
// ---------------- FIXED FIELDS (template_fixed_mapping) ----------------
// 1) Recupera templateid dalla riga datadb (serve per sapere quali fixed_field_key sono permessi)
$stmtTpl = $pdo->prepare("SELECT templateid FROM datadb WHERE iddatadb = ?");
$stmtTpl->execute([$iddatadb]);
$tplRow = $stmtTpl->fetch(PDO::FETCH_ASSOC);
$templateId = isset($tplRow['templateid']) ? (int)$tplRow['templateid'] : 0;
if ($templateId <= 0) {
throw new Exception("Template non trovato per iddatadb=$iddatadb");
}
// 2) Recupera elenco fixed fields visibili per quel template
$fxStmt = $pdo->prepare("
SELECT fixed_field_key, data_type, is_required, default_value
FROM template_fixed_mapping
WHERE template_id = ? AND is_visible_import = 1
");
$fxStmt->execute([$templateId]);
$fixedList = $fxStmt->fetchAll(PDO::FETCH_ASSOC);
// 3) Crea whitelist: key => metadata
$fixedWhitelist = [];
foreach ($fixedList as $fx) {
$k = (string)$fx['fixed_field_key'];
// sicurezza: nome colonna ammesso solo se "safe" (no spazi, no caratteri strani)
if (!preg_match('/^[a-zA-Z0-9_]+$/', $k)) {
continue;
}
$fixedWhitelist[$k] = [
'data_type' => (string)$fx['data_type'], // INT | DATE
'is_required' => (int)$fx['is_required'],
'default_value' => $fx['default_value'] ?? null
];
}
$data = $_POST;
$details = [];
@@ -101,57 +63,22 @@ try {
}
}
// 5. Aggiorna datadb: idclient + FIXED FIELDS (whitelisted)
$setParts = [];
$params = [];
// 5a) idclient (se presente)
// 5. Aggiorna idclient in datadb
if (isset($idclient)) {
$setParts[] = "idclient = ?";
$params[] = $idclient;
}
// 5b) fixed fields dal POST (solo quelli presenti nella whitelist del template)
foreach ($fixedWhitelist as $col => $meta) {
if (!array_key_exists($col, $_POST)) {
continue; // non inviato dal form
}
$val = $_POST[$col];
// Normalizzazione per tipo
if ($meta['data_type'] === 'DATE') {
$val = trim((string)$val);
$val = ($val === '') ? null : $val; // atteso formato Y-m-d
} else { // INT
$val = trim((string)$val);
$val = ($val === '') ? null : (int)$val;
}
$setParts[] = "`$col` = ?";
$params[] = $val;
}
// esegui update solo se c'è qualcosa da aggiornare
if (!empty($setParts)) {
$params[] = $iddatadb;
$sqlUpd = "UPDATE datadb SET " . implode(", ", $setParts) . " WHERE iddatadb = ?";
$updStmt = $pdo->prepare($sqlUpd);
$updStmt->execute($params);
}
// Messaggio risposta (mantengo la tua logica ma includo fixed)
if (!empty($setParts) && !empty($changed)) {
$response['message'] = "Updated details and datadb fields successfully";
} elseif (!empty($setParts)) {
$response['message'] = "Updated datadb fields successfully";
} elseif (!empty($changed)) {
$response['message'] = "Updated details successfully";
$updateStmt = $pdo->prepare("
UPDATE datadb
SET idclient = :idclient
WHERE iddatadb = :iddatadb
");
$updateStmt->execute([
':idclient' => $idclient,
':iddatadb' => $iddatadb
]);
$response['message'] = !empty($changed) ? "Updated details and idclient successfully" : "Updated idclient successfully";
} else {
$response['message'] = "No changes found";
$response['message'] = !empty($changed) ? "Updated details successfully" : "No changes found";
}
$response['success'] = true;
$response['changed'] = $changed; // Debug / optional
@@ -1,48 +0,0 @@
<?php
header('Content-Type: application/json');
include('include/headscript.php');
$dbHandler = DBHandlerSelect::getInstance();
$pdo = $dbHandler->getConnection();
$data = json_decode(file_get_contents('php://input'), true);
$iddatadb = $data['iddatadb'] ?? null;
$partIds = $data['partIds'] ?? [];
$photoList = $data['photoList'] ?? null;
if ($iddatadb) {
if (count($partIds) != 0) {
$idList = array_values(array_map('intval', $partIds));
$placeholders = [];
$parameters = [':iddatadb' => $iddatadb];
foreach ($idList as $index => $id) {
$paramName = ":id{$index}";
$placeholders[] = $paramName;
$parameters[$paramName] = $id;
}
$sql = 'UPDATE identification_parts SET iddatadb = :iddatadb WHERE id IN (' . implode(',', $placeholders) . ')';
$stmt = $pdo->prepare($sql);
$stmt->execute($parameters);
}
if (count($photoList) != 0) {
$placeholders = [];
$parameters = [':iddatadb' => $iddatadb];
foreach ($photoList as $index => $photo) {
$paramName = ":photo{$index}";
$placeholders[] = $paramName;
$parameters[$paramName] = $photo;
}
$stmt = $pdo->prepare('UPDATE datadb_photos SET iddatadb = :iddatadb WHERE file_path IN (' . implode(',', $placeholders) . ')');
$stmt->execute($parameters);
}
echo json_encode(['success' => true, 'message' => '']);
} else {
echo json_encode(['success' => false, 'message' => 'Dati mancanti']);
}
-809
View File
@@ -3,867 +3,58 @@
"value": [
{
"IdSchemaCustomFields": 41,
"ConteggioClienti": 0,
"Nome": "Labostudio",
"Descrizione": null
},
{
"IdSchemaCustomFields": 42,
"ConteggioClienti": 0,
"Nome": "Schema 1",
"Descrizione": null
},
{
"IdSchemaCustomFields": 43,
"ConteggioClienti": 0,
"Nome": "Schema1",
"Descrizione": null
},
{
"IdSchemaCustomFields": 44,
"ConteggioClienti": 0,
"Nome": "Cuoio\/Pelle Standard \/ Leather",
"Descrizione": "Schema per tutti i campioni in cuoio\/pelle per i quali non \u00e8 specificato un destinatario.\r\n"
},
{
"IdSchemaCustomFields": 45,
"ConteggioClienti": 0,
"Nome": "Borse",
"Descrizione": null
},
{
"IdSchemaCustomFields": 46,
"ConteggioClienti": 0,
"Nome": "Borse Burberry",
"Descrizione": null
},
{
"IdSchemaCustomFields": 47,
"ConteggioClienti": 0,
"Nome": "pelle calzatura",
"Descrizione": null
},
{
"IdSchemaCustomFields": 48,
"ConteggioClienti": 0,
"Nome": "Standard Generico \/ Generic Standard",
"Descrizione": "Schema per tutti i campioni di qualsiasi matrice escluso cuoio\/pelle\r\n\r\n"
},
{
"IdSchemaCustomFields": 49,
"ConteggioClienti": 0,
"Nome": "Accessori Metallici \/ Metallic Accessories",
"Descrizione": "Schema per tutti gli Accessori Metallici\r\n\r\n\r\n"
},
{
"IdSchemaCustomFields": 50,
"ConteggioClienti": 0,
"Nome": "Tessile \/ Textiles",
"Descrizione": "Schema per tutti i Tessuti\r\n"
},
{
"IdSchemaCustomFields": 52,
"ConteggioClienti": 0,
"Nome": "Brasport",
"Descrizione": null
},
{
"IdSchemaCustomFields": 54,
"ConteggioClienti": 0,
"Nome": "Chanel Calzatura",
"Descrizione": "\r\n"
},
{
"IdSchemaCustomFields": 55,
"ConteggioClienti": 0,
"Nome": "Burberry",
"Descrizione": null
},
{
"IdSchemaCustomFields": 57,
"ConteggioClienti": 0,
"Nome": "Z_Ralph Lauren - Tessile",
"Descrizione": "Schema per tutti i campioni in tessuto per Ralph Lauren.\r\n\r\n\r\n\r\n"
},
{
"IdSchemaCustomFields": 58,
"ConteggioClienti": 0,
"Nome": "LIU JO",
"Descrizione": null
},
{
"IdSchemaCustomFields": 59,
"ConteggioClienti": 0,
"Nome": "Migrazione tra Componenti",
"Descrizione": null
},
{
"IdSchemaCustomFields": 60,
"ConteggioClienti": 0,
"Nome": "Prova Outsourcing",
"Descrizione": null
},
{
"IdSchemaCustomFields": 61,
"ConteggioClienti": 0,
"Nome": "Marc Jacobs",
"Descrizione": null
},
{
"IdSchemaCustomFields": 62,
"ConteggioClienti": 0,
"Nome": "A. TESTONI",
"Descrizione": null
},
{
"IdSchemaCustomFields": 63,
"ConteggioClienti": 0,
"Nome": "Chanel CHINA PROJECT",
"Descrizione": "\r\n"
},
{
"IdSchemaCustomFields": 66,
"ConteggioClienti": 0,
"Nome": "GUESS",
"Descrizione": null
},
{
"IdSchemaCustomFields": 68,
"ConteggioClienti": 0,
"Nome": "Valentino",
"Descrizione": null
},
{
"IdSchemaCustomFields": 69,
"ConteggioClienti": 0,
"Nome": "DESIGUAL",
"Descrizione": null
},
{
"IdSchemaCustomFields": 70,
"ConteggioClienti": 0,
"Nome": "FENDI PELLETTERIA",
"Descrizione": "Solo per FENDI Pelletteria_INGLESE\r\n"
},
{
"IdSchemaCustomFields": 71,
"ConteggioClienti": 0,
"Nome": "Z_Cuoio\/Pelle Standard ACCREDIA",
"Descrizione": "Schema per tutti i campioni in cuoio\/pelle per i quali non \u00e8 specificato un destinatario.\r\n"
},
{
"IdSchemaCustomFields": 72,
"ConteggioClienti": 0,
"Nome": "Z_BALLY",
"Descrizione": "sia per calzatura che pelletteria. Aggiunto campo TEST DESCRIPTION, compreso nella fatturazione per le divisioni pelletteria.\r\n\r\n"
},
{
"IdSchemaCustomFields": 73,
"ConteggioClienti": 0,
"Nome": "Z_Ralph Lauren - Leather",
"Descrizione": null
},
{
"IdSchemaCustomFields": 74,
"ConteggioClienti": 0,
"Nome": "Z_Ralph Lauren - Fabric",
"Descrizione": null
},
{
"IdSchemaCustomFields": 76,
"ConteggioClienti": 0,
"Nome": "NORDSTROM",
"Descrizione": "\r\n"
},
{
"IdSchemaCustomFields": 77,
"ConteggioClienti": 0,
"Nome": "ONWARD LUXURY GROUP",
"Descrizione": "\r\n"
},
{
"IdSchemaCustomFields": 78,
"ConteggioClienti": 0,
"Nome": "P&G SRL_Progetto Durability",
"Descrizione": "\r\n"
},
{
"IdSchemaCustomFields": 79,
"ConteggioClienti": 0,
"Nome": "Bally qualsiasi divisione",
"Descrizione": null
},
{
"IdSchemaCustomFields": 80,
"ConteggioClienti": 0,
"Nome": "FENDI CALZATURA",
"Descrizione": "\r\n"
},
{
"IdSchemaCustomFields": 81,
"ConteggioClienti": 0,
"Nome": "LouBoutin",
"Descrizione": "\r\n"
},
{
"IdSchemaCustomFields": 82,
"ConteggioClienti": 0,
"Nome": "MONCLER Brand",
"Descrizione": "Da utilizzare solo per il Brand Diretto\r\nGR 19\/03\/2024"
},
{
"IdSchemaCustomFields": 83,
"ConteggioClienti": 0,
"Nome": "JIMMY CHOO",
"Descrizione": null
},
{
"IdSchemaCustomFields": 84,
"ConteggioClienti": 0,
"Nome": "Yves Saint Laurent",
"Descrizione": null
},
{
"IdSchemaCustomFields": 85,
"ConteggioClienti": 0,
"Nome": "BENETTON",
"Descrizione": null
},
{
"IdSchemaCustomFields": 86,
"ConteggioClienti": 0,
"Nome": "UPLOAD REPORT",
"Descrizione": null
},
{
"IdSchemaCustomFields": 87,
"ConteggioClienti": 0,
"Nome": "Hermes Calzatura",
"Descrizione": "\r\n\r\n\r\n"
},
{
"IdSchemaCustomFields": 88,
"ConteggioClienti": 0,
"Nome": "C&A",
"Descrizione": "Schema per fornitori e C&A diretto\r\n"
},
{
"IdSchemaCustomFields": 89,
"ConteggioClienti": 0,
"Nome": "AMERICAN EAGLE",
"Descrizione": "Schema per American Eagle\/Todd snyder\r\n\r\n\r\n\r\n"
},
{
"IdSchemaCustomFields": 91,
"ConteggioClienti": 0,
"Nome": "Ferragamo",
"Descrizione": "\r\n\r\n\r\n"
},
{
"IdSchemaCustomFields": 93,
"ConteggioClienti": 0,
"Nome": "Z_Alexander McQueen",
"Descrizione": "\r\n\r\n"
},
{
"IdSchemaCustomFields": 95,
"ConteggioClienti": 0,
"Nome": "Balenciaga Pelletteria",
"Descrizione": "\r\n"
},
{
"IdSchemaCustomFields": 96,
"ConteggioClienti": 0,
"Nome": "DUNHILL",
"Descrizione": "SCHEMA DUNHILL tutti i tipi di campioni\r\n"
},
{
"IdSchemaCustomFields": 97,
"ConteggioClienti": 0,
"Nome": "PRELIEVI ACQUE",
"Descrizione": null
},
{
"IdSchemaCustomFields": 98,
"ConteggioClienti": 0,
"Nome": "LouBoutin_Tossicologico",
"Descrizione": null
},
{
"IdSchemaCustomFields": 99,
"ConteggioClienti": 0,
"Nome": "ROBAN'S ",
"Descrizione": "tutte le divisioni\r\n\r\n"
},
{
"IdSchemaCustomFields": 100,
"ConteggioClienti": 0,
"Nome": "PROGETTO LUGGAGE",
"Descrizione": "Progetto Luggage Hermes\r\n\r\n\r\n\r\n"
},
{
"IdSchemaCustomFields": 102,
"ConteggioClienti": 0,
"Nome": "Balmain",
"Descrizione": "Schema per tutti i campioni Balmain"
},
{
"IdSchemaCustomFields": 103,
"ConteggioClienti": 0,
"Nome": "ZDHC",
"Descrizione": "Schema di Riferimento ZDHC Wastewaters"
},
{
"IdSchemaCustomFields": 104,
"ConteggioClienti": 0,
"Nome": "GUCCI CDC 4172",
"Descrizione": "\r\n"
},
{
"IdSchemaCustomFields": 105,
"ConteggioClienti": 0,
"Nome": "Z_ASOS",
"Descrizione": null
},
{
"IdSchemaCustomFields": 106,
"ConteggioClienti": 0,
"Nome": "Z_BOUX AVENUE",
"Descrizione": "28\/09 no indicazioni su esigenze di fatturazione (detto da Frosini)\r\n\r\n\r\n"
},
{
"IdSchemaCustomFields": 107,
"ConteggioClienti": 0,
"Nome": "BONPOINT",
"Descrizione": "05\/10 creazione\r\n\r\n\r\n\r\n"
},
{
"IdSchemaCustomFields": 108,
"ConteggioClienti": 0,
"Nome": "Canada Goose",
"Descrizione": null
},
{
"IdSchemaCustomFields": 109,
"ConteggioClienti": 0,
"Nome": "DIESEL",
"Descrizione": null
},
{
"IdSchemaCustomFields": 110,
"ConteggioClienti": 0,
"Nome": "AQC_Leather",
"Descrizione": null
},
{
"IdSchemaCustomFields": 111,
"ConteggioClienti": 0,
"Nome": "BEAUMANOIR (CC-Mor-Bon-Breal)",
"Descrizione": "04\/02\/21_ AGGIORNATO 05\/08\r\n"
},
{
"IdSchemaCustomFields": 112,
"ConteggioClienti": 0,
"Nome": "BooHoo",
"Descrizione": null
},
{
"IdSchemaCustomFields": 114,
"ConteggioClienti": 0,
"Nome": "Golden Goose",
"Descrizione": "Golden goose\r\n"
},
{
"IdSchemaCustomFields": 115,
"ConteggioClienti": 0,
"Nome": "Z_ZLABEL",
"Descrizione": "Schema per ZLABEL"
},
{
"IdSchemaCustomFields": 116,
"ConteggioClienti": 0,
"Nome": "Zadig & Voltaire",
"Descrizione": "Schema per Zadig & Voltaire"
},
{
"IdSchemaCustomFields": 117,
"ConteggioClienti": 0,
"Nome": "Gemo",
"Descrizione": "Schema solo per Gemo\r\n\r\n\r\n\r\n"
},
{
"IdSchemaCustomFields": 118,
"ConteggioClienti": 0,
"Nome": "PROMOD",
"Descrizione": "CHF 06\/08\r\n\r\n\r\n\r\n\r\n"
},
{
"IdSchemaCustomFields": 119,
"ConteggioClienti": 0,
"Nome": "LA HALLE (BEAUMANOIR)",
"Descrizione": "13\/08\/2021 CHF\r\n\r\n"
},
{
"IdSchemaCustomFields": 120,
"ConteggioClienti": 0,
"Nome": "LA REDOUTE",
"Descrizione": "14\/09\/2021 AD\r\n\r\n\r\n\r\n"
},
{
"IdSchemaCustomFields": 121,
"ConteggioClienti": 0,
"Nome": "TOD'S",
"Descrizione": "Schema per materiale gb18401 e GB20400\r\n\r\n\r\n"
},
{
"IdSchemaCustomFields": 122,
"ConteggioClienti": 0,
"Nome": "ASOS rev 1",
"Descrizione": null
},
{
"IdSchemaCustomFields": 123,
"ConteggioClienti": 0,
"Nome": "Monoprix",
"Descrizione": "Schema per Monoprix\r\n\r\n"
},
{
"IdSchemaCustomFields": 124,
"ConteggioClienti": 0,
"Nome": "DIOR Ita",
"Descrizione": "CHF 21\/03\r\n"
},
{
"IdSchemaCustomFields": 125,
"ConteggioClienti": 0,
"Nome": "GEOX - Raw Material",
"Descrizione": "SCHEMA DA USARE SOLO PER GEOX - SOLO PER LE SUOLE E MATERIALI DENOMINATI RPU \/ RAW MATERIAL"
},
{
"IdSchemaCustomFields": 126,
"ConteggioClienti": 0,
"Nome": "CAMAIEU",
"Descrizione": "SCHEMA DA USARE SOLO PER CAMAIEU\r\n"
},
{
"IdSchemaCustomFields": 127,
"ConteggioClienti": 0,
"Nome": "Z_Golden Goose FTW",
"Descrizione": "Golden goose\r\n"
},
{
"IdSchemaCustomFields": 128,
"ConteggioClienti": 0,
"Nome": "STEVE MADDEN - FOOTWEAR",
"Descrizione": "DA UTILIZZARE SOLO PER DESTINAZIONE D'USO CALZATURA\r\n"
},
{
"IdSchemaCustomFields": 129,
"ConteggioClienti": 0,
"Nome": "Carhartt",
"Descrizione": "SCHEMA DA UTILIZZARE SOLO PER RICHIESTE UFFICIALI CARHARTT\r\n\r\n\r\n"
},
{
"IdSchemaCustomFields": 131,
"ConteggioClienti": 0,
"Nome": "DAMART",
"Descrizione": "\r\n\r\n\r\n\r\n"
},
{
"IdSchemaCustomFields": 132,
"ConteggioClienti": 0,
"Nome": "Gruppo OTB",
"Descrizione": "Schema per Staff International, Diesel e Brave kid\r\n\r\n\r\n\r\n\r\n"
},
{
"IdSchemaCustomFields": 133,
"ConteggioClienti": 0,
"Nome": "FERRARI",
"Descrizione": "Schema da usare per Ferrari e fornitori\r\n\r\n"
},
{
"IdSchemaCustomFields": 134,
"ConteggioClienti": 0,
"Nome": "GAP - ABBIGLIAMENTO",
"Descrizione": "Schema GAP da utilizzare solo per destinazione abbigliamento\r\n"
},
{
"IdSchemaCustomFields": 135,
"ConteggioClienti": 0,
"Nome": "GAP - CALZATURA",
"Descrizione": "Schema GAP da utilizzare solo per destinazione calzatura\r\n\r\n"
},
{
"IdSchemaCustomFields": 136,
"ConteggioClienti": 0,
"Nome": "SALLING GROUP",
"Descrizione": "Schema per Salling Group\r\n\r\n\r\n\r\n\r\n"
},
{
"IdSchemaCustomFields": 137,
"ConteggioClienti": 0,
"Nome": "SOFT SURROUNDINGS",
"Descrizione": "Schema da utilizzare per Soft Surroundings\r\n\r\n"
},
{
"IdSchemaCustomFields": 138,
"ConteggioClienti": 0,
"Nome": "E-LIMS - Cuoio\/Pelle Standard \/ Leather",
"Descrizione": "Schema da utilizzare solo per accettazioni online\r\n"
},
{
"IdSchemaCustomFields": 139,
"ConteggioClienti": 0,
"Nome": "Z_ZALANDO",
"Descrizione": "Schema per ZALANDO ------ (NON per ZLABEL che ha il suo)\r\n"
},
{
"IdSchemaCustomFields": 140,
"ConteggioClienti": 0,
"Nome": "LVMH",
"Descrizione": "gennaio 2023\r\n"
},
{
"IdSchemaCustomFields": 141,
"ConteggioClienti": 0,
"Nome": "Valentino - eLims",
"Descrizione": "SCHEMA DA UTILIZZARE SOLO PER L'ACCETTAZIONE ONLINE DI VALENTINO"
},
{
"IdSchemaCustomFields": 142,
"ConteggioClienti": 0,
"Nome": "FENDI PELLETTERIA COMPLIANCE",
"Descrizione": "Solo per FENDI Pelletteria_INGLESE 04\/05\/2023\r\n"
},
{
"IdSchemaCustomFields": 143,
"ConteggioClienti": 0,
"Nome": "TAPESTRY",
"Descrizione": "04\/05\/2023, agg secondo procedura 08\/11\/23\r\n"
},
{
"IdSchemaCustomFields": 144,
"ConteggioClienti": 0,
"Nome": "ALEXANDER MC QUEEN",
"Descrizione": "ALEXANDER MC QUEEN 15\/05\/2023\r\n\r\n\r\n"
},
{
"IdSchemaCustomFields": 145,
"ConteggioClienti": 0,
"Nome": "Hermes_Chimici_Parigi",
"Descrizione": "\r\n"
},
{
"IdSchemaCustomFields": 146,
"ConteggioClienti": 0,
"Nome": "CELINE ABBIGLIAMENTO_Progetto Speciale",
"Descrizione": "IO 202 CELINE ABBIGLIAMENTO_06\/06\/203_CHF\r\n\r\n"
},
{
"IdSchemaCustomFields": 147,
"ConteggioClienti": 0,
"Nome": "GEOX - Prodotto Finito",
"Descrizione": "SCHEMA DA USARE SOLO PER GEOX - SOLO PER PRODOTTO FINITO"
},
{
"IdSchemaCustomFields": 149,
"ConteggioClienti": 0,
"Nome": "Hermes\/IDO",
"Descrizione": "creato il 05\/07\/2023 CHF\r\n"
},
{
"IdSchemaCustomFields": 150,
"ConteggioClienti": 0,
"Nome": "Tessile \/ Textiles PER CLIENTI CON OBLO",
"Descrizione": "Schema per tutti i Tessuti QUANDO IL CLIENTE NECESSITA DI NR OBLO\r\n\r\n\r\n\r\n"
},
{
"IdSchemaCustomFields": 151,
"ConteggioClienti": 0,
"Nome": "J JIll",
"Descrizione": "Schema per cliente J JIll, necessario riportare la recommended fiber content in schema verde\r\n\r\n\r\n\r\n\r\n"
},
{
"IdSchemaCustomFields": 152,
"ConteggioClienti": 0,
"Nome": "AUCHAN - TAPE A L'OEIL",
"Descrizione": "Schema AUCHAN da utilizzare solo per la divisione TAPE A L'OEIL SA, successivamente se uguale da usare anche per le altre divisioni ed uniformarlo come generico"
},
{
"IdSchemaCustomFields": 153,
"ConteggioClienti": 0,
"Nome": "ALL SAINTS - BUSCEMI",
"Descrizione": "SCHE DA USARE SOLO PER ALL SAINTS E BUSCEMI\r\n\r\n\r\n\r\n"
},
{
"IdSchemaCustomFields": 154,
"ConteggioClienti": 0,
"Nome": "CELINE - LVMH",
"Descrizione": "Schema con aggiunta campi LVMH per estrazioni\r\nAD-29\/03\/2024"
},
{
"IdSchemaCustomFields": 155,
"ConteggioClienti": 0,
"Nome": "SeyMeChamLou",
"Descrizione": "DA USARE SOLO PER SEYMECHAMLOU (LOUBOUTIN)\r\n"
},
{
"IdSchemaCustomFields": 156,
"ConteggioClienti": 0,
"Nome": "PRADA",
"Descrizione": "PER ADESSO DA USARE PER ABBIGLIAMENTO E CALZATURA\r\n\r\n\r\n\r\n"
},
{
"IdSchemaCustomFields": 157,
"ConteggioClienti": 0,
"Nome": "Hermes_HCP",
"Descrizione": "Schema per unicamente HCP"
},
{
"IdSchemaCustomFields": 158,
"ConteggioClienti": 0,
"Nome": "STONE ISLAND",
"Descrizione": "Schema per tutti i campioni in cuoio\/pelle per i quali non \u00e8 specificato un destinatario.\r\n"
},
{
"IdSchemaCustomFields": 159,
"ConteggioClienti": 0,
"Nome": "Chanel_Pierre Damien",
"Descrizione": "Schema da usare per Campioni ricevuti da Pierre-Damien Verine e suo staff CHANEL.\r\n"
},
{
"IdSchemaCustomFields": 160,
"ConteggioClienti": 0,
"Nome": "Z_ACNE",
"Descrizione": "\r\n.\r\n"
},
{
"IdSchemaCustomFields": 161,
"ConteggioClienti": 0,
"Nome": "OBERALP GROUP",
"Descrizione": "Schema da usare solo per Oberalp Group\r\n"
},
{
"IdSchemaCustomFields": 162,
"ConteggioClienti": 0,
"Nome": "Club Monaco",
"Descrizione": "da usare solo per Club Monaco GR 20\/02\/2024"
},
{
"IdSchemaCustomFields": 163,
"ConteggioClienti": 0,
"Nome": "Devred",
"Descrizione": "Schema creato per cliente DEVRED\r\nGR 18\/03\/2024\r\n\r\n"
},
{
"IdSchemaCustomFields": 164,
"ConteggioClienti": 0,
"Nome": "MONCLER Supplier",
"Descrizione": "Da utilizzare per fornitori Moncler \r\nGR 19\/03\/2024"
},
{
"IdSchemaCustomFields": 165,
"ConteggioClienti": 0,
"Nome": "Kering Sunglasses",
"Descrizione": "Schema per tutti i campioni di qualsiasi matrice escluso cuoio\/pelle\r\n\r\n"
},
{
"IdSchemaCustomFields": 166,
"ConteggioClienti": 0,
"Nome": "Giorgio Armani",
"Descrizione": "\r\n\r\n"
},
{
"IdSchemaCustomFields": 167,
"ConteggioClienti": 0,
"Nome": "Chanel USA",
"Descrizione": "\r\n"
},
{
"IdSchemaCustomFields": 168,
"ConteggioClienti": 0,
"Nome": "Staff Calzatura",
"Descrizione": "Schema per tutti i campioni di qualsiasi matrice escluso cuoio\/pelle\r\n\r\n"
},
{
"IdSchemaCustomFields": 169,
"ConteggioClienti": 0,
"Nome": "P&G SRL",
"Descrizione": "\r\n"
},
{
"IdSchemaCustomFields": 170,
"ConteggioClienti": 0,
"Nome": "INCOM",
"Descrizione": "Schema da utilizzare per INCOM"
},
{
"IdSchemaCustomFields": 171,
"ConteggioClienti": 0,
"Nome": "RIVER ISLAND",
"Descrizione": "SCHEDA DA USARE SOLO PER RIVER ISLAND - NO CALZATURA\r\n\r\n\r\n"
},
{
"IdSchemaCustomFields": 172,
"ConteggioClienti": 0,
"Nome": "Longchamp",
"Descrizione": "Schema per tutti i campioni con RIchiesta Longchamp ufficiale\r\n"
},
{
"IdSchemaCustomFields": 173,
"ConteggioClienti": 0,
"Nome": "Bottega Veneta",
"Descrizione": "Schema per tutti i campioni di Bottega Veneta di Stella"
},
{
"IdSchemaCustomFields": 174,
"ConteggioClienti": 0,
"Nome": "Mint Velvet",
"Descrizione": "Schema per tutti i campioni di Mint Velvel con richiesta ufficiale.\r\n\r\n"
},
{
"IdSchemaCustomFields": 176,
"ConteggioClienti": 0,
"Nome": "RIVER ISLAND FOOTWEAR",
"Descrizione": "SCHEDA DA USARE SOLO PER RIVER ISLAND - USARE SOLO PER\r\n CALZATURA\r\n"
},
{
"IdSchemaCustomFields": 177,
"ConteggioClienti": 0,
"Nome": "Phoebe philo ACC",
"Descrizione": "(scarpe, borse, cinture, occhiali, gioielleria)\r\n"
},
{
"IdSchemaCustomFields": 178,
"ConteggioClienti": 0,
"Nome": "New Guards Group",
"Descrizione": "\r\n"
},
{
"IdSchemaCustomFields": 179,
"ConteggioClienti": 0,
"Nome": "Giorgio Armani Operation",
"Descrizione": "\r\n\r\n"
},
{
"IdSchemaCustomFields": 180,
"ConteggioClienti": 0,
"Nome": "Chloe'",
"Descrizione": "\r\n"
},
{
"IdSchemaCustomFields": 181,
"ConteggioClienti": 0,
"Nome": "LVMH non diretti",
"Descrizione": "utilizzato ogni qualvolta siano caricati pacchetti \/ prove singole che non rientrano nel programma LVMH \r\n"
},
{
"IdSchemaCustomFields": 182,
"ConteggioClienti": 0,
"Nome": "Ralph Lauren - All testing V.6",
"Descrizione": null
},
{
"IdSchemaCustomFields": 183,
"ConteggioClienti": 0,
"Nome": "TWINSET",
"Descrizione": "Schema da usare per Twinset - come da mail di EP del 20\/05\/2025\r\n\r\n"
},
{
"IdSchemaCustomFields": 184,
"ConteggioClienti": 0,
"Nome": "ACNE STUDIOS rev 01",
"Descrizione": null
},
{
"IdSchemaCustomFields": 185,
"ConteggioClienti": 0,
"Nome": "Brunello cucinelli",
"Descrizione": null
},
{
"IdSchemaCustomFields": 186,
"ConteggioClienti": 0,
"Nome": "TWINSET",
"Descrizione": "Schema per tutti i campioni di qualsiasi matrice escluso cuoio\/pelle\r\n\r\n"
},
{
"IdSchemaCustomFields": 187,
"ConteggioClienti": 0,
"Nome": "LORO PIANA",
"Descrizione": "Schema per tutti i campioni Loro Piana\r\n"
},
{
"IdSchemaCustomFields": 188,
"ConteggioClienti": 0,
"Nome": "LEMAIRE",
"Descrizione": "Schema per tutti i campioni in cuoio\/pelle per i quali non \u00e8 specificato un destinatario.\r\n"
},
{
"IdSchemaCustomFields": 189,
"ConteggioClienti": 0,
"Nome": "TEST_IT_SCHEMA",
"Descrizione": "Test per IT"
},
{
"IdSchemaCustomFields": 190,
"ConteggioClienti": 0,
"Nome": "LBS Interscambio",
"Descrizione": "Schema per tutti i campioni interscambio LBS\r\n"
},
{
"IdSchemaCustomFields": 191,
"ConteggioClienti": 0,
"Nome": "KIABI",
"Descrizione": "Schema per il cliente Kiabi\r\n"
},
{
"IdSchemaCustomFields": 192,
"ConteggioClienti": 0,
"Nome": "INDITEX",
"Descrizione": "Schema da usare solo per richieste INDITEX"
},
{
"IdSchemaCustomFields": 193,
"ConteggioClienti": 0,
"Nome": "AQC_Other materials",
"Descrizione": null
},
{
"IdSchemaCustomFields": 194,
"ConteggioClienti": 0,
"Nome": "Hermes ADM & Silk",
"Descrizione": "SCHEMA DA USARE SOLO PER LA DIVISIONE ADM & SILK\r\n\r\n\r\n\r\n"
},
{
"IdSchemaCustomFields": 195,
"ConteggioClienti": 0,
"Nome": "Chloe'_APPAREL",
"Descrizione": "Schema aggiornato il 18\/12\/25 secondo modulo excel x codifica Elims\r\nGR"
},
{
"IdSchemaCustomFields": 196,
"ConteggioClienti": 0,
"Nome": "DUNHILL_DRAFT NON USARE",
"Descrizione": "SCHEMA DUNHILL NON USARE STUDIO NUOVA PROCEDURA NOVEMBRE 2025\r\n\r\n"
},
{
"IdSchemaCustomFields": 197,
"ConteggioClienti": 0,
"Nome": "BERLUTI - LVMH",
"Descrizione": "Creato per accettazione Elims\r\n"
},
{
"IdSchemaCustomFields": 198,
"ConteggioClienti": 0,
"Nome": "PIAGET (gruppo Cartier)",
"Descrizione": "Schema per tutti i campioni PIAGET (gruppo Cartier)\r\n\r\n"
},
{
"IdSchemaCustomFields": 199,
"ConteggioClienti": 0,
"Nome": "HERMES PAP FEMME",
"Descrizione": "Schema per tutti i campioni di HERMES PAP FEMME \r\n15\/01\/2025\r\n\r\n\r\n"
}
]
}
-35
View File
@@ -1,35 +0,0 @@
<?php
header('Content-Type: application/json');
require_once(__DIR__ . '/include/headscript.php');
$db = DBHandlerSelect::getInstance();
$pdo = $db->getConnection();
$input = json_decode(file_get_contents('php://input'), true);
$id = (int)($input['id'] ?? 0);
$field = (string)($input['field'] ?? '');
$value = $input['value'] ?? null;
if ($id <= 0) {
echo json_encode(['success' => false, 'message' => 'Invalid id']);
exit;
}
$allowed = ['default_value', 'is_visible_import', 'is_required'];
if (!in_array($field, $allowed, true)) {
echo json_encode(['success' => false, 'message' => 'Invalid field']);
exit;
}
if ($field === 'is_visible_import' || $field === 'is_required') {
$value = ((int)$value === 1) ? 1 : 0;
}
$sql = "UPDATE template_fixed_mapping SET {$field} = :val WHERE id = :id";
$stmt = $pdo->prepare($sql);
$ok = $stmt->execute([':val' => $value, ':id' => $id]);
echo json_encode(['success' => (bool)$ok]);