From 6b0d2aa9b9fbbd306e0dee1f5c3c350894104db9 Mon Sep 17 00:00:00 2001 From: solocla Date: Mon, 27 Apr 2026 14:34:45 +0200 Subject: [PATCH 01/42] fixed color and import dahboard --- public/userarea/gridRenderer.js | 43 +++ public/userarea/import_dashboard.php | 313 +++++++++++++++++----- public/userarea/imported.php | 152 +++++++++-- public/userarea/load_active_templates.php | 5 +- public/userarea/schemi_base_response.json | 2 +- 5 files changed, 426 insertions(+), 89 deletions(-) diff --git a/public/userarea/gridRenderer.js b/public/userarea/gridRenderer.js index 59743a4..bf762e0 100644 --- a/public/userarea/gridRenderer.js +++ b/public/userarea/gridRenderer.js @@ -331,6 +331,28 @@ function createCell(col, rowIndex, cellIndex) { const div = document.createElement("div"); div.className = "grid-cell editable-cell"; + + // Field color classification + // Schema/customfield fields are green. + // Fixed and standard fields stay white. + if (col.type === "detail" || col.type === "main_field") { + div.classList.add("schema-field"); + } else if (col.type === "fixed") { + div.classList.add("fixed-field"); + } else { + div.classList.add("standard-field"); + } + + // Required field classification. + // This comes from template_mapping.is_required or template_fixed_mapping.is_required. + if ( + col.isRequired === true || + col.isRequired === 1 || + col.isRequired === "1" + ) { + div.classList.add("required-field"); + } + div.dataset.col = col.key; div.dataset.colType = col.type; div.dataset.row = rowIndex; @@ -744,6 +766,27 @@ columns.forEach((col, colIdx) => { const cell = document.createElement("div"); cell.className = "grid-cell grid-top-cell"; + + // Field color classification for top propagation row + // Schema/customfield fields are green. + // Fixed and standard fields stay white. + if (col.type === "detail" || col.type === "main_field") { + cell.classList.add("schema-field"); + } else if (col.type === "fixed") { + cell.classList.add("fixed-field"); + } else { + cell.classList.add("standard-field"); + } + + // Required field classification also for the top propagation row. + if ( + col.isRequired === true || + col.isRequired === 1 || + col.isRequired === "1" + ) { + cell.classList.add("required-field"); + } + cell.style.flex = `0 0 ${col.width}px`; if ( diff --git a/public/userarea/import_dashboard.php b/public/userarea/import_dashboard.php index a35f1cb..cae5980 100644 --- a/public/userarea/import_dashboard.php +++ b/public/userarea/import_dashboard.php @@ -10,65 +10,125 @@ Template Buttons - <?= htmlspecialchars($titlewebsite, ENT_QUOTES, 'UTF-8'); ?> @@ -87,14 +147,54 @@
Active Templates
- -
+ + + +
+
+
+
Loading XLS templates...
+
+
+ +
+
+
Loading API templates...
+
+
+ +
+
+
Loading PDF templates...
+
+
+
+
@@ -104,46 +204,137 @@ diff --git a/public/userarea/imported.php b/public/userarea/imported.php index dac2e22..e3021b7 100644 --- a/public/userarea/imported.php +++ b/public/userarea/imported.php @@ -374,20 +374,10 @@ $gridMeta = [ transition: background-color 0.3s ease; } - input.auto-input, - select.auto-input { - background-color: #d4edda; - } - input.manual-input, - select.manual-input { - background-color: #fff3cd; - } - input.required-input, - select.required-input { - background-color: #f8d7da; - } + + input.required-input, select.required-input { @@ -426,17 +416,9 @@ $gridMeta = [ outline: none !important; } - textarea.auto-input { - background-color: #d4edda; - } - textarea.manual-input { - background-color: #fff3cd; - } - textarea.required-input { - background-color: #f8d7da; - } + .status-badge { display: inline-block; @@ -1044,11 +1026,7 @@ $gridMeta = [ font-style: italic; } - .api-fixed-select.required-input:invalid, - .api-fixed-select[required]:not([value]):not([data-select2-id]) { - background-color: #f8d7da !important; - border-color: #dc3545 !important; - } + /* ── Pagination bar ── */ .pager-bar { @@ -1161,6 +1139,128 @@ $gridMeta = [ color: #adb5bd; user-select: none; } + + /* ========================================================= + FINAL GRID COLORS + Schema/customfield fields = green + Fixed/standard fields = white + Required fields = red border only + ========================================================= */ + + /* Default: all fields white */ + .grid-container input, + .grid-container select, + .grid-container textarea, + .grid-top input, + .grid-top select, + .grid-top textarea { + background-color: #ffffff !important; + color: #333 !important; + } + + /* Schema/customfield fields: green background */ + .grid-container .schema-field input, + .grid-container .schema-field select, + .grid-container .schema-field textarea, + .grid-top .schema-field input, + .grid-top .schema-field select, + .grid-top .schema-field textarea { + background-color: #dff3e3 !important; + } + + /* Fixed and standard fields: white background */ + .grid-container .fixed-field input, + .grid-container .fixed-field select, + .grid-container .fixed-field textarea, + .grid-container .standard-field input, + .grid-container .standard-field select, + .grid-container .standard-field textarea, + .grid-top .fixed-field input, + .grid-top .fixed-field select, + .grid-top .fixed-field textarea, + .grid-top .standard-field input, + .grid-top .standard-field select, + .grid-top .standard-field textarea { + background-color: #ffffff !important; + } + + /* Required fields: red border only */ + .grid-container .required-field input, + .grid-container .required-field select, + .grid-container .required-field textarea, + .grid-top .required-field input, + .grid-top .required-field select, + .grid-top .required-field textarea { + border: 2px solid #dc3545 !important; + box-shadow: 0 0 0 1px rgba(220, 53, 69, 0.15) !important; + } + + /* Required schema/customfield fields: green + red border */ + .grid-container .schema-field.required-field input, + .grid-container .schema-field.required-field select, + .grid-container .schema-field.required-field textarea, + .grid-top .schema-field.required-field input, + .grid-top .schema-field.required-field select, + .grid-top .schema-field.required-field textarea { + background-color: #dff3e3 !important; + border: 2px solid #dc3545 !important; + } + + /* Required fixed/standard fields: white + red border */ + .grid-container .fixed-field.required-field input, + .grid-container .fixed-field.required-field select, + .grid-container .fixed-field.required-field textarea, + .grid-container .standard-field.required-field input, + .grid-container .standard-field.required-field select, + .grid-container .standard-field.required-field textarea, + .grid-top .fixed-field.required-field input, + .grid-top .fixed-field.required-field select, + .grid-top .fixed-field.required-field textarea, + .grid-top .standard-field.required-field input, + .grid-top .standard-field.required-field select, + .grid-top .standard-field.required-field textarea { + background-color: #ffffff !important; + border: 2px solid #dc3545 !important; + } + + /* Select2 - schema/customfield fields: green */ + .grid-container .schema-field .select2-container--default .select2-selection--single, + .grid-top .schema-field .select2-container--default .select2-selection--single { + background-color: #dff3e3 !important; + } + + /* Select2 - fixed/standard fields: white */ + .grid-container .fixed-field .select2-container--default .select2-selection--single, + .grid-container .standard-field .select2-container--default .select2-selection--single, + .grid-top .fixed-field .select2-container--default .select2-selection--single, + .grid-top .standard-field .select2-container--default .select2-selection--single { + background-color: #ffffff !important; + } + + /* Select2 - required fields: red border only */ + .grid-container .required-field .select2-container--default .select2-selection--single, + .grid-top .required-field .select2-container--default .select2-selection--single { + border: 2px solid #dc3545 !important; + box-shadow: 0 0 0 1px rgba(220, 53, 69, 0.15) !important; + } + + /* Remove old red background from required classes */ + .grid-container input.required-input, + .grid-container select.required-input, + .grid-container textarea.required-input, + .grid-top input.required-input, + .grid-top select.required-input, + .grid-top textarea.required-input { + background-color: inherit !important; + } + + /* Missing required cell: red outline only */ + .grid-cell.missing-required { + background-color: inherit !important; + border-right: 1px solid #dee2e6 !important; + outline: 2px solid #dc3545 !important; + outline-offset: -2px; + } Edit Imported Data - <?= htmlspecialchars($titlewebsite, ENT_QUOTES, 'UTF-8'); ?> diff --git a/public/userarea/load_active_templates.php b/public/userarea/load_active_templates.php index b0d9fb7..29680c9 100644 --- a/public/userarea/load_active_templates.php +++ b/public/userarea/load_active_templates.php @@ -13,7 +13,10 @@ try { } // Recupera solo i template attivi - $stmt = $pdo->query("SELECT id, button_label, button_bg_color, button_text_color, button_size FROM excel_templates WHERE status = 'active'"); + $stmt = $pdo->query("SELECT id, button_label, button_size, button_bg_color, button_text_color, source_type +FROM excel_templates +WHERE status = 'active' +ORDER BY button_label ASC"); $templates = $stmt->fetchAll(PDO::FETCH_ASSOC); $response["success"] = true; diff --git a/public/userarea/schemi_base_response.json b/public/userarea/schemi_base_response.json index 4219d5a..2be9d68 100644 --- a/public/userarea/schemi_base_response.json +++ b/public/userarea/schemi_base_response.json @@ -653,7 +653,7 @@ "IdSchemaCustomFields": 163, "ConteggioClienti": 0, "Nome": "Devred", - "Descrizione": "Schema creato per cliente DEVRED\r\nGR 18\/03\/2024\r\n\r\n" + "Descrizione": "Schema creato per cliente DEVRED\r\nGR 18\/03\/2024 aggiornato il 23\/04\/2026\r\n\r\n" }, { "IdSchemaCustomFields": 164, From 50d578eea1ebe050ecc581202dcf74ed89fa4b86 Mon Sep 17 00:00:00 2001 From: solocla Date: Mon, 27 Apr 2026 16:02:50 +0200 Subject: [PATCH 02/42] commesssa on commessaweb --- public/userarea/export_to_lims.php | 18 +++++++++++++++++- 1 file changed, 17 insertions(+), 1 deletion(-) diff --git a/public/userarea/export_to_lims.php b/public/userarea/export_to_lims.php index 1b5f833..507a387 100644 --- a/public/userarea/export_to_lims.php +++ b/public/userarea/export_to_lims.php @@ -542,7 +542,23 @@ try { "RESPONSE:\n" . json_encode($commessaAfterPatch, JSON_PRETTY_PRINT); $logFileStep10 = $logDir . "commessa_{$commessaId}_get_step10_" . time() . ".txt"; $writeLog($logFileStep10, $logContentStep10, "STEP 10 - GET verify (commessa={$commessaId})"); + // πŸ”Ή STEP 10.1: Save final CodiceCommessa into datadb.commessaweb + // After ImportaCommessa, the API returns the final LIMS job code in CodiceCommessa. + // Example: CodiceCommessa = 2614795, CodiceCommessaWeb = 26C0103. + $finalCodiceCommessa = trim((string)($commessaAfterPatch['CodiceCommessa'] ?? '')); + if ($finalCodiceCommessa !== '') { + $stmt = $pdo->prepare(" + UPDATE datadb + SET commessaweb = :commessaweb, + status = 'l' + WHERE iddatadb = :iddatadb + "); + $stmt->execute([ + 'commessaweb' => substr($finalCodiceCommessa, 0, 30), + 'iddatadb' => $iddatadb + ]); + } // πŸ”Ή STEP 11: Prepare final response $finalCommessa = [ "Cliente" => $clienteId, @@ -557,7 +573,7 @@ try { echo json_encode([ "success" => true, "idcommessaweb" => $commessaId, - "commessaweb" => $commessaWebCode, + "commessaweb" => $finalCodiceCommessa ?: $commessaWebCode, "commessaWeb" => $finalCommessa, "commessaWebApiResponse" => $commessaWeb, // Incluso per debug "totalCampioni" => count($campioni), From cfbbc36116042abf3e0bc26bc3fd7fe77e18ae83 Mon Sep 17 00:00:00 2001 From: "r.mubarakzyanov" Date: Tue, 28 Apr 2026 12:53:21 +0300 Subject: [PATCH 03/42] Fix TZ Issue --- public/userarea/apply_routine.php | 3 +++ public/userarea/class/db-functions.php | 3 +++ public/userarea/cronfiles/get_matrici_cron.php | 2 ++ public/userarea/import_edit2.php | 4 ---- public/userarea/imported.php | 4 ---- public/userarea/include/headscript.php | 3 +++ public/userarea/process_import_xls.php | 3 +++ public/userarea/test_auth.php | 3 +++ public/userarea/tolims.php | 4 ---- 9 files changed, 17 insertions(+), 12 deletions(-) diff --git a/public/userarea/apply_routine.php b/public/userarea/apply_routine.php index 601fb8f..64670f3 100644 --- a/public/userarea/apply_routine.php +++ b/public/userarea/apply_routine.php @@ -3,6 +3,9 @@ ob_start(); session_start(); require_once '../../vendor/autoload.php'; +Dotenv\Dotenv::createImmutable(dirname(__DIR__, 2))->safeLoad(); +date_default_timezone_set($_ENV['APP_TIMEZONE'] ?? 'Europe/Rome'); + $response = ['error' => '', 'rows' => [], 'columns' => [], 'template_id' => 0, 'filename' => '', 'excel_data' => []]; try { diff --git a/public/userarea/class/db-functions.php b/public/userarea/class/db-functions.php index e16888a..18ff684 100644 --- a/public/userarea/class/db-functions.php +++ b/public/userarea/class/db-functions.php @@ -3,6 +3,9 @@ require_once dirname(__DIR__, 3) . '/vendor/autoload.php'; use Dotenv\Dotenv; +Dotenv::createImmutable(dirname(__DIR__, 3))->safeLoad(); +date_default_timezone_set($_ENV['APP_TIMEZONE'] ?? 'Europe/Rome'); + class DBHandlerSelect { private static $instance = null; diff --git a/public/userarea/cronfiles/get_matrici_cron.php b/public/userarea/cronfiles/get_matrici_cron.php index b118694..bce2c7b 100644 --- a/public/userarea/cronfiles/get_matrici_cron.php +++ b/public/userarea/cronfiles/get_matrici_cron.php @@ -17,6 +17,8 @@ try { exit(1); } +date_default_timezone_set($_ENV['APP_TIMEZONE'] ?? 'Europe/Rome'); + // Recupera le variabili d'ambiente $dbHost = $_ENV['DB_HOST']; $dbName = $_ENV['DB_DATABASE']; diff --git a/public/userarea/import_edit2.php b/public/userarea/import_edit2.php index f2dbcfa..acd2f35 100644 --- a/public/userarea/import_edit2.php +++ b/public/userarea/import_edit2.php @@ -1,10 +1,6 @@ safeLoad(); +date_default_timezone_set($_ENV['APP_TIMEZONE'] ?? 'Europe/Rome'); + $response = ['error' => '', 'rows' => [], 'columns' => [], 'template_id' => 0, 'filename' => '']; try { diff --git a/public/userarea/test_auth.php b/public/userarea/test_auth.php index 63d2a91..aef25fd 100644 --- a/public/userarea/test_auth.php +++ b/public/userarea/test_auth.php @@ -8,6 +8,9 @@ ini_set('log_errors', 1); require_once dirname(__DIR__, 2) . '/vendor/autoload.php'; require_once __DIR__ . '/class/VisualLimsApiClient.class.php'; +Dotenv\Dotenv::createImmutable(dirname(__DIR__, 2))->safeLoad(); +date_default_timezone_set($_ENV['APP_TIMEZONE'] ?? 'Europe/Rome'); + header('Content-Type: application/json'); try { diff --git a/public/userarea/tolims.php b/public/userarea/tolims.php index d09635e..458a686 100644 --- a/public/userarea/tolims.php +++ b/public/userarea/tolims.php @@ -1,10 +1,6 @@ Date: Wed, 29 Apr 2026 08:38:16 +0200 Subject: [PATCH 04/42] added get --- public/userarea/get_analisiabilitate.php | 95 ++++++++++++++++++++++++ public/userarea/get_campionematrice.php | 88 ++++++++++++++++++++++ 2 files changed, 183 insertions(+) create mode 100644 public/userarea/get_analisiabilitate.php create mode 100644 public/userarea/get_campionematrice.php diff --git a/public/userarea/get_analisiabilitate.php b/public/userarea/get_analisiabilitate.php new file mode 100644 index 0000000..d7db8ce --- /dev/null +++ b/public/userarea/get_analisiabilitate.php @@ -0,0 +1,95 @@ + 'AnalisiAbilitate' + ]; + + // Debug: salva URL usato + $base_url = 'https://93.43.5.102/limsapi/api/odata/'; + $query = http_build_query($options); + $query = urldecode($query); // rende leggibile $expand invece di %24expand + + $full_url = $base_url . $endpoint . ($query ? '?' . $query : ''); + file_put_contents(__DIR__ . '/last_url_analisi_abilitate.txt', $full_url . PHP_EOL, FILE_APPEND); + + // Chiamata API + $data = $api->get($endpoint, $options); + + // Recupera AnalisiAbilitate dalla risposta + $analisiAbilitate = $data['AnalisiAbilitate'] ?? []; + + // Alcune API OData possono restituire collection dentro "value" + if (isset($analisiAbilitate['value']) && is_array($analisiAbilitate['value'])) { + $analisiAbilitate = $analisiAbilitate['value']; + } + + // Cerca se il RecordKey / IdAnalisi che stai usando Γ¨ effettivamente assegnabile + $matches = []; + + foreach ($analisiAbilitate as $analisi) { + $recordKey = isset($analisi['RecordKey']) ? (string)$analisi['RecordKey'] : ''; + $idAnalisi = isset($analisi['IdAnalisi']) ? (string)$analisi['IdAnalisi'] : ''; + + if ($recordKey === $targetRecordKey || $idAnalisi === $targetIdAnalisi) { + $matches[] = $analisi; + } + } + + // Output diagnostico + $output = [ + 'success' => true, + 'idCampione' => $idCampione, + 'request_url' => $full_url, + 'targetRecordKey' => $targetRecordKey, + 'targetIdAnalisi' => $targetIdAnalisi, + 'enabled_analyses_count' => is_array($analisiAbilitate) ? count($analisiAbilitate) : 0, + 'target_found' => count($matches) > 0, + 'target_matches' => $matches, + 'analisi_abilitate' => $analisiAbilitate, + 'raw_response' => $data + ]; + + // Salva il JSON in locale + file_put_contents( + __DIR__ . '/analisi_abilitate_campione_749027_response.json', + json_encode($output, JSON_PRETTY_PRINT | JSON_UNESCAPED_UNICODE) + ); + + echo json_encode($output, JSON_PRETTY_PRINT | JSON_UNESCAPED_UNICODE); +} catch (Exception $e) { + file_put_contents( + __DIR__ . '/error_log_analisi_abilitate.txt', + date('Y-m-d H:i:s') . ' - ' . $e->getMessage() . PHP_EOL, + FILE_APPEND + ); + + http_response_code(500); + + echo json_encode([ + 'success' => false, + 'error' => $e->getMessage() + ], JSON_PRETTY_PRINT | JSON_UNESCAPED_UNICODE); +} diff --git a/public/userarea/get_campionematrice.php b/public/userarea/get_campionematrice.php new file mode 100644 index 0000000..74aef0e --- /dev/null +++ b/public/userarea/get_campionematrice.php @@ -0,0 +1,88 @@ + 'Matrice' + ]; + + // Debug URL + $base_url = 'https://93.43.5.102/limsapi/api/odata/'; + $query = http_build_query($options); + $queryReadable = urldecode($query); + + $full_url = $base_url . $endpoint . ($queryReadable ? '?' . $queryReadable : ''); + + file_put_contents( + __DIR__ . '/last_url_check_matrice.txt', + $full_url . PHP_EOL, + FILE_APPEND + ); + + // Chiamata API + $data = $api->get($endpoint, $options); + + // Recupero Matrice dalla response + $matrice = $data['Matrice'] ?? null; + + $actualMatriceId = null; + + if (is_array($matrice)) { + // Provo i nomi piΓΉ probabili + $actualMatriceId = $matrice['IdMatrice'] + ?? $matrice['idMatrice'] + ?? $matrice['Id'] + ?? $matrice['ID'] + ?? null; + } + + $matrice_ok = ((int)$actualMatriceId === (int)$expectedMatriceId); + + $output = [ + 'success' => true, + 'idCampione' => $idCampione, + 'expectedMatriceId' => $expectedMatriceId, + 'actualMatriceId' => $actualMatriceId, + 'matrice_ok' => $matrice_ok, + 'request_url' => $full_url, + 'matrice' => $matrice, + 'raw_response' => $data + ]; + + // Salva JSON completo + file_put_contents( + __DIR__ . '/check_matrice_campione_749027_response.json', + json_encode($output, JSON_PRETTY_PRINT | JSON_UNESCAPED_UNICODE) + ); + + echo json_encode($output, JSON_PRETTY_PRINT | JSON_UNESCAPED_UNICODE); +} catch (Exception $e) { + file_put_contents( + __DIR__ . '/error_log_check_matrice.txt', + date('Y-m-d H:i:s') . ' - ' . $e->getMessage() . PHP_EOL, + FILE_APPEND + ); + + http_response_code(500); + + echo json_encode([ + 'success' => false, + 'error' => $e->getMessage() + ], JSON_PRETTY_PRINT | JSON_UNESCAPED_UNICODE); +} From 3a7dd266c89498e54db895aa8d653e33561cce08 Mon Sep 17 00:00:00 2001 From: solocla Date: Wed, 29 Apr 2026 12:16:44 +0200 Subject: [PATCH 05/42] fixed quotation --- public/userarea/quotations.php | 797 +++++++++++++++++++++++---------- 1 file changed, 555 insertions(+), 242 deletions(-) diff --git a/public/userarea/quotations.php b/public/userarea/quotations.php index f2d9b43..067fe1c 100644 --- a/public/userarea/quotations.php +++ b/public/userarea/quotations.php @@ -1,15 +1,15 @@ getConnection(); -// Recupera l'ID dell'utente loggato $user_id = $iduserlogin ?? 1; -// Gestione creazione nuova quotation (crea record vuoto su conferma) -if ($_SERVER['REQUEST_METHOD'] === 'POST' && isset($_POST['action']) && $_POST['action'] === 'create') { - $description = ''; - $customer = ''; +/** + * Helper redirect + */ +function redirectWithMessage($status, $message, $extraQuery = '') +{ + $url = 'quotations.php?status=' . urlencode($status) . '&message=' . urlencode($message); + + if ($extraQuery !== '') { + $url .= '&' . ltrim($extraQuery, '&'); + } + + header("Location: " . $url); + exit; +} + +/** + * CREATE quotation + * Ora description e customer vengono salvati subito dal modale. + */ +if ($_SERVER['REQUEST_METHOD'] === 'POST' && ($_POST['action'] ?? '') === 'create') { + $description = trim($_POST['description'] ?? ''); + $customer = trim($_POST['customer'] ?? ''); + + if ($description === '' || $customer === '') { + redirectWithMessage('error', 'Descrizione e Cliente sono obbligatori'); + } try { - $stmt = $pdo->prepare("INSERT INTO quotations (description, customer, iduser) VALUES (?, ?, ?)"); - $success = $stmt->execute([$description, $customer, $user_id]); + $stmt = $pdo->prepare(" + INSERT INTO quotations + (description, customer, iduser) + VALUES + (?, ?, ?) + "); + + $success = $stmt->execute([ + $description, + $customer, + $user_id + ]); + if ($success) { $newId = $pdo->lastInsertId(); - error_log("Creata nuova quotation ID: $newId"); - header("Location: quotations.php?edit_id=" . $newId . "&status=success&message=" . urlencode("Quotation creata con successo")); - } else { - error_log("Errore: Impossibile creare la quotation, nessun ID generato."); - header("Location: quotations.php?status=error&message=" . urlencode("Errore durante la creazione della quotation")); + error_log("Creata nuova quotation ID: " . $newId); + + redirectWithMessage('success', 'Quotation creata con successo'); } + + error_log("Errore: impossibile creare la quotation"); + redirectWithMessage('error', 'Errore durante la creazione della quotation'); } catch (PDOException $e) { error_log("Errore PDO durante la creazione della quotation: " . $e->getMessage()); - header("Location: quotations.php?status=error&message=" . urlencode("Errore database: " . $e->getMessage())); + redirectWithMessage('error', 'Errore database: ' . $e->getMessage()); } - exit; } -// Gestione modifica quotation -if ($_SERVER['REQUEST_METHOD'] === 'POST' && isset($_POST['action']) && $_POST['action'] === 'update' && isset($_POST['id'])) { +/** + * UPDATE quotation + * Gestisce sia form normale sia salvataggio inline AJAX. + */ +if ($_SERVER['REQUEST_METHOD'] === 'POST' && ($_POST['action'] ?? '') === 'update' && isset($_POST['id'])) { $id = intval($_POST['id']); - $description = $_POST['description'] ?? ''; - $customer = $_POST['customer'] ?? ''; + $description = trim($_POST['description'] ?? ''); + $customer = trim($_POST['customer'] ?? ''); + $isAjax = isset($_POST['ajax']) && $_POST['ajax'] === '1'; + + if ($description === '' || $customer === '') { + if ($isAjax) { + header('Content-Type: application/json'); + echo json_encode([ + 'success' => false, + 'message' => 'Descrizione e Cliente sono obbligatori' + ]); + exit; + } + + redirectWithMessage('error', 'Descrizione e Cliente sono obbligatori'); + } try { - $stmt = $pdo->prepare("UPDATE quotations SET description = ?, customer = ? WHERE id = ? AND iduser = ?"); - $stmt->execute([$description, $customer, $id, $user_id]); - error_log("Modificata quotation ID: $id"); - header("Location: quotations.php?status=success&message=" . urlencode("Quotation modificata con successo")); + $stmt = $pdo->prepare(" + UPDATE quotations + SET description = ?, customer = ? + WHERE id = ? AND iduser = ? + "); + + $stmt->execute([ + $description, + $customer, + $id, + $user_id + ]); + + error_log("Modificata quotation ID: " . $id); + + if ($isAjax) { + header('Content-Type: application/json'); + echo json_encode([ + 'success' => true, + 'message' => 'Quotation modificata con successo' + ]); + exit; + } + + redirectWithMessage('success', 'Quotation modificata con successo'); } catch (PDOException $e) { error_log("Errore PDO durante la modifica della quotation: " . $e->getMessage()); - header("Location: quotations.php?status=error&message=" . urlencode("Errore database: " . $e->getMessage())); + + if ($isAjax) { + header('Content-Type: application/json'); + echo json_encode([ + 'success' => false, + 'message' => 'Errore database: ' . $e->getMessage() + ]); + exit; + } + + redirectWithMessage('error', 'Errore database: ' . $e->getMessage()); } - exit; } -// Gestione cancellazione quotation -if ($_SERVER['REQUEST_METHOD'] === 'POST' && isset($_POST['action']) && $_POST['action'] === 'delete' && isset($_POST['id'])) { +/** + * DELETE quotation + */ +if ($_SERVER['REQUEST_METHOD'] === 'POST' && ($_POST['action'] ?? '') === 'delete' && isset($_POST['id'])) { $id = intval($_POST['id']); try { - $stmt = $pdo->prepare("DELETE FROM quotations WHERE id = ? AND iduser = ?"); - $stmt->execute([$id, $user_id]); - error_log("Cancellata quotation ID: $id"); - header("Location: quotations.php?status=success&message=" . urlencode("Quotation cancellata con successo")); + $stmt = $pdo->prepare(" + DELETE FROM quotations + WHERE id = ? AND iduser = ? + "); + + $stmt->execute([ + $id, + $user_id + ]); + + error_log("Cancellata quotation ID: " . $id); + + redirectWithMessage('success', 'Quotation cancellata con successo'); } catch (PDOException $e) { error_log("Errore PDO durante la cancellazione della quotation: " . $e->getMessage()); - header("Location: quotations.php?status=error&message=" . urlencode("Errore database: " . $e->getMessage())); + redirectWithMessage('error', 'Errore database: ' . $e->getMessage()); } - exit; } -// Recupera tutte le quotations per l'utente +/** + * Recupera tutte le quotations dell'utente. + * Ultima creata in alto. + */ try { - $stmt = $pdo->prepare("SELECT * FROM quotations WHERE iduser = ? ORDER BY creation_date DESC"); - $stmt->execute([$user_id]); + $stmt = $pdo->prepare(" + SELECT * + FROM quotations + WHERE iduser = ? + ORDER BY creation_date DESC, id DESC + "); + + $stmt->execute([ + $user_id + ]); + $quotations = $stmt->fetchAll(PDO::FETCH_ASSOC); } catch (PDOException $e) { error_log("Errore PDO durante il recupero delle quotations: " . $e->getMessage()); $quotations = []; } -// Verifica se Γ¨ richiesta la modifica di una quotation +/** + * Recupera quotation in modifica dettagliata + */ $editQuotation = null; + if (isset($_GET['edit_id'])) { $editId = intval($_GET['edit_id']); + try { - $stmt = $pdo->prepare("SELECT * FROM quotations WHERE id = ? AND iduser = ?"); - $stmt->execute([$editId, $user_id]); + $stmt = $pdo->prepare(" + SELECT * + FROM quotations + WHERE id = ? AND iduser = ? + "); + + $stmt->execute([ + $editId, + $user_id + ]); + $editQuotation = $stmt->fetch(PDO::FETCH_ASSOC); + if (!$editQuotation) { - error_log("Nessuna quotation trovata per id: $editId"); + error_log("Nessuna quotation trovata per id: " . $editId); } } catch (PDOException $e) { error_log("Errore PDO durante il recupero della quotation per modifica: " . $e->getMessage()); @@ -112,10 +227,14 @@ if (isset($_GET['edit_id'])) { + - + + + + + Gestione Quotations - <?= htmlspecialchars($titlewebsite, ENT_QUOTES, 'UTF-8'); ?> @@ -271,50 +444,100 @@ if (isset($_GET['edit_id'])) {
+
-
+
Gestione Quotations
+ + + +
+
+ -
- +
+
+ - -
Modifica Quotation ID:
-
+ +
+ Modifica Quotation ID: +
+ + - + +
- +
+
- +
- - Torna alla Lista + + + + + Torna alla Lista +
+
Azioni
- - + + + +
+ - -
- -
+
Quotations Esistenti
+ @@ -325,110 +548,246 @@ if (isset($_GET['edit_id'])) { + $row): ?> - - - + + + + + + +
Azioni
- + - + - - - - - + + + + + + + + + + +
+ +
- + + +
+ + + - - - \ No newline at end of file From ce00247d1cdf4b9fb29eff62f520cb2a2b07f47e Mon Sep 17 00:00:00 2001 From: solocla Date: Mon, 4 May 2026 11:21:30 +0200 Subject: [PATCH 06/42] edit templtae lingue e colori --- .gitignore | 3 + .../class/VisualLimsApiClient.class.php | 35 + public/userarea/download_rapporto_file.php | 112 +++ public/userarea/edit_template_xls.php | 144 +++- public/userarea/error_log.txt | 3 + .../userarea/get_rapporto_full_by_codice.php | 256 +++++++ public/userarea/get_rapporto_lims.php | 118 +++ public/userarea/get_rapporto_prova.php | 135 +++- public/userarea/gridRenderer.js | 26 +- .../userarea/mapping_template_xls_scheme2.php | 710 +++++++++++++++--- public/userarea/ping_lims_api.php | 36 + public/userarea/save_mapping_json.php | 56 +- public/userarea/schema_dettagli_response.json | 2 +- public/userarea/search_clienti.php | 70 +- public/userarea/search_rapporto_min.php | 73 ++ public/userarea/test_pdf_value.php | 117 +++ public/userarea/update_api_json_nodes.php | 63 ++ 17 files changed, 1805 insertions(+), 154 deletions(-) create mode 100644 public/userarea/download_rapporto_file.php create mode 100644 public/userarea/get_rapporto_full_by_codice.php create mode 100644 public/userarea/get_rapporto_lims.php create mode 100644 public/userarea/ping_lims_api.php create mode 100644 public/userarea/search_rapporto_min.php create mode 100644 public/userarea/test_pdf_value.php create mode 100644 public/userarea/update_api_json_nodes.php diff --git a/.gitignore b/.gitignore index 38634bf..9513359 100644 --- a/.gitignore +++ b/.gitignore @@ -40,10 +40,13 @@ yarn-error.log /public/userarea/logaspi/ /public/userarea/logs/api/ /public/userarea/logs/api/** +/public/userarea/logs/ +/public/userarea/logs/** /public/userarea/photostrf/ /public/userarea/class/*.log /public/userarea/class/curl_auth_debug.log /public/userarea/class/curl_request_debug.log +/public/userarea/schema_dettagli_response.json # File XLSX temporanei importati /public/userarea/imported_trf/*.xlsx diff --git a/public/userarea/class/VisualLimsApiClient.class.php b/public/userarea/class/VisualLimsApiClient.class.php index 61bd945..369d585 100644 --- a/public/userarea/class/VisualLimsApiClient.class.php +++ b/public/userarea/class/VisualLimsApiClient.class.php @@ -255,4 +255,39 @@ class VisualLimsApiClient { return $this->baseUrl; } + + /** + * Recupera contenuto binario - Adattato per https://bvcpsitaly-elims.com/limsapi + */ + public function getRaw($endpoint) + { + $token = $this->getToken(); + + // IMPORTANTE: usa /odata/ e NON /api/odata/ + $url = "{$this->baseUrl}/odata/{$endpoint}"; + + $ch = curl_init($url); + curl_setopt($ch, CURLOPT_RETURNTRANSFER, true); + curl_setopt($ch, CURLOPT_HTTPHEADER, [ + "Authorization: Bearer {$token}", + "Accept: */*" + ]); + 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 cURL: " . $curl_error); + } + + if ($http_code !== 200) { + throw new Exception("HTTP {$http_code} su endpoint: " . $url); + } + + return $response; + } } diff --git a/public/userarea/download_rapporto_file.php b/public/userarea/download_rapporto_file.php new file mode 100644 index 0000000..14ea0ae --- /dev/null +++ b/public/userarea/download_rapporto_file.php @@ -0,0 +1,112 @@ +get("RapportoFile({$id})", []); + + file_put_contents( + $debugDir . "rapporto_file_{$id}.json", + json_encode($data, JSON_PRETTY_PRINT | JSON_UNESCAPED_UNICODE) + ); + + /** + * Provo vari nomi campo possibili per il contenuto PDF. + * Dopo il primo test potremo adattarlo al nome esatto. + */ + $possibleContentFields = [ + 'File', + 'file', + 'Content', + 'content', + 'FileContent', + 'fileContent', + 'Contenuto', + 'contenuto', + 'Bytes', + 'bytes' + ]; + + $base64 = null; + + foreach ($possibleContentFields as $field) { + if (!empty($data[$field])) { + $base64 = $data[$field]; + break; + } + } + + if (!$base64) { + header('Content-Type: application/json; charset=utf-8'); + echo json_encode([ + 'success' => false, + 'message' => 'Record RapportoFile recuperato, ma non ho trovato un campo base64 del PDF.', + 'hint' => 'Apri logs/lims_get_commessa/rapporto_file_' . $id . '.json e controlla il nome reale del campo file.', + 'data' => $data + ], JSON_PRETTY_PRINT | JSON_UNESCAPED_UNICODE); + exit; + } + + /** + * Se il contenuto ha prefisso data URI, lo pulisco. + */ + if (strpos($base64, 'base64,') !== false) { + $parts = explode('base64,', $base64); + $base64 = end($parts); + } + + $pdfBinary = base64_decode($base64, true); + + if ($pdfBinary === false) { + throw new Exception("Il contenuto trovato non Γ¨ un base64 valido"); + } + + $filename = $data['NomeFile'] + ?? $data['nomeFile'] + ?? $data['FileName'] + ?? $data['fileName'] + ?? "rapporto_{$id}.pdf"; + + if (!str_ends_with(strtolower($filename), '.pdf')) { + $filename .= '.pdf'; + } + + header('Content-Type: application/pdf'); + header('Content-Disposition: inline; filename="' . basename($filename) . '"'); + header('Content-Length: ' . strlen($pdfBinary)); + + echo $pdfBinary; +} catch (Exception $e) { + http_response_code(500); + header('Content-Type: application/json; charset=utf-8'); + + echo json_encode([ + 'success' => false, + 'error' => $e->getMessage() + ], JSON_PRETTY_PRINT | JSON_UNESCAPED_UNICODE); +} diff --git a/public/userarea/edit_template_xls.php b/public/userarea/edit_template_xls.php index ef817d2..7915132 100644 --- a/public/userarea/edit_template_xls.php +++ b/public/userarea/edit_template_xls.php @@ -25,6 +25,41 @@ if (!$template) { $stmt = $pdo->prepare("SELECT * FROM routine"); $stmt->execute(); $routines = $stmt->fetchAll(PDO::FETCH_ASSOC); + +$buttonBgPalette = [ + '#0d6efd' => 'Blue', + '#6610f2' => 'Indigo', + '#6f42c1' => 'Purple', + '#d63384' => 'Pink', + '#dc3545' => 'Red', + '#fd7e14' => 'Orange', + '#ffc107' => 'Yellow', + '#198754' => 'Green', + '#20c997' => 'Teal', + '#0dcaf0' => 'Cyan', + '#212529' => 'Dark', + '#6c757d' => 'Gray', + '#495057' => 'Slate', + '#795548' => 'Brown', + '#2f5d50' => 'Sage Green', +]; + +$buttonTextPalette = [ + '#ffffff' => 'White', + '#6c757d' => 'Gray', + '#000000' => 'Black', +]; + +$currentButtonBgColor = strtolower($template['button_bg_color'] ?? '#007bff'); +$currentButtonTextColor = strtolower($template['button_text_color'] ?? '#ffffff'); + +if (!array_key_exists($currentButtonBgColor, array_change_key_case($buttonBgPalette, CASE_LOWER))) { + $currentButtonBgColor = '#0d6efd'; +} + +if (!array_key_exists($currentButtonTextColor, array_change_key_case($buttonTextPalette, CASE_LOWER))) { + $currentButtonTextColor = '#ffffff'; +} ?> @@ -35,6 +70,64 @@ $routines = $stmt->fetchAll(PDO::FETCH_ASSOC); + Edit Template <?= htmlspecialchars($titlewebsite, ENT_QUOTES, 'UTF-8'); ?> @@ -123,12 +216,46 @@ $routines = $stmt->fetchAll(PDO::FETCH_ASSOC);
- + +
+ $colorLabel): ?> + + + + +
- + +
+ $colorLabel): ?> + + + + +
@@ -258,6 +385,16 @@ $routines = $stmt->fetchAll(PDO::FETCH_ASSOC); sourceType.addEventListener('change', updateSourceFields); updateSourceFields(); + function formatClientLabel(client) { + const nome = client.Nominativo || "Name not available"; + const id = client.IdCliente || ""; + const codiceCliente = (client.CodiceCliente ?? client.codiceCliente ?? "").toString().trim(); + const suffix = (codiceCliente.split("_")[1] || "").trim(); + const shortCode = suffix || (codiceCliente ? codiceCliente.charAt(0) : "--"); + + return `${nome.trim()} - ${shortCode} (ID: ${id})`; + } + async function loadClients() { try { clientLoadingStatus.style.display = 'inline'; @@ -280,9 +417,8 @@ $routines = $stmt->fetchAll(PDO::FETCH_ASSOC); select.innerHTML = ''; data.value.forEach(client => { - const nome = client.Nominativo || "Name not available"; const id = client.IdCliente || ""; - const option = new Option(`${nome.trim()} (ID: ${id})`, id); + const option = new Option(formatClientLabel(client), id); if (parseInt(id) === parseInt(selectedClientId)) { option.selected = true; diff --git a/public/userarea/error_log.txt b/public/userarea/error_log.txt index 4c5140a..9d43509 100644 --- a/public/userarea/error_log.txt +++ b/public/userarea/error_log.txt @@ -328,3 +328,6 @@ 2026-03-26 10:57:41 [MoltiplicatorePrezzo] Autenticazione fallita: HTTP 500, Errore cURL: , Risposta: {"title":"Internal Server Error","status":500,"detail":"Timeout expired. The timeout period elapsed prior to obtaining a connection from the pool. This may have occurred because all pooled connections were in use and max pool size was reached.","instance":"POST /api/authentication/authenticate","errorCode":"63ab532c6"} 2026-03-26 10:57:56 [AnagraficaCertestObject] Autenticazione fallita: HTTP 500, Errore cURL: , Risposta: {"title":"Internal Server Error","status":500,"detail":"Timeout expired. The timeout period elapsed prior to obtaining a connection from the pool. This may have occurred because all pooled connections were in use and max pool size was reached.","instance":"POST /api/authentication/authenticate","errorCode":"63ab532c6"} 2026-03-26 10:58:11 [AnagraficaCertestService] Autenticazione fallita: HTTP 500, Errore cURL: , Risposta: {"title":"Internal Server Error","status":500,"detail":"Timeout expired. The timeout period elapsed prior to obtaining a connection from the pool. This may have occurred because all pooled connections were in use and max pool size was reached.","instance":"POST /api/authentication/authenticate","errorCode":"63ab532c6"} +2026-04-30 15:01:25 - Errore nel recupero dati: HTTP 404, Risposta: {"title":"Not Found","status":404,"detail":"Not Found","instance":"GET /api/odata/Rapporto(2621521)","errorCode":"d25cbd678"} +2026-04-30 15:02:04 - Errore nel recupero dati: HTTP 404, Risposta: +2026-04-30 15:03:19 - Errore nella richiesta: URL rejected: Malformed input to a URL function diff --git a/public/userarea/get_rapporto_full_by_codice.php b/public/userarea/get_rapporto_full_by_codice.php new file mode 100644 index 0000000..9d311e0 --- /dev/null +++ b/public/userarea/get_rapporto_full_by_codice.php @@ -0,0 +1,256 @@ + "CodiceRapporto eq '{$codiceRapportoSafe}'", + '$select' => 'IdRapporto,CodiceRapporto,Data,Versione,Firmato,DataStampa', + '$top' => 1 + ]; + + $searchEndpoint = "Rapporto?" . http_build_query($searchParams); + $searchData = $api->get($searchEndpoint); + + $rapporti = $searchData['value'] ?? []; + + if (empty($rapporti)) { + echo json_encode([ + 'success' => false, + 'message' => 'Nessun rapporto trovato per questo CodiceRapporto.', + 'codice_rapporto' => $codiceRapporto + ], JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE); + exit; + } + + $rapportoBase = $rapporti[0]; + $idRapporto = intval($rapportoBase['IdRapporto'] ?? 0); + + if (!$idRapporto) { + throw new Exception("IdRapporto non trovato nella risposta."); + } + + /** + * STEP 2 - Dettaglio Rapporto + */ + $clienteExpandError = null; + $detailEndpoint = "Rapporto({$idRapporto})?\$expand=Cliente,RapportiFiles,CampioniDatiRapporto"; + + try { + $detailData = $api->get($detailEndpoint); + } catch (Exception $e) { + $clienteExpandError = $e->getMessage(); + $detailEndpoint = "Rapporto({$idRapporto})?\$expand=RapportiFiles,CampioniDatiRapporto"; + $detailData = $api->get($detailEndpoint); + } + + file_put_contents( + $debugDir . "rapporto_{$codiceRapportoSafe}_light.json", + json_encode($detailData, JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE) + ); + + $cliente = $detailData['Cliente'] ?? null; + $rapportiFiles = $detailData['RapportiFiles'] ?? []; + $campioniDatiRapporto = $detailData['CampioniDatiRapporto'] ?? []; + + // ====================== CLIENTE ====================== + $clienteInfo = [ + 'found' => is_array($cliente), + 'id_cliente' => $cliente['IdCliente'] ?? $cliente['Id'] ?? null, + 'codice_cliente' => $cliente['CodiceCliente'] ?? $cliente['Codice'] ?? null, + 'nome_cliente' => $cliente['Nominativo'] ?? $cliente['RagioneSociale'] ?? $cliente['Nome'] ?? $cliente['Descrizione'] ?? null, + 'partita_iva' => $cliente['PartitaIva'] ?? null, + 'codice_fiscale' => $cliente['CodiceFiscale'] ?? null + ]; + + // ====================== FILE PDF ====================== + $selectedRapportoFile = null; + $idRapportoFile = null; + $pdfFileName = $codiceRapporto . '.pdf'; + $pdfFullPath = $pdfDir . $pdfFileName; + + if (is_array($rapportiFiles) && count($rapportiFiles) > 0) { + foreach ($rapportiFiles as $file) { + $fileName = $file['FileName'] ?? ''; + $tipoRapporto = $file['TipoRapporto'] ?? ''; + $categoria = $file['Categoria'] ?? ''; + + if ( + stripos($fileName, '.pdf') !== false || + stripos($tipoRapporto, 'Rapporto') !== false || + stripos($categoria, 'Rapporti') !== false + ) { + $selectedRapportoFile = $file; + $idRapportoFile = $file['IdRapportoFile'] ?? null; + break; + } + } + + if (!$selectedRapportoFile && count($rapportiFiles) > 0) { + $selectedRapportoFile = $rapportiFiles[0]; + $idRapportoFile = $rapportiFiles[0]['IdRapportoFile'] ?? null; + } + } + + // ====================== DOWNLOAD PDF - DEBUG AVANZATO ====================== + $pdfDownloaded = false; + $pdfError = null; + $endpointUsato = null; + + if ($downloadPdf && $idRapportoFile) { + + $tentativi = [ + "RapportoFile({$idRapportoFile})/\$value", + "RapportoFile({$idRapportoFile})/Contenuto/\$value", + "RapportoFile({$idRapportoFile})/File/\$value", + "Rapporto({$idRapporto})/RapportiFiles({$idRapportoFile})/\$value", + "Rapporto({$idRapporto})/RapportoFile/\$value", + "GetRapportoFile?IdRapportoFile={$idRapportoFile}", + "RapportoFile/Download?Id={$idRapportoFile}", + "Rapporto/DownloadFile?IdRapporto={$idRapporto}" + ]; + + foreach ($tentativi as $ep) { + try { + $pdfContent = $api->getRaw($ep); + + if (strlen($pdfContent) > 2000) { // PDF decente deve essere piΓΉ grande + file_put_contents($pdfFullPath, $pdfContent); + $pdfDownloaded = true; + $endpointUsato = $ep; + $pdfError = "SUCCESSO con: " . $ep; + break; + } else { + $pdfError = "Contenuto troppo piccolo con: " . $ep; + } + } catch (Exception $e) { + $pdfError = "Fallito con " . $ep . " β†’ " . $e->getMessage(); + } + } + } + + // ====================== CAMPIONI + RATING (ripristinato dal tuo originale) ====================== + $campioniSintesi = []; + $ratingFinale = null; + $ratingSource = null; + $hasIrregolare = false; + $hasPositiveResults = false; + + if (is_array($campioniDatiRapporto)) { + foreach ($campioniDatiRapporto as $campione) { + $giudizioRapporto = $campione['GiudizioRapporto'] ?? null; + $giudizioCertificato = $campione['GiudizioCertificato'] ?? null; + $esitoGiudizioLMR = $campione['EsitoGiudizioLMR'] ?? null; + $esitoGiudizioImpiego = $campione['EsitoGiudizioImpiego'] ?? null; + $risultatiIrregolari = ($campione['RisultatiIrregolari'] ?? false) === true; + $risultatiPositivi = ($campione['RisultatiPositivi'] ?? false) === true; + + $campioniSintesi[] = [ + 'id_campione_dati_rapporto' => $campione['IdCampioneDatiRapporto'] ?? null, + 'codice_campione' => $campione['CodiceCampione'] ?? null, + 'stato_campione' => $campione['StatoCampione'] ?? null, + 'stato_campione_web' => $campione['StatoCampioneWeb'] ?? null, + 'matrice' => $campione['Matrice'] ?? null, + 'macro_matrice' => $campione['MacroMatrice'] ?? null, + 'giudizio_rapporto' => $giudizioRapporto, + 'giudizio_certificato' => $giudizioCertificato, + 'esito_giudizio_lmr' => $esitoGiudizioLMR, + 'esito_giudizio_impiego' => $esitoGiudizioImpiego, + 'risultati_positivi' => $risultatiPositivi, + 'risultati_irregolari' => $risultatiIrregolari + ]; + + if (!$ratingFinale && !empty($giudizioRapporto)) { + $ratingFinale = $giudizioRapporto; + $ratingSource = 'CampioniDatiRapporto.GiudizioRapporto'; + } + + if (!$ratingFinale && !empty($giudizioCertificato)) { + $ratingFinale = $giudizioCertificato; + $ratingSource = 'CampioniDatiRapporto.GiudizioCertificato'; + } + + if ($risultatiIrregolari) $hasIrregolare = true; + if ($risultatiPositivi) $hasPositiveResults = true; + } + } + + // Fallback rating + if (!$ratingFinale) { + if ($hasIrregolare) { + $ratingFinale = 'Irregolare'; + $ratingSource = 'RisultatiIrregolari'; + } elseif ($hasPositiveResults || count($campioniSintesi) > 0) { + $ratingFinale = 'Regolare'; + $ratingSource = 'fallback'; + } + } + + // ====================== RISPOSTA FINALE ====================== + echo json_encode([ + 'success' => true, + + 'codice_rapporto' => $codiceRapporto, + 'id_rapporto' => $idRapporto, + + 'search_endpoint' => $searchEndpoint, + 'detail_endpoint' => $detailEndpoint, + 'cliente_expand_error' => $clienteExpandError, + + 'rapporto_base' => $rapportoBase, + 'cliente' => $clienteInfo, + + 'rating_finale' => $ratingFinale, + 'rating_source' => $ratingSource, + + 'rapporti_files_count' => count($rapportiFiles), + 'selected_rapporto_file' => $selectedRapportoFile, + + 'pdf_file_name' => $pdfFileName, + 'pdf_path' => $pdfDownloaded ? $pdfFullPath : null, + 'pdf_downloaded' => $pdfDownloaded, + 'pdf_error' => $pdfError, + + 'campioni_count' => count($campioniSintesi), + 'campioni_sintesi' => $campioniSintesi, + + 'download_requested' => $downloadPdf + ], JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE); +} catch (Exception $e) { + http_response_code(500); + echo json_encode([ + 'success' => false, + 'error' => $e->getMessage() + ], JSON_PRETTY_PRINT | JSON_UNESCAPED_UNICODE); +} diff --git a/public/userarea/get_rapporto_lims.php b/public/userarea/get_rapporto_lims.php new file mode 100644 index 0000000..a3eff2e --- /dev/null +++ b/public/userarea/get_rapporto_lims.php @@ -0,0 +1,118 @@ + "CodiceCommessa eq '{$commessa}'", + 'Commessa_CodiceCommessa' => "Commessa/CodiceCommessa eq '{$commessa}'", + 'Commessa_IdCommessa' => is_numeric($commessa) ? "Commessa/IdCommessa eq {$commessa}" : null, + 'Codice' => "Codice eq '{$commessa}'" + ]; + + foreach ($filters as $label => $filter) { + if (!$filter) { + continue; + } + + try { + $options = [ + '$filter' => $filter, + '$top' => 10 + ]; + + $data = $api->get('Rapporto', $options); + + $attempts[$label] = [ + 'success' => true, + 'filter' => $filter, + 'records' => isset($data['value']) && is_array($data['value']) ? count($data['value']) : null, + 'data' => $data + ]; + + file_put_contents( + $debugDir . "rapporto_{$commessa}_{$label}.json", + json_encode($data, JSON_PRETTY_PRINT | JSON_UNESCAPED_UNICODE) + ); + } catch (Exception $e) { + $attempts[$label] = [ + 'success' => false, + 'filter' => $filter, + 'error' => $e->getMessage() + ]; + } + } + + /** + * STEP 2 + * Prendo solo eventuali rapporti trovati. + */ + $rapportiFound = []; + + foreach ($attempts as $label => $attempt) { + if (!$attempt['success']) { + continue; + } + + $items = $attempt['data']['value'] ?? []; + + if (!is_array($items)) { + continue; + } + + foreach ($items as $item) { + $rapportiFound[] = [ + 'matched_by' => $label, + 'rapporto' => $item + ]; + } + } + + echo json_encode([ + 'success' => true, + 'input_commessa' => $commessa, + 'message' => 'Ricerca leggera su Rapporto completata. Se trovi un rapporto, poi recuperiamo RapportiFiles solo per quello.', + 'rapporti_found_count' => count($rapportiFound), + 'rapporti_found' => $rapportiFound, + 'attempts_summary' => array_map(function ($a) { + return [ + 'success' => $a['success'], + 'filter' => $a['filter'], + 'records' => $a['records'] ?? null, + 'error' => $a['error'] ?? null + ]; + }, $attempts) + ], JSON_PRETTY_PRINT | JSON_UNESCAPED_UNICODE); +} catch (Exception $e) { + http_response_code(500); + + echo json_encode([ + 'success' => false, + 'error' => $e->getMessage() + ], JSON_PRETTY_PRINT | JSON_UNESCAPED_UNICODE); +} diff --git a/public/userarea/get_rapporto_prova.php b/public/userarea/get_rapporto_prova.php index 16e8020..0ae0cd9 100644 --- a/public/userarea/get_rapporto_prova.php +++ b/public/userarea/get_rapporto_prova.php @@ -2,25 +2,140 @@ require_once dirname(__DIR__, 2) . '/vendor/autoload.php'; require_once dirname(__FILE__) . '/class/VisualLimsApiClient.class.php'; -header('Content-Type: application/json'); +header('Content-Type: application/json; charset=utf-8'); ini_set('display_errors', '0'); error_reporting(E_ALL); try { $api = VisualLimsApiClient::getInstance(); - $rapporto_id = 533329; - // Costruzione manuale dell'endpoint con espansione annidata - $endpoint = "Rapporto($rapporto_id)?\$expand=CampioniDatiRapporto(\$expand=AnalisiDatiRapporto,CustomFieldsDatiRapporto)"; + // Esempi: + // rapporto_by_codice_expand_step.php?codice=2541111&step=base + // rapporto_by_codice_expand_step.php?codice=2541111&step=files + // rapporto_by_codice_expand_step.php?codice=2541111&step=campioni + // rapporto_by_codice_expand_step.php?codice=2541111&step=files_campioni - // Non passiamo options, giΓ  incluso nell'endpoint - $data = $api->get($endpoint); + $codiceRapporto = trim($_GET['codice'] ?? ''); + $step = trim($_GET['step'] ?? 'base'); - file_put_contents(__DIR__ . '/rapporto_expanded.json', json_encode($data, JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES)); - echo json_encode($data); + if ($codiceRapporto === '') { + throw new Exception("Parametro codice mancante. Usa ?codice=2541111"); + } + + $allowedSteps = [ + 'base' => '', + 'files' => 'RapportiFiles', + 'allegati' => 'RapportiAllegati', + 'campioni' => 'CampioniDatiRapporto', + 'files_campioni' => 'RapportiFiles,CampioniDatiRapporto' + ]; + + if (!array_key_exists($step, $allowedSteps)) { + throw new Exception("Step non valido. Usa: " . implode(', ', array_keys($allowedSteps))); + } + + // Escape OData per eventuali apostrofi + $codiceRapportoSafe = str_replace("'", "''", $codiceRapporto); + + /* + * STEP 1 - Trova IdRapporto partendo da CodiceRapporto. + * Query leggera, con $select e $top=1. + */ + $searchParams = [ + '$filter' => "CodiceRapporto eq '{$codiceRapportoSafe}'", + '$select' => 'IdRapporto,CodiceRapporto,Data,Versione,Firmato,DataStampa', + '$top' => 1 + ]; + + $searchEndpoint = "Rapporto?" . http_build_query($searchParams); + + $searchData = $api->get($searchEndpoint); + + $items = $searchData['value'] ?? []; + + if (!is_array($items) || count($items) === 0) { + echo json_encode([ + 'success' => false, + 'message' => 'Nessun rapporto trovato per questo CodiceRapporto.', + 'codice_rapporto' => $codiceRapporto, + 'search_endpoint' => $searchEndpoint, + 'search_data' => $searchData + ], JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE); + exit; + } + + $rapportoBase = $items[0]; + $rapportoId = intval($rapportoBase['IdRapporto'] ?? 0); + + if (!$rapportoId) { + throw new Exception("IdRapporto non trovato nella risposta."); + } + + /* + * STEP 2 - Se step=base, restituisco solo la ricerca base. + */ + if ($step === 'base') { + echo json_encode([ + 'success' => true, + 'codice_rapporto' => $codiceRapporto, + 'id_rapporto' => $rapportoId, + 'step' => $step, + 'search_endpoint' => $searchEndpoint, + 'rapporto_base' => $rapportoBase + ], JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE); + exit; + } + + /* + * STEP 3 - Espande SOLO il rapporto trovato. + */ + $expandValue = $allowedSteps[$step]; + + $detailParams = [ + '$expand' => $expandValue + ]; + + $detailEndpoint = "Rapporto({$rapportoId})?" . http_build_query($detailParams); + + file_put_contents( + __DIR__ . '/last_rapporto_by_codice_expand_endpoint.txt', + '[' . date('Y-m-d H:i:s') . '] SEARCH: ' . $searchEndpoint . PHP_EOL . + '[' . date('Y-m-d H:i:s') . '] DETAIL: ' . $detailEndpoint . PHP_EOL, + FILE_APPEND + ); + + $detailData = $api->get($detailEndpoint); + + file_put_contents( + __DIR__ . "/rapporto_codice_{$codiceRapportoSafe}_{$step}.json", + json_encode([ + 'search' => $searchData, + 'detail' => $detailData + ], JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE) + ); + + echo json_encode([ + 'success' => true, + 'codice_rapporto' => $codiceRapporto, + 'id_rapporto' => $rapportoId, + 'step' => $step, + 'search_endpoint' => $searchEndpoint, + 'detail_endpoint' => $detailEndpoint, + 'rapporto_base' => $rapportoBase, + 'data' => $detailData + ], JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE); } catch (Exception $e) { - file_put_contents(__DIR__ . '/error_log.txt', date('Y-m-d H:i:s') . ' - ' . $e->getMessage() . PHP_EOL, FILE_APPEND); + 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()]); + + echo json_encode([ + 'success' => false, + 'error' => $e->getMessage() + ], JSON_PRETTY_PRINT | JSON_UNESCAPED_UNICODE); } diff --git a/public/userarea/gridRenderer.js b/public/userarea/gridRenderer.js index bf762e0..a1d0149 100644 --- a/public/userarea/gridRenderer.js +++ b/public/userarea/gridRenderer.js @@ -57,7 +57,20 @@ // ── Client data (AJAX Select2, no bulk loading) ────────────────────── function formatClientLabel(client) { - return (client.Nominativo || "").trim(); + const nome = client.Nominativo || client.text || "Nome non disponibile"; + const id = client.IdCliente || client.id || ""; + const codiceCliente = ( + client.CodiceCliente ?? + client.codiceCliente ?? + "" + ) + .toString() + .trim(); + const suffix = (codiceCliente.split("_")[1] || "").trim(); + const shortCode = + suffix || (codiceCliente ? codiceCliente.charAt(0) : "--"); + + return `${nome.trim()} - ${shortCode} (ID: ${id})`; } // Cache of resolved client names: id β†’ name @@ -102,7 +115,9 @@ ); const json = await resp.json(); const item = (json.results || [])[0]; - if (item) clientNameCache[id] = item.text; + if (item) { + clientNameCache[id] = formatClientLabel(item); + } } catch (e) { /* ignore */ } @@ -125,7 +140,12 @@ return { q: params.term || "", limit: 20 }; }, processResults: function (data) { - return { results: data.results || [] }; + const results = (data.results || []).map((item) => ({ + ...item, + text: formatClientLabel(item), + })); + + return { results: results }; }, cache: true, }, diff --git a/public/userarea/mapping_template_xls_scheme2.php b/public/userarea/mapping_template_xls_scheme2.php index c298501..a9f8d92 100644 --- a/public/userarea/mapping_template_xls_scheme2.php +++ b/public/userarea/mapping_template_xls_scheme2.php @@ -7,7 +7,26 @@ if (!isset($_GET['id']) || !is_numeric($_GET['id'])) { $id = intval($_GET['id']); $db = DBHandlerSelect::getInstance(); $pdo = $db->getConnection(); -$stmt = $pdo->prepare("SELECT name, header_row, start_column, target_table, sample_xlsx, idclient, clientname, idschema, schemaname, schemajson, xls_headers FROM excel_templates WHERE id = ?"); + +$stmt = $pdo->prepare(" + SELECT + name, + header_row, + start_column, + target_table, + source_type, + sample_xlsx, + idclient, + clientname, + idschema, + schemaname, + schemajson, + xls_headers, + api_sample_json, + json_nodes + FROM excel_templates + WHERE id = ? +"); $stmt->execute([$id]); $template = $stmt->fetch(PDO::FETCH_ASSOC); @@ -15,13 +34,43 @@ if (!$template) { die("Template not found"); } +$sourceType = strtoupper($template['source_type'] ?? 'XLS'); +if (!in_array($sourceType, ['XLS', 'API', 'PDF'], true)) { + $sourceType = 'XLS'; +} + $clientName = $template['clientname'] ?: ''; $schemaName = $template['schemaname'] ?: ''; $schemajson = $template['schemajson'] ? json_decode($template['schemajson'], true) : []; -$isSchemajsonEmpty = empty(trim($template['schemajson'])); +$isSchemajsonEmpty = empty(trim($template['schemajson'] ?? '')); // Recupera i campi dalla tabella template_mapping -$stmt = $pdo->prepare("SELECT id, field_id, excel_column, is_manual, manual_default, auto_value, data_type, is_required, default_value, has_list, length, decimals, min_value, max_value, default_curr_date, tablename, field_label, main_field, is_visible_import, is_visible_parts FROM template_mapping WHERE template_id = ?"); +$stmt = $pdo->prepare(" + SELECT + id, + field_id, + excel_column, + json_node, + is_manual, + manual_default, + auto_value, + data_type, + is_required, + default_value, + has_list, + length, + decimals, + min_value, + max_value, + default_curr_date, + tablename, + field_label, + main_field, + is_visible_import, + is_visible_parts + FROM template_mapping + WHERE template_id = ? +"); $stmt->execute([$id]); $mappings = $stmt->fetchAll(PDO::FETCH_ASSOC); @@ -37,11 +86,25 @@ $fixedMappings = $stmt->fetchAll(PDO::FETCH_ASSOC); $hasFixedMappings = !empty($fixedMappings); -// Recupera le colonne giΓ  associate nel database +// Recupera le colonne XLS giΓ  associate nel database $usedColumnsFromDB = array_filter(array_column($mappings, 'excel_column')); +// Recupera i nodi JSON giΓ  associati nel database +$usedJsonNodesFromDB = array_filter(array_column($mappings, 'json_node')); + // Decodifica l'header XLS salvato, se presente $xlsHeaders = $template['xls_headers'] ? json_decode($template['xls_headers'], true) : []; +if (!is_array($xlsHeaders)) { + $xlsHeaders = []; +} + +// Decodifica nodi JSON API salvati, se presenti +$jsonNodes = $template['json_nodes'] ? json_decode($template['json_nodes'], true) : []; +if (!is_array($jsonNodes)) { + $jsonNodes = []; +} + +$apiSampleJson = $template['api_sample_json'] ?? ''; ?> @@ -163,7 +226,8 @@ $xlsHeaders = $template['xls_headers'] ? json_decode($template['xls_headers'], t width: 100% !important; } - .xls-columns option.used-option { + .xls-columns option.used-option, + .json-nodes option.used-option { background-color: #fff3cd; color: #856404; font-weight: 600; @@ -176,6 +240,11 @@ $xlsHeaders = $template['xls_headers'] ? json_decode($template['xls_headers'], t overflow: hidden; text-overflow: ellipsis; } + + #apiJsonExample { + font-family: Consolas, Monaco, monospace; + font-size: 13px; + } @@ -195,30 +264,63 @@ $xlsHeaders = $template['xls_headers'] ? json_decode($template['xls_headers'], t

Client: | Schema: | - Header Row: | - Start Column: + Source: + + | + Header Row: | + Start Column: +

-
- -
- - + + +
+ +
+ + +
+ + + βœ… Current file: + + No file uploaded yet. + +
- - - βœ… Current file: - - No file uploaded yet. - - -
+ +
+ + + +
+ + + + + βœ… JSON nodes already loaded: + + No JSON nodes loaded yet. + + +
+
+ +
+ PDF source type is not implemented yet. +
+
@@ -236,15 +338,11 @@ $xlsHeaders = $template['xls_headers'] ? json_decode($template['xls_headers'], t Parts Title Type - Mapping + Default Value - - - - @@ -268,7 +366,6 @@ $xlsHeaders = $template['xls_headers'] ? json_decode($template['xls_headers'], t > - @@ -276,7 +373,6 @@ $xlsHeaders = $template['xls_headers'] ? json_decode($template['xls_headers'], t - @@ -286,9 +382,19 @@ $xlsHeaders = $template['xls_headers'] ? json_decode($template['xls_headers'], t $autoValue = $mapping['auto_value'] ?? 'none'; $hasAuto = ($autoValue && $autoValue !== 'none'); - $mappingValue = $isSceltaMultipla - ? 'manual' - : ($hasAuto ? 'auto' : ($mapping['excel_column'] ? 'xls' : ((int)$mapping['is_manual'] === 1 ? 'manual' : ''))); + if ($isSceltaMultipla) { + $mappingValue = 'manual'; + } elseif ($hasAuto) { + $mappingValue = 'auto'; + } elseif ($sourceType === 'XLS' && !empty($mapping['excel_column'])) { + $mappingValue = 'xls'; + } elseif ($sourceType === 'API' && !empty($mapping['json_node'])) { + $mappingValue = 'json'; + } elseif ((int)$mapping['is_manual'] === 1) { + $mappingValue = 'manual'; + } else { + $mappingValue = ''; + } ?> - + + + + + + + - + () @@ -330,6 +452,15 @@ $xlsHeaders = $template['xls_headers'] ? json_decode($template['xls_headers'], t style="margin-left:5px;"> X + + + () + + @@ -412,7 +543,6 @@ $xlsHeaders = $template['xls_headers'] ? json_decode($template['xls_headers'], t - @@ -461,8 +591,6 @@ $xlsHeaders = $template['xls_headers'] ? json_decode($template['xls_headers'], t
- - @@ -479,38 +607,47 @@ $xlsHeaders = $template['xls_headers'] ? json_decode($template['xls_headers'], t - - + \ No newline at end of file diff --git a/public/userarea/ping_lims_api.php b/public/userarea/ping_lims_api.php new file mode 100644 index 0000000..ab020b0 --- /dev/null +++ b/public/userarea/ping_lims_api.php @@ -0,0 +1,36 @@ +get('Rapporto', [ + '$top' => 1, + '$select' => 'IdRapporto' + ]); + + $elapsed = round(microtime(true) - $start, 3); + + echo json_encode([ + 'success' => true, + 'elapsed_seconds' => $elapsed, + 'data' => $data + ], JSON_PRETTY_PRINT | JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES); +} catch (Exception $e) { + http_response_code(500); + + echo json_encode([ + 'success' => false, + 'error' => $e->getMessage() + ], JSON_PRETTY_PRINT | JSON_UNESCAPED_UNICODE); +} diff --git a/public/userarea/save_mapping_json.php b/public/userarea/save_mapping_json.php index 3139e02..d907c78 100644 --- a/public/userarea/save_mapping_json.php +++ b/public/userarea/save_mapping_json.php @@ -16,9 +16,11 @@ if (!$data || !isset($data['id'])) { exit; } -$mappingId = $data['id']; +$mappingId = (int)$data['id']; $mappingType = $data['mapping_type'] ?? ''; + $excelColumn = $data['excel_column'] ?? null; +$jsonNode = $data['json_node'] ?? null; $manualDefault = $data['manual_default'] ?? null; $autoValue = $data['auto_value'] ?? 'none'; @@ -26,7 +28,7 @@ $tablename = $data['tablename'] ?? ''; try { // Normalize mapping type - $allowedTypes = ['', 'xls', 'manual', 'auto']; + $allowedTypes = ['', 'xls', 'json', 'manual', 'auto']; if (!in_array($mappingType, $allowedTypes, true)) { echo json_encode(["success" => false, "message" => "Invalid mapping_type"]); exit; @@ -41,43 +43,67 @@ try { // Decide what to persist based on mapping_type $isManual = 0; $excelToSave = null; + $jsonNodeToSave = null; $manualToSave = null; $autoToSave = 'none'; if ($mappingType === 'xls') { + $isManual = 0; $excelToSave = $excelColumn ?: null; + $jsonNodeToSave = null; + $manualToSave = null; + $autoToSave = 'none'; + } elseif ($mappingType === 'json') { + + $isManual = 0; + $excelToSave = null; + $jsonNodeToSave = $jsonNode ?: null; $manualToSave = null; $autoToSave = 'none'; } elseif ($mappingType === 'manual') { + $isManual = 1; $excelToSave = null; + $jsonNodeToSave = null; $manualToSave = $manualDefault; $autoToSave = 'none'; } elseif ($mappingType === 'auto') { + $isManual = 0; $excelToSave = null; + $jsonNodeToSave = null; $manualToSave = null; $autoToSave = $autoValue ?: 'none'; } else { - // reset + + // Reset mapping $isManual = 0; $excelToSave = null; + $jsonNodeToSave = null; $manualToSave = null; $autoToSave = 'none'; } $stmt = $pdo->prepare(" - UPDATE template_mapping - SET - is_manual = ?, - excel_column = ?, - manual_default = ?, - auto_value = ? - WHERE id = ? -"); + UPDATE template_mapping + SET + is_manual = ?, + excel_column = ?, + json_node = ?, + manual_default = ?, + auto_value = ? + WHERE id = ? + "); - $result = $stmt->execute([$isManual, $excelToSave, $manualToSave, $autoToSave, $mappingId]); + $result = $stmt->execute([ + $isManual, + $excelToSave, + $jsonNodeToSave, + $manualToSave, + $autoToSave, + $mappingId + ]); if (!$result) { echo json_encode(["success" => false, "message" => "Database update failed"]); @@ -88,15 +114,17 @@ try { "success" => true, "message" => "Mapping updated successfully", "saved" => [ - "id" => (int)$mappingId, + "id" => $mappingId, "mapping_type" => $mappingType, "is_manual" => $isManual, "excel_column" => $excelToSave, + "json_node" => $jsonNodeToSave, "manual_default" => $manualToSave, "auto_value" => $autoToSave ] ]); -} catch (Exception $e) { +} catch (Throwable $e) { echo json_encode(["success" => false, "message" => "Error: " . $e->getMessage()]); } + exit; diff --git a/public/userarea/schema_dettagli_response.json b/public/userarea/schema_dettagli_response.json index ae042f3..436382f 100644 --- a/public/userarea/schema_dettagli_response.json +++ b/public/userarea/schema_dettagli_response.json @@ -1 +1 @@ -{"@odata.context":"https:\/\/bvcpsitaly-elims.com\/limsapi\/api\/odata\/$metadata#SchemaCustomField(SchemiCustomFieldsDettagli(CustomField()))\/$entity","IdSchemaCustomFields":82,"ConteggioClienti":0,"Nome":"MONCLER Brand","Descrizione":"Da utilizzare solo per il Brand Diretto\r\nGR 19\/03\/2024","SchemiCustomFieldsDettagli":[{"IdSchemaCustomFieldsDettaglio":2124,"Ordine":1,"ObbligatorioWeb":"Predefinito","RaggruppamentoWeb":null,"CustomField":{"IdCustomField":1083,"TitoloTraduzione":"MONCLER_Analisi Commissionate da: ","Titolo":"MONCLER_Analisi Commissionate da: ","Descrizione":null,"Tipo":"SceltaMultipla","Decimali":0,"Lunghezza":0,"Minimo":null,"Massimo":null,"ValoreDefault":null,"Elenco":false,"DefaultCurrDate":false,"ObbligatorioWeb":false}},{"IdSchemaCustomFieldsDettaglio":2125,"Ordine":2,"ObbligatorioWeb":"Predefinito","RaggruppamentoWeb":null,"CustomField":{"IdCustomField":214,"TitoloTraduzione":"Season","Titolo":"Season","Descrizione":null,"Tipo":"Testo","Decimali":0,"Lunghezza":0,"Minimo":null,"Massimo":null,"ValoreDefault":null,"Elenco":true,"DefaultCurrDate":false,"ObbligatorioWeb":false}},{"IdSchemaCustomFieldsDettaglio":2126,"Ordine":3,"ObbligatorioWeb":"Predefinito","RaggruppamentoWeb":null,"CustomField":{"IdCustomField":467,"TitoloTraduzione":"Sample Code:","Titolo":"Sample Code:","Descrizione":null,"Tipo":"Testo","Decimali":0,"Lunghezza":0,"Minimo":null,"Massimo":null,"ValoreDefault":null,"Elenco":false,"DefaultCurrDate":false,"ObbligatorioWeb":false}},{"IdSchemaCustomFieldsDettaglio":2127,"Ordine":4,"ObbligatorioWeb":"Predefinito","RaggruppamentoWeb":null,"CustomField":{"IdCustomField":210,"TitoloTraduzione":"Sample Description ","Titolo":"Sample Description ","Descrizione":null,"Tipo":"Testo","Decimali":0,"Lunghezza":0,"Minimo":null,"Massimo":null,"ValoreDefault":null,"Elenco":false,"DefaultCurrDate":false,"ObbligatorioWeb":false}},{"IdSchemaCustomFieldsDettaglio":2128,"Ordine":5,"ObbligatorioWeb":"Predefinito","RaggruppamentoWeb":null,"CustomField":{"IdCustomField":264,"TitoloTraduzione":"Color Code:","Titolo":"Color Code:","Descrizione":null,"Tipo":"Testo","Decimali":0,"Lunghezza":0,"Minimo":null,"Massimo":null,"ValoreDefault":null,"Elenco":false,"DefaultCurrDate":false,"ObbligatorioWeb":false}},{"IdSchemaCustomFieldsDettaglio":2129,"Ordine":6,"ObbligatorioWeb":"Predefinito","RaggruppamentoWeb":null,"CustomField":{"IdCustomField":263,"TitoloTraduzione":"Style Code:","Titolo":"Style Code:","Descrizione":null,"Tipo":"Testo","Decimali":0,"Lunghezza":0,"Minimo":null,"Massimo":null,"ValoreDefault":null,"Elenco":false,"DefaultCurrDate":false,"ObbligatorioWeb":false}},{"IdSchemaCustomFieldsDettaglio":2130,"Ordine":9,"ObbligatorioWeb":"Predefinito","RaggruppamentoWeb":null,"CustomField":{"IdCustomField":468,"TitoloTraduzione":"Style Description:","Titolo":"Style Description:","Descrizione":null,"Tipo":"Testo","Decimali":0,"Lunghezza":0,"Minimo":null,"Massimo":null,"ValoreDefault":null,"Elenco":false,"DefaultCurrDate":false,"ObbligatorioWeb":false}},{"IdSchemaCustomFieldsDettaglio":5787,"Ordine":12,"ObbligatorioWeb":"Predefinito","RaggruppamentoWeb":null,"CustomField":{"IdCustomField":1014,"TitoloTraduzione":"CDC","Titolo":"CDC","Descrizione":null,"Tipo":"Testo","Decimali":0,"Lunghezza":0,"Minimo":null,"Massimo":null,"ValoreDefault":null,"Elenco":false,"DefaultCurrDate":false,"ObbligatorioWeb":false}},{"IdSchemaCustomFieldsDettaglio":4259,"Ordine":13,"ObbligatorioWeb":"Predefinito","RaggruppamentoWeb":null,"CustomField":{"IdCustomField":207,"TitoloTraduzione":"Composition Claimed","Titolo":"Composition Claimed","Descrizione":null,"Tipo":"Testo","Decimali":0,"Lunghezza":0,"Minimo":null,"Massimo":null,"ValoreDefault":null,"Elenco":false,"DefaultCurrDate":false,"ObbligatorioWeb":false}},{"IdSchemaCustomFieldsDettaglio":6544,"Ordine":14,"ObbligatorioWeb":"Predefinito","RaggruppamentoWeb":null,"CustomField":{"IdCustomField":543,"TitoloTraduzione":"Collezione:","Titolo":"Collezione:","Descrizione":null,"Tipo":"Testo","Decimali":0,"Lunghezza":0,"Minimo":null,"Massimo":null,"ValoreDefault":null,"Elenco":false,"DefaultCurrDate":false,"ObbligatorioWeb":false}},{"IdSchemaCustomFieldsDettaglio":2132,"Ordine":15,"ObbligatorioWeb":"Predefinito","RaggruppamentoWeb":null,"CustomField":{"IdCustomField":167,"TitoloTraduzione":"Capitolato di riferimento","Titolo":"Capitolato di riferimento","Descrizione":null,"Tipo":"SceltaMultipla","Decimali":0,"Lunghezza":0,"Minimo":null,"Massimo":null,"ValoreDefault":null,"Elenco":false,"DefaultCurrDate":false,"ObbligatorioWeb":false}},{"IdSchemaCustomFieldsDettaglio":2133,"Ordine":16,"ObbligatorioWeb":"Predefinito","RaggruppamentoWeb":null,"CustomField":{"IdCustomField":161,"TitoloTraduzione":"Categoria:","Titolo":"Categoria:","Descrizione":null,"Tipo":"Testo","Decimali":0,"Lunghezza":0,"Minimo":null,"Massimo":null,"ValoreDefault":null,"Elenco":true,"DefaultCurrDate":false,"ObbligatorioWeb":false}},{"IdSchemaCustomFieldsDettaglio":2134,"Ordine":17,"ObbligatorioWeb":"Predefinito","RaggruppamentoWeb":null,"CustomField":{"IdCustomField":150,"TitoloTraduzione":"Destinazione d'uso: ","Titolo":"Destinazione d'uso: ","Descrizione":null,"Tipo":"SceltaMultipla","Decimali":0,"Lunghezza":0,"Minimo":null,"Massimo":null,"ValoreDefault":null,"Elenco":true,"DefaultCurrDate":false,"ObbligatorioWeb":false}},{"IdSchemaCustomFieldsDettaglio":2135,"Ordine":18,"ObbligatorioWeb":"Predefinito","RaggruppamentoWeb":null,"CustomField":{"IdCustomField":156,"TitoloTraduzione":"Tipologia di Materiale:","Titolo":"Tipologia di Materiale:","Descrizione":null,"Tipo":"SceltaMultipla","Decimali":0,"Lunghezza":0,"Minimo":null,"Massimo":null,"ValoreDefault":null,"Elenco":false,"DefaultCurrDate":false,"ObbligatorioWeb":false}},{"IdSchemaCustomFieldsDettaglio":2136,"Ordine":19,"ObbligatorioWeb":"Predefinito","RaggruppamentoWeb":null,"CustomField":{"IdCustomField":146,"TitoloTraduzione":"Tipologia di Trattamento Superficiale:","Titolo":"Tipologia di Trattamento Superficiale:","Descrizione":null,"Tipo":"SceltaMultipla","Decimali":0,"Lunghezza":0,"Minimo":null,"Massimo":null,"ValoreDefault":null,"Elenco":false,"DefaultCurrDate":false,"ObbligatorioWeb":false}},{"IdSchemaCustomFieldsDettaglio":2137,"Ordine":20,"ObbligatorioWeb":"Predefinito","RaggruppamentoWeb":null,"CustomField":{"IdCustomField":147,"TitoloTraduzione":"Tipologia di concia: ","Titolo":"Tipologia di concia: ","Descrizione":null,"Tipo":"SceltaMultipla","Decimali":0,"Lunghezza":0,"Minimo":null,"Massimo":null,"ValoreDefault":null,"Elenco":false,"DefaultCurrDate":false,"ObbligatorioWeb":false}},{"IdSchemaCustomFieldsDettaglio":2155,"Ordine":21,"ObbligatorioWeb":"Predefinito","RaggruppamentoWeb":null,"CustomField":{"IdCustomField":142,"TitoloTraduzione":"Fornitore:","Titolo":"Fornitore:","Descrizione":null,"Tipo":"Testo","Decimali":0,"Lunghezza":0,"Minimo":null,"Massimo":null,"ValoreDefault":null,"Elenco":true,"DefaultCurrDate":false,"ObbligatorioWeb":false}},{"IdSchemaCustomFieldsDettaglio":2156,"Ordine":22,"ObbligatorioWeb":"Predefinito","RaggruppamentoWeb":null,"CustomField":{"IdCustomField":194,"TitoloTraduzione":"Fabbricante:","Titolo":"Fabbricante:","Descrizione":null,"Tipo":"Testo","Decimali":0,"Lunghezza":0,"Minimo":null,"Massimo":null,"ValoreDefault":null,"Elenco":true,"DefaultCurrDate":false,"ObbligatorioWeb":false}},{"IdSchemaCustomFieldsDettaglio":4022,"Ordine":23,"ObbligatorioWeb":"Predefinito","RaggruppamentoWeb":null,"CustomField":{"IdCustomField":595,"TitoloTraduzione":"Final Customer: ","Titolo":"Final Customer: ","Descrizione":null,"Tipo":"SceltaMultipla","Decimali":0,"Lunghezza":0,"Minimo":null,"Massimo":null,"ValoreDefault":"MONCLER","Elenco":false,"DefaultCurrDate":false,"ObbligatorioWeb":false}},{"IdSchemaCustomFieldsDettaglio":4021,"Ordine":24,"ObbligatorioWeb":"Predefinito","RaggruppamentoWeb":null,"CustomField":{"IdCustomField":748,"TitoloTraduzione":"Analisi Richieste:","Titolo":"Analisi Richieste:","Descrizione":null,"Tipo":"Testo","Decimali":0,"Lunghezza":0,"Minimo":null,"Massimo":null,"ValoreDefault":null,"Elenco":false,"DefaultCurrDate":false,"ObbligatorioWeb":false}},{"IdSchemaCustomFieldsDettaglio":2473,"Ordine":25,"ObbligatorioWeb":"Predefinito","RaggruppamentoWeb":null,"CustomField":{"IdCustomField":189,"TitoloTraduzione":"Tested Component:","Titolo":"Tested Component:","Descrizione":null,"Tipo":"Testo","Decimali":0,"Lunghezza":0,"Minimo":null,"Massimo":null,"ValoreDefault":null,"Elenco":false,"DefaultCurrDate":false,"ObbligatorioWeb":false}},{"IdSchemaCustomFieldsDettaglio":2176,"Ordine":26,"ObbligatorioWeb":"Predefinito","RaggruppamentoWeb":null,"CustomField":{"IdCustomField":166,"TitoloTraduzione":"Note:","Titolo":"Note:","Descrizione":null,"Tipo":"Testo","Decimali":0,"Lunghezza":0,"Minimo":null,"Massimo":null,"ValoreDefault":null,"Elenco":false,"DefaultCurrDate":false,"ObbligatorioWeb":false}},{"IdSchemaCustomFieldsDettaglio":2503,"Ordine":27,"ObbligatorioWeb":"Predefinito","RaggruppamentoWeb":null,"CustomField":{"IdCustomField":555,"TitoloTraduzione":"Additional Info:","Titolo":"Additional Info:","Descrizione":null,"Tipo":"Testo","Decimali":0,"Lunghezza":0,"Minimo":null,"Massimo":null,"ValoreDefault":null,"Elenco":false,"DefaultCurrDate":false,"ObbligatorioWeb":false}},{"IdSchemaCustomFieldsDettaglio":2138,"Ordine":28,"ObbligatorioWeb":"Predefinito","RaggruppamentoWeb":null,"CustomField":{"IdCustomField":362,"TitoloTraduzione":"Tracciante materiale","Titolo":"Tracciante materiale","Descrizione":null,"Tipo":"SceltaMultipla","Decimali":0,"Lunghezza":0,"Minimo":null,"Massimo":null,"ValoreDefault":null,"Elenco":false,"DefaultCurrDate":false,"ObbligatorioWeb":false}},{"IdSchemaCustomFieldsDettaglio":2146,"Ordine":29,"ObbligatorioWeb":"Predefinito","RaggruppamentoWeb":null,"CustomField":{"IdCustomField":193,"TitoloTraduzione":"DDT N. ","Titolo":"DDT N. ","Descrizione":null,"Tipo":"Testo","Decimali":0,"Lunghezza":0,"Minimo":null,"Massimo":null,"ValoreDefault":null,"Elenco":true,"DefaultCurrDate":false,"ObbligatorioWeb":false}},{"IdSchemaCustomFieldsDettaglio":2145,"Ordine":30,"ObbligatorioWeb":"Predefinito","RaggruppamentoWeb":null,"CustomField":{"IdCustomField":370,"TitoloTraduzione":"DDT del","Titolo":"DDT del","Descrizione":null,"Tipo":"Data","Decimali":0,"Lunghezza":0,"Minimo":null,"Massimo":null,"ValoreDefault":null,"Elenco":true,"DefaultCurrDate":false,"ObbligatorioWeb":false}},{"IdSchemaCustomFieldsDettaglio":2480,"Ordine":31,"ObbligatorioWeb":"Predefinito","RaggruppamentoWeb":null,"CustomField":{"IdCustomField":371,"TitoloTraduzione":"Mittente (se diverso da Committente)","Titolo":"Mittente (se diverso da Committente)","Descrizione":null,"Tipo":"Testo","Decimali":0,"Lunghezza":0,"Minimo":null,"Massimo":null,"ValoreDefault":null,"Elenco":true,"DefaultCurrDate":false,"ObbligatorioWeb":false}},{"IdSchemaCustomFieldsDettaglio":2476,"Ordine":32,"ObbligatorioWeb":"Predefinito","RaggruppamentoWeb":null,"CustomField":{"IdCustomField":372,"TitoloTraduzione":"Quantit\u00e0","Titolo":"Quantit\u00e0","Descrizione":null,"Tipo":"Testo","Decimali":0,"Lunghezza":0,"Minimo":null,"Massimo":null,"ValoreDefault":null,"Elenco":false,"DefaultCurrDate":false,"ObbligatorioWeb":false}},{"IdSchemaCustomFieldsDettaglio":2149,"Ordine":33,"ObbligatorioWeb":"Predefinito","RaggruppamentoWeb":null,"CustomField":{"IdCustomField":259,"TitoloTraduzione":"Sample Arrival Date:","Titolo":"Sample Arrival Date:","Descrizione":null,"Tipo":"Data","Decimali":0,"Lunghezza":0,"Minimo":null,"Massimo":null,"ValoreDefault":null,"Elenco":true,"DefaultCurrDate":false,"ObbligatorioWeb":false}},{"IdSchemaCustomFieldsDettaglio":2150,"Ordine":34,"ObbligatorioWeb":"Predefinito","RaggruppamentoWeb":null,"CustomField":{"IdCustomField":348,"TitoloTraduzione":"Sample Arrival Time:","Titolo":"Sample Arrival Time:","Descrizione":null,"Tipo":"Testo","Decimali":0,"Lunghezza":0,"Minimo":null,"Massimo":null,"ValoreDefault":null,"Elenco":true,"DefaultCurrDate":false,"ObbligatorioWeb":false}},{"IdSchemaCustomFieldsDettaglio":2151,"Ordine":35,"ObbligatorioWeb":"Predefinito","RaggruppamentoWeb":null,"CustomField":{"IdCustomField":260,"TitoloTraduzione":"Sample Unlock Date:","Titolo":"Sample Unlock Date:","Descrizione":null,"Tipo":"Data","Decimali":0,"Lunghezza":0,"Minimo":null,"Massimo":null,"ValoreDefault":null,"Elenco":true,"DefaultCurrDate":false,"ObbligatorioWeb":false}},{"IdSchemaCustomFieldsDettaglio":2152,"Ordine":36,"ObbligatorioWeb":"Predefinito","RaggruppamentoWeb":null,"CustomField":{"IdCustomField":349,"TitoloTraduzione":"Sample Unlock Time:","Titolo":"Sample Unlock Time:","Descrizione":null,"Tipo":"Testo","Decimali":0,"Lunghezza":0,"Minimo":null,"Massimo":null,"ValoreDefault":null,"Elenco":true,"DefaultCurrDate":false,"ObbligatorioWeb":false}},{"IdSchemaCustomFieldsDettaglio":2153,"Ordine":37,"ObbligatorioWeb":"Predefinito","RaggruppamentoWeb":null,"CustomField":{"IdCustomField":361,"TitoloTraduzione":"Presa in carico, Data:","Titolo":"Presa in carico, Data:","Descrizione":null,"Tipo":"Data","Decimali":0,"Lunghezza":0,"Minimo":null,"Massimo":null,"ValoreDefault":null,"Elenco":true,"DefaultCurrDate":false,"ObbligatorioWeb":false}},{"IdSchemaCustomFieldsDettaglio":2154,"Ordine":38,"ObbligatorioWeb":"Predefinito","RaggruppamentoWeb":null,"CustomField":{"IdCustomField":360,"TitoloTraduzione":"Presa in carico, Orario:","Titolo":"Presa in carico, Orario:","Descrizione":null,"Tipo":"Testo","Decimali":0,"Lunghezza":0,"Minimo":null,"Massimo":null,"ValoreDefault":null,"Elenco":true,"DefaultCurrDate":false,"ObbligatorioWeb":false}},{"IdSchemaCustomFieldsDettaglio":2157,"Ordine":39,"ObbligatorioWeb":"Predefinito","RaggruppamentoWeb":null,"CustomField":{"IdCustomField":163,"TitoloTraduzione":"Campionamento:","Titolo":"Campionamento:","Descrizione":null,"Tipo":"SceltaMultipla","Decimali":0,"Lunghezza":0,"Minimo":null,"Massimo":null,"ValoreDefault":null,"Elenco":true,"DefaultCurrDate":false,"ObbligatorioWeb":false}},{"IdSchemaCustomFieldsDettaglio":2158,"Ordine":40,"ObbligatorioWeb":"Predefinito","RaggruppamentoWeb":null,"CustomField":{"IdCustomField":165,"TitoloTraduzione":"Condizionamento prima e durante il Test: ","Titolo":"Condizionamento prima e durante il Test: ","Descrizione":null,"Tipo":"SceltaMultipla","Decimali":0,"Lunghezza":0,"Minimo":null,"Massimo":null,"ValoreDefault":null,"Elenco":true,"DefaultCurrDate":false,"ObbligatorioWeb":false}},{"IdSchemaCustomFieldsDettaglio":2159,"Ordine":41,"ObbligatorioWeb":"Predefinito","RaggruppamentoWeb":null,"CustomField":{"IdCustomField":181,"TitoloTraduzione":"Preparazione del campione ai test chimici: ","Titolo":"Preparazione del campione ai test chimici: ","Descrizione":null,"Tipo":"SceltaMultipla","Decimali":0,"Lunghezza":0,"Minimo":null,"Massimo":null,"ValoreDefault":null,"Elenco":false,"DefaultCurrDate":false,"ObbligatorioWeb":false}},{"IdSchemaCustomFieldsDettaglio":2161,"Ordine":42,"ObbligatorioWeb":"Predefinito","RaggruppamentoWeb":null,"CustomField":{"IdCustomField":169,"TitoloTraduzione":"(*Restituzione Materiale Residuo:","Titolo":"(*Restituzione Materiale Residuo:","Descrizione":null,"Tipo":"SceltaMultipla","Decimali":0,"Lunghezza":0,"Minimo":null,"Massimo":null,"ValoreDefault":"","Elenco":false,"DefaultCurrDate":false,"ObbligatorioWeb":false}},{"IdSchemaCustomFieldsDettaglio":2163,"Ordine":43,"ObbligatorioWeb":"Predefinito","RaggruppamentoWeb":null,"CustomField":{"IdCustomField":170,"TitoloTraduzione":"(*Originali:","Titolo":"(*Originali:","Descrizione":null,"Tipo":"SceltaMultipla","Decimali":0,"Lunghezza":0,"Minimo":null,"Massimo":null,"ValoreDefault":"","Elenco":false,"DefaultCurrDate":false,"ObbligatorioWeb":false}},{"IdSchemaCustomFieldsDettaglio":2164,"Ordine":44,"ObbligatorioWeb":"Predefinito","RaggruppamentoWeb":null,"CustomField":{"IdCustomField":171,"TitoloTraduzione":"(*Corriere:","Titolo":"(*Corriere:","Descrizione":null,"Tipo":"SceltaMultipla","Decimali":0,"Lunghezza":0,"Minimo":null,"Massimo":null,"ValoreDefault":"","Elenco":false,"DefaultCurrDate":false,"ObbligatorioWeb":false}},{"IdSchemaCustomFieldsDettaglio":2165,"Ordine":45,"ObbligatorioWeb":"Predefinito","RaggruppamentoWeb":null,"CustomField":{"IdCustomField":241,"TitoloTraduzione":"N. di Prove:","Titolo":"N. di Prove:","Descrizione":null,"Tipo":"SceltaMultipla","Decimali":0,"Lunghezza":0,"Minimo":null,"Massimo":null,"ValoreDefault":null,"Elenco":false,"DefaultCurrDate":false,"ObbligatorioWeb":false}},{"IdSchemaCustomFieldsDettaglio":2166,"Ordine":46,"ObbligatorioWeb":"Predefinito","RaggruppamentoWeb":null,"CustomField":{"IdCustomField":244,"TitoloTraduzione":"Accettatore:","Titolo":"Accettatore:","Descrizione":null,"Tipo":"SceltaMultipla","Decimali":0,"Lunghezza":0,"Minimo":null,"Massimo":null,"ValoreDefault":null,"Elenco":true,"DefaultCurrDate":false,"ObbligatorioWeb":false}},{"IdSchemaCustomFieldsDettaglio":2167,"Ordine":47,"ObbligatorioWeb":"Predefinito","RaggruppamentoWeb":null,"CustomField":{"IdCustomField":258,"TitoloTraduzione":"Login Date:","Titolo":"Login Date:","Descrizione":null,"Tipo":"Data","Decimali":0,"Lunghezza":0,"Minimo":null,"Massimo":null,"ValoreDefault":null,"Elenco":true,"DefaultCurrDate":false,"ObbligatorioWeb":false}},{"IdSchemaCustomFieldsDettaglio":2168,"Ordine":48,"ObbligatorioWeb":"Predefinito","RaggruppamentoWeb":null,"CustomField":{"IdCustomField":350,"TitoloTraduzione":"Login Time:","Titolo":"Login Time:","Descrizione":null,"Tipo":"Testo","Decimali":0,"Lunghezza":0,"Minimo":null,"Massimo":null,"ValoreDefault":null,"Elenco":false,"DefaultCurrDate":false,"ObbligatorioWeb":false}},{"IdSchemaCustomFieldsDettaglio":2169,"Ordine":49,"ObbligatorioWeb":"Predefinito","RaggruppamentoWeb":null,"CustomField":{"IdCustomField":236,"TitoloTraduzione":"Consegna campione:","Titolo":"Consegna campione:","Descrizione":null,"Tipo":"SceltaMultipla","Decimali":0,"Lunghezza":0,"Minimo":null,"Massimo":null,"ValoreDefault":null,"Elenco":true,"DefaultCurrDate":false,"ObbligatorioWeb":false}},{"IdSchemaCustomFieldsDettaglio":2170,"Ordine":50,"ObbligatorioWeb":"Predefinito","RaggruppamentoWeb":null,"CustomField":{"IdCustomField":246,"TitoloTraduzione":"Lab outsourcing:","Titolo":"Lab outsourcing:","Descrizione":null,"Tipo":"SceltaMultipla","Decimali":0,"Lunghezza":0,"Minimo":null,"Massimo":null,"ValoreDefault":null,"Elenco":false,"DefaultCurrDate":false,"ObbligatorioWeb":false}},{"IdSchemaCustomFieldsDettaglio":2171,"Ordine":51,"ObbligatorioWeb":"Predefinito","RaggruppamentoWeb":null,"CustomField":{"IdCustomField":254,"TitoloTraduzione":"AWB: ","Titolo":"AWB: ","Descrizione":null,"Tipo":"Testo","Decimali":0,"Lunghezza":0,"Minimo":null,"Massimo":null,"ValoreDefault":null,"Elenco":false,"DefaultCurrDate":false,"ObbligatorioWeb":false}},{"IdSchemaCustomFieldsDettaglio":2172,"Ordine":52,"ObbligatorioWeb":"Predefinito","RaggruppamentoWeb":null,"CustomField":{"IdCustomField":255,"TitoloTraduzione":"Data ricezione risultati test:","Titolo":"Data ricezione risultati test:","Descrizione":null,"Tipo":"Data","Decimali":0,"Lunghezza":0,"Minimo":null,"Massimo":null,"ValoreDefault":null,"Elenco":false,"DefaultCurrDate":false,"ObbligatorioWeb":false}},{"IdSchemaCustomFieldsDettaglio":2173,"Ordine":53,"ObbligatorioWeb":"Predefinito","RaggruppamentoWeb":null,"CustomField":{"IdCustomField":253,"TitoloTraduzione":"Sample return:","Titolo":"Sample return:","Descrizione":null,"Tipo":"SceltaMultipla","Decimali":0,"Lunghezza":0,"Minimo":null,"Massimo":null,"ValoreDefault":null,"Elenco":true,"DefaultCurrDate":false,"ObbligatorioWeb":false}},{"IdSchemaCustomFieldsDettaglio":2174,"Ordine":54,"ObbligatorioWeb":"Predefinito","RaggruppamentoWeb":null,"CustomField":{"IdCustomField":252,"TitoloTraduzione":"Service required:","Titolo":"Service required:","Descrizione":null,"Tipo":"SceltaMultipla","Decimali":0,"Lunghezza":0,"Minimo":null,"Massimo":null,"ValoreDefault":null,"Elenco":true,"DefaultCurrDate":false,"ObbligatorioWeb":false}},{"IdSchemaCustomFieldsDettaglio":2175,"Ordine":55,"ObbligatorioWeb":"Predefinito","RaggruppamentoWeb":null,"CustomField":{"IdCustomField":261,"TitoloTraduzione":"Tipologia report (solo per Cina)","Titolo":"Tipologia report (solo per Cina)","Descrizione":null,"Tipo":"SceltaMultipla","Decimali":0,"Lunghezza":0,"Minimo":null,"Massimo":null,"ValoreDefault":null,"Elenco":false,"DefaultCurrDate":false,"ObbligatorioWeb":false}},{"IdSchemaCustomFieldsDettaglio":4150,"Ordine":56,"ObbligatorioWeb":"Predefinito","RaggruppamentoWeb":null,"CustomField":{"IdCustomField":772,"TitoloTraduzione":"RATING CHINA MONCLER","Titolo":"RATING CHINA MONCLER","Descrizione":null,"Tipo":"SceltaMultipla","Decimali":0,"Lunghezza":0,"Minimo":null,"Massimo":null,"ValoreDefault":null,"Elenco":false,"DefaultCurrDate":false,"ObbligatorioWeb":false}},{"IdSchemaCustomFieldsDettaglio":4151,"Ordine":57,"ObbligatorioWeb":"Predefinito","RaggruppamentoWeb":null,"CustomField":{"IdCustomField":773,"TitoloTraduzione":"RATING MANUAL MONCLER","Titolo":"RATING MANUAL MONCLER","Descrizione":null,"Tipo":"SceltaMultipla","Decimali":0,"Lunghezza":0,"Minimo":null,"Massimo":null,"ValoreDefault":null,"Elenco":false,"DefaultCurrDate":false,"ObbligatorioWeb":false}},{"IdSchemaCustomFieldsDettaglio":4149,"Ordine":58,"ObbligatorioWeb":"Predefinito","RaggruppamentoWeb":null,"CustomField":{"IdCustomField":771,"TitoloTraduzione":"RATING RSL MONCLER","Titolo":"RATING RSL MONCLER","Descrizione":null,"Tipo":"SceltaMultipla","Decimali":0,"Lunghezza":0,"Minimo":null,"Massimo":null,"ValoreDefault":null,"Elenco":false,"DefaultCurrDate":false,"ObbligatorioWeb":false}}]} \ No newline at end of file +{"@odata.context":"https:\/\/bvcpsitaly-elims.com\/limsapi\/api\/odata\/$metadata#SchemaCustomField(SchemiCustomFieldsDettagli(CustomField()))\/$entity","IdSchemaCustomFields":49,"ConteggioClienti":0,"Nome":"Accessori Metallici \/ Metallic Accessories","Descrizione":"Schema per tutti gli Accessori Metallici\r\n\r\n\r\n","SchemiCustomFieldsDettagli":[{"IdSchemaCustomFieldsDettaglio":576,"Ordine":1,"ObbligatorioWeb":"Predefinito","RaggruppamentoWeb":null,"CustomField":{"IdCustomField":183,"TitoloTraduzione":"Analisi Commissionate da: ","Titolo":"Analisi Commissionate da: ","Descrizione":null,"Tipo":"Testo","Decimali":0,"Lunghezza":0,"Minimo":null,"Massimo":null,"ValoreDefault":null,"Elenco":false,"DefaultCurrDate":false,"ObbligatorioWeb":true}},{"IdSchemaCustomFieldsDettaglio":577,"Ordine":2,"ObbligatorioWeb":"Predefinito","RaggruppamentoWeb":null,"CustomField":{"IdCustomField":184,"TitoloTraduzione":"Ordine d'Acquisto: ","Titolo":"Ordine d'Acquisto: ","Descrizione":null,"Tipo":"Testo","Decimali":0,"Lunghezza":0,"Minimo":null,"Massimo":null,"ValoreDefault":null,"Elenco":true,"DefaultCurrDate":false,"ObbligatorioWeb":false}},{"IdSchemaCustomFieldsDettaglio":495,"Ordine":3,"ObbligatorioWeb":"Predefinito","RaggruppamentoWeb":null,"CustomField":{"IdCustomField":148,"TitoloTraduzione":"Articolo: ","Titolo":"Articolo: ","Descrizione":null,"Tipo":"Testo","Decimali":0,"Lunghezza":0,"Minimo":null,"Massimo":null,"ValoreDefault":null,"Elenco":false,"DefaultCurrDate":false,"ObbligatorioWeb":true}},{"IdSchemaCustomFieldsDettaglio":510,"Ordine":4,"ObbligatorioWeb":"Predefinito","RaggruppamentoWeb":null,"CustomField":{"IdCustomField":172,"TitoloTraduzione":"Finitura:","Titolo":"Finitura:","Descrizione":null,"Tipo":"Testo","Decimali":0,"Lunghezza":0,"Minimo":null,"Massimo":null,"ValoreDefault":null,"Elenco":true,"DefaultCurrDate":false,"ObbligatorioWeb":false}},{"IdSchemaCustomFieldsDettaglio":8863,"Ordine":5,"ObbligatorioWeb":"Predefinito","RaggruppamentoWeb":null,"CustomField":{"IdCustomField":1405,"TitoloTraduzione":"Composizione galvanica","Titolo":"Composizione galvanica","Descrizione":null,"Tipo":"Testo","Decimali":0,"Lunghezza":0,"Minimo":null,"Massimo":null,"ValoreDefault":null,"Elenco":false,"DefaultCurrDate":false,"ObbligatorioWeb":false}},{"IdSchemaCustomFieldsDettaglio":496,"Ordine":6,"ObbligatorioWeb":"Predefinito","RaggruppamentoWeb":null,"CustomField":{"IdCustomField":149,"TitoloTraduzione":"Colore: ","Titolo":"Colore: ","Descrizione":null,"Tipo":"Testo","Decimali":0,"Lunghezza":0,"Minimo":null,"Massimo":null,"ValoreDefault":null,"Elenco":false,"DefaultCurrDate":false,"ObbligatorioWeb":false}},{"IdSchemaCustomFieldsDettaglio":497,"Ordine":7,"ObbligatorioWeb":"Predefinito","RaggruppamentoWeb":null,"CustomField":{"IdCustomField":150,"TitoloTraduzione":"Destinazione d'uso: ","Titolo":"Destinazione d'uso: ","Descrizione":null,"Tipo":"SceltaMultipla","Decimali":0,"Lunghezza":0,"Minimo":null,"Massimo":null,"ValoreDefault":null,"Elenco":true,"DefaultCurrDate":false,"ObbligatorioWeb":false}},{"IdSchemaCustomFieldsDettaglio":505,"Ordine":8,"ObbligatorioWeb":"Predefinito","RaggruppamentoWeb":null,"CustomField":{"IdCustomField":156,"TitoloTraduzione":"Tipologia di Materiale:","Titolo":"Tipologia di Materiale:","Descrizione":null,"Tipo":"SceltaMultipla","Decimali":0,"Lunghezza":0,"Minimo":null,"Massimo":null,"ValoreDefault":null,"Elenco":false,"DefaultCurrDate":false,"ObbligatorioWeb":false}},{"IdSchemaCustomFieldsDettaglio":1567,"Ordine":9,"ObbligatorioWeb":"Predefinito","RaggruppamentoWeb":null,"CustomField":{"IdCustomField":362,"TitoloTraduzione":"Tracciante materiale","Titolo":"Tracciante materiale","Descrizione":null,"Tipo":"SceltaMultipla","Decimali":0,"Lunghezza":0,"Minimo":null,"Massimo":null,"ValoreDefault":null,"Elenco":false,"DefaultCurrDate":false,"ObbligatorioWeb":false}},{"IdSchemaCustomFieldsDettaglio":504,"Ordine":10,"ObbligatorioWeb":"Predefinito","RaggruppamentoWeb":null,"CustomField":{"IdCustomField":161,"TitoloTraduzione":"Categoria:","Titolo":"Categoria:","Descrizione":null,"Tipo":"Testo","Decimali":0,"Lunghezza":0,"Minimo":null,"Massimo":null,"ValoreDefault":null,"Elenco":true,"DefaultCurrDate":false,"ObbligatorioWeb":false}},{"IdSchemaCustomFieldsDettaglio":512,"Ordine":11,"ObbligatorioWeb":"Predefinito","RaggruppamentoWeb":null,"CustomField":{"IdCustomField":162,"TitoloTraduzione":"Stagione:","Titolo":"Stagione:","Descrizione":null,"Tipo":"Testo","Decimali":0,"Lunghezza":0,"Minimo":null,"Massimo":null,"ValoreDefault":null,"Elenco":false,"DefaultCurrDate":false,"ObbligatorioWeb":false}},{"IdSchemaCustomFieldsDettaglio":500,"Ordine":12,"ObbligatorioWeb":"Predefinito","RaggruppamentoWeb":null,"CustomField":{"IdCustomField":166,"TitoloTraduzione":"Note:","Titolo":"Note:","Descrizione":null,"Tipo":"Testo","Decimali":0,"Lunghezza":0,"Minimo":null,"Massimo":null,"ValoreDefault":null,"Elenco":false,"DefaultCurrDate":false,"ObbligatorioWeb":false}},{"IdSchemaCustomFieldsDettaglio":630,"Ordine":13,"ObbligatorioWeb":"Predefinito","RaggruppamentoWeb":null,"CustomField":{"IdCustomField":193,"TitoloTraduzione":"DDT N. ","Titolo":"DDT N. ","Descrizione":null,"Tipo":"Testo","Decimali":0,"Lunghezza":0,"Minimo":null,"Massimo":null,"ValoreDefault":null,"Elenco":true,"DefaultCurrDate":false,"ObbligatorioWeb":false}},{"IdSchemaCustomFieldsDettaglio":1594,"Ordine":14,"ObbligatorioWeb":"Predefinito","RaggruppamentoWeb":null,"CustomField":{"IdCustomField":370,"TitoloTraduzione":"DDT del","Titolo":"DDT del","Descrizione":null,"Tipo":"Data","Decimali":0,"Lunghezza":0,"Minimo":null,"Massimo":null,"ValoreDefault":null,"Elenco":true,"DefaultCurrDate":false,"ObbligatorioWeb":false}},{"IdSchemaCustomFieldsDettaglio":1595,"Ordine":15,"ObbligatorioWeb":"Predefinito","RaggruppamentoWeb":null,"CustomField":{"IdCustomField":371,"TitoloTraduzione":"Mittente (se diverso da Committente)","Titolo":"Mittente (se diverso da Committente)","Descrizione":null,"Tipo":"Testo","Decimali":0,"Lunghezza":0,"Minimo":null,"Massimo":null,"ValoreDefault":null,"Elenco":true,"DefaultCurrDate":false,"ObbligatorioWeb":false}},{"IdSchemaCustomFieldsDettaglio":1596,"Ordine":16,"ObbligatorioWeb":"Predefinito","RaggruppamentoWeb":null,"CustomField":{"IdCustomField":372,"TitoloTraduzione":"Quantit\u00e0","Titolo":"Quantit\u00e0","Descrizione":null,"Tipo":"Testo","Decimali":0,"Lunghezza":0,"Minimo":null,"Massimo":null,"ValoreDefault":null,"Elenco":false,"DefaultCurrDate":false,"ObbligatorioWeb":false}},{"IdSchemaCustomFieldsDettaglio":5640,"Ordine":17,"ObbligatorioWeb":"Predefinito","RaggruppamentoWeb":null,"CustomField":{"IdCustomField":189,"TitoloTraduzione":"Tested Component:","Titolo":"Tested Component:","Descrizione":null,"Tipo":"Testo","Decimali":0,"Lunghezza":0,"Minimo":null,"Massimo":null,"ValoreDefault":null,"Elenco":false,"DefaultCurrDate":false,"ObbligatorioWeb":false}},{"IdSchemaCustomFieldsDettaglio":498,"Ordine":18,"ObbligatorioWeb":"Predefinito","RaggruppamentoWeb":null,"CustomField":{"IdCustomField":142,"TitoloTraduzione":"Fornitore:","Titolo":"Fornitore:","Descrizione":null,"Tipo":"Testo","Decimali":0,"Lunghezza":0,"Minimo":null,"Massimo":null,"ValoreDefault":null,"Elenco":true,"DefaultCurrDate":false,"ObbligatorioWeb":false}},{"IdSchemaCustomFieldsDettaglio":499,"Ordine":19,"ObbligatorioWeb":"Predefinito","RaggruppamentoWeb":null,"CustomField":{"IdCustomField":143,"TitoloTraduzione":"Destinatario:","Titolo":"Destinatario:","Descrizione":null,"Tipo":"Testo","Decimali":0,"Lunghezza":0,"Minimo":null,"Massimo":null,"ValoreDefault":null,"Elenco":false,"DefaultCurrDate":false,"ObbligatorioWeb":false}},{"IdSchemaCustomFieldsDettaglio":506,"Ordine":20,"ObbligatorioWeb":"Predefinito","RaggruppamentoWeb":null,"CustomField":{"IdCustomField":167,"TitoloTraduzione":"Capitolato di riferimento","Titolo":"Capitolato di riferimento","Descrizione":null,"Tipo":"SceltaMultipla","Decimali":0,"Lunghezza":0,"Minimo":null,"Massimo":null,"ValoreDefault":null,"Elenco":false,"DefaultCurrDate":false,"ObbligatorioWeb":false}},{"IdSchemaCustomFieldsDettaglio":962,"Ordine":21,"ObbligatorioWeb":"Predefinito","RaggruppamentoWeb":null,"CustomField":{"IdCustomField":259,"TitoloTraduzione":"Sample Arrival Date:","Titolo":"Sample Arrival Date:","Descrizione":null,"Tipo":"Data","Decimali":0,"Lunghezza":0,"Minimo":null,"Massimo":null,"ValoreDefault":null,"Elenco":true,"DefaultCurrDate":false,"ObbligatorioWeb":false}},{"IdSchemaCustomFieldsDettaglio":1429,"Ordine":22,"ObbligatorioWeb":"Predefinito","RaggruppamentoWeb":null,"CustomField":{"IdCustomField":348,"TitoloTraduzione":"Sample Arrival Time:","Titolo":"Sample Arrival Time:","Descrizione":null,"Tipo":"Testo","Decimali":0,"Lunghezza":0,"Minimo":null,"Massimo":null,"ValoreDefault":null,"Elenco":true,"DefaultCurrDate":false,"ObbligatorioWeb":false}},{"IdSchemaCustomFieldsDettaglio":963,"Ordine":23,"ObbligatorioWeb":"Predefinito","RaggruppamentoWeb":null,"CustomField":{"IdCustomField":260,"TitoloTraduzione":"Sample Unlock Date:","Titolo":"Sample Unlock Date:","Descrizione":null,"Tipo":"Data","Decimali":0,"Lunghezza":0,"Minimo":null,"Massimo":null,"ValoreDefault":null,"Elenco":true,"DefaultCurrDate":false,"ObbligatorioWeb":false}},{"IdSchemaCustomFieldsDettaglio":1428,"Ordine":24,"ObbligatorioWeb":"Predefinito","RaggruppamentoWeb":null,"CustomField":{"IdCustomField":349,"TitoloTraduzione":"Sample Unlock Time:","Titolo":"Sample Unlock Time:","Descrizione":null,"Tipo":"Testo","Decimali":0,"Lunghezza":0,"Minimo":null,"Massimo":null,"ValoreDefault":null,"Elenco":true,"DefaultCurrDate":false,"ObbligatorioWeb":false}},{"IdSchemaCustomFieldsDettaglio":1491,"Ordine":25,"ObbligatorioWeb":"Predefinito","RaggruppamentoWeb":null,"CustomField":{"IdCustomField":361,"TitoloTraduzione":"Presa in carico, Data:","Titolo":"Presa in carico, Data:","Descrizione":null,"Tipo":"Data","Decimali":0,"Lunghezza":0,"Minimo":null,"Massimo":null,"ValoreDefault":null,"Elenco":true,"DefaultCurrDate":false,"ObbligatorioWeb":false}},{"IdSchemaCustomFieldsDettaglio":1492,"Ordine":26,"ObbligatorioWeb":"Predefinito","RaggruppamentoWeb":null,"CustomField":{"IdCustomField":360,"TitoloTraduzione":"Presa in carico, Orario:","Titolo":"Presa in carico, Orario:","Descrizione":null,"Tipo":"Testo","Decimali":0,"Lunghezza":0,"Minimo":null,"Massimo":null,"ValoreDefault":null,"Elenco":true,"DefaultCurrDate":false,"ObbligatorioWeb":false}},{"IdSchemaCustomFieldsDettaglio":501,"Ordine":27,"ObbligatorioWeb":"Predefinito","RaggruppamentoWeb":null,"CustomField":{"IdCustomField":163,"TitoloTraduzione":"Campionamento:","Titolo":"Campionamento:","Descrizione":null,"Tipo":"SceltaMultipla","Decimali":0,"Lunghezza":0,"Minimo":null,"Massimo":null,"ValoreDefault":null,"Elenco":true,"DefaultCurrDate":false,"ObbligatorioWeb":false}},{"IdSchemaCustomFieldsDettaglio":556,"Ordine":28,"ObbligatorioWeb":"Predefinito","RaggruppamentoWeb":null,"CustomField":{"IdCustomField":169,"TitoloTraduzione":"(*Restituzione Materiale Residuo:","Titolo":"(*Restituzione Materiale Residuo:","Descrizione":null,"Tipo":"SceltaMultipla","Decimali":0,"Lunghezza":0,"Minimo":null,"Massimo":null,"ValoreDefault":"","Elenco":false,"DefaultCurrDate":false,"ObbligatorioWeb":false}},{"IdSchemaCustomFieldsDettaglio":557,"Ordine":29,"ObbligatorioWeb":"Predefinito","RaggruppamentoWeb":null,"CustomField":{"IdCustomField":170,"TitoloTraduzione":"(*Originali:","Titolo":"(*Originali:","Descrizione":null,"Tipo":"SceltaMultipla","Decimali":0,"Lunghezza":0,"Minimo":null,"Massimo":null,"ValoreDefault":"","Elenco":false,"DefaultCurrDate":false,"ObbligatorioWeb":false}},{"IdSchemaCustomFieldsDettaglio":558,"Ordine":30,"ObbligatorioWeb":"Predefinito","RaggruppamentoWeb":null,"CustomField":{"IdCustomField":171,"TitoloTraduzione":"(*Corriere:","Titolo":"(*Corriere:","Descrizione":null,"Tipo":"SceltaMultipla","Decimali":0,"Lunghezza":0,"Minimo":null,"Massimo":null,"ValoreDefault":"","Elenco":false,"DefaultCurrDate":false,"ObbligatorioWeb":false}},{"IdSchemaCustomFieldsDettaglio":818,"Ordine":31,"ObbligatorioWeb":"Predefinito","RaggruppamentoWeb":null,"CustomField":{"IdCustomField":241,"TitoloTraduzione":"N. di Prove:","Titolo":"N. di Prove:","Descrizione":null,"Tipo":"SceltaMultipla","Decimali":0,"Lunghezza":0,"Minimo":null,"Massimo":null,"ValoreDefault":null,"Elenco":false,"DefaultCurrDate":false,"ObbligatorioWeb":false}},{"IdSchemaCustomFieldsDettaglio":851,"Ordine":32,"ObbligatorioWeb":"Predefinito","RaggruppamentoWeb":null,"CustomField":{"IdCustomField":244,"TitoloTraduzione":"Accettatore:","Titolo":"Accettatore:","Descrizione":null,"Tipo":"SceltaMultipla","Decimali":0,"Lunghezza":0,"Minimo":null,"Massimo":null,"ValoreDefault":null,"Elenco":true,"DefaultCurrDate":false,"ObbligatorioWeb":false}},{"IdSchemaCustomFieldsDettaglio":937,"Ordine":33,"ObbligatorioWeb":"Predefinito","RaggruppamentoWeb":null,"CustomField":{"IdCustomField":258,"TitoloTraduzione":"Login Date:","Titolo":"Login Date:","Descrizione":null,"Tipo":"Data","Decimali":0,"Lunghezza":0,"Minimo":null,"Massimo":null,"ValoreDefault":null,"Elenco":true,"DefaultCurrDate":false,"ObbligatorioWeb":false}},{"IdSchemaCustomFieldsDettaglio":1427,"Ordine":34,"ObbligatorioWeb":"Predefinito","RaggruppamentoWeb":null,"CustomField":{"IdCustomField":350,"TitoloTraduzione":"Login Time:","Titolo":"Login Time:","Descrizione":null,"Tipo":"Testo","Decimali":0,"Lunghezza":0,"Minimo":null,"Massimo":null,"ValoreDefault":null,"Elenco":false,"DefaultCurrDate":false,"ObbligatorioWeb":false}},{"IdSchemaCustomFieldsDettaglio":1102,"Ordine":35,"ObbligatorioWeb":"Predefinito","RaggruppamentoWeb":null,"CustomField":{"IdCustomField":236,"TitoloTraduzione":"Consegna campione:","Titolo":"Consegna campione:","Descrizione":null,"Tipo":"SceltaMultipla","Decimali":0,"Lunghezza":0,"Minimo":null,"Massimo":null,"ValoreDefault":null,"Elenco":true,"DefaultCurrDate":false,"ObbligatorioWeb":false}},{"IdSchemaCustomFieldsDettaglio":896,"Ordine":36,"ObbligatorioWeb":"Predefinito","RaggruppamentoWeb":null,"CustomField":{"IdCustomField":254,"TitoloTraduzione":"AWB: ","Titolo":"AWB: ","Descrizione":null,"Tipo":"Testo","Decimali":0,"Lunghezza":0,"Minimo":null,"Massimo":null,"ValoreDefault":null,"Elenco":false,"DefaultCurrDate":false,"ObbligatorioWeb":false}},{"IdSchemaCustomFieldsDettaglio":897,"Ordine":37,"ObbligatorioWeb":"Predefinito","RaggruppamentoWeb":null,"CustomField":{"IdCustomField":253,"TitoloTraduzione":"Sample return:","Titolo":"Sample return:","Descrizione":null,"Tipo":"SceltaMultipla","Decimali":0,"Lunghezza":0,"Minimo":null,"Massimo":null,"ValoreDefault":null,"Elenco":true,"DefaultCurrDate":false,"ObbligatorioWeb":false}},{"IdSchemaCustomFieldsDettaglio":898,"Ordine":38,"ObbligatorioWeb":"Predefinito","RaggruppamentoWeb":null,"CustomField":{"IdCustomField":255,"TitoloTraduzione":"Data ricezione risultati test:","Titolo":"Data ricezione risultati test:","Descrizione":null,"Tipo":"Data","Decimali":0,"Lunghezza":0,"Minimo":null,"Massimo":null,"ValoreDefault":null,"Elenco":false,"DefaultCurrDate":false,"ObbligatorioWeb":false}},{"IdSchemaCustomFieldsDettaglio":899,"Ordine":39,"ObbligatorioWeb":"Predefinito","RaggruppamentoWeb":null,"CustomField":{"IdCustomField":252,"TitoloTraduzione":"Service required:","Titolo":"Service required:","Descrizione":null,"Tipo":"SceltaMultipla","Decimali":0,"Lunghezza":0,"Minimo":null,"Massimo":null,"ValoreDefault":null,"Elenco":true,"DefaultCurrDate":false,"ObbligatorioWeb":false}},{"IdSchemaCustomFieldsDettaglio":950,"Ordine":40,"ObbligatorioWeb":"Predefinito","RaggruppamentoWeb":null,"CustomField":{"IdCustomField":246,"TitoloTraduzione":"Lab outsourcing:","Titolo":"Lab outsourcing:","Descrizione":null,"Tipo":"SceltaMultipla","Decimali":0,"Lunghezza":0,"Minimo":null,"Massimo":null,"ValoreDefault":null,"Elenco":false,"DefaultCurrDate":false,"ObbligatorioWeb":false}},{"IdSchemaCustomFieldsDettaglio":1362,"Ordine":41,"ObbligatorioWeb":"Predefinito","RaggruppamentoWeb":null,"CustomField":{"IdCustomField":261,"TitoloTraduzione":"Tipologia report (solo per Cina)","Titolo":"Tipologia report (solo per Cina)","Descrizione":null,"Tipo":"SceltaMultipla","Decimali":0,"Lunghezza":0,"Minimo":null,"Massimo":null,"ValoreDefault":null,"Elenco":false,"DefaultCurrDate":false,"ObbligatorioWeb":false}}]} \ No newline at end of file diff --git a/public/userarea/search_clienti.php b/public/userarea/search_clienti.php index f624c6e..5f00884 100644 --- a/public/userarea/search_clienti.php +++ b/public/userarea/search_clienti.php @@ -2,7 +2,12 @@ require_once dirname(__DIR__, 2) . '/vendor/autoload.php'; require_once __DIR__ . '/class/db-functions.php'; include dirname(__DIR__) . '/../extra/auth.php'; -if (!Auth::check()) { http_response_code(401); echo json_encode(['error' => 'Unauthorized']); exit; } + +if (!Auth::check()) { + http_response_code(401); + echo json_encode(['error' => 'Unauthorized']); + exit; +} require_once __DIR__ . '/class/VisualLimsApiClient.class.php'; @@ -14,44 +19,95 @@ $q = mb_strtolower(trim($_GET['q'] ?? '')); $limit = max(1, min(50, intval($_GET['limit'] ?? 20))); $id = isset($_GET['id']) ? intval($_GET['id']) : null; +function formatClientLabel(array $client): string +{ + $name = trim($client['Nominativo'] ?? ''); + $id = trim((string)($client['IdCliente'] ?? '')); + $code = trim((string)($client['CodiceCliente'] ?? '')); + + $parts = explode('_', $code); + $suffix = trim($parts[1] ?? ''); + + if ($suffix === '' && $code !== '') { + $suffix = substr($code, 0, 1); + } + + if ($suffix === '') { + $suffix = '--'; + } + + return $name . ' - ' . $suffix . ' (ID: ' . $id . ')'; +} + try { // Load from cache or API $cacheFile = __DIR__ . '/cache/clienti.json'; + if (file_exists($cacheFile) && (time() - filemtime($cacheFile) < 3600)) { $data = json_decode(file_get_contents($cacheFile), true); } else { $api = VisualLimsApiClient::getInstance(); + $params = [ '$select' => 'IdCliente,Nominativo,CodiceCliente', '$orderby' => 'Nominativo asc' ]; + $data = $api->get("Cliente?" . http_build_query($params)); - if (!is_dir(__DIR__ . '/cache')) mkdir(__DIR__ . '/cache', 0777, true); + + if (!is_dir(__DIR__ . '/cache')) { + mkdir(__DIR__ . '/cache', 0777, true); + } + file_put_contents($cacheFile, json_encode($data)); } $clients = $data['value'] ?? []; - // If requesting by specific ID (for loading selected value) + // If requesting by specific ID, used for loading selected value if ($id !== null) { foreach ($clients as $c) { if ((int)$c['IdCliente'] === $id) { - echo json_encode(['results' => [['id' => $c['IdCliente'], 'text' => trim($c['Nominativo'] ?? '')]]]); + echo json_encode([ + 'results' => [[ + 'id' => $c['IdCliente'], + 'text' => formatClientLabel($c), + 'IdCliente' => $c['IdCliente'], + 'Nominativo' => trim($c['Nominativo'] ?? ''), + 'CodiceCliente' => trim($c['CodiceCliente'] ?? '') + ]] + ]); exit; } } + echo json_encode(['results' => []]); exit; } // Search by query $results = []; + foreach ($clients as $c) { $name = trim($c['Nominativo'] ?? ''); $code = trim($c['CodiceCliente'] ?? ''); - if ($q === '' || mb_strpos(mb_strtolower($name), $q) !== false || mb_strpos(mb_strtolower($code), $q) !== false) { - $results[] = ['id' => $c['IdCliente'], 'text' => $name]; - if (count($results) >= $limit) break; + + if ( + $q === '' || + mb_strpos(mb_strtolower($name), $q) !== false || + mb_strpos(mb_strtolower($code), $q) !== false + ) { + $results[] = [ + 'id' => $c['IdCliente'], + 'text' => formatClientLabel($c), + 'IdCliente' => $c['IdCliente'], + 'Nominativo' => $name, + 'CodiceCliente' => $code + ]; + + if (count($results) >= $limit) { + break; + } } } diff --git a/public/userarea/search_rapporto_min.php b/public/userarea/search_rapporto_min.php new file mode 100644 index 0000000..1f8487c --- /dev/null +++ b/public/userarea/search_rapporto_min.php @@ -0,0 +1,73 @@ + "Codice eq '{$codice}'", + 'CodiceRapporto' => "CodiceRapporto eq '{$codice}'", + 'Numero' => "Numero eq '{$codice}'" + ]; + + foreach ($filters as $label => $filter) { + try { + $data = $api->get('Rapporto', [ + '$filter' => $filter, + '$top' => 5, + '$select' => 'IdRapporto,Codice,CodiceRapporto,Numero,Data,Versione,Cliente' + ]); + + $attempts[$label] = [ + 'success' => true, + 'filter' => $filter, + 'records' => isset($data['value']) && is_array($data['value']) ? count($data['value']) : null, + 'data' => $data + ]; + + } catch (Exception $e) { + $attempts[$label] = [ + 'success' => false, + 'filter' => $filter, + 'error' => $e->getMessage() + ]; + } + } + + echo json_encode([ + 'success' => true, + 'input_codice' => $codice, + 'attempts' => $attempts + ], JSON_PRETTY_PRINT | JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES); + +} catch (Exception $e) { + http_response_code(500); + + echo json_encode([ + 'success' => false, + 'error' => $e->getMessage() + ], JSON_PRETTY_PRINT | JSON_UNESCAPED_UNICODE); +} \ No newline at end of file diff --git a/public/userarea/test_pdf_value.php b/public/userarea/test_pdf_value.php new file mode 100644 index 0000000..3025c84 --- /dev/null +++ b/public/userarea/test_pdf_value.php @@ -0,0 +1,117 @@ +get("RapportoFile(" . $idRapportoFile . ")"); + + $pdfBody = null; + $strategyUsed = null; + $debugLog = array(); + + // Strategia A - campo FileContent base64 + if (!empty($entityData['FileContent'])) { + $decoded = base64_decode($entityData['FileContent'], true); + if ($decoded !== false && substr($decoded, 0, 4) === '%PDF') { + $pdfBody = $decoded; + $strategyUsed = 'A: FileContent base64'; + } + } + + // Strategia B - campo Contenuto base64 + if (!$pdfBody && !empty($entityData['Contenuto'])) { + $decoded = base64_decode($entityData['Contenuto'], true); + if ($decoded !== false && substr($decoded, 0, 4) === '%PDF') { + $pdfBody = $decoded; + $strategyUsed = 'B: Contenuto base64'; + } + } + + // Recupera token e baseUrl dalla classe + $token = $api->getToken(); + $baseUrl = rtrim($api->getBaseUrl(), '/'); + + $rawEndpoints = array( + 'C: $value' => $baseUrl . '/api/odata/RapportoFile(' . $idRapportoFile . ')/$value', + 'D: FileContent/$value' => $baseUrl . '/api/odata/RapportoFile(' . $idRapportoFile . ')/FileContent/$value', + 'E: Contenuto/$value' => $baseUrl . '/api/odata/RapportoFile(' . $idRapportoFile . ')/Contenuto/$value', + ); + + foreach ($rawEndpoints as $label => $url) { + if ($pdfBody) break; + + $ch = curl_init($url); + curl_setopt($ch, CURLOPT_RETURNTRANSFER, true); + curl_setopt($ch, CURLOPT_HTTPHEADER, array( + 'Authorization: Bearer ' . $token, + 'Accept: application/pdf, application/octet-stream, */*', + )); + curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false); + curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, false); + curl_setopt($ch, CURLOPT_CONNECTTIMEOUT, 5); + curl_setopt($ch, CURLOPT_TIMEOUT, 30); + + $body = curl_exec($ch); + $httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE); + $ct = curl_getinfo($ch, CURLINFO_CONTENT_TYPE); + curl_close($ch); + + $debugLog[$label] = array( + 'url' => $url, + 'http_code' => $httpCode, + 'content_type' => $ct, + 'body_start' => substr((string)$body, 0, 300), + ); + + if ($httpCode === 200 && is_string($body) && substr($body, 0, 4) === '%PDF') { + $pdfBody = $body; + $strategyUsed = $label; + } + } + + // RISPOSTA + if ($pdfBody) { + $fileName = isset($entityData['FileName']) ? $entityData['FileName'] : 'rapporto_' . $idRapportoFile . '.pdf'; + header('Content-Type: application/pdf'); + header('Content-Disposition: inline; filename="' . $fileName . '"'); + header('Content-Length: ' . strlen($pdfBody)); + header('Cache-Control: private, max-age=0, must-revalidate'); + echo $pdfBody; + exit; + } + + // Nessuna strategia ha funzionato + header('Content-Type: application/json; charset=utf-8'); + echo json_encode(array( + 'success' => false, + 'message' => 'Nessuna strategia ha restituito un PDF valido.', + 'id_rapporto_file' => $idRapportoFile, + 'entity_fields' => array_keys($entityData ? $entityData : array()), + 'entity_data' => $entityData, + 'strategies_debug' => $debugLog, + ), JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE); +} catch (Exception $e) { + http_response_code(500); + header('Content-Type: application/json; charset=utf-8'); + echo json_encode(array( + 'success' => false, + 'error' => $e->getMessage(), + ), JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE); +} diff --git a/public/userarea/update_api_json_nodes.php b/public/userarea/update_api_json_nodes.php new file mode 100644 index 0000000..4b42ad9 --- /dev/null +++ b/public/userarea/update_api_json_nodes.php @@ -0,0 +1,63 @@ +getConnection(); + + $stmt = $pdo->prepare(" + UPDATE excel_templates + SET + api_sample_json = ?, + json_nodes = ? + WHERE id = ? + "); + + $stmt->execute([ + $apiSampleJson, + json_encode($decodedNodes, JSON_UNESCAPED_UNICODE), + $templateId + ]); + + echo json_encode([ + 'success' => true, + 'nodes_count' => count($decodedNodes) + ]); +} catch (Throwable $e) { + echo json_encode([ + 'success' => false, + 'message' => $e->getMessage() + ]); +} From 813bd66f96e4617c9f841c945dc476915b9e0558 Mon Sep 17 00:00:00 2001 From: solocla Date: Mon, 4 May 2026 12:10:12 +0200 Subject: [PATCH 07/42] scroll table parts --- public/userarea/modal_partsTable.php | 184 +++++++++++++++++++-------- public/userarea/partsTable.js | 138 ++++++++++++++++++++ 2 files changed, 268 insertions(+), 54 deletions(-) diff --git a/public/userarea/modal_partsTable.php b/public/userarea/modal_partsTable.php index cb26f7a..2080ebc 100644 --- a/public/userarea/modal_partsTable.php +++ b/public/userarea/modal_partsTable.php @@ -32,6 +32,10 @@ +
@@ -44,45 +48,54 @@
- - - - - - - - + + + + + + + + + + + +
NumDescrizioneMatrice - - - - Azioni +
+ + + + + + + + + + + + + + + - - - - - - - - - - - -
NumDescrizioneMatrice + + + + Azioni -
-
- - -
-
- - - - - -
+
+
+ + +
+
+ + + + + +
+
Foto del Campione
@@ -290,28 +303,20 @@ white-space: nowrap; } - /* Select delle righe (colonna Matrice) = 150px */ + /* Select delle righe Matrice: si adatta alla colonna */ .part-matrice { - width: 300px !important; - min-width: 300px !important; - max-width: 300px !important; - flex: 0 0 300px !important; + width: 100% !important; + min-width: 0 !important; + max-width: 100% !important; } .part-matrice.select2-hidden-accessible+.select2 { - width: 300px !important; - min-width: 300px !important; - max-width: 300px !important; - flex: 0 0 300px !important; + width: 100% !important; + min-width: 0 !important; + max-width: 100% !important; } - /* Colonna Descrizione (2Βͺ colonna) = 420px */ - #partsTable th:nth-child(2), - #partsTable td:nth-child(2) { - width: 300px !important; - min-width: 300px !important; - max-width: 300px !important; - } + #partsTable .part-number { text-align: center; @@ -516,6 +521,77 @@ } } + /* Resizable parts table - stable */ + .parts-table-scroll { + width: 100%; + overflow-x: auto; + overflow-y: visible; + contain: inline-size; + } + + #partsTable { + table-layout: fixed !important; + width: max-content !important; + min-width: unset !important; + } + + #partsTable col.parts-col-num { + width: 55px; + } + + #partsTable col.parts-col-description { + width: 320px; + } + + #partsTable col.parts-col-matrice { + width: 360px; + } + + #partsTable col.parts-col-date { + width: 150px; + } + + #partsTable col.parts-col-actions { + width: 230px; + } + + #partsTable th, + #partsTable td { + position: relative; + box-sizing: border-box; + } + + #partsTable th { + user-select: none; + } + + #partsTable th .parts-resizer { + position: absolute; + top: 0; + right: 0; + width: 9px; + height: 100%; + cursor: col-resize; + z-index: 50; + background: rgba(108, 117, 125, 0.12); + border-right: 1px solid rgba(108, 117, 125, 0.45); + } + + #partsTable th .parts-resizer:hover { + background: rgba(108, 117, 125, 0.25); + border-right: 2px solid rgba(73, 80, 87, 0.7); + } + + #partsTable td { + overflow: hidden; + text-overflow: ellipsis; + } + + #partsTable td input, + #partsTable td select, + #partsTable td .select2-container { + max-width: 100% !important; + } /* rosso */ \ No newline at end of file diff --git a/public/userarea/partsTable.js b/public/userarea/partsTable.js index b8b148a..2ed3109 100644 --- a/public/userarea/partsTable.js +++ b/public/userarea/partsTable.js @@ -2463,4 +2463,142 @@ $(document).on("click", "#showHideImageBtn", function () { "", ); } + + if (typeof window.applyPartsColumnWidths === "function") { + setTimeout(window.applyPartsColumnWidths, 150); + } }); +// =================== +// RESIZABLE PARTS TABLE COLUMNS - FIXED COLGROUP VERSION +// =================== +(function () { + // Larghezze default per indice colonna (0-based) + const defaultWidths = [55, 320, 360, 150, 230]; + const savedWidths = [...defaultWidths]; + + function getColgroup() { + return $("#partsTableColgroup"); + } + + function syncColgroupToHeaders() { + const $table = $("#partsTable"); + const $ths = $table.find("thead tr:first th"); + const $colgroup = getColgroup(); + const thCount = $ths.length; + + // Assicura che ci siano esattamente tante quante + while ($colgroup.find("col").length < thCount) { + $colgroup.append(""); + } + while ($colgroup.find("col").length > thCount) { + $colgroup.find("col:last").remove(); + } + + // Applica le larghezze salvate + $colgroup.find("col").each(function (i) { + const w = savedWidths[i] !== undefined ? savedWidths[i] : 150; + $(this).css("width", w + "px"); + }); + + // Imposta larghezza totale della tabella = somma colonne (evita reflow) + const total = savedWidths.slice(0, thCount).reduce((a, b) => a + b, 0); + $table.css("width", total + "px"); + } + + function applyColumnWidth(colIndex, newWidth) { + const w = Math.max(40, Math.round(newWidth)); + savedWidths[colIndex] = w; + + const $col = getColgroup().find("col").eq(colIndex); + if ($col.length) { + $col.css("width", w + "px"); + } + + // Aggiorna larghezza totale tabella senza toccare le altre colonne + const thCount = $("#partsTable thead tr:first th").length; + const total = savedWidths.slice(0, thCount).reduce((a, b) => a + b, 0); + $("#partsTable").css("width", total + "px"); + } + + function addResizers() { + const $table = $("#partsTable"); + if (!$table.length) return; + + $table.find("thead tr:first th").each(function (colIndex) { + const $th = $(this); + + // Salta colonna Num (indice 0) β€” non ridimensionabile + if (colIndex === 0) return; + + // Non aggiungere due volte + if ($th.find(".parts-resizer").length) return; + + const $resizer = $(""); + $th.css("position", "relative"); // necessario per il posizionamento assoluto + $th.append($resizer); + + $resizer.on("mousedown", function (e) { + e.preventDefault(); + e.stopPropagation(); + + const startX = e.pageX; + // Leggi la larghezza ESATTA dalla , non dal + const startWidth = + savedWidths[colIndex] !== undefined + ? savedWidths[colIndex] + : parseInt( + getColgroup() + .find("col") + .eq(colIndex) + .css("width"), + 10, + ) || 150; + + $("body") + .css("user-select", "none") + .css("cursor", "col-resize"); + + $(document).on("mousemove.partsResize", function (ev) { + const delta = ev.pageX - startX; + applyColumnWidth(colIndex, startWidth + delta); + }); + + $(document).on("mouseup.partsResize", function () { + $("body").css("user-select", "").css("cursor", ""); + $(document).off(".partsResize"); + }); + }); + }); + } + + function init() { + syncColgroupToHeaders(); + addResizers(); + } + + // Init al primo show del modale + $(document).on("shown.bs.modal", "#partsModal", function () { + setTimeout(init, 120); + }); + + // Re-init dopo ogni AJAX (nuove righe, caricamenti) + $(document).ajaxComplete(function () { + if ($("#partsModal").hasClass("show")) { + setTimeout(syncColgroupToHeaders, 200); + setTimeout(addResizers, 220); + } + }); + + // Re-init dopo aggiunta/rimozione righe + $(document).on( + "click", + ".add-row-global, .add-mix-global, .add-mix-row, .remove-row, #renumberPartsBtn, #clonePartsBtn", + function () { + setTimeout(syncColgroupToHeaders, 120); + setTimeout(addResizers, 140); + }, + ); + + window.initPartsResizableColumns = init; + window.applyPartsColumnWidths = syncColgroupToHeaders; +})(); From 0f0c6a04b754a656e4ce55380d3a6e9a954b90f4 Mon Sep 17 00:00:00 2001 From: solocla Date: Tue, 5 May 2026 09:07:14 +0200 Subject: [PATCH 08/42] fixed Mix --- public/userarea/annotationsModal.js | 41 +++++++++++------------------ public/userarea/load_parts.php | 10 +++---- public/userarea/partsTable.js | 32 +++++++++++++--------- 3 files changed, 41 insertions(+), 42 deletions(-) diff --git a/public/userarea/annotationsModal.js b/public/userarea/annotationsModal.js index ad66692..8f4a79f 100644 --- a/public/userarea/annotationsModal.js +++ b/public/userarea/annotationsModal.js @@ -614,17 +614,11 @@ $(document).ready(function () { partsListData.forEach((part) => { const partNumber = part.part_number; const partDescription = part.part_description; + const isMixPart = String(part.mix || "N").toUpperCase() === "Y"; + const partColor = - partColors[partNumber] || - (partDescription.toLowerCase().startsWith("mix") - ? "#0000ff" - : "#ff0000"); - if ( - partNumber && - partDescription && - (showMixParts || - !partDescription.toLowerCase().startsWith("mix")) - ) { + partColors[partNumber] || (isMixPart ? "#0000ff" : "#ff0000"); + if (partNumber && partDescription && (showMixParts || !isMixPart)) { const colorOptions = predefinedColors .map( (color) => @@ -922,11 +916,10 @@ $(document).ready(function () { ) { partsListData = response.parts; response.parts.forEach((part) => { - const defaultColor = part.part_description - .toLowerCase() - .startsWith("mix") - ? "#0000ff" - : "#ff0000"; + const isMixPart = + String(part.mix || "N").toUpperCase() === "Y"; + const defaultColor = isMixPart ? "#0000ff" : "#ff0000"; + partColors[part.part_number] = defaultColor; }); updatePartsList(); @@ -1003,11 +996,10 @@ $(document).ready(function () { (p) => p.part_number == marker.partNumber, ); const partDescription = part ? part.part_description : ""; - if ( - !showMixParts && - partDescription && - partDescription.toLowerCase().startsWith("mix") - ) { + const isMixPart = + part && String(part.mix || "N").toUpperCase() === "Y"; + + if (!showMixParts && isMixPart) { console.log("Ignoro marker per parte Mix:", marker.partNumber); return; } @@ -1115,11 +1107,10 @@ $(document).ready(function () { } const partsList = partsListData - .filter( - (part) => - showMixParts || - !part.part_description.toLowerCase().startsWith("mix"), - ) + .filter((part) => { + const isMixPart = String(part.mix || "N").toUpperCase() === "Y"; + return showMixParts || !isMixPart; + }) .map((part) => `${part.part_number} ${part.part_description}`); const text = partsList.join("\n"); diff --git a/public/userarea/load_parts.php b/public/userarea/load_parts.php index 6fb0076..59bef0b 100644 --- a/public/userarea/load_parts.php +++ b/public/userarea/load_parts.php @@ -33,9 +33,9 @@ try { if ($extraFieldId) { $stmt = $pdo->prepare(" SELECT - p.id, p.iddatadb, p.part_number, p.part_description, p.idmatrice, p.note, p.dateexpiry, - cf.value_id AS extra_value_id, - cf.value_text AS extra_value_text + p.id, p.iddatadb, p.part_number, p.part_description, p.mix, p.idmatrice, p.note, p.dateexpiry, + cf.value_id AS extra_value_id, + cf.value_text AS extra_value_text FROM identification_parts p LEFT JOIN identification_parts_customfields cf ON cf.part_id = p.id AND cf.field_id = :extraFieldId @@ -48,8 +48,8 @@ try { ]); } else { $stmt = $pdo->prepare(" - SELECT id, iddatadb, part_number, part_description, idmatrice, note, dateexpiry, - NULL AS extra_value_id, NULL AS extra_value_text + SELECT id, iddatadb, part_number, part_description, mix, idmatrice, note, dateexpiry, + NULL AS extra_value_id, NULL AS extra_value_text FROM identification_parts WHERE iddatadb = :iddatadb ORDER BY part_number ASC diff --git a/public/userarea/partsTable.js b/public/userarea/partsTable.js index 2ed3109..7f5c6e8 100644 --- a/public/userarea/partsTable.js +++ b/public/userarea/partsTable.js @@ -235,6 +235,17 @@ $(document).ready(function () { $row.data("__pid", id); } + function setRowMix($row, isMix) { + const mixValue = isMix === true || isMix === "Y" ? "Y" : "N"; + $row.attr("data-is-mix", mixValue); + $row.data("is-mix", mixValue); + } + + function getRowMix($row) { + const value = $row.attr("data-is-mix") || $row.data("is-mix"); + return value === "Y" ? "Y" : "N"; + } + // =================== // VOICE RECOGNITION SETUP // =================== @@ -698,7 +709,7 @@ $(document).ready(function () { const $saveLoading = $row.find(".save-loading"); const iddatadb = $("#partsModal").data("iddatadb"); const idquotations = $("#partsModal").data("idquotations"); - const isMix = partDescription.startsWith("Mix") ? "Y" : "N"; + const isMix = getRowMix($row); // EXTRA FIELD (0/1) const extra_field_id = $row.find(".part-extra-field-id").val() || null; @@ -837,11 +848,7 @@ $(document).ready(function () { let $mixRow = $("#partsTableBody tr") .filter(function () { - return $(this) - .find(".part-description") - .val() - .trim() - .startsWith("Mix"); + return getRowMix($(this)) === "Y"; }) .last(); @@ -897,7 +904,7 @@ $(document).ready(function () { function addNewRow(nextPartNumber, isMix = false) { const description = isMix ? "Mix" : ""; const newRow = ` - + @@ -918,6 +925,7 @@ $(document).ready(function () { `; $("#partsTableBody").append(newRow); const $newRow = $("#partsTableBody tr:last"); + setRowMix($newRow, isMix ? "Y" : "N"); const $select = $newRow.find(".part-matrice"); const selectedMacro = $("#macro-matrice-filter").val() || ""; @@ -1394,7 +1402,7 @@ $(document).ready(function () { ? $("
").text(part.note).html() : ""; const newRow = ` - + @@ -1418,7 +1426,7 @@ $(document).ready(function () { const $row = $( `#partsTableBody tr[data-part-id="${part.id}"]`, ); - + setRowMix($row, part.mix === "Y" ? "Y" : "N"); if ( part.extra_value_id !== undefined && part.extra_value_id !== null @@ -1883,7 +1891,7 @@ $(document).ready(function () { id: part.partId, part_number: index + 1, part_description: part.partDescription, - mix: part.partDescription.startsWith("Mix") ? "Y" : "N", + mix: getRowMix($rows.eq(index)), idmatrice: partMatrice[index + 1] || null, note: part.note, dateexpiry: part.dateexpiry, @@ -2238,7 +2246,7 @@ $(document).on("change", ".propagate-date-input", function () { const partId = $row.data("part-id"); const partNumber = $row.find(".part-number").val(); const partDescription = $row.find(".part-description").val().trim(); - const mix = partDescription.startsWith("Mix") ? "Y" : "N"; + const mix = $row.attr("data-is-mix") === "Y" ? "Y" : "N"; const idmatrice = $row.find(".part-matrice").val() || null; const note = $row.data("note") || null; @@ -2349,7 +2357,7 @@ $(document).on("click", ".save-common-note-btn", function () { const partId = $row.data("part-id"); const partNumber = $row.find(".part-number").val(); const partDescription = $row.find(".part-description").val().trim(); - const mix = partDescription.startsWith("Mix") ? "Y" : "N"; + const mix = $row.attr("data-is-mix") === "Y" ? "Y" : "N"; const idmatrice = $row.find(".part-matrice").val() || null; const dateexpiry = $row.find(".part-dateexpiry").val() || null; From f3e5cb4ffdcb26b633e6769352f29162d647073e Mon Sep 17 00:00:00 2001 From: solocla Date: Tue, 5 May 2026 09:37:18 +0200 Subject: [PATCH 09/42] fixed capitolato riferimento --- public/userarea/modal_partsTable.php | 128 +++++++++++++++- public/userarea/partsTable.js | 212 ++++++++++++++++++++++++--- 2 files changed, 317 insertions(+), 23 deletions(-) diff --git a/public/userarea/modal_partsTable.php b/public/userarea/modal_partsTable.php index 2080ebc..ac3e849 100644 --- a/public/userarea/modal_partsTable.php +++ b/public/userarea/modal_partsTable.php @@ -20,8 +20,7 @@ - - + +
+
+ `; } - // Testo -> input + hidden field_id + // Campo testo: titolo sopra, input + bottone sotto return ` - - - - `; + +
+
+ ${label} +
+
+ + +
+
+ `; } function initializeExtraFieldSelect2($context) { @@ -191,6 +247,79 @@ $(document).ready(function () { saveRow($row); }); + $(document).on("click", ".extra-propagate-btn", function (e) { + e.preventDefault(); + + if (!partsExtraField) return; + + const isSelect = + (partsExtraField.data_type || "").toLowerCase() === + "sceltamultipla"; + + let propagateValueId = null; + let propagateValueText = null; + + if (isSelect) { + propagateValueId = + $("#partsTable thead .extra-propagate-select").val() || ""; + + if (!propagateValueId) { + const errorMsg = $( + '', + ); + $("#partsModal .modal-body").prepend(errorMsg); + setTimeout(() => { + errorMsg.fadeOut(500, function () { + $(this).remove(); + }); + }, 3500); + return; + } + } else { + propagateValueText = ( + $("#partsTable thead .extra-propagate-input").val() || "" + ).trim(); + + if (!propagateValueText) { + const errorMsg = $( + '', + ); + $("#partsModal .modal-body").prepend(errorMsg); + setTimeout(() => { + errorMsg.fadeOut(500, function () { + $(this).remove(); + }); + }, 3500); + return; + } + } + + $("#partsTableBody tr").each(function () { + const $row = $(this); + + if (isSelect) { + const $select = $row.find(".part-extra-select"); + + if ($select.length) { + $select.val(propagateValueId).trigger("change.select2"); + + $row.data("extra-value-id", propagateValueId); + $row.find(".part-extra-value-id").val(propagateValueId); + } + } else { + const $input = $row.find(".part-extra-field"); + + if ($input.length) { + $input.val(propagateValueText); + + $row.data("extra-value-text", propagateValueText); + } + } + + saveRow($row); + }); + }); + function applyExtraFieldColumn() { const $theadRow = $("#partsTable thead tr"); @@ -203,16 +332,17 @@ $(document).ready(function () { }); // 3) se non c'Γ¨ campo extra -> fine - if (!partsExtraField) return; + if (!partsExtraField) { + if (typeof window.applyPartsColumnWidths === "function") { + setTimeout(window.applyPartsColumnWidths, 100); + } + return; + } - // 4) inserisci header prima di "Azioni" (ultima colonna) - $theadRow - .find("th:last") - .before( - `${partsExtraField.field_label}`, - ); + // 4) inserisci header propagabile prima di "Azioni" ultima colonna + $theadRow.find("th:last").before(buildExtraFieldHeaderHtml()); - // 5) aggiungi cella a ogni riga giΓ  presente (una sola volta) + // 5) aggiungi cella a ogni riga giΓ  presente $("#partsTableBody tr").each(function () { const $row = $(this); $row.find("td:last").before(buildExtraFieldCellHtml($row)); @@ -225,6 +355,33 @@ $(document).ready(function () { }); initializeExtraFieldSelect2($("#partsTableBody")); + + // Select2 anche sul campo di propagazione in testata + if ( + (partsExtraField.data_type || "").toLowerCase() === "sceltamultipla" + ) { + const $headerSelect = $( + "#partsTable thead .extra-propagate-select", + ); + + if ($headerSelect.length && typeof $.fn.select2 !== "undefined") { + if ($headerSelect.hasClass("select2-hidden-accessible")) { + $headerSelect.select2("destroy"); + } + + $headerSelect.select2({ + placeholder: "Select…", + allowClear: true, + width: "100%", + dropdownParent: $("#partsModal"), + minimumResultsForSearch: 0, + }); + } + } + + if (typeof window.applyPartsColumnWidths === "function") { + setTimeout(window.applyPartsColumnWidths, 100); + } } function setPartId($row, id) { @@ -971,7 +1128,7 @@ $(document).ready(function () { // Raccogli tutti i dati della riga per evitare sovrascritture const partNumber = $row.find(".part-number").val(); const partDescription = $row.find(".part-description").val().trim(); - const mix = partDescription.startsWith("Mix") ? "Y" : "N"; + const mix = getRowMix($row); const idmatrice = $row.find(".part-matrice").val() || null; const dateexpiry = $row.find(".part-dateexpiry").val() || null; @@ -1070,7 +1227,7 @@ $(document).ready(function () { // Raccogli tutti i dati della riga per evitare sovrascritture const partNumber = $row.find(".part-number").val(); const partDescription = $row.find(".part-description").val().trim(); - const mix = partDescription.startsWith("Mix") ? "Y" : "N"; + const mix = getRowMix($row); const idmatrice = $row.find(".part-matrice").val() || null; const note = $row.data("note") || null; @@ -1440,6 +1597,19 @@ $(document).ready(function () { ); } + if ( + part.extra_value_text !== undefined && + part.extra_value_text !== null + ) { + $row.data( + "extra-value-text", + part.extra_value_text, + ); + $row.find(".part-extra-field").val( + part.extra_value_text, + ); + } + initializeExtraFieldSelect2($row); const $select = $("#partsTableBody").find( @@ -2481,7 +2651,7 @@ $(document).on("click", "#showHideImageBtn", function () { // =================== (function () { // Larghezze default per indice colonna (0-based) - const defaultWidths = [55, 320, 360, 150, 230]; + const defaultWidths = [55, 320, 360, 150, 220, 230]; const savedWidths = [...defaultWidths]; function getColgroup() { From cbd0c5b68a1d9b4fadc80cae287c1ff9469076fc Mon Sep 17 00:00:00 2001 From: solocla Date: Wed, 6 May 2026 16:44:42 +0200 Subject: [PATCH 10/42] annotation color and clone templates --- public/userarea/annotationsModal.js | 23 +- public/userarea/clone_template.php | 228 ++++++++++++++++++ public/userarea/modal_annotations.php | 10 + public/userarea/schema_dettagli_response.json | 2 +- public/userarea/templates_dashboard.php | 69 +++++- 5 files changed, 318 insertions(+), 14 deletions(-) create mode 100644 public/userarea/clone_template.php diff --git a/public/userarea/annotationsModal.js b/public/userarea/annotationsModal.js index 8f4a79f..a9443f1 100644 --- a/public/userarea/annotationsModal.js +++ b/public/userarea/annotationsModal.js @@ -22,6 +22,8 @@ $(document).ready(function () { let partsListData = []; // DIMENSIONE GLOBALE MARKER let globalMarkerSize = 16; + // COLORE TESTO LISTA DESCRIZIONI + let globalDescriptionColor = "#000000"; // =================== // MODAL INITIALIZATION @@ -138,6 +140,7 @@ $(document).ready(function () { descriptionTextbox = null; markerObjects = {}; globalMarkerSize = 16; + globalDescriptionColor = "#000000"; $("#photoSelectorContainerAnnotations").empty().hide(); $("#samplePhotoAnnotations").attr("src", ""); $("#partsListAnnotations").empty(); @@ -177,7 +180,25 @@ $(document).ready(function () { updateMarkers(); markUnsaved(); }); + // =================== + // COLORE LISTA DESCRIZIONI + // =================== + $(document) + .off("input.descriptionColor", "#descriptionColorPickerAnnotations") + .on( + "input.descriptionColor", + "#descriptionColorPickerAnnotations", + function () { + globalDescriptionColor = $(this).val(); + if (descriptionTextbox && fabricCanvas) { + descriptionTextbox.set("fill", globalDescriptionColor); + fabricCanvas.renderAll(); + } + + markUnsaved(); + }, + ); // =================== // PHOTO LOADERS // =================== @@ -1130,7 +1151,7 @@ $(document).ready(function () { backgroundColor: "transparent", fontFamily: "Arial", fontSize: Math.max(16, Math.round(globalMarkerSize * 0.8)), - fill: "#000000", + fill: globalDescriptionColor, padding: 10, editable: false, selectable: true, diff --git a/public/userarea/clone_template.php b/public/userarea/clone_template.php new file mode 100644 index 0000000..4f75640 --- /dev/null +++ b/public/userarea/clone_template.php @@ -0,0 +1,228 @@ +getConnection(); + + $pdo->beginTransaction(); + + // 1. Get source template + $stmt = $pdo->prepare(" + SELECT + name, + source_type, + header_row, + start_column, + description, + target_table, + sample_xlsx, + button_size, + button_bg_color, + button_text_color, + button_label, + status, + client_specific_fields, + idclient, + clientname, + schemaname, + idschema, + schemajson, + xls_headers, + api_sample_json, + json_nodes, + idroutine + FROM excel_templates + WHERE id = ? + LIMIT 1 + "); + $stmt->execute([$sourceTemplateId]); + $template = $stmt->fetch(PDO::FETCH_ASSOC); + + if (!$template) { + throw new Exception("Template not found."); + } + + // 2. Generate unique clone name + $baseName = 'Copia di ' . $template['name']; + $newName = $baseName; + + $checkStmt = $pdo->prepare("SELECT COUNT(*) FROM excel_templates WHERE name = ?"); + $checkStmt->execute([$newName]); + + if ((int)$checkStmt->fetchColumn() > 0) { + $counter = 2; + + do { + $newName = $baseName . ' (' . $counter . ')'; + $checkStmt->execute([$newName]); + $exists = (int)$checkStmt->fetchColumn() > 0; + $counter++; + } while ($exists); + } + + // 3. Insert cloned template + $insertTemplate = $pdo->prepare(" + INSERT INTO excel_templates ( + name, + source_type, + created_at, + updated_at, + header_row, + start_column, + description, + target_table, + sample_xlsx, + button_size, + button_bg_color, + button_text_color, + button_label, + status, + client_specific_fields, + idclient, + clientname, + schemaname, + idschema, + schemajson, + xls_headers, + api_sample_json, + json_nodes, + idroutine + ) VALUES ( + :name, + :source_type, + NOW(), + NOW(), + :header_row, + :start_column, + :description, + :target_table, + :sample_xlsx, + :button_size, + :button_bg_color, + :button_text_color, + :button_label, + :status, + :client_specific_fields, + :idclient, + :clientname, + :schemaname, + :idschema, + :schemajson, + :xls_headers, + :api_sample_json, + :json_nodes, + :idroutine + ) + "); + + $insertTemplate->execute([ + ':name' => $newName, + ':source_type' => $template['source_type'], + ':header_row' => $template['header_row'], + ':start_column' => $template['start_column'], + ':description' => $template['description'], + ':target_table' => $template['target_table'], + ':sample_xlsx' => $template['sample_xlsx'], + ':button_size' => $template['button_size'], + ':button_bg_color' => $template['button_bg_color'], + ':button_text_color' => $template['button_text_color'], + ':button_label' => $template['button_label'], + ':status' => $template['status'], + ':client_specific_fields' => $template['client_specific_fields'], + ':idclient' => $template['idclient'], + ':clientname' => $template['clientname'], + ':schemaname' => $template['schemaname'], + ':idschema' => $template['idschema'], + ':schemajson' => $template['schemajson'], + ':xls_headers' => $template['xls_headers'], + ':api_sample_json' => $template['api_sample_json'], + ':json_nodes' => $template['json_nodes'], + ':idroutine' => $template['idroutine'] + ]); + + $newTemplateId = (int)$pdo->lastInsertId(); + + if ($newTemplateId <= 0) { + throw new Exception("Unable to create cloned template."); + } + + // 4. Clone template_mapping rows + $cloneMappings = $pdo->prepare(" + INSERT INTO template_mapping ( + template_id, + schema_id, + field_id, + excel_column, + json_node, + is_manual, + manual_default, + auto_value, + data_type, + is_required, + default_value, + has_list, + length, + decimals, + min_value, + max_value, + default_curr_date, + tablename, + field_label, + main_field, + is_visible_import, + is_visible_parts + ) + SELECT + :new_template_id, + schema_id, + field_id, + excel_column, + json_node, + is_manual, + manual_default, + auto_value, + data_type, + is_required, + default_value, + has_list, + length, + decimals, + min_value, + max_value, + default_curr_date, + tablename, + field_label, + main_field, + is_visible_import, + is_visible_parts + FROM template_mapping + WHERE template_id = :source_template_id + "); + + $cloneMappings->execute([ + ':new_template_id' => $newTemplateId, + ':source_template_id' => $sourceTemplateId + ]); + + $pdo->commit(); + + header("Location: templates_dashboard.php?cloned=1&new_id=" . $newTemplateId); + exit; +} catch (Exception $e) { + if (isset($pdo) && $pdo->inTransaction()) { + $pdo->rollBack(); + } + + die("Clone error: " . htmlspecialchars($e->getMessage(), ENT_QUOTES, 'UTF-8')); +} diff --git a/public/userarea/modal_annotations.php b/public/userarea/modal_annotations.php index 35f5135..47fb077 100644 --- a/public/userarea/modal_annotations.php +++ b/public/userarea/modal_annotations.php @@ -46,6 +46,16 @@