fixed validation per dup0licate parts, client inactive, fixed flags scheme, update schema json
This commit is contained in:
@@ -0,0 +1,152 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
/**
|
||||||
|
* debug_odata_metadata.php — TEMPORANEO, da rimuovere dopo il test.
|
||||||
|
* Scopo: leggere $metadata (XML standard OData) per vedere:
|
||||||
|
* - tutti gli EntitySet disponibili (non solo "Cliente")
|
||||||
|
* - tutti i campi (Property) definiti per l'EntityType Cliente
|
||||||
|
* Questo bypassa qualunque vista/proiezione ristretta lato server
|
||||||
|
* applicata sull'endpoint "Cliente" normale.
|
||||||
|
*/
|
||||||
|
|
||||||
|
require_once dirname(__DIR__, 2) . '/vendor/autoload.php';
|
||||||
|
include dirname(__DIR__) . '/../extra/auth.php';
|
||||||
|
|
||||||
|
if (!Auth::check()) {
|
||||||
|
http_response_code(401);
|
||||||
|
echo json_encode(['error' => 'Unauthorized']);
|
||||||
|
exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
use Dotenv\Dotenv;
|
||||||
|
|
||||||
|
header('Content-Type: text/plain; charset=utf-8');
|
||||||
|
ini_set('display_errors', '1');
|
||||||
|
error_reporting(E_ALL);
|
||||||
|
|
||||||
|
$dotenv = Dotenv::createImmutable(dirname(__DIR__, 2));
|
||||||
|
$dotenv->load();
|
||||||
|
|
||||||
|
$baseUrl = $_ENV['API_BASE_URL'];
|
||||||
|
$username = $_ENV['API_USERNAME'];
|
||||||
|
$password = $_ENV['API_PASSWORD'];
|
||||||
|
|
||||||
|
$scriptStart = microtime(true);
|
||||||
|
|
||||||
|
// ── 1. Autenticazione (stessa logica della classe originale) ──────────────
|
||||||
|
$ch = curl_init("{$baseUrl}/api/authentication/authenticate");
|
||||||
|
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
|
||||||
|
curl_setopt($ch, CURLOPT_POST, true);
|
||||||
|
curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode([
|
||||||
|
'Username' => $username,
|
||||||
|
'Password' => $password
|
||||||
|
]));
|
||||||
|
curl_setopt($ch, CURLOPT_HTTPHEADER, [
|
||||||
|
'Content-Type: application/json',
|
||||||
|
'Accept: application/json'
|
||||||
|
]);
|
||||||
|
curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);
|
||||||
|
curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, false);
|
||||||
|
|
||||||
|
$authResponse = curl_exec($ch);
|
||||||
|
$authHttpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
|
||||||
|
curl_close($ch);
|
||||||
|
|
||||||
|
if ($authHttpCode !== 200) {
|
||||||
|
echo "AUTH FAILED: HTTP {$authHttpCode}\n{$authResponse}\n";
|
||||||
|
exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
$tokenData = json_decode($authResponse, true);
|
||||||
|
$token = null;
|
||||||
|
if (is_array($tokenData) && isset($tokenData['token'])) {
|
||||||
|
$token = $tokenData['token'];
|
||||||
|
} elseif (is_string($tokenData)) {
|
||||||
|
$token = trim($tokenData, '"');
|
||||||
|
} else {
|
||||||
|
$token = trim($authResponse, '"');
|
||||||
|
}
|
||||||
|
|
||||||
|
if (empty($token)) {
|
||||||
|
echo "TOKEN NOT RECEIVED\n{$authResponse}\n";
|
||||||
|
exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
$authElapsed = microtime(true) - $scriptStart;
|
||||||
|
|
||||||
|
// ── 2. Richiesta $metadata (XML, non JSON) ─────────────────────────────────
|
||||||
|
// Cache-buster nell'URL: alcuni server/reverse-proxy potrebbero cachare per URL esatto.
|
||||||
|
$cacheBuster = uniqid('cb_', true);
|
||||||
|
$metadataUrl = "{$baseUrl}/api/odata/\$metadata?_cb={$cacheBuster}";
|
||||||
|
|
||||||
|
$metadataStart = microtime(true);
|
||||||
|
|
||||||
|
$ch = curl_init($metadataUrl);
|
||||||
|
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
|
||||||
|
curl_setopt($ch, CURLOPT_HTTPHEADER, [
|
||||||
|
"Authorization: Bearer {$token}",
|
||||||
|
"Accept: application/xml"
|
||||||
|
]);
|
||||||
|
curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);
|
||||||
|
curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, false);
|
||||||
|
|
||||||
|
curl_setopt($ch, CURLOPT_HEADER, true); // includi header risposta per ispezione
|
||||||
|
|
||||||
|
$rawResponse = curl_exec($ch);
|
||||||
|
$curlInfo = curl_getinfo($ch);
|
||||||
|
$httpCode = $curlInfo['http_code'];
|
||||||
|
$curlTotalTime = $curlInfo['total_time'];
|
||||||
|
$curlError = curl_error($ch);
|
||||||
|
$headerSize = $curlInfo['header_size'];
|
||||||
|
curl_close($ch);
|
||||||
|
|
||||||
|
$metadataElapsed = microtime(true) - $metadataStart;
|
||||||
|
|
||||||
|
if ($rawResponse === false || $httpCode !== 200) {
|
||||||
|
echo "METADATA REQUEST FAILED: HTTP {$httpCode}\nError: {$curlError}\n{$rawResponse}\n";
|
||||||
|
exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
$responseHeaders = substr($rawResponse, 0, $headerSize);
|
||||||
|
$xml = substr($rawResponse, $headerSize);
|
||||||
|
|
||||||
|
echo "==================== TIMING (prova che NON è cache) ====================\n";
|
||||||
|
echo "Tempo autenticazione: " . round($authElapsed * 1000, 1) . " ms\n";
|
||||||
|
echo "Tempo chiamata \$metadata (curl total_time): " . round($curlTotalTime * 1000, 1) . " ms\n";
|
||||||
|
echo "Tempo chiamata \$metadata (wall clock PHP): " . round($metadataElapsed * 1000, 1) . " ms\n";
|
||||||
|
echo "Cache-buster usato: {$cacheBuster}\n";
|
||||||
|
echo "Dimensione XML ricevuto: " . strlen($xml) . " bytes\n\n";
|
||||||
|
|
||||||
|
echo "==================== HEADER RISPOSTA (controlla se c'è Cache-Control/Age/X-Cache) ====================\n";
|
||||||
|
echo $responseHeaders . "\n";
|
||||||
|
|
||||||
|
// ── 3. Estrai tutti gli EntitySet disponibili ──────────────────────────────
|
||||||
|
echo "==================== ENTITY SETS DISPONIBILI ====================\n";
|
||||||
|
if (preg_match_all('/<EntitySet Name="([^"]+)"/', $xml, $matches)) {
|
||||||
|
foreach ($matches[1] as $name) {
|
||||||
|
echo "- {$name}\n";
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
echo "(nessun EntitySet trovato — controlla il formato XML grezzo sotto)\n";
|
||||||
|
}
|
||||||
|
|
||||||
|
// ── 4. Estrai tutte le Property dell'EntityType "Cliente" (e simili) ──────
|
||||||
|
echo "\n==================== CAMPI PER ENTITYTYPE CHE CONTENGONO 'Client' ====================\n";
|
||||||
|
if (preg_match_all('/<EntityType Name="([^"]*[Cc]lient[^"]*)"[^>]*>(.*?)<\/EntityType>/s', $xml, $entityMatches, PREG_SET_ORDER)) {
|
||||||
|
foreach ($entityMatches as $entity) {
|
||||||
|
$entityName = $entity[1];
|
||||||
|
$entityBody = $entity[2];
|
||||||
|
echo "\n--- EntityType: {$entityName} ---\n";
|
||||||
|
if (preg_match_all('/<Property Name="([^"]+)" Type="([^"]+)"/', $entityBody, $propMatches, PREG_SET_ORDER)) {
|
||||||
|
foreach ($propMatches as $prop) {
|
||||||
|
echo " {$prop[1]} ({$prop[2]})\n";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
echo "(nessun EntityType con 'Client' nel nome trovato)\n";
|
||||||
|
}
|
||||||
|
|
||||||
|
// ── 5. Salva l'XML grezzo completo per ispezione manuale, se serve ────────
|
||||||
|
file_put_contents(__DIR__ . '/metadata_debug.xml', $xml);
|
||||||
|
echo "\n\nXML completo salvato in: metadata_debug.xml (rimuovilo dopo il test)\n";
|
||||||
@@ -0,0 +1,152 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
/**
|
||||||
|
* debug_odata_metadata.php — TEMPORANEO, da rimuovere dopo il test.
|
||||||
|
* Scopo: leggere $metadata (XML standard OData) per vedere:
|
||||||
|
* - tutti gli EntitySet disponibili (non solo "Cliente")
|
||||||
|
* - tutti i campi (Property) definiti per l'EntityType Cliente
|
||||||
|
* Questo bypassa qualunque vista/proiezione ristretta lato server
|
||||||
|
* applicata sull'endpoint "Cliente" normale.
|
||||||
|
*/
|
||||||
|
|
||||||
|
require_once dirname(__DIR__, 2) . '/vendor/autoload.php';
|
||||||
|
include dirname(__DIR__) . '/../extra/auth.php';
|
||||||
|
|
||||||
|
if (!Auth::check()) {
|
||||||
|
http_response_code(401);
|
||||||
|
echo json_encode(['error' => 'Unauthorized']);
|
||||||
|
exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
use Dotenv\Dotenv;
|
||||||
|
|
||||||
|
header('Content-Type: text/plain; charset=utf-8');
|
||||||
|
ini_set('display_errors', '1');
|
||||||
|
error_reporting(E_ALL);
|
||||||
|
|
||||||
|
$dotenv = Dotenv::createImmutable(dirname(__DIR__, 2));
|
||||||
|
$dotenv->load();
|
||||||
|
|
||||||
|
$baseUrl = $_ENV['API_BASE_URL'];
|
||||||
|
$username = $_ENV['API_USERNAME'];
|
||||||
|
$password = $_ENV['API_PASSWORD'];
|
||||||
|
|
||||||
|
$scriptStart = microtime(true);
|
||||||
|
|
||||||
|
// ── 1. Autenticazione (stessa logica della classe originale) ──────────────
|
||||||
|
$ch = curl_init("{$baseUrl}/api/authentication/authenticate");
|
||||||
|
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
|
||||||
|
curl_setopt($ch, CURLOPT_POST, true);
|
||||||
|
curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode([
|
||||||
|
'Username' => $username,
|
||||||
|
'Password' => $password
|
||||||
|
]));
|
||||||
|
curl_setopt($ch, CURLOPT_HTTPHEADER, [
|
||||||
|
'Content-Type: application/json',
|
||||||
|
'Accept: application/json'
|
||||||
|
]);
|
||||||
|
curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);
|
||||||
|
curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, false);
|
||||||
|
|
||||||
|
$authResponse = curl_exec($ch);
|
||||||
|
$authHttpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
|
||||||
|
curl_close($ch);
|
||||||
|
|
||||||
|
if ($authHttpCode !== 200) {
|
||||||
|
echo "AUTH FAILED: HTTP {$authHttpCode}\n{$authResponse}\n";
|
||||||
|
exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
$tokenData = json_decode($authResponse, true);
|
||||||
|
$token = null;
|
||||||
|
if (is_array($tokenData) && isset($tokenData['token'])) {
|
||||||
|
$token = $tokenData['token'];
|
||||||
|
} elseif (is_string($tokenData)) {
|
||||||
|
$token = trim($tokenData, '"');
|
||||||
|
} else {
|
||||||
|
$token = trim($authResponse, '"');
|
||||||
|
}
|
||||||
|
|
||||||
|
if (empty($token)) {
|
||||||
|
echo "TOKEN NOT RECEIVED\n{$authResponse}\n";
|
||||||
|
exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
$authElapsed = microtime(true) - $scriptStart;
|
||||||
|
|
||||||
|
// ── 2. Richiesta $metadata (XML, non JSON) ─────────────────────────────────
|
||||||
|
// Cache-buster nell'URL: alcuni server/reverse-proxy potrebbero cachare per URL esatto.
|
||||||
|
$cacheBuster = uniqid('cb_', true);
|
||||||
|
$metadataUrl = "{$baseUrl}/api/odata/\$metadata?_cb={$cacheBuster}";
|
||||||
|
|
||||||
|
$metadataStart = microtime(true);
|
||||||
|
|
||||||
|
$ch = curl_init($metadataUrl);
|
||||||
|
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
|
||||||
|
curl_setopt($ch, CURLOPT_HTTPHEADER, [
|
||||||
|
"Authorization: Bearer {$token}",
|
||||||
|
"Accept: application/xml"
|
||||||
|
]);
|
||||||
|
curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);
|
||||||
|
curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, false);
|
||||||
|
|
||||||
|
curl_setopt($ch, CURLOPT_HEADER, true); // includi header risposta per ispezione
|
||||||
|
|
||||||
|
$rawResponse = curl_exec($ch);
|
||||||
|
$curlInfo = curl_getinfo($ch);
|
||||||
|
$httpCode = $curlInfo['http_code'];
|
||||||
|
$curlTotalTime = $curlInfo['total_time'];
|
||||||
|
$curlError = curl_error($ch);
|
||||||
|
$headerSize = $curlInfo['header_size'];
|
||||||
|
curl_close($ch);
|
||||||
|
|
||||||
|
$metadataElapsed = microtime(true) - $metadataStart;
|
||||||
|
|
||||||
|
if ($rawResponse === false || $httpCode !== 200) {
|
||||||
|
echo "METADATA REQUEST FAILED: HTTP {$httpCode}\nError: {$curlError}\n{$rawResponse}\n";
|
||||||
|
exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
$responseHeaders = substr($rawResponse, 0, $headerSize);
|
||||||
|
$xml = substr($rawResponse, $headerSize);
|
||||||
|
|
||||||
|
echo "==================== TIMING (prova che NON è cache) ====================\n";
|
||||||
|
echo "Tempo autenticazione: " . round($authElapsed * 1000, 1) . " ms\n";
|
||||||
|
echo "Tempo chiamata \$metadata (curl total_time): " . round($curlTotalTime * 1000, 1) . " ms\n";
|
||||||
|
echo "Tempo chiamata \$metadata (wall clock PHP): " . round($metadataElapsed * 1000, 1) . " ms\n";
|
||||||
|
echo "Cache-buster usato: {$cacheBuster}\n";
|
||||||
|
echo "Dimensione XML ricevuto: " . strlen($xml) . " bytes\n\n";
|
||||||
|
|
||||||
|
echo "==================== HEADER RISPOSTA (controlla se c'è Cache-Control/Age/X-Cache) ====================\n";
|
||||||
|
echo $responseHeaders . "\n";
|
||||||
|
|
||||||
|
// ── 3. Estrai tutti gli EntitySet disponibili ──────────────────────────────
|
||||||
|
echo "==================== ENTITY SETS DISPONIBILI ====================\n";
|
||||||
|
if (preg_match_all('/<EntitySet Name="([^"]+)"/', $xml, $matches)) {
|
||||||
|
foreach ($matches[1] as $name) {
|
||||||
|
echo "- {$name}\n";
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
echo "(nessun EntitySet trovato — controlla il formato XML grezzo sotto)\n";
|
||||||
|
}
|
||||||
|
|
||||||
|
// ── 4. Estrai tutte le Property dell'EntityType "Cliente" (e simili) ──────
|
||||||
|
echo "\n==================== CAMPI PER ENTITYTYPE CHE CONTENGONO 'Client' ====================\n";
|
||||||
|
if (preg_match_all('/<EntityType Name="([^"]*[Cc]lient[^"]*)"[^>]*>(.*?)<\/EntityType>/s', $xml, $entityMatches, PREG_SET_ORDER)) {
|
||||||
|
foreach ($entityMatches as $entity) {
|
||||||
|
$entityName = $entity[1];
|
||||||
|
$entityBody = $entity[2];
|
||||||
|
echo "\n--- EntityType: {$entityName} ---\n";
|
||||||
|
if (preg_match_all('/<Property Name="([^"]+)" Type="([^"]+)"/', $entityBody, $propMatches, PREG_SET_ORDER)) {
|
||||||
|
foreach ($propMatches as $prop) {
|
||||||
|
echo " {$prop[1]} ({$prop[2]})\n";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
echo "(nessun EntityType con 'Client' nel nome trovato)\n";
|
||||||
|
}
|
||||||
|
|
||||||
|
// ── 5. Salva l'XML grezzo completo per ispezione manuale, se serve ────────
|
||||||
|
file_put_contents(__DIR__ . '/metadata_debug.xml', $xml);
|
||||||
|
echo "\n\nXML completo salvato in: metadata_debug.xml (rimuovilo dopo il test)\n";
|
||||||
@@ -30,7 +30,7 @@ try {
|
|||||||
|
|
||||||
// Parametri OData
|
// Parametri OData
|
||||||
$params = [
|
$params = [
|
||||||
'$select' => 'IdCliente,Nominativo,CodiceCliente',
|
'$select' => 'IdCliente,Nominativo,CodiceCliente,Inattivo',
|
||||||
'$orderby' => 'Nominativo asc'
|
'$orderby' => 'Nominativo asc'
|
||||||
];
|
];
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,127 @@
|
|||||||
|
<?php
|
||||||
|
require_once dirname(__DIR__, 2) . '/vendor/autoload.php';
|
||||||
|
require_once __DIR__ . '/class/VisualLimsApiClient.class.php';
|
||||||
|
|
||||||
|
header('Content-Type: application/json');
|
||||||
|
|
||||||
|
// Disable PHP error display
|
||||||
|
ini_set('display_errors', '0');
|
||||||
|
error_reporting(E_ALL);
|
||||||
|
|
||||||
|
try {
|
||||||
|
$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;
|
||||||
|
}
|
||||||
|
|
||||||
|
// ── DEBUG MODE ───────────────────────────────────────────────────────
|
||||||
|
// Chiamata: get_clienti.php?debug=1
|
||||||
|
// Bypassa cache e $select per vedere TUTTI i campi grezzi restituiti
|
||||||
|
// dall'API per l'entità Cliente. Usare solo per ispezione manuale,
|
||||||
|
// NON lasciare linkato/usato in produzione dal frontend.
|
||||||
|
if (($_GET['debug'] ?? '') === '1') {
|
||||||
|
$debugParams = [
|
||||||
|
'$top' => 5,
|
||||||
|
'$orderby' => 'Nominativo asc'
|
||||||
|
// Nessun $select: chiediamo tutti i campi disponibili
|
||||||
|
];
|
||||||
|
$debugEndpoint = "Cliente?" . http_build_query($debugParams);
|
||||||
|
$debugData = $api->get($debugEndpoint);
|
||||||
|
$debugClients = $debugData['value'] ?? $debugData;
|
||||||
|
|
||||||
|
echo json_encode([
|
||||||
|
'mode' => 'debug - no $select, cache bypassed',
|
||||||
|
'top_level_keys' => is_array($debugData) ? array_keys($debugData) : null,
|
||||||
|
'fields_found_in_first_record' => isset($debugClients[0]) ? array_keys($debugClients[0]) : null,
|
||||||
|
'sample_records' => $debugClients,
|
||||||
|
], JSON_PRETTY_PRINT | JSON_UNESCAPED_UNICODE);
|
||||||
|
exit;
|
||||||
|
}
|
||||||
|
// ── FINE DEBUG MODE ──────────────────────────────────────────────────
|
||||||
|
|
||||||
|
// Parametri OData
|
||||||
|
$params = [
|
||||||
|
'$select' => 'IdCliente,Nominativo,CodiceCliente,Inattivo',
|
||||||
|
'$orderby' => 'Nominativo asc'
|
||||||
|
];
|
||||||
|
|
||||||
|
// Costruisce query string con encoding corretto
|
||||||
|
$queryString = http_build_query($params);
|
||||||
|
|
||||||
|
// Componi endpoint finale
|
||||||
|
$endpoint = "Cliente?$queryString";
|
||||||
|
|
||||||
|
// Funzione per eseguire la chiamata con retry
|
||||||
|
function makeApiRequest($api, $endpoint, $maxRetries = 3)
|
||||||
|
{
|
||||||
|
for ($retry = 0; $retry < $maxRetries; $retry++) {
|
||||||
|
try {
|
||||||
|
// Tenta la chiamata API
|
||||||
|
$data = $api->get($endpoint);
|
||||||
|
// Salva risposta per debug
|
||||||
|
file_put_contents(__DIR__ . '/clienti_response.json', json_encode($data, JSON_PRETTY_PRINT));
|
||||||
|
return $data;
|
||||||
|
} catch (Exception $e) {
|
||||||
|
$errorMessage = $e->getMessage();
|
||||||
|
// Controlla se l'errore è legato all'autenticazione (HTTP 400 con messaggio specifico)
|
||||||
|
if (strpos($errorMessage, 'HTTP 400') !== false && strpos($errorMessage, 'Cannot persist the object') !== false) {
|
||||||
|
// Forza il refresh del token
|
||||||
|
try {
|
||||||
|
// Assumi che VisualLimsApiClient abbia un metodo per il refresh del token
|
||||||
|
$api->refreshToken(); // Da implementare in VisualLimsApiClient se non esiste
|
||||||
|
error_log("Tentativo $retry: Refresh token eseguito per endpoint $endpoint");
|
||||||
|
} catch (Exception $refreshEx) {
|
||||||
|
error_log("Errore durante il refresh del token: " . $refreshEx->getMessage());
|
||||||
|
throw new Exception("Impossibile eseguire il refresh del token: " . $refreshEx->getMessage());
|
||||||
|
}
|
||||||
|
// Ritarda leggermente prima del retry
|
||||||
|
usleep(500000); // 500ms
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
// Altri errori non gestiti dal retry
|
||||||
|
throw $e;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
throw new Exception("Massimo numero di tentativi raggiunto per $endpoint");
|
||||||
|
}
|
||||||
|
|
||||||
|
// Cache file (1 hour TTL)
|
||||||
|
$cacheFile = __DIR__ . '/cache/clienti.json';
|
||||||
|
if (file_exists($cacheFile) && (time() - filemtime($cacheFile) < 3600)) {
|
||||||
|
readfile($cacheFile);
|
||||||
|
exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Esegui la chiamata con retry
|
||||||
|
$data = makeApiRequest($api, $endpoint);
|
||||||
|
|
||||||
|
$json = json_encode($data);
|
||||||
|
if (!is_dir(__DIR__ . '/cache')) mkdir(__DIR__ . '/cache', 0777, true);
|
||||||
|
file_put_contents($cacheFile, $json);
|
||||||
|
|
||||||
|
echo $json;
|
||||||
|
} catch (Exception $e) {
|
||||||
|
http_response_code(500);
|
||||||
|
$errorResponse = [
|
||||||
|
'error' => $e->getMessage(),
|
||||||
|
'file' => $e->getFile(),
|
||||||
|
'line' => $e->getLine(),
|
||||||
|
'trace' => $e->getTraceAsString()
|
||||||
|
];
|
||||||
|
error_log("Errore in get_clienti.php: " . json_encode($errorResponse));
|
||||||
|
echo json_encode($errorResponse);
|
||||||
|
}
|
||||||
@@ -1874,7 +1874,70 @@ $apiSampleJson = $template['api_sample_json'] ?? '';
|
|||||||
document.querySelectorAll('#schemaFieldsBody .main-field-checkbox').forEach(cb => {
|
document.querySelectorAll('#schemaFieldsBody .main-field-checkbox').forEach(cb => {
|
||||||
cb.dataset.originalChecked = cb.checked ? 'true' : 'false';
|
cb.dataset.originalChecked = cb.checked ? 'true' : 'false';
|
||||||
});
|
});
|
||||||
|
// =======================
|
||||||
|
// SAVE: Main / Import / Parts checkboxes
|
||||||
|
// =======================
|
||||||
|
function saveMappingFlag(checkboxEl, field) {
|
||||||
|
const mappingId = checkboxEl.getAttribute('data-mapping-id');
|
||||||
|
const checked = checkboxEl.checked;
|
||||||
|
|
||||||
|
fetch('update_mapping_flag.php', {
|
||||||
|
method: 'POST',
|
||||||
|
headers: {
|
||||||
|
'Content-Type': 'application/json'
|
||||||
|
},
|
||||||
|
body: JSON.stringify({
|
||||||
|
id: mappingId,
|
||||||
|
field: field,
|
||||||
|
value: checked ? 1 : 0
|
||||||
|
})
|
||||||
|
})
|
||||||
|
.then(response => response.json())
|
||||||
|
.then(data => {
|
||||||
|
if (!data.success) {
|
||||||
|
console.error("❌ Error saving flag:", field, data.message);
|
||||||
|
|
||||||
|
if (data.limit_reached) {
|
||||||
|
alert(data.message);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Revert: il server ha rifiutato, riporto la checkbox allo stato precedente
|
||||||
|
checkboxEl.checked = !checked;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
console.log("✅ Flag saved:", field, "=", checked ? 1 : 0, "for id", mappingId);
|
||||||
|
|
||||||
|
// Se il server ha deselezionato un'altra riga (caso "parts"), rifletto anche in UI
|
||||||
|
if (data.unchecked_others && field === 'is_visible_parts') {
|
||||||
|
document.querySelectorAll('#schemaFieldsBody .visible-parts-checkbox').forEach(cb => {
|
||||||
|
if (cb !== checkboxEl) {
|
||||||
|
cb.checked = false;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.catch(error => {
|
||||||
|
console.error("❌ Fetch error saving flag:", field, error);
|
||||||
|
checkboxEl.checked = !checked;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
document.getElementById('schemaFieldsBody').addEventListener('change', function(event) {
|
||||||
|
const el = event.target;
|
||||||
|
|
||||||
|
if (el.classList.contains('main-field-checkbox')) {
|
||||||
|
saveMappingFlag(el, 'main_field');
|
||||||
|
}
|
||||||
|
|
||||||
|
if (el.classList.contains('visible-import-checkbox')) {
|
||||||
|
saveMappingFlag(el, 'is_visible_import');
|
||||||
|
}
|
||||||
|
|
||||||
|
if (el.classList.contains('visible-parts-checkbox')) {
|
||||||
|
saveMappingFlag(el, 'is_visible_parts');
|
||||||
|
}
|
||||||
|
});
|
||||||
// AUTO VALUE select change -> save auto_value
|
// AUTO VALUE select change -> save auto_value
|
||||||
document.getElementById('schemaFieldsBody').addEventListener('change', function(event) {
|
document.getElementById('schemaFieldsBody').addEventListener('change', function(event) {
|
||||||
if (!event.target.classList.contains('auto-value-select')) return;
|
if (!event.target.classList.contains('auto-value-select')) return;
|
||||||
|
|||||||
File diff suppressed because one or more lines are too long
@@ -0,0 +1,107 @@
|
|||||||
|
<?php
|
||||||
|
header('Content-Type: application/json');
|
||||||
|
ini_set('display_errors', 1);
|
||||||
|
ini_set('display_startup_errors', 1);
|
||||||
|
error_reporting(E_ALL);
|
||||||
|
|
||||||
|
require_once(__DIR__ . '/class/db-functions.php');
|
||||||
|
|
||||||
|
$db = DBHandlerSelect::getInstance();
|
||||||
|
$pdo = $db->getConnection();
|
||||||
|
|
||||||
|
$data = json_decode(file_get_contents("php://input"), true);
|
||||||
|
|
||||||
|
if (!$data || !isset($data['id'], $data['field'])) {
|
||||||
|
echo json_encode(["success" => false, "message" => "Invalid or missing parameters"]);
|
||||||
|
exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
$mappingId = (int)$data['id'];
|
||||||
|
$field = $data['field'];
|
||||||
|
$value = isset($data['value']) && $data['value'] ? 1 : 0;
|
||||||
|
|
||||||
|
// Whitelist rigorosa dei campi aggiornabili: evita SQL injection sul nome colonna
|
||||||
|
$allowedFields = ['main_field', 'is_visible_import', 'is_visible_parts'];
|
||||||
|
|
||||||
|
if (!in_array($field, $allowedFields, true)) {
|
||||||
|
echo json_encode(["success" => false, "message" => "Invalid field"]);
|
||||||
|
exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
// Recupero il template_id della riga per poter applicare i vincoli a livello di template
|
||||||
|
$stmt = $pdo->prepare("SELECT template_id FROM template_mapping WHERE id = ?");
|
||||||
|
$stmt->execute([$mappingId]);
|
||||||
|
$row = $stmt->fetch(PDO::FETCH_ASSOC);
|
||||||
|
|
||||||
|
if (!$row) {
|
||||||
|
echo json_encode(["success" => false, "message" => "Mapping not found"]);
|
||||||
|
exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
$templateId = (int)$row['template_id'];
|
||||||
|
|
||||||
|
// Vincolo: max 2 righe con main_field = 1 per template
|
||||||
|
if ($field === 'main_field' && $value === 1) {
|
||||||
|
$stmt = $pdo->prepare("
|
||||||
|
SELECT COUNT(*)
|
||||||
|
FROM template_mapping
|
||||||
|
WHERE template_id = ? AND main_field = 1 AND id != ?
|
||||||
|
");
|
||||||
|
$stmt->execute([$templateId, $mappingId]);
|
||||||
|
$count = (int)$stmt->fetchColumn();
|
||||||
|
|
||||||
|
if ($count >= 2) {
|
||||||
|
echo json_encode([
|
||||||
|
"success" => false,
|
||||||
|
"limit_reached" => true,
|
||||||
|
"message" => "Massimo 2 campi Main consentiti per questo template"
|
||||||
|
]);
|
||||||
|
exit;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
$pdo->beginTransaction();
|
||||||
|
|
||||||
|
$uncheckedOthers = false;
|
||||||
|
|
||||||
|
// Vincolo: solo 1 riga con is_visible_parts = 1 per template (comportamento radio)
|
||||||
|
if ($field === 'is_visible_parts' && $value === 1) {
|
||||||
|
$stmt = $pdo->prepare("
|
||||||
|
UPDATE template_mapping
|
||||||
|
SET is_visible_parts = 0
|
||||||
|
WHERE template_id = ? AND id != ? AND is_visible_parts = 1
|
||||||
|
");
|
||||||
|
$stmt->execute([$templateId, $mappingId]);
|
||||||
|
$uncheckedOthers = $stmt->rowCount() > 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
$stmt = $pdo->prepare("UPDATE template_mapping SET {$field} = ? WHERE id = ?");
|
||||||
|
$result = $stmt->execute([$value, $mappingId]);
|
||||||
|
|
||||||
|
if (!$result) {
|
||||||
|
$pdo->rollBack();
|
||||||
|
echo json_encode(["success" => false, "message" => "Database update failed"]);
|
||||||
|
exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
$pdo->commit();
|
||||||
|
|
||||||
|
echo json_encode([
|
||||||
|
"success" => true,
|
||||||
|
"message" => "Flag updated successfully",
|
||||||
|
"unchecked_others" => $uncheckedOthers,
|
||||||
|
"saved" => [
|
||||||
|
"id" => $mappingId,
|
||||||
|
"field" => $field,
|
||||||
|
"value" => $value
|
||||||
|
]
|
||||||
|
]);
|
||||||
|
} catch (Throwable $e) {
|
||||||
|
if ($pdo->inTransaction()) {
|
||||||
|
$pdo->rollBack();
|
||||||
|
}
|
||||||
|
echo json_encode(["success" => false, "message" => "Error: " . $e->getMessage()]);
|
||||||
|
}
|
||||||
|
|
||||||
|
exit;
|
||||||
@@ -135,25 +135,26 @@ try {
|
|||||||
|
|
||||||
// Update existing field AND clear user-entered value if type changed
|
// Update existing field AND clear user-entered value if type changed
|
||||||
$updateChangedTypeStmt = $pdo->prepare("
|
$updateChangedTypeStmt = $pdo->prepare("
|
||||||
UPDATE template_mapping
|
UPDATE template_mapping
|
||||||
SET
|
SET
|
||||||
schema_id = :schema_id,
|
schema_id = :schema_id,
|
||||||
data_type = :data_type,
|
field_order = :field_order,
|
||||||
is_required = :is_required,
|
data_type = :data_type,
|
||||||
default_value = :default_value,
|
is_required = :is_required,
|
||||||
has_list = :has_list,
|
default_value = :default_value,
|
||||||
length = :length,
|
has_list = :has_list,
|
||||||
decimals = :decimals,
|
length = :length,
|
||||||
min_value = :min_value,
|
decimals = :decimals,
|
||||||
max_value = :max_value,
|
min_value = :min_value,
|
||||||
default_curr_date = :default_curr_date,
|
max_value = :max_value,
|
||||||
tablename = :tablename,
|
default_curr_date = :default_curr_date,
|
||||||
field_label = :field_label,
|
tablename = :tablename,
|
||||||
|
field_label = :field_label,
|
||||||
|
|
||||||
manual_default = NULL,
|
manual_default = NULL,
|
||||||
auto_value = 'none'
|
auto_value = 'none'
|
||||||
|
|
||||||
WHERE id = :id
|
WHERE id = :id
|
||||||
");
|
");
|
||||||
|
|
||||||
$currentFieldIds = [];
|
$currentFieldIds = [];
|
||||||
|
|||||||
@@ -177,6 +177,8 @@ try {
|
|||||||
SELECT iddatadb, part_number, part_description, idmatrice
|
SELECT iddatadb, part_number, part_description, idmatrice
|
||||||
FROM identification_parts
|
FROM identification_parts
|
||||||
WHERE iddatadb IN ($placeholders)
|
WHERE iddatadb IN ($placeholders)
|
||||||
|
AND part_description IS NOT NULL
|
||||||
|
AND TRIM(part_description) <> ''
|
||||||
");
|
");
|
||||||
$stmt->execute($iddatadbList);
|
$stmt->execute($iddatadbList);
|
||||||
$partsInfo = [];
|
$partsInfo = [];
|
||||||
|
|||||||
Reference in New Issue
Block a user