Nominativo] leggendo la stessa cache di search_clienti.php. * Non chiama l'API: usa cache/clienti.json (già mantenuto aggiornato dalla ricerca clienti). */ function resolveClientNames(array $ids): array { $ids = array_values(array_filter(array_map('intval', $ids), fn($v) => $v > 0)); if (empty($ids)) return []; $cacheFile = __DIR__ . '/cache/clienti.json'; if (!file_exists($cacheFile)) return []; $data = json_decode((string)@file_get_contents($cacheFile), true); $clients = $data['value'] ?? []; if (empty($clients)) return []; // indicizza per IdCliente $byId = []; foreach ($clients as $c) { $cid = (int)($c['IdCliente'] ?? 0); if ($cid > 0) { $byId[$cid] = trim((string)($c['Nominativo'] ?? '')); } } $out = []; foreach ($ids as $id) { if (isset($byId[$id]) && $byId[$id] !== '') { $out[$id] = $byId[$id]; } } return $out; } $db = DBHandlerSelect::getInstance(); $pdo = $db->getConnection(); // input: solo POST (endpoint che invia email — niente attivazione via URL) if ($_SERVER['REQUEST_METHOD'] !== 'POST') { http_response_code(405); echo json_encode(['success' => false, 'message' => 'Metodo non consentito']); exit; } $ids = $_POST['ids'] ?? []; if (!is_array($ids)) $ids = explode(',', (string)$ids); $ids = array_values(array_filter(array_map('intval', $ids), fn($v) => $v > 0)); $templateId = (int)($_POST['template_id'] ?? 0); if (empty($ids)) { echo json_encode(['success' => false, 'message' => 'Nessun id']); exit; } // Fallback: se il template_id non arriva, ricavalo dalla prima commessa if ($templateId <= 0) { $tq = $pdo->prepare("SELECT templateid FROM datadb WHERE iddatadb = ? LIMIT 1"); $tq->execute([$ids[0]]); $templateId = (int)$tq->fetchColumn(); } // tutti i main field del template (possono essere fino a 2), in ordine $mainMappingIds = []; $templateName = ''; if ($templateId > 0) { $s = $pdo->prepare(" SELECT id FROM template_mapping WHERE template_id = ? AND main_field = '1' AND is_visible_import = 1 ORDER BY field_order ASC, id ASC "); $s->execute([$templateId]); $mainMappingIds = array_map('intval', $s->fetchAll(PDO::FETCH_COLUMN)); // nome del template per l'intestazione mail $t = $pdo->prepare("SELECT name FROM excel_templates WHERE id = ? LIMIT 1"); $t->execute([$templateId]); $templateName = (string)$t->fetchColumn(); } // esiti delle commesse $ph = implode(',', array_fill(0, count($ids), '?')); $sql = " SELECT d.iddatadb, d.commessaweb, d.status, d.user_id, d.idclient, d.ClienteFatturazione, CONCAT(u.first_name,' ',u.last_name) AS user_name, u.email AS user_email, u.notify_export_email FROM datadb d LEFT JOIN auth_users u ON d.user_id = u.id WHERE d.iddatadb IN ($ph) ORDER BY FIELD(d.iddatadb, $ph) "; $stmt = $pdo->prepare($sql); $stmt->execute(array_merge($ids, $ids)); $rows = $stmt->fetchAll(PDO::FETCH_ASSOC); if (empty($rows)) { echo json_encode(['success' => false, 'message' => 'Righe non trovate']); exit; } // --- Risoluzione nomi cliente (idclient + ClienteFatturazione) --- $clientIdsToResolve = []; foreach ($rows as $r) { if (!empty($r['idclient'])) $clientIdsToResolve[(int)$r['idclient']] = true; if (!empty($r['ClienteFatturazione'])) $clientIdsToResolve[(int)$r['ClienteFatturazione']] = true; } $clientNames = resolveClientNames(array_keys($clientIdsToResolve)); // [id => nome] // helper: id cliente → nome, con fallback all'id se non risolto $clientLabel = function ($id) use ($clientNames) { $id = (int)$id; if ($id <= 0) return '—'; return htmlspecialchars($clientNames[$id] ?? (string)$id); }; // valori dei main field per ogni iddatadb — concatena i main valorizzati $mainValues = []; if (!empty($mainMappingIds)) { $mph = implode(',', array_fill(0, count($mainMappingIds), '?')); $s = $pdo->prepare(" SELECT id AS iddatadb, mapping_id, field_value FROM import_data_details WHERE id IN ($ph) AND mapping_id IN ($mph) "); $s->execute(array_merge($ids, $mainMappingIds)); // raggruppa per iddatadb, mantenendo l'ordine dei main $byRow = []; // iddatadb => [mapping_id => value] foreach ($s->fetchAll(PDO::FETCH_ASSOC) as $r) { $byRow[(int)$r['iddatadb']][(int)$r['mapping_id']] = $r['field_value']; } foreach ($byRow as $idd => $vals) { $parts = []; foreach ($mainMappingIds as $mid) { // ordine dei main $v = trim((string)($vals[$mid] ?? '')); if ($v !== '') $parts[] = $v; } $mainValues[$idd] = implode(' — ', $parts); // es. "descrizione — style code" } } // analisi senza prezzo (NULL o 0) per ogni iddatadb $noPriceByRow = []; // iddatadb => [ [part_number, part_description, analysis_name], ... ] $s = $pdo->prepare(" SELECT a.iddatadb, MAX(a.analysis_name) AS analysis_name FROM identification_parts_analyses a WHERE a.iddatadb IN ($ph) AND (a.analysis_price IS NULL OR a.analysis_price <= 0) GROUP BY a.iddatadb, a.analysis_recordkey ORDER BY a.iddatadb, analysis_name "); $s->execute($ids); foreach ($s->fetchAll(PDO::FETCH_ASSOC) as $r) { $noPriceByRow[(int)$r['iddatadb']][] = $r; } $noPriceTotal = array_sum(array_map('count', $noPriceByRow)); // destinatari: utente/i dell'export che hanno la notifica attiva + admin fisso $recipients = []; foreach ($rows as $r) { if (!empty($r['user_email']) && (int)($r['notify_export_email'] ?? 1) === 1) { $recipients[$r['user_email']] = true; } } $adminEmail = env('EXPORT_SUMMARY_ADMIN', 'admin@cesoft.io'); // sempre, indipendente dalla preferenza if ($adminEmail) $recipients[$adminEmail] = true; $recipients = array_keys($recipients); // Se dopo il filtro non resta nessuno, non inviare if (empty($recipients)) { echo json_encode(['success' => true, 'message' => 'Nessun destinatario (notifiche disattivate)', 'recipients' => []]); exit; } // conteggi. La mail riflette lo stato in DB al momento dell'invio: una riga ancora 'i' // (export interrotto da timeout/errore di rete, o ancora in corso) NON è un successo. $okCount = 0; $errCount = 0; $pendingCount = 0; foreach ($rows as $r) { if ($r['status'] === 'l') $okCount++; elseif ($r['status'] === 'x') $errCount++; else $pendingCount++; } $hasError = $errCount > 0; $hasPending = $pendingCount > 0; // --- tabella righe --- $rowsHtml = ''; foreach ($rows as $r) { $st = $r['status']; if ($st === 'l') { $label = 'Inviata'; $bg = '#e6f4ea'; $col = '#1e7e34'; } elseif ($st === 'x') { $label = 'ERRORE'; $bg = '#fdecea'; $col = '#c0392b'; } else { $label = 'NON CONFERMATA'; $bg = '#fff8e1'; $col = '#8a6d3b'; } $commessa = htmlspecialchars($r['commessaweb'] ?: '—'); $main = htmlspecialchars($mainValues[(int)$r['iddatadb']] ?? '—'); $committente = $clientLabel($r['idclient'] ?? 0); $fatturazione = $clientLabel($r['ClienteFatturazione'] ?? 0); $rowsHtml .= " {$commessa} {$committente} {$fatturazione} {$main} {$label} "; // sotto-riga: solo le analisi No Price di questa commessa $np = $noPriceByRow[(int)$r['iddatadb']] ?? []; if (!empty($np)) { $items = ''; foreach ($np as $n) { $analysisName = htmlspecialchars((string)($n['analysis_name'] ?: 'Unnamed analysis')); $items .= "
  • {$analysisName}
  • "; } $npCount = count($np); $rowsHtml .= "
    No price  Analisi senza prezzo ({$npCount})
    "; } } if ($hasError) { $headerColor = '#c0392b'; $headerText = 'Export completato con errori'; } elseif ($hasPending) { $headerColor = '#b7791f'; $headerText = 'Export da verificare'; } else { $headerColor = '#1e7e34'; $headerText = 'Export completato con successo'; } $total = count($rows); // Committente / Fatturazione in testata solo se uguali per tutte le righe (batch misti → "vari") $headerClient = function ($col) use ($rows, $clientLabel) { $vals = array_unique(array_map(fn($r) => (int)($r[$col] ?? 0), $rows)); return count($vals) === 1 ? $clientLabel(reset($vals)) : 'vari'; }; $html = "

    {$headerText}

    Riepilogo esportazione verso LIMS" . ($templateName !== '' ? " · Template: " . htmlspecialchars($templateName) . "" : "") . "

    Committente: " . $headerClient('idclient') . "  ·  FatturazioneAvanzata: " . $headerClient('ClienteFatturazione') . "

    Totale commesse: {$total}  •  Inviate: {$okCount}  •  Errori: {$errCount}" . ($pendingCount > 0 ? "  •  Non confermate: {$pendingCount}" : "") . ($noPriceTotal > 0 ? "  •  Analisi No price: {$noPriceTotal}" : "") . "

    {$rowsHtml}
    Commessa Committente Fatturazione Riferimento Status
    " . ($hasError ? "

    ⚠️ Alcune commesse non sono state inviate al LIMS. Controlla la sezione Errors nell'applicazione per i dettagli e per rilanciarle.

    " : "") . ($hasPending ? "

    ⚠️ Per alcune commesse l'esito non è ancora confermato (timeout o errore di rete durante l'invio). Non rilanciarle: ricarica la pagina tra qualche minuto e verifica lo stato.

    " : "") . "
    Messaggio automatico da SmartTRF — non rispondere a questa email.
    "; $subjectPrefix = $hasError ? '⚠️ Errori Export LIMS' : ($hasPending ? '⏳ Export LIMS da verificare' : '✅ Export LIMS OK'); $subject = "{$subjectPrefix} — {$total} commesse ({$okCount} ok, {$errCount} err" . ($pendingCount > 0 ? ", {$pendingCount} non confermate" : "") . ")"; $res = Mailer::send($recipients, $subject, $html); echo json_encode([ 'success' => $res['ok'], 'message' => $res['ok'] ? 'Mail inviata' : ('Mail non inviata: ' . $res['error']), 'recipients' => $recipients, 'counts' => ['ok' => $okCount, 'err' => $errCount, 'pending' => $pendingCount, 'total' => $total], ]);