diff --git a/public/userarea/export_to_lims.php b/public/userarea/export_to_lims.php index 76c344b..3ec2720 100644 --- a/public/userarea/export_to_lims.php +++ b/public/userarea/export_to_lims.php @@ -477,6 +477,71 @@ try { $logFilePhotos = $logDir . "commessa_{$commessaId}_photos_step5_2_" . time() . ".txt"; $writeLog($logFilePhotos, $logContentPhotos, "STEP 6.2 - Photos (commessa={$commessaId})"); + // ๐Ÿ”น STEP 6.3: Add Analyses (AnalisiCampione) via Campione({id})/AddAnalisi bound action + $stmt = $pdo->prepare(" + SELECT part_id, analysis_recordkey, analysis_name, analysis_method + FROM identification_parts_analyses + WHERE iddatadb = :iddatadb + ORDER BY part_id, id + "); + $stmt->execute(['iddatadb' => $iddatadb]); + $analysesRows = $stmt->fetchAll(PDO::FETCH_ASSOC); + + $partIdToIndex = []; + foreach ($parts as $idx => $part) { + $partIdToIndex[(int)$part['part_id']] = $idx; + } + + $totalAnalyses = count($analysesRows); + $addedAnalyses = 0; + $failedAnalyses = []; + $logContentStep63Analisi = "Analyses for iddatadb={$iddatadb}: total={$totalAnalyses}\n\n"; + + foreach ($analysesRows as $a) { + $partId = (int)$a['part_id']; + $recordKey = trim((string)($a['analysis_recordkey'] ?? '')); + $idx = $partIdToIndex[$partId] ?? null; + + if ($idx === null || !isset($campioni[$idx]) || $recordKey === '') { + $logContentStep63Analisi .= "SKIP (no campione for part_id={$partId} / empty recordkey): '{$recordKey}'\n"; + continue; + } + + $campioneId = (int)($campioni[$idx]['IdCampione'] ?? 0); + if ($campioneId <= 0) { + $logContentStep63Analisi .= "SKIP (invalid IdCampione for part_id={$partId}): '{$recordKey}'\n"; + continue; + } + + $payload = ['RecordKey' => $recordKey]; + $jsonPayload = json_encode($payload, JSON_UNESCAPED_SLASHES); + + $logContentStep63Analisi .= "curl --location --request POST '{$apiBaseUrl}Campione({$campioneId})/AddAnalisi' \\\n" . + "--header 'Content-Type: application/json' \\\n" . + "--header 'Authorization: Bearer โ€ขโ€ขโ€ขโ€ขโ€ขโ€ข' \\\n" . + "--data '{$jsonPayload}'\n"; + + try { + $result = $api->post("Campione({$campioneId})/AddAnalisi", $payload); + $logContentStep63Analisi .= "OK (part_id={$partId}, campione={$campioneId}): " . + ($a['analysis_name'] ?? '') . "\n---\n"; + $addedAnalyses++; + } catch (Exception $e) { + $errMsg = $e->getMessage(); + $logContentStep63Analisi .= "FAIL: {$errMsg}\n---\n"; + $failedAnalyses[] = [ + 'part_id' => $partId, + 'campione_id' => $campioneId, + 'analysis_recordkey' => $recordKey, + 'analysis_name' => $a['analysis_name'] ?? '', + 'error' => $errMsg, + ]; + } + } + + $logFileStep63Analisi = $logDir . "commessa_{$commessaId}_analyses_step63_" . time() . ".txt"; + $writeLog($logFileStep63Analisi, $logContentStep63Analisi, "STEP 6.3 - AddAnalisi (commessa={$commessaId})"); + // ๐Ÿ”น STEP 7: Update Custom Fields for CommessaWeb if (!empty($fieldValues)) { // GET con espansione per CustomField @@ -629,11 +694,15 @@ try { "totalCampioni" => count($campioni), "totalCustomFields" => count($commessaAfterPatch["CommesseCustomFields"] ?? []), "totalPhotos" => count($photos), + "totalAnalyses" => $totalAnalyses, + "addedAnalyses" => $addedAnalyses, + "failedAnalyses" => $failedAnalyses, "message" => "Export successful", "logFiles" => [ "step5_create" => $logFileStep5, "step5_2_photos" => $logFilePhotos, "step6_campioni" => $logFileStep6, + "step63_analyses" => $logFileStep63Analisi, "step7_patch" => $logFileStep7 ?? null, "step9_1_importa" => $logFileStep91, "step10_get" => $logFileStep10 @@ -649,6 +718,7 @@ try { "step5_create" => $logFileStep5 ?? null, "step5_2_photos" => $logFilePhotos ?? null, "step6_campioni" => $logFileStep6 ?? null, + "step63_analyses" => $logFileStep63Analisi ?? null, "step7_patch" => $logFileStep7 ?? null, "step9_1_importa" => $logFileStep91 ?? null, "step10_get" => $logFileStep10 ?? null diff --git a/public/userarea/validate_export.php b/public/userarea/validate_export.php index aad2723..ecbd835 100644 --- a/public/userarea/validate_export.php +++ b/public/userarea/validate_export.php @@ -88,6 +88,58 @@ $validators[] = function (int $iddatadb, array $ctx): array { return []; }; +// 3. All LIMS-mandatory fields must be filled. +$validators[] = function (int $iddatadb, array $ctx): array { + $record = $ctx['record'] ?? null; + if (!$record) { + return []; + } + + $errors = []; + + // Fixed fields (stored as columns in datadb) + foreach (($ctx['requiredFixed'] ?? []) as $key => $label) { + $col = $ctx['fixedAliasMap'][$key] ?? null; + $val = $col !== null ? ($record[$col] ?? null) : null; + if ($val === null || $val === '' || (int) $val === 0) { + $errors[] = [ + 'field' => $key, + 'message' => $label . ' รจ obbligatorio.', + ]; + } + } + + // Custom fields (values stored in import_data_details, keyed by mapping_id) + foreach (($ctx['requiredCustom'] ?? []) as $cf) { + $val = $ctx['customValues'][(int) $cf['mapping_id']] ?? null; + if ($val === null || trim((string) $val) === '') { + $errors[] = [ + 'field' => 'field_label:' . $cf['field_label'], + 'message' => rtrim($cf['field_label'], ': ') . ' รจ obbligatorio.', + ]; + } + } + + return $errors; +}; + +// Logical fixed_field_key - real datadb column (mirrors imported.php $fixedAliasMap) +$fixedAliasMap = [ + 'ClienteResponsabile' => 'cliente_responsabile_id', + 'ClienteFornitore' => 'cliente_fornitore_id', + 'ClienteAnalisi' => 'clienteAnalisi', + 'MoltiplicatorePrezzo' => 'moltiplicatore_prezzo_id', + 'AnagraficaCertestObject' => 'anagrafica_certest_object_id', + 'AnagraficaCertestService' => 'anagrafica_certest_service_id', + 'ConsegnaRichiesta' => 'consegna_richiesta', +]; + +// Fixed keys NOT enforced by the generic mandatory check above: +// - ConsegnaRichiesta: handled by its dedicated validator (also checks the date) +// - ClienteFornitore / ClienteAnalisi: nullable placeholders, sent as null on +// export and accepted by LIMS. +$skipRequiredFixed = ['ConsegnaRichiesta', 'ClienteFornitore', 'ClienteAnalisi']; + // โ”€โ”€ Main โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ try { @@ -104,9 +156,12 @@ try { $iddatadbList = array_column($rows, 'iddatadb'); $placeholders = implode(',', array_fill(0, count($iddatadbList), '?')); - // Records (datadb) for fixed field validation + // Records (datadb) โ€” templateid + fixed-field columns for mandatory validation $stmt = $pdo->prepare(" - SELECT iddatadb, consegna_richiesta + SELECT iddatadb, templateid, consegna_richiesta, + cliente_responsabile_id, moltiplicatore_prezzo_id, + anagrafica_certest_object_id, anagrafica_certest_service_id, + cliente_fornitore_id, clienteAnalisi FROM datadb WHERE iddatadb IN ($placeholders) "); @@ -128,6 +183,62 @@ try { $partsInfo[(int)$r['iddatadb']][] = $r; } + // Mandatory-field config per template + $templateIds = array_values(array_unique(array_filter(array_map( + fn ($r) => (int)($r['templateid'] ?? 0), + $recordsInfo + )))); + + $requiredFixedByTemplate = []; // template_id => [ fixed_field_key => label ] + $requiredCustomByTemplate = []; // template_id => [ { mapping_id, field_label }, ... ] + + if (!empty($templateIds)) { + $tplPlaceholders = implode(',', array_fill(0, count($templateIds), '?')); + + // Required fixed fields (is_required synced from LIMS ObbligatorioWeb) + $stmt = $pdo->prepare(" + SELECT template_id, fixed_field_key + FROM template_fixed_mapping + WHERE template_id IN ($tplPlaceholders) AND is_required = 1 + "); + $stmt->execute($templateIds); + foreach ($stmt->fetchAll(PDO::FETCH_ASSOC) as $r) { + $key = $r['fixed_field_key']; + if (in_array($key, $skipRequiredFixed, true)) { + continue; + } + $requiredFixedByTemplate[(int)$r['template_id']][$key] = $key; + } + + // Required custom fields that are visible in the import grid + $stmt = $pdo->prepare(" + SELECT id AS mapping_id, template_id, field_label + FROM template_mapping + WHERE template_id IN ($tplPlaceholders) + AND is_required = 1 + AND is_visible_import = 1 + "); + $stmt->execute($templateIds); + foreach ($stmt->fetchAll(PDO::FETCH_ASSOC) as $r) { + $requiredCustomByTemplate[(int)$r['template_id']][] = [ + 'mapping_id' => (int)$r['mapping_id'], + 'field_label' => $r['field_label'], + ]; + } + } + + // Custom field values per record (import_data_details.id is the FK to datadb) + $stmt = $pdo->prepare(" + SELECT id AS iddatadb, mapping_id, field_value + FROM import_data_details + WHERE id IN ($placeholders) + "); + $stmt->execute($iddatadbList); + $customValuesByRecord = []; // iddatadb => [ mapping_id => field_value ] + foreach ($stmt->fetchAll(PDO::FETCH_ASSOC) as $r) { + $customValuesByRecord[(int)$r['iddatadb']][(int)$r['mapping_id']] = $r['field_value']; + } + // โ”€โ”€ Run validators per row โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ $results = []; @@ -137,9 +248,15 @@ try { $index = $rowInfo['index']; // Build context for validators + $record = $recordsInfo[$iddatadb] ?? null; + $templateId = (int)($record['templateid'] ?? 0); $ctx = [ - 'record' => $recordsInfo[$iddatadb] ?? null, - 'parts' => $partsInfo[$iddatadb] ?? [], + 'record' => $record, + 'parts' => $partsInfo[$iddatadb] ?? [], + 'fixedAliasMap' => $fixedAliasMap, + 'requiredFixed' => $requiredFixedByTemplate[$templateId] ?? [], + 'requiredCustom' => $requiredCustomByTemplate[$templateId] ?? [], + 'customValues' => $customValuesByRecord[$iddatadb] ?? [], ]; $errors = [];