LIMS value bindings (table json_lims_binding). // Supports two kinds: 'custom' (template_mapping list fields, value -> import_data_details) // and 'fixed' (template_fixed_mapping list fields, id -> datadb column). // --------------------------------------------------------------------------- // Fixed-field metadata // --------------------------------------------------------------------------- function binding_fixed_alias_map(): array { return [ '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-field keys that are LIMS list dropdowns (bindable). ConsegnaRichiesta is a date. function binding_fixed_is_list(string $key): bool { return in_array($key, [ 'ClienteResponsabile', 'ClienteFornitore', 'ClienteAnalisi', 'MoltiplicatorePrezzo', 'AnagraficaCertestObject', 'AnagraficaCertestService', ], true); } function binding_fixed_column(string $key): ?string { return binding_fixed_alias_map()[$key] ?? null; } // Auto-match only the small global lists; the client-based ones are huge / client-specific. function binding_fixed_auto_matchable(string $key): bool { return in_array($key, [ 'MoltiplicatorePrezzo', 'AnagraficaCertestObject', 'AnagraficaCertestService', ], true); } function binding_fixed_label(string $key): string { $labels = [ 'AnagraficaCertestObject' => 'Anagrafica Certest Object', 'AnagraficaCertestService' => 'Anagrafica Certest Service', 'MoltiplicatorePrezzo' => 'Moltiplicatore Prezzo', 'ClienteResponsabile' => 'Cliente Responsabile', 'ClienteFornitore' => 'Cliente Fornitore', 'ClienteAnalisi' => 'Cliente Analisi', ]; return $labels[$key] ?? $key; } // --------------------------------------------------------------------------- // Bindable check (custom mapping row) // --------------------------------------------------------------------------- function binding_is_list_field(array $mapping): bool { $hasList = (int) ($mapping['has_list'] ?? 0) === 1; $isMultiChoice = strcasecmp(trim((string) ($mapping['data_type'] ?? '')), 'SceltaMultipla') === 0; return $hasList || $isMultiChoice; } // --------------------------------------------------------------------------- // Target keys + lookup / upsert // --------------------------------------------------------------------------- function binding_target_custom(int $mappingId): string { return 'cf:' . $mappingId; } function binding_target_fixed(int $templateId, string $fixedKey): string { return 'fx:' . $templateId . ':' . $fixedKey; } function binding_lookup_target(PDO $pdo, string $targetKey, string $jsonValue): ?array { $stmt = $pdo->prepare( "SELECT id, lims_value_id, lims_value FROM json_lims_binding WHERE target_key = ? AND json_value = ? LIMIT 1" ); $stmt->execute([$targetKey, $jsonValue]); $row = $stmt->fetch(PDO::FETCH_ASSOC); return $row ?: null; } function binding_lookup(PDO $pdo, int $mappingId, string $jsonValue): ?array { return binding_lookup_target($pdo, binding_target_custom($mappingId), $jsonValue); } function binding_lookup_fixed(PDO $pdo, int $templateId, string $fixedKey, string $jsonValue): ?array { return binding_lookup_target($pdo, binding_target_fixed($templateId, $fixedKey), $jsonValue); } function binding_upsert_row( PDO $pdo, string $kind, int $templateId, ?int $mappingId, ?string $fixedFieldKey, string $targetKey, int $fieldId, string $jsonValue, int $limsValueId, string $limsValue, ?int $createdBy ): void { $stmt = $pdo->prepare( "INSERT INTO json_lims_binding (template_id, binding_kind, mapping_id, fixed_field_key, target_key, field_id, json_value, lims_value_id, lims_value, created_by) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?) ON DUPLICATE KEY UPDATE lims_value_id = VALUES(lims_value_id), lims_value = VALUES(lims_value), field_id = VALUES(field_id), template_id = VALUES(template_id), binding_kind = VALUES(binding_kind), mapping_id = VALUES(mapping_id), fixed_field_key = VALUES(fixed_field_key)" ); $stmt->execute([ $templateId, $kind, $mappingId, $fixedFieldKey, $targetKey, $fieldId, $jsonValue, $limsValueId, $limsValue, $createdBy, ]); } function binding_upsert( PDO $pdo, int $templateId, int $mappingId, int $fieldId, string $jsonValue, int $limsValueId, string $limsValue, ?int $createdBy ): void { binding_upsert_row( $pdo, 'custom', $templateId, $mappingId, null, binding_target_custom($mappingId), $fieldId, $jsonValue, $limsValueId, $limsValue, $createdBy ); } function binding_upsert_fixed( PDO $pdo, int $templateId, string $fixedKey, string $jsonValue, int $limsValueId, string $limsValue, ?int $createdBy ): void { binding_upsert_row( $pdo, 'fixed', $templateId, null, $fixedKey, binding_target_fixed($templateId, $fixedKey), 0, $jsonValue, $limsValueId, $limsValue, $createdBy ); } function binding_delete_target(PDO $pdo, string $targetKey, string $jsonValue): int { $stmt = $pdo->prepare("DELETE FROM json_lims_binding WHERE target_key = ? AND json_value = ?"); $stmt->execute([$targetKey, $jsonValue]); return $stmt->rowCount(); } // --------------------------------------------------------------------------- // Custom-field LIMS values (cache/customfield_{id}.json) + auto-match // --------------------------------------------------------------------------- function binding_get_lims_values(int $fieldId): array { static $memo = []; if ($fieldId <= 0) { return []; } if (array_key_exists($fieldId, $memo)) { return $memo[$fieldId]; } $cacheDir = dirname(__DIR__) . '/cache'; $cacheFile = $cacheDir . '/customfield_' . $fieldId . '.json'; try { if (file_exists($cacheFile) && (time() - filemtime($cacheFile) < 3600)) { $values = json_decode(file_get_contents($cacheFile), true); } else { require_once __DIR__ . '/VisualLimsApiClient.class.php'; $api = VisualLimsApiClient::getInstance(); $data = $api->get("CustomField($fieldId)?\$expand=CustomFieldsValues"); $values = $data['CustomFieldsValues'] ?? []; if (!is_dir($cacheDir)) { mkdir($cacheDir, 0777, true); } file_put_contents($cacheFile, json_encode($values)); } } catch (Throwable $e) { error_log("binding_get_lims_values failed for field $fieldId: " . $e->getMessage()); $values = []; } if (!is_array($values)) { $values = []; } return $memo[$fieldId] = $values; } // Exactly one case-insensitive match by Valore -> that value, otherwise null. function binding_auto_match(array $limsValues, string $jsonValue): ?array { $needle = mb_strtolower(trim($jsonValue)); if ($needle === '') { return null; } $matches = []; foreach ($limsValues as $v) { $valore = (string) ($v['Valore'] ?? ''); if (mb_strtolower(trim($valore)) === $needle) { $matches[] = $v; } } return count($matches) === 1 ? $matches[0] : null; } // --------------------------------------------------------------------------- // Fixed-field LIMS values (per-field source) + auto-match // --------------------------------------------------------------------------- function binding_template_idclient(PDO $pdo, int $templateId): int { $stmt = $pdo->prepare("SELECT idclient FROM excel_templates WHERE id = ?"); $stmt->execute([$templateId]); return (int) ($stmt->fetchColumn() ?: 0); } function binding_client_label(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: ' . $id . ')'; } // Fetch an OData payload with a 1-hour file cache. function binding_cached_get($api, string $cacheFile, string $endpoint): array { if (file_exists($cacheFile) && (time() - filemtime($cacheFile) < 3600)) { $data = json_decode(file_get_contents($cacheFile), true); } else { $data = $api->get($endpoint); $dir = dirname($cacheFile); if (!is_dir($dir)) { mkdir($dir, 0777, true); } file_put_contents($cacheFile, json_encode($data)); } return is_array($data) ? $data : []; } // Normalized LIMS value list for a fixed field: [['id' => int, 'text' => string], ...]. function binding_get_fixed_values(PDO $pdo, string $fieldKey, int $templateId): array { static $memo = []; $memoKey = $fieldKey . '|' . $templateId; if (isset($memo[$memoKey])) { return $memo[$memoKey]; } $cacheDir = dirname(__DIR__) . '/cache'; $out = []; try { require_once __DIR__ . '/VisualLimsApiClient.class.php'; $api = VisualLimsApiClient::getInstance(); switch ($fieldKey) { case 'MoltiplicatorePrezzo': $data = binding_cached_get($api, "$cacheDir/moltiplicatori_prezzo.json", 'MoltiplicatorePrezzi'); foreach (($data['value'] ?? []) as $r) { $out[] = ['id' => (int) ($r['IdMoltiplicatorePrezzo'] ?? 0), 'text' => (string) ($r['Descrizione'] ?? '')]; } break; case 'AnagraficaCertestObject': case 'AnagraficaCertestService': $file = $fieldKey === 'AnagraficaCertestObject' ? 'anagrafica_certest_object.json' : 'anagrafica_certest_service.json'; $data = binding_cached_get($api, "$cacheDir/$file", $fieldKey); foreach (($data['value'] ?? []) as $r) { $code = trim((string) ($r['Codice'] ?? '')); $text = ($code !== '' ? $code . ' - ' : '') . (string) ($r['NomeAnagrafica'] ?? ''); $out[] = ['id' => (int) ($r['IdAnagrafica'] ?? 0), 'text' => $text]; } break; case 'ClienteResponsabile': $idCliente = binding_template_idclient($pdo, $templateId); if ($idCliente > 0) { $data = binding_cached_get($api, "$cacheDir/cliente_responsabili_$idCliente.json", "Cliente($idCliente)?\$expand=Responsabili"); foreach (($data['Responsabili'] ?? []) as $r) { $out[] = ['id' => (int) ($r['IdClienteResponsabile'] ?? 0), 'text' => (string) ($r['Nominativo'] ?? '')]; } } break; case 'ClienteFornitore': case 'ClienteAnalisi': $endpoint = 'Cliente?' . http_build_query(['$select' => 'IdCliente,Nominativo,CodiceCliente', '$orderby' => 'Nominativo asc']); $data = binding_cached_get($api, "$cacheDir/clienti.json", $endpoint); foreach (($data['value'] ?? []) as $r) { $out[] = ['id' => (int) ($r['IdCliente'] ?? 0), 'text' => binding_client_label($r)]; } break; } } catch (Throwable $e) { error_log("binding_get_fixed_values($fieldKey) failed: " . $e->getMessage()); $out = []; } return $memo[$memoKey] = $out; } // Exactly one case-insensitive match by text on a [{id,text}] list, otherwise null. function binding_auto_match_fixed(array $values, string $jsonValue): ?array { $needle = mb_strtolower(trim($jsonValue)); if ($needle === '') { return null; } $matches = []; foreach ($values as $v) { if (mb_strtolower(trim((string) ($v['text'] ?? ''))) === $needle) { $matches[] = $v; } } return count($matches) === 1 ? $matches[0] : null; } // --------------------------------------------------------------------------- // JSON node -> column matching (shared with import_insert custom logic) // --------------------------------------------------------------------------- function binding_find_column_index(string $sourceColumn, array $columns): int { $sourceColumn = trim($sourceColumn); if ($sourceColumn === '') { return -1; } $columnsTrimmed = array_map('trim', $columns); $candidates = [ $sourceColumn, preg_replace('/^data\[\]\./', '', $sourceColumn), preg_replace('/^data\.0\./', '', $sourceColumn), str_replace('data[].', 'data.0.', $sourceColumn), str_replace('data.0.', 'data[].', $sourceColumn), ]; $candidates = array_values(array_unique(array_filter(array_map('trim', $candidates), function ($v) { return $v !== ''; }))); foreach ($candidates as $c) { $i = array_search($c, $columnsTrimmed, true); if ($i !== false) { return (int) $i; } } return -1; } // --------------------------------------------------------------------------- // Apply resolved values // --------------------------------------------------------------------------- // Custom: write the resolved LIMS text into import_data_details for the given datadb ids. function binding_apply_to_details( PDO $pdo, int $mappingId, string $limsValue, array $datadbIds ): int { $datadbIds = array_values(array_filter(array_map('intval', $datadbIds))); if (empty($datadbIds)) { return 0; } $placeholders = implode(',', array_fill(0, count($datadbIds), '?')); $sql = "UPDATE import_data_details SET field_value = ? WHERE mapping_id = ? AND id IN ($placeholders)"; $params = array_merge([$limsValue, $mappingId], $datadbIds); $stmt = $pdo->prepare($sql); $stmt->execute($params); return $stmt->rowCount(); } // Fixed: write the resolved LIMS id into a whitelisted datadb column for the given ids. // $limsValueId null clears the column. function binding_apply_to_datadb( PDO $pdo, string $column, ?int $limsValueId, array $datadbIds ): int { if (!in_array($column, array_values(binding_fixed_alias_map()), true)) { throw new InvalidArgumentException("Invalid fixed-field column: $column"); } $datadbIds = array_values(array_filter(array_map('intval', $datadbIds))); if (empty($datadbIds)) { return 0; } $placeholders = implode(',', array_fill(0, count($datadbIds), '?')); $sql = "UPDATE datadb SET `$column` = ? WHERE iddatadb IN ($placeholders)"; $stmt = $pdo->prepare($sql); $stmt->execute(array_merge([$limsValueId], $datadbIds)); return $stmt->rowCount(); }