diff --git a/db/migrations/20260922113321_add_log_file_to_datadb.php b/db/migrations/20260922113321_add_log_file_to_datadb.php
new file mode 100644
index 00000000..f5d82452
--- /dev/null
+++ b/db/migrations/20260922113321_add_log_file_to_datadb.php
@@ -0,0 +1,21 @@
+table('datadb');
+ if (!$table->hasColumn('log_file')) {
+ $table->addColumn('log_file', 'string', [
+ 'limit' => 255,
+ 'null' => true,
+ 'default' => null,
+ 'after' => 'commessaweb', // aggiusta se la colonna non esiste
+ ])->update();
+ }
+ }
+}
diff --git a/db/migrations/20260922114451_add_error_status_to_datadb.php b/db/migrations/20260922114451_add_error_status_to_datadb.php
new file mode 100644
index 00000000..ec28b537
--- /dev/null
+++ b/db/migrations/20260922114451_add_error_status_to_datadb.php
@@ -0,0 +1,23 @@
+execute("ALTER TABLE `datadb`
+ MODIFY `status` ENUM('i','P','l','x') NULL DEFAULT NULL");
+ }
+
+ public function down(): void
+ {
+ // Rollback: riporta le eventuali 'x' a NULL prima di togliere il valore
+ $this->execute("UPDATE `datadb` SET `status` = NULL WHERE `status` = 'x'");
+ $this->execute("ALTER TABLE `datadb`
+ MODIFY `status` ENUM('i','P','l') NULL DEFAULT NULL");
+ }
+}
diff --git a/public/userarea/download_logs.php b/public/userarea/download_logs.php
new file mode 100644
index 00000000..4aeda11c
--- /dev/null
+++ b/public/userarea/download_logs.php
@@ -0,0 +1,37 @@
+getConnection();
+$logDir = __DIR__ . '/logs/api/';
+
+$iddatadb = (int)($_GET['iddatadb'] ?? 0);
+if ($iddatadb <= 0) {
+ http_response_code(400);
+ exit('iddatadb mancante');
+}
+
+$stmt = $pdo->prepare("SELECT log_file, commessaweb FROM datadb WHERE iddatadb = :id LIMIT 1");
+$stmt->execute(['id' => $iddatadb]);
+$row = $stmt->fetch(PDO::FETCH_ASSOC);
+
+if (!$row || empty($row['log_file'])) {
+ http_response_code(404);
+ exit('Nessun log per questa commessa');
+}
+
+// solo basename: niente path traversal
+$path = $logDir . basename($row['log_file']);
+if (!is_file($path)) {
+ http_response_code(404);
+ exit('File di log non trovato: ' . basename($row['log_file']));
+}
+
+while (ob_get_level() > 0) {
+ ob_end_clean();
+}
+$label = preg_replace('/[^A-Za-z0-9_\-]/', '_', (string)($row['commessaweb'] ?: $iddatadb));
+header('Content-Type: text/plain; charset=utf-8');
+header('Content-Disposition: attachment; filename="log_' . $label . '_' . $iddatadb . '.log"');
+header('Content-Length: ' . filesize($path));
+readfile($path);
+exit;
diff --git a/public/userarea/errors.php b/public/userarea/errors.php
new file mode 100644
index 00000000..585302f4
--- /dev/null
+++ b/public/userarea/errors.php
@@ -0,0 +1,363 @@
+getConnection();
+$logDir = __DIR__ . '/logs/api/';
+
+// Estrae un riassunto breve dell'errore dal file di log della commessa
+function extractErrorSummary(string $logDir, ?string $logFile): string
+{
+ if (empty($logFile)) return '';
+ $path = $logDir . basename($logFile);
+ if (!is_file($path)) return '';
+ $content = @file_get_contents($path);
+ if ($content === false || $content === '') return '';
+
+ // 1) prova col "detail" del JSON del 500
+ if (preg_match('/"detail"\s*:\s*"([^"]+)"/', $content, $m)) {
+ return trim(stripcslashes($m[1]));
+ }
+ // 2) fallback: ultima riga MESSAGE:
+ if (preg_match_all('/^MESSAGE:\s*(.+)$/m', $content, $m) && !empty($m[1])) {
+ return trim(end($m[1]));
+ }
+ return '';
+}
+
+// Query: solo le righe in errore per questo template
+$userFilter = $show_all_users ? '' : 'AND d.user_id = ?';
+$baseWhere = "WHERE d.templateid = ? AND d.status = 'x' {$userFilter}";
+$baseParams = [$template_id];
+if (!$show_all_users) $baseParams[] = $user_id;
+
+$countStmt = $pdo->prepare("SELECT COUNT(*) FROM datadb d {$baseWhere}");
+$countStmt->execute($baseParams);
+$totalRows = (int)$countStmt->fetchColumn();
+$totalPages = max(1, (int)ceil($totalRows / $perPage));
+if ($page > $totalPages) $page = $totalPages;
+
+$stmt = $pdo->prepare("
+ SELECT d.*, CONCAT(u.first_name, ' ', u.last_name) AS user_name
+ FROM datadb d
+ LEFT JOIN auth_users u ON d.user_id = u.id
+ {$baseWhere}
+ ORDER BY d.export_date DESC, d.iddatadb DESC
+ LIMIT {$perPage} OFFSET " . (($page - 1) * $perPage) . "
+");
+$stmt->execute($baseParams);
+$rowsData = $stmt->fetchAll(PDO::FETCH_ASSOC);
+?>
+
+
+
+
+
+
+
+
+
+
+ Export Errors - = htmlspecialchars($titlewebsite ?? '', ENT_QUOTES, 'UTF-8'); ?>
+
+
+
+
+
+
+
+
+
+
+
Imported (i)
+
To LIMS (l)
+
Errors (x)
+
+
+
+
(= count($rowsData) ?> of = $totalRows ?> errori= !$show_all_users ? ' — my records' : '' ?>)
+
+
+ 1):
+ $pageQuery = $_GET;
+ unset($pageQuery['page']);
+ $pageBase = 'errors.php?' . http_build_query($pageQuery); ?>
+
+
+
+
+
+
+
+
Nessun errore di export. 🎉
+
+
+
+
+
+ | Azioni |
+ Status |
+ Client |
+ Errore |
+ Data export |
+ File import |
+
+
+
+
+
+ |
+
+
+
+
+
+ |
+
+ Error
+
+ = htmlspecialchars($row['commessaweb']) ?>
+
+ |
+ = htmlspecialchars((string)($row['idclient'] ?? '')) ?> |
+ = htmlspecialchars($errMsg ?: '—') ?> |
+ = htmlspecialchars((string)($row['export_date'] ?? '')) ?> |
+ File |
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/public/userarea/export_to_lims.php b/public/userarea/export_to_lims.php
index fc189ae2..cf0fde72 100644
--- a/public/userarea/export_to_lims.php
+++ b/public/userarea/export_to_lims.php
@@ -55,22 +55,29 @@ $apiBaseUrl = 'https://93.43.5.102/limsapi/api/odata/';
// 🔹 Batch UUID — if present, all logs go to a single file
$batchUuid = $_POST['batch_uuid'] ?? null;
-$writeLog = (function () use ($batchUuid, $logDir) {
- $batchLogFile = $batchUuid ? $logDir . "batch_export_{$batchUuid}.log" : null;
+// File unico per questa commessa (nominato per iddatadb, che conosciamo sempre)
+$iddatadbForLog = $_POST['iddatadb'] ?? 'unknown';
+$commessaLogFile = $logDir . "commessa_{$iddatadbForLog}_" . time() . ".log";
- return function ($individualPath, $content, $stepLabel = null) use ($batchLogFile) {
- if ($batchLogFile) {
- $header = "\n" . str_repeat("=", 60) . "\n";
- if ($stepLabel) {
- $header .= "[{$stepLabel}] " . date('Y-m-d H:i:s') . "\n";
- }
- $header .= str_repeat("=", 60) . "\n";
- file_put_contents($batchLogFile, $header . $content . "\n", FILE_APPEND);
- } else {
- file_put_contents($individualPath, $content);
- }
- };
-})();
+// File di lotto: resta come INDICE (quali commesse nel lotto), non più tutti gli step
+$batchIndexFile = $batchUuid ? $logDir . "batch_export_{$batchUuid}.log" : null;
+
+// $writeLog ora appende sempre sul file unico della commessa, con header di step
+$writeLog = function ($individualPath, $content, $stepLabel = null) use ($commessaLogFile) {
+ $header = "\n" . str_repeat("=", 60) . "\n";
+ if ($stepLabel) {
+ $header .= "[{$stepLabel}] " . date('Y-m-d H:i:s') . "\n";
+ }
+ $header .= str_repeat("=", 60) . "\n";
+ file_put_contents($commessaLogFile, $header . $content . "\n", FILE_APPEND);
+};
+
+// Helper per registrare una riga nell'indice di lotto
+$writeBatchIndex = function ($line) use ($batchIndexFile) {
+ if ($batchIndexFile) {
+ file_put_contents($batchIndexFile, "[" . date('Y-m-d H:i:s') . "] " . $line . "\n", FILE_APPEND);
+ }
+};
// 🔹 Funzione per validare e convertire date
function validateDate($value)
@@ -693,16 +700,20 @@ try {
}
}
- // 🔹 STEP 8: Update datadb with idcommessaweb, commessaweb, status and export date
+ // 🔹 STEP 8: Update datadb with idcommessaweb, commessaweb, log_file and export date
+ // NB: lo status 'l' viene impostato SOLO dopo invio+import ok (vedi più sotto),
+ // così una riga rimasta indietro non risulta "To LIMS" per errore.
$stmt = $pdo->prepare("
UPDATE datadb
- SET idcommessaweb = :idcommessaweb, commessaweb = :commessaweb, status = 'l', export_date = NOW()
+ SET idcommessaweb = :idcommessaweb, commessaweb = :commessaweb,
+ log_file = :log_file, export_date = NOW()
WHERE iddatadb = :iddatadb
");
$stmt->execute([
'idcommessaweb' => $commessaId,
- 'commessaweb' => $commessaWebCode,
- 'iddatadb' => $iddatadb
+ 'commessaweb' => $commessaWebCode,
+ 'log_file' => basename($commessaLogFile),
+ 'iddatadb' => $iddatadb
]);
// 🔹 STEP 9: Send CommessaWeb to laboratory (commentato come richiesto)
@@ -770,9 +781,12 @@ try {
");
$stmt->execute([
'commessaweb' => substr($finalCodiceCommessa, 0, 30),
- 'iddatadb' => $iddatadb
+ 'iddatadb' => $iddatadb
]);
}
+
+ // Indice di lotto: esito OK di questa commessa
+ $writeBatchIndex("OK iddatadb={$iddatadb} commessa={$commessaId} codice={$finalCodiceCommessa} log=" . basename($commessaLogFile));
// 🔹 STEP 11: Prepare final response
$finalCommessa = [
"Cliente" => $clienteId,
@@ -818,10 +832,32 @@ try {
$errContent .= "MESSAGE: " . $e->getMessage() . "\n\n";
$errContent .= "TRACE:\n" . $e->getTraceAsString() . "\n";
- $errName = $errCommessaId
- ? "commessa_{$errCommessaId}_ERROR_{$errIddatadb}_" . time() . ".txt"
- : "commessa_ERROR_{$errIddatadb}_" . time() . ".txt";
- @file_put_contents($logDir . $errName, $errContent);
+ // Appendi l'errore in coda al file unico della commessa (se esiste),
+ // così resta tutto in un posto solo.
+ if (isset($commessaLogFile)) {
+ $header = "\n" . str_repeat("=", 60) . "\n[ERROR] " . date('Y-m-d H:i:s') . "\n" . str_repeat("=", 60) . "\n";
+ @file_put_contents($commessaLogFile, $header . $errContent . "\n", FILE_APPEND);
+ $errFileToSave = basename($commessaLogFile);
+ } else {
+ // fallback: file d'errore dedicato
+ $errName = "commessa_ERROR_{$errIddatadb}_" . time() . ".txt";
+ @file_put_contents($logDir . $errName, $errContent);
+ $errFileToSave = $errName;
+ }
+
+ // Segna la riga come errore ('x') e registra il log_file, senza lasciarla 'l'
+ if ($errIddatadb !== 'unknown') {
+ try {
+ $up = $pdo->prepare("UPDATE datadb SET status = 'x', log_file = :log_file WHERE iddatadb = :iddatadb");
+ $up->execute(['log_file' => $errFileToSave, 'iddatadb' => $errIddatadb]);
+ } catch (Exception $ignore) { /* non bloccare la risposta d'errore */ }
+ }
+ }
+
+ // Indice di lotto: esito KO di questa commessa
+ if (isset($writeBatchIndex)) {
+ $writeBatchIndex("FAIL iddatadb={$errIddatadb} commessa=" . ($errCommessaId ?? '-') . " log={$errFileToSave} :: " . $e->getMessage());
+ }
error_log("LIMS Export Error: " . $e->getMessage() . "\nTrace: " . $e->getTraceAsString());
diff --git a/public/userarea/import_xls2.php b/public/userarea/import_xls2.php
index b3d94d03..5b280800 100644
--- a/public/userarea/import_xls2.php
+++ b/public/userarea/import_xls2.php
@@ -164,6 +164,7 @@ error_log("Loaded template: " . print_r($template, true));