Compare commits
36 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 82d6a2ee18 | |||
| d8eddb3aa5 | |||
| f60dc64b2d | |||
| 4e4cae1df8 | |||
| 8838edf3a1 | |||
| e75be99e43 | |||
| a482d975da | |||
| 598a2cc84c | |||
| 6ec0c2062e | |||
| 5eb5bd1613 | |||
| 03771e3ca8 | |||
| 03642fdfab | |||
| f6ea17388c | |||
| 1c2b4ab7a6 | |||
| 31cb23b00e | |||
| d29563d20d | |||
| 82af925ac1 | |||
| 5d8360dd87 | |||
| 683073c244 | |||
| 8d6fe92481 | |||
| dbc66723a6 | |||
| 218fc14462 | |||
| 29e4b41874 | |||
| eef9ae8d36 | |||
| 68c867a3f4 | |||
| a9827e4e81 | |||
| b51936f784 | |||
| 15b6f38e8b | |||
| 12c6cc5f95 | |||
| a0b12463c0 | |||
| 07ddcafd3f | |||
| 7843d4b1fc | |||
| 4eae855e23 | |||
| c709f64a17 | |||
| d5f0690f59 | |||
| 6bbd3fcae9 |
File diff suppressed because it is too large
Load Diff
@@ -29,7 +29,7 @@ class VisualLimsApiClient
|
|||||||
return self::$instance;
|
return self::$instance;
|
||||||
}
|
}
|
||||||
|
|
||||||
private function authenticate()
|
private function authenticate($retryCount = 0, $maxRetries = 3)
|
||||||
{
|
{
|
||||||
$ch = curl_init("{$this->baseUrl}/api/authentication/authenticate");
|
$ch = curl_init("{$this->baseUrl}/api/authentication/authenticate");
|
||||||
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
|
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
|
||||||
@@ -45,16 +45,22 @@ class VisualLimsApiClient
|
|||||||
curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);
|
curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);
|
||||||
curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, false);
|
curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, false);
|
||||||
curl_setopt($ch, CURLOPT_VERBOSE, true);
|
curl_setopt($ch, CURLOPT_VERBOSE, true);
|
||||||
$log = fopen(__DIR__ . '/curl_auth_debug.log', 'w') ?: fopen('php://stderr', 'w');
|
$log = fopen(__DIR__ . '/curl_auth_debug.log', 'a') ?: fopen('php://stderr', 'w');
|
||||||
curl_setopt($ch, CURLOPT_STDERR, $log);
|
curl_setopt($ch, CURLOPT_STDERR, $log);
|
||||||
|
|
||||||
$response = curl_exec($ch);
|
$response = curl_exec($ch);
|
||||||
$http_code = curl_getinfo($ch, CURLINFO_HTTP_CODE);
|
$http_code = curl_getinfo($ch, CURLINFO_HTTP_CODE);
|
||||||
$curl_error = curl_error($ch);
|
$curl_error = curl_error($ch);
|
||||||
|
$log_message = date('Y-m-d H:i:s') . " - Auth attempt {$retryCount}: HTTP {$http_code}, Error: {$curl_error}, Response: " . substr($response, 0, 1000) . "\n";
|
||||||
|
fwrite($log, $log_message);
|
||||||
fclose($log);
|
fclose($log);
|
||||||
curl_close($ch);
|
curl_close($ch);
|
||||||
|
|
||||||
if ($response === false || $http_code != 200) {
|
if ($response === false || $http_code != 200) {
|
||||||
|
if ($http_code === 400 && strpos($response, 'Cannot persist the object') !== false && $retryCount < $maxRetries) {
|
||||||
|
usleep(500000); // Ritardo di 500ms
|
||||||
|
return $this->authenticate($retryCount + 1, $maxRetries); // Riprova
|
||||||
|
}
|
||||||
throw new Exception("Autenticazione fallita: HTTP {$http_code}, Errore cURL: {$curl_error}, Risposta: " . substr($response, 0, 1000));
|
throw new Exception("Autenticazione fallita: HTTP {$http_code}, Errore cURL: {$curl_error}, Risposta: " . substr($response, 0, 1000));
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -191,5 +197,4 @@ class VisualLimsApiClient
|
|||||||
{
|
{
|
||||||
return $this->baseUrl;
|
return $this->baseUrl;
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,77 @@
|
|||||||
|
<?php
|
||||||
|
header('Content-Type: application/json');
|
||||||
|
|
||||||
|
require_once(__DIR__ . '/include/headscript.php');
|
||||||
|
|
||||||
|
$db = DBHandlerSelect::getInstance();
|
||||||
|
$pdo = $db->getConnection();
|
||||||
|
|
||||||
|
$input = json_decode(file_get_contents('php://input'), true);
|
||||||
|
$templateId = (int)($input['template_id'] ?? 0);
|
||||||
|
|
||||||
|
if ($templateId <= 0) {
|
||||||
|
echo json_encode(['success' => false, 'message' => 'Invalid template_id']);
|
||||||
|
exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
// If already exists, do nothing
|
||||||
|
$stmt = $pdo->prepare("SELECT COUNT(*) FROM template_fixed_mapping WHERE template_id = ?");
|
||||||
|
$stmt->execute([$templateId]);
|
||||||
|
if ((int)$stmt->fetchColumn() > 0) {
|
||||||
|
echo json_encode(['success' => true, 'created' => 0, 'message' => 'Fixed fields already exist']);
|
||||||
|
exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* FIXED FIELDS STANDARD (no UI selection)
|
||||||
|
* is_manual always 1
|
||||||
|
* is_visible_import default 1
|
||||||
|
*/
|
||||||
|
$fixedFields = [
|
||||||
|
// fixed_field_key, data_type
|
||||||
|
['ClienteResponsabile', 'INT'],
|
||||||
|
['MoltiplicatorePrezzo', 'INT'],
|
||||||
|
['AnagraficaCertestObject', 'INT'],
|
||||||
|
['AnagraficaCertestService', 'INT'],
|
||||||
|
['ConsegnaRichiesta', 'DATE'],
|
||||||
|
];
|
||||||
|
|
||||||
|
|
||||||
|
try {
|
||||||
|
$pdo->beginTransaction();
|
||||||
|
|
||||||
|
$ins = $pdo->prepare("
|
||||||
|
INSERT INTO template_fixed_mapping
|
||||||
|
(template_id, fixed_field_key, is_manual, data_type, is_required, default_value, is_visible_import)
|
||||||
|
VALUES
|
||||||
|
(:template_id, :fixed_field_key, 1, :data_type, 1, NULL, 1)
|
||||||
|
");
|
||||||
|
|
||||||
|
|
||||||
|
foreach ($fixedFields as $f) {
|
||||||
|
$ins->execute([
|
||||||
|
':template_id' => $templateId,
|
||||||
|
':fixed_field_key' => $f[0],
|
||||||
|
':data_type' => $f[1],
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
$pdo->commit();
|
||||||
|
|
||||||
|
// Return rows (for client render)
|
||||||
|
$stmt = $pdo->prepare("
|
||||||
|
SELECT id, template_id, fixed_field_key, is_manual, data_type, is_required, default_value, is_visible_import
|
||||||
|
FROM template_fixed_mapping
|
||||||
|
WHERE template_id = ?
|
||||||
|
ORDER BY id ASC
|
||||||
|
");
|
||||||
|
|
||||||
|
$stmt->execute([$templateId]);
|
||||||
|
$rows = $stmt->fetchAll(PDO::FETCH_ASSOC);
|
||||||
|
|
||||||
|
echo json_encode(['success' => true, 'created' => count($rows), 'rows' => $rows]);
|
||||||
|
} catch (Exception $e) {
|
||||||
|
if ($pdo->inTransaction()) $pdo->rollBack();
|
||||||
|
echo json_encode(['success' => false, 'message' => $e->getMessage()]);
|
||||||
|
}
|
||||||
@@ -0,0 +1,103 @@
|
|||||||
|
<?php
|
||||||
|
require_once dirname(__DIR__, 3) . '/vendor/autoload.php'; // Risale a root/vendor/
|
||||||
|
require_once dirname(__FILE__) . '/../class/VisualLimsApiClient.class.php'; // In root/public/userarea/class/
|
||||||
|
|
||||||
|
use Dotenv\Dotenv;
|
||||||
|
|
||||||
|
// Debug: Log the path where we expect the .env file
|
||||||
|
$envPath = dirname(__DIR__, 3);
|
||||||
|
file_put_contents(__DIR__ . '/debug_log.txt', date('Y-m-d H:i:s') . ' - Expected .env path: ' . $envPath . PHP_EOL, FILE_APPEND);
|
||||||
|
|
||||||
|
// Carica il file .env dalla root del progetto
|
||||||
|
try {
|
||||||
|
$dotenv = Dotenv::createImmutable($envPath);
|
||||||
|
$dotenv->load();
|
||||||
|
} catch (Exception $e) {
|
||||||
|
file_put_contents(__DIR__ . '/error_log.txt', date('Y-m-d H:i:s') . ' - Errore caricamento .env: ' . $e->getMessage() . PHP_EOL, FILE_APPEND);
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Recupera le variabili d'ambiente
|
||||||
|
$dbHost = $_ENV['DB_HOST'];
|
||||||
|
$dbName = $_ENV['DB_DATABASE'];
|
||||||
|
$dbUser = $_ENV['DB_USERNAME'];
|
||||||
|
$dbPass = $_ENV['DB_PASSWORD'];
|
||||||
|
$dbPrefix = $_ENV['DB_PREFIX'];
|
||||||
|
|
||||||
|
// Debug: Log database connection details (excluding password)
|
||||||
|
file_put_contents(__DIR__ . '/debug_log.txt', date('Y-m-d H:i:s') . " - DB Connection: host=$dbHost, dbname=$dbName, user=$dbUser, prefix=$dbPrefix" . PHP_EOL, FILE_APPEND);
|
||||||
|
|
||||||
|
// Connessione al database MySQL
|
||||||
|
try {
|
||||||
|
$pdo = new PDO("mysql:host=$dbHost;dbname=$dbName;charset=utf8mb4", $dbUser, $dbPass);
|
||||||
|
$pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
|
||||||
|
} catch (PDOException $e) {
|
||||||
|
file_put_contents(__DIR__ . '/error_log.txt', date('Y-m-d H:i:s') . ' - Errore connessione DB: ' . $e->getMessage() . PHP_EOL, FILE_APPEND);
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
$api = VisualLimsApiClient::getInstance();
|
||||||
|
|
||||||
|
// Endpoint per recuperare le Matrici
|
||||||
|
$endpoint = 'Matrice';
|
||||||
|
|
||||||
|
// (Opzionale) aggiungi parametri
|
||||||
|
$options = []; // es. ['$top' => 100]
|
||||||
|
|
||||||
|
// Debug: salva URL usato
|
||||||
|
$base_url = 'https://93.43.5.102/limsapi/api/odata/';
|
||||||
|
$query = http_build_query($options);
|
||||||
|
$full_url = $base_url . $endpoint . ($query ? '?' . $query : '');
|
||||||
|
file_put_contents(__DIR__ . '/last_url.txt', $full_url . PHP_EOL, FILE_APPEND);
|
||||||
|
|
||||||
|
// Chiamata API
|
||||||
|
$data = $api->get($endpoint, $options);
|
||||||
|
|
||||||
|
// Debug: Log the API response size
|
||||||
|
file_put_contents(__DIR__ . '/debug_log.txt', date('Y-m-d H:i:s') . ' - API response received, value count: ' . (isset($data['value']) ? count($data['value']) : 0) . PHP_EOL, FILE_APPEND);
|
||||||
|
|
||||||
|
// Salva il JSON in locale (opzionale, per debug)
|
||||||
|
file_put_contents(__DIR__ . '/matrici_response.json', json_encode($data, JSON_PRETTY_PRINT));
|
||||||
|
|
||||||
|
// Svuota la tabella (dump rapido)
|
||||||
|
$pdo->exec("TRUNCATE TABLE {$dbPrefix}matrici");
|
||||||
|
|
||||||
|
// Debug: Log after truncate
|
||||||
|
file_put_contents(__DIR__ . '/debug_log.txt', date('Y-m-d H:i:s') . ' - Table truncated: ' . $dbPrefix . 'matrici' . PHP_EOL, FILE_APPEND);
|
||||||
|
|
||||||
|
// Prepara l'insert
|
||||||
|
$stmt = $pdo->prepare("
|
||||||
|
INSERT INTO {$dbPrefix}matrici (
|
||||||
|
IdMatrice, NomeMatriceTraduzione, DescrizioneTraduzione, MacroMatrice, NomeMatrice, Descrizione
|
||||||
|
) VALUES (
|
||||||
|
:IdMatrice, :NomeMatriceTraduzione, :DescrizioneTraduzione, :MacroMatrice, :NomeMatrice, :Descrizione
|
||||||
|
)
|
||||||
|
");
|
||||||
|
|
||||||
|
// Inserisci i dati
|
||||||
|
$insertedRows = 0;
|
||||||
|
if (isset($data['value']) && is_array($data['value'])) {
|
||||||
|
foreach ($data['value'] as $item) {
|
||||||
|
$stmt->execute([
|
||||||
|
':IdMatrice' => $item['IdMatrice'],
|
||||||
|
':NomeMatriceTraduzione' => $item['NomeMatriceTraduzione'],
|
||||||
|
':DescrizioneTraduzione' => $item['DescrizioneTraduzione'] ?? null,
|
||||||
|
':MacroMatrice' => $item['MacroMatrice'] ?? null,
|
||||||
|
':NomeMatrice' => $item['NomeMatrice'],
|
||||||
|
':Descrizione' => $item['Descrizione'] ?? null,
|
||||||
|
]);
|
||||||
|
$insertedRows++;
|
||||||
|
// Debug: Log each inserted row
|
||||||
|
file_put_contents(__DIR__ . '/debug_log.txt', date('Y-m-d H:i:s') . ' - Inserted row with IdMatrice: ' . $item['IdMatrice'] . PHP_EOL, FILE_APPEND);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Log successo
|
||||||
|
file_put_contents(__DIR__ . '/success_log.txt', date('Y-m-d H:i:s') . ' - Aggiornamento completato: ' . $insertedRows . ' record inseriti.' . PHP_EOL, FILE_APPEND);
|
||||||
|
} catch (Exception $e) {
|
||||||
|
file_put_contents(__DIR__ . '/error_log.txt', date('Y-m-d H:i:s') . ' - ' . $e->getMessage() . PHP_EOL, FILE_APPEND);
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
exit(0); // Esci con successo per cron
|
||||||
File diff suppressed because one or more lines are too long
@@ -12,3 +12,18 @@
|
|||||||
2025-08-26 16:49:24 - Risposta non JSON valida: <?xml version="1.0" encoding="utf-8"?><edmx:Edmx Version="4.0" xmlns:edmx="http://docs.oasis-open.org/odata/ns/edmx"><edmx:DataServices><Schema Namespace="DevExpress.ExpressApp.SystemModule" xmlns="http://docs.oasis-open.org/odata/ns/edm"><EntityType Name="DashboardViewItemDescriptor"><Key><PropertyRef Name="ViewId" /></Key><Property Name="ViewId" Type="Edm.String" Nullable="false" /></EntityType><EntityType Name="DashboardOrganizationItem" BaseType="DevExpress.ExpressApp.NonPersistentLiteObject" Abstract="true"><Property Name="Visibility" Type="DevExpress.ExpressApp.Editors.ViewItemVisibility" Nullable="false" /></EntityType><EntityType Name="DashboardOrganizationItem_1OfIModelDashboardViewItem" BaseType="DevExpress.ExpressApp.SystemModule.DashboardOrganizationItem" Abstract="true" /><EntityType Name="ViewDashboardOrganizationItem" BaseType="DevExpress.ExpressApp.SystemModule.DashboardOrganizationItem_1OfIModelDashboardViewItem"><Property Name="ObjectType" Type="System.Type" /><Proper
|
2025-08-26 16:49:24 - Risposta non JSON valida: <?xml version="1.0" encoding="utf-8"?><edmx:Edmx Version="4.0" xmlns:edmx="http://docs.oasis-open.org/odata/ns/edmx"><edmx:DataServices><Schema Namespace="DevExpress.ExpressApp.SystemModule" xmlns="http://docs.oasis-open.org/odata/ns/edm"><EntityType Name="DashboardViewItemDescriptor"><Key><PropertyRef Name="ViewId" /></Key><Property Name="ViewId" Type="Edm.String" Nullable="false" /></EntityType><EntityType Name="DashboardOrganizationItem" BaseType="DevExpress.ExpressApp.NonPersistentLiteObject" Abstract="true"><Property Name="Visibility" Type="DevExpress.ExpressApp.Editors.ViewItemVisibility" Nullable="false" /></EntityType><EntityType Name="DashboardOrganizationItem_1OfIModelDashboardViewItem" BaseType="DevExpress.ExpressApp.SystemModule.DashboardOrganizationItem" Abstract="true" /><EntityType Name="ViewDashboardOrganizationItem" BaseType="DevExpress.ExpressApp.SystemModule.DashboardOrganizationItem_1OfIModelDashboardViewItem"><Property Name="ObjectType" Type="System.Type" /><Proper
|
||||||
2025-08-26 16:50:23 - Risposta non JSON valida: <?xml version="1.0" encoding="utf-8"?><edmx:Edmx Version="4.0" xmlns:edmx="http://docs.oasis-open.org/odata/ns/edmx"><edmx:DataServices><Schema Namespace="DevExpress.ExpressApp.SystemModule" xmlns="http://docs.oasis-open.org/odata/ns/edm"><EntityType Name="DashboardViewItemDescriptor"><Key><PropertyRef Name="ViewId" /></Key><Property Name="ViewId" Type="Edm.String" Nullable="false" /></EntityType><EntityType Name="DashboardOrganizationItem" BaseType="DevExpress.ExpressApp.NonPersistentLiteObject" Abstract="true"><Property Name="Visibility" Type="DevExpress.ExpressApp.Editors.ViewItemVisibility" Nullable="false" /></EntityType><EntityType Name="DashboardOrganizationItem_1OfIModelDashboardViewItem" BaseType="DevExpress.ExpressApp.SystemModule.DashboardOrganizationItem" Abstract="true" /><EntityType Name="ViewDashboardOrganizationItem" BaseType="DevExpress.ExpressApp.SystemModule.DashboardOrganizationItem_1OfIModelDashboardViewItem"><Property Name="ObjectType" Type="System.Type" /><Proper
|
2025-08-26 16:50:23 - Risposta non JSON valida: <?xml version="1.0" encoding="utf-8"?><edmx:Edmx Version="4.0" xmlns:edmx="http://docs.oasis-open.org/odata/ns/edmx"><edmx:DataServices><Schema Namespace="DevExpress.ExpressApp.SystemModule" xmlns="http://docs.oasis-open.org/odata/ns/edm"><EntityType Name="DashboardViewItemDescriptor"><Key><PropertyRef Name="ViewId" /></Key><Property Name="ViewId" Type="Edm.String" Nullable="false" /></EntityType><EntityType Name="DashboardOrganizationItem" BaseType="DevExpress.ExpressApp.NonPersistentLiteObject" Abstract="true"><Property Name="Visibility" Type="DevExpress.ExpressApp.Editors.ViewItemVisibility" Nullable="false" /></EntityType><EntityType Name="DashboardOrganizationItem_1OfIModelDashboardViewItem" BaseType="DevExpress.ExpressApp.SystemModule.DashboardOrganizationItem" Abstract="true" /><EntityType Name="ViewDashboardOrganizationItem" BaseType="DevExpress.ExpressApp.SystemModule.DashboardOrganizationItem_1OfIModelDashboardViewItem"><Property Name="ObjectType" Type="System.Type" /><Proper
|
||||||
2025-09-08 08:39:17 - Risposta non JSON valida: <?xml version="1.0" encoding="utf-8"?><edmx:Edmx Version="4.0" xmlns:edmx="http://docs.oasis-open.org/odata/ns/edmx"><edmx:DataServices><Schema Namespace="DevExpress.ExpressApp.SystemModule" xmlns="http://docs.oasis-open.org/odata/ns/edm"><EntityType Name="DashboardViewItemDescriptor"><Key><PropertyRef Name="ViewId" /></Key><Property Name="ViewId" Type="Edm.String" Nullable="false" /></EntityType><EntityType Name="DashboardOrganizationItem" BaseType="DevExpress.ExpressApp.NonPersistentLiteObject" Abstract="true"><Property Name="Visibility" Type="DevExpress.ExpressApp.Editors.ViewItemVisibility" Nullable="false" /></EntityType><EntityType Name="DashboardOrganizationItem_1OfIModelDashboardViewItem" BaseType="DevExpress.ExpressApp.SystemModule.DashboardOrganizationItem" Abstract="true" /><EntityType Name="ViewDashboardOrganizationItem" BaseType="DevExpress.ExpressApp.SystemModule.DashboardOrganizationItem_1OfIModelDashboardViewItem"><Property Name="ObjectType" Type="System.Type" /><Proper
|
2025-09-08 08:39:17 - Risposta non JSON valida: <?xml version="1.0" encoding="utf-8"?><edmx:Edmx Version="4.0" xmlns:edmx="http://docs.oasis-open.org/odata/ns/edmx"><edmx:DataServices><Schema Namespace="DevExpress.ExpressApp.SystemModule" xmlns="http://docs.oasis-open.org/odata/ns/edm"><EntityType Name="DashboardViewItemDescriptor"><Key><PropertyRef Name="ViewId" /></Key><Property Name="ViewId" Type="Edm.String" Nullable="false" /></EntityType><EntityType Name="DashboardOrganizationItem" BaseType="DevExpress.ExpressApp.NonPersistentLiteObject" Abstract="true"><Property Name="Visibility" Type="DevExpress.ExpressApp.Editors.ViewItemVisibility" Nullable="false" /></EntityType><EntityType Name="DashboardOrganizationItem_1OfIModelDashboardViewItem" BaseType="DevExpress.ExpressApp.SystemModule.DashboardOrganizationItem" Abstract="true" /><EntityType Name="ViewDashboardOrganizationItem" BaseType="DevExpress.ExpressApp.SystemModule.DashboardOrganizationItem_1OfIModelDashboardViewItem"><Property Name="ObjectType" Type="System.Type" /><Proper
|
||||||
|
2026-01-27 15:33:53 - Errore nel recupero dati: HTTP 404, Risposta:
|
||||||
|
2026-01-27 15:34:06 - Errore nel recupero dati: HTTP 404, Risposta:
|
||||||
|
2026-01-27 15:34:10 - Errore nel recupero dati: HTTP 404, Risposta:
|
||||||
|
2026-01-27 15:35:13 - Errore nel recupero dati: HTTP 404, Risposta:
|
||||||
|
2026-01-29 14:33:38 [ClienteResponsabile] Errore nel recupero dati: HTTP 404, Risposta:
|
||||||
|
2026-01-29 14:33:39 [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-29 14:34:04 [ClienteResponsabile] Errore nel recupero dati: HTTP 404, Risposta:
|
||||||
|
2026-01-29 14:37:29 [ClienteResponsabile] Errore nel recupero dati: HTTP 404, Risposta:
|
||||||
|
2026-01-29 14:41:55 [ClienteResponsabile] Errore nel recupero dati: HTTP 404, Risposta:
|
||||||
|
2026-01-29 14:42:03 [ClienteResponsabile] Errore nel recupero dati: HTTP 404, Risposta:
|
||||||
|
2026-01-29 14:42:52 [ClienteResponsabile] Errore nel recupero dati: HTTP 404, Risposta:
|
||||||
|
2026-01-29 14:43:00 [ClienteResponsabile] Errore nel recupero dati: HTTP 404, Risposta:
|
||||||
|
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 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"}
|
||||||
|
|||||||
@@ -33,14 +33,14 @@ try {
|
|||||||
throw new Exception("Missing iddatadb");
|
throw new Exception("Missing iddatadb");
|
||||||
}
|
}
|
||||||
|
|
||||||
// 🔹 STEP 1+2: Fetch Cliente ID + Schema ID
|
// 🔹 STEP 1+2: Fetch Cliente ID from datadb and Schema ID from excel_templates
|
||||||
$stmt = $pdo->prepare("
|
$stmt = $pdo->prepare("
|
||||||
SELECT et.idclient AS clienteId, et.idschema AS schemaId
|
SELECT d.idclient AS clienteId, et.idschema AS schemaId
|
||||||
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
|
||||||
LIMIT 1
|
LIMIT 1
|
||||||
");
|
");
|
||||||
$stmt->execute(['iddatadb' => $iddatadb]);
|
$stmt->execute(['iddatadb' => $iddatadb]);
|
||||||
$result = $stmt->fetch(PDO::FETCH_ASSOC);
|
$result = $stmt->fetch(PDO::FETCH_ASSOC);
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,40 @@
|
|||||||
|
<?php
|
||||||
|
require_once dirname(__DIR__, 2) . '/vendor/autoload.php';
|
||||||
|
require_once dirname(__FILE__) . '/class/VisualLimsApiClient.class.php';
|
||||||
|
|
||||||
|
header('Content-Type: application/json');
|
||||||
|
|
||||||
|
ini_set('display_errors', '0');
|
||||||
|
error_reporting(E_ALL);
|
||||||
|
|
||||||
|
try {
|
||||||
|
$api = VisualLimsApiClient::getInstance();
|
||||||
|
|
||||||
|
// Endpoint: GET api/odata/AnagraficaCertestObject
|
||||||
|
$endpoint = 'AnagraficaCertestObject';
|
||||||
|
|
||||||
|
// Opzionale: parametri OData ($top, $filter, $orderby, ecc.)
|
||||||
|
$options = []; // es: ['$top' => 100]
|
||||||
|
|
||||||
|
// Debug: salva URL usata
|
||||||
|
$base_url = 'https://93.43.5.102/limsapi/api/odata/';
|
||||||
|
$query = http_build_query($options);
|
||||||
|
$full_url = $base_url . $endpoint . ($query ? '?' . $query : '');
|
||||||
|
file_put_contents(__DIR__ . '/last_url.txt', $full_url . PHP_EOL, FILE_APPEND);
|
||||||
|
|
||||||
|
// Chiamata API
|
||||||
|
$data = $api->get($endpoint, $options);
|
||||||
|
|
||||||
|
// Salva il JSON in locale
|
||||||
|
file_put_contents(__DIR__ . '/anagrafica_certest_object_response.json', json_encode($data, JSON_PRETTY_PRINT));
|
||||||
|
|
||||||
|
echo json_encode($data);
|
||||||
|
} catch (Exception $e) {
|
||||||
|
file_put_contents(
|
||||||
|
__DIR__ . '/error_log.txt',
|
||||||
|
date('Y-m-d H:i:s') . ' - ' . $e->getMessage() . PHP_EOL,
|
||||||
|
FILE_APPEND
|
||||||
|
);
|
||||||
|
http_response_code(500);
|
||||||
|
echo json_encode(['error' => $e->getMessage()]);
|
||||||
|
}
|
||||||
@@ -0,0 +1,40 @@
|
|||||||
|
<?php
|
||||||
|
require_once dirname(__DIR__, 2) . '/vendor/autoload.php';
|
||||||
|
require_once dirname(__FILE__) . '/class/VisualLimsApiClient.class.php';
|
||||||
|
|
||||||
|
header('Content-Type: application/json');
|
||||||
|
|
||||||
|
ini_set('display_errors', '0');
|
||||||
|
error_reporting(E_ALL);
|
||||||
|
|
||||||
|
try {
|
||||||
|
$api = VisualLimsApiClient::getInstance();
|
||||||
|
|
||||||
|
// Endpoint: GET api/odata/AnagraficaCertestService
|
||||||
|
$endpoint = 'AnagraficaCertestService';
|
||||||
|
|
||||||
|
// Opzionale: parametri OData ($top, $filter, $orderby, ecc.)
|
||||||
|
$options = []; // es: ['$top' => 100]
|
||||||
|
|
||||||
|
// Debug: salva URL usata
|
||||||
|
$base_url = 'https://93.43.5.102/limsapi/api/odata/';
|
||||||
|
$query = http_build_query($options);
|
||||||
|
$full_url = $base_url . $endpoint . ($query ? '?' . $query : '');
|
||||||
|
file_put_contents(__DIR__ . '/last_url.txt', $full_url . PHP_EOL, FILE_APPEND);
|
||||||
|
|
||||||
|
// Chiamata API
|
||||||
|
$data = $api->get($endpoint, $options);
|
||||||
|
|
||||||
|
// Salva il JSON in locale
|
||||||
|
file_put_contents(__DIR__ . '/anagrafica_certest_service_response.json', json_encode($data, JSON_PRETTY_PRINT));
|
||||||
|
|
||||||
|
echo json_encode($data);
|
||||||
|
} catch (Exception $e) {
|
||||||
|
file_put_contents(
|
||||||
|
__DIR__ . '/error_log.txt',
|
||||||
|
date('Y-m-d H:i:s') . ' - ' . $e->getMessage() . PHP_EOL,
|
||||||
|
FILE_APPEND
|
||||||
|
);
|
||||||
|
http_response_code(500);
|
||||||
|
echo json_encode(['error' => $e->getMessage()]);
|
||||||
|
}
|
||||||
@@ -0,0 +1,40 @@
|
|||||||
|
<?php
|
||||||
|
require_once dirname(__DIR__, 2) . '/vendor/autoload.php';
|
||||||
|
require_once dirname(__FILE__) . '/class/VisualLimsApiClient.class.php';
|
||||||
|
|
||||||
|
header('Content-Type: application/json');
|
||||||
|
ini_set('display_errors', '0');
|
||||||
|
error_reporting(E_ALL);
|
||||||
|
|
||||||
|
try {
|
||||||
|
$idCliente = isset($_GET['id_cliente']) ? (int)$_GET['id_cliente'] : 0;
|
||||||
|
if ($idCliente <= 0) {
|
||||||
|
http_response_code(400);
|
||||||
|
echo json_encode(['error' => 'Missing or invalid id_cliente']);
|
||||||
|
exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
$api = VisualLimsApiClient::getInstance();
|
||||||
|
|
||||||
|
// Build endpoint with OData $expand WITHOUT encoding '$' as %24
|
||||||
|
$endpoint = "Cliente($idCliente)?\$expand=Responsabili";
|
||||||
|
|
||||||
|
// Debug URL (real final URL)
|
||||||
|
$base_url = 'https://93.43.5.102/limsapi/api/odata/';
|
||||||
|
$full_url = $base_url . $endpoint;
|
||||||
|
file_put_contents(__DIR__ . '/last_url.txt', $full_url . PHP_EOL, FILE_APPEND);
|
||||||
|
|
||||||
|
// Call API: options must be empty because expand is already in endpoint
|
||||||
|
$data = $api->get($endpoint, []);
|
||||||
|
|
||||||
|
|
||||||
|
file_put_contents(__DIR__ . '/cliente_responsabili_response.json', json_encode($data, JSON_PRETTY_PRINT));
|
||||||
|
|
||||||
|
// Return only the list (standard shape used by the frontend)
|
||||||
|
$responsabili = $data['Responsabili'] ?? [];
|
||||||
|
echo json_encode(['value' => $responsabili]);
|
||||||
|
} catch (Exception $e) {
|
||||||
|
file_put_contents(__DIR__ . '/error_log.txt', date('Y-m-d H:i:s') . ' - ' . $e->getMessage() . PHP_EOL, FILE_APPEND);
|
||||||
|
http_response_code(500);
|
||||||
|
echo json_encode(['error' => $e->getMessage()]);
|
||||||
|
}
|
||||||
@@ -1,24 +1,74 @@
|
|||||||
<?php
|
<?php
|
||||||
require_once dirname(__DIR__, 2) . '/vendor/autoload.php'; // Torna al livello di public per trovare vendor/
|
require_once dirname(__DIR__, 2) . '/vendor/autoload.php';
|
||||||
require_once dirname(__FILE__) . '/class/VisualLimsApiClient.class.php';
|
require_once __DIR__ . '/class/VisualLimsApiClient.class.php';
|
||||||
|
|
||||||
header('Content-Type: application/json');
|
header('Content-Type: application/json');
|
||||||
|
|
||||||
// Disabilita la visualizzazione degli errori PHP per evitare output HTML
|
// Disable PHP error display
|
||||||
ini_set('display_errors', '0');
|
ini_set('display_errors', '0');
|
||||||
error_reporting(E_ALL);
|
error_reporting(E_ALL);
|
||||||
|
|
||||||
try {
|
try {
|
||||||
$api = VisualLimsApiClient::getInstance();
|
$api = VisualLimsApiClient::getInstance();
|
||||||
$data = $api->get("Cliente"); // Recupera i clienti
|
|
||||||
|
|
||||||
// Salva la risposta in un file per debug
|
// Parametri OData
|
||||||
file_put_contents(__DIR__ . '/clienti_response.json', json_encode($data));
|
$params = [
|
||||||
|
'$select' => 'IdCliente,Nominativo,CodiceNazioneFatturazione',
|
||||||
|
'$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");
|
||||||
|
}
|
||||||
|
|
||||||
|
// Esegui la chiamata con retry
|
||||||
|
$data = makeApiRequest($api, $endpoint);
|
||||||
|
|
||||||
echo json_encode($data);
|
echo json_encode($data);
|
||||||
} catch (Exception $e) {
|
} catch (Exception $e) {
|
||||||
http_response_code(500);
|
http_response_code(500);
|
||||||
echo json_encode([
|
$errorResponse = [
|
||||||
'error' => $e->getMessage()
|
'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);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,89 @@
|
|||||||
|
<?php
|
||||||
|
// get_fixed_field_data.php
|
||||||
|
|
||||||
|
require_once dirname(__DIR__, 2) . '/vendor/autoload.php';
|
||||||
|
require_once dirname(__FILE__) . '/class/VisualLimsApiClient.class.php';
|
||||||
|
|
||||||
|
header('Content-Type: application/json');
|
||||||
|
ini_set('display_errors', '0');
|
||||||
|
error_reporting(E_ALL);
|
||||||
|
|
||||||
|
$field = $_GET['field'] ?? ''; // es: MoltiplicatorePrezzo, AnagraficaCertestObject, ...
|
||||||
|
|
||||||
|
if (!$field) {
|
||||||
|
http_response_code(400);
|
||||||
|
echo json_encode(['error' => 'Parametro "field" mancante']);
|
||||||
|
exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
$api = VisualLimsApiClient::getInstance();
|
||||||
|
$base_url = 'https://93.43.5.102/limsapi/api/odata/';
|
||||||
|
|
||||||
|
$data = null;
|
||||||
|
$endpoint = null;
|
||||||
|
$cache_file = null;
|
||||||
|
$options = []; // qui puoi aggiungere filtri/ordering per campo se serve
|
||||||
|
|
||||||
|
try {
|
||||||
|
switch ($field) {
|
||||||
|
case 'MoltiplicatorePrezzo':
|
||||||
|
$endpoint = 'MoltiplicatorePrezzi';
|
||||||
|
$cache_file = __DIR__ . '/cache/moltiplicatori_prezzo.json';
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 'AnagraficaCertestObject':
|
||||||
|
$endpoint = 'AnagraficaCertestObject';
|
||||||
|
$cache_file = __DIR__ . '/cache/anagrafica_certest_object.json';
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 'AnagraficaCertestService':
|
||||||
|
$endpoint = 'AnagraficaCertestService';
|
||||||
|
$cache_file = __DIR__ . '/cache/anagrafica_certest_service.json';
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 'ClienteResponsabile':
|
||||||
|
$id_cliente = (int)($_GET['id_cliente'] ?? 0);
|
||||||
|
if ($id_cliente <= 0) {
|
||||||
|
http_response_code(400);
|
||||||
|
echo json_encode(['error' => 'Manca o invalido id_cliente']);
|
||||||
|
exit;
|
||||||
|
}
|
||||||
|
$endpoint = "Cliente($id_cliente)?\$expand=Responsabili";
|
||||||
|
$cache_file = __DIR__ . '/cache/cliente_responsabili_' . $id_cliente . '.json';
|
||||||
|
break;
|
||||||
|
|
||||||
|
// case 'FuturoCampo5':
|
||||||
|
// $endpoint = 'QualcosaElse';
|
||||||
|
// break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
http_response_code(400);
|
||||||
|
echo json_encode(['error' => "Campo non supportato: $field"]);
|
||||||
|
exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Opzionale: caching semplice (molto utile se i dati cambiano poco)
|
||||||
|
if ($cache_file && file_exists($cache_file) && (time() - filemtime($cache_file) < 3600)) { // 1 ora
|
||||||
|
$data = json_decode(file_get_contents($cache_file), true);
|
||||||
|
} else {
|
||||||
|
$data = $api->get($endpoint, $options);
|
||||||
|
|
||||||
|
if ($cache_file) {
|
||||||
|
file_put_contents($cache_file, json_encode($data, JSON_PRETTY_PRINT));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Log ultimo URL (opzionale, per debug)
|
||||||
|
$full_url = $base_url . $endpoint . ($options ? '?' . http_build_query($options) : '');
|
||||||
|
file_put_contents(__DIR__ . '/last_urls.log', date('c') . " - $field - $full_url\n", FILE_APPEND);
|
||||||
|
|
||||||
|
echo json_encode($data);
|
||||||
|
} catch (Exception $e) {
|
||||||
|
file_put_contents(
|
||||||
|
__DIR__ . '/error_log.txt',
|
||||||
|
date('Y-m-d H:i:s') . " [$field] " . $e->getMessage() . PHP_EOL,
|
||||||
|
FILE_APPEND
|
||||||
|
);
|
||||||
|
http_response_code(500);
|
||||||
|
echo json_encode(['error' => $e->getMessage()]);
|
||||||
|
}
|
||||||
@@ -0,0 +1,59 @@
|
|||||||
|
<?php
|
||||||
|
require_once dirname(__DIR__, 2) . '/vendor/autoload.php'; // Risale a root/vendor/
|
||||||
|
use Dotenv\Dotenv;
|
||||||
|
|
||||||
|
// Set JSON header
|
||||||
|
header('Content-Type: application/json');
|
||||||
|
|
||||||
|
// Debug: Log the path where we expect the .env file
|
||||||
|
$envPath = dirname(__DIR__, 2);
|
||||||
|
file_put_contents(__DIR__ . '/debug_log.txt', date('Y-m-d H:i:s') . ' - Expected .env path: ' . $envPath . PHP_EOL, FILE_APPEND);
|
||||||
|
|
||||||
|
// Carica il file .env dalla root del progetto
|
||||||
|
try {
|
||||||
|
$dotenv = Dotenv::createImmutable($envPath);
|
||||||
|
$dotenv->load();
|
||||||
|
} catch (Exception $e) {
|
||||||
|
file_put_contents(__DIR__ . '/error_log.txt', date('Y-m-d H:i:s') . ' - Errore caricamento .env: ' . $e->getMessage() . PHP_EOL, FILE_APPEND);
|
||||||
|
echo json_encode(['success' => false, 'message' => 'Errore caricamento configurazione: ' . $e->getMessage()]);
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Recupera le variabili d'ambiente
|
||||||
|
$dbHost = $_ENV['DB_HOST'];
|
||||||
|
$dbName = $_ENV['DB_DATABASE'];
|
||||||
|
$dbUser = $_ENV['DB_USERNAME'];
|
||||||
|
$dbPass = $_ENV['DB_PASSWORD'];
|
||||||
|
$dbPrefix = $_ENV['DB_PREFIX'];
|
||||||
|
|
||||||
|
// Debug: Log database connection details (excluding password)
|
||||||
|
file_put_contents(__DIR__ . '/debug_log.txt', date('Y-m-d H:i:s') . " - DB Connection: host=$dbHost, dbname=$dbName, user=$dbUser, prefix=$dbPrefix" . PHP_EOL, FILE_APPEND);
|
||||||
|
|
||||||
|
// Connessione al database MySQL
|
||||||
|
try {
|
||||||
|
$pdo = new PDO("mysql:host=$dbHost;dbname=$dbName;charset=utf8mb4", $dbUser, $dbPass);
|
||||||
|
$pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
|
||||||
|
} catch (PDOException $e) {
|
||||||
|
file_put_contents(__DIR__ . '/error_log.txt', date('Y-m-d H:i:s') . ' - Errore connessione DB: ' . $e->getMessage() . PHP_EOL, FILE_APPEND);
|
||||||
|
echo json_encode(['success' => false, 'message' => 'Errore connessione al database: ' . $e->getMessage()]);
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
// Query per recuperare i valori distinti di MacroMatrice, escludendo quelli che iniziano con '*' e ordinandoli
|
||||||
|
$query = "SELECT DISTINCT MacroMatrice FROM {$dbPrefix}matrici WHERE MacroMatrice IS NOT NULL AND MacroMatrice NOT LIKE '*%' ORDER BY MacroMatrice ASC";
|
||||||
|
$stmt = $pdo->prepare($query);
|
||||||
|
$stmt->execute();
|
||||||
|
$macroMatrici = $stmt->fetchAll(PDO::FETCH_COLUMN);
|
||||||
|
|
||||||
|
// Debug: Log del numero di MacroMatrice recuperate
|
||||||
|
file_put_contents(__DIR__ . '/debug_log.txt', date('Y-m-d H:i:s') . ' - Retrieved ' . count($macroMatrici) . ' MacroMatrice from database' . PHP_EOL, FILE_APPEND);
|
||||||
|
|
||||||
|
// Restituisci risposta JSON
|
||||||
|
echo json_encode(['success' => true, 'value' => $macroMatrici]);
|
||||||
|
} catch (PDOException $e) {
|
||||||
|
// Log errore e restituisci risposta di errore
|
||||||
|
file_put_contents(__DIR__ . '/error_log.txt', date('Y-m-d H:i:s') . ' - Errore nel recupero delle MacroMatrice: ' . $e->getMessage() . PHP_EOL, FILE_APPEND);
|
||||||
|
echo json_encode(['success' => false, 'message' => 'Errore nel recupero delle MacroMatrice: ' . $e->getMessage()]);
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
@@ -0,0 +1,59 @@
|
|||||||
|
<?php
|
||||||
|
require_once dirname(__DIR__, 2) . '/vendor/autoload.php'; // Risale a root/vendor/
|
||||||
|
use Dotenv\Dotenv;
|
||||||
|
|
||||||
|
// Set JSON header
|
||||||
|
header('Content-Type: application/json');
|
||||||
|
|
||||||
|
// Debug: Log the path where we expect the .env file
|
||||||
|
$envPath = dirname(__DIR__, 2);
|
||||||
|
file_put_contents(__DIR__ . '/debug_log.txt', date('Y-m-d H:i:s') . ' - Expected .env path: ' . $envPath . PHP_EOL, FILE_APPEND);
|
||||||
|
|
||||||
|
// Carica il file .env dalla root del progetto
|
||||||
|
try {
|
||||||
|
$dotenv = Dotenv::createImmutable($envPath);
|
||||||
|
$dotenv->load();
|
||||||
|
} catch (Exception $e) {
|
||||||
|
file_put_contents(__DIR__ . '/error_log.txt', date('Y-m-d H:i:s') . ' - Errore caricamento .env: ' . $e->getMessage() . PHP_EOL, FILE_APPEND);
|
||||||
|
echo json_encode(['success' => false, 'message' => 'Errore caricamento configurazione: ' . $e->getMessage()]);
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Recupera le variabili d'ambiente
|
||||||
|
$dbHost = $_ENV['DB_HOST'];
|
||||||
|
$dbName = $_ENV['DB_DATABASE'];
|
||||||
|
$dbUser = $_ENV['DB_USERNAME'];
|
||||||
|
$dbPass = $_ENV['DB_PASSWORD'];
|
||||||
|
$dbPrefix = $_ENV['DB_PREFIX'];
|
||||||
|
|
||||||
|
// Debug: Log database connection details (excluding password)
|
||||||
|
file_put_contents(__DIR__ . '/debug_log.txt', date('Y-m-d H:i:s') . " - DB Connection: host=$dbHost, dbname=$dbName, user=$dbUser, prefix=$dbPrefix" . PHP_EOL, FILE_APPEND);
|
||||||
|
|
||||||
|
// Connessione al database MySQL
|
||||||
|
try {
|
||||||
|
$pdo = new PDO("mysql:host=$dbHost;dbname=$dbName;charset=utf8mb4", $dbUser, $dbPass);
|
||||||
|
$pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
|
||||||
|
} catch (PDOException $e) {
|
||||||
|
file_put_contents(__DIR__ . '/error_log.txt', date('Y-m-d H:i:s') . ' - Errore connessione DB: ' . $e->getMessage() . PHP_EOL, FILE_APPEND);
|
||||||
|
echo json_encode(['success' => false, 'message' => 'Errore connessione al database: ' . $e->getMessage()]);
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
// Query per recuperare le matrici, includendo MacroMatrice, escludendo quelle che iniziano con '*' e ordinandole
|
||||||
|
$query = "SELECT IdMatrice, NomeMatrice, MacroMatrice FROM {$dbPrefix}matrici WHERE NomeMatrice NOT LIKE '*%' ORDER BY NomeMatrice ASC";
|
||||||
|
$stmt = $pdo->prepare($query);
|
||||||
|
$stmt->execute();
|
||||||
|
$matrici = $stmt->fetchAll(PDO::FETCH_ASSOC);
|
||||||
|
|
||||||
|
// Debug: Log del numero di matrici recuperate
|
||||||
|
file_put_contents(__DIR__ . '/debug_log.txt', date('Y-m-d H:i:s') . ' - Retrieved ' . count($matrici) . ' matrices from database' . PHP_EOL, FILE_APPEND);
|
||||||
|
|
||||||
|
// Restituisci risposta JSON
|
||||||
|
echo json_encode(['success' => true, 'value' => $matrici]);
|
||||||
|
} catch (PDOException $e) {
|
||||||
|
// Log errore e restituisci risposta di errore
|
||||||
|
file_put_contents(__DIR__ . '/error_log.txt', date('Y-m-d H:i:s') . ' - Errore nel recupero delle matrici: ' . $e->getMessage() . PHP_EOL, FILE_APPEND);
|
||||||
|
echo json_encode(['success' => false, 'message' => 'Errore nel recupero delle matrici: ' . $e->getMessage()]);
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
@@ -0,0 +1,41 @@
|
|||||||
|
<?php
|
||||||
|
require_once dirname(__DIR__, 2) . '/vendor/autoload.php';
|
||||||
|
require_once dirname(__FILE__) . '/class/VisualLimsApiClient.class.php';
|
||||||
|
|
||||||
|
header('Content-Type: application/json');
|
||||||
|
|
||||||
|
ini_set('display_errors', '0');
|
||||||
|
error_reporting(E_ALL);
|
||||||
|
|
||||||
|
try {
|
||||||
|
$api = VisualLimsApiClient::getInstance();
|
||||||
|
|
||||||
|
// Endpoint per recuperare i Moltiplicatori Prezzo
|
||||||
|
// (dal documento: GET api/odata/MoltiplicatorePrezzi)
|
||||||
|
$endpoint = 'MoltiplicatorePrezzi';
|
||||||
|
|
||||||
|
// Opzionale: parametri OData ($top, $filter, $orderby, ecc.)
|
||||||
|
$options = []; // es: ['$top' => 100]
|
||||||
|
|
||||||
|
// Debug: salva URL usata
|
||||||
|
$base_url = 'https://93.43.5.102/limsapi/api/odata/';
|
||||||
|
$query = http_build_query($options);
|
||||||
|
$full_url = $base_url . $endpoint . ($query ? '?' . $query : '');
|
||||||
|
file_put_contents(__DIR__ . '/last_url.txt', $full_url . PHP_EOL, FILE_APPEND);
|
||||||
|
|
||||||
|
// Chiamata API
|
||||||
|
$data = $api->get($endpoint, $options);
|
||||||
|
|
||||||
|
// Salva il JSON in locale
|
||||||
|
file_put_contents(__DIR__ . '/moltiplicatori_prezzo_response.json', json_encode($data, JSON_PRETTY_PRINT));
|
||||||
|
|
||||||
|
echo json_encode($data);
|
||||||
|
} catch (Exception $e) {
|
||||||
|
file_put_contents(
|
||||||
|
__DIR__ . '/error_log.txt',
|
||||||
|
date('Y-m-d H:i:s') . ' - ' . $e->getMessage() . PHP_EOL,
|
||||||
|
FILE_APPEND
|
||||||
|
);
|
||||||
|
http_response_code(500);
|
||||||
|
echo json_encode(['error' => $e->getMessage()]);
|
||||||
|
}
|
||||||
+1206
-100
File diff suppressed because it is too large
Load Diff
@@ -75,6 +75,12 @@ foreach ($selected_rows as $rowIndex) {
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Recupera l'idclient di default dal template
|
||||||
|
$template_stmt = $pdo->prepare("SELECT idclient FROM excel_templates WHERE id = ?");
|
||||||
|
$template_stmt->execute([$template_id]);
|
||||||
|
$template = $template_stmt->fetch(PDO::FETCH_ASSOC);
|
||||||
|
$default_idclient = $template['idclient'] ?? null;
|
||||||
|
|
||||||
$values = [
|
$values = [
|
||||||
$template_id,
|
$template_id,
|
||||||
$importReferenceCode,
|
$importReferenceCode,
|
||||||
@@ -83,9 +89,10 @@ foreach ($selected_rows as $rowIndex) {
|
|||||||
$user_id,
|
$user_id,
|
||||||
null,
|
null,
|
||||||
date('Y-m-d'),
|
date('Y-m-d'),
|
||||||
$excelrow // Aggiunto excelrow per la colonna excelrow
|
$excelrow,
|
||||||
|
$default_idclient // Aggiungi idclient
|
||||||
];
|
];
|
||||||
$sql = "INSERT INTO datadb (templateid, importreferencecode, filename_import, status, user_id, limscode, importdate, excelrow) VALUES (?, ?, ?, ?, ?, ?, ?, ?)";
|
$sql = "INSERT INTO datadb (templateid, importreferencecode, filename_import, status, user_id, limscode, importdate, excelrow, idclient) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)";
|
||||||
$stmt = $pdo->prepare($sql);
|
$stmt = $pdo->prepare($sql);
|
||||||
$stmt->execute($values);
|
$stmt->execute($values);
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,25 @@
|
|||||||
|
<?php
|
||||||
|
require_once(__DIR__ . '/../class/db-functions.php');
|
||||||
|
|
||||||
|
$db = DBHandlerSelect::getInstance()->getConnection();
|
||||||
|
|
||||||
|
ini_set('display_errors', 1);
|
||||||
|
ini_set('display_startup_errors', 1);
|
||||||
|
error_reporting(E_ALL | E_STRICT);
|
||||||
|
|
||||||
|
// Inizializza la sessione
|
||||||
|
if (session_status() == PHP_SESSION_NONE) {
|
||||||
|
session_start();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Imposta variabili di sessione di default per evitare errori
|
||||||
|
$_SESSION['iduserlogin'] = '1'; // Nessun utente loggato
|
||||||
|
$_SESSION['nameuser'] = 'Ospite';
|
||||||
|
$_SESSION['surnameuser'] = '';
|
||||||
|
$_SESSION['emailuser'] = '';
|
||||||
|
$_SESSION['photouser'] = '';
|
||||||
|
$photouser = $_SESSION['photouser'];
|
||||||
|
$photousername = '';
|
||||||
|
$iduserlogin = $_SESSION['iduserlogin'];
|
||||||
|
// Include file di lingua, se necessario
|
||||||
|
require_once(__DIR__ . '/../../languages/en/general.php');
|
||||||
@@ -14,9 +14,9 @@ if (!$iddatadb) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
$stmt = $pdo->prepare("SELECT id, iddatadb, part_number, part_description FROM identification_parts WHERE iddatadb = :iddatadb ORDER BY part_number ASC");
|
$stmt = $pdo->prepare("SELECT id, iddatadb, part_number, part_description, idmatrice, note, dateexpiry FROM identification_parts WHERE iddatadb = :iddatadb ORDER BY part_number ASC");
|
||||||
$stmt->execute([':iddatadb' => $iddatadb]);
|
$stmt->execute([':iddatadb' => $iddatadb]);
|
||||||
$parts = $stmt->fetchAll();
|
$parts = $stmt->fetchAll(PDO::FETCH_ASSOC);
|
||||||
|
|
||||||
echo json_encode(['success' => true, 'parts' => $parts]);
|
echo json_encode(['success' => true, 'parts' => $parts]);
|
||||||
} catch (PDOException $e) {
|
} catch (PDOException $e) {
|
||||||
|
|||||||
@@ -0,0 +1,32 @@
|
|||||||
|
<?php
|
||||||
|
header('Content-Type: application/json');
|
||||||
|
|
||||||
|
include('include/headscript.php');
|
||||||
|
|
||||||
|
$dbHandler = DBHandlerSelect::getInstance();
|
||||||
|
$pdo = $dbHandler->getConnection();
|
||||||
|
|
||||||
|
// Recupera l'ID dell'utente loggato
|
||||||
|
$user_id = $iduserlogin ?? 1;
|
||||||
|
|
||||||
|
if (!$user_id) {
|
||||||
|
echo json_encode(['success' => false, 'message' => "ID dell'utente autenticato mancante"]);
|
||||||
|
exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
$stmt = $pdo->prepare(
|
||||||
|
"SELECT DISTINCT q.*
|
||||||
|
FROM quotations q
|
||||||
|
INNER JOIN identification_parts ip
|
||||||
|
ON ip.idquotations = q.id
|
||||||
|
AND ip.iddatadb IS NULL
|
||||||
|
WHERE q.iduser = :iduser"
|
||||||
|
);
|
||||||
|
$stmt->execute([':iduser' => $user_id]);
|
||||||
|
$quotations = $stmt->fetchAll(PDO::FETCH_ASSOC);
|
||||||
|
|
||||||
|
echo json_encode(['success' => true, 'quotations' => $quotations]);
|
||||||
|
} catch (PDOException $e) {
|
||||||
|
echo json_encode(['success' => false, 'message' => 'Errore nel caricamento delle quotations: ' . $e->getMessage()]);
|
||||||
|
}
|
||||||
@@ -97,6 +97,31 @@ if (!$template) {
|
|||||||
<ul id="associationsList" class="list-group border p-3"></ul>
|
<ul id="associationsList" class="list-group border p-3"></ul>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<!-- Default Values (Fixed fields) -->
|
||||||
|
<div class="mt-4">
|
||||||
|
<h5>Default Values (Fixed fields)</h5>
|
||||||
|
|
||||||
|
<div class="row g-3">
|
||||||
|
<div class="col-md-6">
|
||||||
|
<label class="form-label">MoltiplicatorePrezzo (default)</label>
|
||||||
|
<select id="defaultMoltiplicatorePrezzo" class="form-select">
|
||||||
|
<option value="">Loading...</option>
|
||||||
|
</select>
|
||||||
|
<small class="text-muted">Loaded from API: get_moltiplicatoreprezzo.php</small>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="col-md-6">
|
||||||
|
<label class="form-label">ConsegnaRichiesta (default)</label>
|
||||||
|
<input type="date" id="defaultConsegnaRichiesta" class="form-control">
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="mt-3">
|
||||||
|
<button class="btn btn-success" id="saveDefaultsBtn">💾 Save defaults</button>
|
||||||
|
<span id="defaultsStatus" class="ms-2 text-muted"></span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
<!-- Save Button -->
|
<!-- Save Button -->
|
||||||
<div class="mt-4 text-end">
|
<div class="mt-4 text-end">
|
||||||
<a href="templates_dashboard.php" class="btn btn-primary">⬅ Back to Template Dashboard</a>
|
<a href="templates_dashboard.php" class="btn btn-primary">⬅ Back to Template Dashboard</a>
|
||||||
@@ -273,7 +298,7 @@ if (!$template) {
|
|||||||
}
|
}
|
||||||
})
|
})
|
||||||
.catch(error => console.error('Error loading table columns:', error));
|
.catch(error => console.error('Error loading table columns:', error));
|
||||||
*/
|
*/
|
||||||
/** =======================
|
/** =======================
|
||||||
* CARICAMENTO MAPPATURE GIÀ ESISTENTI
|
* CARICAMENTO MAPPATURE GIÀ ESISTENTI
|
||||||
* ======================= */
|
* ======================= */
|
||||||
@@ -441,6 +466,97 @@ if (!$template) {
|
|||||||
})
|
})
|
||||||
.catch(error => console.error("❌ Fetch error:", error));
|
.catch(error => console.error("❌ Fetch error:", error));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** =======================
|
||||||
|
* DEFAULTS (Fixed fields)
|
||||||
|
* - stored in localStorage for now
|
||||||
|
* ======================= */
|
||||||
|
|
||||||
|
const defaultsKey = "tpl_defaults_" + templateId;
|
||||||
|
|
||||||
|
function loadDefaultsFromLocal() {
|
||||||
|
try {
|
||||||
|
const raw = localStorage.getItem(defaultsKey);
|
||||||
|
if (!raw) return;
|
||||||
|
const saved = JSON.parse(raw);
|
||||||
|
|
||||||
|
if (saved && typeof saved === "object") {
|
||||||
|
if (saved.moltiplicatore_prezzo_id !== undefined) {
|
||||||
|
document.getElementById('defaultMoltiplicatorePrezzo').value = saved.moltiplicatore_prezzo_id || "";
|
||||||
|
}
|
||||||
|
if (saved.consegna_richiesta !== undefined) {
|
||||||
|
document.getElementById('defaultConsegnaRichiesta').value = saved.consegna_richiesta || "";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (e) {
|
||||||
|
console.warn("⚠️ Cannot parse saved defaults:", e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function saveDefaultsToLocal() {
|
||||||
|
const moltiplicatoreId = document.getElementById('defaultMoltiplicatorePrezzo').value || "";
|
||||||
|
const consegna = document.getElementById('defaultConsegnaRichiesta').value || "";
|
||||||
|
|
||||||
|
const payload = {
|
||||||
|
moltiplicatore_prezzo_id: moltiplicatoreId,
|
||||||
|
consegna_richiesta: consegna
|
||||||
|
};
|
||||||
|
|
||||||
|
localStorage.setItem(defaultsKey, JSON.stringify(payload));
|
||||||
|
|
||||||
|
const status = document.getElementById('defaultsStatus');
|
||||||
|
status.textContent = "✅ Defaults saved for this template (local)";
|
||||||
|
setTimeout(() => status.textContent = "", 2500);
|
||||||
|
}
|
||||||
|
|
||||||
|
function loadMoltiplicatoriPrezzo() {
|
||||||
|
const select = document.getElementById('defaultMoltiplicatorePrezzo');
|
||||||
|
select.innerHTML = `<option value="">-- Select --</option>`;
|
||||||
|
|
||||||
|
fetch('get_moltiplicatoreprezzo.php')
|
||||||
|
.then(r => r.json())
|
||||||
|
.then(data => {
|
||||||
|
// OData tipico: { value: [...] }
|
||||||
|
const rows = Array.isArray(data?.value) ? data.value : (Array.isArray(data) ? data : []);
|
||||||
|
if (!rows.length) {
|
||||||
|
select.innerHTML = `<option value="">(No data)</option>`;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
rows.forEach(item => {
|
||||||
|
// ⚠️ Qui i nomi campi dipendono dal JSON reale:
|
||||||
|
// - id: item.Id / item.ID / item.idMoltiplicatorePrezzo
|
||||||
|
// - label: item.Descrizione / item.Nome / item.Codice
|
||||||
|
const id = item.IdMoltiplicatorePrezzo;
|
||||||
|
const label = `${item.Descrizione} (x${item.Fattore})`;
|
||||||
|
|
||||||
|
|
||||||
|
if (id === undefined || id === null) return;
|
||||||
|
|
||||||
|
const opt = document.createElement('option');
|
||||||
|
opt.value = String(id);
|
||||||
|
opt.textContent = String(label);
|
||||||
|
select.appendChild(opt);
|
||||||
|
});
|
||||||
|
|
||||||
|
// dopo aver popolato, prova a rimettere il valore salvato
|
||||||
|
loadDefaultsFromLocal();
|
||||||
|
})
|
||||||
|
.catch(err => {
|
||||||
|
console.error("❌ Error loading MoltiplicatorePrezzo:", err);
|
||||||
|
select.innerHTML = `<option value="">(Load error)</option>`;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// Events
|
||||||
|
document.getElementById('saveDefaultsBtn').addEventListener('click', function() {
|
||||||
|
saveDefaultsToLocal();
|
||||||
|
});
|
||||||
|
|
||||||
|
// Init
|
||||||
|
loadMoltiplicatoriPrezzo();
|
||||||
|
loadDefaultsFromLocal();
|
||||||
|
|
||||||
});
|
});
|
||||||
</script>
|
</script>
|
||||||
</body>
|
</body>
|
||||||
|
|||||||
@@ -25,6 +25,18 @@ $stmt = $pdo->prepare("SELECT id, field_id, excel_column, is_manual, manual_defa
|
|||||||
$stmt->execute([$id]);
|
$stmt->execute([$id]);
|
||||||
$mappings = $stmt->fetchAll(PDO::FETCH_ASSOC);
|
$mappings = $stmt->fetchAll(PDO::FETCH_ASSOC);
|
||||||
|
|
||||||
|
// Recupera i fixed fields dalla tabella template_fixed_mapping
|
||||||
|
$stmt = $pdo->prepare("
|
||||||
|
SELECT id, fixed_field_key, is_manual, data_type, is_required, default_value, is_visible_import
|
||||||
|
FROM template_fixed_mapping
|
||||||
|
WHERE template_id = ?
|
||||||
|
ORDER BY id ASC
|
||||||
|
");
|
||||||
|
$stmt->execute([$id]);
|
||||||
|
$fixedMappings = $stmt->fetchAll(PDO::FETCH_ASSOC);
|
||||||
|
|
||||||
|
$hasFixedMappings = !empty($fixedMappings);
|
||||||
|
|
||||||
// Recupera le colonne già associate nel database
|
// Recupera le colonne già associate nel database
|
||||||
$usedColumnsFromDB = array_filter(array_column($mappings, 'excel_column'));
|
$usedColumnsFromDB = array_filter(array_column($mappings, 'excel_column'));
|
||||||
|
|
||||||
@@ -42,6 +54,8 @@ $xlsHeaders = $template['xls_headers'] ? json_decode($template['xls_headers'], t
|
|||||||
<?php include('cssinclude.php'); ?>
|
<?php include('cssinclude.php'); ?>
|
||||||
<title>Configure Template <?= htmlspecialchars($template['name'], ENT_QUOTES, 'UTF-8'); ?></title>
|
<title>Configure Template <?= htmlspecialchars($template['name'], ENT_QUOTES, 'UTF-8'); ?></title>
|
||||||
<script src="https://cdnjs.cloudflare.com/ajax/libs/xlsx/0.18.5/xlsx.full.min.js"></script>
|
<script src="https://cdnjs.cloudflare.com/ajax/libs/xlsx/0.18.5/xlsx.full.min.js"></script>
|
||||||
|
<link href="https://cdn.jsdelivr.net/npm/select2@4.1.0-rc.0/dist/css/select2.min.css" rel="stylesheet" />
|
||||||
|
|
||||||
<style>
|
<style>
|
||||||
.dropdown-select {
|
.dropdown-select {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
@@ -89,6 +103,11 @@ $xlsHeaders = $template['xls_headers'] ? json_decode($template['xls_headers'], t
|
|||||||
width: 100px;
|
width: 100px;
|
||||||
text-align: center;
|
text-align: center;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* fix bootstrap/select2 width */
|
||||||
|
.select2-container {
|
||||||
|
width: 100% !important;
|
||||||
|
}
|
||||||
</style>
|
</style>
|
||||||
</head>
|
</head>
|
||||||
|
|
||||||
@@ -138,67 +157,215 @@ $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>Main</th>
|
<th style="width:100px; text-align:center;">Main</th>
|
||||||
<th>Visible on Import</th>
|
<th style="width:120px; text-align:center;">Visible on Import</th>
|
||||||
<th>Title</th>
|
<th>Title</th>
|
||||||
<th>Type</th>
|
<th style="width:140px;">Type</th>
|
||||||
<th>Mapping</th>
|
<th>Mapping</th>
|
||||||
<th>Default Value</th>
|
<th>Default Value</th>
|
||||||
</tr>
|
</tr>
|
||||||
</thead>
|
</thead>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
<tbody id="schemaFieldsBody">
|
<tbody id="schemaFieldsBody">
|
||||||
<?php foreach ($mappings as $mapping): ?>
|
<?php foreach ($mappings as $mapping): ?>
|
||||||
<tr>
|
<tr>
|
||||||
<td>
|
<td class="text-center">
|
||||||
<input type="checkbox" class="main-field-checkbox" data-mapping-id="<?php echo $mapping['id']; ?>" <?php echo $mapping['main_field'] == 1 ? 'checked' : ''; ?>>
|
<input type="checkbox"
|
||||||
|
class="main-field-checkbox"
|
||||||
|
data-mapping-id="<?php echo (int)$mapping['id']; ?>"
|
||||||
|
<?php echo ((int)$mapping['main_field'] === 1) ? 'checked' : ''; ?>>
|
||||||
</td>
|
</td>
|
||||||
<td>
|
|
||||||
<input type="checkbox" class="visible-import-checkbox" data-mapping-id="<?php echo $mapping['id']; ?>" <?php echo (isset($mapping['is_visible_import']) ? $mapping['is_visible_import'] : 1) == 1 ? 'checked' : ''; ?>>
|
<td class="text-center">
|
||||||
|
<input type="checkbox"
|
||||||
|
class="visible-import-checkbox"
|
||||||
|
data-mapping-id="<?php echo (int)$mapping['id']; ?>"
|
||||||
|
<?php echo ((int)($mapping['is_visible_import'] ?? 1) === 1) ? 'checked' : ''; ?>>
|
||||||
</td>
|
</td>
|
||||||
|
|
||||||
<td>
|
<td>
|
||||||
<?php echo htmlspecialchars($mapping['field_label'] ?? 'N/A'); ?>
|
<?php echo htmlspecialchars($mapping['field_label'] ?? 'N/A'); ?>
|
||||||
<?php if ($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');
|
||||||
$mappingValue = $isSceltaMultipla ? 'manual' : ($mapping['excel_column'] ? 'xls' : ($mapping['is_manual'] ? 'manual' : ''));
|
$mappingValue = $isSceltaMultipla
|
||||||
|
? 'manual'
|
||||||
|
: ($mapping['excel_column'] ? 'xls' : ((int)$mapping['is_manual'] === 1 ? 'manual' : ''));
|
||||||
?>
|
?>
|
||||||
<select class="form-select mapping-select" data-id="<?php echo $mapping['id']; ?>" data-field-id="<?php echo $mapping['field_id']; ?>" <?php echo $isSceltaMultipla ? 'disabled' : ''; ?>>
|
|
||||||
|
<select class="form-select mapping-select"
|
||||||
|
data-id="<?php echo (int)$mapping['id']; ?>"
|
||||||
|
data-field-id="<?php echo (int)$mapping['field_id']; ?>"
|
||||||
|
<?php echo $isSceltaMultipla ? 'disabled' : ''; ?>>
|
||||||
|
|
||||||
<?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>
|
||||||
<?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>
|
||||||
</select>
|
</select>
|
||||||
<select class="form-select xls-columns" style="display:<?php echo $mappingValue === 'xls' ? 'block' : 'none'; ?>" data-id="<?php echo $mapping['id']; ?>" <?php echo $mapping['excel_column'] ? 'data-current-xls="' . htmlspecialchars($mapping['excel_column']) . '"' : ''; ?>></select>
|
|
||||||
<?php if ($mapping['excel_column']): ?>
|
<select class="form-select xls-columns"
|
||||||
<span class="mapped-column" style="margin-left: 5px;"><?php echo htmlspecialchars($mapping['excel_column']); ?></span>
|
style="display:<?php echo ($mappingValue === 'xls') ? 'block' : 'none'; ?>"
|
||||||
<button class="btn btn-danger btn-sm remove-xls" data-id="<?php echo $mapping['id']; ?>" style="margin-left: 5px;">X</button>
|
data-id="<?php echo (int)$mapping['id']; ?>"
|
||||||
|
<?php echo $mapping['excel_column'] ? 'data-current-xls="' . htmlspecialchars($mapping['excel_column']) . '"' : ''; ?>>
|
||||||
|
</select>
|
||||||
|
|
||||||
|
<?php if (!empty($mapping['excel_column'])): ?>
|
||||||
|
<span class="mapped-column" style="margin-left:5px;">
|
||||||
|
(<?php echo htmlspecialchars($mapping['excel_column']); ?>)
|
||||||
|
</span>
|
||||||
|
<button class="btn btn-danger btn-sm remove-xls"
|
||||||
|
data-id="<?php echo (int)$mapping['id']; ?>"
|
||||||
|
style="margin-left:5px;">
|
||||||
|
X
|
||||||
|
</button>
|
||||||
<?php endif; ?>
|
<?php endif; ?>
|
||||||
</td>
|
</td>
|
||||||
|
|
||||||
<td>
|
<td>
|
||||||
<?php if ($mapping['data_type'] === 'SceltaMultipla'): ?>
|
<?php if ($mapping['data_type'] === 'SceltaMultipla'): ?>
|
||||||
<select class="form-select dropdown-select manual-default" data-id="<?php echo $mapping['id']; ?>" data-field-id="<?php echo $mapping['field_id']; ?>" data-manual-default="<?php echo htmlspecialchars($mapping['manual_default'] ?? ''); ?>" style="display:block;">
|
<select class="form-select dropdown-select manual-default"
|
||||||
|
data-id="<?php echo (int)$mapping['id']; ?>"
|
||||||
|
data-field-id="<?php echo (int)$mapping['field_id']; ?>"
|
||||||
|
data-manual-default="<?php echo htmlspecialchars($mapping['manual_default'] ?? ''); ?>"
|
||||||
|
style="display:block;">
|
||||||
<option value="">Seleziona...</option>
|
<option value="">Seleziona...</option>
|
||||||
</select>
|
</select>
|
||||||
<?php else: ?>
|
<?php else: ?>
|
||||||
<input type="text" class="form-control manual-default" placeholder="Default value" value="<?php echo htmlspecialchars($mapping['manual_default'] ?? ''); ?>" style="display:<?php echo $mapping['is_manual'] ? 'block' : 'none'; ?>" data-field-id="<?php echo $mapping['field_id']; ?>">
|
<input type="text"
|
||||||
|
class="form-control manual-default"
|
||||||
|
placeholder="Default value"
|
||||||
|
value="<?php echo htmlspecialchars($mapping['manual_default'] ?? ''); ?>"
|
||||||
|
style="display:<?php echo ((int)$mapping['is_manual'] === 1) ? 'block' : 'none'; ?>"
|
||||||
|
data-field-id="<?php echo (int)$mapping['field_id']; ?>">
|
||||||
<?php endif; ?>
|
<?php endif; ?>
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
<?php endforeach; ?>
|
<?php endforeach; ?>
|
||||||
</tbody>
|
</tbody>
|
||||||
|
|
||||||
</table>
|
</table>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<!-- Defaults for fixed fields (not part of XLS mapping) -->
|
||||||
|
<!-- Fixed fields defaults (saved in DB, autosave) -->
|
||||||
|
<div class="row mt-4">
|
||||||
|
<div class="col-12 d-flex align-items-center justify-content-between">
|
||||||
|
<h5 class="mb-0">Fixed Fields Defaults</h5>
|
||||||
|
|
||||||
|
<?php if (!$hasFixedMappings): ?>
|
||||||
|
<button id="btnAddFixedFields" class="btn btn-outline-primary">
|
||||||
|
Add Fixed Fields
|
||||||
|
</button>
|
||||||
|
<?php endif; ?>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="col-12">
|
||||||
|
<small class="text-muted">Defaults are saved automatically when changed</small>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="row mt-3" id="fixedFieldsSection" style="display: <?php echo $hasFixedMappings ? 'block' : 'none'; ?>;">
|
||||||
|
<div class="col-12">
|
||||||
|
<table class="table table-striped" id="fixedFieldsTable">
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
<th style="width:120px; text-align:center;">Visible on Import</th>
|
||||||
|
<th style="width:120px; text-align:center;">Required</th>
|
||||||
|
<th>Field</th>
|
||||||
|
<th style="width:140px;">Type</th>
|
||||||
|
<th>Default Value</th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
|
||||||
|
<tbody>
|
||||||
|
<?php foreach ($fixedMappings as $fm): ?>
|
||||||
|
<tr>
|
||||||
|
|
||||||
|
<td class="text-center">
|
||||||
|
<input type="checkbox"
|
||||||
|
class="fixed-visible-checkbox"
|
||||||
|
data-fixed-id="<?php echo (int)$fm['id']; ?>"
|
||||||
|
<?php echo ((int)$fm['is_visible_import'] === 1) ? 'checked' : ''; ?>>
|
||||||
|
</td>
|
||||||
|
|
||||||
|
<td class="text-center">
|
||||||
|
<input type="checkbox"
|
||||||
|
class="fixed-required-checkbox"
|
||||||
|
data-fixed-id="<?php echo (int)$fm['id']; ?>"
|
||||||
|
<?php echo ((int)$fm['is_required'] === 1) ? 'checked' : ''; ?>>
|
||||||
|
</td>
|
||||||
|
|
||||||
|
<td>
|
||||||
|
<strong><?php echo htmlspecialchars($fm['fixed_field_key']); ?></strong>
|
||||||
|
</td>
|
||||||
|
|
||||||
|
|
||||||
|
<td><?php echo htmlspecialchars($fm['data_type']); ?></td>
|
||||||
|
|
||||||
|
<td>
|
||||||
|
|
||||||
|
<?php if ($fm['data_type'] === 'DATE'): ?>
|
||||||
|
|
||||||
|
<input type="date"
|
||||||
|
class="form-control fixed-default-input"
|
||||||
|
data-fixed-id="<?php echo (int)$fm['id']; ?>"
|
||||||
|
value="<?php echo htmlspecialchars($fm['default_value'] ?? ''); ?>">
|
||||||
|
|
||||||
|
<?php elseif (in_array($fm['fixed_field_key'], [
|
||||||
|
'ClienteResponsabile',
|
||||||
|
'MoltiplicatorePrezzo',
|
||||||
|
'AnagraficaCertestObject',
|
||||||
|
'AnagraficaCertestService'
|
||||||
|
])): ?>
|
||||||
|
|
||||||
|
<select class="form-select fixed-default-select"
|
||||||
|
data-fixed-id="<?php echo (int)$fm['id']; ?>"
|
||||||
|
data-fixed-key="<?php echo htmlspecialchars($fm['fixed_field_key']); ?>"
|
||||||
|
data-current-value="<?php echo htmlspecialchars($fm['default_value'] ?? ''); ?>">
|
||||||
|
<option value="">Loading...</option>
|
||||||
|
</select>
|
||||||
|
|
||||||
|
<?php else: ?>
|
||||||
|
|
||||||
|
<input type="text"
|
||||||
|
class="form-control fixed-default-input"
|
||||||
|
data-fixed-id="<?php echo (int)$fm['id']; ?>"
|
||||||
|
value="<?php echo htmlspecialchars($fm['default_value'] ?? ''); ?>">
|
||||||
|
|
||||||
|
<?php endif; ?>
|
||||||
|
|
||||||
|
</td>
|
||||||
|
|
||||||
|
</tr>
|
||||||
|
<?php endforeach; ?>
|
||||||
|
</tbody>
|
||||||
|
|
||||||
|
</table>
|
||||||
|
|
||||||
|
<div id="fixedSaveStatus" class="small mt-2 text-muted"></div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
<div class="mt-4 text-end">
|
<div class="mt-4 text-end">
|
||||||
<a href="templates_dashboard.php" class="btn btn-primary">⬅ Back to Template Dashboard</a>
|
<a href="templates_dashboard.php" class="btn btn-primary">⬅ Back to Template Dashboard</a>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -209,7 +376,7 @@ $xlsHeaders = $template['xls_headers'] ? json_decode($template['xls_headers'], t
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<?php include('jsinclude.php'); ?>
|
<?php include('jsinclude.php'); ?>
|
||||||
<script src="https://code.jquery.com/jquery-3.6.0.min.js"></script>
|
<script src="https://cdn.jsdelivr.net/npm/select2@4.1.0-rc.0/dist/js/select2.min.js"></script>
|
||||||
<script>
|
<script>
|
||||||
let availableXlsColumns = <?php echo json_encode($xlsHeaders); ?> || [];
|
let availableXlsColumns = <?php echo json_encode($xlsHeaders); ?> || [];
|
||||||
let usedColumnsFromDB = <?php echo json_encode($usedColumnsFromDB); ?> || [];
|
let usedColumnsFromDB = <?php echo json_encode($usedColumnsFromDB); ?> || [];
|
||||||
@@ -316,6 +483,80 @@ $xlsHeaders = $template['xls_headers'] ? json_decode($template['xls_headers'], t
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function initSelect2ForSchemaDropdowns() {
|
||||||
|
// dropdown-select = SceltaMultipla (manual default)
|
||||||
|
if (window.jQuery && $.fn.select2) {
|
||||||
|
$('.dropdown-select').each(function() {
|
||||||
|
if ($(this).hasClass('select2-hidden-accessible')) return; // già inizializzato
|
||||||
|
$(this).select2({
|
||||||
|
width: '100%',
|
||||||
|
placeholder: 'Seleziona...',
|
||||||
|
allowClear: true
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function initSelect2ForFixedDropdowns() {
|
||||||
|
if (!(window.jQuery && $.fn.select2)) return;
|
||||||
|
|
||||||
|
$('.fixed-default-select').each(function() {
|
||||||
|
const $el = $(this);
|
||||||
|
|
||||||
|
// se era già Select2, lo resettiamo (utile quando renderizzi righe nuove)
|
||||||
|
if ($el.hasClass('select2-hidden-accessible')) {
|
||||||
|
$el.select2('destroy');
|
||||||
|
}
|
||||||
|
|
||||||
|
$el.select2({
|
||||||
|
width: '100%',
|
||||||
|
placeholder: 'Seleziona...',
|
||||||
|
allowClear: true
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function saveFixedDefaultSelect(selectEl) {
|
||||||
|
const id = selectEl.dataset.fixedId;
|
||||||
|
const value = selectEl.value;
|
||||||
|
|
||||||
|
fetch('update_fixed_field.php', {
|
||||||
|
method: 'POST',
|
||||||
|
headers: {
|
||||||
|
'Content-Type': 'application/json'
|
||||||
|
},
|
||||||
|
body: JSON.stringify({
|
||||||
|
id,
|
||||||
|
field: 'default_value',
|
||||||
|
value
|
||||||
|
})
|
||||||
|
})
|
||||||
|
.then(r => r.json())
|
||||||
|
.then(d => {
|
||||||
|
if (!d.success) throw new Error(d.message || 'Update failed');
|
||||||
|
// aggiorno anche il dataset così rimane coerente
|
||||||
|
selectEl.dataset.currentValue = value;
|
||||||
|
fixedStatus('✅ Saved');
|
||||||
|
})
|
||||||
|
.catch(err => {
|
||||||
|
console.error(err);
|
||||||
|
fixedStatus('❌ Save error', true);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// Eventi Select2 (jQuery)
|
||||||
|
if (window.jQuery) {
|
||||||
|
$(document).on('select2:select select2:clear', '.fixed-default-select', function() {
|
||||||
|
saveFixedDefaultSelect(this);
|
||||||
|
});
|
||||||
|
|
||||||
|
// fallback (nel caso cambi senza select2)
|
||||||
|
$(document).on('change', '.fixed-default-select', function() {
|
||||||
|
saveFixedDefaultSelect(this);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
document.addEventListener("DOMContentLoaded", function() {
|
document.addEventListener("DOMContentLoaded", function() {
|
||||||
let templateId = <?php echo $id; ?>;
|
let templateId = <?php echo $id; ?>;
|
||||||
let schemaId = <?php echo json_encode($template['idschema'] ?? 0); ?>;
|
let schemaId = <?php echo json_encode($template['idschema'] ?? 0); ?>;
|
||||||
@@ -403,9 +644,13 @@ $xlsHeaders = $template['xls_headers'] ? json_decode($template['xls_headers'], t
|
|||||||
dropdown.disabled = true;
|
dropdown.disabled = true;
|
||||||
});
|
});
|
||||||
} finally {
|
} finally {
|
||||||
|
// attiva Select2 dopo aver messo le options
|
||||||
|
initSelect2ForSchemaDropdowns();
|
||||||
|
|
||||||
console.log('Nascondo overlay di caricamento.');
|
console.log('Nascondo overlay di caricamento.');
|
||||||
loadingOverlay.style.display = 'none';
|
loadingOverlay.style.display = 'none';
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Carica i dropdown con overlay
|
// Carica i dropdown con overlay
|
||||||
@@ -713,9 +958,352 @@ $xlsHeaders = $template['xls_headers'] ? json_decode($template['xls_headers'], t
|
|||||||
.catch(error => console.error("❌ Fetch error:", error));
|
.catch(error => console.error("❌ Fetch error:", error));
|
||||||
}
|
}
|
||||||
|
|
||||||
if (availableXlsColumns.length) updateXlsDropdowns();
|
// =======================
|
||||||
|
// FIXED FIELDS (DB autosave)
|
||||||
|
// =======================
|
||||||
|
|
||||||
|
function escapeHtml(str) {
|
||||||
|
return String(str ?? '')
|
||||||
|
.replaceAll('&', '&')
|
||||||
|
.replaceAll('<', '<')
|
||||||
|
.replaceAll('>', '>')
|
||||||
|
.replaceAll('"', '"')
|
||||||
|
.replaceAll("'", ''');
|
||||||
|
}
|
||||||
|
|
||||||
|
function fixedStatus(msg, isError = false) {
|
||||||
|
const el = document.getElementById('fixedSaveStatus');
|
||||||
|
if (!el) return;
|
||||||
|
el.textContent = msg;
|
||||||
|
el.classList.toggle('text-danger', isError);
|
||||||
|
el.classList.toggle('text-muted', !isError);
|
||||||
|
setTimeout(() => {
|
||||||
|
el.textContent = '';
|
||||||
|
}, 2000);
|
||||||
|
}
|
||||||
|
|
||||||
|
function renderFixedRows(rows) {
|
||||||
|
const tbody = document.querySelector('#fixedFieldsTable tbody');
|
||||||
|
if (!tbody) return;
|
||||||
|
|
||||||
|
const keysWithDropdown = [
|
||||||
|
'ClienteResponsabile',
|
||||||
|
'MoltiplicatorePrezzo',
|
||||||
|
'AnagraficaCertestObject',
|
||||||
|
'AnagraficaCertestService'
|
||||||
|
];
|
||||||
|
|
||||||
|
tbody.innerHTML = rows.map(r => {
|
||||||
|
const type = String(r.data_type || '').toUpperCase();
|
||||||
|
const dv = r.default_value ?? '';
|
||||||
|
const isDate = type === 'DATE';
|
||||||
|
const isDropdown = keysWithDropdown.includes(String(r.fixed_field_key || ''));
|
||||||
|
|
||||||
|
return `
|
||||||
|
<tr>
|
||||||
|
<td class="text-center">
|
||||||
|
<input type="checkbox"
|
||||||
|
class="fixed-visible-checkbox"
|
||||||
|
data-fixed-id="${r.id}"
|
||||||
|
${parseInt(r.is_visible_import) === 1 ? 'checked' : ''}>
|
||||||
|
</td>
|
||||||
|
|
||||||
|
<td class="text-center">
|
||||||
|
<input type="checkbox"
|
||||||
|
class="fixed-required-checkbox"
|
||||||
|
data-fixed-id="${r.id}"
|
||||||
|
${parseInt(r.is_required) === 1 ? 'checked' : ''}>
|
||||||
|
</td>
|
||||||
|
|
||||||
|
<td><strong>${escapeHtml(r.fixed_field_key)}</strong></td>
|
||||||
|
|
||||||
|
|
||||||
|
<td>${escapeHtml(r.data_type || '')}</td>
|
||||||
|
|
||||||
|
<td>
|
||||||
|
${
|
||||||
|
isDate
|
||||||
|
? `<input type="date"
|
||||||
|
class="form-control fixed-default-input"
|
||||||
|
data-fixed-id="${r.id}"
|
||||||
|
value="${escapeHtml(dv)}">`
|
||||||
|
: isDropdown
|
||||||
|
? `<select class="form-select fixed-default-select"
|
||||||
|
data-fixed-id="${r.id}"
|
||||||
|
data-fixed-key="${escapeHtml(r.fixed_field_key)}"
|
||||||
|
data-current-value="${escapeHtml(dv)}">
|
||||||
|
<option value="">Loading...</option>
|
||||||
|
</select>`
|
||||||
|
: `<input type="text"
|
||||||
|
class="form-control fixed-default-input"
|
||||||
|
data-fixed-id="${r.id}"
|
||||||
|
value="${escapeHtml(dv)}"
|
||||||
|
placeholder="Default value">`
|
||||||
|
}
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
`;
|
||||||
|
}).join('');
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
// Button: Add Fixed Fields
|
||||||
|
const btnAddFixedFields = document.getElementById('btnAddFixedFields');
|
||||||
|
if (btnAddFixedFields) {
|
||||||
|
btnAddFixedFields.addEventListener('click', async () => {
|
||||||
|
btnAddFixedFields.disabled = true;
|
||||||
|
btnAddFixedFields.textContent = 'Creating...';
|
||||||
|
|
||||||
|
try {
|
||||||
|
const res = await fetch('create_fixed_fields.php', {
|
||||||
|
method: 'POST',
|
||||||
|
headers: {
|
||||||
|
'Content-Type': 'application/json'
|
||||||
|
},
|
||||||
|
body: JSON.stringify({
|
||||||
|
template_id: templateId
|
||||||
|
})
|
||||||
});
|
});
|
||||||
|
const data = await res.json();
|
||||||
|
|
||||||
|
if (!data.success) throw new Error(data.message || 'Create failed');
|
||||||
|
|
||||||
|
document.getElementById('fixedFieldsSection').style.display = 'block';
|
||||||
|
btnAddFixedFields.style.display = 'none';
|
||||||
|
|
||||||
|
if (Array.isArray(data.rows)) {
|
||||||
|
renderFixedRows(data.rows);
|
||||||
|
await fillFixedDropdowns();
|
||||||
|
initSelect2ForFixedDropdowns();
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
fixedStatus('✅ Fixed fields created');
|
||||||
|
} catch (e) {
|
||||||
|
console.error(e);
|
||||||
|
fixedStatus('❌ ' + e.message, true);
|
||||||
|
btnAddFixedFields.disabled = false;
|
||||||
|
btnAddFixedFields.textContent = 'Add Fixed Fields';
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// Autosave: Visible on import
|
||||||
|
document.addEventListener('change', (e) => {
|
||||||
|
if (!e.target.classList.contains('fixed-visible-checkbox')) return;
|
||||||
|
|
||||||
|
const id = e.target.dataset.fixedId;
|
||||||
|
const value = e.target.checked ? 1 : 0;
|
||||||
|
|
||||||
|
fetch('update_fixed_field.php', {
|
||||||
|
method: 'POST',
|
||||||
|
headers: {
|
||||||
|
'Content-Type': 'application/json'
|
||||||
|
},
|
||||||
|
body: JSON.stringify({
|
||||||
|
id,
|
||||||
|
field: 'is_visible_import',
|
||||||
|
value
|
||||||
|
})
|
||||||
|
})
|
||||||
|
.then(r => r.json())
|
||||||
|
.then(d => {
|
||||||
|
if (!d.success) throw new Error(d.message || 'Update failed');
|
||||||
|
fixedStatus('✅ Saved');
|
||||||
|
})
|
||||||
|
.catch(err => {
|
||||||
|
console.error(err);
|
||||||
|
e.target.checked = !e.target.checked;
|
||||||
|
fixedStatus('❌ Save error', true);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
document.addEventListener('change', (e) => {
|
||||||
|
if (!e.target.classList.contains('fixed-required-checkbox')) return;
|
||||||
|
|
||||||
|
const id = e.target.dataset.fixedId;
|
||||||
|
const value = e.target.checked ? 1 : 0;
|
||||||
|
|
||||||
|
fetch('update_fixed_field.php', {
|
||||||
|
method: 'POST',
|
||||||
|
headers: {
|
||||||
|
'Content-Type': 'application/json'
|
||||||
|
},
|
||||||
|
body: JSON.stringify({
|
||||||
|
id,
|
||||||
|
field: 'is_required',
|
||||||
|
value
|
||||||
|
})
|
||||||
|
})
|
||||||
|
.then(r => r.json())
|
||||||
|
.then(d => {
|
||||||
|
if (!d.success) throw new Error(d.message || 'Update failed');
|
||||||
|
fixedStatus('✅ Saved');
|
||||||
|
})
|
||||||
|
.catch(err => {
|
||||||
|
console.error(err);
|
||||||
|
e.target.checked = !e.target.checked;
|
||||||
|
fixedStatus('❌ Save error', true);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
// Autosave: manual_default (debounce)
|
||||||
|
let fixedTimer = null;
|
||||||
|
document.addEventListener('input', (e) => {
|
||||||
|
if (!e.target.classList.contains('fixed-default-input')) return;
|
||||||
|
|
||||||
|
clearTimeout(fixedTimer);
|
||||||
|
fixedTimer = setTimeout(() => {
|
||||||
|
const id = e.target.dataset.fixedId;
|
||||||
|
const value = e.target.value;
|
||||||
|
|
||||||
|
fetch('update_fixed_field.php', {
|
||||||
|
method: 'POST',
|
||||||
|
headers: {
|
||||||
|
'Content-Type': 'application/json'
|
||||||
|
},
|
||||||
|
body: JSON.stringify({
|
||||||
|
id,
|
||||||
|
field: 'default_value',
|
||||||
|
value
|
||||||
|
})
|
||||||
|
|
||||||
|
})
|
||||||
|
.then(r => r.json())
|
||||||
|
.then(d => {
|
||||||
|
if (!d.success) throw new Error(d.message || 'Update failed');
|
||||||
|
fixedStatus('✅ Saved');
|
||||||
|
})
|
||||||
|
.catch(err => {
|
||||||
|
console.error(err);
|
||||||
|
fixedStatus('❌ Save error', true);
|
||||||
|
});
|
||||||
|
}, 350);
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
if (availableXlsColumns.length) updateXlsDropdowns();
|
||||||
|
|
||||||
|
fillFixedDropdowns().then(() => {
|
||||||
|
initSelect2ForFixedDropdowns();
|
||||||
|
});
|
||||||
|
|
||||||
|
});
|
||||||
|
|
||||||
|
async function fillFixedDropdowns() {
|
||||||
|
const selects = document.querySelectorAll('.fixed-default-select');
|
||||||
|
console.log('[fillFixedDropdowns] found selects:', selects.length);
|
||||||
|
|
||||||
|
if (!selects.length) return;
|
||||||
|
|
||||||
|
const clientId = <?php echo (int)($template['idclient'] ?? 0); ?>;
|
||||||
|
|
||||||
|
async function fetchJson(url) {
|
||||||
|
const r = await fetch(url);
|
||||||
|
const text = await r.text();
|
||||||
|
if (!r.ok) throw new Error(`HTTP ${r.status} on ${url}: ${text.slice(0, 200)}`);
|
||||||
|
try {
|
||||||
|
return JSON.parse(text);
|
||||||
|
} catch (e) {
|
||||||
|
throw new Error(`Invalid JSON from ${url}: ${text.slice(0, 200)}`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function normalizeList(j, key) {
|
||||||
|
// Accetta vari formati: {value:[]}, {Responsabili:[]}, {results:[]}, array diretto
|
||||||
|
if (Array.isArray(j)) return j;
|
||||||
|
if (j && Array.isArray(j.value)) return j.value;
|
||||||
|
if (j && Array.isArray(j.Responsabili)) return j.Responsabili;
|
||||||
|
if (j && Array.isArray(j.results)) return j.results;
|
||||||
|
|
||||||
|
console.warn('[fillFixedDropdowns] unknown JSON shape for', key, j);
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
|
||||||
|
async function loadData(key) {
|
||||||
|
if (key === 'MoltiplicatorePrezzo') {
|
||||||
|
const j = await fetchJson('get_moltiplicatoreprezzo.php');
|
||||||
|
const list = normalizeList(j, key);
|
||||||
|
return list.map(x => ({
|
||||||
|
id: x.IdMoltiplicatorePrezzo,
|
||||||
|
label: `${x.Descrizione} (x${x.Fattore})`
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (key === 'AnagraficaCertestObject') {
|
||||||
|
const j = await fetchJson('get_anagrafica_certest_object.php');
|
||||||
|
const list = normalizeList(j, key);
|
||||||
|
return list.map(x => ({
|
||||||
|
id: x.IdAnagrafica,
|
||||||
|
label: `${x.Codice} - ${x.NomeAnagrafica}`
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (key === 'AnagraficaCertestService') {
|
||||||
|
const j = await fetchJson('get_anagrafica_certest_service.php');
|
||||||
|
const list = normalizeList(j, key);
|
||||||
|
return list.map(x => ({
|
||||||
|
id: x.IdAnagrafica,
|
||||||
|
label: `${x.Codice} - ${x.NomeAnagrafica}`
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (key === 'ClienteResponsabile') {
|
||||||
|
if (!clientId) return [];
|
||||||
|
const j = await fetchJson('get_cliente_responsabile.php?id_cliente=' + clientId);
|
||||||
|
const list = normalizeList(j, key);
|
||||||
|
|
||||||
|
// supporto due possibili campi:
|
||||||
|
// - {IdClienteResponsabile, Nominativo}
|
||||||
|
// - {Id, Nome} o simili
|
||||||
|
return list.map(x => ({
|
||||||
|
id: x.IdClienteResponsabile ?? x.Id ?? x.id,
|
||||||
|
label: x.Nominativo ?? x.Nome ?? x.name ?? ''
|
||||||
|
})).filter(x => x.id != null && x.label);
|
||||||
|
}
|
||||||
|
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
|
||||||
|
for (const select of selects) {
|
||||||
|
const key = select.dataset.fixedKey;
|
||||||
|
const current = select.dataset.currentValue;
|
||||||
|
|
||||||
|
// IMPORTANT: togli "Loading..." subito (così se crasha non resta bloccato)
|
||||||
|
select.innerHTML = '<option value="">Loading...</option>';
|
||||||
|
|
||||||
|
try {
|
||||||
|
console.log('[fillFixedDropdowns] loading', key);
|
||||||
|
const items = await loadData(key);
|
||||||
|
|
||||||
|
if (!items.length) {
|
||||||
|
select.innerHTML = '<option value="">No data</option>';
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
select.innerHTML = '<option value=""></option>';
|
||||||
|
|
||||||
|
for (const it of items) {
|
||||||
|
const opt = document.createElement('option');
|
||||||
|
opt.value = it.id;
|
||||||
|
opt.textContent = it.label;
|
||||||
|
select.appendChild(opt);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (current) select.value = current;
|
||||||
|
|
||||||
|
} catch (err) {
|
||||||
|
console.error('[fillFixedDropdowns] error for', key, err);
|
||||||
|
select.innerHTML = '<option value="">Error loading data</option>';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
|
||||||
</body>
|
</body>
|
||||||
|
|
||||||
</html>
|
</html>
|
||||||
@@ -0,0 +1,274 @@
|
|||||||
|
<!-- Modal per la gestione delle annotazioni -->
|
||||||
|
<div class="modal fade" id="annotationsModal" tabindex="-1" aria-labelledby="annotationsModalLabel" aria-hidden="true">
|
||||||
|
<div class="modal-dialog modal-xl" style="max-width: 90% !important; width: 90% !important;">
|
||||||
|
<div class="modal-content">
|
||||||
|
<div class="modal-header">
|
||||||
|
<h5 class="modal-title" id="annotationsModalLabel">Annotazioni per TRF: <span id="trfHeaderAnnotations"></span></h5>
|
||||||
|
|
||||||
|
<!-- SLIDER PER DIMENSIONE MARKER -->
|
||||||
|
<div style="display: flex; align-items: center; gap: 10px; margin-left: 20px;">
|
||||||
|
<label for="markerSizeSlider" style="margin: 0; font-size: 0.9rem; white-space: nowrap;">Dimensione marker:</label>
|
||||||
|
<input type="range" id="markerSizeSlider" min="16" max="48" value="16" step="2" style="width: 120px;">
|
||||||
|
<span id="markerSizeValue" style="font-weight: bold; min-width: 30px;">24px</span>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
|
||||||
|
</div>
|
||||||
|
<div class="modal-body">
|
||||||
|
<div class="row">
|
||||||
|
<!-- COLONNA SINISTRA RIDOTTA -->
|
||||||
|
<div class="col-md-6">
|
||||||
|
<div style="display: flex; justify-content: space-between; align-items: center; margin-bottom: 10px;">
|
||||||
|
<h6 style="margin: 0;">Elenco Parti</h6>
|
||||||
|
<div style="display: flex; align-items: center;">
|
||||||
|
<input type="checkbox" id="showMixPartsAnnotations" name="showMixPartsAnnotations" style="margin-right: 5px;">
|
||||||
|
<label for="showMixPartsAnnotations" style="font-size: 0.9rem;">Mix</label>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<ul id="partsListAnnotations" class="list-group" style="max-height: 500px; overflow-y: auto;"></ul>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- COLONNA DESTRA PIÙ GRANDE -->
|
||||||
|
<div class="col-md-6">
|
||||||
|
<h6>Foto del Campione</h6>
|
||||||
|
<div style="display: flex; align-items: center; margin-bottom: 10px;">
|
||||||
|
<button type="button" class="btn btn-primary btn-sm" id="downloadPhotoBtnAnnotations" style="padding: 0.1rem 0.5rem; font-size: 0.8rem; margin-right: 10px;"><i class="fas fa-download"></i></button>
|
||||||
|
<div id="photoSelectorContainerAnnotations" style="display: none;"></div>
|
||||||
|
</div>
|
||||||
|
<div style="position: relative; width: 100%; min-height: 500px; border: 1px solid #ddd; border-radius: 4px; overflow: hidden;">
|
||||||
|
<img id="samplePhotoAnnotations" src="" alt="Foto del campione" style="max-width: 100%; max-height: 100%; object-fit: contain; position: absolute; top: 0; left: 0;">
|
||||||
|
<canvas id="photoCanvasAnnotations" style="position: absolute; top: 0; left: 0; width: 100%; height: 100%;"></canvas>
|
||||||
|
<canvas id="overlayCanvasAnnotations" style="position: absolute; top: 0; left: 0; width: 100%; height: 100%; z-index: 1000;"></canvas>
|
||||||
|
<div id="descriptionListAnnotations" class="draggable-description" style="display: none;"></div>
|
||||||
|
<div id="markerContainerAnnotations"></div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="modal-footer">
|
||||||
|
<button type="button" class="btn btn-primary btn-sm" id="addDescriptionsBtnAnnotations" style="padding: 0.1rem 0.5rem; font-size: 0.8rem;">Aggiungi Lista Descrizioni</button>
|
||||||
|
<button type="button" class="btn btn-danger btn-sm" id="removeAnnotationsBtnAnnotations" style="padding: 0.1rem 0.5rem; font-size: 0.8rem;">Rimuovi Descrizioni</button>
|
||||||
|
<button type="button" class="btn btn-warning btn-sm" id="undoMarkerBtnAnnotations" style="padding: 0.1rem 0.5rem; font-size: 0.8rem;">Undo Marker</button>
|
||||||
|
<button type="button" class="btn btn-success btn-sm" id="savePhotoBtnAnnotations" style="padding: 0.1rem 0.5rem; font-size: 0.8rem;">Salva Foto con Nome</button>
|
||||||
|
<button type="button" class="btn btn-primary btn-sm" id="backToPartsBtnAnnotations" style="padding: 0.1rem 0.5rem; font-size: 0.8rem;">Torna alle Parti</button>
|
||||||
|
<button type="button" class="btn btn-secondary btn-sm" data-bs-dismiss="modal" style="padding: 0.1rem 0.5rem; font-size: 0.8rem;">Chiudi</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<style>
|
||||||
|
#annotationsModal {
|
||||||
|
z-index: 1070 !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
#annotationsModal .modal-backdrop {
|
||||||
|
z-index: 1065 !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
#annotationsModal .modal-content {
|
||||||
|
width: 100% !important;
|
||||||
|
max-width: 100% !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
#partsListAnnotations .list-group-item {
|
||||||
|
cursor: pointer;
|
||||||
|
transition: background-color 0.2s;
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-between;
|
||||||
|
align-items: center;
|
||||||
|
padding: 5px 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
#partsListAnnotations .list-group-item:hover {
|
||||||
|
background-color: #f5f5f5;
|
||||||
|
}
|
||||||
|
|
||||||
|
#partsListAnnotations .list-group-item.active {
|
||||||
|
background-color: #e9ecef;
|
||||||
|
border-color: #dee2e6;
|
||||||
|
}
|
||||||
|
|
||||||
|
.draggable-description {
|
||||||
|
position: absolute;
|
||||||
|
background: rgba(255, 255, 255, 0.8);
|
||||||
|
padding: 5px;
|
||||||
|
font-family: Arial, sans-serif;
|
||||||
|
color: #000000;
|
||||||
|
cursor: move;
|
||||||
|
user-select: none;
|
||||||
|
z-index: 1000;
|
||||||
|
min-width: 100px;
|
||||||
|
min-height: 50px;
|
||||||
|
overflow: visible;
|
||||||
|
border: 1px solid #ccc;
|
||||||
|
}
|
||||||
|
|
||||||
|
.draggable-description.active-interaction {
|
||||||
|
border: 2px dashed #000;
|
||||||
|
}
|
||||||
|
|
||||||
|
.draggable-description div {
|
||||||
|
white-space: nowrap;
|
||||||
|
}
|
||||||
|
|
||||||
|
.resize-handle {
|
||||||
|
position: absolute;
|
||||||
|
bottom: 0;
|
||||||
|
right: 0;
|
||||||
|
width: 10px;
|
||||||
|
height: 10px;
|
||||||
|
background: #888;
|
||||||
|
cursor: se-resize;
|
||||||
|
}
|
||||||
|
|
||||||
|
.draggable-marker {
|
||||||
|
position: absolute;
|
||||||
|
width: 24px;
|
||||||
|
height: 24px;
|
||||||
|
border-radius: 50%;
|
||||||
|
color: #ffffff;
|
||||||
|
text-align: center;
|
||||||
|
line-height: 24px;
|
||||||
|
font-size: 12px;
|
||||||
|
cursor: move;
|
||||||
|
user-select: none;
|
||||||
|
z-index: 1000;
|
||||||
|
}
|
||||||
|
|
||||||
|
#markerContainerAnnotations {
|
||||||
|
position: absolute;
|
||||||
|
top: 0;
|
||||||
|
left: 0;
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
#savePhotoBtnAnnotations {
|
||||||
|
transition: all 0.3s ease-in-out;
|
||||||
|
}
|
||||||
|
|
||||||
|
#savePhotoBtnAnnotations.unsaved {
|
||||||
|
background-color: #dc3545 !important;
|
||||||
|
border-color: #dc3545 !important;
|
||||||
|
color: white !important;
|
||||||
|
animation: pulse 1.2s infinite;
|
||||||
|
}
|
||||||
|
|
||||||
|
@keyframes pulse {
|
||||||
|
0% {
|
||||||
|
box-shadow: 0 0 0 0 rgba(220, 53, 69, 0.7);
|
||||||
|
}
|
||||||
|
|
||||||
|
70% {
|
||||||
|
box-shadow: 0 0 10px 15px rgba(220, 53, 69, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
100% {
|
||||||
|
box-shadow: 0 0 0 0 rgba(220, 53, 69, 0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.color-picker-container {
|
||||||
|
position: relative;
|
||||||
|
display: inline-block;
|
||||||
|
overflow: visible;
|
||||||
|
z-index: 2000;
|
||||||
|
}
|
||||||
|
|
||||||
|
.color-picker {
|
||||||
|
display: none;
|
||||||
|
position: absolute;
|
||||||
|
right: 0;
|
||||||
|
top: 25px;
|
||||||
|
background: #fff;
|
||||||
|
border: 1px solid #ccc;
|
||||||
|
padding: 5px;
|
||||||
|
z-index: 2000;
|
||||||
|
box-shadow: 0 2px 5px rgba(0, 0, 0, 0.2);
|
||||||
|
flex-wrap: wrap;
|
||||||
|
width: 120px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.color-option {
|
||||||
|
width: 24px;
|
||||||
|
height: 24px;
|
||||||
|
margin: 3px;
|
||||||
|
border: 1px solid #000;
|
||||||
|
cursor: pointer;
|
||||||
|
display: inline-block;
|
||||||
|
pointer-events: auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
.color-option:hover {
|
||||||
|
border: 2px solid #000;
|
||||||
|
margin: 2px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.selected-color {
|
||||||
|
width: 24px;
|
||||||
|
height: 24px;
|
||||||
|
margin-left: 5px;
|
||||||
|
border: 1px solid #000;
|
||||||
|
cursor: pointer;
|
||||||
|
display: inline-block;
|
||||||
|
pointer-events: auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
.selected-color:hover {
|
||||||
|
border: 2px solid #000;
|
||||||
|
margin-left: 4px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.temp-alert {
|
||||||
|
position: absolute;
|
||||||
|
top: 10px;
|
||||||
|
left: 50%;
|
||||||
|
transform: translateX(-50%);
|
||||||
|
z-index: 3000;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Stile per lo slider */
|
||||||
|
#markerSizeSlider {
|
||||||
|
-webkit-appearance: none;
|
||||||
|
height: 6px;
|
||||||
|
border-radius: 3px;
|
||||||
|
background: #ddd;
|
||||||
|
outline: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
#markerSizeSlider::-webkit-slider-thumb {
|
||||||
|
-webkit-appearance: none;
|
||||||
|
appearance: none;
|
||||||
|
width: 16px;
|
||||||
|
height: 16px;
|
||||||
|
border-radius: 50%;
|
||||||
|
background: #007bff;
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
|
||||||
|
#markerSizeSlider::-moz-range-thumb {
|
||||||
|
width: 16px;
|
||||||
|
height: 16px;
|
||||||
|
border-radius: 50%;
|
||||||
|
background: #007bff;
|
||||||
|
cursor: pointer;
|
||||||
|
border: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
#partsListAnnotations .list-group-item.dragging {
|
||||||
|
opacity: 0.6;
|
||||||
|
background-color: #d1e7dd !important;
|
||||||
|
transform: rotate(3deg);
|
||||||
|
box-shadow: 0 4px 8px rgba(0, 0, 0, 0.2);
|
||||||
|
}
|
||||||
|
|
||||||
|
#partsListAnnotations .placeholder {
|
||||||
|
background-color: #f8d7da !important;
|
||||||
|
border: 2px dashed #dc3545;
|
||||||
|
height: 40px;
|
||||||
|
margin: 2px 0;
|
||||||
|
border-radius: 4px;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
<script src="https://code.jquery.com/ui/1.13.2/jquery-ui.min.js"></script>
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
<!-- Modal modificato con pulsante per riconoscimento vocale e download -->
|
<!-- Modal modificato con pulsante per riconoscimento vocale, download e selezione matrici -->
|
||||||
<div class="modal fade" id="partsModal" tabindex="-1" aria-labelledby="partsModalLabel" aria-hidden="true">
|
<div class="modal fade" id="partsModal" tabindex="-1" aria-labelledby="partsModalLabel" aria-hidden="true">
|
||||||
<div class="modal-dialog modal-xl" style="max-width: 80% !important; width: 80% !important;">
|
<div class="modal-dialog modal-xl" style="max-width: 80% !important; width: 80% !important;">
|
||||||
<div class="modal-content">
|
<div class="modal-content">
|
||||||
@@ -9,10 +9,12 @@
|
|||||||
<div class="modal-body">
|
<div class="modal-body">
|
||||||
<div class="row">
|
<div class="row">
|
||||||
<div class="col-md-6">
|
<div class="col-md-6">
|
||||||
<div style="display: flex; justify-content: space-between; align-items: center; margin-bottom: 10px;">
|
<div style="display: flex; justify-content: space-between; align-items: center; margin-bottom: 10px; min-width: 0;">
|
||||||
<h6 style="margin: 0; white-space: nowrap;">Elenco Parti</h6>
|
<h6 style="margin: 0; white-space: nowrap;">Elenco Parti</h6>
|
||||||
<div style="display: flex; align-items: center;">
|
<div style="display: flex; align-items: center; min-width: 0;">
|
||||||
<input type="checkbox" id="showMixParts" name="showMixParts" style="margin-right: 5px;">
|
<select id="global-matrice" class="ms-2" style="width: 250px !important; min-width: 250px !important;">
|
||||||
|
</select>
|
||||||
|
<input type="checkbox" id="showMixParts" name="showMixParts" style="margin-right: 5px; margin-left: 10px;">
|
||||||
<label for="showMixParts" style="font-size: 0.9rem; margin-right: 10px;">Mix</label>
|
<label for="showMixParts" style="font-size: 0.9rem; margin-right: 10px;">Mix</label>
|
||||||
<button type="button" class="btn btn-info btn-sm" id="renumberPartsBtn" style="padding: 0.1rem 0.5rem; font-size: 0.8rem;">Rinumera Parti</button>
|
<button type="button" class="btn btn-info btn-sm" id="renumberPartsBtn" style="padding: 0.1rem 0.5rem; font-size: 0.8rem;">Rinumera Parti</button>
|
||||||
<button type="button" class="btn btn-secondary btn-sm ms-2" id="toggleVoiceBtn" style="padding: 0.1rem 0.5rem; font-size: 0.8rem;"><i class="fas fa-microphone"></i> Voce</button>
|
<button type="button" class="btn btn-secondary btn-sm ms-2" id="toggleVoiceBtn" style="padding: 0.1rem 0.5rem; font-size: 0.8rem;"><i class="fas fa-microphone"></i> Voce</button>
|
||||||
@@ -45,13 +47,14 @@
|
|||||||
<div class="col-md-6">
|
<div class="col-md-6">
|
||||||
<h6>Foto del Campione</h6>
|
<h6>Foto del Campione</h6>
|
||||||
<div style="display: flex; align-items: center; margin-bottom: 10px;">
|
<div style="display: flex; align-items: center; margin-bottom: 10px;">
|
||||||
<button type="button" class="btn btn-primary btn-sm" id="downloadPhotoBtn" style="padding: 0.1rem 0.5rem; font-size: 0.8rem; margin-right: 10px;"><i class="fas fa-download"></i> </button>
|
<button type="button" class="btn btn-primary btn-sm" id="downloadPhotoBtn" style="padding: 0.1rem 0.5rem; font-size: 0.8rem; margin-right: 10px;"><i class="fas fa-download"></i></button>
|
||||||
<div id="photoSelectorContainer" style="display: none;"></div>
|
<div id="photoSelectorContainer" style="display: none;"></div>
|
||||||
</div>
|
</div>
|
||||||
<div style="position: relative; width: 100%; min-height: 400px;">
|
<div style="position: relative; width: 100%; min-height: 400px;">
|
||||||
<img id="samplePhoto" src="" alt="Foto del campione" style="max-width: 100%; max-height: 100%; object-fit: contain; position: absolute; top: 0; left: 0;">
|
<img id="samplePhoto" src="" alt="Foto del campione" style="max-width: 100%; max-height: 100%; object-fit: contain; position: absolute; top: 0; left: 0;">
|
||||||
<canvas id="photoCanvas" style="position: absolute; top: 0; left: 0; width: 100%; height: 100%;"></canvas>
|
<canvas id="photoCanvas" style="position: absolute; top: 0; left: 0; width: 100%; height: 100%;"></canvas>
|
||||||
<canvas id="overlayCanvas" style="position: absolute; top: 0; left: 0; width: 100%; height: 100%; z-index: 1000;"></canvas> <!-- Nuovo canvas per Fabric.js -->
|
<canvas id="overlayCanvas" style="position: absolute; top: 0; left: 0; width: 100%; height: 100%; z-index: 1000;"></canvas>
|
||||||
|
<div id="descriptionList" class="draggable-description" style="display: none;"></div>
|
||||||
<div id="markerContainer"></div>
|
<div id="markerContainer"></div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -249,4 +252,59 @@
|
|||||||
border: 2px solid #000;
|
border: 2px solid #000;
|
||||||
margin: 2px;
|
margin: 2px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Stili per Select2 in #partsList e #global-matrice */
|
||||||
|
#partsList .select2-container,
|
||||||
|
.select2-container--default #global-matrice {
|
||||||
|
width: 250px !important;
|
||||||
|
min-width: 250px !important;
|
||||||
|
margin-left: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
#partsList .select2-selection--single,
|
||||||
|
.select2-container--default #global-matrice .select2-selection--single {
|
||||||
|
height: 26px !important;
|
||||||
|
padding: 0.2rem 0.5rem !important;
|
||||||
|
font-size: 0.9rem !important;
|
||||||
|
border: 1px solid #ced4da !important;
|
||||||
|
background-color: #fff !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
#partsList .select2-selection__rendered,
|
||||||
|
.select2-container--default #global-matrice .select2-selection__rendered {
|
||||||
|
line-height: 24px !important;
|
||||||
|
overflow: hidden !important;
|
||||||
|
text-overflow: ellipsis !important;
|
||||||
|
white-space: nowrap !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
#partsList .select2-selection__arrow,
|
||||||
|
.select2-container--default #global-matrice .select2-selection__arrow {
|
||||||
|
height: 26px !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
#partsList .save-status,
|
||||||
|
#partsList .save-loading {
|
||||||
|
margin-left: 5px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.select2-container--open .select2-dropdown {
|
||||||
|
z-index: 1051 !important;
|
||||||
|
border: 1px solid #aaa !important;
|
||||||
|
border-radius: 4px !important;
|
||||||
|
background: white !important;
|
||||||
|
overflow-y: auto !important;
|
||||||
|
max-height: 200px !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.select2-container--default .select2-search--dropdown .select2-search__field {
|
||||||
|
width: 100% !important;
|
||||||
|
padding: 0.2rem !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.select2-container--default .part-matrice,
|
||||||
|
.select2-container--default #global-matrice {
|
||||||
|
width: 250px !important;
|
||||||
|
min-width: 250px !important;
|
||||||
|
}
|
||||||
</style>
|
</style>
|
||||||
@@ -0,0 +1,496 @@
|
|||||||
|
<div class="modal fade" id="partsModal" tabindex="-1" aria-labelledby="partsModalLabel" aria-hidden="true">
|
||||||
|
<div class="modal-dialog modal-xl" style="max-width: 80% !important;">
|
||||||
|
<div class="modal-content">
|
||||||
|
<div class="modal-header">
|
||||||
|
<h5 class="modal-title" id="partsModalLabel">Parti per TRF: <span id="trfHeader"></span></h5>
|
||||||
|
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
|
||||||
|
</div>
|
||||||
|
<div class="modal-body">
|
||||||
|
<div class="row parts-row">
|
||||||
|
<div class="col-md-9">
|
||||||
|
<!-- Prima riga: Elenco Parti, Rinumera, Voce -->
|
||||||
|
<div style="display: flex; justify-content: space-between; align-items: center; margin-bottom: 10px;">
|
||||||
|
<h6 style="margin: 0;">Elenco Parti</h6>
|
||||||
|
<div style="display: flex; align-items: center;">
|
||||||
|
<button type="button" class="btn btn-info btn-sm" id="renumberPartsBtn" style="padding: 0.1rem 0.5rem; font-size: 0.8rem;">Rinumera Parti</button>
|
||||||
|
<button type="button" class="btn btn-secondary btn-sm ms-2" id="toggleVoiceBtn" style="padding: 0.1rem 0.5rem; font-size: 0.8rem;"><i class="fas fa-microphone"></i> Voce</button>
|
||||||
|
<button type="button" class="btn btn-info btn-sm ms-2 d-none" id="quotationeBtn" style="padding: 0.1rem 0.5rem; font-size: 0.8rem;">Add Quotation</button>
|
||||||
|
<button type="button" class="btn btn-primary btn-sm ms-2" id="showHideImageBtn" style="padding: 0.1rem 0.5rem; font-size: 0.8rem;">
|
||||||
|
<i class="fas fa-eye-slash" style="font-size: 0.8rem;"></i>
|
||||||
|
<i class="fas fa-image ms-1" style="font-size: 0.8rem;"></i>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<!-- Seconda riga: +, M, MacroMatrice, Matrice globale, Propaga -->
|
||||||
|
<div style="display: flex; align-items: center; margin-bottom: 10px;">
|
||||||
|
<button type="button" class="btn btn-success btn-sm add-row-global" style="padding: 0.1rem 0.3rem; font-size: 0.8rem; margin-right: 5px;"><i class="fas fa-plus fa-xs"></i></button>
|
||||||
|
<button type="button" class="btn btn-primary btn-sm add-mix-global" style="padding: 0.1rem 0.3rem; font-size: 0.8rem; margin-right: 10px;">M</button>
|
||||||
|
<select id="macro-matrice-filter" class="form-control form-control-sm ms-2" style="width: 200px !important; min-width: 200px !important; margin-right: 10px;">
|
||||||
|
<option value="">Tutte le MacroMatrici</option>
|
||||||
|
</select>
|
||||||
|
<select id="global-matrice" class="form-control form-control-sm" style="width: 350px !important; margin-right: 10px;"></select>
|
||||||
|
<button type="button" class="btn btn-primary btn-sm propagate-all-btn" style="padding: 0.1rem 0.5rem; font-size: 0.8rem;"><i class="fas fa-arrow-right fa-xs"></i> Propaga a tutte</button>
|
||||||
|
</div>
|
||||||
|
<table class="table table-striped table-sm" id="partsTable">
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
<th style="width: 80px;">Numero</th>
|
||||||
|
<th>Descrizione</th>
|
||||||
|
<th style="width: 200px;">Matrice</th>
|
||||||
|
<th style="width: 150px;">
|
||||||
|
<input type="date" class="form-control form-control-sm propagate-date-input" style="width: 130px; margin-left: 5px; display: inline-block;" title="Propaga data a tutte le parti">
|
||||||
|
</th>
|
||||||
|
<th style="width: 200px;">
|
||||||
|
<button type="button" class="btn btn-light btn-sm propagate-note-btn" style="padding: 0.2rem 0.4rem; font-size: 0.9rem; margin-left: 5px;" title="Propaga nota a tutte le parti">
|
||||||
|
<i class="fas fa-sticky-note"></i>
|
||||||
|
</button>
|
||||||
|
Azioni
|
||||||
|
|
||||||
|
</th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody id="partsTableBody">
|
||||||
|
<tr data-part-id="new">
|
||||||
|
<td><input type="number" class="form-control form-control-sm part-number" value="1" style="width: 80px;"></td>
|
||||||
|
<td><input type="text" class="form-control form-control-sm part-description" placeholder="Inserisci descrizione"></td>
|
||||||
|
<td>
|
||||||
|
<div style="display: flex; align-items: center;">
|
||||||
|
<button type="button" class="btn btn-primary btn-sm propagate-matrice-btn" style="padding: 0.1rem 0.3rem; font-size: 0.8rem; margin-right: 3px;"><i class="fas fa-arrow-right fa-xs"></i></button>
|
||||||
|
<select class="part-matrice form-control form-control-sm" style="width: 150px;"></select>
|
||||||
|
</div>
|
||||||
|
</td>
|
||||||
|
<td><input type="date" class="form-control form-control-sm part-dateexpiry" style="width: 130px;"></td>
|
||||||
|
<td>
|
||||||
|
<button type="button" class="btn btn-light btn-sm note-btn" style="padding: 0.2rem 0.4rem; font-size: 0.9rem;" title="Aggiungi/Modifica nota"><i class="fas fa-sticky-note"></i></button>
|
||||||
|
<button type="button" class="btn btn-warning btn-sm add-mix-row" style="padding: 0.1rem 0.3rem; font-size: 0.8rem;">M+</button>
|
||||||
|
<button type="button" class="btn btn-danger btn-sm remove-row" style="padding: 0.1rem 0.3rem; font-size: 0.8rem; display: none;"><i class="fas fa-trash fa-xs"></i></button>
|
||||||
|
<span class="save-status text-success" style="display: none; margin-left: 5px;"><i class="fas fa-check fa-xs"></i></span>
|
||||||
|
<span class="save-loading text-warning" style="display: none; margin-left: 5px;"><i class="fas fa-spinner fa-spin fa-xs"></i></span>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
<div class="col-md-3">
|
||||||
|
<h6>Foto del Campione</h6>
|
||||||
|
<div style="display: flex; align-items: center; margin-bottom: 10px;">
|
||||||
|
<button type="button" class="btn btn-primary btn-sm" id="downloadPhotoBtn" style="padding: 0.1rem 0.5rem; font-size: 0.8rem; margin-right: 10px;"><i class="fas fa-download"></i></button>
|
||||||
|
<div id="photoSelectorContainer" style="display: none;"></div>
|
||||||
|
</div>
|
||||||
|
<div style="position: relative; width: 100%; min-height: 400px;">
|
||||||
|
<img id="samplePhoto" src="" alt="Foto del campione" style="max-width: 100%; max-height: 400px; object-fit: contain;">
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="modal-footer">
|
||||||
|
<button type="button" class="btn btn-primary btn-sm" id="openAnnotationsBtn">Apri Annotazioni</button>
|
||||||
|
<button type="button" class="btn btn-secondary btn-sm" data-bs-dismiss="modal">Chiudi</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Modale per la nota -->
|
||||||
|
<div class="modal fade" id="noteModal" tabindex="-1" aria-labelledby="noteModalLabel" aria-hidden="true">
|
||||||
|
<div class="modal-dialog modal-md">
|
||||||
|
<div class="modal-content">
|
||||||
|
<div class="modal-header">
|
||||||
|
<h5 class="modal-title" id="noteModalLabel">Nota per Parte</h5>
|
||||||
|
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
|
||||||
|
</div>
|
||||||
|
<div class="modal-body">
|
||||||
|
<textarea class="form-control part-note" rows="5" placeholder="Inserisci una nota"></textarea>
|
||||||
|
</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 save-note-btn">Salva</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="modal fade" id="commonNoteModal" tabindex="-1" aria-labelledby="commonNoteModalLabel" aria-hidden="true">
|
||||||
|
<div class="modal-dialog">
|
||||||
|
<div class="modal-content">
|
||||||
|
<div class="modal-header">
|
||||||
|
<h5 class="modal-title" id="commonNoteModalLabel">Nota comune per tutte le parti</h5>
|
||||||
|
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
|
||||||
|
</div>
|
||||||
|
<div class="modal-body">
|
||||||
|
<textarea class="form-control part-note" rows="4" placeholder="Inserisci nota comune"></textarea>
|
||||||
|
</div>
|
||||||
|
<div class="modal-footer">
|
||||||
|
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal">Chiudi</button>
|
||||||
|
<button type="button" class="btn btn-primary save-common-note-btn">Salva</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Modale di conferma per l'eliminazione -->
|
||||||
|
<div class="modal fade" id="confirmDeleteModal" tabindex="-1" aria-labelledby="confirmDeleteModalLabel" aria-hidden="true">
|
||||||
|
<div class="modal-dialog modal-sm">
|
||||||
|
<div class="modal-content">
|
||||||
|
<div class="modal-header">
|
||||||
|
<h5 class="modal-title" id="confirmDeleteModalLabel">Conferma Eliminazione</h5>
|
||||||
|
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
|
||||||
|
</div>
|
||||||
|
<div class="modal-body">
|
||||||
|
Sei sicuro di voler eliminare questa parte?
|
||||||
|
</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-danger btn-sm" id="confirmDeleteBtn">Elimina</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<!-- Finestra modale "Aggiungi preventivo" -->
|
||||||
|
<div class="modal fade" id="addQuotationModal" tabindex="-1" aria-labelledby="addQuotationModalLabel" aria-hidden="true">
|
||||||
|
<div class="modal-dialog modal-md">
|
||||||
|
<div class="modal-content">
|
||||||
|
<div class="modal-header">
|
||||||
|
<h5 class="modal-title" id="addQuotationModalLabel">Choose a quotation</h5>
|
||||||
|
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
|
||||||
|
</div>
|
||||||
|
<div class="modal-body">
|
||||||
|
<select id="addQuotationSelect" class="form-control form-control-sm" style="width: 100% !important; min-width: 100% !important"></select>
|
||||||
|
</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="addQuotationBtn">Confirm</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<style>
|
||||||
|
/* --- Base --- */
|
||||||
|
#partsModal {
|
||||||
|
z-index: 1060 !important
|
||||||
|
}
|
||||||
|
|
||||||
|
#partsModal .modal-backdrop {
|
||||||
|
z-index: 1055 !important
|
||||||
|
}
|
||||||
|
|
||||||
|
#partsModal .modal-content {
|
||||||
|
width: 100% !important;
|
||||||
|
max-width: 100% !important
|
||||||
|
}
|
||||||
|
|
||||||
|
#addQuotationModal {
|
||||||
|
z-index: 1060 !important
|
||||||
|
}
|
||||||
|
|
||||||
|
#addQuotationModal .modal-backdrop {
|
||||||
|
z-index: 1055 !important
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Tabelle */
|
||||||
|
#partsTable tr {
|
||||||
|
display: table-row !important
|
||||||
|
}
|
||||||
|
|
||||||
|
#partsTable tr:hover {
|
||||||
|
background: #f5f5f5
|
||||||
|
}
|
||||||
|
|
||||||
|
#partsTable td,
|
||||||
|
#partsTable th {
|
||||||
|
padding: .2rem;
|
||||||
|
vertical-align: middle
|
||||||
|
}
|
||||||
|
|
||||||
|
#partsTable input,
|
||||||
|
#partsTable select {
|
||||||
|
height: 24px;
|
||||||
|
padding: .1rem .3rem
|
||||||
|
}
|
||||||
|
|
||||||
|
#partsTable button {
|
||||||
|
padding: .1rem .3rem;
|
||||||
|
margin: 0 2px
|
||||||
|
}
|
||||||
|
|
||||||
|
#partsTable i {
|
||||||
|
font-size: .6rem !important
|
||||||
|
}
|
||||||
|
|
||||||
|
/* --- Larghezze fisse header --- */
|
||||||
|
/* MacroMatrici = 250px */
|
||||||
|
#macro-matrice-filter {
|
||||||
|
width: 250px !important;
|
||||||
|
min-width: 250px !important;
|
||||||
|
max-width: 250px !important;
|
||||||
|
flex: 0 0 250px !important;
|
||||||
|
box-sizing: border-box;
|
||||||
|
overflow: hidden;
|
||||||
|
text-overflow: ellipsis;
|
||||||
|
white-space: nowrap;
|
||||||
|
}
|
||||||
|
|
||||||
|
#macro-matrice-filter.select2-hidden-accessible+.select2 {
|
||||||
|
width: 250px !important;
|
||||||
|
min-width: 250px !important;
|
||||||
|
max-width: 250px !important;
|
||||||
|
flex: 0 0 250px !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
#macro-matrice-filter.select2-hidden-accessible+.select2 .select2-selection__rendered {
|
||||||
|
overflow: hidden;
|
||||||
|
text-overflow: ellipsis;
|
||||||
|
white-space: nowrap;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Matrice globale = 450px */
|
||||||
|
#global-matrice {
|
||||||
|
width: 450px !important;
|
||||||
|
min-width: 450px !important;
|
||||||
|
max-width: 450px !important;
|
||||||
|
flex: 0 0 450px !important;
|
||||||
|
box-sizing: border-box;
|
||||||
|
overflow: hidden;
|
||||||
|
text-overflow: ellipsis;
|
||||||
|
white-space: nowrap;
|
||||||
|
}
|
||||||
|
|
||||||
|
#global-matrice.select2-hidden-accessible+.select2 {
|
||||||
|
width: 450px !important;
|
||||||
|
min-width: 450px !important;
|
||||||
|
max-width: 450px !important;
|
||||||
|
flex: 0 0 450px !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
#global-matrice.select2-hidden-accessible+.select2 .select2-selection__rendered {
|
||||||
|
overflow: hidden;
|
||||||
|
text-overflow: ellipsis;
|
||||||
|
white-space: nowrap;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Select delle righe (colonna Matrice) = 150px */
|
||||||
|
.part-matrice {
|
||||||
|
width: 300px !important;
|
||||||
|
min-width: 300px !important;
|
||||||
|
max-width: 300px !important;
|
||||||
|
flex: 0 0 300px !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.part-matrice.select2-hidden-accessible+.select2 {
|
||||||
|
width: 300px !important;
|
||||||
|
min-width: 300px !important;
|
||||||
|
max-width: 300px !important;
|
||||||
|
flex: 0 0 300px !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Colonna Descrizione (2ª colonna) = 420px */
|
||||||
|
#partsTable th:nth-child(2),
|
||||||
|
#partsTable td:nth-child(2) {
|
||||||
|
width: 350 !important;
|
||||||
|
min-width: 350px !important;
|
||||||
|
max-width: 350px !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
#partsTable td:nth-child(2) .part-description {
|
||||||
|
width: 100% !important;
|
||||||
|
max-width: 100% !important;
|
||||||
|
overflow: hidden;
|
||||||
|
text-overflow: ellipsis;
|
||||||
|
white-space: nowrap;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Aspetto Select2 */
|
||||||
|
.select2-container--default .select2-selection--single {
|
||||||
|
height: 24px !important;
|
||||||
|
padding: .1rem .3rem !important;
|
||||||
|
font-size: .8rem !important;
|
||||||
|
border: 1px solid #ced4da !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.select2-container--default .select2-selection__arrow {
|
||||||
|
height: 24px !important
|
||||||
|
}
|
||||||
|
|
||||||
|
.select2-container--open .select2-dropdown {
|
||||||
|
z-index: 1061 !important;
|
||||||
|
border: 1px solid #aaa !important;
|
||||||
|
border-radius: 4px !important;
|
||||||
|
background: #fff !important;
|
||||||
|
max-height: 200px !important;
|
||||||
|
overflow-y: auto !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Evita stretching del flex nella riga dei filtri */
|
||||||
|
#partsModal .modal-body>.row .col-md-9>div[style*="display: flex"]>* {
|
||||||
|
flex: 0 0 auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Altri modali e pulsanti */
|
||||||
|
.propagate-matrice-btn,
|
||||||
|
.propagate-all-btn {
|
||||||
|
padding: .1rem .3rem !important;
|
||||||
|
font-size: .8rem !important
|
||||||
|
}
|
||||||
|
|
||||||
|
.save-status,
|
||||||
|
.save-loading {
|
||||||
|
margin-left: 5px
|
||||||
|
}
|
||||||
|
|
||||||
|
#confirmDeleteModal {
|
||||||
|
z-index: 1070 !important
|
||||||
|
}
|
||||||
|
|
||||||
|
#confirmDeleteModal .modal-backdrop {
|
||||||
|
z-index: 1065 !important
|
||||||
|
}
|
||||||
|
|
||||||
|
.note-btn {
|
||||||
|
padding: .2rem .4rem !important;
|
||||||
|
font-size: .9rem !important
|
||||||
|
}
|
||||||
|
|
||||||
|
.note-btn.has-note {
|
||||||
|
color: #dc3545 !important
|
||||||
|
}
|
||||||
|
|
||||||
|
#noteModal {
|
||||||
|
z-index: 1090 !important
|
||||||
|
}
|
||||||
|
|
||||||
|
#noteModal .modal-backdrop {
|
||||||
|
z-index: 1085 !important
|
||||||
|
}
|
||||||
|
|
||||||
|
#noteModal .modal-dialog {
|
||||||
|
position: relative;
|
||||||
|
z-index: 1090 !important
|
||||||
|
}
|
||||||
|
|
||||||
|
#noteModal textarea,
|
||||||
|
#commonNoteModal textarea {
|
||||||
|
resize: vertical
|
||||||
|
}
|
||||||
|
|
||||||
|
#commonNoteModal {
|
||||||
|
z-index: 1095 !important
|
||||||
|
}
|
||||||
|
|
||||||
|
#commonNoteModal .modal-backdrop {
|
||||||
|
z-index: 1090 !important
|
||||||
|
}
|
||||||
|
|
||||||
|
#commonNoteModal .modal-dialog {
|
||||||
|
position: relative;
|
||||||
|
z-index: 1095 !important
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Evidenza salvataggio riga nel parts table */
|
||||||
|
/* Aumenta la specificità per le classi di flash */
|
||||||
|
table#partsTable tr.row-saving {
|
||||||
|
background-color: #f0ad4e !important;
|
||||||
|
/* Arancione per salvataggio in corso */
|
||||||
|
transition: background-color 0.3s ease;
|
||||||
|
}
|
||||||
|
|
||||||
|
table#partsTable tr.row-success {
|
||||||
|
background-color: #5cb85c !important;
|
||||||
|
/* Verde per successo */
|
||||||
|
transition: background-color 0.3s ease;
|
||||||
|
}
|
||||||
|
|
||||||
|
table#partsTable tr.row-error {
|
||||||
|
background-color: #d9534f !important;
|
||||||
|
/* Rosso per errore */
|
||||||
|
transition: background-color 0.3s ease;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Stato base: nascosti (verrà sovrascritto dallo style inline di jQuery) */
|
||||||
|
#partsModal .save-loading,
|
||||||
|
#partsModal .save-status {
|
||||||
|
display: none;
|
||||||
|
align-items: center;
|
||||||
|
gap: 6px;
|
||||||
|
padding: 2px 8px;
|
||||||
|
border-radius: 999px;
|
||||||
|
font-weight: 700;
|
||||||
|
font-size: 12px;
|
||||||
|
line-height: 1.2;
|
||||||
|
margin-left: 5px;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Quando NON sono nascosti via style inline (jQuery .show()), forzali a inline-flex */
|
||||||
|
#partsModal .save-loading:not([style*="display: none"]),
|
||||||
|
#partsModal .save-status:not([style*="display: none"]) {
|
||||||
|
display: inline-flex;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Loading (giallo) */
|
||||||
|
/* Loading (giallo) */
|
||||||
|
#partsModal .save-loading {
|
||||||
|
background: #ffd753ff;
|
||||||
|
border: 1px solid #ffd042ff;
|
||||||
|
color: #111;
|
||||||
|
/* testo nero */
|
||||||
|
}
|
||||||
|
|
||||||
|
#partsModal .save-loading i {
|
||||||
|
color: #111;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* icona nera */
|
||||||
|
|
||||||
|
#partsModal .save-loading::after {
|
||||||
|
content: " Salvataggio…";
|
||||||
|
color: #111;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Salvato (verde) */
|
||||||
|
#partsModal .save-status {
|
||||||
|
background: #5dff83ff;
|
||||||
|
border: 1px solid #4effafff;
|
||||||
|
color: #111;
|
||||||
|
/* testo nero */
|
||||||
|
}
|
||||||
|
|
||||||
|
#partsModal .save-status i {
|
||||||
|
color: #111;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* icona nera */
|
||||||
|
#partsModal .save-status::after {
|
||||||
|
content: " Salvato";
|
||||||
|
color: #111;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Animazioni */
|
||||||
|
@keyframes pulse {
|
||||||
|
|
||||||
|
0%,
|
||||||
|
100% {
|
||||||
|
transform: scale(1);
|
||||||
|
opacity: .9
|
||||||
|
}
|
||||||
|
|
||||||
|
50% {
|
||||||
|
transform: scale(1.05);
|
||||||
|
opacity: 1
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@keyframes pop {
|
||||||
|
0% {
|
||||||
|
transform: scale(.85);
|
||||||
|
opacity: 0
|
||||||
|
}
|
||||||
|
|
||||||
|
100% {
|
||||||
|
transform: scale(1);
|
||||||
|
opacity: 1
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/* rosso */
|
||||||
|
</style>
|
||||||
+290
-14
@@ -12,11 +12,13 @@ $(document).ready(function () {
|
|||||||
|
|
||||||
let photoAnnotations = {};
|
let photoAnnotations = {};
|
||||||
let partColors = {};
|
let partColors = {};
|
||||||
|
let partMatrice = {};
|
||||||
let selectedPartNumber = null;
|
let selectedPartNumber = null;
|
||||||
let unsavedChanges = false;
|
let unsavedChanges = false;
|
||||||
let fabricCanvas = null;
|
let fabricCanvas = null;
|
||||||
let descriptionTextbox = null;
|
let descriptionTextbox = null;
|
||||||
let markerObjects = {};
|
let markerObjects = {};
|
||||||
|
let matrici = [];
|
||||||
|
|
||||||
// ===================
|
// ===================
|
||||||
// VOICE RECOGNITION SETUP
|
// VOICE RECOGNITION SETUP
|
||||||
@@ -137,22 +139,83 @@ $(document).ready(function () {
|
|||||||
.data("iddatadb", iddatadb)
|
.data("iddatadb", iddatadb)
|
||||||
.data("idquotations", idquotations);
|
.data("idquotations", idquotations);
|
||||||
|
|
||||||
|
// Precarica le matrici se iddatadb (assumendo matrici solo per iddatadb)
|
||||||
|
if (iddatadb) {
|
||||||
|
if (matrici.length === 0) {
|
||||||
|
$.ajax({
|
||||||
|
url: "get_matrici_db.php",
|
||||||
|
method: "GET",
|
||||||
|
dataType: "json",
|
||||||
|
success: function (data) {
|
||||||
|
matrici = data.value || [];
|
||||||
|
initializeGlobalSelect2();
|
||||||
loadPhoto(iddatadb, idquotations);
|
loadPhoto(iddatadb, idquotations);
|
||||||
loadExistingParts(iddatadb, idquotations);
|
loadExistingParts(iddatadb, idquotations);
|
||||||
|
},
|
||||||
const modal = new bootstrap.Modal(
|
error: function (xhr, status, error) {
|
||||||
document.getElementById("partsModal"),
|
matrici = [];
|
||||||
|
initializeGlobalSelect2();
|
||||||
|
loadPhoto(iddatadb, idquotations);
|
||||||
|
loadExistingParts(iddatadb, idquotations);
|
||||||
|
const errorMsg = $(
|
||||||
|
'<div class="alert alert-danger temp-alert" role="alert">Errore nel caricamento delle matrici: ' +
|
||||||
|
error +
|
||||||
|
" (" +
|
||||||
|
xhr.status +
|
||||||
|
")</div>",
|
||||||
);
|
);
|
||||||
|
$("#partsModal .modal-body").prepend(errorMsg);
|
||||||
|
setTimeout(function () {
|
||||||
|
errorMsg.fadeOut(500, function () {
|
||||||
|
$(this).remove();
|
||||||
|
});
|
||||||
|
}, 5000);
|
||||||
|
},
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
initializeGlobalSelect2();
|
||||||
|
loadPhoto(iddatadb, idquotations);
|
||||||
|
loadExistingParts(iddatadb, idquotations);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
loadPhoto(iddatadb, idquotations);
|
||||||
|
loadExistingParts(iddatadb, idquotations);
|
||||||
|
}
|
||||||
|
|
||||||
|
const modalElement = document.getElementById("partsModal");
|
||||||
|
if (modalElement) {
|
||||||
|
// Verifica se il modale è già stato inizializzato
|
||||||
|
let modal = bootstrap.Modal.getInstance(modalElement);
|
||||||
|
if (!modal) {
|
||||||
|
modal = new bootstrap.Modal(modalElement, {
|
||||||
|
backdrop: true,
|
||||||
|
keyboard: true,
|
||||||
|
focus: true,
|
||||||
|
});
|
||||||
|
}
|
||||||
modal.show();
|
modal.show();
|
||||||
|
} else {
|
||||||
|
console.error("Elemento modale non trovato: #partsModal");
|
||||||
|
const errorMsg = $(
|
||||||
|
'<div class="alert alert-danger temp-alert" role="alert">Errore: Modale non trovato.</div>',
|
||||||
|
);
|
||||||
|
$("body").prepend(errorMsg);
|
||||||
|
setTimeout(function () {
|
||||||
|
errorMsg.fadeOut(500, function () {
|
||||||
|
$(this).remove();
|
||||||
|
});
|
||||||
|
}, 5000);
|
||||||
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
$("#partsModal .close-btn, #partsModal").on("click", function (event) {
|
$("#partsModal .close-btn, #partsModal").on("click", function (event) {
|
||||||
if (event.target === this) {
|
if (event.target === this || $(event.target).hasClass("close-btn")) {
|
||||||
const modal = bootstrap.Modal.getInstance(
|
const modalElement = document.getElementById("partsModal");
|
||||||
document.getElementById("partsModal"),
|
const modal = bootstrap.Modal.getInstance(modalElement);
|
||||||
);
|
if (modal) {
|
||||||
modal.hide();
|
modal.hide();
|
||||||
}
|
}
|
||||||
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
$("#partsModal").on("hide.bs.modal", function (e) {
|
$("#partsModal").on("hide.bs.modal", function (e) {
|
||||||
@@ -165,7 +228,7 @@ $(document).ready(function () {
|
|||||||
});
|
});
|
||||||
|
|
||||||
$("#partsModal").on("hidden.bs.modal", function () {
|
$("#partsModal").on("hidden.bs.modal", function () {
|
||||||
// Reset global state to initial values
|
// Resetta lo stato
|
||||||
photoData = {
|
photoData = {
|
||||||
naturalWidth: 0,
|
naturalWidth: 0,
|
||||||
naturalHeight: 0,
|
naturalHeight: 0,
|
||||||
@@ -175,21 +238,33 @@ $(document).ready(function () {
|
|||||||
};
|
};
|
||||||
photoAnnotations = {};
|
photoAnnotations = {};
|
||||||
partColors = {};
|
partColors = {};
|
||||||
|
partMatrice = {};
|
||||||
selectedPartNumber = null;
|
selectedPartNumber = null;
|
||||||
unsavedChanges = false;
|
unsavedChanges = false;
|
||||||
if (fabricCanvas) {
|
if (fabricCanvas) {
|
||||||
fabricCanvas.off(); // Rimuove tutti gli eventi
|
fabricCanvas.off();
|
||||||
fabricCanvas.dispose();
|
fabricCanvas.dispose();
|
||||||
fabricCanvas = null;
|
fabricCanvas = null;
|
||||||
}
|
}
|
||||||
descriptionTextbox = null;
|
descriptionTextbox = null;
|
||||||
markerObjects = {};
|
markerObjects = {};
|
||||||
// Clear UI elements
|
matrici = [];
|
||||||
$("#photoSelectorContainer").empty().hide();
|
$("#photoSelectorContainer").empty().hide();
|
||||||
$("#samplePhoto").attr("src", "");
|
$("#samplePhoto").attr("src", "");
|
||||||
$("#partsTableBody").empty();
|
$("#partsTableBody").empty();
|
||||||
// Remove any temporary messages
|
$("#global-matrice").empty();
|
||||||
$(".temp-alert").remove();
|
$(".temp-alert").remove();
|
||||||
|
|
||||||
|
// Rimuovi manualmente il backdrop e ripristina il body
|
||||||
|
const modalElement = document.getElementById("partsModal");
|
||||||
|
const modal = bootstrap.Modal.getInstance(modalElement);
|
||||||
|
if (modal) {
|
||||||
|
modal.dispose(); // Distrugge l'istanza del modale
|
||||||
|
}
|
||||||
|
$(".modal-backdrop").remove();
|
||||||
|
$("body").removeClass("modal-open");
|
||||||
|
$("body").css("padding-right", "");
|
||||||
|
$(":focus").blur();
|
||||||
});
|
});
|
||||||
|
|
||||||
// ===================
|
// ===================
|
||||||
@@ -535,6 +610,7 @@ $(document).ready(function () {
|
|||||||
if (response.success) {
|
if (response.success) {
|
||||||
$row.remove();
|
$row.remove();
|
||||||
delete partColors[partNumber];
|
delete partColors[partNumber];
|
||||||
|
delete partMatrice[partNumber];
|
||||||
if (markerObjects[partNumber]) {
|
if (markerObjects[partNumber]) {
|
||||||
fabricCanvas.remove(markerObjects[partNumber]);
|
fabricCanvas.remove(markerObjects[partNumber]);
|
||||||
delete markerObjects[partNumber];
|
delete markerObjects[partNumber];
|
||||||
@@ -576,6 +652,7 @@ $(document).ready(function () {
|
|||||||
} else {
|
} else {
|
||||||
$row.remove();
|
$row.remove();
|
||||||
delete partColors[partNumber];
|
delete partColors[partNumber];
|
||||||
|
delete partMatrice[partNumber];
|
||||||
if (markerObjects[partNumber]) {
|
if (markerObjects[partNumber]) {
|
||||||
fabricCanvas.remove(markerObjects[partNumber]);
|
fabricCanvas.remove(markerObjects[partNumber]);
|
||||||
delete markerObjects[partNumber];
|
delete markerObjects[partNumber];
|
||||||
@@ -717,6 +794,9 @@ $(document).ready(function () {
|
|||||||
</tr>`;
|
</tr>`;
|
||||||
$("#partsTableBody").append(newRow);
|
$("#partsTableBody").append(newRow);
|
||||||
partColors[part.part_number] = defaultColor;
|
partColors[part.part_number] = defaultColor;
|
||||||
|
if (part.idmatrice) {
|
||||||
|
partMatrice[part.part_number] = part.idmatrice;
|
||||||
|
}
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
addNewRow(1);
|
addNewRow(1);
|
||||||
@@ -743,6 +823,163 @@ $(document).ready(function () {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Funzione per inizializzare Select2 sulle tendine delle matrici
|
||||||
|
function initializeSelect2($select, partNumber, partId, idmatrice) {
|
||||||
|
if (typeof $.fn.select2 === "undefined") {
|
||||||
|
$select.replaceWith(
|
||||||
|
'<input type="text" class="form-control form-control-sm" placeholder="Select2 non disponibile" disabled>',
|
||||||
|
);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const options = matrici.map(function (matrice) {
|
||||||
|
return {
|
||||||
|
id: matrice.IdMatrice,
|
||||||
|
text: matrice.NomeMatrice, // Updated to use NomeMatrice
|
||||||
|
};
|
||||||
|
});
|
||||||
|
|
||||||
|
$select.select2({
|
||||||
|
placeholder: "Seleziona matrice",
|
||||||
|
allowClear: true,
|
||||||
|
data: options,
|
||||||
|
dropdownParent: $("#partsModal"),
|
||||||
|
matcher: function (params, data) {
|
||||||
|
if (!params.term || params.term.length < 3) {
|
||||||
|
return data;
|
||||||
|
}
|
||||||
|
const term = params.term.toUpperCase();
|
||||||
|
if (data.text.toUpperCase().indexOf(term) >= 0) {
|
||||||
|
return data;
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
if (partId && partId !== "new" && idmatrice) {
|
||||||
|
const matrice = matrici.find((m) => m.IdMatrice == idmatrice);
|
||||||
|
if (matrice) {
|
||||||
|
const option = new Option(
|
||||||
|
matrice.NomeMatrice, // Updated to use NomeMatrice
|
||||||
|
matrice.IdMatrice,
|
||||||
|
true,
|
||||||
|
true,
|
||||||
|
);
|
||||||
|
$select.append(option).trigger("change");
|
||||||
|
partMatrice[partNumber] = matrice.IdMatrice;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
$select.on("change", function () {
|
||||||
|
const idmatrice = $(this).val();
|
||||||
|
const $listItem = $(this).closest("li");
|
||||||
|
const $saveStatus = $listItem.find(".save-status");
|
||||||
|
const $saveLoading = $listItem.find(".save-loading");
|
||||||
|
|
||||||
|
partMatrice[partNumber] = idmatrice || null;
|
||||||
|
|
||||||
|
if (partId && partId !== "new") {
|
||||||
|
$saveLoading.show();
|
||||||
|
$saveStatus.hide();
|
||||||
|
|
||||||
|
const iddatadb = $("#partsModal").data("iddatadb");
|
||||||
|
const idquotations = $("#partsModal").data("idquotations");
|
||||||
|
const endpoint = idquotations
|
||||||
|
? "save_matrice_quotation.php"
|
||||||
|
: "save_matrice.php";
|
||||||
|
const data = idquotations
|
||||||
|
? { idquotations: idquotations }
|
||||||
|
: { iddatadb: iddatadb };
|
||||||
|
|
||||||
|
$.ajax({
|
||||||
|
url: endpoint,
|
||||||
|
method: "POST",
|
||||||
|
data: JSON.stringify({
|
||||||
|
...data,
|
||||||
|
parts: [
|
||||||
|
{
|
||||||
|
id: partId,
|
||||||
|
idmatrice: idmatrice || null,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
}),
|
||||||
|
contentType: "application/json",
|
||||||
|
success: function (response) {
|
||||||
|
if (response.success) {
|
||||||
|
$saveLoading.hide();
|
||||||
|
$saveStatus.show();
|
||||||
|
setTimeout(() => $saveStatus.hide(), 2000);
|
||||||
|
} else {
|
||||||
|
const errorMsg = $(
|
||||||
|
'<div class="alert alert-danger temp-alert" role="alert">Errore nel salvataggio della matrice: ' +
|
||||||
|
response.message +
|
||||||
|
"</div>",
|
||||||
|
);
|
||||||
|
$("#partsModal .modal-body").prepend(errorMsg);
|
||||||
|
setTimeout(function () {
|
||||||
|
errorMsg.fadeOut(500, function () {
|
||||||
|
$(this).remove();
|
||||||
|
});
|
||||||
|
}, 5000);
|
||||||
|
$saveLoading.hide();
|
||||||
|
}
|
||||||
|
},
|
||||||
|
error: function (xhr, status, error) {
|
||||||
|
const errorMsg = $(
|
||||||
|
'<div class="alert alert-danger temp-alert" role="alert">Errore nel salvataggio della matrice: ' +
|
||||||
|
error +
|
||||||
|
" (" +
|
||||||
|
xhr.status +
|
||||||
|
")</div>",
|
||||||
|
);
|
||||||
|
$("#partsModal .modal-body").prepend(errorMsg);
|
||||||
|
setTimeout(function () {
|
||||||
|
errorMsg.fadeOut(500, function () {
|
||||||
|
$(this).remove();
|
||||||
|
});
|
||||||
|
}, 5000);
|
||||||
|
$saveLoading.hide();
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// Funzione per inizializzare Select2 sul dropdown globale
|
||||||
|
function initializeGlobalSelect2() {
|
||||||
|
const $select = $("#global-matrice");
|
||||||
|
if (typeof $.fn.select2 === "undefined") {
|
||||||
|
$select.replaceWith(
|
||||||
|
'<input type="text" class="form-control form-control-sm" placeholder="Select2 non disponibile" disabled>',
|
||||||
|
);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const options = matrici.map(function (matrice) {
|
||||||
|
return {
|
||||||
|
id: matrice.IdMatrice,
|
||||||
|
text: matrice.NomeMatrice, // Updated to use NomeMatrice
|
||||||
|
};
|
||||||
|
});
|
||||||
|
|
||||||
|
$select.select2({
|
||||||
|
placeholder: "Seleziona matrice globale",
|
||||||
|
allowClear: true,
|
||||||
|
data: options,
|
||||||
|
dropdownParent: $("#partsModal"),
|
||||||
|
matcher: function (params, data) {
|
||||||
|
if (!params.term || params.term.length < 3) {
|
||||||
|
return data;
|
||||||
|
}
|
||||||
|
const term = params.term.toUpperCase();
|
||||||
|
if (data.text.toUpperCase().indexOf(term) >= 0) {
|
||||||
|
return data;
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
// ===================
|
// ===================
|
||||||
// PARTS LIST
|
// PARTS LIST
|
||||||
// ===================
|
// ===================
|
||||||
@@ -763,6 +1000,7 @@ $(document).ready(function () {
|
|||||||
$("#partsTableBody tr").each(function () {
|
$("#partsTableBody tr").each(function () {
|
||||||
const partNumber = $(this).find(".part-number").val();
|
const partNumber = $(this).find(".part-number").val();
|
||||||
const partDescription = $(this).find(".part-description").val();
|
const partDescription = $(this).find(".part-description").val();
|
||||||
|
const partId = $(this).data("part-id");
|
||||||
const partColor =
|
const partColor =
|
||||||
partColors[partNumber] ||
|
partColors[partNumber] ||
|
||||||
(partDescription.startsWith("Mix") ? "#0000ff" : "#ff0000");
|
(partDescription.startsWith("Mix") ? "#0000ff" : "#ff0000");
|
||||||
@@ -778,17 +1016,30 @@ $(document).ready(function () {
|
|||||||
)
|
)
|
||||||
.join("");
|
.join("");
|
||||||
const listItem = `
|
const listItem = `
|
||||||
<li class="list-group-item" data-part-number="${partNumber}">
|
<li class="list-group-item" data-part-number="${partNumber}" data-part-id="${partId}">
|
||||||
${partNumber} - ${partDescription}
|
${partNumber} - ${partDescription}
|
||||||
<div style="display: flex; align-items: center;">
|
<div style="display: flex; align-items: center;">
|
||||||
|
<button type="button" class="btn btn-primary btn-sm propagate-matrice-btn" style="padding: 0.1rem 0.3rem; font-size: 0.8rem; margin-right: 3px;"><i class="fas fa-arrow-right fa-xs"></i></button>
|
||||||
|
<select class="part-matrice" style="width: 250px !important; margin-right: 10px;"></select>
|
||||||
<button type="button" class="btn btn-success btn-sm add-to-mix-btn" style="padding: 0.1rem 0.3rem; font-size: 0.8rem;"><i class="fas fa-plus fa-xs"></i></button>
|
<button type="button" class="btn btn-success btn-sm add-to-mix-btn" style="padding: 0.1rem 0.3rem; font-size: 0.8rem;"><i class="fas fa-plus fa-xs"></i></button>
|
||||||
<div class="color-picker-container">
|
<div class="color-picker-container">
|
||||||
<div class="color-option selected-color" style="background-color: ${partColor}; margin-left: 5px;"></div>
|
<div class="color-option selected-color" style="background-color: ${partColor}; margin-left: 5px;"></div>
|
||||||
<div class="color-picker">${colorOptions}</div>
|
<div class="color-picker">${colorOptions}</div>
|
||||||
</div>
|
</div>
|
||||||
|
<span class="save-status text-success" style="display: none; margin-left: 5px;"><i class="fas fa-check fa-xs"></i></span>
|
||||||
|
<span class="save-loading text-warning" style="display: none; margin-left: 5px;"><i class="fas fa-spinner fa-spin fa-xs"></i></span>
|
||||||
</div>
|
</div>
|
||||||
</li>`;
|
</li>`;
|
||||||
$("#partsList").append(listItem);
|
$("#partsList").append(listItem);
|
||||||
|
const $select = $("#partsList").find(
|
||||||
|
`li[data-part-number="${partNumber}"] .part-matrice`,
|
||||||
|
);
|
||||||
|
initializeSelect2(
|
||||||
|
$select,
|
||||||
|
partNumber,
|
||||||
|
partId,
|
||||||
|
partMatrice[partNumber],
|
||||||
|
);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -862,11 +1113,32 @@ $(document).ready(function () {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
$(document).on("click", ".propagate-matrice-btn", function () {
|
||||||
|
const $listItem = $(this).closest("li");
|
||||||
|
const globalVal = $("#global-matrice").val();
|
||||||
|
if (globalVal) {
|
||||||
|
$listItem.find(".part-matrice").val(globalVal).trigger("change");
|
||||||
|
} else {
|
||||||
|
const errorMsg = $(
|
||||||
|
'<div class="alert alert-danger temp-alert" role="alert">Seleziona una matrice globale prima di propagare.</div>',
|
||||||
|
);
|
||||||
|
$("#partsModal .modal-body").prepend(errorMsg);
|
||||||
|
setTimeout(function () {
|
||||||
|
errorMsg.fadeOut(500, function () {
|
||||||
|
$(this).remove();
|
||||||
|
});
|
||||||
|
}, 5000);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
$("#partsList").on("click", "li", function (e) {
|
$("#partsList").on("click", "li", function (e) {
|
||||||
if (
|
if (
|
||||||
$(e.target).hasClass("add-to-mix-btn") ||
|
$(e.target).hasClass("add-to-mix-btn") ||
|
||||||
$(e.target).hasClass("color-option") ||
|
$(e.target).hasClass("color-option") ||
|
||||||
$(e.target).closest(".color-picker-container").length
|
$(e.target).closest(".color-picker-container").length ||
|
||||||
|
$(e.target).hasClass("part-matrice") ||
|
||||||
|
$(e.target).closest(".select2-container").length ||
|
||||||
|
$(e.target).hasClass("propagate-matrice-btn")
|
||||||
)
|
)
|
||||||
return;
|
return;
|
||||||
selectedPartNumber = $(this).data("part-number");
|
selectedPartNumber = $(this).data("part-number");
|
||||||
@@ -892,6 +1164,7 @@ $(document).ready(function () {
|
|||||||
? { idquotations: idquotations }
|
? { idquotations: idquotations }
|
||||||
: { iddatadb: iddatadb };
|
: { iddatadb: iddatadb };
|
||||||
let newPartColors = {};
|
let newPartColors = {};
|
||||||
|
let newPartMatrice = {};
|
||||||
let newMarkerObjects = {};
|
let newMarkerObjects = {};
|
||||||
|
|
||||||
let partsData = $rows
|
let partsData = $rows
|
||||||
@@ -908,6 +1181,7 @@ $(document).ready(function () {
|
|||||||
partsData.forEach((part, index) => {
|
partsData.forEach((part, index) => {
|
||||||
const newNumber = index + 1;
|
const newNumber = index + 1;
|
||||||
newPartColors[newNumber] = partColors[part.partNumber] || "#ff0000";
|
newPartColors[newNumber] = partColors[part.partNumber] || "#ff0000";
|
||||||
|
newPartMatrice[newNumber] = partMatrice[part.partNumber] || null;
|
||||||
if (markerObjects[part.partNumber]) {
|
if (markerObjects[part.partNumber]) {
|
||||||
newMarkerObjects[newNumber] = markerObjects[part.partNumber];
|
newMarkerObjects[newNumber] = markerObjects[part.partNumber];
|
||||||
}
|
}
|
||||||
@@ -934,6 +1208,7 @@ $(document).ready(function () {
|
|||||||
}
|
}
|
||||||
|
|
||||||
partColors = newPartColors;
|
partColors = newPartColors;
|
||||||
|
partMatrice = newPartMatrice;
|
||||||
markerObjects = newMarkerObjects;
|
markerObjects = newMarkerObjects;
|
||||||
|
|
||||||
const partsToSave = partsData.map((part) => ({
|
const partsToSave = partsData.map((part) => ({
|
||||||
@@ -941,6 +1216,7 @@ $(document).ready(function () {
|
|||||||
part_number: part.partNumber,
|
part_number: part.partNumber,
|
||||||
part_description: part.partDescription,
|
part_description: part.partDescription,
|
||||||
mix: part.partDescription.startsWith("Mix") ? "Y" : "N",
|
mix: part.partDescription.startsWith("Mix") ? "Y" : "N",
|
||||||
|
idmatrice: partMatrice[part.partNumber] || null,
|
||||||
}));
|
}));
|
||||||
|
|
||||||
$.ajax({
|
$.ajax({
|
||||||
@@ -1157,7 +1433,7 @@ $(document).ready(function () {
|
|||||||
width: annotations.descriptionSize.width,
|
width: annotations.descriptionSize.width,
|
||||||
scaleX: 1,
|
scaleX: 1,
|
||||||
scaleY: 1,
|
scaleY: 1,
|
||||||
backgroundColor: "rgba(255, 255, 255, 0.8)",
|
backgroundColor: "rgba(255, 255, 255, 0)", // Changed to fully transparent
|
||||||
fontFamily: "Arial",
|
fontFamily: "Arial",
|
||||||
fontSize: 24,
|
fontSize: 24,
|
||||||
fill: "#000000",
|
fill: "#000000",
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
@@ -108,7 +108,7 @@ try {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Recupera routine dal template
|
// Recupera routine dal template
|
||||||
$stmt = $pdo->prepare("SELECT idroutine FROM excel_templates WHERE id = ?");
|
$stmt = $pdo->prepare("SELECT idroutine, idclient FROM excel_templates WHERE id = ?");
|
||||||
$stmt->execute([$template_id]);
|
$stmt->execute([$template_id]);
|
||||||
$template = $stmt->fetch(PDO::FETCH_ASSOC);
|
$template = $stmt->fetch(PDO::FETCH_ASSOC);
|
||||||
|
|
||||||
@@ -133,6 +133,9 @@ try {
|
|||||||
error_log("Nessuna routine associata al template {$template_id}");
|
error_log("Nessuna routine associata al template {$template_id}");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Aggiungi idclient alla risposta
|
||||||
|
$response['idclient'] = $template['idclient'] ?? null;
|
||||||
|
|
||||||
// Salva i dati in sessione
|
// Salva i dati in sessione
|
||||||
$_SESSION['excel_data'] = $excelData;
|
$_SESSION['excel_data'] = $excelData;
|
||||||
$_SESSION['template_id'] = $template_id;
|
$_SESSION['template_id'] = $template_id;
|
||||||
|
|||||||
@@ -399,14 +399,18 @@ if (isset($_GET['edit_id'])) {
|
|||||||
<a href="javaScript:;" class="back-to-top"><i class='bx bxs-up-arrow-alt'></i></a>
|
<a href="javaScript:;" class="back-to-top"><i class='bx bxs-up-arrow-alt'></i></a>
|
||||||
<?php include('include/footer.php'); ?>
|
<?php include('include/footer.php'); ?>
|
||||||
</div>
|
</div>
|
||||||
<?php include('modal_parts.php'); ?>
|
<div id="partsModalContainer"></div>
|
||||||
<?php include('photos_functions.php'); ?>
|
<div id="annotationsModalContainer"></div>
|
||||||
|
<?php include 'photos_functions.php'; ?>
|
||||||
|
|
||||||
<?php include('jsinclude.php'); ?>
|
<?php include('jsinclude.php'); ?>
|
||||||
<script src="https://code.jquery.com/jquery-3.6.0.min.js"></script>
|
<script src="https://code.jquery.com/jquery-3.6.0.min.js"></script>
|
||||||
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/js/bootstrap.bundle.min.js"></script>
|
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/js/bootstrap.bundle.min.js"></script>
|
||||||
<script src="https://cdn.datatables.net/1.13.4/js/jquery.dataTables.min.js"></script>
|
<script src="https://cdn.datatables.net/1.13.4/js/jquery.dataTables.min.js"></script>
|
||||||
<script src="photos.js"></script>
|
<script src="photos.js"></script>
|
||||||
<script src="parts.js"></script>
|
<script src="annotationsModal.js"></script>
|
||||||
|
<script src="partsTable.js"></script>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
document.addEventListener("DOMContentLoaded", function() {
|
document.addEventListener("DOMContentLoaded", function() {
|
||||||
// Mostra messaggi di stato se presenti
|
// Mostra messaggi di stato se presenti
|
||||||
@@ -423,6 +427,45 @@ if (isset($_GET['edit_id'])) {
|
|||||||
}, 5000);
|
}, 5000);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
$(document).on('click', '.parts-btn', function() {
|
||||||
|
const idquotations = $(this).data('idquotations');
|
||||||
|
$.ajax({
|
||||||
|
url: 'modal_partsTable.php',
|
||||||
|
method: 'GET',
|
||||||
|
data: {
|
||||||
|
idquotations: idquotations
|
||||||
|
},
|
||||||
|
success: function(response) {
|
||||||
|
$('#partsModalContainer').html(response);
|
||||||
|
const modalElement = document.getElementById('partsModal');
|
||||||
|
if (!modalElement) return;
|
||||||
|
$("#trfHeader").text(`Quotation #${idquotations}`);
|
||||||
|
$("#partsModal").data("idquotations", idquotations);
|
||||||
|
let modal = bootstrap.Modal.getInstance(modalElement) || new bootstrap.Modal(modalElement, {
|
||||||
|
backdrop: true
|
||||||
|
});
|
||||||
|
modal.show();
|
||||||
|
if (typeof window.loadParts === 'function') window.loadParts(null, idquotations);
|
||||||
|
},
|
||||||
|
error: function(xhr, status, error) {
|
||||||
|
alert('Errore nel caricamento del modale: ' + error);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
$(document).on('hidden.bs.modal', '#partsModal', function() {
|
||||||
|
$('#partsModalContainer').empty();
|
||||||
|
$('.modal-backdrop').remove();
|
||||||
|
$('body').removeClass('modal-open').css('padding-right', '');
|
||||||
|
});
|
||||||
|
|
||||||
|
$(document).on('hidden.bs.modal', '#annotationsModal', function() {
|
||||||
|
$('#annotationsModalContainer').empty();
|
||||||
|
$('.modal-backdrop').remove();
|
||||||
|
$('body').removeClass('modal-open').css('padding-right', '');
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
// Inizializza DataTables se non siamo in modalità modifica
|
// Inizializza DataTables se non siamo in modalità modifica
|
||||||
if (!document.querySelector('#editForm')) {
|
if (!document.querySelector('#editForm')) {
|
||||||
$('#quotationsTable').DataTable({
|
$('#quotationsTable').DataTable({
|
||||||
@@ -524,12 +567,7 @@ if (isset($_GET['edit_id'])) {
|
|||||||
</script>
|
</script>
|
||||||
|
|
||||||
<!-- Modale per le foto in quotations.php -->
|
<!-- Modale per le foto in quotations.php -->
|
||||||
<div class="modal" id="photosModal">
|
|
||||||
<div class="modal-content">
|
|
||||||
<span class="close-btn">×</span>
|
|
||||||
<div class="popup-content"></div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
</body>
|
</body>
|
||||||
|
|
||||||
|
|||||||
@@ -11,13 +11,52 @@ try {
|
|||||||
}
|
}
|
||||||
|
|
||||||
$iddatadb = intval($_POST['iddatadb']);
|
$iddatadb = intval($_POST['iddatadb']);
|
||||||
|
$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) ----------------
|
||||||
|
|
||||||
|
// 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->execute([$iddatadb]);
|
||||||
|
$tplRow = $stmtTpl->fetch(PDO::FETCH_ASSOC);
|
||||||
|
|
||||||
|
$templateId = isset($tplRow['templateid']) ? (int)$tplRow['templateid'] : 0;
|
||||||
|
if ($templateId <= 0) {
|
||||||
|
throw new Exception("Template non trovato per iddatadb=$iddatadb");
|
||||||
|
}
|
||||||
|
|
||||||
|
// 2) Recupera elenco fixed fields visibili per quel template
|
||||||
|
$fxStmt = $pdo->prepare("
|
||||||
|
SELECT fixed_field_key, data_type, is_required, default_value
|
||||||
|
FROM template_fixed_mapping
|
||||||
|
WHERE template_id = ? AND is_visible_import = 1
|
||||||
|
");
|
||||||
|
$fxStmt->execute([$templateId]);
|
||||||
|
$fixedList = $fxStmt->fetchAll(PDO::FETCH_ASSOC);
|
||||||
|
|
||||||
|
// 3) Crea whitelist: key => metadata
|
||||||
|
$fixedWhitelist = [];
|
||||||
|
foreach ($fixedList as $fx) {
|
||||||
|
$k = (string)$fx['fixed_field_key'];
|
||||||
|
|
||||||
|
// sicurezza: nome colonna ammesso solo se "safe" (no spazi, no caratteri strani)
|
||||||
|
if (!preg_match('/^[a-zA-Z0-9_]+$/', $k)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
$fixedWhitelist[$k] = [
|
||||||
|
'data_type' => (string)$fx['data_type'], // INT | DATE
|
||||||
|
'is_required' => (int)$fx['is_required'],
|
||||||
|
'default_value' => $fx['default_value'] ?? null
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
$data = $_POST;
|
$data = $_POST;
|
||||||
$details = [];
|
$details = [];
|
||||||
|
|
||||||
// 1. POST-დან ამოვიღოთ მხოლოდ details
|
// 1. Estrarre i dettagli da POST
|
||||||
foreach ($data as $key => $value) {
|
foreach ($data as $key => $value) {
|
||||||
if (preg_match('/^details(\d+)field_value$/', $key, $matches)) {
|
if (preg_match('/^details(\d+)field_value$/', $key, $matches)) {
|
||||||
$id = $matches[1];
|
$id = $matches[1];
|
||||||
@@ -25,16 +64,15 @@ try {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// 2. DB-დან წამოვიღოთ არსებული მნიშვნელობები
|
// 2. Recupera i valori esistenti da import_data_details
|
||||||
$stmt = $pdo->prepare("SELECT mapping_id, field_value FROM import_data_details WHERE id = ?");
|
$stmt = $pdo->prepare("SELECT mapping_id, field_value FROM import_data_details WHERE id = ?");
|
||||||
$stmt->execute([$iddatadb]);
|
$stmt->execute([$iddatadb]);
|
||||||
|
|
||||||
$currentValues = [];
|
$currentValues = [];
|
||||||
while ($row = $stmt->fetch(PDO::FETCH_ASSOC)) {
|
while ($row = $stmt->fetch(PDO::FETCH_ASSOC)) {
|
||||||
$currentValues[$row['mapping_id']] = $row['field_value'];
|
$currentValues[$row['mapping_id']] = $row['field_value'];
|
||||||
}
|
}
|
||||||
|
|
||||||
// 3. შევადაროთ POST-ს და DB-ს
|
// 3. Confronta i valori nuovi con quelli esistenti
|
||||||
$changed = [];
|
$changed = [];
|
||||||
foreach ($details as $id => $newValue) {
|
foreach ($details as $id => $newValue) {
|
||||||
$oldValue = $currentValues[$id] ?? null;
|
$oldValue = $currentValues[$id] ?? null;
|
||||||
@@ -46,7 +84,7 @@ try {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// 4. თუ არის ცვლილებები → UPDATE
|
// 4. Aggiorna i dettagli se ci sono modifiche
|
||||||
if (!empty($changed)) {
|
if (!empty($changed)) {
|
||||||
$updateStmt = $pdo->prepare("
|
$updateStmt = $pdo->prepare("
|
||||||
UPDATE import_data_details
|
UPDATE import_data_details
|
||||||
@@ -61,15 +99,62 @@ try {
|
|||||||
':mappingId' => $mappingId
|
':mappingId' => $mappingId
|
||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
$response['success'] = true;
|
// 5. Aggiorna datadb: idclient + FIXED FIELDS (whitelisted)
|
||||||
$response['message'] = "Updated successfully";
|
$setParts = [];
|
||||||
$response['changed'] = $changed; // Debug / optional
|
$params = [];
|
||||||
|
|
||||||
|
// 5a) idclient (se presente)
|
||||||
|
if (isset($idclient)) {
|
||||||
|
$setParts[] = "idclient = ?";
|
||||||
|
$params[] = $idclient;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 5b) fixed fields dal POST (solo quelli presenti nella whitelist del template)
|
||||||
|
foreach ($fixedWhitelist as $col => $meta) {
|
||||||
|
if (!array_key_exists($col, $_POST)) {
|
||||||
|
continue; // non inviato dal form
|
||||||
|
}
|
||||||
|
|
||||||
|
$val = $_POST[$col];
|
||||||
|
|
||||||
|
// Normalizzazione per tipo
|
||||||
|
if ($meta['data_type'] === 'DATE') {
|
||||||
|
$val = trim((string)$val);
|
||||||
|
$val = ($val === '') ? null : $val; // atteso formato Y-m-d
|
||||||
|
} else { // INT
|
||||||
|
$val = trim((string)$val);
|
||||||
|
$val = ($val === '') ? null : (int)$val;
|
||||||
|
}
|
||||||
|
|
||||||
|
$setParts[] = "`$col` = ?";
|
||||||
|
$params[] = $val;
|
||||||
|
}
|
||||||
|
|
||||||
|
// esegui update solo se c'è qualcosa da aggiornare
|
||||||
|
if (!empty($setParts)) {
|
||||||
|
$params[] = $iddatadb;
|
||||||
|
$sqlUpd = "UPDATE datadb SET " . implode(", ", $setParts) . " WHERE iddatadb = ?";
|
||||||
|
$updStmt = $pdo->prepare($sqlUpd);
|
||||||
|
$updStmt->execute($params);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Messaggio risposta (mantengo la tua logica ma includo fixed)
|
||||||
|
if (!empty($setParts) && !empty($changed)) {
|
||||||
|
$response['message'] = "Updated details and datadb fields successfully";
|
||||||
|
} elseif (!empty($setParts)) {
|
||||||
|
$response['message'] = "Updated datadb fields successfully";
|
||||||
|
} elseif (!empty($changed)) {
|
||||||
|
$response['message'] = "Updated details successfully";
|
||||||
} else {
|
} else {
|
||||||
$response['success'] = true;
|
|
||||||
$response['message'] = "No changes found";
|
$response['message'] = "No changes found";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
$response['success'] = true;
|
||||||
|
$response['changed'] = $changed; // Debug / optional
|
||||||
|
|
||||||
} catch (Exception $e) {
|
} catch (Exception $e) {
|
||||||
$response['success'] = false;
|
$response['success'] = false;
|
||||||
$response['message'] = $e->getMessage();
|
$response['message'] = $e->getMessage();
|
||||||
|
|||||||
@@ -0,0 +1,39 @@
|
|||||||
|
<?php
|
||||||
|
header('Content-Type: application/json');
|
||||||
|
include('include/headscript.php');
|
||||||
|
|
||||||
|
$dbHandler = DBHandlerSelect::getInstance();
|
||||||
|
$pdo = $dbHandler->getConnection();
|
||||||
|
|
||||||
|
$data = json_decode(file_get_contents('php://input'), true);
|
||||||
|
|
||||||
|
$iddatadb = $data['iddatadb'] ?? null;
|
||||||
|
$parts = $data['parts'] ?? [];
|
||||||
|
|
||||||
|
if (!$iddatadb || empty($parts)) {
|
||||||
|
echo json_encode(['success' => false, 'message' => 'Dati mancanti']);
|
||||||
|
exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
$part = $parts[0];
|
||||||
|
$partId = $part['id'] ?? null;
|
||||||
|
$idmatrice = $part['idmatrice'] ?? null;
|
||||||
|
|
||||||
|
if (!$partId) {
|
||||||
|
echo json_encode(['success' => false, 'message' => 'ID parte mancante']);
|
||||||
|
exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
$stmt = $pdo->prepare("UPDATE identification_parts
|
||||||
|
SET idmatrice = :idmatrice,
|
||||||
|
updated_at = NOW()
|
||||||
|
WHERE id = :id");
|
||||||
|
$stmt->execute([
|
||||||
|
':id' => $partId,
|
||||||
|
':idmatrice' => $idmatrice // Può essere NULL
|
||||||
|
]);
|
||||||
|
echo json_encode(['success' => true, 'message' => 'Matrice aggiornata con successo']);
|
||||||
|
} catch (PDOException $e) {
|
||||||
|
echo json_encode(['success' => false, 'message' => 'Errore nel salvataggio della matrice: ' . $e->getMessage()]);
|
||||||
|
}
|
||||||
@@ -15,46 +15,64 @@ if (!$iddatadb || empty($parts)) {
|
|||||||
exit;
|
exit;
|
||||||
}
|
}
|
||||||
|
|
||||||
$part = $parts[0];
|
try {
|
||||||
$partId = $part['id'] ?? null; // part_id თუ არსებობს
|
$pdo->beginTransaction();
|
||||||
$partNumber = $part['part_number'] ?? null;
|
$results = [];
|
||||||
$partDescription = $part['part_description'] ?? '';
|
|
||||||
$mix = $part['mix'] ?? 'N';
|
|
||||||
|
|
||||||
if ($partDescription) {
|
foreach ($parts as $part) {
|
||||||
try {
|
$partId = $part['id'] ?? null;
|
||||||
|
$partNumber = $part['part_number'] ?? null;
|
||||||
|
$partDescription = $part['part_description'] ?? '';
|
||||||
|
$mix = $part['mix'] ?? 'N';
|
||||||
|
$idmatrice = $part['idmatrice'] ?? null;
|
||||||
|
$note = $part['note'] ?? null;
|
||||||
|
$dateexpiry = $part['dateexpiry'] ?? null;
|
||||||
|
|
||||||
|
if ($partDescription || $note || $dateexpiry) {
|
||||||
if ($partId) {
|
if ($partId) {
|
||||||
// UPDATE თუ უკვე არსებობს part
|
// UPDATE se la parte esiste
|
||||||
$stmt = $pdo->prepare("UPDATE identification_parts
|
$stmt = $pdo->prepare("UPDATE identification_parts
|
||||||
SET part_number = :part_number,
|
SET part_number = :part_number,
|
||||||
part_description = :part_description,
|
part_description = :part_description,
|
||||||
mix = :mix,
|
mix = :mix,
|
||||||
|
idmatrice = :idmatrice,
|
||||||
|
note = :note,
|
||||||
|
dateexpiry = :dateexpiry,
|
||||||
updated_at = NOW()
|
updated_at = NOW()
|
||||||
WHERE id = :id");
|
WHERE id = :id");
|
||||||
$stmt->execute([
|
$stmt->execute([
|
||||||
':id' => $partId,
|
':id' => $partId,
|
||||||
':part_number' => $partNumber,
|
':part_number' => $partNumber,
|
||||||
':part_description' => $partDescription,
|
':part_description' => $partDescription,
|
||||||
':mix' => $mix
|
':mix' => $mix,
|
||||||
|
':idmatrice' => $idmatrice,
|
||||||
|
':note' => $note,
|
||||||
|
':dateexpiry' => $dateexpiry,
|
||||||
]);
|
]);
|
||||||
echo json_encode(['success' => true, 'part_id' => $partId, 'part_number'=>$partNumber, 'message' => 'Parte aggiornata con successo']);
|
$results[] = ['part_id' => $partId, 'part_number' => $partNumber, 'message' => 'Parte aggiornata con successo'];
|
||||||
} else {
|
} else {
|
||||||
// INSERT თუ ახალია
|
// INSERT per nuova parte
|
||||||
$stmt = $pdo->prepare("INSERT INTO identification_parts
|
$stmt = $pdo->prepare("INSERT INTO identification_parts
|
||||||
(iddatadb, part_number, part_description, mix, created_at, updated_at)
|
(iddatadb, part_number, part_description, mix, idmatrice, note, dateexpiry, created_at, updated_at)
|
||||||
VALUES (:iddatadb, :part_number, :part_description, :mix, NOW(), NOW())");
|
VALUES (:iddatadb, :part_number, :part_description, :mix, :idmatrice, :note, :dateexpiry, NOW(), NOW())");
|
||||||
$stmt->execute([
|
$stmt->execute([
|
||||||
':iddatadb' => $iddatadb,
|
':iddatadb' => $iddatadb,
|
||||||
':part_number' => $partNumber,
|
':part_number' => $partNumber,
|
||||||
':part_description' => $partDescription,
|
':part_description' => $partDescription,
|
||||||
':mix' => $mix
|
':mix' => $mix,
|
||||||
|
':idmatrice' => $idmatrice,
|
||||||
|
':note' => $note,
|
||||||
|
':dateexpiry' => $dateexpiry,
|
||||||
]);
|
]);
|
||||||
$newId = $pdo->lastInsertId();
|
$newId = $pdo->lastInsertId();
|
||||||
echo json_encode(['success' => true, 'part_id' => $newId, 'part_number'=>$partNumber, 'message' => 'Parte salvata con successo']);
|
$results[] = ['part_id' => $newId, 'part_number' => $partNumber, 'message' => 'Parte salvata con successo'];
|
||||||
}
|
}
|
||||||
} catch (PDOException $e) {
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
$pdo->commit();
|
||||||
|
echo json_encode(['success' => true, 'results' => $results]);
|
||||||
|
} catch (PDOException $e) {
|
||||||
|
$pdo->rollBack();
|
||||||
echo json_encode(['success' => false, 'message' => 'Errore nel salvataggio: ' . $e->getMessage()]);
|
echo json_encode(['success' => false, 'message' => 'Errore nel salvataggio: ' . $e->getMessage()]);
|
||||||
}
|
|
||||||
} else {
|
|
||||||
echo json_encode(['success' => false, 'message' => 'Descrizione mancante']);
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,48 @@
|
|||||||
|
<?php
|
||||||
|
header('Content-Type: application/json');
|
||||||
|
include('include/headscript.php');
|
||||||
|
|
||||||
|
$dbHandler = DBHandlerSelect::getInstance();
|
||||||
|
$pdo = $dbHandler->getConnection();
|
||||||
|
|
||||||
|
$data = json_decode(file_get_contents('php://input'), true);
|
||||||
|
|
||||||
|
$iddatadb = $data['iddatadb'] ?? null;
|
||||||
|
$partIds = $data['partIds'] ?? [];
|
||||||
|
$photoList = $data['photoList'] ?? null;
|
||||||
|
|
||||||
|
if ($iddatadb) {
|
||||||
|
if (count($partIds) != 0) {
|
||||||
|
$idList = array_values(array_map('intval', $partIds));
|
||||||
|
$placeholders = [];
|
||||||
|
$parameters = [':iddatadb' => $iddatadb];
|
||||||
|
|
||||||
|
foreach ($idList as $index => $id) {
|
||||||
|
$paramName = ":id{$index}";
|
||||||
|
$placeholders[] = $paramName;
|
||||||
|
$parameters[$paramName] = $id;
|
||||||
|
}
|
||||||
|
|
||||||
|
$sql = 'UPDATE identification_parts SET iddatadb = :iddatadb WHERE id IN (' . implode(',', $placeholders) . ')';
|
||||||
|
$stmt = $pdo->prepare($sql);
|
||||||
|
$stmt->execute($parameters);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (count($photoList) != 0) {
|
||||||
|
$placeholders = [];
|
||||||
|
$parameters = [':iddatadb' => $iddatadb];
|
||||||
|
|
||||||
|
foreach ($photoList as $index => $photo) {
|
||||||
|
$paramName = ":photo{$index}";
|
||||||
|
$placeholders[] = $paramName;
|
||||||
|
$parameters[$paramName] = $photo;
|
||||||
|
}
|
||||||
|
|
||||||
|
$stmt = $pdo->prepare('UPDATE datadb_photos SET iddatadb = :iddatadb WHERE file_path IN (' . implode(',', $placeholders) . ')');
|
||||||
|
$stmt->execute($parameters);
|
||||||
|
}
|
||||||
|
|
||||||
|
echo json_encode(['success' => true, 'message' => '']);
|
||||||
|
} else {
|
||||||
|
echo json_encode(['success' => false, 'message' => 'Dati mancanti']);
|
||||||
|
}
|
||||||
@@ -3,58 +3,867 @@
|
|||||||
"value": [
|
"value": [
|
||||||
{
|
{
|
||||||
"IdSchemaCustomFields": 41,
|
"IdSchemaCustomFields": 41,
|
||||||
|
"ConteggioClienti": 0,
|
||||||
"Nome": "Labostudio",
|
"Nome": "Labostudio",
|
||||||
"Descrizione": null
|
"Descrizione": null
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"IdSchemaCustomFields": 42,
|
"IdSchemaCustomFields": 42,
|
||||||
|
"ConteggioClienti": 0,
|
||||||
"Nome": "Schema 1",
|
"Nome": "Schema 1",
|
||||||
"Descrizione": null
|
"Descrizione": null
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"IdSchemaCustomFields": 43,
|
"IdSchemaCustomFields": 43,
|
||||||
|
"ConteggioClienti": 0,
|
||||||
"Nome": "Schema1",
|
"Nome": "Schema1",
|
||||||
"Descrizione": null
|
"Descrizione": null
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"IdSchemaCustomFields": 44,
|
"IdSchemaCustomFields": 44,
|
||||||
|
"ConteggioClienti": 0,
|
||||||
"Nome": "Cuoio\/Pelle Standard \/ Leather",
|
"Nome": "Cuoio\/Pelle Standard \/ Leather",
|
||||||
"Descrizione": "Schema per tutti i campioni in cuoio\/pelle per i quali non \u00e8 specificato un destinatario.\r\n"
|
"Descrizione": "Schema per tutti i campioni in cuoio\/pelle per i quali non \u00e8 specificato un destinatario.\r\n"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"IdSchemaCustomFields": 45,
|
"IdSchemaCustomFields": 45,
|
||||||
|
"ConteggioClienti": 0,
|
||||||
"Nome": "Borse",
|
"Nome": "Borse",
|
||||||
"Descrizione": null
|
"Descrizione": null
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"IdSchemaCustomFields": 46,
|
"IdSchemaCustomFields": 46,
|
||||||
|
"ConteggioClienti": 0,
|
||||||
"Nome": "Borse Burberry",
|
"Nome": "Borse Burberry",
|
||||||
"Descrizione": null
|
"Descrizione": null
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"IdSchemaCustomFields": 47,
|
"IdSchemaCustomFields": 47,
|
||||||
|
"ConteggioClienti": 0,
|
||||||
"Nome": "pelle calzatura",
|
"Nome": "pelle calzatura",
|
||||||
"Descrizione": null
|
"Descrizione": null
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"IdSchemaCustomFields": 48,
|
"IdSchemaCustomFields": 48,
|
||||||
|
"ConteggioClienti": 0,
|
||||||
"Nome": "Standard Generico \/ Generic Standard",
|
"Nome": "Standard Generico \/ Generic Standard",
|
||||||
"Descrizione": "Schema per tutti i campioni di qualsiasi matrice escluso cuoio\/pelle\r\n\r\n"
|
"Descrizione": "Schema per tutti i campioni di qualsiasi matrice escluso cuoio\/pelle\r\n\r\n"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"IdSchemaCustomFields": 49,
|
"IdSchemaCustomFields": 49,
|
||||||
|
"ConteggioClienti": 0,
|
||||||
"Nome": "Accessori Metallici \/ Metallic Accessories",
|
"Nome": "Accessori Metallici \/ Metallic Accessories",
|
||||||
"Descrizione": "Schema per tutti gli Accessori Metallici\r\n\r\n\r\n"
|
"Descrizione": "Schema per tutti gli Accessori Metallici\r\n\r\n\r\n"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"IdSchemaCustomFields": 50,
|
"IdSchemaCustomFields": 50,
|
||||||
|
"ConteggioClienti": 0,
|
||||||
"Nome": "Tessile \/ Textiles",
|
"Nome": "Tessile \/ Textiles",
|
||||||
"Descrizione": "Schema per tutti i Tessuti\r\n"
|
"Descrizione": "Schema per tutti i Tessuti\r\n"
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"IdSchemaCustomFields": 52,
|
||||||
|
"ConteggioClienti": 0,
|
||||||
|
"Nome": "Brasport",
|
||||||
|
"Descrizione": null
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"IdSchemaCustomFields": 54,
|
||||||
|
"ConteggioClienti": 0,
|
||||||
|
"Nome": "Chanel Calzatura",
|
||||||
|
"Descrizione": "\r\n"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"IdSchemaCustomFields": 55,
|
||||||
|
"ConteggioClienti": 0,
|
||||||
|
"Nome": "Burberry",
|
||||||
|
"Descrizione": null
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"IdSchemaCustomFields": 57,
|
||||||
|
"ConteggioClienti": 0,
|
||||||
|
"Nome": "Z_Ralph Lauren - Tessile",
|
||||||
|
"Descrizione": "Schema per tutti i campioni in tessuto per Ralph Lauren.\r\n\r\n\r\n\r\n"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"IdSchemaCustomFields": 58,
|
||||||
|
"ConteggioClienti": 0,
|
||||||
|
"Nome": "LIU JO",
|
||||||
|
"Descrizione": null
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"IdSchemaCustomFields": 59,
|
||||||
|
"ConteggioClienti": 0,
|
||||||
|
"Nome": "Migrazione tra Componenti",
|
||||||
|
"Descrizione": null
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"IdSchemaCustomFields": 60,
|
||||||
|
"ConteggioClienti": 0,
|
||||||
|
"Nome": "Prova Outsourcing",
|
||||||
|
"Descrizione": null
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"IdSchemaCustomFields": 61,
|
||||||
|
"ConteggioClienti": 0,
|
||||||
|
"Nome": "Marc Jacobs",
|
||||||
|
"Descrizione": null
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"IdSchemaCustomFields": 62,
|
||||||
|
"ConteggioClienti": 0,
|
||||||
|
"Nome": "A. TESTONI",
|
||||||
|
"Descrizione": null
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"IdSchemaCustomFields": 63,
|
||||||
|
"ConteggioClienti": 0,
|
||||||
|
"Nome": "Chanel CHINA PROJECT",
|
||||||
|
"Descrizione": "\r\n"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"IdSchemaCustomFields": 66,
|
||||||
|
"ConteggioClienti": 0,
|
||||||
|
"Nome": "GUESS",
|
||||||
|
"Descrizione": null
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"IdSchemaCustomFields": 68,
|
||||||
|
"ConteggioClienti": 0,
|
||||||
|
"Nome": "Valentino",
|
||||||
|
"Descrizione": null
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"IdSchemaCustomFields": 69,
|
||||||
|
"ConteggioClienti": 0,
|
||||||
|
"Nome": "DESIGUAL",
|
||||||
|
"Descrizione": null
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"IdSchemaCustomFields": 70,
|
||||||
|
"ConteggioClienti": 0,
|
||||||
|
"Nome": "FENDI PELLETTERIA",
|
||||||
|
"Descrizione": "Solo per FENDI Pelletteria_INGLESE\r\n"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"IdSchemaCustomFields": 71,
|
||||||
|
"ConteggioClienti": 0,
|
||||||
|
"Nome": "Z_Cuoio\/Pelle Standard ACCREDIA",
|
||||||
|
"Descrizione": "Schema per tutti i campioni in cuoio\/pelle per i quali non \u00e8 specificato un destinatario.\r\n"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"IdSchemaCustomFields": 72,
|
||||||
|
"ConteggioClienti": 0,
|
||||||
|
"Nome": "Z_BALLY",
|
||||||
|
"Descrizione": "sia per calzatura che pelletteria. Aggiunto campo TEST DESCRIPTION, compreso nella fatturazione per le divisioni pelletteria.\r\n\r\n"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"IdSchemaCustomFields": 73,
|
||||||
|
"ConteggioClienti": 0,
|
||||||
|
"Nome": "Z_Ralph Lauren - Leather",
|
||||||
|
"Descrizione": null
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"IdSchemaCustomFields": 74,
|
||||||
|
"ConteggioClienti": 0,
|
||||||
|
"Nome": "Z_Ralph Lauren - Fabric",
|
||||||
|
"Descrizione": null
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"IdSchemaCustomFields": 76,
|
||||||
|
"ConteggioClienti": 0,
|
||||||
|
"Nome": "NORDSTROM",
|
||||||
|
"Descrizione": "\r\n"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"IdSchemaCustomFields": 77,
|
||||||
|
"ConteggioClienti": 0,
|
||||||
|
"Nome": "ONWARD LUXURY GROUP",
|
||||||
|
"Descrizione": "\r\n"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"IdSchemaCustomFields": 78,
|
||||||
|
"ConteggioClienti": 0,
|
||||||
|
"Nome": "P&G SRL_Progetto Durability",
|
||||||
|
"Descrizione": "\r\n"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"IdSchemaCustomFields": 79,
|
||||||
|
"ConteggioClienti": 0,
|
||||||
|
"Nome": "Bally qualsiasi divisione",
|
||||||
|
"Descrizione": null
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"IdSchemaCustomFields": 80,
|
||||||
|
"ConteggioClienti": 0,
|
||||||
|
"Nome": "FENDI CALZATURA",
|
||||||
|
"Descrizione": "\r\n"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"IdSchemaCustomFields": 81,
|
||||||
|
"ConteggioClienti": 0,
|
||||||
|
"Nome": "LouBoutin",
|
||||||
|
"Descrizione": "\r\n"
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"IdSchemaCustomFields": 82,
|
"IdSchemaCustomFields": 82,
|
||||||
|
"ConteggioClienti": 0,
|
||||||
"Nome": "MONCLER Brand",
|
"Nome": "MONCLER Brand",
|
||||||
"Descrizione": "Da utilizzare solo per il Brand Diretto\r\nGR 19\/03\/2024"
|
"Descrizione": "Da utilizzare solo per il Brand Diretto\r\nGR 19\/03\/2024"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"IdSchemaCustomFields": 83,
|
||||||
|
"ConteggioClienti": 0,
|
||||||
|
"Nome": "JIMMY CHOO",
|
||||||
|
"Descrizione": null
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"IdSchemaCustomFields": 84,
|
||||||
|
"ConteggioClienti": 0,
|
||||||
|
"Nome": "Yves Saint Laurent",
|
||||||
|
"Descrizione": null
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"IdSchemaCustomFields": 85,
|
||||||
|
"ConteggioClienti": 0,
|
||||||
|
"Nome": "BENETTON",
|
||||||
|
"Descrizione": null
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"IdSchemaCustomFields": 86,
|
||||||
|
"ConteggioClienti": 0,
|
||||||
|
"Nome": "UPLOAD REPORT",
|
||||||
|
"Descrizione": null
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"IdSchemaCustomFields": 87,
|
||||||
|
"ConteggioClienti": 0,
|
||||||
|
"Nome": "Hermes Calzatura",
|
||||||
|
"Descrizione": "\r\n\r\n\r\n"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"IdSchemaCustomFields": 88,
|
||||||
|
"ConteggioClienti": 0,
|
||||||
|
"Nome": "C&A",
|
||||||
|
"Descrizione": "Schema per fornitori e C&A diretto\r\n"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"IdSchemaCustomFields": 89,
|
||||||
|
"ConteggioClienti": 0,
|
||||||
|
"Nome": "AMERICAN EAGLE",
|
||||||
|
"Descrizione": "Schema per American Eagle\/Todd snyder\r\n\r\n\r\n\r\n"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"IdSchemaCustomFields": 91,
|
||||||
|
"ConteggioClienti": 0,
|
||||||
|
"Nome": "Ferragamo",
|
||||||
|
"Descrizione": "\r\n\r\n\r\n"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"IdSchemaCustomFields": 93,
|
||||||
|
"ConteggioClienti": 0,
|
||||||
|
"Nome": "Z_Alexander McQueen",
|
||||||
|
"Descrizione": "\r\n\r\n"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"IdSchemaCustomFields": 95,
|
||||||
|
"ConteggioClienti": 0,
|
||||||
|
"Nome": "Balenciaga Pelletteria",
|
||||||
|
"Descrizione": "\r\n"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"IdSchemaCustomFields": 96,
|
||||||
|
"ConteggioClienti": 0,
|
||||||
|
"Nome": "DUNHILL",
|
||||||
|
"Descrizione": "SCHEMA DUNHILL tutti i tipi di campioni\r\n"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"IdSchemaCustomFields": 97,
|
||||||
|
"ConteggioClienti": 0,
|
||||||
|
"Nome": "PRELIEVI ACQUE",
|
||||||
|
"Descrizione": null
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"IdSchemaCustomFields": 98,
|
||||||
|
"ConteggioClienti": 0,
|
||||||
|
"Nome": "LouBoutin_Tossicologico",
|
||||||
|
"Descrizione": null
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"IdSchemaCustomFields": 99,
|
||||||
|
"ConteggioClienti": 0,
|
||||||
|
"Nome": "ROBAN'S ",
|
||||||
|
"Descrizione": "tutte le divisioni\r\n\r\n"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"IdSchemaCustomFields": 100,
|
||||||
|
"ConteggioClienti": 0,
|
||||||
|
"Nome": "PROGETTO LUGGAGE",
|
||||||
|
"Descrizione": "Progetto Luggage Hermes\r\n\r\n\r\n\r\n"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"IdSchemaCustomFields": 102,
|
||||||
|
"ConteggioClienti": 0,
|
||||||
|
"Nome": "Balmain",
|
||||||
|
"Descrizione": "Schema per tutti i campioni Balmain"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"IdSchemaCustomFields": 103,
|
||||||
|
"ConteggioClienti": 0,
|
||||||
|
"Nome": "ZDHC",
|
||||||
|
"Descrizione": "Schema di Riferimento ZDHC Wastewaters"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"IdSchemaCustomFields": 104,
|
||||||
|
"ConteggioClienti": 0,
|
||||||
|
"Nome": "GUCCI CDC 4172",
|
||||||
|
"Descrizione": "\r\n"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"IdSchemaCustomFields": 105,
|
||||||
|
"ConteggioClienti": 0,
|
||||||
|
"Nome": "Z_ASOS",
|
||||||
|
"Descrizione": null
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"IdSchemaCustomFields": 106,
|
||||||
|
"ConteggioClienti": 0,
|
||||||
|
"Nome": "Z_BOUX AVENUE",
|
||||||
|
"Descrizione": "28\/09 no indicazioni su esigenze di fatturazione (detto da Frosini)\r\n\r\n\r\n"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"IdSchemaCustomFields": 107,
|
||||||
|
"ConteggioClienti": 0,
|
||||||
|
"Nome": "BONPOINT",
|
||||||
|
"Descrizione": "05\/10 creazione\r\n\r\n\r\n\r\n"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"IdSchemaCustomFields": 108,
|
||||||
|
"ConteggioClienti": 0,
|
||||||
|
"Nome": "Canada Goose",
|
||||||
|
"Descrizione": null
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"IdSchemaCustomFields": 109,
|
||||||
|
"ConteggioClienti": 0,
|
||||||
|
"Nome": "DIESEL",
|
||||||
|
"Descrizione": null
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"IdSchemaCustomFields": 110,
|
||||||
|
"ConteggioClienti": 0,
|
||||||
|
"Nome": "AQC_Leather",
|
||||||
|
"Descrizione": null
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"IdSchemaCustomFields": 111,
|
||||||
|
"ConteggioClienti": 0,
|
||||||
|
"Nome": "BEAUMANOIR (CC-Mor-Bon-Breal)",
|
||||||
|
"Descrizione": "04\/02\/21_ AGGIORNATO 05\/08\r\n"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"IdSchemaCustomFields": 112,
|
||||||
|
"ConteggioClienti": 0,
|
||||||
|
"Nome": "BooHoo",
|
||||||
|
"Descrizione": null
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"IdSchemaCustomFields": 114,
|
||||||
|
"ConteggioClienti": 0,
|
||||||
|
"Nome": "Golden Goose",
|
||||||
|
"Descrizione": "Golden goose\r\n"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"IdSchemaCustomFields": 115,
|
||||||
|
"ConteggioClienti": 0,
|
||||||
|
"Nome": "Z_ZLABEL",
|
||||||
|
"Descrizione": "Schema per ZLABEL"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"IdSchemaCustomFields": 116,
|
||||||
|
"ConteggioClienti": 0,
|
||||||
|
"Nome": "Zadig & Voltaire",
|
||||||
|
"Descrizione": "Schema per Zadig & Voltaire"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"IdSchemaCustomFields": 117,
|
||||||
|
"ConteggioClienti": 0,
|
||||||
|
"Nome": "Gemo",
|
||||||
|
"Descrizione": "Schema solo per Gemo\r\n\r\n\r\n\r\n"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"IdSchemaCustomFields": 118,
|
||||||
|
"ConteggioClienti": 0,
|
||||||
|
"Nome": "PROMOD",
|
||||||
|
"Descrizione": "CHF 06\/08\r\n\r\n\r\n\r\n\r\n"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"IdSchemaCustomFields": 119,
|
||||||
|
"ConteggioClienti": 0,
|
||||||
|
"Nome": "LA HALLE (BEAUMANOIR)",
|
||||||
|
"Descrizione": "13\/08\/2021 CHF\r\n\r\n"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"IdSchemaCustomFields": 120,
|
||||||
|
"ConteggioClienti": 0,
|
||||||
|
"Nome": "LA REDOUTE",
|
||||||
|
"Descrizione": "14\/09\/2021 AD\r\n\r\n\r\n\r\n"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"IdSchemaCustomFields": 121,
|
||||||
|
"ConteggioClienti": 0,
|
||||||
|
"Nome": "TOD'S",
|
||||||
|
"Descrizione": "Schema per materiale gb18401 e GB20400\r\n\r\n\r\n"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"IdSchemaCustomFields": 122,
|
||||||
|
"ConteggioClienti": 0,
|
||||||
|
"Nome": "ASOS rev 1",
|
||||||
|
"Descrizione": null
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"IdSchemaCustomFields": 123,
|
||||||
|
"ConteggioClienti": 0,
|
||||||
|
"Nome": "Monoprix",
|
||||||
|
"Descrizione": "Schema per Monoprix\r\n\r\n"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"IdSchemaCustomFields": 124,
|
||||||
|
"ConteggioClienti": 0,
|
||||||
|
"Nome": "DIOR Ita",
|
||||||
|
"Descrizione": "CHF 21\/03\r\n"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"IdSchemaCustomFields": 125,
|
||||||
|
"ConteggioClienti": 0,
|
||||||
|
"Nome": "GEOX - Raw Material",
|
||||||
|
"Descrizione": "SCHEMA DA USARE SOLO PER GEOX - SOLO PER LE SUOLE E MATERIALI DENOMINATI RPU \/ RAW MATERIAL"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"IdSchemaCustomFields": 126,
|
||||||
|
"ConteggioClienti": 0,
|
||||||
|
"Nome": "CAMAIEU",
|
||||||
|
"Descrizione": "SCHEMA DA USARE SOLO PER CAMAIEU\r\n"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"IdSchemaCustomFields": 127,
|
||||||
|
"ConteggioClienti": 0,
|
||||||
|
"Nome": "Z_Golden Goose FTW",
|
||||||
|
"Descrizione": "Golden goose\r\n"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"IdSchemaCustomFields": 128,
|
||||||
|
"ConteggioClienti": 0,
|
||||||
|
"Nome": "STEVE MADDEN - FOOTWEAR",
|
||||||
|
"Descrizione": "DA UTILIZZARE SOLO PER DESTINAZIONE D'USO CALZATURA\r\n"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"IdSchemaCustomFields": 129,
|
||||||
|
"ConteggioClienti": 0,
|
||||||
|
"Nome": "Carhartt",
|
||||||
|
"Descrizione": "SCHEMA DA UTILIZZARE SOLO PER RICHIESTE UFFICIALI CARHARTT\r\n\r\n\r\n"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"IdSchemaCustomFields": 131,
|
||||||
|
"ConteggioClienti": 0,
|
||||||
|
"Nome": "DAMART",
|
||||||
|
"Descrizione": "\r\n\r\n\r\n\r\n"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"IdSchemaCustomFields": 132,
|
||||||
|
"ConteggioClienti": 0,
|
||||||
|
"Nome": "Gruppo OTB",
|
||||||
|
"Descrizione": "Schema per Staff International, Diesel e Brave kid\r\n\r\n\r\n\r\n\r\n"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"IdSchemaCustomFields": 133,
|
||||||
|
"ConteggioClienti": 0,
|
||||||
|
"Nome": "FERRARI",
|
||||||
|
"Descrizione": "Schema da usare per Ferrari e fornitori\r\n\r\n"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"IdSchemaCustomFields": 134,
|
||||||
|
"ConteggioClienti": 0,
|
||||||
|
"Nome": "GAP - ABBIGLIAMENTO",
|
||||||
|
"Descrizione": "Schema GAP da utilizzare solo per destinazione abbigliamento\r\n"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"IdSchemaCustomFields": 135,
|
||||||
|
"ConteggioClienti": 0,
|
||||||
|
"Nome": "GAP - CALZATURA",
|
||||||
|
"Descrizione": "Schema GAP da utilizzare solo per destinazione calzatura\r\n\r\n"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"IdSchemaCustomFields": 136,
|
||||||
|
"ConteggioClienti": 0,
|
||||||
|
"Nome": "SALLING GROUP",
|
||||||
|
"Descrizione": "Schema per Salling Group\r\n\r\n\r\n\r\n\r\n"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"IdSchemaCustomFields": 137,
|
||||||
|
"ConteggioClienti": 0,
|
||||||
|
"Nome": "SOFT SURROUNDINGS",
|
||||||
|
"Descrizione": "Schema da utilizzare per Soft Surroundings\r\n\r\n"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"IdSchemaCustomFields": 138,
|
||||||
|
"ConteggioClienti": 0,
|
||||||
|
"Nome": "E-LIMS - Cuoio\/Pelle Standard \/ Leather",
|
||||||
|
"Descrizione": "Schema da utilizzare solo per accettazioni online\r\n"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"IdSchemaCustomFields": 139,
|
||||||
|
"ConteggioClienti": 0,
|
||||||
|
"Nome": "Z_ZALANDO",
|
||||||
|
"Descrizione": "Schema per ZALANDO ------ (NON per ZLABEL che ha il suo)\r\n"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"IdSchemaCustomFields": 140,
|
||||||
|
"ConteggioClienti": 0,
|
||||||
|
"Nome": "LVMH",
|
||||||
|
"Descrizione": "gennaio 2023\r\n"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"IdSchemaCustomFields": 141,
|
||||||
|
"ConteggioClienti": 0,
|
||||||
|
"Nome": "Valentino - eLims",
|
||||||
|
"Descrizione": "SCHEMA DA UTILIZZARE SOLO PER L'ACCETTAZIONE ONLINE DI VALENTINO"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"IdSchemaCustomFields": 142,
|
||||||
|
"ConteggioClienti": 0,
|
||||||
|
"Nome": "FENDI PELLETTERIA COMPLIANCE",
|
||||||
|
"Descrizione": "Solo per FENDI Pelletteria_INGLESE 04\/05\/2023\r\n"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"IdSchemaCustomFields": 143,
|
||||||
|
"ConteggioClienti": 0,
|
||||||
|
"Nome": "TAPESTRY",
|
||||||
|
"Descrizione": "04\/05\/2023, agg secondo procedura 08\/11\/23\r\n"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"IdSchemaCustomFields": 144,
|
||||||
|
"ConteggioClienti": 0,
|
||||||
|
"Nome": "ALEXANDER MC QUEEN",
|
||||||
|
"Descrizione": "ALEXANDER MC QUEEN 15\/05\/2023\r\n\r\n\r\n"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"IdSchemaCustomFields": 145,
|
||||||
|
"ConteggioClienti": 0,
|
||||||
|
"Nome": "Hermes_Chimici_Parigi",
|
||||||
|
"Descrizione": "\r\n"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"IdSchemaCustomFields": 146,
|
||||||
|
"ConteggioClienti": 0,
|
||||||
|
"Nome": "CELINE ABBIGLIAMENTO_Progetto Speciale",
|
||||||
|
"Descrizione": "IO 202 CELINE ABBIGLIAMENTO_06\/06\/203_CHF\r\n\r\n"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"IdSchemaCustomFields": 147,
|
||||||
|
"ConteggioClienti": 0,
|
||||||
|
"Nome": "GEOX - Prodotto Finito",
|
||||||
|
"Descrizione": "SCHEMA DA USARE SOLO PER GEOX - SOLO PER PRODOTTO FINITO"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"IdSchemaCustomFields": 149,
|
||||||
|
"ConteggioClienti": 0,
|
||||||
|
"Nome": "Hermes\/IDO",
|
||||||
|
"Descrizione": "creato il 05\/07\/2023 CHF\r\n"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"IdSchemaCustomFields": 150,
|
||||||
|
"ConteggioClienti": 0,
|
||||||
|
"Nome": "Tessile \/ Textiles PER CLIENTI CON OBLO",
|
||||||
|
"Descrizione": "Schema per tutti i Tessuti QUANDO IL CLIENTE NECESSITA DI NR OBLO\r\n\r\n\r\n\r\n"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"IdSchemaCustomFields": 151,
|
||||||
|
"ConteggioClienti": 0,
|
||||||
|
"Nome": "J JIll",
|
||||||
|
"Descrizione": "Schema per cliente J JIll, necessario riportare la recommended fiber content in schema verde\r\n\r\n\r\n\r\n\r\n"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"IdSchemaCustomFields": 152,
|
||||||
|
"ConteggioClienti": 0,
|
||||||
|
"Nome": "AUCHAN - TAPE A L'OEIL",
|
||||||
|
"Descrizione": "Schema AUCHAN da utilizzare solo per la divisione TAPE A L'OEIL SA, successivamente se uguale da usare anche per le altre divisioni ed uniformarlo come generico"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"IdSchemaCustomFields": 153,
|
||||||
|
"ConteggioClienti": 0,
|
||||||
|
"Nome": "ALL SAINTS - BUSCEMI",
|
||||||
|
"Descrizione": "SCHE DA USARE SOLO PER ALL SAINTS E BUSCEMI\r\n\r\n\r\n\r\n"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"IdSchemaCustomFields": 154,
|
||||||
|
"ConteggioClienti": 0,
|
||||||
|
"Nome": "CELINE - LVMH",
|
||||||
|
"Descrizione": "Schema con aggiunta campi LVMH per estrazioni\r\nAD-29\/03\/2024"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"IdSchemaCustomFields": 155,
|
||||||
|
"ConteggioClienti": 0,
|
||||||
|
"Nome": "SeyMeChamLou",
|
||||||
|
"Descrizione": "DA USARE SOLO PER SEYMECHAMLOU (LOUBOUTIN)\r\n"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"IdSchemaCustomFields": 156,
|
||||||
|
"ConteggioClienti": 0,
|
||||||
|
"Nome": "PRADA",
|
||||||
|
"Descrizione": "PER ADESSO DA USARE PER ABBIGLIAMENTO E CALZATURA\r\n\r\n\r\n\r\n"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"IdSchemaCustomFields": 157,
|
||||||
|
"ConteggioClienti": 0,
|
||||||
|
"Nome": "Hermes_HCP",
|
||||||
|
"Descrizione": "Schema per unicamente HCP"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"IdSchemaCustomFields": 158,
|
||||||
|
"ConteggioClienti": 0,
|
||||||
|
"Nome": "STONE ISLAND",
|
||||||
|
"Descrizione": "Schema per tutti i campioni in cuoio\/pelle per i quali non \u00e8 specificato un destinatario.\r\n"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"IdSchemaCustomFields": 159,
|
||||||
|
"ConteggioClienti": 0,
|
||||||
|
"Nome": "Chanel_Pierre Damien",
|
||||||
|
"Descrizione": "Schema da usare per Campioni ricevuti da Pierre-Damien Verine e suo staff CHANEL.\r\n"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"IdSchemaCustomFields": 160,
|
||||||
|
"ConteggioClienti": 0,
|
||||||
|
"Nome": "Z_ACNE",
|
||||||
|
"Descrizione": "\r\n.\r\n"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"IdSchemaCustomFields": 161,
|
||||||
|
"ConteggioClienti": 0,
|
||||||
|
"Nome": "OBERALP GROUP",
|
||||||
|
"Descrizione": "Schema da usare solo per Oberalp Group\r\n"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"IdSchemaCustomFields": 162,
|
||||||
|
"ConteggioClienti": 0,
|
||||||
|
"Nome": "Club Monaco",
|
||||||
|
"Descrizione": "da usare solo per Club Monaco GR 20\/02\/2024"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"IdSchemaCustomFields": 163,
|
||||||
|
"ConteggioClienti": 0,
|
||||||
|
"Nome": "Devred",
|
||||||
|
"Descrizione": "Schema creato per cliente DEVRED\r\nGR 18\/03\/2024\r\n\r\n"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"IdSchemaCustomFields": 164,
|
||||||
|
"ConteggioClienti": 0,
|
||||||
|
"Nome": "MONCLER Supplier",
|
||||||
|
"Descrizione": "Da utilizzare per fornitori Moncler \r\nGR 19\/03\/2024"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"IdSchemaCustomFields": 165,
|
||||||
|
"ConteggioClienti": 0,
|
||||||
|
"Nome": "Kering Sunglasses",
|
||||||
|
"Descrizione": "Schema per tutti i campioni di qualsiasi matrice escluso cuoio\/pelle\r\n\r\n"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"IdSchemaCustomFields": 166,
|
||||||
|
"ConteggioClienti": 0,
|
||||||
|
"Nome": "Giorgio Armani",
|
||||||
|
"Descrizione": "\r\n\r\n"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"IdSchemaCustomFields": 167,
|
||||||
|
"ConteggioClienti": 0,
|
||||||
|
"Nome": "Chanel USA",
|
||||||
|
"Descrizione": "\r\n"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"IdSchemaCustomFields": 168,
|
||||||
|
"ConteggioClienti": 0,
|
||||||
|
"Nome": "Staff Calzatura",
|
||||||
|
"Descrizione": "Schema per tutti i campioni di qualsiasi matrice escluso cuoio\/pelle\r\n\r\n"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"IdSchemaCustomFields": 169,
|
||||||
|
"ConteggioClienti": 0,
|
||||||
|
"Nome": "P&G SRL",
|
||||||
|
"Descrizione": "\r\n"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"IdSchemaCustomFields": 170,
|
||||||
|
"ConteggioClienti": 0,
|
||||||
|
"Nome": "INCOM",
|
||||||
|
"Descrizione": "Schema da utilizzare per INCOM"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"IdSchemaCustomFields": 171,
|
||||||
|
"ConteggioClienti": 0,
|
||||||
|
"Nome": "RIVER ISLAND",
|
||||||
|
"Descrizione": "SCHEDA DA USARE SOLO PER RIVER ISLAND - NO CALZATURA\r\n\r\n\r\n"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"IdSchemaCustomFields": 172,
|
||||||
|
"ConteggioClienti": 0,
|
||||||
|
"Nome": "Longchamp",
|
||||||
|
"Descrizione": "Schema per tutti i campioni con RIchiesta Longchamp ufficiale\r\n"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"IdSchemaCustomFields": 173,
|
||||||
|
"ConteggioClienti": 0,
|
||||||
|
"Nome": "Bottega Veneta",
|
||||||
|
"Descrizione": "Schema per tutti i campioni di Bottega Veneta di Stella"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"IdSchemaCustomFields": 174,
|
||||||
|
"ConteggioClienti": 0,
|
||||||
|
"Nome": "Mint Velvet",
|
||||||
|
"Descrizione": "Schema per tutti i campioni di Mint Velvel con richiesta ufficiale.\r\n\r\n"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"IdSchemaCustomFields": 176,
|
||||||
|
"ConteggioClienti": 0,
|
||||||
|
"Nome": "RIVER ISLAND FOOTWEAR",
|
||||||
|
"Descrizione": "SCHEDA DA USARE SOLO PER RIVER ISLAND - USARE SOLO PER\r\n CALZATURA\r\n"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"IdSchemaCustomFields": 177,
|
||||||
|
"ConteggioClienti": 0,
|
||||||
|
"Nome": "Phoebe philo ACC",
|
||||||
|
"Descrizione": "(scarpe, borse, cinture, occhiali, gioielleria)\r\n"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"IdSchemaCustomFields": 178,
|
||||||
|
"ConteggioClienti": 0,
|
||||||
|
"Nome": "New Guards Group",
|
||||||
|
"Descrizione": "\r\n"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"IdSchemaCustomFields": 179,
|
||||||
|
"ConteggioClienti": 0,
|
||||||
|
"Nome": "Giorgio Armani Operation",
|
||||||
|
"Descrizione": "\r\n\r\n"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"IdSchemaCustomFields": 180,
|
||||||
|
"ConteggioClienti": 0,
|
||||||
|
"Nome": "Chloe'",
|
||||||
|
"Descrizione": "\r\n"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"IdSchemaCustomFields": 181,
|
||||||
|
"ConteggioClienti": 0,
|
||||||
|
"Nome": "LVMH non diretti",
|
||||||
|
"Descrizione": "utilizzato ogni qualvolta siano caricati pacchetti \/ prove singole che non rientrano nel programma LVMH \r\n"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"IdSchemaCustomFields": 182,
|
||||||
|
"ConteggioClienti": 0,
|
||||||
|
"Nome": "Ralph Lauren - All testing V.6",
|
||||||
|
"Descrizione": null
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"IdSchemaCustomFields": 183,
|
||||||
|
"ConteggioClienti": 0,
|
||||||
|
"Nome": "TWINSET",
|
||||||
|
"Descrizione": "Schema da usare per Twinset - come da mail di EP del 20\/05\/2025\r\n\r\n"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"IdSchemaCustomFields": 184,
|
||||||
|
"ConteggioClienti": 0,
|
||||||
|
"Nome": "ACNE STUDIOS rev 01",
|
||||||
|
"Descrizione": null
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"IdSchemaCustomFields": 185,
|
||||||
|
"ConteggioClienti": 0,
|
||||||
|
"Nome": "Brunello cucinelli",
|
||||||
|
"Descrizione": null
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"IdSchemaCustomFields": 186,
|
||||||
|
"ConteggioClienti": 0,
|
||||||
|
"Nome": "TWINSET",
|
||||||
|
"Descrizione": "Schema per tutti i campioni di qualsiasi matrice escluso cuoio\/pelle\r\n\r\n"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"IdSchemaCustomFields": 187,
|
||||||
|
"ConteggioClienti": 0,
|
||||||
|
"Nome": "LORO PIANA",
|
||||||
|
"Descrizione": "Schema per tutti i campioni Loro Piana\r\n"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"IdSchemaCustomFields": 188,
|
||||||
|
"ConteggioClienti": 0,
|
||||||
|
"Nome": "LEMAIRE",
|
||||||
|
"Descrizione": "Schema per tutti i campioni in cuoio\/pelle per i quali non \u00e8 specificato un destinatario.\r\n"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"IdSchemaCustomFields": 189,
|
||||||
|
"ConteggioClienti": 0,
|
||||||
|
"Nome": "TEST_IT_SCHEMA",
|
||||||
|
"Descrizione": "Test per IT"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"IdSchemaCustomFields": 190,
|
||||||
|
"ConteggioClienti": 0,
|
||||||
|
"Nome": "LBS Interscambio",
|
||||||
|
"Descrizione": "Schema per tutti i campioni interscambio LBS\r\n"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"IdSchemaCustomFields": 191,
|
||||||
|
"ConteggioClienti": 0,
|
||||||
|
"Nome": "KIABI",
|
||||||
|
"Descrizione": "Schema per il cliente Kiabi\r\n"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"IdSchemaCustomFields": 192,
|
||||||
|
"ConteggioClienti": 0,
|
||||||
|
"Nome": "INDITEX",
|
||||||
|
"Descrizione": "Schema da usare solo per richieste INDITEX"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"IdSchemaCustomFields": 193,
|
||||||
|
"ConteggioClienti": 0,
|
||||||
|
"Nome": "AQC_Other materials",
|
||||||
|
"Descrizione": null
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"IdSchemaCustomFields": 194,
|
||||||
|
"ConteggioClienti": 0,
|
||||||
|
"Nome": "Hermes ADM & Silk",
|
||||||
|
"Descrizione": "SCHEMA DA USARE SOLO PER LA DIVISIONE ADM & SILK\r\n\r\n\r\n\r\n"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"IdSchemaCustomFields": 195,
|
||||||
|
"ConteggioClienti": 0,
|
||||||
|
"Nome": "Chloe'_APPAREL",
|
||||||
|
"Descrizione": "Schema aggiornato il 18\/12\/25 secondo modulo excel x codifica Elims\r\nGR"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"IdSchemaCustomFields": 196,
|
||||||
|
"ConteggioClienti": 0,
|
||||||
|
"Nome": "DUNHILL_DRAFT NON USARE",
|
||||||
|
"Descrizione": "SCHEMA DUNHILL NON USARE STUDIO NUOVA PROCEDURA NOVEMBRE 2025\r\n\r\n"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"IdSchemaCustomFields": 197,
|
||||||
|
"ConteggioClienti": 0,
|
||||||
|
"Nome": "BERLUTI - LVMH",
|
||||||
|
"Descrizione": "Creato per accettazione Elims\r\n"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"IdSchemaCustomFields": 198,
|
||||||
|
"ConteggioClienti": 0,
|
||||||
|
"Nome": "PIAGET (gruppo Cartier)",
|
||||||
|
"Descrizione": "Schema per tutti i campioni PIAGET (gruppo Cartier)\r\n\r\n"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"IdSchemaCustomFields": 199,
|
||||||
|
"ConteggioClienti": 0,
|
||||||
|
"Nome": "HERMES PAP FEMME",
|
||||||
|
"Descrizione": "Schema per tutti i campioni di HERMES PAP FEMME \r\n15\/01\/2025\r\n\r\n\r\n"
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
@@ -0,0 +1,35 @@
|
|||||||
|
<?php
|
||||||
|
header('Content-Type: application/json');
|
||||||
|
|
||||||
|
require_once(__DIR__ . '/include/headscript.php');
|
||||||
|
|
||||||
|
$db = DBHandlerSelect::getInstance();
|
||||||
|
$pdo = $db->getConnection();
|
||||||
|
|
||||||
|
$input = json_decode(file_get_contents('php://input'), true);
|
||||||
|
|
||||||
|
$id = (int)($input['id'] ?? 0);
|
||||||
|
$field = (string)($input['field'] ?? '');
|
||||||
|
$value = $input['value'] ?? null;
|
||||||
|
|
||||||
|
if ($id <= 0) {
|
||||||
|
echo json_encode(['success' => false, 'message' => 'Invalid id']);
|
||||||
|
exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
$allowed = ['default_value', 'is_visible_import', 'is_required'];
|
||||||
|
if (!in_array($field, $allowed, true)) {
|
||||||
|
echo json_encode(['success' => false, 'message' => 'Invalid field']);
|
||||||
|
exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($field === 'is_visible_import' || $field === 'is_required') {
|
||||||
|
$value = ((int)$value === 1) ? 1 : 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
$sql = "UPDATE template_fixed_mapping SET {$field} = :val WHERE id = :id";
|
||||||
|
$stmt = $pdo->prepare($sql);
|
||||||
|
$ok = $stmt->execute([':val' => $value, ':id' => $id]);
|
||||||
|
|
||||||
|
echo json_encode(['success' => (bool)$ok]);
|
||||||
@@ -1,6 +1,6 @@
|
|||||||
<?php
|
<?php
|
||||||
// upload_photos_mobile.php
|
// upload_photos_mobile.php
|
||||||
include('include/headscript.php');
|
include('include/headscriptnologin.php');
|
||||||
|
|
||||||
$db = DBHandlerSelect::getInstance();
|
$db = DBHandlerSelect::getInstance();
|
||||||
$pdo = $db->getConnection();
|
$pdo = $db->getConnection();
|
||||||
|
|||||||
Reference in New Issue
Block a user