diff --git a/.gitignore b/.gitignore
index 568f76e..5f152ec 100644
--- a/.gitignore
+++ b/.gitignore
@@ -51,4 +51,16 @@ public/userarea/class/curl_request_debug.log
# Ignora cartella photostrf in public/userarea
/public/userarea/photostrf/
public/userarea/customfield_values_response.json
+/public/userarea/logaspi/
+public/userarea/logsapi/campione_762_1.json
+public/userarea/logsapi/campione_763_1.json
+public/userarea/logsapi/campione_762_2.json
+public/userarea/logsapi/campione_763_2.json
+public/userarea/logsapi/commessaweb_create_762.json
+public/userarea/logsapi/commessaweb_create_763.json
+public/userarea/logsapi/commessaweb_customfields_762.json
+public/userarea/logsapi/commessaweb_customfields_763.json
+public/userarea/logsapi/commessaweb_invia_762.json
+public/userarea/logsapi/commessaweb_invia_763.json
+public/userarea/logsapi/last_auth_url.txt
diff --git a/public/userarea/export_to_lims.js b/public/userarea/export_to_lims.js
new file mode 100644
index 0000000..74e2be2
--- /dev/null
+++ b/public/userarea/export_to_lims.js
@@ -0,0 +1,239 @@
+document.addEventListener("DOMContentLoaded", () => {
+ console.log("export_to_lims.js loaded");
+
+ // Debug: verifica che i pulsanti siano trovati
+ const exportButtons = document.querySelectorAll(".export-lims-btn");
+ console.log(`Found ${exportButtons.length} export-lims-btn buttons`);
+
+ if (exportButtons.length === 0) {
+ console.warn("No .export-lims-btn buttons found in the DOM");
+ return;
+ }
+
+ exportButtons.forEach((btn) => {
+ btn.addEventListener("click", (e) => {
+ e.preventDefault();
+ const rowIndex = btn.dataset.row;
+ const iddatadb = btn.dataset.iddatadb;
+ console.log(
+ `Export to LIMS clicked for row ${rowIndex}, iddatadb: ${iddatadb}`,
+ );
+
+ // Mostra il modale di conferma
+ const confirmModalElement =
+ document.getElementById("exportConfirmModal");
+ if (!confirmModalElement) {
+ console.error("exportConfirmModal not found in the DOM");
+ alert("Errore: Modale di conferma non trovato");
+ return;
+ }
+
+ const confirmModal = new bootstrap.Modal(confirmModalElement, {
+ keyboard: false,
+ });
+ document.getElementById("exportIddatadb").textContent = iddatadb;
+ confirmModal.show();
+
+ // Gestisci il click su "Conferma"
+ const confirmBtn = document.getElementById("exportConfirmBtn");
+ if (!confirmBtn) {
+ console.error("exportConfirmBtn not found in the DOM");
+ confirmModal.hide();
+ alert("Errore: Pulsante di conferma non trovato");
+ return;
+ }
+
+ const confirmHandler = async () => {
+ console.log(`Confirmed export for iddatadb: ${iddatadb}`);
+ confirmModal.hide();
+
+ const formData = new FormData();
+ formData.append("iddatadb", iddatadb);
+
+ try {
+ const response = await fetch("export_to_lims.php", {
+ method: "POST",
+ body: formData,
+ });
+ if (!response.ok)
+ throw new Error(
+ `HTTP error! status: ${response.status}`,
+ );
+ const data = await response.json();
+
+ console.log("Export response:", data);
+
+ // Mostra il modale di risposta
+ const responseModalElement = document.getElementById(
+ "exportResponseModal",
+ );
+ if (!responseModalElement) {
+ console.error(
+ "exportResponseModal not found in the DOM",
+ );
+ alert("Errore: Modale di risposta non trovato");
+ return;
+ }
+
+ const responseModal = new bootstrap.Modal(
+ responseModalElement,
+ {
+ keyboard: false,
+ },
+ );
+ const responseMessage = document.getElementById(
+ "exportResponseMessage",
+ );
+ if (data.success) {
+ responseMessage.innerHTML = `${data.message.replace(/\n/g, "
")}
ID CommessaWeb: ${data.idcommessaweb}`;
+ document.getElementById(
+ "exportResponseModalLabel",
+ ).textContent = "Esportazione Completata";
+ responseModal.show();
+
+ // Aggiorna la UI per riflettere lo stato 'To LIMS'
+ const statusCell = btn
+ .closest(".grid-row")
+ .querySelector(
+ '.grid-cell[data-col="status"] .status-badge',
+ );
+ if (statusCell) {
+ statusCell.classList.remove("status-i", "status-P");
+ statusCell.classList.add("status-l");
+ statusCell.textContent = "To LIMS";
+ }
+
+ // Gestisci la chiusura del modale di risposta
+ responseModalElement.addEventListener(
+ "hidden.bs.modal",
+ () => {
+ console.log(
+ "exportResponseModal closed, cleaning up",
+ );
+ // Rimuovi tutti i backdrop residui
+ document
+ .querySelectorAll(".modal-backdrop")
+ .forEach((backdrop) => {
+ console.log(
+ "Removing backdrop:",
+ backdrop,
+ );
+ backdrop.remove();
+ });
+ // Ripristina il body
+ document.body.classList.remove("modal-open");
+ document.body.style.paddingRight = "";
+ // Nascondi l'overlay
+ const overlay = document.querySelector(
+ ".overlay.toggle-icon",
+ );
+ if (overlay) {
+ overlay.style.display = "none";
+ }
+ },
+ { once: true },
+ );
+ } else {
+ responseMessage.textContent = `Errore durante la generazione dei payload: ${data.message}`;
+ document.getElementById(
+ "exportResponseModalLabel",
+ ).textContent = "Errore Esportazione";
+ responseModal.show();
+
+ // Gestisci la chiusura del modale di risposta anche in caso di errore
+ responseModalElement.addEventListener(
+ "hidden.bs.modal",
+ () => {
+ console.log(
+ "exportResponseModal closed, cleaning up",
+ );
+ // Rimuovi tutti i backdrop residui
+ document
+ .querySelectorAll(".modal-backdrop")
+ .forEach((backdrop) => {
+ console.log(
+ "Removing backdrop:",
+ backdrop,
+ );
+ backdrop.remove();
+ });
+ // Ripristina il body
+ document.body.classList.remove("modal-open");
+ document.body.style.paddingRight = "";
+ // Nascondi l'overlay
+ const overlay = document.querySelector(
+ ".overlay.toggle-icon",
+ );
+ if (overlay) {
+ overlay.style.display = "none";
+ }
+ },
+ { once: true },
+ );
+ }
+ } catch (error) {
+ console.error("Export error:", error);
+ const responseModalElement = document.getElementById(
+ "exportResponseModal",
+ );
+ if (!responseModalElement) {
+ console.error(
+ "exportResponseModal not found in the DOM",
+ );
+ alert("Errore: Modale di risposta non trovato");
+ return;
+ }
+ const responseModal = new bootstrap.Modal(
+ responseModalElement,
+ {
+ keyboard: false,
+ },
+ );
+ document.getElementById(
+ "exportResponseMessage",
+ ).textContent =
+ `Errore durante la generazione dei payload: ${error.message}`;
+ document.getElementById(
+ "exportResponseModalLabel",
+ ).textContent = "Errore Esportazione";
+ responseModal.show();
+
+ // Gestisci la chiusura del modale di risposta in caso di errore
+ responseModalElement.addEventListener(
+ "hidden.bs.modal",
+ () => {
+ console.log(
+ "exportResponseModal closed, cleaning up",
+ );
+ // Rimuovi tutti i backdrop residui
+ document
+ .querySelectorAll(".modal-backdrop")
+ .forEach((backdrop) => {
+ console.log("Removing backdrop:", backdrop);
+ backdrop.remove();
+ });
+ // Ripristina il body
+ document.body.classList.remove("modal-open");
+ document.body.style.paddingRight = "";
+ // Nascondi l'overlay
+ const overlay = document.querySelector(
+ ".overlay.toggle-icon",
+ );
+ if (overlay) {
+ overlay.style.display = "none";
+ }
+ },
+ { once: true },
+ );
+ }
+
+ // Rimuovi il listener dopo l'esecuzione
+ confirmBtn.removeEventListener("click", confirmHandler);
+ };
+
+ // Rimuovi eventuali listener precedenti
+ confirmBtn.removeEventListener("click", confirmHandler);
+ confirmBtn.addEventListener("click", confirmHandler);
+ });
+ });
+});
diff --git a/public/userarea/export_to_lims.php b/public/userarea/export_to_lims.php
new file mode 100644
index 0000000..43798d0
--- /dev/null
+++ b/public/userarea/export_to_lims.php
@@ -0,0 +1,199 @@
+getConnection();
+
+ // Simula idcommessaweb per il test (sostituire con risposta API nel flusso reale)
+ $idcommessaweb = 10176; // Fittizio per il test
+
+ // Salva idcommessaweb in datadb (simulazione per il test)
+ $updateStmt = $pdo->prepare("UPDATE datadb SET idcommessaweb = :idcommessaweb WHERE iddatadb = :iddatadb");
+ $updateStmt->execute(['idcommessaweb' => $idcommessaweb, 'iddatadb' => $iddatadb]);
+
+ // Step 1: Creazione payload per CommessaWeb
+ $queryCommessa = "
+ SELECT
+ d.iddatadb,
+ e.idclient AS Cliente,
+ e.idschema AS SchemaCustomField
+ FROM datadb d
+ LEFT JOIN excel_templates e ON d.templateid = e.id
+ WHERE d.iddatadb = :iddatadb
+ ";
+
+ $stmtCommessa = $pdo->prepare($queryCommessa);
+ $stmtCommessa->execute(['iddatadb' => $iddatadb]);
+ $recordCommessa = $stmtCommessa->fetch(PDO::FETCH_ASSOC);
+
+ if (!$recordCommessa) {
+ throw new Exception("Nessun record trovato per iddatadb: {$iddatadb}");
+ }
+
+ // Payload per creazione CommessaWeb
+ $payloadCommessa = [
+ 'Cliente' => (int)$recordCommessa['Cliente'],
+ 'SchemaCustomField' => (int)$recordCommessa['SchemaCustomField'],
+ 'Richiedente' => null,
+ 'Descrizione' => 'example'
+ ];
+
+ // Salva il payload di creazione CommessaWeb
+ $outputFileCommessa = $logDir . "/commessaweb_create_{$iddatadb}.json";
+ file_put_contents($outputFileCommessa, json_encode($payloadCommessa, JSON_PRETTY_PRINT));
+
+ // Step 2: Creazione payload per campi custom (CommesseCustomFields)
+ $queryCustomFields = "
+ SELECT
+ tm.field_id AS IdCommesseCustomFields,
+ idd.field_value AS Valore
+ FROM import_data_details idd
+ JOIN template_mapping tm ON idd.mapping_id = tm.id
+ WHERE idd.id = :iddatadb
+ ";
+
+ $stmtCustomFields = $pdo->prepare($queryCustomFields);
+ $stmtCustomFields->execute(['iddatadb' => $iddatadb]);
+ $customFields = $stmtCustomFields->fetchAll(PDO::FETCH_ASSOC);
+
+ // Costruisci l'array CommesseCustomFields
+ $commesseCustomFields = [];
+ foreach ($customFields as $field) {
+ $commesseCustomFields[] = [
+ 'IdCommesseCustomFields' => (int)$field['IdCommesseCustomFields'],
+ 'Valore' => $field['Valore'] ?? ''
+ ];
+ }
+
+ // Payload per aggiornamento campi custom
+ $payloadCustomFields = [
+ 'CommesseCustomFields' => $commesseCustomFields
+ ];
+
+ // Salva il payload dei campi custom
+ $outputFileCustomFields = $logDir . "/commessaweb_customfields_{$iddatadb}.json";
+ file_put_contents($outputFileCustomFields, json_encode($payloadCustomFields, JSON_PRETTY_PRINT));
+
+ // Step 3: Creazione payload per Campioni (da identification_parts)
+ $queryCampioni = "
+ SELECT
+ part_number,
+ idmatrice AS Matrice,
+ part_description AS NoteWeb
+ FROM identification_parts
+ WHERE iddatadb = :iddatadb
+ ";
+
+ $stmtCampioni = $pdo->prepare($queryCampioni);
+ $stmtCampioni->execute(['iddatadb' => $iddatadb]);
+ $campioni = $stmtCampioni->fetchAll(PDO::FETCH_ASSOC);
+
+ $payloadsCampioni = [];
+ foreach ($campioni as $campione) {
+ $payloadCampione = [
+ 'Commessa' => $idcommessaweb, // Usa idcommessaweb simulato
+ 'Matrice' => (int)$campione['Matrice'],
+ 'SottoMatrice' => null,
+ 'SchemaCustomField' => 1,
+ 'NoteWeb' => $campione['NoteWeb'] ?? ''
+ ];
+
+ // Salva il payload del campione
+ $outputFileCampione = $logDir . "/campione_{$iddatadb}_{$campione['part_number']}.json";
+ file_put_contents($outputFileCampione, json_encode($payloadCampione, JSON_PRETTY_PRINT));
+ $payloadsCampioni[] = $payloadCampione;
+ }
+
+ // Step 4: Creazione payload per InviaCommessa
+ $payloadInviaCommessa = []; // Corpo vuoto, come tipico per azioni OData
+ $outputFileInviaCommessa = $logDir . "/commessaweb_invia_{$iddatadb}.json";
+ file_put_contents($outputFileInviaCommessa, json_encode($payloadInviaCommessa, JSON_PRETTY_PRINT));
+
+ // Aggiorna lo status a 'l' (To LIMS)
+ $updateStmt = $pdo->prepare("UPDATE datadb SET status = 'l' WHERE iddatadb = :iddatadb");
+ $updateStmt->execute(['iddatadb' => $iddatadb]);
+
+ // Risposta di successo
+ echo json_encode([
+ 'success' => true,
+ 'message' => "Payload generati e salvati in {$outputFileCommessa}, {$outputFileCustomFields}, file campioni e {$outputFileInviaCommessa}",
+ 'idcommessaweb' => $idcommessaweb,
+ 'payload_commessa' => $payloadCommessa,
+ 'payload_customfields' => $payloadCustomFields,
+ 'payload_campioni' => $payloadsCampioni,
+ 'payload_invia_commessa' => $payloadInviaCommessa
+ ]);
+} catch (Exception $e) {
+ // Log dell'errore
+ file_put_contents($logDir . '/export_lims_error.log', date('Y-m-d H:i:s') . ' - ' . $e->getMessage() . PHP_EOL, FILE_APPEND);
+ http_response_code(500);
+ echo json_encode([
+ 'success' => false,
+ 'message' => 'Errore: ' . $e->getMessage()
+ ]);
+}
+
+/* Flusso reale (commentato, da attivare quando pronto)
+try {
+ $apiClient = VisualLimsApiClient::getInstance();
+
+ // Step 1: Crea CommessaWeb
+ $response = $apiClient->post('/api/odata/CommessaWeb', $payloadCommessa);
+ if ($response['success'] && isset($response['CommessaId'])) {
+ $idcommessaweb = $response['CommessaId'];
+ // Salva idcommessaweb in datadb
+ $updateStmt = $pdo->prepare("UPDATE datadb SET idcommessaweb = :idcommessaweb WHERE iddatadb = :iddatadb");
+ $updateStmt->execute(['idcommessaweb' => $idcommessaweb, 'iddatadb' => $iddatadb]);
+ } else {
+ throw new Exception('Errore nella creazione della CommessaWeb: ' . json_encode($response));
+ }
+
+ // Step 2: Aggiorna CommesseCustomFields
+ $apiClient->patch("/api/odata/CommessaWeb({$idcommessaweb})", $payloadCustomFields);
+
+ // Step 3: Crea Campioni
+ foreach ($payloadsCampioni as $payloadCampione) {
+ $apiClient->post('/api/odata/Campione', $payloadCampione);
+ }
+
+ // Step 4: Invia Commessa
+ $apiClient->post("/api/odata/CommessaWeb({$idcommessaweb})/InviaCommessa", $payloadInviaCommessa);
+
+ // (Opzionale) Recupera il numero commessaweb con una GET
+ $commessaData = $apiClient->get("/api/odata/CommessaWeb({$idcommessaweb})");
+ $commessaweb = $commessaData['Numero'] ?? ''; // Da confermare il nome del campo
+ if ($commessaweb) {
+ $updateStmt = $pdo->prepare("UPDATE datadb SET commessaweb = :commessaweb WHERE iddatadb = :iddatadb");
+ $updateStmt->execute(['commessaweb' => $commessaweb, 'iddatadb' => $iddatadb]);
+ }
+} catch (Exception $e) {
+ file_put_contents($logDir . '/export_lims_error.log', date('Y-m-d H:i:s') . ' - Flusso reale fallito: ' . $e->getMessage() . PHP_EOL, FILE_APPEND);
+ throw $e;
+}
+*/
diff --git a/public/userarea/import_edit2.php b/public/userarea/import_edit2.php
index e23039d..9ff0fea 100644
--- a/public/userarea/import_edit2.php
+++ b/public/userarea/import_edit2.php
@@ -405,6 +405,16 @@ foreach ($stmt->fetchAll(PDO::FETCH_ASSOC) as $row) {
.save-all-btn:hover {
background-color: #218838;
}
+
+ #exportConfirmModal,
+ #exportResponseModal {
+ z-index: 1300 !important;
+ }
+
+ #exportConfirmModal .modal-backdrop,
+ #exportResponseModal .modal-backdrop {
+ z-index: 1299 !important;
+ }