diff --git a/db/migrations/20260715073632_add_export_date_to_datadb.php b/db/migrations/20260715073632_add_export_date_to_datadb.php
new file mode 100644
index 00000000..b1d16dc8
--- /dev/null
+++ b/db/migrations/20260715073632_add_export_date_to_datadb.php
@@ -0,0 +1,25 @@
+table('datadb')
+ ->addColumn('export_date', 'datetime', [
+ 'null' => true,
+ 'default' => null,
+ 'after' => 'commessaweb',
+ 'comment' => 'Data/ora esportazione verso LIMS (valorizzata quando status = l)',
+ ])
+ ->addIndex(['export_date'], ['name' => 'idx_datadb_export_date'])
+ ->update();
+ }
+}
diff --git a/db/migrations/20260715074428_create_lims_daily_samples_table.php b/db/migrations/20260715074428_create_lims_daily_samples_table.php
new file mode 100644
index 00000000..5a59dd0c
--- /dev/null
+++ b/db/migrations/20260715074428_create_lims_daily_samples_table.php
@@ -0,0 +1,57 @@
+table('lims_daily_samples', [
+ 'id' => false,
+ 'primary_key' => ['id'],
+ 'comment' => 'Totale campioni inseriti nel LIMS per giornata',
+ ]);
+
+ $table
+ ->addColumn('id', 'integer', [
+ 'identity' => true,
+ 'signed' => false,
+ ])
+ ->addColumn('sample_date', 'date', [
+ 'null' => false,
+ 'comment' => 'Giornata di riferimento',
+ ])
+ ->addColumn('total_samples', 'integer', [
+ 'signed' => false,
+ 'null' => false,
+ 'default' => 0,
+ 'comment' => 'Totale campioni inseriti nel LIMS in quella giornata',
+ ])
+ ->addColumn('note', 'string', [
+ 'limit' => 255,
+ 'null' => true,
+ 'default' => null,
+ ])
+ ->addColumn('created_at', 'timestamp', [
+ 'null' => true,
+ 'default' => 'CURRENT_TIMESTAMP',
+ ])
+ ->addColumn('updated_at', 'timestamp', [
+ 'null' => true,
+ 'default' => null,
+ 'update' => 'CURRENT_TIMESTAMP',
+ ])
+ ->addIndex(['sample_date'], [
+ 'unique' => true,
+ 'name' => 'uq_lims_daily_samples_date',
+ ])
+ ->create();
+ }
+}
diff --git a/public/userarea/export_to_lims.php b/public/userarea/export_to_lims.php
index 76409d89..45bb9eb7 100644
--- a/public/userarea/export_to_lims.php
+++ b/public/userarea/export_to_lims.php
@@ -675,10 +675,10 @@ try {
}
}
- // ๐น STEP 8: Update datadb with idcommessaweb, commessaweb, and status
+ // ๐น STEP 8: Update datadb with idcommessaweb, commessaweb, status and export date
$stmt = $pdo->prepare("
UPDATE datadb
- SET idcommessaweb = :idcommessaweb, commessaweb = :commessaweb, status = 'l'
+ SET idcommessaweb = :idcommessaweb, commessaweb = :commessaweb, status = 'l', export_date = NOW()
WHERE iddatadb = :iddatadb
");
$stmt->execute([
diff --git a/public/userarea/search_clienti.php b/public/userarea/search_clienti.php
index 165214ca..436390d0 100644
--- a/public/userarea/search_clienti.php
+++ b/public/userarea/search_clienti.php
@@ -1,136 +1,416 @@
-
+
+
-if (!Auth::check()) {
- http_response_code(401);
- echo json_encode(['error' => 'Unauthorized']);
- exit;
-}
-
-require_once __DIR__ . '/class/VisualLimsApiClient.class.php';
-
-header('Content-Type: application/json');
-ini_set('display_errors', '0');
-error_reporting(E_ALL);
-
-$q = mb_strtolower(trim($_GET['q'] ?? ''));
-$limit = max(1, min(50, intval($_GET['limit'] ?? 20)));
-$id = isset($_GET['id']) ? intval($_GET['id']) : null;
-
-$tipo = $_GET['tipo'] ?? ''; // 'attivo' | 'inattivo' | '' (nessun filtro)
-
-function isInattivo(array $c): bool
-{
- $v = $c['Inattivo'] ?? false;
- return $v === true || $v === 1 || $v === '1' || $v === 'true';
-}
-
-function formatClientLabel(array $client): string
-{
- $name = trim($client['Nominativo'] ?? '');
- $id = trim((string)($client['IdCliente'] ?? ''));
- $code = trim((string)($client['CodiceCliente'] ?? ''));
-
- $parts = explode('_', $code);
- $suffix = trim($parts[1] ?? '');
-
- if ($suffix === '' && $code !== '') {
- $suffix = substr($code, 0, 1);
- }
-
- if ($suffix === '') {
- $suffix = '--';
- }
-
- return $name . ' - ' . $suffix . ' - ' . $id;
-}
-
-try {
- // Load from cache or API
- $cacheFile = __DIR__ . '/cache/clienti.json';
-
- if (file_exists($cacheFile) && (time() - filemtime($cacheFile) < 3600)) {
- $data = json_decode(file_get_contents($cacheFile), true);
- } else {
- $api = VisualLimsApiClient::getInstance();
-
- $params = [
- '$select' => 'IdCliente,Nominativo,CodiceCliente,Inattivo',
- '$orderby' => 'Nominativo asc'
- ];
-
- $data = $api->get("Cliente?" . http_build_query($params));
-
- if (!is_dir(__DIR__ . '/cache')) {
- mkdir(__DIR__ . '/cache', 0777, true);
+
+
+
+
+
+ TRF-Project - Statistiche Export LIMS
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
Statistiche Export LIMS
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
Export (status L)
+
0
+
+
+
+
+
+
+
Campioni inseriti LIMS
+
0
+
+
+
+
+
+
+
% Esportati / inseriti
+
โ
+
+
+
+
+
+
+
+
+
+
+ Campioni totali inseriti nel LIMS per questa giornata:
+
+
+ Un valore al giorno. Usato per calcolare la % di export.
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ | ID Cliente |
+ Cliente |
+ Export |
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ | ID Utente |
+ Utente |
+ Export |
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/public/userarea/stats_export_lims.php b/public/userarea/stats_export_lims.php
new file mode 100644
index 00000000..21807c45
--- /dev/null
+++ b/public/userarea/stats_export_lims.php
@@ -0,0 +1,481 @@
+
+
+
+
+
+
+
+
+
+ TRF-Project - Statistiche Export LIMS
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
Statistiche Export LIMS
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
Export (status L)
+
0
+
+
+
+
+
+
+
Campioni inseriti LIMS
+
0
+
+
+
+
+
+
+
% Esportati / inseriti
+
โ
+
+
+
+
+
+
+
+
+
+
+ Campioni totali inseriti nel LIMS per questa giornata:
+
+
+ Un valore al giorno. Usato per calcolare la % di export.
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ | ID Cliente |
+ Cliente |
+ Export |
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ | ID Utente |
+ Utente |
+ Export |
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/public/userarea/stats_export_lims_data.php b/public/userarea/stats_export_lims_data.php
new file mode 100644
index 00000000..b211abbd
--- /dev/null
+++ b/public/userarea/stats_export_lims_data.php
@@ -0,0 +1,90 @@
+getConnection();
+
+try {
+ // Data richiesta (default oggi). Formato atteso: Y-m-d
+ $date = $_GET['date'] ?? date('Y-m-d');
+ $d = DateTime::createFromFormat('Y-m-d', $date);
+ if (!$d || $d->format('Y-m-d') !== $date) {
+ throw new Exception('Data non valida');
+ }
+
+ // --- Export per cliente (status = 'l', giornata su export_date) ---
+ $stmtC = $pdo->prepare("
+ SELECT d.idclient AS idclient,
+ COUNT(*) AS tot
+ FROM datadb d
+ WHERE d.status = 'l'
+ AND DATE(d.export_date) = :d
+ GROUP BY d.idclient
+ ORDER BY tot DESC
+ ");
+ $stmtC->execute(['d' => $date]);
+ $byClient = $stmtC->fetchAll(PDO::FETCH_ASSOC);
+
+ // --- Risolvi Nominativo cliente dalla cache locale (search_clienti.php) ---
+ $clientNames = [];
+ $cacheFile = __DIR__ . '/cache/clienti.json';
+ if (is_file($cacheFile)) {
+ $cache = json_decode(file_get_contents($cacheFile), true);
+ foreach (($cache['value'] ?? []) as $c) {
+ $clientNames[(int)($c['IdCliente'] ?? 0)] = trim($c['Nominativo'] ?? '');
+ }
+ }
+
+ // --- Export per utente (status = 'l', giornata su export_date) ---
+ $stmtU = $pdo->prepare("
+ SELECT d.user_id AS user_id,
+ TRIM(CONCAT(COALESCE(u.first_name,''),' ',COALESCE(u.last_name,''))) AS username,
+ COUNT(*) AS tot
+ FROM datadb d
+ LEFT JOIN auth_users u ON u.id = d.user_id
+ WHERE d.status = 'l'
+ AND DATE(d.export_date) = :d
+ GROUP BY d.user_id, username
+ ORDER BY tot DESC
+ ");
+ $stmtU->execute(['d' => $date]);
+ $byUser = $stmtU->fetchAll(PDO::FETCH_ASSOC);
+
+ // Normalizza tipi (COUNT torna stringa in PDO)
+ foreach ($byClient as &$r) {
+ $r['idclient'] = $r['idclient'] !== null ? (int)$r['idclient'] : null;
+ $r['tot'] = (int)$r['tot'];
+ $nm = $clientNames[$r['idclient']] ?? '';
+ $r['clientname'] = $nm !== '' ? $nm : null;
+ }
+ unset($r);
+ foreach ($byUser as &$r) {
+ $r['user_id'] = $r['user_id'] !== null ? (int)$r['user_id'] : null;
+ $r['tot'] = (int)$r['tot'];
+ if (isset($r['username'])) $r['username'] = $r['username'] !== null && $r['username'] !== '' ? $r['username'] : null;
+ }
+ unset($r);
+
+ // --- Totale export della giornata ---
+ $totExport = array_sum(array_column($byClient, 'tot'));
+
+ // --- Campioni totali inseriti nel LIMS per la giornata (tabella lims_daily_samples) ---
+ $stmtL = $pdo->prepare("SELECT total_samples FROM lims_daily_samples WHERE sample_date = :d LIMIT 1");
+ $stmtL->execute(['d' => $date]);
+ $limsSamples = $stmtL->fetchColumn();
+ $limsSamples = ($limsSamples !== false) ? (int)$limsSamples : null;
+
+ echo json_encode([
+ 'success' => true,
+ 'date' => $date,
+ 'totals' => ['export' => (int)$totExport],
+ 'byClient' => $byClient,
+ 'byUser' => $byUser,
+ 'limsSamples' => $limsSamples,
+ ]);
+} catch (Exception $e) {
+ error_log('Stats export LIMS error: ' . $e->getMessage());
+ echo json_encode(['success' => false, 'message' => $e->getMessage()]);
+}
diff --git a/public/userarea/stats_save_lims_samples.php b/public/userarea/stats_save_lims_samples.php
new file mode 100644
index 00000000..ef412389
--- /dev/null
+++ b/public/userarea/stats_save_lims_samples.php
@@ -0,0 +1,36 @@
+getConnection();
+
+try {
+ $date = $_POST['date'] ?? null;
+ $total = $_POST['total_samples'] ?? null;
+
+ $d = $date ? DateTime::createFromFormat('Y-m-d', $date) : null;
+ if (!$d || $d->format('Y-m-d') !== $date) {
+ throw new Exception('Data non valida');
+ }
+ if (!is_numeric($total) || (int)$total < 0) {
+ throw new Exception('Valore campioni non valido');
+ }
+ $total = (int)$total;
+
+ // Upsert: un solo record per giornata (unique su sample_date)
+ $stmt = $pdo->prepare("
+ INSERT INTO lims_daily_samples (sample_date, total_samples)
+ VALUES (:d, :t)
+ ON DUPLICATE KEY UPDATE total_samples = VALUES(total_samples)
+ ");
+ $stmt->execute(['d' => $date, 't' => $total]);
+
+ echo json_encode(['success' => true, 'date' => $date, 'total_samples' => $total]);
+} catch (Exception $e) {
+ error_log('Save LIMS samples error: ' . $e->getMessage());
+ echo json_encode(['success' => false, 'message' => $e->getMessage()]);
+}