Compare commits
22 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| a626c8283e | |||
| c7416ba4a9 | |||
| 1fed113c5c | |||
| e3994d6f9f | |||
| 9af0df3cca | |||
| 48d2a3ff42 | |||
| cf44e67922 | |||
| 35021e9d9b | |||
| 497ebda65a | |||
| b0024edb70 | |||
| b9852ba226 | |||
| 407d6884a1 | |||
| 1a4beadbb2 | |||
| 4bb0445cff | |||
| 73dd8f4ce8 | |||
| bdc4e0e60c | |||
| ef8f2d8000 | |||
| 74c9a1d02a | |||
| cd3bccd183 | |||
| 78154e43a9 | |||
| 9fe9243e60 | |||
| a8330d4aba |
+6
-17
@@ -7,9 +7,9 @@ LOG_CHANNEL=stack
|
|||||||
|
|
||||||
DB_CONNECTION=mysql
|
DB_CONNECTION=mysql
|
||||||
DB_HOST="localhost"
|
DB_HOST="localhost"
|
||||||
DB_DATABASE="trfcertest"
|
DB_DATABASE="xxxx"
|
||||||
DB_USERNAME="solocla"
|
DB_USERNAME="xxxx"
|
||||||
DB_PASSWORD="xxxxxxx"
|
DB_PASSWORD="xxxxx"
|
||||||
DB_PREFIX="auth_"
|
DB_PREFIX="auth_"
|
||||||
|
|
||||||
BROADCAST_DRIVER=log
|
BROADCAST_DRIVER=log
|
||||||
@@ -41,19 +41,8 @@ MIX_PUSHER_APP_CLUSTER="${PUSHER_APP_CLUSTER}"
|
|||||||
|
|
||||||
# Credenziali API VisualLims
|
# Credenziali API VisualLims
|
||||||
SIMULATE_EXPORT_LIMS=true
|
SIMULATE_EXPORT_LIMS=true
|
||||||
API_BASE_URL=https://93.43.5.102/limsapi
|
API_BASE_URL=https://bvcpsitaly-elims.com/limsapi
|
||||||
API_USERNAME=xxxx
|
API_USERNAME=WebApiUser
|
||||||
API_PASSWORD=XXXX
|
API_PASSWORD=webapiuser01
|
||||||
|
|
||||||
BASE_URL=http://localhost:8000/userarea/
|
BASE_URL=http://localhost:8000/userarea/
|
||||||
|
|
||||||
# Credenziali ENTRAID
|
|
||||||
|
|
||||||
AZURE_CLIENT_ID=your-client-id
|
|
||||||
AZURE_CLIENT_SECRET=your-client-secret
|
|
||||||
AZURE_REDIRECT_URI=https://your-app.com/auth/azure/callback
|
|
||||||
AZURE_TENANT_ID=
|
|
||||||
|
|
||||||
MICROSOFT_CLIENT_ID=your_client_id_here
|
|
||||||
MICROSOFT_CLIENT_SECRET=your_client_secret_here
|
|
||||||
MICROSOFT_REDIRECT_URI="${APP_URL}/auth/microsoft/callback"
|
|
||||||
+31
-42
@@ -1,66 +1,55 @@
|
|||||||
.DS_Store
|
.DS_Store
|
||||||
|
|
||||||
/node_modules
|
/node_modules
|
||||||
/public/hot
|
|
||||||
/public/storage
|
|
||||||
/storage/*.key
|
|
||||||
/vendor
|
/vendor
|
||||||
|
|
||||||
/.idea
|
/.idea
|
||||||
/.fleet
|
/.fleet
|
||||||
/.vscode
|
/.vscode
|
||||||
/.vagrant
|
/.vagrant
|
||||||
Homestead.json
|
|
||||||
Homestead.yaml
|
/public/hot
|
||||||
npm-debug.log
|
/public/storage
|
||||||
yarn-error.log
|
|
||||||
.env
|
|
||||||
.phpunit.result.cache
|
|
||||||
.php_cs.cache
|
|
||||||
/documentation
|
|
||||||
/.phpunit.cache
|
|
||||||
/public/build
|
/public/build
|
||||||
|
|
||||||
|
/storage/*.key
|
||||||
|
|
||||||
|
.env
|
||||||
.env.backup
|
.env.backup
|
||||||
.env.production
|
.env.production
|
||||||
auth.json
|
auth.json
|
||||||
# File di debug e temporanei JSON e log
|
|
||||||
|
.phpunit.result.cache
|
||||||
|
.php_cs.cache
|
||||||
|
/.phpunit.cache
|
||||||
|
|
||||||
|
npm-debug.log
|
||||||
|
yarn-error.log
|
||||||
|
|
||||||
|
/documentation
|
||||||
|
|
||||||
|
# --- Runtime / Debug (userarea) ---
|
||||||
/public/userarea/*.json
|
/public/userarea/*.json
|
||||||
/public/userarea/*.log
|
/public/userarea/*.log
|
||||||
/public/userarea/*.txt
|
/public/userarea/*.txt
|
||||||
|
/public/userarea/*_response.json
|
||||||
# File di log nella sottocartella class
|
/public/userarea/error_log.txt
|
||||||
|
/public/userarea/import_debug.log
|
||||||
|
/public/userarea/last_url.txt
|
||||||
|
/public/userarea/logaspi/
|
||||||
|
/public/userarea/logsapi/
|
||||||
|
/public/userarea/photostrf/
|
||||||
/public/userarea/class/*.log
|
/public/userarea/class/*.log
|
||||||
|
/public/userarea/class/curl_auth_debug.log
|
||||||
|
/public/userarea/class/curl_request_debug.log
|
||||||
|
|
||||||
# File XLSX temporanei importati
|
# File XLSX temporanei importati
|
||||||
/public/userarea/imported_trf/*.xlsx
|
/public/userarea/imported_trf/*.xlsx
|
||||||
/public/userarea/xlstemplates/*.xlsx
|
/public/userarea/xlstemplates/*.xlsx
|
||||||
|
|
||||||
# Ignora cartelle di foto generate
|
# Cartelle foto generate
|
||||||
/public/photostrf/
|
/public/photostrf/
|
||||||
/public/photostrf/qrcodes/
|
/public/photostrf/qrcodes/
|
||||||
public/userarea/import_debug.log
|
|
||||||
public/userarea/last_url.txt
|
|
||||||
public/userarea/class/curl_auth_debug.log
|
|
||||||
public/userarea/class/curl_request_debug.log
|
|
||||||
public/userarea/last_url.txt
|
|
||||||
public/userarea/class/curl_auth_debug.log
|
|
||||||
public/userarea/class/curl_request_debug.log
|
|
||||||
|
|
||||||
# Ignora tutti i log
|
# Ignora tutti i log ovunque
|
||||||
*.log
|
*.log
|
||||||
|
|
||||||
|
|
||||||
# Ignora cartella photostrf in public/userarea
|
|
||||||
/public/userarea/photostrf/
|
|
||||||
public/userarea/customfield_values_response.json
|
|
||||||
/public/userarea/logaspi/
|
|
||||||
|
|
||||||
public/userarea/logsapi/campione_762_1.json
|
|
||||||
public/userarea/logsapi/campione_763_1.json
|
|
||||||
public/userarea/logsapi/campione_762_2.json
|
|
||||||
public/userarea/logsapi/campione_763_2.json
|
|
||||||
public/userarea/logsapi/commessaweb_create_762.json
|
|
||||||
public/userarea/logsapi/commessaweb_create_763.json
|
|
||||||
public/userarea/logsapi/commessaweb_customfields_762.json
|
|
||||||
public/userarea/logsapi/commessaweb_customfields_763.json
|
|
||||||
public/userarea/logsapi/commessaweb_invia_762.json
|
|
||||||
public/userarea/logsapi/commessaweb_invia_763.json
|
|
||||||
public/userarea/logsapi/last_auth_url.txt
|
|
||||||
|
|||||||
@@ -0,0 +1,91 @@
|
|||||||
|
<?php
|
||||||
|
header('Content-Type: application/json');
|
||||||
|
|
||||||
|
ini_set('display_errors', 1);
|
||||||
|
error_reporting(E_ALL);
|
||||||
|
|
||||||
|
require_once(__DIR__ . '/include/headscript.php'); // Auth + $iduserlogin + DBHandlerSelect
|
||||||
|
|
||||||
|
try {
|
||||||
|
if ($_SERVER['REQUEST_METHOD'] !== 'POST') {
|
||||||
|
throw new Exception('Invalid request method');
|
||||||
|
}
|
||||||
|
|
||||||
|
$iddatadb = isset($_POST['iddatadb']) ? (int)$_POST['iddatadb'] : 0;
|
||||||
|
|
||||||
|
if ($iddatadb <= 0) {
|
||||||
|
throw new Exception('Missing iddatadb');
|
||||||
|
}
|
||||||
|
|
||||||
|
// ✅ Supporta sia singola stringa (con |) che array part_descriptions[]
|
||||||
|
$parts = [];
|
||||||
|
|
||||||
|
if (isset($_POST['part_descriptions']) && is_array($_POST['part_descriptions'])) {
|
||||||
|
$parts = $_POST['part_descriptions'];
|
||||||
|
} else {
|
||||||
|
$raw = isset($_POST['part_description']) ? (string)$_POST['part_description'] : '';
|
||||||
|
// split su "|" per multi-part
|
||||||
|
$parts = preg_split('/\s*\|\s*/', $raw);
|
||||||
|
}
|
||||||
|
|
||||||
|
// normalizza: trim + rimuovi vuoti + dedup
|
||||||
|
$cleanParts = [];
|
||||||
|
foreach ($parts as $p) {
|
||||||
|
$p = trim((string)$p);
|
||||||
|
if ($p === '') continue;
|
||||||
|
|
||||||
|
// limita lunghezza come da DB varchar(255)
|
||||||
|
if (mb_strlen($p) > 255) {
|
||||||
|
$p = mb_substr($p, 0, 255);
|
||||||
|
}
|
||||||
|
$cleanParts[] = $p;
|
||||||
|
}
|
||||||
|
$cleanParts = array_values(array_unique($cleanParts));
|
||||||
|
|
||||||
|
if (count($cleanParts) === 0) {
|
||||||
|
throw new Exception('Part description is empty');
|
||||||
|
}
|
||||||
|
|
||||||
|
$db = DBHandlerSelect::getInstance();
|
||||||
|
$pdo = $db->getConnection();
|
||||||
|
|
||||||
|
$pdo->beginTransaction();
|
||||||
|
|
||||||
|
try {
|
||||||
|
// prende il prossimo part_number per iddatadb
|
||||||
|
$stmtMax = $pdo->prepare("SELECT COALESCE(MAX(part_number), 0) AS maxnum FROM identification_parts WHERE iddatadb = ?");
|
||||||
|
$stmtMax->execute([$iddatadb]);
|
||||||
|
$nextNumber = (int)$stmtMax->fetchColumn() + 1;
|
||||||
|
|
||||||
|
$stmtIns = $pdo->prepare("
|
||||||
|
INSERT INTO identification_parts (iddatadb, part_number, part_description)
|
||||||
|
VALUES (?, ?, ?)
|
||||||
|
");
|
||||||
|
|
||||||
|
$insertedIds = [];
|
||||||
|
|
||||||
|
foreach ($cleanParts as $p) {
|
||||||
|
$ok = $stmtIns->execute([$iddatadb, $nextNumber, $p]);
|
||||||
|
if (!$ok) {
|
||||||
|
throw new Exception('Insert failed');
|
||||||
|
}
|
||||||
|
$insertedIds[] = $pdo->lastInsertId();
|
||||||
|
$nextNumber++;
|
||||||
|
}
|
||||||
|
|
||||||
|
$pdo->commit();
|
||||||
|
|
||||||
|
echo json_encode([
|
||||||
|
'success' => true,
|
||||||
|
'message' => 'Parts added',
|
||||||
|
'count' => count($insertedIds),
|
||||||
|
'ids' => $insertedIds
|
||||||
|
]);
|
||||||
|
} catch (Exception $e) {
|
||||||
|
if ($pdo->inTransaction()) $pdo->rollBack();
|
||||||
|
throw $e;
|
||||||
|
}
|
||||||
|
} catch (Exception $e) {
|
||||||
|
echo json_encode(['success' => false, 'message' => $e->getMessage()]);
|
||||||
|
}
|
||||||
|
exit;
|
||||||
@@ -24,8 +24,18 @@ class VisualLimsApiClient
|
|||||||
public static function getInstance()
|
public static function getInstance()
|
||||||
{
|
{
|
||||||
if (self::$instance === null) {
|
if (self::$instance === null) {
|
||||||
|
$dotenv = Dotenv::createImmutable(dirname(__DIR__, 3));
|
||||||
|
$dotenv->load();
|
||||||
|
|
||||||
|
$simulate = ($_ENV['SIMULATE_EXPORT_LIMS'] ?? '') === 'true';
|
||||||
|
|
||||||
|
if ($simulate) {
|
||||||
|
require_once __DIR__ . '/VisualLimsApiClientMock.class.php';
|
||||||
|
self::$instance = new VisualLimsApiClientMock();
|
||||||
|
} else {
|
||||||
self::$instance = new VisualLimsApiClient();
|
self::$instance = new VisualLimsApiClient();
|
||||||
}
|
}
|
||||||
|
}
|
||||||
return self::$instance;
|
return self::$instance;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -193,6 +203,53 @@ class VisualLimsApiClient
|
|||||||
return json_decode($response, true);
|
return json_decode($response, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* POST a file as multipart/form-data (used for photo/attachment uploads).
|
||||||
|
*
|
||||||
|
* @param string $endpoint OData endpoint, e.g. "Campione(613388)/UploadCampioneFile"
|
||||||
|
* @param string $filePath Absolute path to the file on disk
|
||||||
|
* @param string $fileName Original file name to send
|
||||||
|
* @return array|null Decoded JSON response
|
||||||
|
*/
|
||||||
|
public function postMultipart($endpoint, $filePath, $fileName)
|
||||||
|
{
|
||||||
|
$token = $this->getToken();
|
||||||
|
$url = "{$this->baseUrl}/api/odata/{$endpoint}";
|
||||||
|
|
||||||
|
$cfile = new CURLFile($filePath, mime_content_type($filePath) ?: 'application/octet-stream', $fileName);
|
||||||
|
|
||||||
|
$payload = [
|
||||||
|
'file' => $cfile,
|
||||||
|
];
|
||||||
|
|
||||||
|
$ch = curl_init($url);
|
||||||
|
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
|
||||||
|
curl_setopt($ch, CURLOPT_POST, true);
|
||||||
|
curl_setopt($ch, CURLOPT_POSTFIELDS, $payload);
|
||||||
|
curl_setopt($ch, CURLOPT_HTTPHEADER, [
|
||||||
|
"Authorization: Bearer {$token}",
|
||||||
|
"Accept: application/json",
|
||||||
|
// Content-Type is set automatically to multipart/form-data by cURL
|
||||||
|
]);
|
||||||
|
curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);
|
||||||
|
curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, false);
|
||||||
|
|
||||||
|
$response = curl_exec($ch);
|
||||||
|
$http_code = curl_getinfo($ch, CURLINFO_HTTP_CODE);
|
||||||
|
$curl_error = curl_error($ch);
|
||||||
|
curl_close($ch);
|
||||||
|
|
||||||
|
if ($response === false) {
|
||||||
|
throw new Exception("Errore nella richiesta POST multipart: {$curl_error}");
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($http_code < 200 || $http_code >= 300) {
|
||||||
|
throw new Exception("POST multipart fallito: HTTP {$http_code}, Risposta: " . substr($response, 0, 1000));
|
||||||
|
}
|
||||||
|
|
||||||
|
return json_decode($response, true);
|
||||||
|
}
|
||||||
|
|
||||||
public function getBaseUrl()
|
public function getBaseUrl()
|
||||||
{
|
{
|
||||||
return $this->baseUrl;
|
return $this->baseUrl;
|
||||||
|
|||||||
@@ -0,0 +1,135 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Mock implementation of VisualLimsApiClient.
|
||||||
|
* Activated when SIMULATE_EXPORT_LIMS=true in .env.
|
||||||
|
* All HTTP calls are skipped; fake but structurally valid data is returned.
|
||||||
|
* Every simulated call is logged via error_log() with a [SIMULATE] prefix.
|
||||||
|
*/
|
||||||
|
class VisualLimsApiClientMock
|
||||||
|
{
|
||||||
|
private int $fakeCommessaId;
|
||||||
|
|
||||||
|
public function __construct()
|
||||||
|
{
|
||||||
|
// Stable fake ID for the lifetime of a single request
|
||||||
|
$this->fakeCommessaId = mt_rand(90001, 99999);
|
||||||
|
error_log("[SIMULATE] VisualLimsApiClientMock initialised (fakeCommessaId={$this->fakeCommessaId})");
|
||||||
|
}
|
||||||
|
|
||||||
|
public function get(string $endpoint): array
|
||||||
|
{
|
||||||
|
error_log("[SIMULATE] GET {$endpoint}");
|
||||||
|
|
||||||
|
// --- Fixed-field dropdown lists ---
|
||||||
|
|
||||||
|
if (str_starts_with($endpoint, 'MoltiplicatorePrezzi')) {
|
||||||
|
return ['value' => [
|
||||||
|
['IdMoltiplicatorePrezzo' => 1, 'Codice' => 'MP-01', 'Descrizione' => 'Standard (1x)'],
|
||||||
|
['IdMoltiplicatorePrezzo' => 2, 'Codice' => 'MP-02', 'Descrizione' => 'Urgente (1.5x)'],
|
||||||
|
['IdMoltiplicatorePrezzo' => 3, 'Codice' => 'MP-03', 'Descrizione' => 'Extra Urgente (2x)'],
|
||||||
|
]];
|
||||||
|
}
|
||||||
|
|
||||||
|
if (str_starts_with($endpoint, 'AnagraficaCertestObject')) {
|
||||||
|
return ['value' => [
|
||||||
|
['IdAnagrafica' => 1, 'Codice' => 'OBJ-01', 'NomeAnagrafica' => 'Articolo Tessile'],
|
||||||
|
['IdAnagrafica' => 2, 'Codice' => 'OBJ-02', 'NomeAnagrafica' => 'Componente Meccanico'],
|
||||||
|
['IdAnagrafica' => 3, 'Codice' => 'OBJ-03', 'NomeAnagrafica' => 'Materiale Plastico'],
|
||||||
|
]];
|
||||||
|
}
|
||||||
|
|
||||||
|
if (str_starts_with($endpoint, 'AnagraficaCertestService')) {
|
||||||
|
return ['value' => [
|
||||||
|
['IdAnagrafica' => 1, 'Codice' => 'SRV-01', 'NomeAnagrafica' => 'Analisi Chimica'],
|
||||||
|
['IdAnagrafica' => 2, 'Codice' => 'SRV-02', 'NomeAnagrafica' => 'Test Meccanico'],
|
||||||
|
['IdAnagrafica' => 3, 'Codice' => 'SRV-03', 'NomeAnagrafica' => 'Prova Ambientale'],
|
||||||
|
]];
|
||||||
|
}
|
||||||
|
|
||||||
|
// Cliente? list — get_clienti.php exits early in simulate mode, but guard here too
|
||||||
|
if (str_starts_with($endpoint, 'Cliente?')) {
|
||||||
|
return ['value' => []];
|
||||||
|
}
|
||||||
|
|
||||||
|
// Cliente(N)?$expand=Responsabili
|
||||||
|
if (str_starts_with($endpoint, 'Cliente(')) {
|
||||||
|
preg_match('/Cliente\((\d+)\)/', $endpoint, $m);
|
||||||
|
$clienteId = isset($m[1]) ? (int) $m[1] : 0;
|
||||||
|
return [
|
||||||
|
'IdCliente' => $clienteId,
|
||||||
|
'Responsabili' => [
|
||||||
|
['IdClienteResponsabile' => 1, 'Nominativo' => 'Marco Bianchi'],
|
||||||
|
['IdClienteResponsabile' => 2, 'Nominativo' => 'Giulia Ferrari'],
|
||||||
|
['IdClienteResponsabile' => 3, 'Nominativo' => 'Andrea Russo'],
|
||||||
|
],
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
// --- CustomField dropdown values (get_customfield_values.php) ---
|
||||||
|
|
||||||
|
if (str_starts_with($endpoint, 'CustomField(')) {
|
||||||
|
preg_match('/CustomField\((\d+)\)/', $endpoint, $m);
|
||||||
|
$fieldId = isset($m[1]) ? (int) $m[1] : 0;
|
||||||
|
return [
|
||||||
|
'CustomFieldsValues' => [
|
||||||
|
['IdCustomFieldsValue' => $fieldId * 10 + 1, 'Valore' => 'Opzione A'],
|
||||||
|
['IdCustomFieldsValue' => $fieldId * 10 + 2, 'Valore' => 'Opzione B'],
|
||||||
|
['IdCustomFieldsValue' => $fieldId * 10 + 3, 'Valore' => 'Opzione C'],
|
||||||
|
],
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
// --- CommessaWeb OData calls (STEP 7 GET + STEP 10 verification) ---
|
||||||
|
|
||||||
|
preg_match('/\((\d+)\)/', $endpoint, $m);
|
||||||
|
$id = isset($m[1]) ? (int) $m[1] : $this->fakeCommessaId;
|
||||||
|
|
||||||
|
return [
|
||||||
|
'IdCommessa' => $id,
|
||||||
|
'CodiceCommessa' => "SIM-{$id}",
|
||||||
|
'CommesseCustomFields' => [], // Empty → PATCH step is skipped correctly
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
public function post(string $endpoint, array $payload): array
|
||||||
|
{
|
||||||
|
error_log("[SIMULATE] POST {$endpoint} payload=" . json_encode($payload));
|
||||||
|
|
||||||
|
// CommessaWeb creation
|
||||||
|
if ($endpoint === 'CommessaWeb') {
|
||||||
|
return [
|
||||||
|
'IdCommessa' => $this->fakeCommessaId,
|
||||||
|
'CodiceCommessa' => "SIM-{$this->fakeCommessaId}",
|
||||||
|
'Richiedente' => $payload['Richiedente'] ?? '',
|
||||||
|
'Descrizione' => $payload['Descrizione'] ?? '',
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
// Campione creation
|
||||||
|
if ($endpoint === 'Campione') {
|
||||||
|
return [
|
||||||
|
'IdCampione' => mt_rand(10001, 19999),
|
||||||
|
'Commessa' => $payload['Commessa'] ?? null,
|
||||||
|
'Matrice' => $payload['Matrice'] ?? null,
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
// InviaCommessa / ImportaCommessa (currently commented out upstream)
|
||||||
|
return ['simulated' => true, 'endpoint' => $endpoint];
|
||||||
|
}
|
||||||
|
|
||||||
|
public function patch(string $endpoint, array $payload): array
|
||||||
|
{
|
||||||
|
error_log("[SIMULATE] PATCH {$endpoint} payload=" . json_encode($payload));
|
||||||
|
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
|
||||||
|
public function postMultipart(string $endpoint, string $filePath, string $fileName): array
|
||||||
|
{
|
||||||
|
error_log("[SIMULATE] POST multipart {$endpoint} file={$fileName}");
|
||||||
|
|
||||||
|
return ['simulated' => true, 'file' => $fileName];
|
||||||
|
}
|
||||||
|
}
|
||||||
File diff suppressed because one or more lines are too long
@@ -27,3 +27,288 @@
|
|||||||
2026-01-30 10:50:43 [AnagraficaCertestService] Autenticazione fallita: HTTP 400, Errore cURL: , Risposta: {"title":"Bad Request","status":400,"detail":"Cannot persist the object. It was modified or deleted (purged) by another application.","instance":"POST /api/authentication/authenticate","errorCode":"96bfc1252b"}
|
2026-01-30 10:50:43 [AnagraficaCertestService] Autenticazione fallita: HTTP 400, Errore cURL: , Risposta: {"title":"Bad Request","status":400,"detail":"Cannot persist the object. It was modified or deleted (purged) by another application.","instance":"POST /api/authentication/authenticate","errorCode":"96bfc1252b"}
|
||||||
2026-01-30 10:50:43 [MoltiplicatorePrezzo] Autenticazione fallita: HTTP 400, Errore cURL: , Risposta: {"title":"Bad Request","status":400,"detail":"Cannot persist the object. It was modified or deleted (purged) by another application.","instance":"POST /api/authentication/authenticate","errorCode":"96bfc1252b"}
|
2026-01-30 10:50:43 [MoltiplicatorePrezzo] Autenticazione fallita: HTTP 400, Errore cURL: , Risposta: {"title":"Bad Request","status":400,"detail":"Cannot persist the object. It was modified or deleted (purged) by another application.","instance":"POST /api/authentication/authenticate","errorCode":"96bfc1252b"}
|
||||||
2026-01-30 11:09:22 [MoltiplicatorePrezzo] Autenticazione fallita: HTTP 400, Errore cURL: , Risposta: {"title":"Bad Request","status":400,"detail":"Cannot persist the object. It was modified or deleted (purged) by another application.","instance":"POST /api/authentication/authenticate","errorCode":"96bfc1252b"}
|
2026-01-30 11:09:22 [MoltiplicatorePrezzo] Autenticazione fallita: HTTP 400, Errore cURL: , Risposta: {"title":"Bad Request","status":400,"detail":"Cannot persist the object. It was modified or deleted (purged) by another application.","instance":"POST /api/authentication/authenticate","errorCode":"96bfc1252b"}
|
||||||
|
2026-02-19 10:00:13 [MoltiplicatorePrezzo] Autenticazione fallita: HTTP 400, Errore cURL: , Risposta: {"title":"Bad Request","status":400,"detail":"Cannot persist the object. It was modified or deleted (purged) by another application.","instance":"POST /api/authentication/authenticate","errorCode":"96bfc1252b"}
|
||||||
|
2026-02-19 10:00:42 [AnagraficaCertestObject] Autenticazione fallita: HTTP 400, Errore cURL: , Risposta: {"title":"Bad Request","status":400,"detail":"Cannot persist the object. It was modified or deleted (purged) by another application.","instance":"POST /api/authentication/authenticate","errorCode":"96bfc1252b"}
|
||||||
|
2026-02-19 10:00:44 [MoltiplicatorePrezzo] Autenticazione fallita: HTTP 400, Errore cURL: , Risposta: {"title":"Bad Request","status":400,"detail":"Cannot persist the object. It was modified or deleted (purged) by another application.","instance":"POST /api/authentication/authenticate","errorCode":"96bfc1252b"}
|
||||||
|
2026-02-23 10:44:04 - Autenticazione fallita: HTTP 404, Errore cURL: , Risposta: <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN""http://www.w3.org/TR/html4/strict.dtd">
|
||||||
|
<HTML><HEAD><TITLE>Not Found</TITLE>
|
||||||
|
<META HTTP-EQUIV="Content-Type" Content="text/html; charset=us-ascii"></HEAD>
|
||||||
|
<BODY><h2>Not Found</h2>
|
||||||
|
<hr><p>HTTP Error 404. The requested resource is not found.</p>
|
||||||
|
</BODY></HTML>
|
||||||
|
|
||||||
|
2026-02-23 10:44:42 [AnagraficaCertestService] Autenticazione fallita: HTTP 404, Errore cURL: , Risposta: <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN""http://www.w3.org/TR/html4/strict.dtd">
|
||||||
|
<HTML><HEAD><TITLE>Not Found</TITLE>
|
||||||
|
<META HTTP-EQUIV="Content-Type" Content="text/html; charset=us-ascii"></HEAD>
|
||||||
|
<BODY><h2>Not Found</h2>
|
||||||
|
<hr><p>HTTP Error 404. The requested resource is not found.</p>
|
||||||
|
</BODY></HTML>
|
||||||
|
|
||||||
|
2026-02-23 10:44:42 [AnagraficaCertestObject] Autenticazione fallita: HTTP 404, Errore cURL: , Risposta: <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN""http://www.w3.org/TR/html4/strict.dtd">
|
||||||
|
<HTML><HEAD><TITLE>Not Found</TITLE>
|
||||||
|
<META HTTP-EQUIV="Content-Type" Content="text/html; charset=us-ascii"></HEAD>
|
||||||
|
<BODY><h2>Not Found</h2>
|
||||||
|
<hr><p>HTTP Error 404. The requested resource is not found.</p>
|
||||||
|
</BODY></HTML>
|
||||||
|
|
||||||
|
2026-02-23 10:44:42 [MoltiplicatorePrezzo] Autenticazione fallita: HTTP 404, Errore cURL: , Risposta: <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN""http://www.w3.org/TR/html4/strict.dtd">
|
||||||
|
<HTML><HEAD><TITLE>Not Found</TITLE>
|
||||||
|
<META HTTP-EQUIV="Content-Type" Content="text/html; charset=us-ascii"></HEAD>
|
||||||
|
<BODY><h2>Not Found</h2>
|
||||||
|
<hr><p>HTTP Error 404. The requested resource is not found.</p>
|
||||||
|
</BODY></HTML>
|
||||||
|
|
||||||
|
2026-02-23 10:44:42 [AnagraficaCertestService] Autenticazione fallita: HTTP 404, Errore cURL: , Risposta: <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN""http://www.w3.org/TR/html4/strict.dtd">
|
||||||
|
<HTML><HEAD><TITLE>Not Found</TITLE>
|
||||||
|
<META HTTP-EQUIV="Content-Type" Content="text/html; charset=us-ascii"></HEAD>
|
||||||
|
<BODY><h2>Not Found</h2>
|
||||||
|
<hr><p>HTTP Error 404. The requested resource is not found.</p>
|
||||||
|
</BODY></HTML>
|
||||||
|
|
||||||
|
2026-02-23 10:44:42 [AnagraficaCertestObject] Autenticazione fallita: HTTP 404, Errore cURL: , Risposta: <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN""http://www.w3.org/TR/html4/strict.dtd">
|
||||||
|
<HTML><HEAD><TITLE>Not Found</TITLE>
|
||||||
|
<META HTTP-EQUIV="Content-Type" Content="text/html; charset=us-ascii"></HEAD>
|
||||||
|
<BODY><h2>Not Found</h2>
|
||||||
|
<hr><p>HTTP Error 404. The requested resource is not found.</p>
|
||||||
|
</BODY></HTML>
|
||||||
|
|
||||||
|
2026-02-23 10:44:42 [MoltiplicatorePrezzo] Autenticazione fallita: HTTP 404, Errore cURL: , Risposta: <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN""http://www.w3.org/TR/html4/strict.dtd">
|
||||||
|
<HTML><HEAD><TITLE>Not Found</TITLE>
|
||||||
|
<META HTTP-EQUIV="Content-Type" Content="text/html; charset=us-ascii"></HEAD>
|
||||||
|
<BODY><h2>Not Found</h2>
|
||||||
|
<hr><p>HTTP Error 404. The requested resource is not found.</p>
|
||||||
|
</BODY></HTML>
|
||||||
|
|
||||||
|
2026-02-23 10:44:42 [AnagraficaCertestService] Autenticazione fallita: HTTP 404, Errore cURL: , Risposta: <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN""http://www.w3.org/TR/html4/strict.dtd">
|
||||||
|
<HTML><HEAD><TITLE>Not Found</TITLE>
|
||||||
|
<META HTTP-EQUIV="Content-Type" Content="text/html; charset=us-ascii"></HEAD>
|
||||||
|
<BODY><h2>Not Found</h2>
|
||||||
|
<hr><p>HTTP Error 404. The requested resource is not found.</p>
|
||||||
|
</BODY></HTML>
|
||||||
|
|
||||||
|
2026-02-23 10:44:42 [AnagraficaCertestObject] Autenticazione fallita: HTTP 404, Errore cURL: , Risposta: <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN""http://www.w3.org/TR/html4/strict.dtd">
|
||||||
|
<HTML><HEAD><TITLE>Not Found</TITLE>
|
||||||
|
<META HTTP-EQUIV="Content-Type" Content="text/html; charset=us-ascii"></HEAD>
|
||||||
|
<BODY><h2>Not Found</h2>
|
||||||
|
<hr><p>HTTP Error 404. The requested resource is not found.</p>
|
||||||
|
</BODY></HTML>
|
||||||
|
|
||||||
|
2026-02-23 10:44:42 [MoltiplicatorePrezzo] Autenticazione fallita: HTTP 404, Errore cURL: , Risposta: <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN""http://www.w3.org/TR/html4/strict.dtd">
|
||||||
|
<HTML><HEAD><TITLE>Not Found</TITLE>
|
||||||
|
<META HTTP-EQUIV="Content-Type" Content="text/html; charset=us-ascii"></HEAD>
|
||||||
|
<BODY><h2>Not Found</h2>
|
||||||
|
<hr><p>HTTP Error 404. The requested resource is not found.</p>
|
||||||
|
</BODY></HTML>
|
||||||
|
|
||||||
|
2026-02-23 10:44:43 [MoltiplicatorePrezzo] Autenticazione fallita: HTTP 404, Errore cURL: , Risposta: <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN""http://www.w3.org/TR/html4/strict.dtd">
|
||||||
|
<HTML><HEAD><TITLE>Not Found</TITLE>
|
||||||
|
<META HTTP-EQUIV="Content-Type" Content="text/html; charset=us-ascii"></HEAD>
|
||||||
|
<BODY><h2>Not Found</h2>
|
||||||
|
<hr><p>HTTP Error 404. The requested resource is not found.</p>
|
||||||
|
</BODY></HTML>
|
||||||
|
|
||||||
|
2026-02-23 10:44:43 [AnagraficaCertestObject] Autenticazione fallita: HTTP 404, Errore cURL: , Risposta: <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN""http://www.w3.org/TR/html4/strict.dtd">
|
||||||
|
<HTML><HEAD><TITLE>Not Found</TITLE>
|
||||||
|
<META HTTP-EQUIV="Content-Type" Content="text/html; charset=us-ascii"></HEAD>
|
||||||
|
<BODY><h2>Not Found</h2>
|
||||||
|
<hr><p>HTTP Error 404. The requested resource is not found.</p>
|
||||||
|
</BODY></HTML>
|
||||||
|
|
||||||
|
2026-02-23 10:44:43 [AnagraficaCertestService] Autenticazione fallita: HTTP 404, Errore cURL: , Risposta: <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN""http://www.w3.org/TR/html4/strict.dtd">
|
||||||
|
<HTML><HEAD><TITLE>Not Found</TITLE>
|
||||||
|
<META HTTP-EQUIV="Content-Type" Content="text/html; charset=us-ascii"></HEAD>
|
||||||
|
<BODY><h2>Not Found</h2>
|
||||||
|
<hr><p>HTTP Error 404. The requested resource is not found.</p>
|
||||||
|
</BODY></HTML>
|
||||||
|
|
||||||
|
2026-02-23 10:44:52 [AnagraficaCertestObject] Autenticazione fallita: HTTP 404, Errore cURL: , Risposta: <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN""http://www.w3.org/TR/html4/strict.dtd">
|
||||||
|
<HTML><HEAD><TITLE>Not Found</TITLE>
|
||||||
|
<META HTTP-EQUIV="Content-Type" Content="text/html; charset=us-ascii"></HEAD>
|
||||||
|
<BODY><h2>Not Found</h2>
|
||||||
|
<hr><p>HTTP Error 404. The requested resource is not found.</p>
|
||||||
|
</BODY></HTML>
|
||||||
|
|
||||||
|
2026-02-23 10:44:52 [MoltiplicatorePrezzo] Autenticazione fallita: HTTP 404, Errore cURL: , Risposta: <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN""http://www.w3.org/TR/html4/strict.dtd">
|
||||||
|
<HTML><HEAD><TITLE>Not Found</TITLE>
|
||||||
|
<META HTTP-EQUIV="Content-Type" Content="text/html; charset=us-ascii"></HEAD>
|
||||||
|
<BODY><h2>Not Found</h2>
|
||||||
|
<hr><p>HTTP Error 404. The requested resource is not found.</p>
|
||||||
|
</BODY></HTML>
|
||||||
|
|
||||||
|
2026-02-23 10:44:52 [AnagraficaCertestService] Autenticazione fallita: HTTP 404, Errore cURL: , Risposta: <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN""http://www.w3.org/TR/html4/strict.dtd">
|
||||||
|
<HTML><HEAD><TITLE>Not Found</TITLE>
|
||||||
|
<META HTTP-EQUIV="Content-Type" Content="text/html; charset=us-ascii"></HEAD>
|
||||||
|
<BODY><h2>Not Found</h2>
|
||||||
|
<hr><p>HTTP Error 404. The requested resource is not found.</p>
|
||||||
|
</BODY></HTML>
|
||||||
|
|
||||||
|
2026-02-23 10:44:52 [AnagraficaCertestObject] Autenticazione fallita: HTTP 404, Errore cURL: , Risposta: <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN""http://www.w3.org/TR/html4/strict.dtd">
|
||||||
|
<HTML><HEAD><TITLE>Not Found</TITLE>
|
||||||
|
<META HTTP-EQUIV="Content-Type" Content="text/html; charset=us-ascii"></HEAD>
|
||||||
|
<BODY><h2>Not Found</h2>
|
||||||
|
<hr><p>HTTP Error 404. The requested resource is not found.</p>
|
||||||
|
</BODY></HTML>
|
||||||
|
|
||||||
|
2026-02-23 10:44:52 [MoltiplicatorePrezzo] Autenticazione fallita: HTTP 404, Errore cURL: , Risposta: <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN""http://www.w3.org/TR/html4/strict.dtd">
|
||||||
|
<HTML><HEAD><TITLE>Not Found</TITLE>
|
||||||
|
<META HTTP-EQUIV="Content-Type" Content="text/html; charset=us-ascii"></HEAD>
|
||||||
|
<BODY><h2>Not Found</h2>
|
||||||
|
<hr><p>HTTP Error 404. The requested resource is not found.</p>
|
||||||
|
</BODY></HTML>
|
||||||
|
|
||||||
|
2026-02-23 10:44:52 [AnagraficaCertestService] Autenticazione fallita: HTTP 404, Errore cURL: , Risposta: <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN""http://www.w3.org/TR/html4/strict.dtd">
|
||||||
|
<HTML><HEAD><TITLE>Not Found</TITLE>
|
||||||
|
<META HTTP-EQUIV="Content-Type" Content="text/html; charset=us-ascii"></HEAD>
|
||||||
|
<BODY><h2>Not Found</h2>
|
||||||
|
<hr><p>HTTP Error 404. The requested resource is not found.</p>
|
||||||
|
</BODY></HTML>
|
||||||
|
|
||||||
|
2026-02-23 10:44:52 [AnagraficaCertestObject] Autenticazione fallita: HTTP 404, Errore cURL: , Risposta: <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN""http://www.w3.org/TR/html4/strict.dtd">
|
||||||
|
<HTML><HEAD><TITLE>Not Found</TITLE>
|
||||||
|
<META HTTP-EQUIV="Content-Type" Content="text/html; charset=us-ascii"></HEAD>
|
||||||
|
<BODY><h2>Not Found</h2>
|
||||||
|
<hr><p>HTTP Error 404. The requested resource is not found.</p>
|
||||||
|
</BODY></HTML>
|
||||||
|
|
||||||
|
2026-02-23 10:44:52 [MoltiplicatorePrezzo] Autenticazione fallita: HTTP 404, Errore cURL: , Risposta: <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN""http://www.w3.org/TR/html4/strict.dtd">
|
||||||
|
<HTML><HEAD><TITLE>Not Found</TITLE>
|
||||||
|
<META HTTP-EQUIV="Content-Type" Content="text/html; charset=us-ascii"></HEAD>
|
||||||
|
<BODY><h2>Not Found</h2>
|
||||||
|
<hr><p>HTTP Error 404. The requested resource is not found.</p>
|
||||||
|
</BODY></HTML>
|
||||||
|
|
||||||
|
2026-02-23 10:44:52 [AnagraficaCertestService] Autenticazione fallita: HTTP 404, Errore cURL: , Risposta: <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN""http://www.w3.org/TR/html4/strict.dtd">
|
||||||
|
<HTML><HEAD><TITLE>Not Found</TITLE>
|
||||||
|
<META HTTP-EQUIV="Content-Type" Content="text/html; charset=us-ascii"></HEAD>
|
||||||
|
<BODY><h2>Not Found</h2>
|
||||||
|
<hr><p>HTTP Error 404. The requested resource is not found.</p>
|
||||||
|
</BODY></HTML>
|
||||||
|
|
||||||
|
2026-02-23 10:44:52 [MoltiplicatorePrezzo] Autenticazione fallita: HTTP 404, Errore cURL: , Risposta: <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN""http://www.w3.org/TR/html4/strict.dtd">
|
||||||
|
<HTML><HEAD><TITLE>Not Found</TITLE>
|
||||||
|
<META HTTP-EQUIV="Content-Type" Content="text/html; charset=us-ascii"></HEAD>
|
||||||
|
<BODY><h2>Not Found</h2>
|
||||||
|
<hr><p>HTTP Error 404. The requested resource is not found.</p>
|
||||||
|
</BODY></HTML>
|
||||||
|
|
||||||
|
2026-02-23 10:44:52 [AnagraficaCertestObject] Autenticazione fallita: HTTP 404, Errore cURL: , Risposta: <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN""http://www.w3.org/TR/html4/strict.dtd">
|
||||||
|
<HTML><HEAD><TITLE>Not Found</TITLE>
|
||||||
|
<META HTTP-EQUIV="Content-Type" Content="text/html; charset=us-ascii"></HEAD>
|
||||||
|
<BODY><h2>Not Found</h2>
|
||||||
|
<hr><p>HTTP Error 404. The requested resource is not found.</p>
|
||||||
|
</BODY></HTML>
|
||||||
|
|
||||||
|
2026-02-23 10:44:53 [AnagraficaCertestService] Autenticazione fallita: HTTP 404, Errore cURL: , Risposta: <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN""http://www.w3.org/TR/html4/strict.dtd">
|
||||||
|
<HTML><HEAD><TITLE>Not Found</TITLE>
|
||||||
|
<META HTTP-EQUIV="Content-Type" Content="text/html; charset=us-ascii"></HEAD>
|
||||||
|
<BODY><h2>Not Found</h2>
|
||||||
|
<hr><p>HTTP Error 404. The requested resource is not found.</p>
|
||||||
|
</BODY></HTML>
|
||||||
|
|
||||||
|
2026-02-23 10:48:26 - Autenticazione fallita: HTTP 404, Errore cURL: , Risposta: <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN""http://www.w3.org/TR/html4/strict.dtd">
|
||||||
|
<HTML><HEAD><TITLE>Not Found</TITLE>
|
||||||
|
<META HTTP-EQUIV="Content-Type" Content="text/html; charset=us-ascii"></HEAD>
|
||||||
|
<BODY><h2>Not Found</h2>
|
||||||
|
<hr><p>HTTP Error 404. The requested resource is not found.</p>
|
||||||
|
</BODY></HTML>
|
||||||
|
|
||||||
|
2026-02-23 10:48:26 - Autenticazione fallita: HTTP 404, Errore cURL: , Risposta: <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN""http://www.w3.org/TR/html4/strict.dtd">
|
||||||
|
<HTML><HEAD><TITLE>Not Found</TITLE>
|
||||||
|
<META HTTP-EQUIV="Content-Type" Content="text/html; charset=us-ascii"></HEAD>
|
||||||
|
<BODY><h2>Not Found</h2>
|
||||||
|
<hr><p>HTTP Error 404. The requested resource is not found.</p>
|
||||||
|
</BODY></HTML>
|
||||||
|
|
||||||
|
2026-02-23 10:48:27 - Autenticazione fallita: HTTP 404, Errore cURL: , Risposta: <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN""http://www.w3.org/TR/html4/strict.dtd">
|
||||||
|
<HTML><HEAD><TITLE>Not Found</TITLE>
|
||||||
|
<META HTTP-EQUIV="Content-Type" Content="text/html; charset=us-ascii"></HEAD>
|
||||||
|
<BODY><h2>Not Found</h2>
|
||||||
|
<hr><p>HTTP Error 404. The requested resource is not found.</p>
|
||||||
|
</BODY></HTML>
|
||||||
|
|
||||||
|
2026-02-23 10:48:27 - Autenticazione fallita: HTTP 404, Errore cURL: , Risposta: <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN""http://www.w3.org/TR/html4/strict.dtd">
|
||||||
|
<HTML><HEAD><TITLE>Not Found</TITLE>
|
||||||
|
<META HTTP-EQUIV="Content-Type" Content="text/html; charset=us-ascii"></HEAD>
|
||||||
|
<BODY><h2>Not Found</h2>
|
||||||
|
<hr><p>HTTP Error 404. The requested resource is not found.</p>
|
||||||
|
</BODY></HTML>
|
||||||
|
|
||||||
|
2026-02-23 10:48:27 - Autenticazione fallita: HTTP 404, Errore cURL: , Risposta: <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN""http://www.w3.org/TR/html4/strict.dtd">
|
||||||
|
<HTML><HEAD><TITLE>Not Found</TITLE>
|
||||||
|
<META HTTP-EQUIV="Content-Type" Content="text/html; charset=us-ascii"></HEAD>
|
||||||
|
<BODY><h2>Not Found</h2>
|
||||||
|
<hr><p>HTTP Error 404. The requested resource is not found.</p>
|
||||||
|
</BODY></HTML>
|
||||||
|
|
||||||
|
2026-02-23 10:49:04 - Autenticazione fallita: HTTP 404, Errore cURL: , Risposta: <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN""http://www.w3.org/TR/html4/strict.dtd">
|
||||||
|
<HTML><HEAD><TITLE>Not Found</TITLE>
|
||||||
|
<META HTTP-EQUIV="Content-Type" Content="text/html; charset=us-ascii"></HEAD>
|
||||||
|
<BODY><h2>Not Found</h2>
|
||||||
|
<hr><p>HTTP Error 404. The requested resource is not found.</p>
|
||||||
|
</BODY></HTML>
|
||||||
|
|
||||||
|
2026-02-23 10:49:04 - Autenticazione fallita: HTTP 404, Errore cURL: , Risposta: <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN""http://www.w3.org/TR/html4/strict.dtd">
|
||||||
|
<HTML><HEAD><TITLE>Not Found</TITLE>
|
||||||
|
<META HTTP-EQUIV="Content-Type" Content="text/html; charset=us-ascii"></HEAD>
|
||||||
|
<BODY><h2>Not Found</h2>
|
||||||
|
<hr><p>HTTP Error 404. The requested resource is not found.</p>
|
||||||
|
</BODY></HTML>
|
||||||
|
|
||||||
|
2026-02-23 10:49:04 - Autenticazione fallita: HTTP 404, Errore cURL: , Risposta: <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN""http://www.w3.org/TR/html4/strict.dtd">
|
||||||
|
<HTML><HEAD><TITLE>Not Found</TITLE>
|
||||||
|
<META HTTP-EQUIV="Content-Type" Content="text/html; charset=us-ascii"></HEAD>
|
||||||
|
<BODY><h2>Not Found</h2>
|
||||||
|
<hr><p>HTTP Error 404. The requested resource is not found.</p>
|
||||||
|
</BODY></HTML>
|
||||||
|
|
||||||
|
2026-02-23 10:49:05 - Autenticazione fallita: HTTP 404, Errore cURL: , Risposta: <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN""http://www.w3.org/TR/html4/strict.dtd">
|
||||||
|
<HTML><HEAD><TITLE>Not Found</TITLE>
|
||||||
|
<META HTTP-EQUIV="Content-Type" Content="text/html; charset=us-ascii"></HEAD>
|
||||||
|
<BODY><h2>Not Found</h2>
|
||||||
|
<hr><p>HTTP Error 404. The requested resource is not found.</p>
|
||||||
|
</BODY></HTML>
|
||||||
|
|
||||||
|
2026-02-23 10:49:05 - Autenticazione fallita: HTTP 404, Errore cURL: , Risposta: <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN""http://www.w3.org/TR/html4/strict.dtd">
|
||||||
|
<HTML><HEAD><TITLE>Not Found</TITLE>
|
||||||
|
<META HTTP-EQUIV="Content-Type" Content="text/html; charset=us-ascii"></HEAD>
|
||||||
|
<BODY><h2>Not Found</h2>
|
||||||
|
<hr><p>HTTP Error 404. The requested resource is not found.</p>
|
||||||
|
</BODY></HTML>
|
||||||
|
|
||||||
|
2026-02-23 14:26:52 - Autenticazione fallita: HTTP 404, Errore cURL: , Risposta: <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN""http://www.w3.org/TR/html4/strict.dtd">
|
||||||
|
<HTML><HEAD><TITLE>Not Found</TITLE>
|
||||||
|
<META HTTP-EQUIV="Content-Type" Content="text/html; charset=us-ascii"></HEAD>
|
||||||
|
<BODY><h2>Not Found</h2>
|
||||||
|
<hr><p>HTTP Error 404. The requested resource is not found.</p>
|
||||||
|
</BODY></HTML>
|
||||||
|
|
||||||
|
2026-02-23 14:26:59 - Autenticazione fallita: HTTP 404, Errore cURL: , Risposta: <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN""http://www.w3.org/TR/html4/strict.dtd">
|
||||||
|
<HTML><HEAD><TITLE>Not Found</TITLE>
|
||||||
|
<META HTTP-EQUIV="Content-Type" Content="text/html; charset=us-ascii"></HEAD>
|
||||||
|
<BODY><h2>Not Found</h2>
|
||||||
|
<hr><p>HTTP Error 404. The requested resource is not found.</p>
|
||||||
|
</BODY></HTML>
|
||||||
|
|
||||||
|
2026-02-23 15:54:16 - Autenticazione fallita: HTTP 404, Errore cURL: , Risposta: <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN""http://www.w3.org/TR/html4/strict.dtd">
|
||||||
|
<HTML><HEAD><TITLE>Not Found</TITLE>
|
||||||
|
<META HTTP-EQUIV="Content-Type" Content="text/html; charset=us-ascii"></HEAD>
|
||||||
|
<BODY><h2>Not Found</h2>
|
||||||
|
<hr><p>HTTP Error 404. The requested resource is not found.</p>
|
||||||
|
</BODY></HTML>
|
||||||
|
|
||||||
|
2026-02-23 16:22:45 - Autenticazione fallita: HTTP 404, Errore cURL: , Risposta: <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN""http://www.w3.org/TR/html4/strict.dtd">
|
||||||
|
<HTML><HEAD><TITLE>Not Found</TITLE>
|
||||||
|
<META HTTP-EQUIV="Content-Type" Content="text/html; charset=us-ascii"></HEAD>
|
||||||
|
<BODY><h2>Not Found</h2>
|
||||||
|
<hr><p>HTTP Error 404. The requested resource is not found.</p>
|
||||||
|
</BODY></HTML>
|
||||||
|
|
||||||
|
2026-02-23 16:32:17 - Autenticazione fallita: HTTP 404, Errore cURL: , Risposta: <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN""http://www.w3.org/TR/html4/strict.dtd">
|
||||||
|
<HTML><HEAD><TITLE>Not Found</TITLE>
|
||||||
|
<META HTTP-EQUIV="Content-Type" Content="text/html; charset=us-ascii"></HEAD>
|
||||||
|
<BODY><h2>Not Found</h2>
|
||||||
|
<hr><p>HTTP Error 404. The requested resource is not found.</p>
|
||||||
|
</BODY></HTML>
|
||||||
|
|
||||||
|
2026-02-25 15:23:12 [AnagraficaCertestObject] Autenticazione fallita: HTTP 400, Errore cURL: , Risposta: {"title":"Bad Request","status":400,"detail":"Cannot persist the object. It was modified or deleted (purged) by another application.","instance":"POST /api/authentication/authenticate","errorCode":"96bfc1252b"}
|
||||||
|
2026-02-26 15:01:32 [AnagraficaCertestObject] Autenticazione fallita: HTTP 400, Errore cURL: , Risposta: {"title":"Bad Request","status":400,"detail":"Cannot persist the object. It was modified or deleted (purged) by another application.","instance":"POST /api/authentication/authenticate","errorCode":"96bfc1252b"}
|
||||||
|
|||||||
+114
-132
@@ -1,7 +1,6 @@
|
|||||||
document.addEventListener("DOMContentLoaded", () => {
|
document.addEventListener("DOMContentLoaded", () => {
|
||||||
console.log("export_to_lims.js loaded");
|
console.log("export_to_lims.js loaded");
|
||||||
|
|
||||||
// Debug: verifica che i pulsanti siano trovati
|
|
||||||
const exportButtons = document.querySelectorAll(".export-lims-btn");
|
const exportButtons = document.querySelectorAll(".export-lims-btn");
|
||||||
console.log(`Found ${exportButtons.length} export-lims-btn buttons`);
|
console.log(`Found ${exportButtons.length} export-lims-btn buttons`);
|
||||||
|
|
||||||
@@ -10,20 +9,24 @@ document.addEventListener("DOMContentLoaded", () => {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
exportButtons.forEach((btn) => {
|
// Tracks the active confirm handler so it can be replaced on re-open
|
||||||
btn.addEventListener("click", (e) => {
|
let pendingConfirmHandler = null;
|
||||||
e.preventDefault();
|
|
||||||
const rowIndex = btn.dataset.row;
|
|
||||||
const iddatadb = btn.dataset.iddatadb;
|
|
||||||
console.log(
|
|
||||||
`Export to LIMS clicked for row ${rowIndex}, iddatadb: ${iddatadb}`,
|
|
||||||
);
|
|
||||||
|
|
||||||
// Mostra il modale di conferma
|
// ── Helpers ──────────────────────────────────────────────────────────────
|
||||||
const confirmModalElement =
|
|
||||||
document.getElementById("exportConfirmModal");
|
function cleanupBackdrop() {
|
||||||
|
document.querySelectorAll(".modal-backdrop").forEach((b) => b.remove());
|
||||||
|
document.body.classList.remove("modal-open");
|
||||||
|
document.body.style.paddingRight = "";
|
||||||
|
const overlay = document.querySelector(".overlay.toggle-icon");
|
||||||
|
if (overlay) overlay.style.display = "none";
|
||||||
|
}
|
||||||
|
|
||||||
|
// ── Step 2: show export-confirm modal, send on "Conferma" ────────────────
|
||||||
|
|
||||||
|
function startExportConfirmFlow(iddatadb, btn) {
|
||||||
|
const confirmModalElement = document.getElementById("exportConfirmModal");
|
||||||
if (!confirmModalElement) {
|
if (!confirmModalElement) {
|
||||||
console.error("exportConfirmModal not found in the DOM");
|
|
||||||
alert("Errore: Modale di conferma non trovato");
|
alert("Errore: Modale di conferma non trovato");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -34,16 +37,15 @@ document.addEventListener("DOMContentLoaded", () => {
|
|||||||
document.getElementById("exportIddatadb").textContent = iddatadb;
|
document.getElementById("exportIddatadb").textContent = iddatadb;
|
||||||
confirmModal.show();
|
confirmModal.show();
|
||||||
|
|
||||||
// Gestisci il click su "Conferma"
|
|
||||||
const confirmBtn = document.getElementById("exportConfirmBtn");
|
const confirmBtn = document.getElementById("exportConfirmBtn");
|
||||||
if (!confirmBtn) {
|
if (!confirmBtn) {
|
||||||
console.error("exportConfirmBtn not found in the DOM");
|
|
||||||
confirmModal.hide();
|
confirmModal.hide();
|
||||||
alert("Errore: Pulsante di conferma non trovato");
|
alert("Errore: Pulsante di conferma non trovato");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const confirmHandler = async () => {
|
const confirmHandler = async () => {
|
||||||
|
pendingConfirmHandler = null;
|
||||||
console.log(`Confirmed export for iddatadb: ${iddatadb}`);
|
console.log(`Confirmed export for iddatadb: ${iddatadb}`);
|
||||||
confirmModal.hide();
|
confirmModal.hide();
|
||||||
|
|
||||||
@@ -56,184 +58,164 @@ document.addEventListener("DOMContentLoaded", () => {
|
|||||||
body: formData,
|
body: formData,
|
||||||
});
|
});
|
||||||
if (!response.ok)
|
if (!response.ok)
|
||||||
throw new Error(
|
throw new Error(`HTTP error! status: ${response.status}`);
|
||||||
`HTTP error! status: ${response.status}`,
|
|
||||||
);
|
|
||||||
const data = await response.json();
|
const data = await response.json();
|
||||||
|
|
||||||
console.log("Export response:", data);
|
console.log("Export response:", data);
|
||||||
|
|
||||||
// Mostra il modale di risposta
|
const responseModalElement =
|
||||||
const responseModalElement = document.getElementById(
|
document.getElementById("exportResponseModal");
|
||||||
"exportResponseModal",
|
|
||||||
);
|
|
||||||
if (!responseModalElement) {
|
if (!responseModalElement) {
|
||||||
console.error(
|
|
||||||
"exportResponseModal not found in the DOM",
|
|
||||||
);
|
|
||||||
alert("Errore: Modale di risposta non trovato");
|
alert("Errore: Modale di risposta non trovato");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const responseModal = new bootstrap.Modal(
|
const responseModal = new bootstrap.Modal(
|
||||||
responseModalElement,
|
responseModalElement,
|
||||||
{
|
{ keyboard: false },
|
||||||
keyboard: false,
|
|
||||||
},
|
|
||||||
);
|
);
|
||||||
const responseMessage = document.getElementById(
|
const responseMessage = document.getElementById(
|
||||||
"exportResponseMessage",
|
"exportResponseMessage",
|
||||||
);
|
);
|
||||||
|
|
||||||
if (data.success) {
|
if (data.success) {
|
||||||
responseMessage.innerHTML = `${data.message.replace(/\n/g, "<br>")}<br>ID CommessaWeb: ${data.idcommessaweb}`;
|
responseMessage.innerHTML =
|
||||||
|
`${data.message.replace(/\n/g, "<br>")}` +
|
||||||
|
`<br>ID CommessaWeb: ${data.idcommessaweb}` +
|
||||||
|
`<br>Codice CommessaWeb: ${data.commessaweb}` +
|
||||||
|
(data.totalPhotos > 0
|
||||||
|
? `<br>Foto trovate: ${data.totalPhotos}`
|
||||||
|
: "");
|
||||||
document.getElementById(
|
document.getElementById(
|
||||||
"exportResponseModalLabel",
|
"exportResponseModalLabel",
|
||||||
).textContent = "Esportazione Completata";
|
).textContent = "Esportazione Completata";
|
||||||
responseModal.show();
|
responseModal.show();
|
||||||
|
|
||||||
// Aggiorna la UI per riflettere lo stato 'To LIMS'
|
// Update status badge
|
||||||
const statusCell = btn
|
const gridRow = btn.closest(".grid-row");
|
||||||
.closest(".grid-row")
|
const statusBadge = gridRow?.querySelector(
|
||||||
.querySelector(
|
|
||||||
'.grid-cell[data-col="status"] .status-badge',
|
'.grid-cell[data-col="status"] .status-badge',
|
||||||
);
|
);
|
||||||
if (statusCell) {
|
if (statusBadge) {
|
||||||
statusCell.classList.remove("status-i", "status-P");
|
statusBadge.classList.remove("status-i", "status-P");
|
||||||
statusCell.classList.add("status-l");
|
statusBadge.classList.add("status-l");
|
||||||
statusCell.textContent = "To LIMS";
|
statusBadge.textContent = "To LIMS";
|
||||||
}
|
}
|
||||||
|
|
||||||
// Gestisci la chiusura del modale di risposta
|
// Insert/update CommessaWeb code span
|
||||||
responseModalElement.addEventListener(
|
const statusCell = gridRow?.querySelector(
|
||||||
"hidden.bs.modal",
|
'.grid-cell[data-col="status"]',
|
||||||
() => {
|
|
||||||
console.log(
|
|
||||||
"exportResponseModal closed, cleaning up",
|
|
||||||
);
|
);
|
||||||
// Rimuovi tutti i backdrop residui
|
if (statusCell && data.commessaweb) {
|
||||||
document
|
let cwSpan =
|
||||||
.querySelectorAll(".modal-backdrop")
|
statusCell.querySelector(".commessaweb-code");
|
||||||
.forEach((backdrop) => {
|
if (!cwSpan) {
|
||||||
console.log(
|
cwSpan = document.createElement("span");
|
||||||
"Removing backdrop:",
|
cwSpan.className = "commessaweb-code";
|
||||||
backdrop,
|
cwSpan.style.cssText =
|
||||||
|
"display:block; font-size:0.75em; color:#555; margin-top:2px;";
|
||||||
|
cwSpan.title = "CommessaWeb";
|
||||||
|
const hiddenInput = statusCell.querySelector(
|
||||||
|
'input[type="hidden"]',
|
||||||
);
|
);
|
||||||
backdrop.remove();
|
hiddenInput
|
||||||
});
|
? statusCell.insertBefore(cwSpan, hiddenInput)
|
||||||
// Ripristina il body
|
: statusCell.appendChild(cwSpan);
|
||||||
document.body.classList.remove("modal-open");
|
}
|
||||||
document.body.style.paddingRight = "";
|
cwSpan.textContent = data.commessaweb;
|
||||||
// Nascondi l'overlay
|
|
||||||
const overlay = document.querySelector(
|
|
||||||
".overlay.toggle-icon",
|
|
||||||
);
|
|
||||||
if (overlay) {
|
|
||||||
overlay.style.display = "none";
|
|
||||||
}
|
}
|
||||||
},
|
|
||||||
{ once: true },
|
|
||||||
);
|
|
||||||
} else {
|
} else {
|
||||||
responseMessage.textContent = `Errore durante la generazione dei payload: ${data.message}`;
|
responseMessage.textContent = `Errore durante la generazione dei payload: ${data.message}`;
|
||||||
document.getElementById(
|
document.getElementById(
|
||||||
"exportResponseModalLabel",
|
"exportResponseModalLabel",
|
||||||
).textContent = "Errore Esportazione";
|
).textContent = "Errore Esportazione";
|
||||||
responseModal.show();
|
responseModal.show();
|
||||||
|
}
|
||||||
|
|
||||||
// Gestisci la chiusura del modale di risposta anche in caso di errore
|
|
||||||
responseModalElement.addEventListener(
|
responseModalElement.addEventListener(
|
||||||
"hidden.bs.modal",
|
"hidden.bs.modal",
|
||||||
() => {
|
cleanupBackdrop,
|
||||||
console.log(
|
|
||||||
"exportResponseModal closed, cleaning up",
|
|
||||||
);
|
|
||||||
// Rimuovi tutti i backdrop residui
|
|
||||||
document
|
|
||||||
.querySelectorAll(".modal-backdrop")
|
|
||||||
.forEach((backdrop) => {
|
|
||||||
console.log(
|
|
||||||
"Removing backdrop:",
|
|
||||||
backdrop,
|
|
||||||
);
|
|
||||||
backdrop.remove();
|
|
||||||
});
|
|
||||||
// Ripristina il body
|
|
||||||
document.body.classList.remove("modal-open");
|
|
||||||
document.body.style.paddingRight = "";
|
|
||||||
// Nascondi l'overlay
|
|
||||||
const overlay = document.querySelector(
|
|
||||||
".overlay.toggle-icon",
|
|
||||||
);
|
|
||||||
if (overlay) {
|
|
||||||
overlay.style.display = "none";
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{ once: true },
|
{ once: true },
|
||||||
);
|
);
|
||||||
}
|
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error("Export error:", error);
|
console.error("Export error:", error);
|
||||||
const responseModalElement = document.getElementById(
|
const responseModalElement =
|
||||||
"exportResponseModal",
|
document.getElementById("exportResponseModal");
|
||||||
);
|
|
||||||
if (!responseModalElement) {
|
if (!responseModalElement) {
|
||||||
console.error(
|
|
||||||
"exportResponseModal not found in the DOM",
|
|
||||||
);
|
|
||||||
alert("Errore: Modale di risposta non trovato");
|
alert("Errore: Modale di risposta non trovato");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
const responseModal = new bootstrap.Modal(
|
const responseModal = new bootstrap.Modal(
|
||||||
responseModalElement,
|
responseModalElement,
|
||||||
{
|
{ keyboard: false },
|
||||||
keyboard: false,
|
|
||||||
},
|
|
||||||
);
|
);
|
||||||
document.getElementById(
|
document.getElementById(
|
||||||
"exportResponseMessage",
|
"exportResponseMessage",
|
||||||
).textContent =
|
).textContent = `Errore: ${error.message}`;
|
||||||
`Errore durante la generazione dei payload: ${error.message}`;
|
|
||||||
document.getElementById(
|
document.getElementById(
|
||||||
"exportResponseModalLabel",
|
"exportResponseModalLabel",
|
||||||
).textContent = "Errore Esportazione";
|
).textContent = "Errore Esportazione";
|
||||||
responseModal.show();
|
responseModal.show();
|
||||||
|
|
||||||
// Gestisci la chiusura del modale di risposta in caso di errore
|
|
||||||
responseModalElement.addEventListener(
|
responseModalElement.addEventListener(
|
||||||
"hidden.bs.modal",
|
"hidden.bs.modal",
|
||||||
() => {
|
cleanupBackdrop,
|
||||||
console.log(
|
|
||||||
"exportResponseModal closed, cleaning up",
|
|
||||||
);
|
|
||||||
// Rimuovi tutti i backdrop residui
|
|
||||||
document
|
|
||||||
.querySelectorAll(".modal-backdrop")
|
|
||||||
.forEach((backdrop) => {
|
|
||||||
console.log("Removing backdrop:", backdrop);
|
|
||||||
backdrop.remove();
|
|
||||||
});
|
|
||||||
// Ripristina il body
|
|
||||||
document.body.classList.remove("modal-open");
|
|
||||||
document.body.style.paddingRight = "";
|
|
||||||
// Nascondi l'overlay
|
|
||||||
const overlay = document.querySelector(
|
|
||||||
".overlay.toggle-icon",
|
|
||||||
);
|
|
||||||
if (overlay) {
|
|
||||||
overlay.style.display = "none";
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{ once: true },
|
{ once: true },
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Rimuovi il listener dopo l'esecuzione
|
|
||||||
confirmBtn.removeEventListener("click", confirmHandler);
|
|
||||||
};
|
};
|
||||||
|
|
||||||
// Rimuovi eventuali listener precedenti
|
if (pendingConfirmHandler) {
|
||||||
confirmBtn.removeEventListener("click", confirmHandler);
|
confirmBtn.removeEventListener("click", pendingConfirmHandler);
|
||||||
confirmBtn.addEventListener("click", confirmHandler);
|
}
|
||||||
|
pendingConfirmHandler = confirmHandler;
|
||||||
|
confirmBtn.addEventListener("click", confirmHandler, { once: true });
|
||||||
|
}
|
||||||
|
|
||||||
|
// ── Step 1: check unsaved changes, save if needed, then export ───────────
|
||||||
|
|
||||||
|
exportButtons.forEach((btn) => {
|
||||||
|
btn.addEventListener("click", (e) => {
|
||||||
|
e.preventDefault();
|
||||||
|
const rowIndex = btn.dataset.row;
|
||||||
|
const iddatadb = btn.dataset.iddatadb;
|
||||||
|
console.log(
|
||||||
|
`Export to LIMS clicked for row ${rowIndex}, iddatadb: ${iddatadb}`,
|
||||||
|
);
|
||||||
|
|
||||||
|
const gridRow = btn.closest(".grid-row");
|
||||||
|
|
||||||
|
if (gridRow && gridRow.querySelector(".cell-changed")) {
|
||||||
|
const unsavedModal = new bootstrap.Modal(
|
||||||
|
document.getElementById("exportUnsavedModal"),
|
||||||
|
{ keyboard: false },
|
||||||
|
);
|
||||||
|
unsavedModal.show();
|
||||||
|
|
||||||
|
document.getElementById("saveAndExportBtn").addEventListener("click", () => {
|
||||||
|
unsavedModal.hide();
|
||||||
|
const saveBtn = gridRow.querySelector(".save-btn");
|
||||||
|
if (!saveBtn) return;
|
||||||
|
|
||||||
|
const observer = new MutationObserver(() => {
|
||||||
|
if (!gridRow.querySelector(".cell-changed")) {
|
||||||
|
observer.disconnect();
|
||||||
|
startExportConfirmFlow(iddatadb, btn);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
observer.observe(gridRow, {
|
||||||
|
subtree: true,
|
||||||
|
attributes: true,
|
||||||
|
attributeFilter: ["class"],
|
||||||
|
});
|
||||||
|
|
||||||
|
saveBtn.click();
|
||||||
|
}, { once: true });
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// No unsaved changes — go straight to export confirm
|
||||||
|
startExportConfirmFlow(iddatadb, btn);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -13,6 +13,8 @@ if (!is_dir($logDir)) {
|
|||||||
mkdir($logDir, 0755, true);
|
mkdir($logDir, 0755, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
$uploadDir = realpath(__DIR__ . '/../photostrf') . '/';
|
||||||
|
|
||||||
// 🔹 Base URL API
|
// 🔹 Base URL API
|
||||||
$apiBaseUrl = 'https://93.43.5.102/limsapi/api/odata/';
|
$apiBaseUrl = 'https://93.43.5.102/limsapi/api/odata/';
|
||||||
|
|
||||||
@@ -34,8 +36,15 @@ try {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// 🔹 STEP 1+2: Fetch Cliente ID from datadb and Schema ID from excel_templates
|
// 🔹 STEP 1+2: Fetch Cliente ID from datadb and Schema ID from excel_templates
|
||||||
|
// Also fetch fixed fields stored in datadb
|
||||||
$stmt = $pdo->prepare("
|
$stmt = $pdo->prepare("
|
||||||
SELECT d.idclient AS clienteId, et.idschema AS schemaId
|
SELECT d.idclient AS clienteId, et.idschema AS schemaId,
|
||||||
|
d.cliente_responsabile_id,
|
||||||
|
d.moltiplicatore_prezzo_id,
|
||||||
|
d.anagrafica_certest_object_id,
|
||||||
|
d.anagrafica_certest_service_id,
|
||||||
|
d.cliente_fornitore_id,
|
||||||
|
d.consegna_richiesta
|
||||||
FROM datadb as d
|
FROM datadb as d
|
||||||
INNER JOIN excel_templates as et ON d.templateid = et.id
|
INNER JOIN excel_templates as et ON d.templateid = et.id
|
||||||
WHERE d.iddatadb = :iddatadb
|
WHERE d.iddatadb = :iddatadb
|
||||||
@@ -51,6 +60,14 @@ try {
|
|||||||
$clienteId = (int) $result['clienteId'];
|
$clienteId = (int) $result['clienteId'];
|
||||||
$schemaId = (int) $result['schemaId'];
|
$schemaId = (int) $result['schemaId'];
|
||||||
|
|
||||||
|
// Extract fixed fields (nullable INTs)
|
||||||
|
$clienteResponsabile = !empty($result['cliente_responsabile_id']) ? (int) $result['cliente_responsabile_id'] : null;
|
||||||
|
$moltiplicatorePrezzo = !empty($result['moltiplicatore_prezzo_id']) ? (int) $result['moltiplicatore_prezzo_id'] : null;
|
||||||
|
$anagraficaObject = !empty($result['anagrafica_certest_object_id']) ? (int) $result['anagrafica_certest_object_id'] : null;
|
||||||
|
$anagraficaService = !empty($result['anagrafica_certest_service_id'])? (int) $result['anagrafica_certest_service_id']: null;
|
||||||
|
$clienteFornitore = !empty($result['cliente_fornitore_id'])? (int) $result['cliente_fornitore_id']: null;
|
||||||
|
$consegnaRichiesta = !empty($result['consegna_richiesta']) ? $result['consegna_richiesta'] : null;
|
||||||
|
|
||||||
// 🔹 STEP 3: Fetch Parts (including idmatrice)
|
// 🔹 STEP 3: Fetch Parts (including idmatrice)
|
||||||
$stmt = $pdo->prepare("
|
$stmt = $pdo->prepare("
|
||||||
SELECT part_number, part_description, material, color, mix, idmatrice
|
SELECT part_number, part_description, material, color, mix, idmatrice
|
||||||
@@ -93,12 +110,20 @@ try {
|
|||||||
// 🔹 Initialize API client
|
// 🔹 Initialize API client
|
||||||
$api = VisualLimsApiClient::getInstance();
|
$api = VisualLimsApiClient::getInstance();
|
||||||
|
|
||||||
// 🔹 STEP 5: Create CommessaWeb (NOT WebOrder)
|
// 🔹 STEP 5: Create CommessaWeb
|
||||||
|
// Fixed fields are sent as direct properties — they are navigation properties on Commessa
|
||||||
|
// accepted by the XAF API even though not explicitly listed in OData $metadata
|
||||||
$commessaWebPayload = [
|
$commessaWebPayload = [
|
||||||
"Cliente" => $clienteId,
|
"Cliente" => $clienteId,
|
||||||
"SchemaCustomField" => $schemaId,
|
"SchemaCustomField" => $schemaId,
|
||||||
"Richiedente" => "Test Web Import",
|
"Richiedente" => "Test Web Import", // TODO: replace with real value
|
||||||
"Descrizione" => "TEST CommessaWeb",
|
"Descrizione" => "TEST CommessaWeb", // TODO: replace with real value
|
||||||
|
"CustomerManager" => $clienteResponsabile,
|
||||||
|
"PriceMultiplier" => $moltiplicatorePrezzo,
|
||||||
|
"CertestObjectMasterData" => $anagraficaObject,
|
||||||
|
"CertestServiceMasterData" => $anagraficaService,
|
||||||
|
"CustomerSupplier" => $clienteFornitore, // PLACEHOLDER — to be implemented
|
||||||
|
// DeliveryRequest goes to Campione, not CommessaWeb
|
||||||
];
|
];
|
||||||
|
|
||||||
// Costruisci log curl-like per STEP 5
|
// Costruisci log curl-like per STEP 5
|
||||||
@@ -135,7 +160,8 @@ try {
|
|||||||
"Matrice" => $matriceId,
|
"Matrice" => $matriceId,
|
||||||
"SottoMatrice" => null,
|
"SottoMatrice" => null,
|
||||||
"SchemaCustomField" => $schemaId,
|
"SchemaCustomField" => $schemaId,
|
||||||
"NoteWeb" => $part["part_description"] ?? ""
|
"NoteWeb" => $part["part_description"] ?? "",
|
||||||
|
"DeliveryRequest" => $consegnaRichiesta,
|
||||||
];
|
];
|
||||||
|
|
||||||
// Costruisci curl-like per questo campione
|
// Costruisci curl-like per questo campione
|
||||||
@@ -162,11 +188,62 @@ try {
|
|||||||
$logFileStep6 = $logDir . "commessa_{$commessaId}_campioni_step6_" . time() . ".txt";
|
$logFileStep6 = $logDir . "commessa_{$commessaId}_campioni_step6_" . time() . ".txt";
|
||||||
file_put_contents($logFileStep6, $logContentStep6);
|
file_put_contents($logFileStep6, $logContentStep6);
|
||||||
|
|
||||||
|
// 🔹 STEP 6.1: Fetch photos linked to this iddatadb
|
||||||
|
$stmtPhotos = $pdo->prepare("
|
||||||
|
SELECT id, file_path, file_name
|
||||||
|
FROM datadb_photos
|
||||||
|
WHERE iddatadb = :iddatadb
|
||||||
|
ORDER BY id ASC
|
||||||
|
");
|
||||||
|
$stmtPhotos->execute(['iddatadb' => $iddatadb]);
|
||||||
|
$photos = $stmtPhotos->fetchAll(PDO::FETCH_ASSOC);
|
||||||
|
|
||||||
|
|
||||||
|
// 🔹 STEP 6.2: Upload photos to the first (main) Campione only
|
||||||
|
$photosUploaded = 0;
|
||||||
|
$logContentPhotos = "Photos for CommessaWeb {$commessaId} (iddatadb={$iddatadb}):\n";
|
||||||
|
$logContentPhotos .= "Total photos found: " . count($photos) . ", campioni: " . count($campioni) . "\n\n";
|
||||||
|
|
||||||
|
if (!empty($campioni) && !empty($photos)) {
|
||||||
|
$mainCampione = $campioni[0];
|
||||||
|
$campioneId = (int)($mainCampione['IdCampione'] ?? 0);
|
||||||
|
|
||||||
|
if ($campioneId > 0) {
|
||||||
|
$logContentPhotos .= "=== Campione {$campioneId} (main) ===\n";
|
||||||
|
|
||||||
|
foreach ($photos as $photo) {
|
||||||
|
$photoPath = $uploadDir . '/' . ltrim($photo['file_path'], './');
|
||||||
|
$fullPath = realpath($photoPath);
|
||||||
|
|
||||||
|
if (!$fullPath || !file_exists($fullPath)) {
|
||||||
|
$logContentPhotos .= "SKIP (file not found): {$photoPath}\n";
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
$photoEndpoint = "Campione({$campioneId})/UploadCampioneFile";
|
||||||
|
$logContentPhotos .= "curl --location --request POST '{$apiBaseUrl}{$photoEndpoint}' \\\n" .
|
||||||
|
"--header 'Authorization: Bearer ••••••' \\\n" .
|
||||||
|
"--form 'file=@{$fullPath}'\n\n";
|
||||||
|
|
||||||
|
$photoResult = $api->postMultipart($photoEndpoint, $fullPath, $photo['file_name']);
|
||||||
|
$logContentPhotos .= "RESPONSE:\n" . json_encode($photoResult, JSON_PRETTY_PRINT) . "\n\n---\n";
|
||||||
|
$photosUploaded++;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
$logContentPhotos .= "SKIP: main campione has invalid IdCampione\n";
|
||||||
|
}
|
||||||
|
} elseif (empty($campioni)) {
|
||||||
|
$logContentPhotos .= "SKIP: no campioni created, cannot upload photos\n";
|
||||||
|
}
|
||||||
|
|
||||||
|
$logFilePhotos = $logDir . "commessa_{$commessaId}_photos_step5_2_" . time() . ".txt";
|
||||||
|
file_put_contents($logFilePhotos, $logContentPhotos);
|
||||||
|
|
||||||
// 🔹 STEP 7: Update Custom Fields for CommessaWeb
|
// 🔹 STEP 7: Update Custom Fields for CommessaWeb
|
||||||
if (!empty($fieldValues)) {
|
if (!empty($fieldValues)) {
|
||||||
// GET con espansione per CustomField
|
// GET con espansione per CustomField
|
||||||
$expand = "CommesseCustomFields(\$expand=CustomField)";
|
$expand = "CommesseCustomFields(\$expand=CustomField)";
|
||||||
$commessaWithFields = $api->get("CommessaWeb(" . $commessaId . ")?\$expand=" . $expand);
|
$commessaWithFields = $api->get("CommessaWeb({$commessaId})?\$expand={$expand}");
|
||||||
|
|
||||||
// Logga il GET
|
// Logga il GET
|
||||||
$logContentGet = "curl --location --request GET '{$apiBaseUrl}CommessaWeb({$commessaId})?\$expand=CommesseCustomFields(\$expand=CustomField)' \\\n" .
|
$logContentGet = "curl --location --request GET '{$apiBaseUrl}CommessaWeb({$commessaId})?\$expand=CommesseCustomFields(\$expand=CustomField)' \\\n" .
|
||||||
@@ -195,6 +272,7 @@ try {
|
|||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
$logFileStep7 = null;
|
||||||
if (!empty($commessaCustomFields)) {
|
if (!empty($commessaCustomFields)) {
|
||||||
$updatePayload = ["CommesseCustomFields" => $commessaCustomFields];
|
$updatePayload = ["CommesseCustomFields" => $commessaCustomFields];
|
||||||
|
|
||||||
@@ -239,6 +317,20 @@ try {
|
|||||||
file_put_contents($logFileStep9, $logContentStep9);
|
file_put_contents($logFileStep9, $logContentStep9);
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
// 🔹 STEP 9.5: Importazione da CommessaWeb a Commessa (commentato come richiesto)
|
||||||
|
// Supplier call: POST api/odata/CommessaWeb(XXX)/ImportaCommessa
|
||||||
|
|
||||||
|
$importResult = $api->post("CommessaWeb({$commessaId})/ImportaCommessa", []);
|
||||||
|
|
||||||
|
// Logga il POST
|
||||||
|
$logContentStep91 = "curl --location --request POST '{$apiBaseUrl}CommessaWeb({$commessaId})/ImportaCommessa' \\\n" .
|
||||||
|
"--header 'Content-Type: application/json' \\\n" .
|
||||||
|
"--header 'Authorization: Bearer ••••••' \\\n" .
|
||||||
|
"--data '{}'\n\n" .
|
||||||
|
"RESPONSE:\n" . json_encode($importResult, JSON_PRETTY_PRINT);
|
||||||
|
$logFileStep91 = $logDir . "commessa_{$commessaId}_importa_step91_" . time() . ".txt";
|
||||||
|
file_put_contents($logFileStep91, $logContentStep91);
|
||||||
|
|
||||||
// 🔹 STEP 10: GET di controllo post-PATCH
|
// 🔹 STEP 10: GET di controllo post-PATCH
|
||||||
$expand = "CommesseCustomFields(\$expand=CustomField)";
|
$expand = "CommesseCustomFields(\$expand=CustomField)";
|
||||||
$commessaAfterPatch = $api->get("CommessaWeb(" . $commessaId . ")?\$expand=" . $expand);
|
$commessaAfterPatch = $api->get("CommessaWeb(" . $commessaId . ")?\$expand=" . $expand);
|
||||||
@@ -263,15 +355,20 @@ try {
|
|||||||
|
|
||||||
echo json_encode([
|
echo json_encode([
|
||||||
"success" => true,
|
"success" => true,
|
||||||
|
"idcommessaweb" => $commessaId,
|
||||||
|
"commessaweb" => $commessaWebCode,
|
||||||
"commessaWeb" => $finalCommessa,
|
"commessaWeb" => $finalCommessa,
|
||||||
"commessaWebApiResponse" => $commessaWeb, // Incluso per debug
|
"commessaWebApiResponse" => $commessaWeb, // Incluso per debug
|
||||||
"totalCampioni" => count($campioni),
|
"totalCampioni" => count($campioni),
|
||||||
"totalCustomFields" => count($commessaAfterPatch["CommesseCustomFields"] ?? []),
|
"totalCustomFields" => count($commessaAfterPatch["CommesseCustomFields"] ?? []),
|
||||||
|
"totalPhotos" => count($photos),
|
||||||
"message" => "Export successful",
|
"message" => "Export successful",
|
||||||
"logFiles" => [
|
"logFiles" => [
|
||||||
"step5_create" => $logFileStep5,
|
"step5_create" => $logFileStep5,
|
||||||
|
"step5_2_photos" => $logFilePhotos,
|
||||||
"step6_campioni" => $logFileStep6,
|
"step6_campioni" => $logFileStep6,
|
||||||
"step7_patch" => $logFileStep7,
|
"step7_patch" => $logFileStep7 ?? null,
|
||||||
|
"step9_1_importa" => $logFileStep91,
|
||||||
"step10_get" => $logFileStep10
|
"step10_get" => $logFileStep10
|
||||||
]
|
]
|
||||||
]);
|
]);
|
||||||
@@ -283,8 +380,10 @@ try {
|
|||||||
"message" => "Export failed: " . $e->getMessage(),
|
"message" => "Export failed: " . $e->getMessage(),
|
||||||
"logFiles" => [
|
"logFiles" => [
|
||||||
"step5_create" => $logFileStep5 ?? null,
|
"step5_create" => $logFileStep5 ?? null,
|
||||||
|
"step5_2_photos" => $logFilePhotos ?? null,
|
||||||
"step6_campioni" => $logFileStep6 ?? null,
|
"step6_campioni" => $logFileStep6 ?? null,
|
||||||
"step7_patch" => $logFileStep7 ?? null,
|
"step7_patch" => $logFileStep7 ?? null,
|
||||||
|
"step9_1_importa" => $logFileStep91 ?? null,
|
||||||
"step10_get" => $logFileStep10 ?? null
|
"step10_get" => $logFileStep10 ?? null
|
||||||
]
|
]
|
||||||
]);
|
]);
|
||||||
|
|||||||
@@ -9,11 +9,28 @@ ini_set('display_errors', '0');
|
|||||||
error_reporting(E_ALL);
|
error_reporting(E_ALL);
|
||||||
|
|
||||||
try {
|
try {
|
||||||
$api = VisualLimsApiClient::getInstance();
|
$api = VisualLimsApiClient::getInstance(); // also loads dotenv
|
||||||
|
|
||||||
|
// In simulate mode: return fake clients built from idclient values already in datadb.
|
||||||
|
// This ensures client dropdowns auto-select the correct row idclient, which in turn
|
||||||
|
// triggers the ClienteResponsabile select to populate via the mock.
|
||||||
|
if (($_ENV['SIMULATE_EXPORT_LIMS'] ?? '') === 'true') {
|
||||||
|
require_once __DIR__ . '/class/db-functions.php';
|
||||||
|
$pdo = DBHandlerSelect::getInstance()->getConnection();
|
||||||
|
$stmt = $pdo->query("SELECT DISTINCT idclient FROM datadb WHERE idclient IS NOT NULL AND idclient > 0 ORDER BY idclient ASC");
|
||||||
|
$ids = $stmt->fetchAll(PDO::FETCH_COLUMN);
|
||||||
|
$fakeClients = array_map(fn($id) => [
|
||||||
|
'IdCliente' => (int) $id,
|
||||||
|
'Nominativo' => "Cliente Simulato {$id}",
|
||||||
|
'CodiceCliente' => "SIM_{$id}",
|
||||||
|
], $ids);
|
||||||
|
echo json_encode(['value' => $fakeClients]);
|
||||||
|
exit;
|
||||||
|
}
|
||||||
|
|
||||||
// Parametri OData
|
// Parametri OData
|
||||||
$params = [
|
$params = [
|
||||||
'$select' => 'IdCliente,Nominativo,CodiceNazioneFatturazione',
|
'$select' => 'IdCliente,Nominativo,CodiceCliente',
|
||||||
'$orderby' => 'Nominativo asc'
|
'$orderby' => 'Nominativo asc'
|
||||||
];
|
];
|
||||||
|
|
||||||
|
|||||||
@@ -16,7 +16,8 @@ if (!$field) {
|
|||||||
exit;
|
exit;
|
||||||
}
|
}
|
||||||
|
|
||||||
$api = VisualLimsApiClient::getInstance();
|
$api = VisualLimsApiClient::getInstance(); // also loads dotenv as a side-effect
|
||||||
|
$simulate = ($_ENV['SIMULATE_EXPORT_LIMS'] ?? '') === 'true';
|
||||||
$base_url = 'https://93.43.5.102/limsapi/api/odata/';
|
$base_url = 'https://93.43.5.102/limsapi/api/odata/';
|
||||||
|
|
||||||
$data = null;
|
$data = null;
|
||||||
@@ -44,10 +45,13 @@ try {
|
|||||||
case 'ClienteResponsabile':
|
case 'ClienteResponsabile':
|
||||||
$id_cliente = (int)($_GET['id_cliente'] ?? 0);
|
$id_cliente = (int)($_GET['id_cliente'] ?? 0);
|
||||||
if ($id_cliente <= 0) {
|
if ($id_cliente <= 0) {
|
||||||
|
if (!$simulate) {
|
||||||
http_response_code(400);
|
http_response_code(400);
|
||||||
echo json_encode(['error' => 'Manca o invalido id_cliente']);
|
echo json_encode(['error' => 'Manca o invalido id_cliente']);
|
||||||
exit;
|
exit;
|
||||||
}
|
}
|
||||||
|
$id_cliente = 1; // dummy — mock ignores the actual ID
|
||||||
|
}
|
||||||
$endpoint = "Cliente($id_cliente)?\$expand=Responsabili";
|
$endpoint = "Cliente($id_cliente)?\$expand=Responsabili";
|
||||||
$cache_file = __DIR__ . '/cache/cliente_responsabili_' . $id_cliente . '.json';
|
$cache_file = __DIR__ . '/cache/cliente_responsabili_' . $id_cliente . '.json';
|
||||||
break;
|
break;
|
||||||
@@ -62,13 +66,13 @@ try {
|
|||||||
exit;
|
exit;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Opzionale: caching semplice (molto utile se i dati cambiano poco)
|
// Caching skipped in simulate mode to avoid polluting real-data cache files
|
||||||
if ($cache_file && file_exists($cache_file) && (time() - filemtime($cache_file) < 3600)) { // 1 ora
|
if (!$simulate && $cache_file && file_exists($cache_file) && (time() - filemtime($cache_file) < 3600)) { // 1 ora
|
||||||
$data = json_decode(file_get_contents($cache_file), true);
|
$data = json_decode(file_get_contents($cache_file), true);
|
||||||
} else {
|
} else {
|
||||||
$data = $api->get($endpoint, $options);
|
$data = $api->get($endpoint, $options);
|
||||||
|
|
||||||
if ($cache_file) {
|
if (!$simulate && $cache_file) {
|
||||||
file_put_contents($cache_file, json_encode($data, JSON_PRETTY_PRINT));
|
file_put_contents($cache_file, json_encode($data, JSON_PRETTY_PRINT));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -15,17 +15,31 @@ try {
|
|||||||
$endpoint = 'MoltiplicatorePrezzi';
|
$endpoint = 'MoltiplicatorePrezzi';
|
||||||
|
|
||||||
// Opzionale: parametri OData ($top, $filter, $orderby, ecc.)
|
// Opzionale: parametri OData ($top, $filter, $orderby, ecc.)
|
||||||
$options = []; // es: ['$top' => 100]
|
$options = [
|
||||||
|
'$orderby' => 'Descrizione asc'
|
||||||
|
];
|
||||||
|
|
||||||
// Debug: salva URL usata
|
// Debug: salva URL usata
|
||||||
$base_url = 'https://93.43.5.102/limsapi/api/odata/';
|
$base_url = 'https://93.43.5.102/limsapi/api/odata/';
|
||||||
$query = http_build_query($options);
|
$queryParts = [];
|
||||||
|
foreach ($options as $k => $v) {
|
||||||
|
// mantieni il $ nella chiave, encoda solo il valore
|
||||||
|
$queryParts[] = $k . '=' . rawurlencode($v);
|
||||||
|
}
|
||||||
|
$query = implode('&', $queryParts);
|
||||||
$full_url = $base_url . $endpoint . ($query ? '?' . $query : '');
|
$full_url = $base_url . $endpoint . ($query ? '?' . $query : '');
|
||||||
file_put_contents(__DIR__ . '/last_url.txt', $full_url . PHP_EOL, FILE_APPEND);
|
file_put_contents(__DIR__ . '/last_url.txt', $full_url . PHP_EOL, FILE_APPEND);
|
||||||
|
|
||||||
// Chiamata API
|
// Chiamata API
|
||||||
$data = $api->get($endpoint, $options);
|
$data = $api->get($endpoint, $options);
|
||||||
|
// ✅ Force sort locally by "Descrizione" (A→Z)
|
||||||
|
if (isset($data['value']) && is_array($data['value'])) {
|
||||||
|
usort($data['value'], function ($a, $b) {
|
||||||
|
$da = isset($a['Descrizione']) ? trim((string)$a['Descrizione']) : '';
|
||||||
|
$db = isset($b['Descrizione']) ? trim((string)$b['Descrizione']) : '';
|
||||||
|
return strcasecmp($da, $db); // case-insensitive alphabetical
|
||||||
|
});
|
||||||
|
}
|
||||||
// Salva il JSON in locale
|
// Salva il JSON in locale
|
||||||
file_put_contents(__DIR__ . '/moltiplicatori_prezzo_response.json', json_encode($data, JSON_PRETTY_PRINT));
|
file_put_contents(__DIR__ . '/moltiplicatori_prezzo_response.json', json_encode($data, JSON_PRETTY_PRINT));
|
||||||
|
|
||||||
|
|||||||
@@ -59,7 +59,8 @@ foreach ($allMappings as $mapping) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (!$mainFieldMapping) {
|
if (!$mainFieldMapping) {
|
||||||
$mainFieldMapping = reset(array_filter($allMappings, fn($m) => !$m['is_manual']));
|
$filtered = array_filter($allMappings, fn($m) => !$m['is_manual']);
|
||||||
|
$mainFieldMapping = reset($filtered);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Retrieve data from datadb
|
// Retrieve data from datadb
|
||||||
|
|||||||
+526
-102
@@ -1,6 +1,10 @@
|
|||||||
<?php
|
<?php
|
||||||
include('include/headscript.php');
|
include('include/headscript.php');
|
||||||
|
|
||||||
|
// ✅ FIX timezone (Rome)
|
||||||
|
ini_set('date.timezone', 'Europe/Rome');
|
||||||
|
date_default_timezone_set('Europe/Rome');
|
||||||
|
|
||||||
if ($_SERVER['REQUEST_METHOD'] !== 'POST' || !isset($_POST['template_id']) || !isset($_POST['selected_rows']) || !isset($_POST['filename'])) {
|
if ($_SERVER['REQUEST_METHOD'] !== 'POST' || !isset($_POST['template_id']) || !isset($_POST['selected_rows']) || !isset($_POST['filename'])) {
|
||||||
header("Location: xlstemplates_grid.php?status=error&message=" . urlencode("Richiesta non valida"));
|
header("Location: xlstemplates_grid.php?status=error&message=" . urlencode("Richiesta non valida"));
|
||||||
exit;
|
exit;
|
||||||
@@ -22,10 +26,36 @@ $pdo = $db->getConnection();
|
|||||||
$importReferenceCode = date('YmdHis') . '-' . uniqid();
|
$importReferenceCode = date('YmdHis') . '-' . uniqid();
|
||||||
|
|
||||||
// Recupera tutti i mapping dal template, includendo is_visible_import
|
// Recupera tutti i mapping dal template, includendo is_visible_import
|
||||||
$stmt = $pdo->prepare("SELECT id, excel_column, data_type, is_required, manual_default, is_manual, field_label, field_id, main_field, is_visible_import FROM template_mapping WHERE template_id = ?");
|
$stmt = $pdo->prepare("SELECT id, excel_column, data_type, is_required, manual_default, is_manual, field_label, field_id, main_field, is_visible_import, auto_value
|
||||||
|
FROM template_mapping
|
||||||
|
WHERE template_id = ?");
|
||||||
$stmt->execute([$template_id]);
|
$stmt->execute([$template_id]);
|
||||||
$allMappings = $stmt->fetchAll(PDO::FETCH_ASSOC);
|
$allMappings = $stmt->fetchAll(PDO::FETCH_ASSOC);
|
||||||
|
|
||||||
|
$timeLabels = [
|
||||||
|
'Sample Arrival Time:',
|
||||||
|
'Sample Unlock Time:',
|
||||||
|
'Login Time:',
|
||||||
|
'Presa in carico, Orario:',
|
||||||
|
];
|
||||||
|
|
||||||
|
// Returns the auto value for current session (import) based on mapping.auto_value
|
||||||
|
function getImportAutoValue(array $mapping): string
|
||||||
|
{
|
||||||
|
$auto = $mapping['auto_value'] ?? 'none';
|
||||||
|
|
||||||
|
if ($auto === 'import_date') {
|
||||||
|
return date('Y-m-d');
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($auto === 'import_time') {
|
||||||
|
// HTML <input type="time"> expects HH:MM (seconds optional)
|
||||||
|
return date('H:i');
|
||||||
|
}
|
||||||
|
|
||||||
|
return '';
|
||||||
|
}
|
||||||
|
|
||||||
if (empty($allMappings)) {
|
if (empty($allMappings)) {
|
||||||
header("Location: import_xls.php?id=$template_id&status=error&message=" . urlencode("Nessun mapping trovato per il template"));
|
header("Location: import_xls.php?id=$template_id&status=error&message=" . urlencode("Nessun mapping trovato per il template"));
|
||||||
exit;
|
exit;
|
||||||
@@ -47,7 +77,52 @@ $template = $template_stmt->fetch(PDO::FETCH_ASSOC);
|
|||||||
$default_idclient = $template['idclient'] ?? null;
|
$default_idclient = $template['idclient'] ?? null;
|
||||||
|
|
||||||
$insertedIds = $_POST['inserted_ids'] ?? $_SESSION['inserted_ids'];
|
$insertedIds = $_POST['inserted_ids'] ?? $_SESSION['inserted_ids'];
|
||||||
|
// ------------------------------------------------------------
|
||||||
|
// AUTO SET: schema field_id = 244 with auth_users.lims_user_id (if present)
|
||||||
|
// ------------------------------------------------------------
|
||||||
|
try {
|
||||||
|
// 1) Read logged user lims_user_id
|
||||||
|
$stmtUser = $pdo->prepare("SELECT lims_user_id FROM auth_users WHERE id = ? LIMIT 1");
|
||||||
|
$stmtUser->execute([(int)$user_id]);
|
||||||
|
$limsUserId = $stmtUser->fetchColumn();
|
||||||
|
|
||||||
|
// normalize
|
||||||
|
$limsUserId = ($limsUserId !== false && $limsUserId !== null && $limsUserId !== '') ? (string)$limsUserId : '';
|
||||||
|
|
||||||
|
if ($limsUserId !== '' && !empty($insertedIds)) {
|
||||||
|
|
||||||
|
// 2) Find mapping_id for field_id = 244 in this template
|
||||||
|
$stmtMap = $pdo->prepare("SELECT id FROM template_mapping WHERE template_id = ? AND field_id = 244 LIMIT 1");
|
||||||
|
$stmtMap->execute([(int)$template_id]);
|
||||||
|
$mappingId244 = (int)$stmtMap->fetchColumn();
|
||||||
|
|
||||||
|
if ($mappingId244 > 0) {
|
||||||
|
|
||||||
|
// 3) Upsert value into import_data_details for every inserted datadb row
|
||||||
|
$pdo->beginTransaction();
|
||||||
|
|
||||||
|
$updStmt = $pdo->prepare("UPDATE import_data_details SET field_value = ? WHERE id = ? AND mapping_id = ?");
|
||||||
|
$insStmt = $pdo->prepare("INSERT INTO import_data_details (id, mapping_id, field_value) VALUES (?, ?, ?)");
|
||||||
|
|
||||||
|
foreach ($insertedIds as $iddatadb) {
|
||||||
|
$iddatadb = (int)$iddatadb;
|
||||||
|
if ($iddatadb <= 0) continue;
|
||||||
|
|
||||||
|
$updStmt->execute([$limsUserId, $iddatadb, $mappingId244]);
|
||||||
|
|
||||||
|
if ($updStmt->rowCount() === 0) {
|
||||||
|
$insStmt->execute([$iddatadb, $mappingId244, $limsUserId]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
$pdo->commit();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (Exception $e) {
|
||||||
|
// Do not block import page if this fails; just log
|
||||||
|
error_log("[AUTO FIELD 244] " . $e->getMessage());
|
||||||
|
if ($pdo->inTransaction()) $pdo->rollBack();
|
||||||
|
}
|
||||||
// Recupera i dati appena inseriti con i nomi degli utenti
|
// Recupera i dati appena inseriti con i nomi degli utenti
|
||||||
$stmt = $pdo->prepare("
|
$stmt = $pdo->prepare("
|
||||||
SELECT d.*, CONCAT(u.first_name, ' ', u.last_name) AS user_name
|
SELECT d.*, CONCAT(u.first_name, ' ', u.last_name) AS user_name
|
||||||
@@ -60,7 +135,8 @@ $importedData = $stmt->fetchAll(PDO::FETCH_ASSOC);
|
|||||||
|
|
||||||
// Recupera i dettagli manuali da import_data_details
|
// Recupera i dettagli manuali da import_data_details
|
||||||
$stmt = $pdo->prepare("
|
$stmt = $pdo->prepare("
|
||||||
SELECT d.id AS detail_id, d.id AS datadb_id, d.mapping_id, d.field_value, m.field_label, m.data_type, m.is_required, m.manual_default
|
SELECT d.id AS detail_id, d.id AS datadb_id, d.mapping_id, d.field_value,
|
||||||
|
m.field_id, m.field_label, m.data_type, m.is_required, m.manual_default
|
||||||
FROM import_data_details d
|
FROM import_data_details d
|
||||||
JOIN template_mapping m ON d.mapping_id = m.id
|
JOIN template_mapping m ON d.mapping_id = m.id
|
||||||
WHERE d.id IN (" . implode(',', array_fill(0, count($insertedIds), '?')) . ")
|
WHERE d.id IN (" . implode(',', array_fill(0, count($insertedIds), '?')) . ")
|
||||||
@@ -74,6 +150,9 @@ $slugMapping = [];
|
|||||||
foreach ($stmt->fetchAll(PDO::FETCH_ASSOC) as $row) {
|
foreach ($stmt->fetchAll(PDO::FETCH_ASSOC) as $row) {
|
||||||
$slugMapping[$row['mysql_column_name']] = $row['user_friendly_slug'];
|
$slugMapping[$row['mysql_column_name']] = $row['user_friendly_slug'];
|
||||||
}
|
}
|
||||||
|
// --- FIX LABELS ONLY (do not change keys) ---
|
||||||
|
$slugMapping['AnagraficaCertestObject'] = 'Object';
|
||||||
|
$slugMapping['AnagraficaCertestService'] = 'Service';
|
||||||
|
|
||||||
// ---------------- FIXED FIELDS (from template_fixed_mapping) ----------------
|
// ---------------- FIXED FIELDS (from template_fixed_mapping) ----------------
|
||||||
$fixedStmt = $pdo->prepare("
|
$fixedStmt = $pdo->prepare("
|
||||||
@@ -113,6 +192,16 @@ foreach ($tempMap as $f) {
|
|||||||
$fixedFields[] = $f;
|
$fixedFields[] = $f;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Maps logical fixed_field_key → real datadb column name (mirrors JS fixedAliasMap)
|
||||||
|
$fixedAliasMap = [
|
||||||
|
'ClienteResponsabile' => 'cliente_responsabile_id',
|
||||||
|
'MoltiplicatorePrezzo' => 'moltiplicatore_prezzo_id',
|
||||||
|
'AnagraficaCertestObject' => 'anagrafica_certest_object_id',
|
||||||
|
'AnagraficaCertestService' => 'anagrafica_certest_service_id',
|
||||||
|
'ClienteFornitore' => 'cliente_fornitore_id',
|
||||||
|
'ConsegnaRichiesta' => 'consegna_richiesta',
|
||||||
|
];
|
||||||
|
|
||||||
// helper default (DATE can use 'today' if you already use it elsewhere)
|
// helper default (DATE can use 'today' if you already use it elsewhere)
|
||||||
function fixedDefaultValue(array $f): string
|
function fixedDefaultValue(array $f): string
|
||||||
{
|
{
|
||||||
@@ -155,6 +244,13 @@ function fixedDefaultValue(array $f): string
|
|||||||
background-color: #f8d7da;
|
background-color: #f8d7da;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
input.required-input,
|
||||||
|
select.required-input {
|
||||||
|
background-color: #f8d7da;
|
||||||
|
border-color: #ced4da !important;
|
||||||
|
box-shadow: none !important;
|
||||||
|
}
|
||||||
|
|
||||||
input,
|
input,
|
||||||
select,
|
select,
|
||||||
textarea {
|
textarea {
|
||||||
@@ -482,11 +578,23 @@ function fixedDefaultValue(array $f): string
|
|||||||
z-index: 1049;
|
z-index: 1049;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Required empty cell: keep light red background BUT NO red border */
|
||||||
.grid-cell.missing-required {
|
.grid-cell.missing-required {
|
||||||
border: 2px solid red;
|
background-color: #ffe6e6 !important;
|
||||||
background-color: #ffe6e6;
|
|
||||||
|
/* remove the red border you set */
|
||||||
|
border: 0 !important;
|
||||||
|
|
||||||
|
/* keep grid separators */
|
||||||
|
border-right: 1px solid #dee2e6 !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* if it is the last column, don't force the right border */
|
||||||
|
.grid-cell.missing-required:last-child {
|
||||||
|
border-right: none !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
.dropdown-select {
|
.dropdown-select {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
box-sizing: border-box;
|
box-sizing: border-box;
|
||||||
@@ -540,12 +648,14 @@ function fixedDefaultValue(array $f): string
|
|||||||
}
|
}
|
||||||
|
|
||||||
#exportConfirmModal,
|
#exportConfirmModal,
|
||||||
#exportResponseModal {
|
#exportResponseModal,
|
||||||
|
#exportUnsavedModal {
|
||||||
z-index: 1300 !important;
|
z-index: 1300 !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
#exportConfirmModal .modal-backdrop,
|
#exportConfirmModal .modal-backdrop,
|
||||||
#exportResponseModal .modal-backdrop {
|
#exportResponseModal .modal-backdrop,
|
||||||
|
#exportUnsavedModal .modal-backdrop {
|
||||||
z-index: 1299 !important;
|
z-index: 1299 !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -636,13 +746,11 @@ function fixedDefaultValue(array $f): string
|
|||||||
<div class="card radius-10">
|
<div class="card radius-10">
|
||||||
<div class="card-header">
|
<div class="card-header">
|
||||||
<div class="d-flex align-items-center">
|
<div class="d-flex align-items-center">
|
||||||
<div>
|
|
||||||
<h6 class="mb-0">Modifica Dati Importati</h6>
|
|
||||||
<div id="unsavedChanges" style="display:none; color: red; font-weight: bold; margin:10px 0;">
|
<div id="unsavedChanges" style="display:none; color: red; font-weight: bold; margin:10px 0;">
|
||||||
⚠️ Unsaved changes detected! Please save before leaving this page.
|
⚠️ Unsaved changes detected! Please save before leaving this page.<br>
|
||||||
<ul id="changedFields" style="margin-top:5px; font-weight:normal; color:darkred;"></ul>
|
<span id="changedRows" style="font-weight:normal; color:darkred;"></span>
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="card-body">
|
<div class="card-body">
|
||||||
@@ -711,7 +819,23 @@ function fixedDefaultValue(array $f): string
|
|||||||
echo "<button type='button' class='propagate-btn' data-column='auto_$autoIndex'><i class='fas fa-arrow-down'></i></button>";
|
echo "<button type='button' class='propagate-btn' data-column='auto_$autoIndex'><i class='fas fa-arrow-down'></i></button>";
|
||||||
echo "</div>";
|
echo "</div>";
|
||||||
} else {
|
} else {
|
||||||
echo "<div class='grid-cell grid-top-cell' style='flex: 0 0 150px;' data-index='$topColIndex'></div>";
|
// Show auto import date/time in header too (read-only)
|
||||||
|
$autoVal = getImportAutoValue($mapping);
|
||||||
|
|
||||||
|
echo "<div class='grid-cell grid-top-cell' style='flex: 0 0 150px;' data-index='$topColIndex'>";
|
||||||
|
|
||||||
|
if (($mapping['auto_value'] ?? 'none') === 'import_date') {
|
||||||
|
echo "<input type='text' class='custom-field date-picker auto-input' value='" . htmlspecialchars($autoVal) . "' readonly>";
|
||||||
|
echo "<button type='button' class='propagate-btn' data-column='auto_$autoIndex'><i class='fas fa-arrow-down'></i></button>";
|
||||||
|
} elseif (($mapping['auto_value'] ?? 'none') === 'import_time') {
|
||||||
|
echo "<input type='time' class='custom-field auto-input' value='" . htmlspecialchars($autoVal) . "' readonly>";
|
||||||
|
echo "<button type='button' class='propagate-btn' data-column='auto_$autoIndex'><i class='fas fa-arrow-down'></i></button>";
|
||||||
|
} else {
|
||||||
|
// keep empty cell for other auto fields
|
||||||
|
echo "";
|
||||||
|
}
|
||||||
|
|
||||||
|
echo "</div>";
|
||||||
}
|
}
|
||||||
|
|
||||||
$autoIndex++;
|
$autoIndex++;
|
||||||
@@ -738,6 +862,8 @@ function fixedDefaultValue(array $f): string
|
|||||||
echo "<input type='text' class='custom-field date-picker $inputClass' data-column='manual_$manualIndex' value='" . htmlspecialchars($fieldValue) . "' " . ($mapping['is_required'] ? 'required' : '') . ">";
|
echo "<input type='text' class='custom-field date-picker $inputClass' data-column='manual_$manualIndex' value='" . htmlspecialchars($fieldValue) . "' " . ($mapping['is_required'] ? 'required' : '') . ">";
|
||||||
} elseif ($mapping['data_type'] === 'INT') {
|
} elseif ($mapping['data_type'] === 'INT') {
|
||||||
echo "<input type='number' class='custom-field $inputClass' data-column='manual_$manualIndex' value='" . htmlspecialchars($fieldValue) . "' " . ($mapping['is_required'] ? 'required' : '') . ">";
|
echo "<input type='number' class='custom-field $inputClass' data-column='manual_$manualIndex' value='" . htmlspecialchars($fieldValue) . "' " . ($mapping['is_required'] ? 'required' : '') . ">";
|
||||||
|
} elseif (in_array($mapping['field_label'], $timeLabels)) {
|
||||||
|
echo "<input type='time' class='custom-field $inputClass' data-column='manual_$manualIndex' value='" . htmlspecialchars($fieldValue) . "' " . ($mapping['is_required'] ? 'required' : '') . ">";
|
||||||
} else {
|
} else {
|
||||||
echo "<input type='text' class='custom-field $inputClass' data-column='manual_$manualIndex' value='" . htmlspecialchars($fieldValue) . "' " . ($mapping['is_required'] ? 'required' : '') . ">";
|
echo "<input type='text' class='custom-field $inputClass' data-column='manual_$manualIndex' value='" . htmlspecialchars($fieldValue) . "' " . ($mapping['is_required'] ? 'required' : '') . ">";
|
||||||
}
|
}
|
||||||
@@ -941,6 +1067,9 @@ function fixedDefaultValue(array $f): string
|
|||||||
<span class="status-badge status-<?= htmlspecialchars($row['status'] ?? 'i') ?>">
|
<span class="status-badge status-<?= htmlspecialchars($row['status'] ?? 'i') ?>">
|
||||||
<?= htmlspecialchars($row['status'] === 'i' ? 'Imported' : ($row['status'] === 'P' ? 'In Progress' : 'To LIMS')) ?>
|
<?= htmlspecialchars($row['status'] === 'i' ? 'Imported' : ($row['status'] === 'P' ? 'In Progress' : 'To LIMS')) ?>
|
||||||
</span>
|
</span>
|
||||||
|
<?php if (!empty($row['commessaweb'])): ?>
|
||||||
|
<span class="commessaweb-code" style="display:block; font-size:0.75em; color:#555; margin-top:2px;" title="CommessaWeb"><?= htmlspecialchars($row['commessaweb']) ?></span>
|
||||||
|
<?php endif; ?>
|
||||||
<input type="hidden" name="rows[<?= $index ?>][status]" value="<?= htmlspecialchars($row['status'] ?? 'i') ?>">
|
<input type="hidden" name="rows[<?= $index ?>][status]" value="<?= htmlspecialchars($row['status'] ?? 'i') ?>">
|
||||||
</div>
|
</div>
|
||||||
<?php
|
<?php
|
||||||
@@ -952,6 +1081,14 @@ function fixedDefaultValue(array $f): string
|
|||||||
$detail = array_filter($rowDetails, fn($d) => $d['mapping_id'] == $mapping['id']);
|
$detail = array_filter($rowDetails, fn($d) => $d['mapping_id'] == $mapping['id']);
|
||||||
$detail = reset($detail) ?: ['field_value' => $mapping['manual_default']];
|
$detail = reset($detail) ?: ['field_value' => $mapping['manual_default']];
|
||||||
$fieldValue = $detail['field_value'] ?? $mapping['manual_default'] ?? '';
|
$fieldValue = $detail['field_value'] ?? $mapping['manual_default'] ?? '';
|
||||||
|
|
||||||
|
// Auto-fill import date/time only if empty
|
||||||
|
if ($fieldValue === '' || $fieldValue === null) {
|
||||||
|
$autoVal = getImportAutoValue($mapping);
|
||||||
|
if ($autoVal !== '') {
|
||||||
|
$fieldValue = $autoVal;
|
||||||
|
}
|
||||||
|
}
|
||||||
$requiredClass = ($mapping['is_required'] && (is_null($fieldValue) || $fieldValue === '')) ? 'missing-required' : '';
|
$requiredClass = ($mapping['is_required'] && (is_null($fieldValue) || $fieldValue === '')) ? 'missing-required' : '';
|
||||||
$inputClass = 'auto-input';
|
$inputClass = 'auto-input';
|
||||||
if ($mapping['is_required']) $inputClass .= ' required-input';
|
if ($mapping['is_required']) $inputClass .= ' required-input';
|
||||||
@@ -964,6 +1101,10 @@ function fixedDefaultValue(array $f): string
|
|||||||
echo "<input type='text' name='rows[$index][details][{$mapping['id']}][field_value]' value='" . htmlspecialchars($fieldValue) . "' class='cell-input date-picker $inputClass' " . ($mapping['is_required'] ? 'required' : '') . ">";
|
echo "<input type='text' name='rows[$index][details][{$mapping['id']}][field_value]' value='" . htmlspecialchars($fieldValue) . "' class='cell-input date-picker $inputClass' " . ($mapping['is_required'] ? 'required' : '') . ">";
|
||||||
} elseif ($mapping['data_type'] === 'INT') {
|
} elseif ($mapping['data_type'] === 'INT') {
|
||||||
echo "<input type='number' name='rows[$index][details][{$mapping['id']}][field_value]' value='" . htmlspecialchars($fieldValue) . "' class='cell-input $inputClass' " . ($mapping['is_required'] ? 'required' : '') . ">";
|
echo "<input type='number' name='rows[$index][details][{$mapping['id']}][field_value]' value='" . htmlspecialchars($fieldValue) . "' class='cell-input $inputClass' " . ($mapping['is_required'] ? 'required' : '') . ">";
|
||||||
|
} elseif (($mapping['auto_value'] ?? 'none') === 'import_time') {
|
||||||
|
echo "<input type='time' name='rows[$index][details][{$mapping['id']}][field_value]' value='" . htmlspecialchars($fieldValue) . "' class='cell-input $inputClass' " . ($mapping['is_required'] ? 'required' : '') . ">";
|
||||||
|
} elseif (in_array($mapping['field_label'], $timeLabels)) {
|
||||||
|
echo "<input type='time' name='rows[$index][details][{$mapping['id']}][field_value]' value='" . htmlspecialchars($fieldValue) . "' class='cell-input $inputClass' " . ($mapping['is_required'] ? 'required' : '') . ">";
|
||||||
} else {
|
} else {
|
||||||
echo "<input type='text' name='rows[$index][details][{$mapping['id']}][field_value]' value='" . htmlspecialchars($fieldValue) . "' class='cell-input $inputClass' " . ($mapping['is_required'] ? 'required' : '') . ">";
|
echo "<input type='text' name='rows[$index][details][{$mapping['id']}][field_value]' value='" . htmlspecialchars($fieldValue) . "' class='cell-input $inputClass' " . ($mapping['is_required'] ? 'required' : '') . ">";
|
||||||
}
|
}
|
||||||
@@ -993,6 +1134,8 @@ function fixedDefaultValue(array $f): string
|
|||||||
echo "<input type='text' name='rows[$index][details][{$mapping['id']}][field_value]' value='" . htmlspecialchars($fieldValue) . "' class='cell-input date-picker $inputClass' " . ($mapping['is_required'] ? 'required' : '') . ">";
|
echo "<input type='text' name='rows[$index][details][{$mapping['id']}][field_value]' value='" . htmlspecialchars($fieldValue) . "' class='cell-input date-picker $inputClass' " . ($mapping['is_required'] ? 'required' : '') . ">";
|
||||||
} elseif ($mapping['data_type'] === 'INT') {
|
} elseif ($mapping['data_type'] === 'INT') {
|
||||||
echo "<input type='number' name='rows[$index][details][{$mapping['id']}][field_value]' value='" . htmlspecialchars($fieldValue) . "' class='cell-input $inputClass' " . ($mapping['is_required'] ? 'required' : '') . ">";
|
echo "<input type='number' name='rows[$index][details][{$mapping['id']}][field_value]' value='" . htmlspecialchars($fieldValue) . "' class='cell-input $inputClass' " . ($mapping['is_required'] ? 'required' : '') . ">";
|
||||||
|
} elseif (in_array($mapping['field_label'], $timeLabels)) {
|
||||||
|
echo "<input type='time' name='rows[$index][details][{$mapping['id']}][field_value]' value='" . htmlspecialchars($fieldValue) . "' class='cell-input $inputClass' " . ($mapping['is_required'] ? 'required' : '') . ">";
|
||||||
} else {
|
} else {
|
||||||
echo "<input type='text' name='rows[$index][details][{$mapping['id']}][field_value]' value='" . htmlspecialchars($fieldValue) . "' class='cell-input $inputClass' " . ($mapping['is_required'] ? 'required' : '') . ">";
|
echo "<input type='text' name='rows[$index][details][{$mapping['id']}][field_value]' value='" . htmlspecialchars($fieldValue) . "' class='cell-input $inputClass' " . ($mapping['is_required'] ? 'required' : '') . ">";
|
||||||
}
|
}
|
||||||
@@ -1031,7 +1174,8 @@ function fixedDefaultValue(array $f): string
|
|||||||
if (!empty($fixedFields)) {
|
if (!empty($fixedFields)) {
|
||||||
foreach ($fixedFields as $f) {
|
foreach ($fixedFields as $f) {
|
||||||
$key = $f['fixed_field_key'];
|
$key = $f['fixed_field_key'];
|
||||||
$val = $row[$key] ?? '';
|
$dbCol = $fixedAliasMap[$key] ?? $key;
|
||||||
|
$val = $row[$dbCol] ?? '';
|
||||||
if ($val === '' || $val === null) {
|
if ($val === '' || $val === null) {
|
||||||
$val = fixedDefaultValue($f);
|
$val = fixedDefaultValue($f);
|
||||||
}
|
}
|
||||||
@@ -1153,84 +1297,63 @@ function fixedDefaultValue(array $f): string
|
|||||||
instance.input.dispatchEvent(event);
|
instance.input.dispatchEvent(event);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
flatpickr(".time-picker", {
|
||||||
|
enableTime: true,
|
||||||
|
noCalendar: true,
|
||||||
|
dateFormat: "H:i",
|
||||||
|
time_24hr: true,
|
||||||
|
allowInput: true
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
$(document).on("click", ".export-lims-btn", function() {
|
|
||||||
let rowId = $(this).data("row");
|
|
||||||
let idDataDb = $(this).data("iddatadb");
|
|
||||||
|
|
||||||
$.ajax({
|
|
||||||
url: "export_to_lims.php",
|
|
||||||
method: "POST",
|
|
||||||
data: {
|
|
||||||
iddatadb: idDataDb
|
|
||||||
},
|
|
||||||
dataType: "json",
|
|
||||||
beforeSend: function() {
|
|
||||||
alert("Export started in background for row " + rowId);
|
|
||||||
},
|
|
||||||
success: function(response) {
|
|
||||||
if (response.success) {
|
|
||||||
alert(response.message);
|
|
||||||
} else {
|
|
||||||
alert("❌ Error: " + response.message);
|
|
||||||
}
|
|
||||||
},
|
|
||||||
error: function(xhr, status, error) {
|
|
||||||
alert("❌ AJAX error: " + error);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
document.addEventListener("DOMContentLoaded", function() {
|
document.addEventListener("DOMContentLoaded", function() {
|
||||||
const inputs = document.querySelectorAll(".cell-input, .dropdown-select, .carrier-select, .awb-input, .date-picker");
|
const inputs = document.querySelectorAll(".cell-input, .dropdown-select, .carrier-select, .awb-input, .date-picker");
|
||||||
const unsavedDiv = document.getElementById("unsavedChanges");
|
const unsavedDiv = document.getElementById("unsavedChanges");
|
||||||
const changedList = document.getElementById("changedFields");
|
const changedRowsEl = document.getElementById("changedRows");
|
||||||
let hasChanges = false;
|
let hasChanges = false;
|
||||||
let changedFields = {};
|
|
||||||
|
|
||||||
function renderChangedList() {
|
// ✅ solo righe modificate (Set)
|
||||||
changedList.innerHTML = "";
|
let changedRows = new Set();
|
||||||
Object.keys(changedFields).forEach(rowIndex => {
|
|
||||||
const fields = changedFields[rowIndex];
|
function renderChangedRows() {
|
||||||
if (fields.length > 0) {
|
const rows = Array.from(changedRows)
|
||||||
const li = document.createElement("li");
|
.map(n => Number(n))
|
||||||
li.textContent = `Row ${parseInt(rowIndex) + 1}: ${fields.join(", ")}`;
|
.sort((a, b) => a - b);
|
||||||
changedList.appendChild(li);
|
|
||||||
|
if (rows.length === 0) {
|
||||||
|
unsavedDiv.style.display = "none";
|
||||||
|
changedRowsEl.textContent = "";
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
});
|
|
||||||
unsavedDiv.style.display = Object.keys(changedFields).length > 0 ? "block" : "none";
|
// Visuale: "Row 1, Row 5, Row 6"
|
||||||
|
changedRowsEl.textContent = rows.map(r => `Row ${r + 1}`).join(", ");
|
||||||
|
unsavedDiv.style.display = "block";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
inputs.forEach(el => {
|
inputs.forEach(el => {
|
||||||
el.addEventListener("change", () => {
|
el.addEventListener("change", () => {
|
||||||
|
if (el.hasAttribute('data-restoring')) return;
|
||||||
hasChanges = true;
|
hasChanges = true;
|
||||||
|
|
||||||
const gridCell = el.closest(".grid-cell");
|
const gridCell = el.closest(".grid-cell");
|
||||||
const colIndex = gridCell?.dataset.index;
|
|
||||||
const rowIndex = gridCell?.dataset.row;
|
const rowIndex = gridCell?.dataset.row;
|
||||||
let label = "Unknown field";
|
|
||||||
|
|
||||||
if (colIndex) {
|
|
||||||
const header = document.querySelector(`.grid-header[data-index="${colIndex}"]`);
|
|
||||||
if (header) {
|
|
||||||
label = header.textContent.replace(":", "").trim();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
// ✅ segnamo solo la riga
|
||||||
if (rowIndex !== undefined) {
|
if (rowIndex !== undefined) {
|
||||||
if (!changedFields[rowIndex]) {
|
changedRows.add(rowIndex);
|
||||||
changedFields[rowIndex] = [];
|
|
||||||
}
|
|
||||||
if (!changedFields[rowIndex].includes(label)) {
|
|
||||||
changedFields[rowIndex].push(label);
|
|
||||||
}
|
|
||||||
gridCell.classList.add("cell-changed");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
renderChangedList();
|
// (se vuoi mantenere highlight cella gialla, lascia questa riga)
|
||||||
|
if (gridCell) gridCell.classList.add("cell-changed");
|
||||||
|
|
||||||
|
renderChangedRows();
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
document.querySelectorAll(".save-btn").forEach(btn => {
|
document.querySelectorAll(".save-btn").forEach(btn => {
|
||||||
btn.addEventListener("click", () => {
|
btn.addEventListener("click", () => {
|
||||||
const rowIndex = btn.dataset.row;
|
const rowIndex = btn.dataset.row;
|
||||||
@@ -1263,9 +1386,21 @@ function fixedDefaultValue(array $f): string
|
|||||||
if (!inp.classList.contains('fixed-input')) return;
|
if (!inp.classList.contains('fixed-input')) return;
|
||||||
|
|
||||||
const m = inp.name.match(/rows\[\d+\]\[([^\]]+)\]/);
|
const m = inp.name.match(/rows\[\d+\]\[([^\]]+)\]/);
|
||||||
|
// Map: fixed key (logical) -> datadb real column
|
||||||
|
const fixedAliasMap = {
|
||||||
|
ClienteResponsabile: 'cliente_responsabile_id',
|
||||||
|
MoltiplicatorePrezzo: 'moltiplicatore_prezzo_id',
|
||||||
|
AnagraficaCertestObject: 'anagrafica_certest_object_id',
|
||||||
|
AnagraficaCertestService: 'anagrafica_certest_service_id',
|
||||||
|
ConsegnaRichiesta: 'consegna_richiesta'
|
||||||
|
};
|
||||||
|
|
||||||
if (m && m[1]) {
|
if (m && m[1]) {
|
||||||
formData.append(m[1], inp.value);
|
const logicalKey = m[1];
|
||||||
|
const realKey = fixedAliasMap[logicalKey] || logicalKey;
|
||||||
|
formData.append(realKey, inp.value);
|
||||||
}
|
}
|
||||||
|
|
||||||
});
|
});
|
||||||
|
|
||||||
formData.append('iddatadb', iddatadb);
|
formData.append('iddatadb', iddatadb);
|
||||||
@@ -1288,15 +1423,19 @@ function fixedDefaultValue(array $f): string
|
|||||||
});
|
});
|
||||||
setTimeout(() => cells.forEach(cell => cell.classList.remove('flash-success')), 500);
|
setTimeout(() => cells.forEach(cell => cell.classList.remove('flash-success')), 500);
|
||||||
|
|
||||||
if (changedFields[rowIndex]) {
|
// ✅ rimuovi riga dal set (non mostrare più Row X)
|
||||||
delete changedFields[rowIndex];
|
changedRows.delete(rowIndex);
|
||||||
|
|
||||||
|
// se vuoi continuare a togliere highlight giallo di TUTTA la riga:
|
||||||
document.querySelectorAll(`.grid-cell[data-row="${rowIndex}"]`)
|
document.querySelectorAll(`.grid-cell[data-row="${rowIndex}"]`)
|
||||||
.forEach(cell => cell.classList.remove("cell-changed"));
|
.forEach(cell => cell.classList.remove("cell-changed"));
|
||||||
renderChangedList();
|
|
||||||
hasChanges = Object.keys(changedFields).length > 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
alert('Salvataggio riga avvenuto con successo!');
|
renderChangedRows();
|
||||||
|
hasChanges = changedRows.size > 0;
|
||||||
|
|
||||||
|
|
||||||
|
const toastEl = document.getElementById('saveSuccessToast');
|
||||||
|
if (toastEl) bootstrap.Toast.getOrCreateInstance(toastEl).show();
|
||||||
} else {
|
} else {
|
||||||
alert('Errore durante il salvataggio: ' + data.message);
|
alert('Errore durante il salvataggio: ' + data.message);
|
||||||
}
|
}
|
||||||
@@ -1345,9 +1484,21 @@ function fixedDefaultValue(array $f): string
|
|||||||
fixedInputs.forEach(inp => {
|
fixedInputs.forEach(inp => {
|
||||||
if (!inp.classList.contains('fixed-input')) return;
|
if (!inp.classList.contains('fixed-input')) return;
|
||||||
const m = inp.name.match(/rows\[\d+\]\[([^\]]+)\]/);
|
const m = inp.name.match(/rows\[\d+\]\[([^\]]+)\]/);
|
||||||
|
// Map: fixed key (logical) -> datadb real column
|
||||||
|
const fixedAliasMap = {
|
||||||
|
ClienteResponsabile: 'cliente_responsabile_id',
|
||||||
|
MoltiplicatorePrezzo: 'moltiplicatore_prezzo_id',
|
||||||
|
AnagraficaCertestObject: 'anagrafica_certest_object_id',
|
||||||
|
AnagraficaCertestService: 'anagrafica_certest_service_id',
|
||||||
|
ConsegnaRichiesta: 'consegna_richiesta'
|
||||||
|
};
|
||||||
|
|
||||||
if (m && m[1]) {
|
if (m && m[1]) {
|
||||||
formData.append(m[1], inp.value);
|
const logicalKey = m[1];
|
||||||
|
const realKey = fixedAliasMap[logicalKey] || logicalKey;
|
||||||
|
formData.append(realKey, inp.value);
|
||||||
}
|
}
|
||||||
|
|
||||||
});
|
});
|
||||||
|
|
||||||
formData.append('iddatadb', iddatadb);
|
formData.append('iddatadb', iddatadb);
|
||||||
@@ -1370,11 +1521,10 @@ function fixedDefaultValue(array $f): string
|
|||||||
});
|
});
|
||||||
setTimeout(() => cells.forEach(cell => cell.classList.remove('flash-success')), 500);
|
setTimeout(() => cells.forEach(cell => cell.classList.remove('flash-success')), 500);
|
||||||
|
|
||||||
if (changedFields[rowIndex]) {
|
changedRows.delete(rowIndex);
|
||||||
delete changedFields[rowIndex];
|
|
||||||
document.querySelectorAll(`.grid-cell[data-row="${rowIndex}"]`)
|
document.querySelectorAll(`.grid-cell[data-row="${rowIndex}"]`)
|
||||||
.forEach(cell => cell.classList.remove("cell-changed"));
|
.forEach(cell => cell.classList.remove("cell-changed"));
|
||||||
}
|
|
||||||
} else {
|
} else {
|
||||||
errorMessages.push(`Riga ${parseInt(rowIndex) + 1}: ${data.message}`);
|
errorMessages.push(`Riga ${parseInt(rowIndex) + 1}: ${data.message}`);
|
||||||
}
|
}
|
||||||
@@ -1383,8 +1533,9 @@ function fixedDefaultValue(array $f): string
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
renderChangedList();
|
renderChangedRows();
|
||||||
hasChanges = Object.keys(changedFields).length > 0;
|
hasChanges = changedRows.size > 0;
|
||||||
|
|
||||||
|
|
||||||
if (errorMessages.length === 0) {
|
if (errorMessages.length === 0) {
|
||||||
alert(`Tutte le ${successCount} righe salvate con successo!`);
|
alert(`Tutte le ${successCount} righe salvate con successo!`);
|
||||||
@@ -1402,7 +1553,7 @@ function fixedDefaultValue(array $f): string
|
|||||||
});
|
});
|
||||||
|
|
||||||
// Gestisci la chiusura dei modali per rimuovere i backdrop
|
// Gestisci la chiusura dei modali per rimuovere i backdrop
|
||||||
document.querySelectorAll('#exportConfirmModal, #exportResponseModal').forEach(modal => {
|
document.querySelectorAll('#exportConfirmModal, #exportResponseModal, #exportUnsavedModal').forEach(modal => {
|
||||||
modal.addEventListener('hidden.bs.modal', () => {
|
modal.addEventListener('hidden.bs.modal', () => {
|
||||||
// Rimuovi tutti i backdrop residui
|
// Rimuovi tutti i backdrop residui
|
||||||
document.querySelectorAll('.modal-backdrop').forEach(backdrop => {
|
document.querySelectorAll('.modal-backdrop').forEach(backdrop => {
|
||||||
@@ -1498,10 +1649,12 @@ function fixedDefaultValue(array $f): string
|
|||||||
// Ripristina il valore corrente
|
// Ripristina il valore corrente
|
||||||
if (currentValue) {
|
if (currentValue) {
|
||||||
dropdown.value = currentValue;
|
dropdown.value = currentValue;
|
||||||
|
dropdown.setAttribute('data-restoring', '');
|
||||||
const event = new Event('change', {
|
const event = new Event('change', {
|
||||||
bubbles: true
|
bubbles: true
|
||||||
});
|
});
|
||||||
dropdown.dispatchEvent(event);
|
dropdown.dispatchEvent(event);
|
||||||
|
dropdown.removeAttribute('data-restoring');
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@@ -1608,7 +1761,9 @@ function fixedDefaultValue(array $f): string
|
|||||||
}, 0);
|
}, 0);
|
||||||
});
|
});
|
||||||
|
|
||||||
const changeEvent = new Event('change', { bubbles: true });
|
const changeEvent = new Event('change', {
|
||||||
|
bubbles: true
|
||||||
|
});
|
||||||
input.dispatchEvent(changeEvent);
|
input.dispatchEvent(changeEvent);
|
||||||
} else if (element.tagName === 'INPUT' && element.type === 'text') {
|
} else if (element.tagName === 'INPUT' && element.type === 'text') {
|
||||||
const cell = element.closest('.grid-cell');
|
const cell = element.closest('.grid-cell');
|
||||||
@@ -1624,7 +1779,9 @@ function fixedDefaultValue(array $f): string
|
|||||||
button.addEventListener('click', async function() {
|
button.addEventListener('click', async function() {
|
||||||
const column = this.getAttribute('data-column');
|
const column = this.getAttribute('data-column');
|
||||||
const input = this.previousElementSibling;
|
const input = this.previousElementSibling;
|
||||||
const value = input.tagName === 'SELECT' ? input.value : input.value;
|
const value = $(input).hasClass('select2-hidden-accessible') ?
|
||||||
|
$(input).val() :
|
||||||
|
(input.tagName === 'SELECT' || input.tagName === 'INPUT' ? input.value : '');
|
||||||
|
|
||||||
console.log('Propagate clicked for column:', column, 'with value:', value); // Debug
|
console.log('Propagate clicked for column:', column, 'with value:', value); // Debug
|
||||||
|
|
||||||
@@ -1649,11 +1806,14 @@ function fixedDefaultValue(array $f): string
|
|||||||
if (targetInput) {
|
if (targetInput) {
|
||||||
console.log('Setting value on target input:', targetInput, 'with value:', value); // Debug
|
console.log('Setting value on target input:', targetInput, 'with value:', value); // Debug
|
||||||
if (targetInput.tagName === 'SELECT') {
|
if (targetInput.tagName === 'SELECT') {
|
||||||
|
if ($(targetInput).hasClass('select2-hidden-accessible')) {
|
||||||
|
$(targetInput).val(value).trigger('change');
|
||||||
|
} else {
|
||||||
targetInput.value = value;
|
targetInput.value = value;
|
||||||
const event = new Event('change', {
|
targetInput.dispatchEvent(new Event('change', {
|
||||||
bubbles: true
|
bubbles: true
|
||||||
});
|
}));
|
||||||
targetInput.dispatchEvent(event);
|
}
|
||||||
} else if (targetInput.classList.contains('date-picker')) {
|
} else if (targetInput.classList.contains('date-picker')) {
|
||||||
const flatpickrInstance = targetInput._flatpickr;
|
const flatpickrInstance = targetInput._flatpickr;
|
||||||
if (flatpickrInstance && value) {
|
if (flatpickrInstance && value) {
|
||||||
@@ -1678,6 +1838,16 @@ function fixedDefaultValue(array $f): string
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
requestAnimationFrame(() => {
|
||||||
|
setTimeout(() => {
|
||||||
|
autoFitColumnsByHeaderKeepWider({
|
||||||
|
minPx: 150,
|
||||||
|
maxPx: 380,
|
||||||
|
extraPx: 40,
|
||||||
|
ignoreIndexes: [] // es: ['2'] se vuoi escludere Client
|
||||||
|
});
|
||||||
|
}, 50);
|
||||||
|
});
|
||||||
|
|
||||||
// Gestione del ridimensionamento delle colonne
|
// Gestione del ridimensionamento delle colonne
|
||||||
const resizers = document.querySelectorAll('.resizer');
|
const resizers = document.querySelectorAll('.resizer');
|
||||||
@@ -1763,26 +1933,63 @@ function fixedDefaultValue(array $f): string
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const items = dropdownData[fieldId];
|
||||||
|
// ✅ Sort alphabetically by Valore (A→Z)
|
||||||
|
items.sort((a, b) => String(a.Valore || '').localeCompare(String(b.Valore || ''), 'it', {
|
||||||
|
sensitivity: 'base'
|
||||||
|
}));
|
||||||
|
const valToSelect = currentValue || selectedValue;
|
||||||
|
|
||||||
|
if (items.length > 12) {
|
||||||
|
// Select2 con ricerca
|
||||||
dropdown.innerHTML = '<option value="">Seleziona...</option>';
|
dropdown.innerHTML = '<option value="">Seleziona...</option>';
|
||||||
dropdownData[fieldId].forEach(value => {
|
items.forEach(value => {
|
||||||
const option = document.createElement('option');
|
const option = document.createElement('option');
|
||||||
option.value = value.IdCustomFieldsValue;
|
option.value = value.IdCustomFieldsValue;
|
||||||
option.textContent = value.Valore;
|
option.textContent = value.Valore;
|
||||||
if (currentValue && currentValue === String(value.IdCustomFieldsValue)) {
|
if (valToSelect && valToSelect === String(value.IdCustomFieldsValue)) {
|
||||||
option.selected = true;
|
|
||||||
} else if (!currentValue && selectedValue === String(value.IdCustomFieldsValue)) {
|
|
||||||
option.selected = true;
|
option.selected = true;
|
||||||
}
|
}
|
||||||
dropdown.appendChild(option);
|
dropdown.appendChild(option);
|
||||||
});
|
});
|
||||||
|
$(dropdown).select2({
|
||||||
// Ripristina il valore corrente
|
placeholder: "Seleziona...",
|
||||||
if (currentValue || selectedValue) {
|
allowClear: true,
|
||||||
dropdown.value = currentValue || selectedValue;
|
width: '100%'
|
||||||
const event = new Event('change', {
|
|
||||||
bubbles: true
|
|
||||||
});
|
});
|
||||||
dropdown.dispatchEvent(event);
|
// sposta la select DOPO il container Select2 così previousElementSibling del btn torna corretto
|
||||||
|
const s2container = $(dropdown).next('.select2-container');
|
||||||
|
if (s2container.length) {
|
||||||
|
s2container.after(dropdown);
|
||||||
|
}
|
||||||
|
if (valToSelect) {
|
||||||
|
dropdown.setAttribute('data-restoring', '');
|
||||||
|
$(dropdown).val(valToSelect).trigger('change');
|
||||||
|
dropdown.removeAttribute('data-restoring');
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// Select nativa
|
||||||
|
try {
|
||||||
|
$(dropdown).select2('destroy');
|
||||||
|
} catch (e) {}
|
||||||
|
dropdown.innerHTML = '<option value="">Seleziona...</option>';
|
||||||
|
items.forEach(value => {
|
||||||
|
const option = document.createElement('option');
|
||||||
|
option.value = value.IdCustomFieldsValue;
|
||||||
|
option.textContent = value.Valore;
|
||||||
|
if (valToSelect && valToSelect === String(value.IdCustomFieldsValue)) {
|
||||||
|
option.selected = true;
|
||||||
|
}
|
||||||
|
dropdown.appendChild(option);
|
||||||
|
});
|
||||||
|
if (valToSelect) {
|
||||||
|
dropdown.value = valToSelect;
|
||||||
|
dropdown.setAttribute('data-restoring', '');
|
||||||
|
dropdown.dispatchEvent(new Event('change', {
|
||||||
|
bubbles: true
|
||||||
|
}));
|
||||||
|
dropdown.removeAttribute('data-restoring');
|
||||||
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@@ -1820,8 +2027,13 @@ function fixedDefaultValue(array $f): string
|
|||||||
clientData.forEach(client => {
|
clientData.forEach(client => {
|
||||||
const nome = client.Nominativo || "Nome non disponibile";
|
const nome = client.Nominativo || "Nome non disponibile";
|
||||||
const id = client.IdCliente || "ID non disponibile";
|
const id = client.IdCliente || "ID non disponibile";
|
||||||
const codice = (client.CodiceNazioneFatturazione || '').trim();
|
|
||||||
const option = new Option(`${nome.trim()} - ${codice} (ID: ${id})`, id);
|
// CodiceCliente es: "Bl01858_E" -> vogliamo "E"
|
||||||
|
const codiceCliente = (client.CodiceCliente || '').toString().trim();
|
||||||
|
const suffix = (codiceCliente.split('_')[1] || '').trim(); // parte dopo "_"
|
||||||
|
const shortCode = suffix || '--';
|
||||||
|
|
||||||
|
const option = new Option(`${nome.trim()} - ${shortCode} (ID: ${id})`, id);
|
||||||
|
|
||||||
if (parseInt(id) === parseInt(<?php echo json_encode($default_idclient ?? 0); ?>)) {
|
if (parseInt(id) === parseInt(<?php echo json_encode($default_idclient ?? 0); ?>)) {
|
||||||
option.selected = true;
|
option.selected = true;
|
||||||
@@ -1852,8 +2064,13 @@ function fixedDefaultValue(array $f): string
|
|||||||
clientData.forEach(client => {
|
clientData.forEach(client => {
|
||||||
const nome = client.Nominativo || "Nome non disponibile";
|
const nome = client.Nominativo || "Nome non disponibile";
|
||||||
const id = client.IdCliente || "ID non disponibile";
|
const id = client.IdCliente || "ID non disponibile";
|
||||||
const codice = (client.CodiceNazioneFatturazione || '').trim();
|
|
||||||
const option = new Option(`${nome.trim()} - ${codice} (ID: ${id})`, id);
|
// CodiceCliente es: "Bl01858_E" -> vogliamo "E"
|
||||||
|
const codiceCliente = (client.CodiceCliente || '').toString().trim();
|
||||||
|
const suffix = (codiceCliente.split('_')[1] || '').trim(); // parte dopo "_"
|
||||||
|
const shortCode = suffix || '--';
|
||||||
|
|
||||||
|
const option = new Option(`${nome.trim()} - ${shortCode} (ID: ${id})`, id);
|
||||||
|
|
||||||
if (String(id) === String(currentValue)) {
|
if (String(id) === String(currentValue)) {
|
||||||
option.selected = true;
|
option.selected = true;
|
||||||
@@ -1864,10 +2081,12 @@ function fixedDefaultValue(array $f): string
|
|||||||
// Ripristina il valore corrente
|
// Ripristina il valore corrente
|
||||||
if (currentValue) {
|
if (currentValue) {
|
||||||
dropdown.value = currentValue;
|
dropdown.value = currentValue;
|
||||||
|
dropdown.setAttribute('data-restoring', '');
|
||||||
const event = new Event('change', {
|
const event = new Event('change', {
|
||||||
bubbles: true
|
bubbles: true
|
||||||
});
|
});
|
||||||
dropdown.dispatchEvent(event);
|
dropdown.dispatchEvent(event);
|
||||||
|
dropdown.removeAttribute('data-restoring');
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@@ -1952,6 +2171,98 @@ function fixedDefaultValue(array $f): string
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// Quick add part from "Tested Component" (+ button)
|
||||||
|
$(document).on('click', '.add-part-btn', async function() {
|
||||||
|
const rowIndex = $(this).data('row');
|
||||||
|
const $row = $(this).closest('.grid-row');
|
||||||
|
const iddatadb = $row.data('id');
|
||||||
|
|
||||||
|
// Input inside the same cell
|
||||||
|
const $cell = $(this).closest('.grid-cell');
|
||||||
|
const $input = $cell.find('input[name^="rows["][name$="[tested_component]"]');
|
||||||
|
|
||||||
|
const raw = ($input.val() || '').trim();
|
||||||
|
|
||||||
|
// ✅ split multiplo con "|"
|
||||||
|
const parts = raw
|
||||||
|
.split('|')
|
||||||
|
.map(s => s.trim())
|
||||||
|
.filter(s => s.length > 0);
|
||||||
|
|
||||||
|
// (opzionale) dedup per evitare doppioni tipo "Cap | Cap"
|
||||||
|
const uniqueParts = [...new Set(parts)];
|
||||||
|
|
||||||
|
if (!uniqueParts.length) {
|
||||||
|
alert('⚠️ Inserisci prima una descrizione nel campo Tested Component.');
|
||||||
|
$input.focus();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
let okCount = 0;
|
||||||
|
const errors = [];
|
||||||
|
|
||||||
|
// ✅ esegue una chiamata per ogni parte (compatibile con l’attuale add_part_quick.php)
|
||||||
|
for (const p of uniqueParts) {
|
||||||
|
const formData = new FormData();
|
||||||
|
formData.append('iddatadb', iddatadb);
|
||||||
|
formData.append('part_description', p);
|
||||||
|
|
||||||
|
const resp = await fetch('add_part_quick.php', {
|
||||||
|
method: 'POST',
|
||||||
|
body: formData
|
||||||
|
});
|
||||||
|
|
||||||
|
let data = {};
|
||||||
|
try {
|
||||||
|
data = await resp.json();
|
||||||
|
} catch (e) {}
|
||||||
|
|
||||||
|
if (!resp.ok || !data.success) {
|
||||||
|
errors.push(`"${p}": ${(data && data.message) ? data.message : ('HTTP ' + resp.status)}`);
|
||||||
|
} else {
|
||||||
|
okCount++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// feedback UI
|
||||||
|
$input.addClass('cell-changed');
|
||||||
|
setTimeout(() => $input.removeClass('cell-changed'), 1200);
|
||||||
|
$input.trigger('change');
|
||||||
|
|
||||||
|
if (errors.length === 0) {
|
||||||
|
showQuickPartModal(`✅ Parti aggiunte: ${okCount}`);
|
||||||
|
} else {
|
||||||
|
// aggiunge comunque se alcune sono ok
|
||||||
|
showQuickPartModal(`⚠️ Parti aggiunte: ${okCount}\nErrori:\n- ${errors.join('\n- ')}`);
|
||||||
|
console.error('Add parts errors:', errors);
|
||||||
|
}
|
||||||
|
|
||||||
|
} catch (err) {
|
||||||
|
alert('❌ Errore aggiunta parti: ' + err.message);
|
||||||
|
console.error(err);
|
||||||
|
}
|
||||||
|
|
||||||
|
});
|
||||||
|
|
||||||
|
function showQuickPartModal(message) {
|
||||||
|
const modalEl = document.getElementById('quickPartModal');
|
||||||
|
if (!modalEl) {
|
||||||
|
alert(message); // fallback se il modal non esiste
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
document.getElementById('quickPartModalMessage').textContent = message;
|
||||||
|
|
||||||
|
let modal = bootstrap.Modal.getInstance(modalEl);
|
||||||
|
if (!modal) modal = new bootstrap.Modal(modalEl, {
|
||||||
|
backdrop: true,
|
||||||
|
keyboard: true
|
||||||
|
});
|
||||||
|
modal.show();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
$(document).on('click', '.parts-btn', function() {
|
$(document).on('click', '.parts-btn', function() {
|
||||||
const iddatadb = $(this).data('iddatadb') || null;
|
const iddatadb = $(this).data('iddatadb') || null;
|
||||||
const idquotations = $(this).data('idquotations') || null;
|
const idquotations = $(this).data('idquotations') || null;
|
||||||
@@ -2116,6 +2427,12 @@ function fixedDefaultValue(array $f): string
|
|||||||
id: item[config.idKey],
|
id: item[config.idKey],
|
||||||
text: (item.Codice ? item.Codice + ' - ' : '') + (item[config.textKey] || 'Senza nome')
|
text: (item.Codice ? item.Codice + ' - ' : '') + (item[config.textKey] || 'Senza nome')
|
||||||
}));
|
}));
|
||||||
|
|
||||||
|
// ✅ Sort alphabetically by text
|
||||||
|
results.sort((a, b) => String(a.text || '').localeCompare(String(b.text || ''), 'it', {
|
||||||
|
sensitivity: 'base'
|
||||||
|
}));
|
||||||
|
|
||||||
fixedFieldDataCache[cacheKey] = results;
|
fixedFieldDataCache[cacheKey] = results;
|
||||||
return results;
|
return results;
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
@@ -2175,10 +2492,16 @@ function fixedDefaultValue(array $f): string
|
|||||||
id: item[config.idKey],
|
id: item[config.idKey],
|
||||||
text: (item.Codice ? item.Codice + ' - ' : '') + (item[config.textKey] || 'Senza nome')
|
text: (item.Codice ? item.Codice + ' - ' : '') + (item[config.textKey] || 'Senza nome')
|
||||||
}));
|
}));
|
||||||
|
// ✅ Sort alphabetically by text
|
||||||
|
results.sort((a, b) => String(a.text || '').localeCompare(String(b.text || ''), 'it', {
|
||||||
|
sensitivity: 'base'
|
||||||
|
}));
|
||||||
} else {
|
} else {
|
||||||
results = await loadFixedFieldData(fieldKey);
|
results = await loadFixedFieldData(fieldKey);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (results.length > 12) {
|
||||||
|
// Select2 con ricerca
|
||||||
$select.select2('destroy').empty().select2({
|
$select.select2('destroy').empty().select2({
|
||||||
data: [{
|
data: [{
|
||||||
id: '',
|
id: '',
|
||||||
@@ -2188,12 +2511,26 @@ function fixedDefaultValue(array $f): string
|
|||||||
allowClear: true,
|
allowClear: true,
|
||||||
width: '100%'
|
width: '100%'
|
||||||
});
|
});
|
||||||
|
} else {
|
||||||
|
// Select nativa senza Select2
|
||||||
|
try {
|
||||||
|
$select.select2('destroy');
|
||||||
|
} catch (e) {}
|
||||||
|
$select.empty();
|
||||||
|
$select.append(new Option('Seleziona...', '', true, false));
|
||||||
|
results.forEach(r => {
|
||||||
|
$select.append(new Option(r.text, r.id));
|
||||||
|
});
|
||||||
|
$select.css('width', '100%');
|
||||||
|
}
|
||||||
|
|
||||||
// Imposta valore iniziale
|
// Imposta valore iniziale
|
||||||
if (currentVal) {
|
if (currentVal) {
|
||||||
const found = results.find(r => String(r.id) === String(currentVal));
|
const found = results.find(r => String(r.id) === String(currentVal));
|
||||||
if (found) {
|
if (found) {
|
||||||
|
$select[0].setAttribute('data-restoring', '');
|
||||||
$select.val(currentVal).trigger('change');
|
$select.val(currentVal).trigger('change');
|
||||||
|
$select[0].removeAttribute('data-restoring');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -2285,6 +2622,64 @@ function fixedDefaultValue(array $f): string
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
</script>
|
</script>
|
||||||
|
<script>
|
||||||
|
function autoFitColumnsByHeaderKeepWider({
|
||||||
|
minPx = 150,
|
||||||
|
maxPx = 380, // limite solo per l'auto-fit (NON riduce se già più largo)
|
||||||
|
extraPx = 40,
|
||||||
|
ignoreIndexes = []
|
||||||
|
} = {}) {
|
||||||
|
|
||||||
|
// misura testo con stesso font dell'header
|
||||||
|
const measurer = document.createElement('span');
|
||||||
|
measurer.style.position = 'absolute';
|
||||||
|
measurer.style.visibility = 'hidden';
|
||||||
|
measurer.style.whiteSpace = 'nowrap';
|
||||||
|
measurer.style.left = '-99999px';
|
||||||
|
measurer.style.top = '-99999px';
|
||||||
|
document.body.appendChild(measurer);
|
||||||
|
|
||||||
|
const headers = document.querySelectorAll('.grid-row .grid-header[data-index]');
|
||||||
|
headers.forEach(header => {
|
||||||
|
const idx = header.getAttribute('data-index');
|
||||||
|
if (!idx || ignoreIndexes.includes(String(idx))) return;
|
||||||
|
|
||||||
|
// testo header senza il resizer
|
||||||
|
const clone = header.cloneNode(true);
|
||||||
|
clone.querySelectorAll('.resizer').forEach(r => r.remove());
|
||||||
|
const label = (clone.textContent || '').replace(/\s+/g, ' ').trim();
|
||||||
|
|
||||||
|
// misura
|
||||||
|
const headerStyle = window.getComputedStyle(header);
|
||||||
|
measurer.style.font = headerStyle.font; // importante: usa stesso font completo
|
||||||
|
measurer.textContent = label;
|
||||||
|
|
||||||
|
let desired = Math.ceil(measurer.offsetWidth + extraPx);
|
||||||
|
desired = Math.max(minPx, desired);
|
||||||
|
desired = Math.min(maxPx, desired); // clamp SOLO della misura calcolata
|
||||||
|
|
||||||
|
// larghezza corrente (flex-basis), fallback a offsetWidth
|
||||||
|
const cs = window.getComputedStyle(header);
|
||||||
|
let current = parseFloat(cs.flexBasis);
|
||||||
|
if (Number.isNaN(current) || !current) current = header.offsetWidth;
|
||||||
|
|
||||||
|
// ✅ non restringere mai
|
||||||
|
const finalWidth = Math.max(current, desired);
|
||||||
|
|
||||||
|
// applica a header + celle + top
|
||||||
|
const targets = document.querySelectorAll(
|
||||||
|
`.grid-header[data-index="${idx}"], .grid-cell[data-index="${idx}"], .grid-top .grid-cell[data-index="${idx}"]`
|
||||||
|
);
|
||||||
|
|
||||||
|
targets.forEach(el => {
|
||||||
|
el.style.flex = `0 0 ${finalWidth}px`;
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
measurer.remove();
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
<!-- Modale di conferma per l'esportazione -->
|
<!-- Modale di conferma per l'esportazione -->
|
||||||
<div class="modal fade" id="exportConfirmModal" tabindex="-1" aria-labelledby="exportConfirmModalLabel" aria-hidden="true">
|
<div class="modal fade" id="exportConfirmModal" tabindex="-1" aria-labelledby="exportConfirmModalLabel" aria-hidden="true">
|
||||||
<div class="modal-dialog">
|
<div class="modal-dialog">
|
||||||
@@ -2322,6 +2717,35 @@ function fixedDefaultValue(array $f): string
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<!-- Modal: unsaved changes before export -->
|
||||||
|
<div class="modal fade" id="exportUnsavedModal" tabindex="-1" aria-hidden="true">
|
||||||
|
<div class="modal-dialog">
|
||||||
|
<div class="modal-content">
|
||||||
|
<div class="modal-header">
|
||||||
|
<h5 class="modal-title">Modifiche non salvate</h5>
|
||||||
|
<button type="button" class="btn-close" data-bs-dismiss="modal"></button>
|
||||||
|
</div>
|
||||||
|
<div class="modal-body">
|
||||||
|
La riga contiene modifiche non salvate. Salvare prima di procedere con l'esportazione?
|
||||||
|
</div>
|
||||||
|
<div class="modal-footer">
|
||||||
|
<button type="button" class="btn btn-secondary btn-sm" data-bs-dismiss="modal">Annulla</button>
|
||||||
|
<button type="button" class="btn btn-primary btn-sm" id="saveAndExportBtn">Salva ed Esporta</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Toast: row saved successfully -->
|
||||||
|
<div class="position-fixed bottom-0 end-0 p-3" style="z-index:9999">
|
||||||
|
<div id="saveSuccessToast" class="toast align-items-center text-bg-success border-0" role="alert" data-bs-delay="2500">
|
||||||
|
<div class="d-flex">
|
||||||
|
<div class="toast-body"><i class="fas fa-check-circle me-2"></i>Riga salvata con successo.</div>
|
||||||
|
<button type="button" class="btn-close btn-close-white me-2 m-auto" data-bs-dismiss="toast"></button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
</body>
|
</body>
|
||||||
|
|
||||||
</html>
|
</html>
|
||||||
@@ -20,9 +20,10 @@ if ($_SERVER['REQUEST_METHOD'] !== 'POST' || !isset($_POST['template_id']) || !i
|
|||||||
|
|
||||||
$template_id = intval($_POST['template_id']);
|
$template_id = intval($_POST['template_id']);
|
||||||
$selected_rows = array_map('intval', $_POST['selected_rows']);
|
$selected_rows = array_map('intval', $_POST['selected_rows']);
|
||||||
$columns = json_decode($_POST['columns'], true);
|
$columns = json_decode(urldecode($_POST['columns'] ?? '[]'), true);
|
||||||
$rows = json_decode($_POST['rows'], true);
|
$rows = json_decode(urldecode($_POST['rows'] ?? '[]'), true);
|
||||||
$excelrows = json_decode($_POST['excelrows'], true);
|
$excelrows = json_decode(urldecode($_POST['excelrows'] ?? '[]'), true);
|
||||||
|
|
||||||
$newFilename = htmlspecialchars($_POST['filename']);
|
$newFilename = htmlspecialchars($_POST['filename']);
|
||||||
|
|
||||||
$_SESSION['template_id'] = $template_id;
|
$_SESSION['template_id'] = $template_id;
|
||||||
|
|||||||
@@ -121,6 +121,16 @@ error_log("Loaded template: " . print_r($template, true));
|
|||||||
transform: rotate(360deg);
|
transform: rotate(360deg);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.column-filters th {
|
||||||
|
background: #ffffff;
|
||||||
|
cursor: default;
|
||||||
|
}
|
||||||
|
|
||||||
|
.column-filters input {
|
||||||
|
width: 100%;
|
||||||
|
min-width: 80px;
|
||||||
|
}
|
||||||
</style>
|
</style>
|
||||||
<title><?= htmlspecialchars($template['name']) ?> - <?= htmlspecialchars($titlewebsite, ENT_QUOTES, 'UTF-8'); ?></title>
|
<title><?= htmlspecialchars($template['name']) ?> - <?= htmlspecialchars($titlewebsite, ENT_QUOTES, 'UTF-8'); ?></title>
|
||||||
</head>
|
</head>
|
||||||
@@ -305,13 +315,12 @@ error_log("Loaded template: " . print_r($template, true));
|
|||||||
let html = `
|
let html = `
|
||||||
<form id="selectRowsForm" action="import_insert.php" method="POST">
|
<form id="selectRowsForm" action="import_insert.php" method="POST">
|
||||||
<input type="hidden" name="template_id" value="${data.template_id}">
|
<input type="hidden" name="template_id" value="${data.template_id}">
|
||||||
<input type="hidden" name="columns" value='${JSON.stringify(data.columns)}'>
|
<input type="hidden" name="columns" value="${encodeURIComponent(JSON.stringify(data.columns))}">
|
||||||
<input type="hidden" name="rows" value='${JSON.stringify(data.rows)}'>
|
<input type="hidden" name="rows" value="${encodeURIComponent(JSON.stringify(data.rows))}">
|
||||||
<input type="hidden" name="excelrows" value='${JSON.stringify(data.excel_data.map(row => row.excelrow))}'>
|
<input type="hidden" name="excelrows" value="${encodeURIComponent(JSON.stringify(data.excel_data.map(r => r.excelrow)))}">
|
||||||
|
|
||||||
<input type="hidden" name="filename" value="${data.filename}">
|
<input type="hidden" name="filename" value="${data.filename}">
|
||||||
<div class="search-container">
|
|
||||||
<input type="text" id="searchInput" class="form-control" placeholder="Cerca nelle righe...">
|
|
||||||
</div>
|
|
||||||
<div class="table-container">
|
<div class="table-container">
|
||||||
<table class="table table-striped table-bordered">
|
<table class="table table-striped table-bordered">
|
||||||
<thead>
|
<thead>
|
||||||
@@ -319,6 +328,17 @@ error_log("Loaded template: " . print_r($template, true));
|
|||||||
<th><input type="checkbox" id="selectAll"> Seleziona</th>
|
<th><input type="checkbox" id="selectAll"> Seleziona</th>
|
||||||
${data.columns.map(col => `<th>${col || 'Colonna senza nome'}<div class="resize-handle"></div></th>`).join('')}
|
${data.columns.map(col => `<th>${col || 'Colonna senza nome'}<div class="resize-handle"></div></th>`).join('')}
|
||||||
</tr>
|
</tr>
|
||||||
|
<tr class="column-filters">
|
||||||
|
<th></th>
|
||||||
|
${data.columns.map((col, i) => `
|
||||||
|
<th>
|
||||||
|
<input type="text"
|
||||||
|
class="form-control form-control-sm column-filter"
|
||||||
|
data-col-index="${i}"
|
||||||
|
placeholder="Filter...">
|
||||||
|
</th>
|
||||||
|
`).join('')}
|
||||||
|
</tr>
|
||||||
</thead>
|
</thead>
|
||||||
<tbody>
|
<tbody>
|
||||||
${data.excel_data.map((row, index) => `
|
${data.excel_data.map((row, index) => `
|
||||||
@@ -393,14 +413,39 @@ error_log("Loaded template: " . print_r($template, true));
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
const searchInput = document.getElementById('searchInput');
|
|
||||||
const rows = document.querySelectorAll('.table tbody tr');
|
const rows = document.querySelectorAll('.table tbody tr');
|
||||||
|
const filterInputs = document.querySelectorAll('.column-filter');
|
||||||
|
|
||||||
searchInput.addEventListener('input', function() {
|
// Stato filtri: key = colIndex, value = testo
|
||||||
const searchTerm = this.value.toLowerCase();
|
const activeFilters = {};
|
||||||
|
|
||||||
|
function applyColumnFilters() {
|
||||||
rows.forEach(row => {
|
rows.forEach(row => {
|
||||||
const text = Array.from(row.cells).slice(1).map(cell => cell.textContent.toLowerCase()).join(' ');
|
// Le celle di data partono da index 1 (perché index 0 è checkbox)
|
||||||
row.style.display = text.includes(searchTerm) ? '' : 'none';
|
let visible = true;
|
||||||
|
|
||||||
|
for (const [colIndexStr, filterValue] of Object.entries(activeFilters)) {
|
||||||
|
const colIndex = parseInt(colIndexStr, 10); // 0..N-1 sulle colonne dati
|
||||||
|
const cell = row.cells[colIndex + 1]; // +1 per saltare la colonna checkbox
|
||||||
|
|
||||||
|
const cellText = (cell?.textContent || '').toLowerCase();
|
||||||
|
const searchText = (filterValue || '').toLowerCase().trim();
|
||||||
|
|
||||||
|
if (searchText && !cellText.includes(searchText)) {
|
||||||
|
visible = false;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
row.style.display = visible ? '' : 'none';
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
filterInputs.forEach(input => {
|
||||||
|
input.addEventListener('input', function() {
|
||||||
|
const colIndex = this.dataset.colIndex; // string
|
||||||
|
activeFilters[colIndex] = this.value;
|
||||||
|
applyColumnFilters();
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
@@ -35,8 +35,6 @@ $kindofrole = $user->present()->role_id;
|
|||||||
//$iduserlogin="1";
|
//$iduserlogin="1";
|
||||||
//$nameuser="Claudio";
|
//$nameuser="Claudio";
|
||||||
//$emailuser="info@claudiosironi.com";
|
//$emailuser="info@claudiosironi.com";
|
||||||
?>
|
|
||||||
<?php
|
|
||||||
if (session_status() == PHP_SESSION_NONE) {
|
if (session_status() == PHP_SESSION_NONE) {
|
||||||
session_start();
|
session_start();
|
||||||
}
|
}
|
||||||
@@ -49,13 +47,8 @@ $_SESSION["emailuser"] = $emailuser;
|
|||||||
$_SESSION["photouser"] = $avatar;
|
$_SESSION["photouser"] = $avatar;
|
||||||
$photouser = $_SESSION["photouser"];
|
$photouser = $_SESSION["photouser"];
|
||||||
$photousername = basename($avatar);
|
$photousername = basename($avatar);
|
||||||
?>
|
|
||||||
|
|
||||||
|
|
||||||
<?php //include files
|
|
||||||
|
|
||||||
|
//include files
|
||||||
require_once(__DIR__ . '/../../languages/en/general.php');
|
require_once(__DIR__ . '/../../languages/en/general.php');
|
||||||
|
|
||||||
//include("generalsettings.php");
|
//include("generalsettings.php");
|
||||||
|
|
||||||
?>
|
|
||||||
@@ -21,7 +21,7 @@ $schemajson = $template['schemajson'] ? json_decode($template['schemajson'], tru
|
|||||||
$isSchemajsonEmpty = empty(trim($template['schemajson']));
|
$isSchemajsonEmpty = empty(trim($template['schemajson']));
|
||||||
|
|
||||||
// Recupera i campi dalla tabella template_mapping
|
// Recupera i campi dalla tabella template_mapping
|
||||||
$stmt = $pdo->prepare("SELECT id, field_id, excel_column, is_manual, manual_default, 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 FROM template_mapping WHERE template_id = ?");
|
$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->execute([$id]);
|
$stmt->execute([$id]);
|
||||||
$mappings = $stmt->fetchAll(PDO::FETCH_ASSOC);
|
$mappings = $stmt->fetchAll(PDO::FETCH_ASSOC);
|
||||||
|
|
||||||
@@ -108,6 +108,14 @@ $xlsHeaders = $template['xls_headers'] ? json_decode($template['xls_headers'], t
|
|||||||
.select2-container {
|
.select2-container {
|
||||||
width: 100% !important;
|
width: 100% !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Make Title column narrower + ellipsis */
|
||||||
|
#schemaFieldsTable td.title-col {
|
||||||
|
max-width: 320px;
|
||||||
|
white-space: nowrap;
|
||||||
|
overflow: hidden;
|
||||||
|
text-overflow: ellipsis;
|
||||||
|
}
|
||||||
</style>
|
</style>
|
||||||
</head>
|
</head>
|
||||||
|
|
||||||
@@ -157,9 +165,10 @@ $xlsHeaders = $template['xls_headers'] ? json_decode($template['xls_headers'], t
|
|||||||
<table id="schemaFieldsTable" class="table table-striped">
|
<table id="schemaFieldsTable" class="table table-striped">
|
||||||
<thead>
|
<thead>
|
||||||
<tr>
|
<tr>
|
||||||
<th style="width:100px; text-align:center;">Main</th>
|
<th style="width:90px; text-align:center;">Main</th>
|
||||||
<th style="width:120px; text-align:center;">Visible on Import</th>
|
<th style="width:110px; text-align:center;">Import</th>
|
||||||
<th>Title</th>
|
<th style="width:110px; text-align:center;">Parts</th>
|
||||||
|
<th style="width:320px;">Title</th>
|
||||||
<th style="width:140px;">Type</th>
|
<th style="width:140px;">Type</th>
|
||||||
<th>Mapping</th>
|
<th>Mapping</th>
|
||||||
<th>Default Value</th>
|
<th>Default Value</th>
|
||||||
@@ -169,6 +178,7 @@ $xlsHeaders = $template['xls_headers'] ? json_decode($template['xls_headers'], t
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
<tbody id="schemaFieldsBody">
|
<tbody id="schemaFieldsBody">
|
||||||
<?php foreach ($mappings as $mapping): ?>
|
<?php foreach ($mappings as $mapping): ?>
|
||||||
<tr>
|
<tr>
|
||||||
@@ -185,22 +195,34 @@ $xlsHeaders = $template['xls_headers'] ? json_decode($template['xls_headers'], t
|
|||||||
data-mapping-id="<?php echo (int)$mapping['id']; ?>"
|
data-mapping-id="<?php echo (int)$mapping['id']; ?>"
|
||||||
<?php echo ((int)($mapping['is_visible_import'] ?? 1) === 1) ? 'checked' : ''; ?>>
|
<?php echo ((int)($mapping['is_visible_import'] ?? 1) === 1) ? 'checked' : ''; ?>>
|
||||||
</td>
|
</td>
|
||||||
|
<td class="text-center">
|
||||||
|
<input type="checkbox"
|
||||||
|
class="visible-parts-checkbox"
|
||||||
|
data-mapping-id="<?php echo (int)$mapping['id']; ?>"
|
||||||
|
<?php echo ((int)($mapping['is_visible_parts'] ?? 0) === 1) ? 'checked' : ''; ?>>
|
||||||
|
</td>
|
||||||
|
|
||||||
<td>
|
|
||||||
|
<td class="title-col">
|
||||||
<?php echo htmlspecialchars($mapping['field_label'] ?? 'N/A'); ?>
|
<?php echo htmlspecialchars($mapping['field_label'] ?? 'N/A'); ?>
|
||||||
<?php if ((int)$mapping['is_required'] === 1): ?>
|
<?php if ((int)$mapping['is_required'] === 1): ?>
|
||||||
<span class="badge bg-danger ms-2">Required</span>
|
<span class="badge bg-danger ms-2">Required</span>
|
||||||
<?php endif; ?>
|
<?php endif; ?>
|
||||||
</td>
|
</td>
|
||||||
|
|
||||||
|
|
||||||
<td><?php echo htmlspecialchars($mapping['data_type'] ?? 'N/A'); ?></td>
|
<td><?php echo htmlspecialchars($mapping['data_type'] ?? 'N/A'); ?></td>
|
||||||
|
|
||||||
<td>
|
<td>
|
||||||
<?php
|
<?php
|
||||||
$isSceltaMultipla = ($mapping['data_type'] === 'SceltaMultipla');
|
$isSceltaMultipla = ($mapping['data_type'] === 'SceltaMultipla');
|
||||||
|
|
||||||
|
$autoValue = $mapping['auto_value'] ?? 'none';
|
||||||
|
$hasAuto = ($autoValue && $autoValue !== 'none');
|
||||||
|
|
||||||
$mappingValue = $isSceltaMultipla
|
$mappingValue = $isSceltaMultipla
|
||||||
? 'manual'
|
? 'manual'
|
||||||
: ($mapping['excel_column'] ? 'xls' : ((int)$mapping['is_manual'] === 1 ? 'manual' : ''));
|
: ($hasAuto ? 'auto' : ($mapping['excel_column'] ? 'xls' : ((int)$mapping['is_manual'] === 1 ? 'manual' : '')));
|
||||||
?>
|
?>
|
||||||
|
|
||||||
<select class="form-select mapping-select"
|
<select class="form-select mapping-select"
|
||||||
@@ -211,6 +233,7 @@ $xlsHeaders = $template['xls_headers'] ? json_decode($template['xls_headers'], t
|
|||||||
<?php if (!$isSceltaMultipla): ?>
|
<?php if (!$isSceltaMultipla): ?>
|
||||||
<option value="">Select Option</option>
|
<option value="">Select Option</option>
|
||||||
<option value="xls" <?php echo ($mappingValue === 'xls') ? 'selected' : ''; ?>>Map to XLS Column</option>
|
<option value="xls" <?php echo ($mappingValue === 'xls') ? 'selected' : ''; ?>>Map to XLS Column</option>
|
||||||
|
<option value="auto" <?php echo ($mappingValue === 'auto') ? 'selected' : ''; ?>>Auto value</option>
|
||||||
<?php endif; ?>
|
<?php endif; ?>
|
||||||
|
|
||||||
<option value="manual" <?php echo ($mappingValue === 'manual') ? 'selected' : ''; ?>>Manual Entry</option>
|
<option value="manual" <?php echo ($mappingValue === 'manual') ? 'selected' : ''; ?>>Manual Entry</option>
|
||||||
@@ -222,6 +245,16 @@ $xlsHeaders = $template['xls_headers'] ? json_decode($template['xls_headers'], t
|
|||||||
<?php echo $mapping['excel_column'] ? 'data-current-xls="' . htmlspecialchars($mapping['excel_column']) . '"' : ''; ?>>
|
<?php echo $mapping['excel_column'] ? 'data-current-xls="' . htmlspecialchars($mapping['excel_column']) . '"' : ''; ?>>
|
||||||
</select>
|
</select>
|
||||||
|
|
||||||
|
<select class="form-select auto-value-select"
|
||||||
|
style="display:<?php echo ($mappingValue === 'auto') ? 'block' : 'none'; ?>; margin-top:6px;"
|
||||||
|
data-id="<?php echo (int)$mapping['id']; ?>">
|
||||||
|
<option value="none">Select auto value</option>
|
||||||
|
<option value="import_date" <?php echo ($autoValue === 'import_date') ? 'selected' : ''; ?>>Current date (import)</option>
|
||||||
|
<option value="import_time" <?php echo ($autoValue === 'import_time') ? 'selected' : ''; ?>>Current time (import)</option>
|
||||||
|
<option value="export_date" <?php echo ($autoValue === 'export_date') ? 'selected' : ''; ?>>Current date (export)</option>
|
||||||
|
<option value="export_time" <?php echo ($autoValue === 'export_time') ? 'selected' : ''; ?>>Current time (export)</option>
|
||||||
|
</select>
|
||||||
|
|
||||||
<?php if (!empty($mapping['excel_column'])): ?>
|
<?php if (!empty($mapping['excel_column'])): ?>
|
||||||
<span class="mapped-column" style="margin-left:5px;">
|
<span class="mapped-column" style="margin-left:5px;">
|
||||||
(<?php echo htmlspecialchars($mapping['excel_column']); ?>)
|
(<?php echo htmlspecialchars($mapping['excel_column']); ?>)
|
||||||
@@ -248,7 +281,7 @@ $xlsHeaders = $template['xls_headers'] ? json_decode($template['xls_headers'], t
|
|||||||
class="form-control manual-default"
|
class="form-control manual-default"
|
||||||
placeholder="Default value"
|
placeholder="Default value"
|
||||||
value="<?php echo htmlspecialchars($mapping['manual_default'] ?? ''); ?>"
|
value="<?php echo htmlspecialchars($mapping['manual_default'] ?? ''); ?>"
|
||||||
style="display:<?php echo ((int)$mapping['is_manual'] === 1) ? 'block' : 'none'; ?>"
|
style="display:<?php echo ($mappingValue === 'manual') ? 'block' : 'none'; ?>"
|
||||||
data-field-id="<?php echo (int)$mapping['field_id']; ?>">
|
data-field-id="<?php echo (int)$mapping['field_id']; ?>">
|
||||||
<?php endif; ?>
|
<?php endif; ?>
|
||||||
</td>
|
</td>
|
||||||
@@ -665,6 +698,61 @@ $xlsHeaders = $template['xls_headers'] ? json_decode($template['xls_headers'], t
|
|||||||
|
|
||||||
loadDropdownsWithOverlay();
|
loadDropdownsWithOverlay();
|
||||||
|
|
||||||
|
// =======================
|
||||||
|
// SAVE: SceltaMultipla dropdown (Select2-safe)
|
||||||
|
// =======================
|
||||||
|
|
||||||
|
// 1) listener nativo a livello document (più robusto del delegation su tbody)
|
||||||
|
document.addEventListener('change', function(event) {
|
||||||
|
const el = event.target;
|
||||||
|
if (!(el && el.matches && el.matches('select.manual-default.dropdown-select'))) return;
|
||||||
|
|
||||||
|
const tr = el.closest('tr');
|
||||||
|
const mappingId =
|
||||||
|
el.getAttribute('data-id') ||
|
||||||
|
tr?.querySelector('.mapping-select')?.getAttribute('data-id');
|
||||||
|
|
||||||
|
const xlsSelect = tr?.querySelector('.xls-columns');
|
||||||
|
|
||||||
|
console.log('[SceltaMultipla change] saving', {
|
||||||
|
mappingId,
|
||||||
|
value: el.value
|
||||||
|
});
|
||||||
|
|
||||||
|
if (!mappingId) {
|
||||||
|
console.error('❌ Missing mappingId for SceltaMultipla dropdown');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
saveMapping(mappingId, 'manual', el.value, xlsSelect ? xlsSelect.value : null);
|
||||||
|
});
|
||||||
|
|
||||||
|
// 2) eventi Select2 (quando Select2 “mangia” il change)
|
||||||
|
if (window.jQuery) {
|
||||||
|
$(document).on('select2:select select2:clear', 'select.manual-default.dropdown-select', function() {
|
||||||
|
const el = this;
|
||||||
|
const tr = el.closest('tr');
|
||||||
|
const mappingId =
|
||||||
|
el.getAttribute('data-id') ||
|
||||||
|
tr?.querySelector('.mapping-select')?.getAttribute('data-id');
|
||||||
|
|
||||||
|
const xlsSelect = tr?.querySelector('.xls-columns');
|
||||||
|
|
||||||
|
console.log('[SceltaMultipla select2] saving', {
|
||||||
|
mappingId,
|
||||||
|
value: el.value
|
||||||
|
});
|
||||||
|
|
||||||
|
if (!mappingId) {
|
||||||
|
console.error('❌ Missing mappingId for SceltaMultipla dropdown (select2)');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
saveMapping(mappingId, 'manual', el.value, xlsSelect ? xlsSelect.value : null);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
async function loadClientAndSchemaNames() {
|
async function loadClientAndSchemaNames() {
|
||||||
if (<?php echo json_encode($template['idclient'] ?? 0); ?> > 0) {
|
if (<?php echo json_encode($template['idclient'] ?? 0); ?> > 0) {
|
||||||
let response = await fetch(`get_clienti.php?id=<?php echo $template['idclient']; ?>`);
|
let response = await fetch(`get_clienti.php?id=<?php echo $template['idclient']; ?>`);
|
||||||
@@ -729,27 +817,57 @@ $xlsHeaders = $template['xls_headers'] ? json_decode($template['xls_headers'], t
|
|||||||
let mappingId = event.target.getAttribute('data-id');
|
let mappingId = event.target.getAttribute('data-id');
|
||||||
let xlsSelect = tr.querySelector('.xls-columns');
|
let xlsSelect = tr.querySelector('.xls-columns');
|
||||||
let manualInput = tr.querySelector('.manual-default');
|
let manualInput = tr.querySelector('.manual-default');
|
||||||
|
let autoSelect = tr.querySelector('.auto-value-select');
|
||||||
let mappedColumn = tr.querySelector('.mapped-column');
|
let mappedColumn = tr.querySelector('.mapped-column');
|
||||||
let removeBtn = tr.querySelector('.remove-xls');
|
let removeBtn = tr.querySelector('.remove-xls');
|
||||||
if (event.target.value === 'xls') {
|
if (event.target.value === 'xls') {
|
||||||
xlsSelect.style.display = 'block';
|
xlsSelect.style.display = 'block';
|
||||||
|
if (autoSelect) autoSelect.style.display = 'none';
|
||||||
|
|
||||||
manualInput.style.display = 'none';
|
manualInput.style.display = 'none';
|
||||||
manualInput.value = '';
|
manualInput.value = '';
|
||||||
|
|
||||||
if (mappedColumn) mappedColumn.style.display = 'none';
|
if (mappedColumn) mappedColumn.style.display = 'none';
|
||||||
if (removeBtn) removeBtn.style.display = xlsSelect.value ? 'inline-block' : 'none';
|
if (removeBtn) removeBtn.style.display = xlsSelect.value ? 'inline-block' : 'none';
|
||||||
|
|
||||||
} else if (event.target.value === 'manual') {
|
} else if (event.target.value === 'manual') {
|
||||||
xlsSelect.style.display = 'none';
|
xlsSelect.style.display = 'none';
|
||||||
|
if (autoSelect) autoSelect.style.display = 'none';
|
||||||
|
|
||||||
manualInput.style.display = 'block';
|
manualInput.style.display = 'block';
|
||||||
|
|
||||||
if (mappedColumn) mappedColumn.style.display = 'none';
|
if (mappedColumn) mappedColumn.style.display = 'none';
|
||||||
if (removeBtn) removeBtn.style.display = 'none';
|
if (removeBtn) removeBtn.style.display = 'none';
|
||||||
} else {
|
|
||||||
|
} else if (event.target.value === 'auto') {
|
||||||
xlsSelect.style.display = 'none';
|
xlsSelect.style.display = 'none';
|
||||||
|
xlsSelect.value = ''; // libera UI
|
||||||
|
|
||||||
manualInput.style.display = 'none';
|
manualInput.style.display = 'none';
|
||||||
manualInput.value = '';
|
manualInput.value = '';
|
||||||
|
|
||||||
|
if (mappedColumn) mappedColumn.style.display = 'none';
|
||||||
|
if (removeBtn) removeBtn.style.display = 'none';
|
||||||
|
|
||||||
|
if (autoSelect) autoSelect.style.display = 'block';
|
||||||
|
|
||||||
|
} else {
|
||||||
|
xlsSelect.style.display = 'none';
|
||||||
|
if (autoSelect) autoSelect.style.display = 'none';
|
||||||
|
|
||||||
|
manualInput.style.display = 'none';
|
||||||
|
manualInput.value = '';
|
||||||
|
|
||||||
if (mappedColumn) mappedColumn.style.display = 'none';
|
if (mappedColumn) mappedColumn.style.display = 'none';
|
||||||
if (removeBtn) removeBtn.style.display = 'none';
|
if (removeBtn) removeBtn.style.display = 'none';
|
||||||
}
|
}
|
||||||
saveMapping(mappingId, event.target.value, manualInput.value, xlsSelect.value);
|
saveMapping(
|
||||||
|
mappingId,
|
||||||
|
event.target.value,
|
||||||
|
manualInput.value,
|
||||||
|
xlsSelect.value,
|
||||||
|
autoSelect ? autoSelect.value : null
|
||||||
|
);
|
||||||
updateXlsDropdowns();
|
updateXlsDropdowns();
|
||||||
} else if (event.target.classList.contains('main-field-checkbox')) {
|
} else if (event.target.classList.contains('main-field-checkbox')) {
|
||||||
const checkbox = event.target;
|
const checkbox = event.target;
|
||||||
@@ -822,19 +940,65 @@ $xlsHeaders = $template['xls_headers'] ? json_decode($template['xls_headers'], t
|
|||||||
console.error("❌ Fetch error:", error);
|
console.error("❌ Fetch error:", error);
|
||||||
checkbox.checked = !checkbox.checked;
|
checkbox.checked = !checkbox.checked;
|
||||||
});
|
});
|
||||||
|
} else if (event.target.classList.contains('visible-parts-checkbox')) {
|
||||||
|
const checkbox = event.target;
|
||||||
|
const mappingId = checkbox.dataset.mappingId;
|
||||||
|
const value = checkbox.checked ? 1 : 0;
|
||||||
|
|
||||||
|
fetch('update_visible_parts.php', {
|
||||||
|
method: 'POST',
|
||||||
|
headers: {
|
||||||
|
'Content-Type': 'application/json'
|
||||||
|
},
|
||||||
|
body: JSON.stringify({
|
||||||
|
template_id: <?php echo $id; ?>,
|
||||||
|
mapping_id: mappingId,
|
||||||
|
value: value
|
||||||
|
})
|
||||||
|
})
|
||||||
|
.then(response => response.json())
|
||||||
|
.then(data => {
|
||||||
|
if (!data.success) {
|
||||||
|
console.error("❌ Error updating is_visible_parts:", data.message);
|
||||||
|
checkbox.checked = !checkbox.checked;
|
||||||
}
|
}
|
||||||
|
})
|
||||||
|
.catch(error => {
|
||||||
|
console.error("❌ Fetch error:", error);
|
||||||
|
checkbox.checked = !checkbox.checked;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
});
|
});
|
||||||
|
|
||||||
// Salva lo stato originale dei checkbox al caricamento
|
// Salva lo stato originale dei checkbox al caricamento
|
||||||
document.querySelectorAll('.main-field-checkbox').forEach(cb => {
|
document.querySelectorAll('.main-field-checkbox').forEach(cb => {
|
||||||
cb.dataset.originalChecked = cb.checked;
|
cb.dataset.originalChecked = cb.checked;
|
||||||
});
|
});
|
||||||
|
// AUTO VALUE select change -> save auto_value
|
||||||
|
document.getElementById('schemaFieldsBody').addEventListener('change', function(event) {
|
||||||
|
if (!event.target.classList.contains('auto-value-select')) return;
|
||||||
|
|
||||||
|
const tr = event.target.closest('tr');
|
||||||
|
const mappingId = event.target.getAttribute('data-id');
|
||||||
|
|
||||||
|
const xlsSelect = tr.querySelector('.xls-columns');
|
||||||
|
const manualInput = tr.querySelector('.manual-default');
|
||||||
|
|
||||||
|
saveMapping(
|
||||||
|
mappingId,
|
||||||
|
'auto',
|
||||||
|
manualInput ? manualInput.value : '',
|
||||||
|
xlsSelect ? xlsSelect.value : null,
|
||||||
|
event.target.value
|
||||||
|
);
|
||||||
|
});
|
||||||
document.getElementById('schemaFieldsBody').addEventListener('change', function(event) {
|
document.getElementById('schemaFieldsBody').addEventListener('change', function(event) {
|
||||||
if (event.target.classList.contains('xls-columns')) {
|
if (event.target.classList.contains('xls-columns')) {
|
||||||
let tr = event.target.closest('tr');
|
let tr = event.target.closest('tr');
|
||||||
let mappingId = event.target.getAttribute('data-id');
|
let mappingId = event.target.getAttribute('data-id');
|
||||||
let manualInput = tr.querySelector('.manual-default');
|
let manualInput = tr.querySelector('.manual-default');
|
||||||
|
let autoSelect = tr.querySelector('.auto-value-select');
|
||||||
let mappedColumn = tr.querySelector('.mapped-column');
|
let mappedColumn = tr.querySelector('.mapped-column');
|
||||||
let removeBtn = tr.querySelector('.remove-xls');
|
let removeBtn = tr.querySelector('.remove-xls');
|
||||||
|
|
||||||
@@ -875,6 +1039,10 @@ $xlsHeaders = $template['xls_headers'] ? json_decode($template['xls_headers'], t
|
|||||||
mappedColumn.style.display = event.target.value ? 'inline' : 'none';
|
mappedColumn.style.display = event.target.value ? 'inline' : 'none';
|
||||||
}
|
}
|
||||||
if (removeBtn) removeBtn.style.display = event.target.value ? 'inline-block' : 'none';
|
if (removeBtn) removeBtn.style.display = event.target.value ? 'inline-block' : 'none';
|
||||||
|
const mappingSelect = tr.querySelector('.mapping-select');
|
||||||
|
if (mappingSelect && mappingSelect.value !== 'xls') {
|
||||||
|
return; // non salvare XLS se non siamo in modalità XLS
|
||||||
|
}
|
||||||
saveMapping(mappingId, 'xls', manualInput.value, event.target.value);
|
saveMapping(mappingId, 'xls', manualInput.value, event.target.value);
|
||||||
updateXlsDropdowns();
|
updateXlsDropdowns();
|
||||||
}
|
}
|
||||||
@@ -926,7 +1094,7 @@ $xlsHeaders = $template['xls_headers'] ? json_decode($template['xls_headers'], t
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
function saveMapping(mappingId, mappingType, defaultValue, excelColumn) {
|
function saveMapping(mappingId, mappingType, defaultValue, excelColumn, autoValue) {
|
||||||
console.log("Saving mapping:", {
|
console.log("Saving mapping:", {
|
||||||
id: mappingId,
|
id: mappingId,
|
||||||
mapping_type: mappingType,
|
mapping_type: mappingType,
|
||||||
@@ -943,6 +1111,7 @@ $xlsHeaders = $template['xls_headers'] ? json_decode($template['xls_headers'], t
|
|||||||
mapping_type: mappingType,
|
mapping_type: mappingType,
|
||||||
excel_column: mappingType === 'xls' ? excelColumn : null,
|
excel_column: mappingType === 'xls' ? excelColumn : null,
|
||||||
manual_default: mappingType === 'manual' ? defaultValue : null,
|
manual_default: mappingType === 'manual' ? defaultValue : null,
|
||||||
|
auto_value: mappingType === 'auto' ? (autoValue || 'none') : 'none',
|
||||||
tablename: "<?php echo $template['target_table']; ?>"
|
tablename: "<?php echo $template['target_table']; ?>"
|
||||||
})
|
})
|
||||||
}).then(response => response.json())
|
}).then(response => response.json())
|
||||||
|
|||||||
@@ -12,11 +12,23 @@ try {
|
|||||||
|
|
||||||
$iddatadb = intval($_POST['iddatadb']);
|
$iddatadb = intval($_POST['iddatadb']);
|
||||||
$idclient = isset($_POST['idclient']) ? (is_numeric($_POST['idclient']) ? intval($_POST['idclient']) : null) : null;
|
$idclient = isset($_POST['idclient']) ? (is_numeric($_POST['idclient']) ? intval($_POST['idclient']) : null) : null;
|
||||||
|
|
||||||
$db = DBHandlerSelect::getInstance();
|
$db = DBHandlerSelect::getInstance();
|
||||||
$pdo = $db->getConnection();
|
$pdo = $db->getConnection();
|
||||||
|
|
||||||
// ---------------- FIXED FIELDS (template_fixed_mapping) ----------------
|
// ---------------- FIXED FIELDS (template_fixed_mapping) ----------------
|
||||||
|
|
||||||
|
// ALIAS: fixed_field_key "logico" -> colonna reale su datadb
|
||||||
|
// (NON tocchiamo MySQL, gestiamo solo qui la traduzione)
|
||||||
|
$fixedAliasMap = [
|
||||||
|
'ClienteResponsabile' => 'cliente_responsabile_id',
|
||||||
|
'MoltiplicatorePrezzo' => 'moltiplicatore_prezzo_id',
|
||||||
|
'AnagraficaCertestObject' => 'anagrafica_certest_object_id',
|
||||||
|
'AnagraficaCertestService' => 'anagrafica_certest_service_id',
|
||||||
|
'ClienteFornitore' => 'cliente_fornitore_id',
|
||||||
|
'ConsegnaRichiesta' => 'consegna_richiesta',
|
||||||
|
];
|
||||||
|
|
||||||
// 1) Recupera templateid dalla riga datadb (serve per sapere quali fixed_field_key sono permessi)
|
// 1) Recupera templateid dalla riga datadb (serve per sapere quali fixed_field_key sono permessi)
|
||||||
$stmtTpl = $pdo->prepare("SELECT templateid FROM datadb WHERE iddatadb = ?");
|
$stmtTpl = $pdo->prepare("SELECT templateid FROM datadb WHERE iddatadb = ?");
|
||||||
$stmtTpl->execute([$iddatadb]);
|
$stmtTpl->execute([$iddatadb]);
|
||||||
@@ -32,19 +44,20 @@ try {
|
|||||||
SELECT fixed_field_key, data_type, is_required, default_value
|
SELECT fixed_field_key, data_type, is_required, default_value
|
||||||
FROM template_fixed_mapping
|
FROM template_fixed_mapping
|
||||||
WHERE template_id = ? AND is_visible_import = 1
|
WHERE template_id = ? AND is_visible_import = 1
|
||||||
");
|
");
|
||||||
$fxStmt->execute([$templateId]);
|
$fxStmt->execute([$templateId]);
|
||||||
$fixedList = $fxStmt->fetchAll(PDO::FETCH_ASSOC);
|
$fixedList = $fxStmt->fetchAll(PDO::FETCH_ASSOC);
|
||||||
|
|
||||||
// 3) Crea whitelist: key => metadata
|
// 3) Crea whitelist LOGICA: fixed_field_key => metadata
|
||||||
$fixedWhitelist = [];
|
$fixedWhitelist = [];
|
||||||
foreach ($fixedList as $fx) {
|
foreach ($fixedList as $fx) {
|
||||||
$k = (string)$fx['fixed_field_key'];
|
$k = (string)$fx['fixed_field_key'];
|
||||||
|
|
||||||
// sicurezza: nome colonna ammesso solo se "safe" (no spazi, no caratteri strani)
|
// sicurezza: key ammessa solo se "safe"
|
||||||
if (!preg_match('/^[a-zA-Z0-9_]+$/', $k)) {
|
if (!preg_match('/^[a-zA-Z0-9_]+$/', $k)) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
$fixedWhitelist[$k] = [
|
$fixedWhitelist[$k] = [
|
||||||
'data_type' => (string)$fx['data_type'], // INT | DATE
|
'data_type' => (string)$fx['data_type'], // INT | DATE
|
||||||
'is_required' => (int)$fx['is_required'],
|
'is_required' => (int)$fx['is_required'],
|
||||||
@@ -52,7 +65,20 @@ try {
|
|||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 3b) Crea whitelist REALE: colonna datadb reale => metadata
|
||||||
|
$realWhitelist = [];
|
||||||
|
foreach ($fixedWhitelist as $logicalKey => $meta) {
|
||||||
|
$realCol = $fixedAliasMap[$logicalKey] ?? $logicalKey;
|
||||||
|
|
||||||
|
// sicurezza: anche la colonna reale deve essere "safe"
|
||||||
|
if (!preg_match('/^[a-zA-Z0-9_]+$/', $realCol)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
$realWhitelist[$realCol] = $meta;
|
||||||
|
}
|
||||||
|
|
||||||
|
// ---------------- DETAILS (import_data_details) ----------------
|
||||||
$data = $_POST;
|
$data = $_POST;
|
||||||
$details = [];
|
$details = [];
|
||||||
|
|
||||||
@@ -101,7 +127,7 @@ try {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// 5. Aggiorna datadb: idclient + FIXED FIELDS (whitelisted)
|
// ---------------- UPDATE datadb: idclient + FIXED FIELDS ----------------
|
||||||
$setParts = [];
|
$setParts = [];
|
||||||
$params = [];
|
$params = [];
|
||||||
|
|
||||||
@@ -111,13 +137,33 @@ try {
|
|||||||
$params[] = $idclient;
|
$params[] = $idclient;
|
||||||
}
|
}
|
||||||
|
|
||||||
// 5b) fixed fields dal POST (solo quelli presenti nella whitelist del template)
|
// 5b) fixed fields dal POST
|
||||||
foreach ($fixedWhitelist as $col => $meta) {
|
// QUI è il punto chiave: accettiamo SOLO colonne reali (realWhitelist),
|
||||||
if (!array_key_exists($col, $_POST)) {
|
// ma se dal JS arrivassero ancora key logiche, funzionerebbe uguale
|
||||||
continue; // non inviato dal form
|
// perché sotto controlliamo anche l'alias.
|
||||||
|
foreach ($realWhitelist as $realCol => $meta) {
|
||||||
|
|
||||||
|
$postKeyToRead = null;
|
||||||
|
|
||||||
|
// Caso 1: POST contiene già la colonna reale
|
||||||
|
if (array_key_exists($realCol, $_POST)) {
|
||||||
|
$postKeyToRead = $realCol;
|
||||||
|
} else {
|
||||||
|
// Caso 2: POST contiene la key logica -> troviamo quale logica mappa su questa colonna reale
|
||||||
|
// (fallback, utile se non hai ancora aggiornato il JS)
|
||||||
|
foreach ($fixedAliasMap as $logical => $mappedReal) {
|
||||||
|
if ($mappedReal === $realCol && array_key_exists($logical, $_POST)) {
|
||||||
|
$postKeyToRead = $logical;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
$val = $_POST[$col];
|
if ($postKeyToRead === null) {
|
||||||
|
continue; // non inviato
|
||||||
|
}
|
||||||
|
|
||||||
|
$val = $_POST[$postKeyToRead];
|
||||||
|
|
||||||
// Normalizzazione per tipo
|
// Normalizzazione per tipo
|
||||||
if ($meta['data_type'] === 'DATE') {
|
if ($meta['data_type'] === 'DATE') {
|
||||||
@@ -128,13 +174,14 @@ try {
|
|||||||
$val = ($val === '') ? null : (int)$val;
|
$val = ($val === '') ? null : (int)$val;
|
||||||
}
|
}
|
||||||
|
|
||||||
$setParts[] = "`$col` = ?";
|
$setParts[] = "`$realCol` = ?";
|
||||||
$params[] = $val;
|
$params[] = $val;
|
||||||
}
|
}
|
||||||
|
|
||||||
// esegui update solo se c'è qualcosa da aggiornare
|
// esegui update solo se c'è qualcosa da aggiornare
|
||||||
if (!empty($setParts)) {
|
if (!empty($setParts)) {
|
||||||
$params[] = $iddatadb;
|
$params[] = $iddatadb;
|
||||||
|
|
||||||
$sqlUpd = "UPDATE datadb SET " . implode(", ", $setParts) . " WHERE iddatadb = ?";
|
$sqlUpd = "UPDATE datadb SET " . implode(", ", $setParts) . " WHERE iddatadb = ?";
|
||||||
$updStmt = $pdo->prepare($sqlUpd);
|
$updStmt = $pdo->prepare($sqlUpd);
|
||||||
$updStmt->execute($params);
|
$updStmt->execute($params);
|
||||||
@@ -151,7 +198,6 @@ try {
|
|||||||
$response['message'] = "No changes found";
|
$response['message'] = "No changes found";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
$response['success'] = true;
|
$response['success'] = true;
|
||||||
$response['changed'] = $changed; // Debug / optional
|
$response['changed'] = $changed; // Debug / optional
|
||||||
|
|
||||||
|
|||||||
@@ -20,19 +20,82 @@ $mappingId = $data['id'];
|
|||||||
$mappingType = $data['mapping_type'] ?? '';
|
$mappingType = $data['mapping_type'] ?? '';
|
||||||
$excelColumn = $data['excel_column'] ?? null;
|
$excelColumn = $data['excel_column'] ?? null;
|
||||||
$manualDefault = $data['manual_default'] ?? null;
|
$manualDefault = $data['manual_default'] ?? null;
|
||||||
|
|
||||||
|
$autoValue = $data['auto_value'] ?? 'none';
|
||||||
$tablename = $data['tablename'] ?? '';
|
$tablename = $data['tablename'] ?? '';
|
||||||
|
|
||||||
try {
|
try {
|
||||||
$stmt = $pdo->prepare("UPDATE template_mapping SET is_manual = ?, excel_column = ?, manual_default = ? WHERE id = ?");
|
// Normalize mapping type
|
||||||
$isManual = ($mappingType === 'manual') ? 1 : 0;
|
$allowedTypes = ['', 'xls', 'manual', 'auto'];
|
||||||
$result = $stmt->execute([$isManual, $excelColumn, $manualDefault, $mappingId]);
|
if (!in_array($mappingType, $allowedTypes, true)) {
|
||||||
|
echo json_encode(["success" => false, "message" => "Invalid mapping_type"]);
|
||||||
|
exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Normalize auto_value
|
||||||
|
$allowedAuto = ['none', 'import_date', 'import_time', 'export_date', 'export_time'];
|
||||||
|
if (!in_array($autoValue, $allowedAuto, true)) {
|
||||||
|
$autoValue = 'none';
|
||||||
|
}
|
||||||
|
|
||||||
|
// Decide what to persist based on mapping_type
|
||||||
|
$isManual = 0;
|
||||||
|
$excelToSave = null;
|
||||||
|
$manualToSave = null;
|
||||||
|
$autoToSave = 'none';
|
||||||
|
|
||||||
|
if ($mappingType === 'xls') {
|
||||||
|
$isManual = 0;
|
||||||
|
$excelToSave = $excelColumn ?: null;
|
||||||
|
$manualToSave = null;
|
||||||
|
$autoToSave = 'none';
|
||||||
|
} elseif ($mappingType === 'manual') {
|
||||||
|
$isManual = 1;
|
||||||
|
$excelToSave = null;
|
||||||
|
$manualToSave = $manualDefault;
|
||||||
|
$autoToSave = 'none';
|
||||||
|
} elseif ($mappingType === 'auto') {
|
||||||
|
$isManual = 0;
|
||||||
|
$excelToSave = null;
|
||||||
|
$manualToSave = null;
|
||||||
|
$autoToSave = $autoValue ?: 'none';
|
||||||
|
} else {
|
||||||
|
// reset
|
||||||
|
$isManual = 0;
|
||||||
|
$excelToSave = null;
|
||||||
|
$manualToSave = null;
|
||||||
|
$autoToSave = 'none';
|
||||||
|
}
|
||||||
|
|
||||||
|
$stmt = $pdo->prepare("
|
||||||
|
UPDATE template_mapping
|
||||||
|
SET
|
||||||
|
is_manual = ?,
|
||||||
|
excel_column = ?,
|
||||||
|
manual_default = ?,
|
||||||
|
auto_value = ?
|
||||||
|
WHERE id = ?
|
||||||
|
");
|
||||||
|
|
||||||
|
$result = $stmt->execute([$isManual, $excelToSave, $manualToSave, $autoToSave, $mappingId]);
|
||||||
|
|
||||||
if (!$result) {
|
if (!$result) {
|
||||||
echo json_encode(["success" => false, "message" => "Database update failed"]);
|
echo json_encode(["success" => false, "message" => "Database update failed"]);
|
||||||
exit;
|
exit;
|
||||||
}
|
}
|
||||||
|
|
||||||
echo json_encode(["success" => true, "message" => "Mapping updated successfully", "data" => $data]); // Aggiunto debug
|
echo json_encode([
|
||||||
|
"success" => true,
|
||||||
|
"message" => "Mapping updated successfully",
|
||||||
|
"saved" => [
|
||||||
|
"id" => (int)$mappingId,
|
||||||
|
"mapping_type" => $mappingType,
|
||||||
|
"is_manual" => $isManual,
|
||||||
|
"excel_column" => $excelToSave,
|
||||||
|
"manual_default" => $manualToSave,
|
||||||
|
"auto_value" => $autoToSave
|
||||||
|
]
|
||||||
|
]);
|
||||||
} catch (Exception $e) {
|
} catch (Exception $e) {
|
||||||
echo json_encode(["success" => false, "message" => "Error: " . $e->getMessage()]);
|
echo json_encode(["success" => false, "message" => "Error: " . $e->getMessage()]);
|
||||||
}
|
}
|
||||||
|
|||||||
File diff suppressed because one or more lines are too long
@@ -1,5 +1,5 @@
|
|||||||
{
|
{
|
||||||
"@odata.context": "https:\/\/93.43.5.102\/limsapi\/api\/odata\/$metadata#SchemaCustomField",
|
"@odata.context": "https:\/\/bvcpsitaly-elims.com\/limsapi\/api\/odata\/$metadata#SchemaCustomField",
|
||||||
"value": [
|
"value": [
|
||||||
{
|
{
|
||||||
"IdSchemaCustomFields": 41,
|
"IdSchemaCustomFields": 41,
|
||||||
@@ -460,7 +460,7 @@
|
|||||||
{
|
{
|
||||||
"IdSchemaCustomFields": 129,
|
"IdSchemaCustomFields": 129,
|
||||||
"ConteggioClienti": 0,
|
"ConteggioClienti": 0,
|
||||||
"Nome": "Carhartt",
|
"Nome": "Z_Carhartt",
|
||||||
"Descrizione": "SCHEMA DA UTILIZZARE SOLO PER RICHIESTE UFFICIALI CARHARTT\r\n\r\n\r\n"
|
"Descrizione": "SCHEMA DA UTILIZZARE SOLO PER RICHIESTE UFFICIALI CARHARTT\r\n\r\n\r\n"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@@ -864,6 +864,18 @@
|
|||||||
"ConteggioClienti": 0,
|
"ConteggioClienti": 0,
|
||||||
"Nome": "HERMES PAP FEMME",
|
"Nome": "HERMES PAP FEMME",
|
||||||
"Descrizione": "Schema per tutti i campioni di HERMES PAP FEMME \r\n15\/01\/2025\r\n\r\n\r\n"
|
"Descrizione": "Schema per tutti i campioni di HERMES PAP FEMME \r\n15\/01\/2025\r\n\r\n\r\n"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"IdSchemaCustomFields": 200,
|
||||||
|
"ConteggioClienti": 0,
|
||||||
|
"Nome": "CARHARTT ( provvisiorio in revisione)",
|
||||||
|
"Descrizione": "Schema per tutti i campioni CARHARTT provvissorio\r\n\r\n"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"IdSchemaCustomFields": 201,
|
||||||
|
"ConteggioClienti": 0,
|
||||||
|
"Nome": "ROSSIMODA",
|
||||||
|
"Descrizione": "Per tutti i campioni di ROSSIMODA\r\n\r\n"
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
@@ -0,0 +1,31 @@
|
|||||||
|
<?php
|
||||||
|
require_once 'include/headscript.php';
|
||||||
|
|
||||||
|
header('Content-Type: application/json');
|
||||||
|
|
||||||
|
$data = json_decode(file_get_contents('php://input'), true);
|
||||||
|
|
||||||
|
$template_id = isset($data['template_id']) ? (int)$data['template_id'] : 0;
|
||||||
|
$mapping_id = isset($data['mapping_id']) ? (int)$data['mapping_id'] : 0;
|
||||||
|
$value = isset($data['value']) ? (int)$data['value'] : null;
|
||||||
|
|
||||||
|
// Validate input
|
||||||
|
if ($template_id <= 0 || $mapping_id <= 0 || $value === null) {
|
||||||
|
echo json_encode(['success' => false, 'message' => 'Invalid input']);
|
||||||
|
exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Normalize to 0/1
|
||||||
|
$value = ($value === 1) ? 1 : 0;
|
||||||
|
|
||||||
|
$db = DBHandlerSelect::getInstance();
|
||||||
|
$pdo = $db->getConnection();
|
||||||
|
|
||||||
|
$stmt = $pdo->prepare("UPDATE template_mapping SET is_visible_parts = ? WHERE id = ? AND template_id = ?");
|
||||||
|
$result = $stmt->execute([$value, $mapping_id, $template_id]);
|
||||||
|
|
||||||
|
if ($result) {
|
||||||
|
echo json_encode(['success' => true]);
|
||||||
|
} else {
|
||||||
|
echo json_encode(['success' => false, 'message' => 'Failed to update is_visible_parts']);
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user