Compare commits
4 Commits
540c44d89a
...
2598a4c91b
| Author | SHA1 | Date | |
|---|---|---|---|
| 2598a4c91b | |||
| 5e677a8b9c | |||
| 1fed113c5c | |||
| e3994d6f9f |
@ -24,7 +24,17 @@ class VisualLimsApiClient
|
|||||||
public static function getInstance()
|
public static function getInstance()
|
||||||
{
|
{
|
||||||
if (self::$instance === null) {
|
if (self::$instance === null) {
|
||||||
self::$instance = new VisualLimsApiClient();
|
$dotenv = Dotenv::createImmutable(dirname(__DIR__, 3));
|
||||||
|
$dotenv->load();
|
||||||
|
|
||||||
|
$simulate = ($_ENV['SIMULATE_EXPORT_LIMS'] ?? '') === 'true';
|
||||||
|
|
||||||
|
if ($simulate) {
|
||||||
|
require_once __DIR__ . '/VisualLimsApiClientMock.class.php';
|
||||||
|
self::$instance = new VisualLimsApiClientMock();
|
||||||
|
} else {
|
||||||
|
self::$instance = new VisualLimsApiClient();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return self::$instance;
|
return self::$instance;
|
||||||
}
|
}
|
||||||
@ -193,6 +203,56 @@ class VisualLimsApiClient
|
|||||||
return json_decode($response, true);
|
return json_decode($response, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* POST a file as multipart/form-data (used for photo/attachment uploads).
|
||||||
|
*
|
||||||
|
* @param string $endpoint OData endpoint, e.g. "AllegatoCommessaWeb"
|
||||||
|
* @param string $filePath Absolute path to the file on disk
|
||||||
|
* @param string $fileName Original file name to send
|
||||||
|
* @param int $commessaId CommessaWeb ID to link the attachment to
|
||||||
|
* @return array|null Decoded JSON response
|
||||||
|
*/
|
||||||
|
public function postMultipart($endpoint, $filePath, $fileName, $commessaId)
|
||||||
|
{
|
||||||
|
$token = $this->getToken();
|
||||||
|
$url = "{$this->baseUrl}/api/odata/{$endpoint}";
|
||||||
|
|
||||||
|
$cfile = new CURLFile($filePath, mime_content_type($filePath) ?: 'application/octet-stream', $fileName);
|
||||||
|
|
||||||
|
$payload = [
|
||||||
|
'IdCommessa' => $commessaId,
|
||||||
|
'NomeFile' => $fileName,
|
||||||
|
'file' => $cfile,
|
||||||
|
];
|
||||||
|
|
||||||
|
$ch = curl_init($url);
|
||||||
|
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
|
||||||
|
curl_setopt($ch, CURLOPT_POST, true);
|
||||||
|
curl_setopt($ch, CURLOPT_POSTFIELDS, $payload);
|
||||||
|
curl_setopt($ch, CURLOPT_HTTPHEADER, [
|
||||||
|
"Authorization: Bearer {$token}",
|
||||||
|
"Accept: application/json",
|
||||||
|
// Content-Type is set automatically to multipart/form-data by cURL
|
||||||
|
]);
|
||||||
|
curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);
|
||||||
|
curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, false);
|
||||||
|
|
||||||
|
$response = curl_exec($ch);
|
||||||
|
$http_code = curl_getinfo($ch, CURLINFO_HTTP_CODE);
|
||||||
|
$curl_error = curl_error($ch);
|
||||||
|
curl_close($ch);
|
||||||
|
|
||||||
|
if ($response === false) {
|
||||||
|
throw new Exception("Errore nella richiesta POST multipart: {$curl_error}");
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($http_code < 200 || $http_code >= 300) {
|
||||||
|
throw new Exception("POST multipart fallito: HTTP {$http_code}, Risposta: " . substr($response, 0, 1000));
|
||||||
|
}
|
||||||
|
|
||||||
|
return json_decode($response, true);
|
||||||
|
}
|
||||||
|
|
||||||
public function getBaseUrl()
|
public function getBaseUrl()
|
||||||
{
|
{
|
||||||
return $this->baseUrl;
|
return $this->baseUrl;
|
||||||
|
|||||||
135
public/userarea/class/VisualLimsApiClientMock.class.php
Normal file
135
public/userarea/class/VisualLimsApiClientMock.class.php
Normal file
@ -0,0 +1,135 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Mock implementation of VisualLimsApiClient.
|
||||||
|
* Activated when SIMULATE_EXPORT_LIMS=true in .env.
|
||||||
|
* All HTTP calls are skipped; fake but structurally valid data is returned.
|
||||||
|
* Every simulated call is logged via error_log() with a [SIMULATE] prefix.
|
||||||
|
*/
|
||||||
|
class VisualLimsApiClientMock
|
||||||
|
{
|
||||||
|
private int $fakeCommessaId;
|
||||||
|
|
||||||
|
public function __construct()
|
||||||
|
{
|
||||||
|
// Stable fake ID for the lifetime of a single request
|
||||||
|
$this->fakeCommessaId = mt_rand(90001, 99999);
|
||||||
|
error_log("[SIMULATE] VisualLimsApiClientMock initialised (fakeCommessaId={$this->fakeCommessaId})");
|
||||||
|
}
|
||||||
|
|
||||||
|
public function get(string $endpoint): array
|
||||||
|
{
|
||||||
|
error_log("[SIMULATE] GET {$endpoint}");
|
||||||
|
|
||||||
|
// --- Fixed-field dropdown lists ---
|
||||||
|
|
||||||
|
if (str_starts_with($endpoint, 'MoltiplicatorePrezzi')) {
|
||||||
|
return ['value' => [
|
||||||
|
['IdMoltiplicatorePrezzo' => 1, 'Codice' => 'MP-01', 'Descrizione' => 'Standard (1x)'],
|
||||||
|
['IdMoltiplicatorePrezzo' => 2, 'Codice' => 'MP-02', 'Descrizione' => 'Urgente (1.5x)'],
|
||||||
|
['IdMoltiplicatorePrezzo' => 3, 'Codice' => 'MP-03', 'Descrizione' => 'Extra Urgente (2x)'],
|
||||||
|
]];
|
||||||
|
}
|
||||||
|
|
||||||
|
if (str_starts_with($endpoint, 'AnagraficaCertestObject')) {
|
||||||
|
return ['value' => [
|
||||||
|
['IdAnagrafica' => 1, 'Codice' => 'OBJ-01', 'NomeAnagrafica' => 'Articolo Tessile'],
|
||||||
|
['IdAnagrafica' => 2, 'Codice' => 'OBJ-02', 'NomeAnagrafica' => 'Componente Meccanico'],
|
||||||
|
['IdAnagrafica' => 3, 'Codice' => 'OBJ-03', 'NomeAnagrafica' => 'Materiale Plastico'],
|
||||||
|
]];
|
||||||
|
}
|
||||||
|
|
||||||
|
if (str_starts_with($endpoint, 'AnagraficaCertestService')) {
|
||||||
|
return ['value' => [
|
||||||
|
['IdAnagrafica' => 1, 'Codice' => 'SRV-01', 'NomeAnagrafica' => 'Analisi Chimica'],
|
||||||
|
['IdAnagrafica' => 2, 'Codice' => 'SRV-02', 'NomeAnagrafica' => 'Test Meccanico'],
|
||||||
|
['IdAnagrafica' => 3, 'Codice' => 'SRV-03', 'NomeAnagrafica' => 'Prova Ambientale'],
|
||||||
|
]];
|
||||||
|
}
|
||||||
|
|
||||||
|
// Cliente? list — get_clienti.php exits early in simulate mode, but guard here too
|
||||||
|
if (str_starts_with($endpoint, 'Cliente?')) {
|
||||||
|
return ['value' => []];
|
||||||
|
}
|
||||||
|
|
||||||
|
// Cliente(N)?$expand=Responsabili
|
||||||
|
if (str_starts_with($endpoint, 'Cliente(')) {
|
||||||
|
preg_match('/Cliente\((\d+)\)/', $endpoint, $m);
|
||||||
|
$clienteId = isset($m[1]) ? (int) $m[1] : 0;
|
||||||
|
return [
|
||||||
|
'IdCliente' => $clienteId,
|
||||||
|
'Responsabili' => [
|
||||||
|
['IdClienteResponsabile' => 1, 'Nominativo' => 'Marco Bianchi'],
|
||||||
|
['IdClienteResponsabile' => 2, 'Nominativo' => 'Giulia Ferrari'],
|
||||||
|
['IdClienteResponsabile' => 3, 'Nominativo' => 'Andrea Russo'],
|
||||||
|
],
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
// --- CustomField dropdown values (get_customfield_values.php) ---
|
||||||
|
|
||||||
|
if (str_starts_with($endpoint, 'CustomField(')) {
|
||||||
|
preg_match('/CustomField\((\d+)\)/', $endpoint, $m);
|
||||||
|
$fieldId = isset($m[1]) ? (int) $m[1] : 0;
|
||||||
|
return [
|
||||||
|
'CustomFieldsValues' => [
|
||||||
|
['IdCustomFieldsValue' => $fieldId * 10 + 1, 'Valore' => 'Opzione A'],
|
||||||
|
['IdCustomFieldsValue' => $fieldId * 10 + 2, 'Valore' => 'Opzione B'],
|
||||||
|
['IdCustomFieldsValue' => $fieldId * 10 + 3, 'Valore' => 'Opzione C'],
|
||||||
|
],
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
// --- CommessaWeb OData calls (STEP 7 GET + STEP 10 verification) ---
|
||||||
|
|
||||||
|
preg_match('/\((\d+)\)/', $endpoint, $m);
|
||||||
|
$id = isset($m[1]) ? (int) $m[1] : $this->fakeCommessaId;
|
||||||
|
|
||||||
|
return [
|
||||||
|
'IdCommessa' => $id,
|
||||||
|
'CodiceCommessa' => "SIM-{$id}",
|
||||||
|
'CommesseCustomFields' => [], // Empty → PATCH step is skipped correctly
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
public function post(string $endpoint, array $payload): array
|
||||||
|
{
|
||||||
|
error_log("[SIMULATE] POST {$endpoint} payload=" . json_encode($payload));
|
||||||
|
|
||||||
|
// CommessaWeb creation
|
||||||
|
if ($endpoint === 'CommessaWeb') {
|
||||||
|
return [
|
||||||
|
'IdCommessa' => $this->fakeCommessaId,
|
||||||
|
'CodiceCommessa' => "SIM-{$this->fakeCommessaId}",
|
||||||
|
'Richiedente' => $payload['Richiedente'] ?? '',
|
||||||
|
'Descrizione' => $payload['Descrizione'] ?? '',
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
// Campione creation
|
||||||
|
if ($endpoint === 'Campione') {
|
||||||
|
return [
|
||||||
|
'IdCampione' => mt_rand(10001, 19999),
|
||||||
|
'Commessa' => $payload['Commessa'] ?? null,
|
||||||
|
'Matrice' => $payload['Matrice'] ?? null,
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
// InviaCommessa / ImportaCommessa (currently commented out upstream)
|
||||||
|
return ['simulated' => true, 'endpoint' => $endpoint];
|
||||||
|
}
|
||||||
|
|
||||||
|
public function patch(string $endpoint, array $payload): array
|
||||||
|
{
|
||||||
|
error_log("[SIMULATE] PATCH {$endpoint} payload=" . json_encode($payload));
|
||||||
|
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
|
||||||
|
public function postMultipart(string $endpoint, string $filePath, string $fileName, int $commessaId): array
|
||||||
|
{
|
||||||
|
error_log("[SIMULATE] POST multipart {$endpoint} file={$fileName} commessaId={$commessaId}");
|
||||||
|
|
||||||
|
return ['simulated' => true, 'file' => $fileName];
|
||||||
|
}
|
||||||
|
}
|
||||||
File diff suppressed because one or more lines are too long
@ -1,7 +1,6 @@
|
|||||||
document.addEventListener("DOMContentLoaded", () => {
|
document.addEventListener("DOMContentLoaded", () => {
|
||||||
console.log("export_to_lims.js loaded");
|
console.log("export_to_lims.js loaded");
|
||||||
|
|
||||||
// Debug: verifica che i pulsanti siano trovati
|
|
||||||
const exportButtons = document.querySelectorAll(".export-lims-btn");
|
const exportButtons = document.querySelectorAll(".export-lims-btn");
|
||||||
console.log(`Found ${exportButtons.length} export-lims-btn buttons`);
|
console.log(`Found ${exportButtons.length} export-lims-btn buttons`);
|
||||||
|
|
||||||
@ -10,6 +9,170 @@ document.addEventListener("DOMContentLoaded", () => {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Tracks the active confirm handler so it can be replaced on re-open
|
||||||
|
let pendingConfirmHandler = null;
|
||||||
|
|
||||||
|
// ── Helpers ──────────────────────────────────────────────────────────────
|
||||||
|
|
||||||
|
function cleanupBackdrop() {
|
||||||
|
document.querySelectorAll(".modal-backdrop").forEach((b) => b.remove());
|
||||||
|
document.body.classList.remove("modal-open");
|
||||||
|
document.body.style.paddingRight = "";
|
||||||
|
const overlay = document.querySelector(".overlay.toggle-icon");
|
||||||
|
if (overlay) overlay.style.display = "none";
|
||||||
|
}
|
||||||
|
|
||||||
|
// ── Step 2: show export-confirm modal, send on "Conferma" ────────────────
|
||||||
|
|
||||||
|
function startExportConfirmFlow(iddatadb, btn) {
|
||||||
|
const confirmModalElement = document.getElementById("exportConfirmModal");
|
||||||
|
if (!confirmModalElement) {
|
||||||
|
alert("Errore: Modale di conferma non trovato");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const confirmModal = new bootstrap.Modal(confirmModalElement, {
|
||||||
|
keyboard: false,
|
||||||
|
});
|
||||||
|
document.getElementById("exportIddatadb").textContent = iddatadb;
|
||||||
|
confirmModal.show();
|
||||||
|
|
||||||
|
const confirmBtn = document.getElementById("exportConfirmBtn");
|
||||||
|
if (!confirmBtn) {
|
||||||
|
confirmModal.hide();
|
||||||
|
alert("Errore: Pulsante di conferma non trovato");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const confirmHandler = async () => {
|
||||||
|
pendingConfirmHandler = null;
|
||||||
|
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);
|
||||||
|
|
||||||
|
const responseModalElement =
|
||||||
|
document.getElementById("exportResponseModal");
|
||||||
|
if (!responseModalElement) {
|
||||||
|
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, "<br>")}` +
|
||||||
|
`<br>ID CommessaWeb: ${data.idcommessaweb}` +
|
||||||
|
`<br>Codice CommessaWeb: ${data.commessaweb}` +
|
||||||
|
(data.totalPhotos > 0
|
||||||
|
? `<br>Foto trovate: ${data.totalPhotos}`
|
||||||
|
: "");
|
||||||
|
document.getElementById(
|
||||||
|
"exportResponseModalLabel",
|
||||||
|
).textContent = "Esportazione Completata";
|
||||||
|
responseModal.show();
|
||||||
|
|
||||||
|
// Update status badge
|
||||||
|
const gridRow = btn.closest(".grid-row");
|
||||||
|
const statusBadge = gridRow?.querySelector(
|
||||||
|
'.grid-cell[data-col="status"] .status-badge',
|
||||||
|
);
|
||||||
|
if (statusBadge) {
|
||||||
|
statusBadge.classList.remove("status-i", "status-P");
|
||||||
|
statusBadge.classList.add("status-l");
|
||||||
|
statusBadge.textContent = "To LIMS";
|
||||||
|
}
|
||||||
|
|
||||||
|
// Insert/update CommessaWeb code span
|
||||||
|
const statusCell = gridRow?.querySelector(
|
||||||
|
'.grid-cell[data-col="status"]',
|
||||||
|
);
|
||||||
|
if (statusCell && data.commessaweb) {
|
||||||
|
let cwSpan =
|
||||||
|
statusCell.querySelector(".commessaweb-code");
|
||||||
|
if (!cwSpan) {
|
||||||
|
cwSpan = document.createElement("span");
|
||||||
|
cwSpan.className = "commessaweb-code";
|
||||||
|
cwSpan.style.cssText =
|
||||||
|
"display:block; font-size:0.75em; color:#555; margin-top:2px;";
|
||||||
|
cwSpan.title = "CommessaWeb";
|
||||||
|
const hiddenInput = statusCell.querySelector(
|
||||||
|
'input[type="hidden"]',
|
||||||
|
);
|
||||||
|
hiddenInput
|
||||||
|
? statusCell.insertBefore(cwSpan, hiddenInput)
|
||||||
|
: statusCell.appendChild(cwSpan);
|
||||||
|
}
|
||||||
|
cwSpan.textContent = data.commessaweb;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
responseMessage.textContent = `Errore durante la generazione dei payload: ${data.message}`;
|
||||||
|
document.getElementById(
|
||||||
|
"exportResponseModalLabel",
|
||||||
|
).textContent = "Errore Esportazione";
|
||||||
|
responseModal.show();
|
||||||
|
}
|
||||||
|
|
||||||
|
responseModalElement.addEventListener(
|
||||||
|
"hidden.bs.modal",
|
||||||
|
cleanupBackdrop,
|
||||||
|
{ once: true },
|
||||||
|
);
|
||||||
|
} catch (error) {
|
||||||
|
console.error("Export error:", error);
|
||||||
|
const responseModalElement =
|
||||||
|
document.getElementById("exportResponseModal");
|
||||||
|
if (!responseModalElement) {
|
||||||
|
alert("Errore: Modale di risposta non trovato");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const responseModal = new bootstrap.Modal(
|
||||||
|
responseModalElement,
|
||||||
|
{ keyboard: false },
|
||||||
|
);
|
||||||
|
document.getElementById(
|
||||||
|
"exportResponseMessage",
|
||||||
|
).textContent = `Errore: ${error.message}`;
|
||||||
|
document.getElementById(
|
||||||
|
"exportResponseModalLabel",
|
||||||
|
).textContent = "Errore Esportazione";
|
||||||
|
responseModal.show();
|
||||||
|
responseModalElement.addEventListener(
|
||||||
|
"hidden.bs.modal",
|
||||||
|
cleanupBackdrop,
|
||||||
|
{ once: true },
|
||||||
|
);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
if (pendingConfirmHandler) {
|
||||||
|
confirmBtn.removeEventListener("click", pendingConfirmHandler);
|
||||||
|
}
|
||||||
|
pendingConfirmHandler = confirmHandler;
|
||||||
|
confirmBtn.addEventListener("click", confirmHandler, { once: true });
|
||||||
|
}
|
||||||
|
|
||||||
|
// ── Step 1: check unsaved changes, save if needed, then export ───────────
|
||||||
|
|
||||||
exportButtons.forEach((btn) => {
|
exportButtons.forEach((btn) => {
|
||||||
btn.addEventListener("click", (e) => {
|
btn.addEventListener("click", (e) => {
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
@ -19,221 +182,40 @@ document.addEventListener("DOMContentLoaded", () => {
|
|||||||
`Export to LIMS clicked for row ${rowIndex}, iddatadb: ${iddatadb}`,
|
`Export to LIMS clicked for row ${rowIndex}, iddatadb: ${iddatadb}`,
|
||||||
);
|
);
|
||||||
|
|
||||||
// Mostra il modale di conferma
|
const gridRow = btn.closest(".grid-row");
|
||||||
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, {
|
if (gridRow && gridRow.querySelector(".cell-changed")) {
|
||||||
keyboard: false,
|
const unsavedModal = new bootstrap.Modal(
|
||||||
});
|
document.getElementById("exportUnsavedModal"),
|
||||||
document.getElementById("exportIddatadb").textContent = iddatadb;
|
{ keyboard: false },
|
||||||
confirmModal.show();
|
);
|
||||||
|
unsavedModal.show();
|
||||||
|
|
||||||
// Gestisci il click su "Conferma"
|
document.getElementById("saveAndExportBtn").addEventListener("click", () => {
|
||||||
const confirmBtn = document.getElementById("exportConfirmBtn");
|
unsavedModal.hide();
|
||||||
if (!confirmBtn) {
|
const saveBtn = gridRow.querySelector(".save-btn");
|
||||||
console.error("exportConfirmBtn not found in the DOM");
|
if (!saveBtn) return;
|
||||||
confirmModal.hide();
|
|
||||||
alert("Errore: Pulsante di conferma non trovato");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
const confirmHandler = async () => {
|
const observer = new MutationObserver(() => {
|
||||||
console.log(`Confirmed export for iddatadb: ${iddatadb}`);
|
if (!gridRow.querySelector(".cell-changed")) {
|
||||||
confirmModal.hide();
|
observer.disconnect();
|
||||||
|
startExportConfirmFlow(iddatadb, btn);
|
||||||
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, "<br>")}<br>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";
|
|
||||||
}
|
}
|
||||||
|
});
|
||||||
|
observer.observe(gridRow, {
|
||||||
|
subtree: true,
|
||||||
|
attributes: true,
|
||||||
|
attributeFilter: ["class"],
|
||||||
|
});
|
||||||
|
|
||||||
// Gestisci la chiusura del modale di risposta
|
saveBtn.click();
|
||||||
responseModalElement.addEventListener(
|
}, { once: true });
|
||||||
"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
|
return;
|
||||||
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
|
// No unsaved changes — go straight to export confirm
|
||||||
responseModalElement.addEventListener(
|
startExportConfirmFlow(iddatadb, btn);
|
||||||
"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);
|
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
@ -13,6 +13,8 @@ if (!is_dir($logDir)) {
|
|||||||
mkdir($logDir, 0755, true);
|
mkdir($logDir, 0755, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
$uploadDir = realpath(__DIR__ . '/../photostrf') . '/';
|
||||||
|
|
||||||
// 🔹 Base URL API
|
// 🔹 Base URL API
|
||||||
$apiBaseUrl = 'https://93.43.5.102/limsapi/api/odata/';
|
$apiBaseUrl = 'https://93.43.5.102/limsapi/api/odata/';
|
||||||
|
|
||||||
@ -34,8 +36,14 @@ try {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// 🔹 STEP 1+2: Fetch Cliente ID from datadb and Schema ID from excel_templates
|
// 🔹 STEP 1+2: Fetch Cliente ID from datadb and Schema ID from excel_templates
|
||||||
|
// Also fetch fixed fields stored in datadb
|
||||||
$stmt = $pdo->prepare("
|
$stmt = $pdo->prepare("
|
||||||
SELECT d.idclient AS clienteId, et.idschema AS schemaId
|
SELECT d.idclient AS clienteId, et.idschema AS schemaId,
|
||||||
|
d.cliente_responsabile_id,
|
||||||
|
d.moltiplicatore_prezzo_id,
|
||||||
|
d.anagrafica_certest_object_id,
|
||||||
|
d.anagrafica_certest_service_id,
|
||||||
|
d.cliente_fornitore_id
|
||||||
FROM datadb as d
|
FROM datadb as d
|
||||||
INNER JOIN excel_templates as et ON d.templateid = et.id
|
INNER JOIN excel_templates as et ON d.templateid = et.id
|
||||||
WHERE d.iddatadb = :iddatadb
|
WHERE d.iddatadb = :iddatadb
|
||||||
@ -51,6 +59,13 @@ try {
|
|||||||
$clienteId = (int) $result['clienteId'];
|
$clienteId = (int) $result['clienteId'];
|
||||||
$schemaId = (int) $result['schemaId'];
|
$schemaId = (int) $result['schemaId'];
|
||||||
|
|
||||||
|
// Extract fixed fields (nullable INTs)
|
||||||
|
$clienteResponsabile = !empty($result['cliente_responsabile_id']) ? (int) $result['cliente_responsabile_id'] : null;
|
||||||
|
$moltiplicatorePrezzo = !empty($result['moltiplicatore_prezzo_id']) ? (int) $result['moltiplicatore_prezzo_id'] : null;
|
||||||
|
$anagraficaObject = !empty($result['anagrafica_certest_object_id']) ? (int) $result['anagrafica_certest_object_id'] : null;
|
||||||
|
$anagraficaService = !empty($result['anagrafica_certest_service_id']) ? (int) $result['anagrafica_certest_service_id'] : null;
|
||||||
|
$clienteFornitore = !empty($result['cliente_fornitore_id']) ? (int) $result['cliente_fornitore_id'] : null;
|
||||||
|
|
||||||
// 🔹 STEP 3: Fetch Parts (including idmatrice)
|
// 🔹 STEP 3: Fetch Parts (including idmatrice)
|
||||||
$stmt = $pdo->prepare("
|
$stmt = $pdo->prepare("
|
||||||
SELECT part_number, part_description, material, color, mix, idmatrice
|
SELECT part_number, part_description, material, color, mix, idmatrice
|
||||||
@ -94,11 +109,18 @@ try {
|
|||||||
$api = VisualLimsApiClient::getInstance();
|
$api = VisualLimsApiClient::getInstance();
|
||||||
|
|
||||||
// 🔹 STEP 5: Create CommessaWeb (NOT WebOrder)
|
// 🔹 STEP 5: Create CommessaWeb (NOT WebOrder)
|
||||||
|
// Includes fixed fields fetched from datadb in STEP 1+2
|
||||||
$commessaWebPayload = [
|
$commessaWebPayload = [
|
||||||
"Cliente" => $clienteId,
|
"Cliente" => $clienteId,
|
||||||
"SchemaCustomField" => $schemaId,
|
"SchemaCustomField" => $schemaId,
|
||||||
"Richiedente" => "Test Web Import",
|
"Richiedente" => "Test Web Import",
|
||||||
"Descrizione" => "TEST CommessaWeb",
|
"Descrizione" => "TEST CommessaWeb",
|
||||||
|
// Fixed fields from datadb
|
||||||
|
"ClienteResponsabile" => $clienteResponsabile,
|
||||||
|
"MoltiplicatorePrezzo" => $moltiplicatorePrezzo,
|
||||||
|
"AnagraficaCertestObject" => $anagraficaObject,
|
||||||
|
"AnagraficaCertestService" => $anagraficaService,
|
||||||
|
"ClienteFornitore" => $clienteFornitore, // PLACEHOLDER — to be implemented
|
||||||
];
|
];
|
||||||
|
|
||||||
// Costruisci log curl-like per STEP 5
|
// Costruisci log curl-like per STEP 5
|
||||||
@ -119,6 +141,49 @@ try {
|
|||||||
$commessaId = $commessaWeb["IdCommessa"];
|
$commessaId = $commessaWeb["IdCommessa"];
|
||||||
$commessaWebCode = substr($commessaWeb["CodiceCommessa"] ?? "TEST CommessaWeb", 0, 30); // Limite a 30 caratteri
|
$commessaWebCode = substr($commessaWeb["CodiceCommessa"] ?? "TEST CommessaWeb", 0, 30); // Limite a 30 caratteri
|
||||||
|
|
||||||
|
// 🔹 STEP 5.1: Fetch photos linked to this iddatadb
|
||||||
|
$stmtPhotos = $pdo->prepare("
|
||||||
|
SELECT id, file_path, file_name
|
||||||
|
FROM datadb_photos
|
||||||
|
WHERE iddatadb = :iddatadb
|
||||||
|
ORDER BY id ASC
|
||||||
|
");
|
||||||
|
$stmtPhotos->execute(['iddatadb' => $iddatadb]);
|
||||||
|
$photos = $stmtPhotos->fetchAll(PDO::FETCH_ASSOC);
|
||||||
|
|
||||||
|
// 🔹 STEP 5.2: Upload photos to CommessaWeb
|
||||||
|
// NOTE: The sample number corresponds to the CommessaWeb ID ($commessaId).
|
||||||
|
// Photos may be multiple or may not exist at all.
|
||||||
|
|
||||||
|
$photosUploaded = 0;
|
||||||
|
$logContentPhotos = "Photos for CommessaWeb {$commessaId} (iddatadb={$iddatadb}):\n";
|
||||||
|
$logContentPhotos .= "Total photos found: " . count($photos) . "\n\n";
|
||||||
|
|
||||||
|
foreach ($photos as $photo) {
|
||||||
|
// Build absolute path from the relative path stored in DB
|
||||||
|
$photoPath = $uploadDir . '/' . ltrim($photo['file_path'], './');
|
||||||
|
$fullPath = realpath($photoPath);
|
||||||
|
|
||||||
|
if (!$fullPath || !file_exists($fullPath)) {
|
||||||
|
$logContentPhotos .= "SKIP (file not found): full - $photoPath " . $photo['file_path'] . "\n";
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Construct curl-like log entry
|
||||||
|
$logContentPhotos .= "curl --location --request POST '{$apiBaseUrl}AllegatoCommessaWeb' \\\n" .
|
||||||
|
"--header 'Authorization: Bearer ••••••' \\\n" .
|
||||||
|
"--form 'IdCommessa={$commessaId}' \\\n" .
|
||||||
|
"--form 'file=@{$fullPath}'\n\n";
|
||||||
|
|
||||||
|
// ENDPOINT NAME TO BE CONFIRMED
|
||||||
|
$photoResult = $api->postMultipart("AllegatoCommessaWeb", $fullPath, $photo['file_name'], $commessaId);
|
||||||
|
$logContentPhotos .= "RESPONSE:\n" . json_encode($photoResult, JSON_PRETTY_PRINT) . "\n\n---\n";
|
||||||
|
$photosUploaded++;
|
||||||
|
}
|
||||||
|
|
||||||
|
$logFilePhotos = $logDir . "commessa_{$commessaId}_photos_step5_2_" . time() . ".txt";
|
||||||
|
file_put_contents($logFilePhotos, $logContentPhotos);
|
||||||
|
|
||||||
// 🔹 STEP 6: Create Campioni (Samples) for each part
|
// 🔹 STEP 6: Create Campioni (Samples) for each part
|
||||||
$campioni = [];
|
$campioni = [];
|
||||||
$logContentStep6 = "";
|
$logContentStep6 = "";
|
||||||
@ -226,18 +291,32 @@ try {
|
|||||||
]);
|
]);
|
||||||
|
|
||||||
// 🔹 STEP 9: Send CommessaWeb to laboratory (commentato come richiesto)
|
// 🔹 STEP 9: Send CommessaWeb to laboratory (commentato come richiesto)
|
||||||
/*
|
|
||||||
$sendResult = $api->post("CommessaWeb({$commessaId})/InviaCommessa", []);
|
$sendResult = $api->post("CommessaWeb({$commessaId})/InviaCommessa", []);
|
||||||
|
|
||||||
// Logga il POST
|
// Logga il POST
|
||||||
$logContentStep9 = "curl --location --request POST '{$apiBaseUrl}CommessaWeb({$commessaId})/InviaCommessa' \\\n" .
|
$logContentStep9 = "curl --location --request POST '{$apiBaseUrl}CommessaWeb({$commessaId})/InviaCommessa' \\\n" .
|
||||||
"--header 'Content-Type: application/json' \\\n" .
|
"--header 'Content-Type: application/json' \\\n" .
|
||||||
"--header 'Authorization: Bearer ••••••' \\\n" .
|
"--header 'Authorization: Bearer ••••••' \\\n" .
|
||||||
"--data '{}'\n\n" .
|
"--data '{}'\n\n" .
|
||||||
"RESPONSE:\n" . json_encode($sendResult, JSON_PRETTY_PRINT);
|
"RESPONSE:\n" . json_encode($sendResult, JSON_PRETTY_PRINT);
|
||||||
$logFileStep9 = $logDir . "commessa_{$commessaId}_send_step9_" . time() . ".txt";
|
$logFileStep9 = $logDir . "commessa_{$commessaId}_send_step9_" . time() . ".txt";
|
||||||
file_put_contents($logFileStep9, $logContentStep9);
|
file_put_contents($logFileStep9, $logContentStep9);
|
||||||
*/
|
|
||||||
|
|
||||||
|
// 🔹 STEP 9.5: Importazione da CommessaWeb a Commessa (commentato come richiesto)
|
||||||
|
// Supplier call: POST api/odata/CommessaWeb(XXX)/ImportaCommessa
|
||||||
|
|
||||||
|
$importResult = $api->post("CommessaWeb({$commessaId})/ImportaCommessa", []);
|
||||||
|
|
||||||
|
// Logga il POST
|
||||||
|
$logContentStep91 = "curl --location --request POST '{$apiBaseUrl}CommessaWeb({$commessaId})/ImportaCommessa' \\\n" .
|
||||||
|
"--header 'Content-Type: application/json' \\\n" .
|
||||||
|
"--header 'Authorization: Bearer ••••••' \\\n" .
|
||||||
|
"--data '{}'\n\n" .
|
||||||
|
"RESPONSE:\n" . json_encode($importResult, JSON_PRETTY_PRINT);
|
||||||
|
$logFileStep91 = $logDir . "commessa_{$commessaId}_importa_step91_" . time() . ".txt";
|
||||||
|
file_put_contents($logFileStep91, $logContentStep91);
|
||||||
|
|
||||||
// 🔹 STEP 10: GET di controllo post-PATCH
|
// 🔹 STEP 10: GET di controllo post-PATCH
|
||||||
$expand = "CommesseCustomFields(\$expand=CustomField)";
|
$expand = "CommesseCustomFields(\$expand=CustomField)";
|
||||||
@ -262,17 +341,22 @@ try {
|
|||||||
];
|
];
|
||||||
|
|
||||||
echo json_encode([
|
echo json_encode([
|
||||||
"success" => true,
|
"success" => true,
|
||||||
"commessaWeb" => $finalCommessa,
|
"idcommessaweb" => $commessaId,
|
||||||
|
"commessaweb" => $commessaWebCode,
|
||||||
|
"commessaWeb" => $finalCommessa,
|
||||||
"commessaWebApiResponse" => $commessaWeb, // Incluso per debug
|
"commessaWebApiResponse" => $commessaWeb, // Incluso per debug
|
||||||
"totalCampioni" => count($campioni),
|
"totalCampioni" => count($campioni),
|
||||||
"totalCustomFields" => count($commessaAfterPatch["CommesseCustomFields"] ?? []),
|
"totalCustomFields" => count($commessaAfterPatch["CommesseCustomFields"] ?? []),
|
||||||
"message" => "Export successful",
|
"totalPhotos" => count($photos),
|
||||||
"logFiles" => [
|
"message" => "Export successful",
|
||||||
"step5_create" => $logFileStep5,
|
"logFiles" => [
|
||||||
"step6_campioni" => $logFileStep6,
|
"step5_create" => $logFileStep5,
|
||||||
"step7_patch" => $logFileStep7,
|
"step5_2_photos" => $logFilePhotos,
|
||||||
"step10_get" => $logFileStep10
|
"step6_campioni" => $logFileStep6,
|
||||||
|
"step7_patch" => $logFileStep7 ?? null,
|
||||||
|
"step9_1_importa" => $logFileStep91,
|
||||||
|
"step10_get" => $logFileStep10
|
||||||
]
|
]
|
||||||
]);
|
]);
|
||||||
} catch (Exception $e) {
|
} catch (Exception $e) {
|
||||||
@ -282,10 +366,12 @@ try {
|
|||||||
"success" => false,
|
"success" => false,
|
||||||
"message" => "Export failed: " . $e->getMessage(),
|
"message" => "Export failed: " . $e->getMessage(),
|
||||||
"logFiles" => [
|
"logFiles" => [
|
||||||
"step5_create" => $logFileStep5 ?? null,
|
"step5_create" => $logFileStep5 ?? null,
|
||||||
"step6_campioni" => $logFileStep6 ?? null,
|
"step5_2_photos" => $logFilePhotos ?? null,
|
||||||
"step7_patch" => $logFileStep7 ?? null,
|
"step6_campioni" => $logFileStep6 ?? null,
|
||||||
"step10_get" => $logFileStep10 ?? null
|
"step7_patch" => $logFileStep7 ?? null,
|
||||||
|
"step9_1_importa" => $logFileStep91 ?? null,
|
||||||
|
"step10_get" => $logFileStep10 ?? null
|
||||||
]
|
]
|
||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
|
|||||||
@ -9,7 +9,24 @@ ini_set('display_errors', '0');
|
|||||||
error_reporting(E_ALL);
|
error_reporting(E_ALL);
|
||||||
|
|
||||||
try {
|
try {
|
||||||
$api = VisualLimsApiClient::getInstance();
|
$api = VisualLimsApiClient::getInstance(); // also loads dotenv
|
||||||
|
|
||||||
|
// In simulate mode: return fake clients built from idclient values already in datadb.
|
||||||
|
// This ensures client dropdowns auto-select the correct row idclient, which in turn
|
||||||
|
// triggers the ClienteResponsabile select to populate via the mock.
|
||||||
|
if (($_ENV['SIMULATE_EXPORT_LIMS'] ?? '') === 'true') {
|
||||||
|
require_once __DIR__ . '/class/db-functions.php';
|
||||||
|
$pdo = DBHandlerSelect::getInstance()->getConnection();
|
||||||
|
$stmt = $pdo->query("SELECT DISTINCT idclient FROM datadb WHERE idclient IS NOT NULL AND idclient > 0 ORDER BY idclient ASC");
|
||||||
|
$ids = $stmt->fetchAll(PDO::FETCH_COLUMN);
|
||||||
|
$fakeClients = array_map(fn($id) => [
|
||||||
|
'IdCliente' => (int) $id,
|
||||||
|
'Nominativo' => "Cliente Simulato {$id}",
|
||||||
|
'CodiceCliente' => "SIM_{$id}",
|
||||||
|
], $ids);
|
||||||
|
echo json_encode(['value' => $fakeClients]);
|
||||||
|
exit;
|
||||||
|
}
|
||||||
|
|
||||||
// Parametri OData
|
// Parametri OData
|
||||||
$params = [
|
$params = [
|
||||||
|
|||||||
@ -16,7 +16,8 @@ if (!$field) {
|
|||||||
exit;
|
exit;
|
||||||
}
|
}
|
||||||
|
|
||||||
$api = VisualLimsApiClient::getInstance();
|
$api = VisualLimsApiClient::getInstance(); // also loads dotenv as a side-effect
|
||||||
|
$simulate = ($_ENV['SIMULATE_EXPORT_LIMS'] ?? '') === 'true';
|
||||||
$base_url = 'https://93.43.5.102/limsapi/api/odata/';
|
$base_url = 'https://93.43.5.102/limsapi/api/odata/';
|
||||||
|
|
||||||
$data = null;
|
$data = null;
|
||||||
@ -44,9 +45,12 @@ try {
|
|||||||
case 'ClienteResponsabile':
|
case 'ClienteResponsabile':
|
||||||
$id_cliente = (int)($_GET['id_cliente'] ?? 0);
|
$id_cliente = (int)($_GET['id_cliente'] ?? 0);
|
||||||
if ($id_cliente <= 0) {
|
if ($id_cliente <= 0) {
|
||||||
http_response_code(400);
|
if (!$simulate) {
|
||||||
echo json_encode(['error' => 'Manca o invalido id_cliente']);
|
http_response_code(400);
|
||||||
exit;
|
echo json_encode(['error' => 'Manca o invalido id_cliente']);
|
||||||
|
exit;
|
||||||
|
}
|
||||||
|
$id_cliente = 1; // dummy — mock ignores the actual ID
|
||||||
}
|
}
|
||||||
$endpoint = "Cliente($id_cliente)?\$expand=Responsabili";
|
$endpoint = "Cliente($id_cliente)?\$expand=Responsabili";
|
||||||
$cache_file = __DIR__ . '/cache/cliente_responsabili_' . $id_cliente . '.json';
|
$cache_file = __DIR__ . '/cache/cliente_responsabili_' . $id_cliente . '.json';
|
||||||
@ -62,13 +66,13 @@ try {
|
|||||||
exit;
|
exit;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Opzionale: caching semplice (molto utile se i dati cambiano poco)
|
// Caching skipped in simulate mode to avoid polluting real-data cache files
|
||||||
if ($cache_file && file_exists($cache_file) && (time() - filemtime($cache_file) < 3600)) { // 1 ora
|
if (!$simulate && $cache_file && file_exists($cache_file) && (time() - filemtime($cache_file) < 3600)) { // 1 ora
|
||||||
$data = json_decode(file_get_contents($cache_file), true);
|
$data = json_decode(file_get_contents($cache_file), true);
|
||||||
} else {
|
} else {
|
||||||
$data = $api->get($endpoint, $options);
|
$data = $api->get($endpoint, $options);
|
||||||
|
|
||||||
if ($cache_file) {
|
if (!$simulate && $cache_file) {
|
||||||
file_put_contents($cache_file, json_encode($data, JSON_PRETTY_PRINT));
|
file_put_contents($cache_file, json_encode($data, JSON_PRETTY_PRINT));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -59,7 +59,8 @@ foreach ($allMappings as $mapping) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (!$mainFieldMapping) {
|
if (!$mainFieldMapping) {
|
||||||
$mainFieldMapping = reset(array_filter($allMappings, fn($m) => !$m['is_manual']));
|
$filtered = array_filter($allMappings, fn($m) => !$m['is_manual']);
|
||||||
|
$mainFieldMapping = reset($filtered);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Retrieve data from datadb
|
// Retrieve data from datadb
|
||||||
|
|||||||
@ -192,6 +192,16 @@ foreach ($tempMap as $f) {
|
|||||||
$fixedFields[] = $f;
|
$fixedFields[] = $f;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Maps logical fixed_field_key → real datadb column name (mirrors JS fixedAliasMap)
|
||||||
|
$fixedAliasMap = [
|
||||||
|
'ClienteResponsabile' => 'cliente_responsabile_id',
|
||||||
|
'MoltiplicatorePrezzo' => 'moltiplicatore_prezzo_id',
|
||||||
|
'AnagraficaCertestObject' => 'anagrafica_certest_object_id',
|
||||||
|
'AnagraficaCertestService' => 'anagrafica_certest_service_id',
|
||||||
|
'ClienteFornitore' => 'cliente_fornitore_id',
|
||||||
|
'ConsegnaRichiesta' => 'consegna_richiesta',
|
||||||
|
];
|
||||||
|
|
||||||
// helper default (DATE can use 'today' if you already use it elsewhere)
|
// helper default (DATE can use 'today' if you already use it elsewhere)
|
||||||
function fixedDefaultValue(array $f): string
|
function fixedDefaultValue(array $f): string
|
||||||
{
|
{
|
||||||
@ -638,12 +648,14 @@ function fixedDefaultValue(array $f): string
|
|||||||
}
|
}
|
||||||
|
|
||||||
#exportConfirmModal,
|
#exportConfirmModal,
|
||||||
#exportResponseModal {
|
#exportResponseModal,
|
||||||
|
#exportUnsavedModal {
|
||||||
z-index: 1300 !important;
|
z-index: 1300 !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
#exportConfirmModal .modal-backdrop,
|
#exportConfirmModal .modal-backdrop,
|
||||||
#exportResponseModal .modal-backdrop {
|
#exportResponseModal .modal-backdrop,
|
||||||
|
#exportUnsavedModal .modal-backdrop {
|
||||||
z-index: 1299 !important;
|
z-index: 1299 !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1055,6 +1067,9 @@ function fixedDefaultValue(array $f): string
|
|||||||
<span class="status-badge status-<?= htmlspecialchars($row['status'] ?? 'i') ?>">
|
<span class="status-badge status-<?= htmlspecialchars($row['status'] ?? 'i') ?>">
|
||||||
<?= htmlspecialchars($row['status'] === 'i' ? 'Imported' : ($row['status'] === 'P' ? 'In Progress' : 'To LIMS')) ?>
|
<?= htmlspecialchars($row['status'] === 'i' ? 'Imported' : ($row['status'] === 'P' ? 'In Progress' : 'To LIMS')) ?>
|
||||||
</span>
|
</span>
|
||||||
|
<?php if (!empty($row['commessaweb'])): ?>
|
||||||
|
<span class="commessaweb-code" style="display:block; font-size:0.75em; color:#555; margin-top:2px;" title="CommessaWeb"><?= htmlspecialchars($row['commessaweb']) ?></span>
|
||||||
|
<?php endif; ?>
|
||||||
<input type="hidden" name="rows[<?= $index ?>][status]" value="<?= htmlspecialchars($row['status'] ?? 'i') ?>">
|
<input type="hidden" name="rows[<?= $index ?>][status]" value="<?= htmlspecialchars($row['status'] ?? 'i') ?>">
|
||||||
</div>
|
</div>
|
||||||
<?php
|
<?php
|
||||||
@ -1159,7 +1174,8 @@ function fixedDefaultValue(array $f): string
|
|||||||
if (!empty($fixedFields)) {
|
if (!empty($fixedFields)) {
|
||||||
foreach ($fixedFields as $f) {
|
foreach ($fixedFields as $f) {
|
||||||
$key = $f['fixed_field_key'];
|
$key = $f['fixed_field_key'];
|
||||||
$val = $row[$key] ?? '';
|
$dbCol = $fixedAliasMap[$key] ?? $key;
|
||||||
|
$val = $row[$dbCol] ?? '';
|
||||||
if ($val === '' || $val === null) {
|
if ($val === '' || $val === null) {
|
||||||
$val = fixedDefaultValue($f);
|
$val = fixedDefaultValue($f);
|
||||||
}
|
}
|
||||||
@ -1290,32 +1306,6 @@ function fixedDefaultValue(array $f): string
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
$(document).on("click", ".export-lims-btn", function() {
|
|
||||||
let rowId = $(this).data("row");
|
|
||||||
let idDataDb = $(this).data("iddatadb");
|
|
||||||
|
|
||||||
$.ajax({
|
|
||||||
url: "export_to_lims.php",
|
|
||||||
method: "POST",
|
|
||||||
data: {
|
|
||||||
iddatadb: idDataDb
|
|
||||||
},
|
|
||||||
dataType: "json",
|
|
||||||
beforeSend: function() {
|
|
||||||
alert("Export started in background for row " + rowId);
|
|
||||||
},
|
|
||||||
success: function(response) {
|
|
||||||
if (response.success) {
|
|
||||||
alert(response.message);
|
|
||||||
} else {
|
|
||||||
alert("❌ Error: " + response.message);
|
|
||||||
}
|
|
||||||
},
|
|
||||||
error: function(xhr, status, error) {
|
|
||||||
alert("❌ AJAX error: " + error);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
document.addEventListener("DOMContentLoaded", function() {
|
document.addEventListener("DOMContentLoaded", function() {
|
||||||
const inputs = document.querySelectorAll(".cell-input, .dropdown-select, .carrier-select, .awb-input, .date-picker");
|
const inputs = document.querySelectorAll(".cell-input, .dropdown-select, .carrier-select, .awb-input, .date-picker");
|
||||||
@ -1345,6 +1335,7 @@ function fixedDefaultValue(array $f): string
|
|||||||
|
|
||||||
inputs.forEach(el => {
|
inputs.forEach(el => {
|
||||||
el.addEventListener("change", () => {
|
el.addEventListener("change", () => {
|
||||||
|
if (el.hasAttribute('data-restoring')) return;
|
||||||
hasChanges = true;
|
hasChanges = true;
|
||||||
|
|
||||||
const gridCell = el.closest(".grid-cell");
|
const gridCell = el.closest(".grid-cell");
|
||||||
@ -1443,7 +1434,8 @@ function fixedDefaultValue(array $f): string
|
|||||||
hasChanges = changedRows.size > 0;
|
hasChanges = changedRows.size > 0;
|
||||||
|
|
||||||
|
|
||||||
alert('Salvataggio riga avvenuto con successo!');
|
const toastEl = document.getElementById('saveSuccessToast');
|
||||||
|
if (toastEl) bootstrap.Toast.getOrCreateInstance(toastEl).show();
|
||||||
} else {
|
} else {
|
||||||
alert('Errore durante il salvataggio: ' + data.message);
|
alert('Errore durante il salvataggio: ' + data.message);
|
||||||
}
|
}
|
||||||
@ -1561,7 +1553,7 @@ function fixedDefaultValue(array $f): string
|
|||||||
});
|
});
|
||||||
|
|
||||||
// Gestisci la chiusura dei modali per rimuovere i backdrop
|
// Gestisci la chiusura dei modali per rimuovere i backdrop
|
||||||
document.querySelectorAll('#exportConfirmModal, #exportResponseModal').forEach(modal => {
|
document.querySelectorAll('#exportConfirmModal, #exportResponseModal, #exportUnsavedModal').forEach(modal => {
|
||||||
modal.addEventListener('hidden.bs.modal', () => {
|
modal.addEventListener('hidden.bs.modal', () => {
|
||||||
// Rimuovi tutti i backdrop residui
|
// Rimuovi tutti i backdrop residui
|
||||||
document.querySelectorAll('.modal-backdrop').forEach(backdrop => {
|
document.querySelectorAll('.modal-backdrop').forEach(backdrop => {
|
||||||
@ -1665,10 +1657,12 @@ function fixedDefaultValue(array $f): string
|
|||||||
// Ripristina il valore corrente
|
// Ripristina il valore corrente
|
||||||
if (currentValue) {
|
if (currentValue) {
|
||||||
dropdown.value = currentValue;
|
dropdown.value = currentValue;
|
||||||
|
dropdown.setAttribute('data-restoring', '');
|
||||||
const event = new Event('change', {
|
const event = new Event('change', {
|
||||||
bubbles: true
|
bubbles: true
|
||||||
});
|
});
|
||||||
dropdown.dispatchEvent(event);
|
dropdown.dispatchEvent(event);
|
||||||
|
dropdown.removeAttribute('data-restoring');
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@ -1977,7 +1971,9 @@ function fixedDefaultValue(array $f): string
|
|||||||
s2container.after(dropdown);
|
s2container.after(dropdown);
|
||||||
}
|
}
|
||||||
if (valToSelect) {
|
if (valToSelect) {
|
||||||
|
dropdown.setAttribute('data-restoring', '');
|
||||||
$(dropdown).val(valToSelect).trigger('change');
|
$(dropdown).val(valToSelect).trigger('change');
|
||||||
|
dropdown.removeAttribute('data-restoring');
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
// Select nativa
|
// Select nativa
|
||||||
@ -1996,9 +1992,11 @@ function fixedDefaultValue(array $f): string
|
|||||||
});
|
});
|
||||||
if (valToSelect) {
|
if (valToSelect) {
|
||||||
dropdown.value = valToSelect;
|
dropdown.value = valToSelect;
|
||||||
|
dropdown.setAttribute('data-restoring', '');
|
||||||
dropdown.dispatchEvent(new Event('change', {
|
dropdown.dispatchEvent(new Event('change', {
|
||||||
bubbles: true
|
bubbles: true
|
||||||
}));
|
}));
|
||||||
|
dropdown.removeAttribute('data-restoring');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
@ -2091,10 +2089,12 @@ function fixedDefaultValue(array $f): string
|
|||||||
// Ripristina il valore corrente
|
// Ripristina il valore corrente
|
||||||
if (currentValue) {
|
if (currentValue) {
|
||||||
dropdown.value = currentValue;
|
dropdown.value = currentValue;
|
||||||
|
dropdown.setAttribute('data-restoring', '');
|
||||||
const event = new Event('change', {
|
const event = new Event('change', {
|
||||||
bubbles: true
|
bubbles: true
|
||||||
});
|
});
|
||||||
dropdown.dispatchEvent(event);
|
dropdown.dispatchEvent(event);
|
||||||
|
dropdown.removeAttribute('data-restoring');
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@ -2536,7 +2536,9 @@ function fixedDefaultValue(array $f): string
|
|||||||
if (currentVal) {
|
if (currentVal) {
|
||||||
const found = results.find(r => String(r.id) === String(currentVal));
|
const found = results.find(r => String(r.id) === String(currentVal));
|
||||||
if (found) {
|
if (found) {
|
||||||
|
$select[0].setAttribute('data-restoring', '');
|
||||||
$select.val(currentVal).trigger('change');
|
$select.val(currentVal).trigger('change');
|
||||||
|
$select[0].removeAttribute('data-restoring');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -2723,6 +2725,35 @@ function fixedDefaultValue(array $f): string
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<!-- Modal: unsaved changes before export -->
|
||||||
|
<div class="modal fade" id="exportUnsavedModal" tabindex="-1" aria-hidden="true">
|
||||||
|
<div class="modal-dialog">
|
||||||
|
<div class="modal-content">
|
||||||
|
<div class="modal-header">
|
||||||
|
<h5 class="modal-title">Modifiche non salvate</h5>
|
||||||
|
<button type="button" class="btn-close" data-bs-dismiss="modal"></button>
|
||||||
|
</div>
|
||||||
|
<div class="modal-body">
|
||||||
|
La riga contiene modifiche non salvate. Salvare prima di procedere con l'esportazione?
|
||||||
|
</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="saveAndExportBtn">Salva ed Esporta</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Toast: row saved successfully -->
|
||||||
|
<div class="position-fixed bottom-0 end-0 p-3" style="z-index:9999">
|
||||||
|
<div id="saveSuccessToast" class="toast align-items-center text-bg-success border-0" role="alert" data-bs-delay="2500">
|
||||||
|
<div class="d-flex">
|
||||||
|
<div class="toast-body"><i class="fas fa-check-circle me-2"></i>Riga salvata con successo.</div>
|
||||||
|
<button type="button" class="btn-close btn-close-white me-2 m-auto" data-bs-dismiss="toast"></button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
</body>
|
</body>
|
||||||
|
|
||||||
</html>
|
</html>
|
||||||
@ -35,8 +35,6 @@ $kindofrole = $user->present()->role_id;
|
|||||||
//$iduserlogin="1";
|
//$iduserlogin="1";
|
||||||
//$nameuser="Claudio";
|
//$nameuser="Claudio";
|
||||||
//$emailuser="info@claudiosironi.com";
|
//$emailuser="info@claudiosironi.com";
|
||||||
?>
|
|
||||||
<?php
|
|
||||||
if (session_status() == PHP_SESSION_NONE) {
|
if (session_status() == PHP_SESSION_NONE) {
|
||||||
session_start();
|
session_start();
|
||||||
}
|
}
|
||||||
@ -49,13 +47,8 @@ $_SESSION["emailuser"] = $emailuser;
|
|||||||
$_SESSION["photouser"] = $avatar;
|
$_SESSION["photouser"] = $avatar;
|
||||||
$photouser = $_SESSION["photouser"];
|
$photouser = $_SESSION["photouser"];
|
||||||
$photousername = basename($avatar);
|
$photousername = basename($avatar);
|
||||||
?>
|
|
||||||
|
|
||||||
|
|
||||||
<?php //include files
|
|
||||||
|
|
||||||
|
//include files
|
||||||
require_once(__DIR__ . '/../../languages/en/general.php');
|
require_once(__DIR__ . '/../../languages/en/general.php');
|
||||||
|
|
||||||
//include("generalsettings.php");
|
//include("generalsettings.php");
|
||||||
|
|
||||||
?>
|
|
||||||
@ -0,0 +1,28 @@
|
|||||||
|
curl --location --request POST 'https://93.43.5.102/limsapi/api/odata/CommessaWeb' \
|
||||||
|
--header 'Content-Type: application/json' \
|
||||||
|
--header 'Authorization: Bearer ••••••' \
|
||||||
|
--data '{
|
||||||
|
"Cliente": 3378,
|
||||||
|
"SchemaCustomField": 82,
|
||||||
|
"Richiedente": "Test Web Import",
|
||||||
|
"Descrizione": "TEST CommessaWeb",
|
||||||
|
"ClienteResponsabile": 7586,
|
||||||
|
"MoltiplicatorePrezzo": 3,
|
||||||
|
"AnagraficaCertestObject": 8963,
|
||||||
|
"AnagraficaCertestService": 9007,
|
||||||
|
"ClienteFornitore": null
|
||||||
|
}'
|
||||||
|
|
||||||
|
RESPONSE:
|
||||||
|
{
|
||||||
|
"@odata.context": "https:\/\/bvcpsitaly-elims.com\/limsapi\/api\/odata\/$metadata#CommessaWeb\/$entity",
|
||||||
|
"IdCommessa": 562479,
|
||||||
|
"CodiceCommessa": "26C0013",
|
||||||
|
"RiferimentoCertificato": null,
|
||||||
|
"StatoCommessaWeb": "Nuova",
|
||||||
|
"DataCreazioneWeb": "2026-03-06T09:09:16.9338074+01:00",
|
||||||
|
"CodiceCommessaWeb": "26C0013",
|
||||||
|
"DataInviatoWeb": null,
|
||||||
|
"Richiedente": "Test Web Import",
|
||||||
|
"Descrizione": "TEST CommessaWeb"
|
||||||
|
}
|
||||||
Loading…
x
Reference in New Issue
Block a user