diff --git a/public/userarea/bindings_manage.php b/public/userarea/bindings_manage.php
index a5db220..032a13e 100644
--- a/public/userarea/bindings_manage.php
+++ b/public/userarea/bindings_manage.php
@@ -1,33 +1,140 @@
getConnection();
-// Filtro opzionale per template.
+// Filtri comuni.
$templateFilter = isset($_GET['template_id']) ? intval($_GET['template_id']) : 0;
+$search = trim($_GET['q'] ?? '');
+$perPage = 50;
+$page = max(1, intval($_GET['page'] ?? 1));
+$dir = (strtolower($_GET['dir'] ?? 'asc') === 'desc') ? 'DESC' : 'ASC';
+
+// Modalita': overview (gruppi per campo) oppure detail (valori di un campo).
+$focusTarget = trim($_GET['target'] ?? '');
+$mode = $focusTarget !== '' ? 'detail' : 'overview';
$templates = $pdo->query("SELECT id, name FROM excel_templates ORDER BY name")->fetchAll(PDO::FETCH_ASSOC);
-$where = '';
+// Helper: URL che preserva i filtri correnti.
+$bmUrl = function (array $ov) use ($templateFilter, $search, $dir, $page, $focusTarget) {
+ $base = [
+ 'template_id' => $templateFilter ?: null,
+ 'q' => $search !== '' ? $search : null,
+ 'order' => $_GET['order'] ?? null,
+ 'dir' => strtolower($dir),
+ 'page' => $page,
+ 'target' => $focusTarget !== '' ? $focusTarget : null,
+ ];
+ $p = array_filter(array_merge($base, $ov), fn($v) => $v !== null && $v !== '');
+ return 'bindings_manage.php?' . http_build_query($p);
+};
+
+// Filtri WHERE comuni (template/kind/search).
+$conds = [];
$params = [];
if ($templateFilter > 0) {
- $where = 'WHERE b.template_id = ?';
+ $conds[] = 'b.template_id = ?';
$params[] = $templateFilter;
}
-$sql = "SELECT b.id, b.template_id, b.mapping_id, b.field_id, b.json_value,
- b.lims_value_id, b.lims_value, b.updated_at,
- t.name AS template_name,
- m.field_label
+if ($mode === 'detail') {
+ // ---- DETAIL: valori (json_value -> lims) di un singolo campo (target_key) ----
+ $conds[] = 'b.target_key = ?';
+ $params[] = $focusTarget;
+ if ($search !== '') {
+ $conds[] = '(b.json_value LIKE ? OR b.lims_value LIKE ?)';
+ $like = '%' . $search . '%';
+ array_push($params, $like, $like);
+ }
+ $where = 'WHERE ' . implode(' AND ', $conds);
+
+ $orderCols = ['json' => 'b.json_value', 'lims' => 'b.lims_value'];
+ $order = array_key_exists($_GET['order'] ?? '', $orderCols) ? $_GET['order'] : 'json';
+ $orderSql = $orderCols[$order] . ' ' . $dir;
+
+ $countStmt = $pdo->prepare("SELECT COUNT(*) FROM json_lims_binding b $where");
+ $countStmt->execute($params);
+ $total = (int) $countStmt->fetchColumn();
+ $totalPages = max(1, (int) ceil($total / $perPage));
+ if ($page > $totalPages) $page = $totalPages;
+ $offset = ($page - 1) * $perPage;
+
+ $sql = "SELECT b.id, b.template_id, b.binding_kind, b.mapping_id, b.fixed_field_key, b.field_id,
+ b.json_value, b.lims_value_id, b.lims_value,
+ t.name AS template_name, m.field_label
+ FROM json_lims_binding b
+ LEFT JOIN excel_templates t ON t.id = b.template_id
+ LEFT JOIN template_mapping m ON m.id = b.mapping_id
+ $where
+ ORDER BY $orderSql, b.json_value ASC
+ LIMIT $perPage OFFSET $offset";
+ $stmt = $pdo->prepare($sql);
+ $stmt->execute($params);
+ $rows = $stmt->fetchAll(PDO::FETCH_ASSOC);
+
+ // Intestazione del gruppo (tutte le righe condividono campo/template/kind).
+ $head = $rows[0] ?? null;
+ if (!$head) {
+ // Target vuoto (es. dopo aver svuotato il campo): ricavo i meta dal target_key.
+ $head = ['binding_kind' => str_starts_with($focusTarget, 'fx:') ? 'fixed' : 'custom'];
+ }
+ $headKind = $head['binding_kind'] ?? 'custom';
+ if ($headKind === 'fixed') {
+ $headFixedKey = $head['fixed_field_key'] ?? (explode(':', $focusTarget)[2] ?? '');
+ $headLabel = binding_fixed_label((string) $headFixedKey);
+ $headTpl = (int) ($head['template_id'] ?? (explode(':', $focusTarget)[1] ?? 0));
+ } else {
+ $headLabel = $head['field_label'] ?? ('mapping ' . ($head['mapping_id'] ?? ''));
+ $headTpl = (int) ($head['template_id'] ?? 0);
+ }
+ $headTplName = $headTpl ? ($pdo->query("SELECT name FROM excel_templates WHERE id=" . $headTpl)->fetchColumn() ?: ('#' . $headTpl)) : '';
+} else {
+ // ---- OVERVIEW: un gruppo per campo (target_key) con il conteggio ----
+ if ($search !== '') {
+ $conds[] = '(t.name LIKE ? OR m.field_label LIKE ? OR b.fixed_field_key LIKE ?)';
+ $like = '%' . $search . '%';
+ array_push($params, $like, $like, $like);
+ }
+ $where = $conds ? ('WHERE ' . implode(' AND ', $conds)) : '';
+
+ $orderCols = ['template' => 'template_name', 'field' => 'field_label', 'count' => 'cnt'];
+ $order = array_key_exists($_GET['order'] ?? '', $orderCols) ? $_GET['order'] : 'template';
+ $orderSql = $orderCols[$order] . ' ' . $dir;
+
+ $countStmt = $pdo->prepare("SELECT COUNT(DISTINCT b.target_key)
FROM json_lims_binding b
LEFT JOIN excel_templates t ON t.id = b.template_id
- LEFT JOIN template_mapping m ON m.id = b.mapping_id
- $where
- ORDER BY t.name, m.field_label, b.json_value";
-$stmt = $pdo->prepare($sql);
-$stmt->execute($params);
-$bindings = $stmt->fetchAll(PDO::FETCH_ASSOC);
+ LEFT JOIN template_mapping m ON m.id = b.mapping_id $where");
+ $countStmt->execute($params);
+ $total = (int) $countStmt->fetchColumn();
+ $totalPages = max(1, (int) ceil($total / $perPage));
+ if ($page > $totalPages) $page = $totalPages;
+ $offset = ($page - 1) * $perPage;
+
+ $sql = "SELECT b.target_key, b.template_id, b.binding_kind, b.mapping_id, b.fixed_field_key,
+ MAX(t.name) AS template_name, MAX(m.field_label) AS field_label,
+ COUNT(*) AS cnt, MAX(b.updated_at) AS last_updated
+ FROM json_lims_binding b
+ LEFT JOIN excel_templates t ON t.id = b.template_id
+ LEFT JOIN template_mapping m ON m.id = b.mapping_id
+ $where
+ GROUP BY b.target_key, b.template_id, b.binding_kind, b.mapping_id, b.fixed_field_key
+ ORDER BY $orderSql, template_name ASC
+ LIMIT $perPage OFFSET $offset";
+ $stmt = $pdo->prepare($sql);
+ $stmt->execute($params);
+ $groups = $stmt->fetchAll(PDO::FETCH_ASSOC);
+}
+
+$sortLink = function (string $col, string $label) use ($bmUrl, $order, $dir) {
+ $nextDir = ($order === $col && $dir === 'ASC') ? 'desc' : 'asc';
+ $caret = $order === $col ? ($dir === 'ASC' ? ' ▲' : ' ▼') : '';
+ return '' . $label . $caret . '';
+};
?>
@@ -51,6 +158,14 @@ $bindings = $stmt->fetchAll(PDO::FETCH_ASSOC);
.row-status {
font-size: 12px;
}
+
+ .group-row {
+ cursor: pointer;
+ }
+
+ .group-row:hover {
+ background-color: #f1f5ff;
+ }
Gestione Binding JSON → LIMS - = htmlspecialchars($titlewebsite ?? '', ENT_QUOTES, 'UTF-8'); ?>
@@ -66,82 +181,156 @@ $bindings = $stmt->fetchAll(PDO::FETCH_ASSOC);
-
- Le modifiche ai binding si applicano alle importazioni future.
- I record già importati non vengono ricalcolati.
-
+
+
+
+
+ Tutti i campi
+
+
+ = htmlspecialchars($headLabel) ?>
+ fixed
+ · = htmlspecialchars((string) $headTplName) ?>
+
+
+
= $total ?> valore/i · pagina = $page ?>/= $totalPages ?>
+
-
-
-
-
- | Template |
- Campo (template_mapping) |
- Valore JSON |
- Valore LIMS |
- Azioni |
-
-
-
-
-
- | Nessun binding presente. |
+
+
+
+
+ | = $sortLink('json', 'Valore JSON') ?> |
+ = $sortLink('lims', 'Valore LIMS') ?> |
+ Azioni |
-
-
-
- | = htmlspecialchars($b['template_name'] ?? ('#' . $b['template_id'])) ?> |
- = htmlspecialchars($b['field_label'] ?? ('mapping ' . $b['mapping_id'])) ?> |
- = htmlspecialchars($b['json_value']) ?> |
-
-
-
- |
-
-
-
- |
+
+
+
+
+ | Nessun valore per questo campo. |
-
-
-
-
-
+
+
+
+
+ | = htmlspecialchars($b['json_value']) ?> |
+
+
+
+ |
+
+
+
+ |
+
+
+
+
+
+
+
+
+
+ Scegli un campo per gestirne i valori. Le modifiche valgono per le importazioni future.
+
+
= $total ?> camp= $total === 1 ? 'o' : 'i' ?> con binding · pagina = $page ?>/= $totalPages ?>
+
+
+
+
+
+ | = $sortLink('template', 'Template') ?> |
+ = $sortLink('field', 'Campo') ?> |
+ = $sortLink('count', '# Binding') ?> |
+ |
+
+
+
+
+
+ | Nessun binding presente. |
+
+
+
+ $g['target_key'], 'page' => 1, 'order' => null, 'q' => null]);
+ ?>
+
+ | = htmlspecialchars($g['template_name'] ?? ('#' . $g['template_id'])) ?> |
+
+ = htmlspecialchars($gLabel) ?>
+ fixed
+ campo rimosso
+ |
+ = (int) $g['cnt'] ?> |
+ Apri |
+
+
+
+
+
+
+
+
+ 1): ?>
+
+
@@ -159,48 +348,28 @@ $bindings = $stmt->fetchAll(PDO::FETCH_ASSOC);
$(function() {
const $globalError = $('#globalError');
- // Filtro testuale generale su tutte le colonne.
- const $rows = () => $('#bindingsTable tbody tr').not('.no-data-row');
- $('#bindingSearch').on('input', function() {
- const q = $(this).val().toLowerCase().trim();
- $rows().each(function() {
- const text = $(this).text().toLowerCase();
- $(this).toggle(q === '' || text.indexOf(q) !== -1);
- });
- });
-
- // Ordinamento per colonna (click sull'intestazione).
- $('#bindingsTable thead .sortable').on('click', function() {
- const col = +$(this).data('col');
- const asc = !($(this).data('asc'));
- $('#bindingsTable thead .sortable').data('asc', null).find('.sort-caret').html('');
- $(this).data('asc', asc).find('.sort-caret').html(asc ? ' ▲' : ' ▼');
-
- const $tbody = $('#bindingsTable tbody');
- const rows = $tbody.find('tr').not('.no-data-row').get();
- rows.sort((a, b) => {
- const x = $(a).find('td').eq(col).text().trim().toLowerCase();
- const y = $(b).find('td').eq(col).text().trim().toLowerCase();
- return asc ? x.localeCompare(y) : y.localeCompare(x);
- });
- rows.forEach(r => $tbody.append(r));
- });
-
- $('.binding-select').each(function() {
- const fieldId = $(this).data('field-id');
+ // Select2 solo nelle righe valore della pagina corrente (max 50), saltando le disabilitate.
+ $('.binding-select').not(':disabled').each(function() {
+ const $row = $(this).closest('tr');
+ const isFixed = $row.data('kind') === 'fixed';
const initialVal = $(this).val();
$(this).select2({
width: '220px',
ajax: {
- url: 'search_customfield_values.php',
+ url: isFixed ? 'search_fixed_field_values.php' : 'search_customfield_values.php',
dataType: 'json',
delay: 200,
- data: params => ({
- field_id: fieldId,
+ data: params => isFixed ? {
+ field_key: $row.data('fixed-key'),
+ template_id: $row.data('template-id'),
q: params.term || '',
limit: 50
- }),
+ } : {
+ field_id: $row.data('field-id'),
+ q: params.term || '',
+ limit: 50
+ },
processResults: data => ({
results: data.results || []
})
@@ -208,7 +377,6 @@ $bindings = $stmt->fetchAll(PDO::FETCH_ASSOC);
minimumInputLength: 0
});
- // Abilito Salva solo quando il valore cambia davvero.
$(this).data('original-val', initialVal);
});
@@ -229,9 +397,13 @@ $bindings = $stmt->fetchAll(PDO::FETCH_ASSOC);
$btn.prop('disabled', true);
$status.text('Salvataggio...').removeClass('text-success text-danger').addClass('text-muted');
+ const isFixed = $row.data('kind') === 'fixed';
+ const targetFields = isFixed
+ ? { kind: 'fixed', fixed_field_key: $row.data('fixed-key') }
+ : { kind: 'custom', mapping_id: $row.data('mapping-id'), field_id: $row.data('field-id') };
+
$.post('save_binding.php', {
- mapping_id: $row.data('mapping-id'),
- field_id: $row.data('field-id'),
+ ...targetFields,
template_id: $row.data('template-id'),
json_value: String($row.data('json-value')),
lims_value_id: $select.val(),
diff --git a/public/userarea/class/binding-functions.php b/public/userarea/class/binding-functions.php
index faaa9cf..06930a9 100644
--- a/public/userarea/class/binding-functions.php
+++ b/public/userarea/class/binding-functions.php
@@ -1,6 +1,70 @@
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
{
@@ -9,19 +73,75 @@ function binding_is_list_field(array $mapping): bool
return $hasList || $isMultiChoice;
}
-function binding_lookup(PDO $pdo, int $mappingId, string $jsonValue): ?array
+// ---------------------------------------------------------------------------
+// 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 mapping_id = ? AND json_value = ?
+ WHERE target_key = ? AND json_value = ?
LIMIT 1"
);
- $stmt->execute([$mappingId, $jsonValue]);
+ $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,
@@ -32,20 +152,38 @@ function binding_upsert(
string $limsValue,
?int $createdBy
): void {
- $stmt = $pdo->prepare(
- "INSERT INTO json_lims_binding
- (template_id, mapping_id, 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_upsert_row(
+ $pdo, 'custom', $templateId, $mappingId, null,
+ binding_target_custom($mappingId), $fieldId, $jsonValue, $limsValueId, $limsValue, $createdBy
);
- $stmt->execute([$templateId, $mappingId, $fieldId, $jsonValue, $limsValueId, $limsValue, $createdBy]);
}
-// LIMS list values for a field, reusing the cache/customfield_{id}.json cache (1h).
+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 = [];
@@ -103,7 +241,166 @@ function binding_auto_match(array $limsValues, string $jsonValue): ?array
return count($matches) === 1 ? $matches[0] : null;
}
-// Scrive il valore risolto sui record indicati (datadb_ids gia' delimita l'import corrente).
+// ---------------------------------------------------------------------------
+// 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,
@@ -126,3 +423,26 @@ function binding_apply_to_details(
$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();
+}
diff --git a/public/userarea/import_insert.php b/public/userarea/import_insert.php
index a0fb58a..0766926 100644
--- a/public/userarea/import_insert.php
+++ b/public/userarea/import_insert.php
@@ -84,10 +84,22 @@ foreach ($allMappings as $mapping) {
}
}
+// Campi fixed mappati da JSON (default_source='json') per questo template.
+$fixedJsonFields = [];
+if ($source_type === 'json') {
+ $fxStmt = $pdo->prepare("
+ SELECT fixed_field_key, json_node, data_type
+ FROM template_fixed_mapping
+ WHERE template_id = ? AND default_source = 'json' AND json_node IS NOT NULL AND json_node <> ''
+ ");
+ $fxStmt->execute([$template_id]);
+ $fixedJsonFields = $fxStmt->fetchAll(PDO::FETCH_ASSOC);
+}
+
// Inserisci le righe selezionate in datadb
$insertedIds = [];
-// Binding JSON -> LIMS senza corrispondenza salvata, per "mapping_id|json_value".
+// Binding JSON -> LIMS senza corrispondenza salvata, per "kind:key|json_value".
$pendingBindings = [];
// Binding risolti in automatico durante questo import (solo per visualizzazione).
$autoBindings = [];
@@ -258,9 +270,10 @@ foreach ($selected_rows as $loopIndex => $rowIndex) {
if ($existing) {
$fieldValue = $existing['lims_value'];
- $key = $mapping['id'] . '|' . $jsonValue;
+ $key = 'cf:' . $mapping['id'] . '|' . $jsonValue;
if (!isset($savedBindings[$key])) {
$savedBindings[$key] = [
+ 'kind' => 'custom',
'mapping_id' => (int) $mapping['id'],
'field_id' => (int) $mapping['field_id'],
'field_label' => $mapping['field_label'],
@@ -289,9 +302,10 @@ foreach ($selected_rows as $loopIndex => $rowIndex) {
);
$fieldValue = (string) $autoMatch['Valore'];
- $key = $mapping['id'] . '|' . $jsonValue;
+ $key = 'cf:' . $mapping['id'] . '|' . $jsonValue;
if (!isset($autoBindings[$key])) {
$autoBindings[$key] = [
+ 'kind' => 'custom',
'mapping_id' => (int) $mapping['id'],
'field_id' => (int) $mapping['field_id'],
'field_label' => $mapping['field_label'],
@@ -303,9 +317,10 @@ foreach ($selected_rows as $loopIndex => $rowIndex) {
}
$autoBindings[$key]['datadb_ids'][] = (int) $iddatadb;
} else {
- $key = $mapping['id'] . '|' . $jsonValue;
+ $key = 'cf:' . $mapping['id'] . '|' . $jsonValue;
if (!isset($pendingBindings[$key])) {
$pendingBindings[$key] = [
+ 'kind' => 'custom',
'mapping_id' => (int) $mapping['id'],
'field_id' => (int) $mapping['field_id'],
'field_label' => $mapping['field_label'],
@@ -326,6 +341,104 @@ foreach ($selected_rows as $loopIndex => $rowIndex) {
$stmt->execute([$iddatadb, $mapping['id'], $fieldValue]);
error_log("Inserted into import_data_details for ID $iddatadb, Mapping ID: " . $mapping['id'] . ", Field Value: " . var_export($fieldValue, true));
}
+
+ // ---- Fixed fields mappati da JSON (scrivono colonne datadb) ----
+ if ($source_type === 'json' && !empty($fixedJsonFields)) {
+ $fixedUpdates = []; // colonna datadb => valore (id LIMS o data)
+
+ foreach ($fixedJsonFields as $fx) {
+ $fixedKey = $fx['fixed_field_key'];
+ $column = binding_fixed_column($fixedKey);
+ if (!$column) {
+ continue;
+ }
+
+ $idx = binding_find_column_index((string) $fx['json_node'], $columns);
+ $raw = ($idx >= 0 && isset($row[$idx])) ? trim((string) $row[$idx]) : '';
+ if ($raw === '') {
+ continue;
+ }
+
+ // Campo non a lista (es. ConsegnaRichiesta DATE): scrivo il valore direttamente.
+ if (!binding_fixed_is_list($fixedKey)) {
+ if (($fx['data_type'] ?? '') === 'DATE') {
+ $fixedUpdates[$column] = date('Y-m-d', strtotime($raw));
+ }
+ continue;
+ }
+
+ // Binding gia' salvato.
+ $existing = binding_lookup_fixed($pdo, (int) $template_id, $fixedKey, $raw);
+ if ($existing) {
+ $fixedUpdates[$column] = (int) $existing['lims_value_id'];
+ $key = 'fx:' . $fixedKey . '|' . $raw;
+ if (!isset($savedBindings[$key])) {
+ $savedBindings[$key] = [
+ 'kind' => 'fixed',
+ 'fixed_field_key' => $fixedKey,
+ 'field_label' => binding_fixed_label($fixedKey),
+ 'json_value' => $raw,
+ 'lims_value' => (string) $existing['lims_value'],
+ 'lims_value_id' => (int) $existing['lims_value_id'],
+ 'datadb_ids' => [],
+ ];
+ }
+ $savedBindings[$key]['datadb_ids'][] = (int) $iddatadb;
+ continue;
+ }
+
+ // Auto-match 1-a-1 (solo per le liste globali piccole).
+ $autoMatch = null;
+ if (binding_fixed_auto_matchable($fixedKey)) {
+ $fixedValues = binding_get_fixed_values($pdo, $fixedKey, (int) $template_id);
+ $autoMatch = binding_auto_match_fixed($fixedValues, $raw);
+ }
+
+ if ($autoMatch) {
+ binding_upsert_fixed($pdo, (int) $template_id, $fixedKey, $raw, (int) $autoMatch['id'], (string) $autoMatch['text'], $user_id);
+ $fixedUpdates[$column] = (int) $autoMatch['id'];
+ $key = 'fx:' . $fixedKey . '|' . $raw;
+ if (!isset($autoBindings[$key])) {
+ $autoBindings[$key] = [
+ 'kind' => 'fixed',
+ 'fixed_field_key' => $fixedKey,
+ 'field_label' => binding_fixed_label($fixedKey),
+ 'json_value' => $raw,
+ 'lims_value' => (string) $autoMatch['text'],
+ 'lims_value_id' => (int) $autoMatch['id'],
+ 'datadb_ids' => [],
+ ];
+ }
+ $autoBindings[$key]['datadb_ids'][] = (int) $iddatadb;
+ continue;
+ }
+
+ // Nessuna corrispondenza: colonna lasciata vuota, segnalo come pending.
+ $key = 'fx:' . $fixedKey . '|' . $raw;
+ if (!isset($pendingBindings[$key])) {
+ $pendingBindings[$key] = [
+ 'kind' => 'fixed',
+ 'fixed_field_key' => $fixedKey,
+ 'field_label' => binding_fixed_label($fixedKey),
+ 'json_value' => $raw,
+ 'datadb_ids' => [],
+ ];
+ }
+ $pendingBindings[$key]['datadb_ids'][] = (int) $iddatadb;
+ }
+
+ if (!empty($fixedUpdates)) {
+ $setParts = [];
+ $params = [];
+ foreach ($fixedUpdates as $col => $val) {
+ $setParts[] = "`$col` = ?";
+ $params[] = $val;
+ }
+ $params[] = (int) $iddatadb;
+ $upd = $pdo->prepare("UPDATE datadb SET " . implode(', ', $setParts) . " WHERE iddatadb = ?");
+ $upd->execute($params);
+ }
+ }
}
$_SESSION['inserted_ids'] = $insertedIds;
diff --git a/public/userarea/resolve_bindings.php b/public/userarea/resolve_bindings.php
index 5178881..834274c 100644
--- a/public/userarea/resolve_bindings.php
+++ b/public/userarea/resolve_bindings.php
@@ -37,6 +37,37 @@ foreach ($savedItems as $s) {
$resolvedItems[] = $s;
}
+// Raggruppa le righe per campo (custom: mapping_id, fixed: fixed_field_key),
+// mantenendo l'ordine di prima apparizione. Ogni gruppo elenca prima i pending.
+$groups = [];
+$groupOrder = [];
+$pushToGroup = function (array $row, string $type, $idx = null) use (&$groups, &$groupOrder) {
+ $kind = $row['kind'] ?? 'custom';
+ $gkey = $kind === 'fixed'
+ ? 'fx:' . ($row['fixed_field_key'] ?? '')
+ : 'cf:' . (int) ($row['mapping_id'] ?? 0);
+ if (!isset($groups[$gkey])) {
+ $groups[$gkey] = [
+ 'label' => $row['field_label'] ?? $gkey,
+ 'kind' => $kind,
+ 'pending' => [],
+ 'resolved' => [],
+ ];
+ $groupOrder[] = $gkey;
+ }
+ if ($type === 'pending') {
+ $groups[$gkey]['pending'][] = ['idx' => $idx, 'data' => $row];
+ } else {
+ $groups[$gkey]['resolved'][] = ['data' => $row];
+ }
+};
+foreach ($items as $idx => $item) {
+ $pushToGroup($item, 'pending', $idx);
+}
+foreach ($resolvedItems as $res) {
+ $pushToGroup($res, 'resolved');
+}
+
$db = DBHandlerSelect::getInstance();
$pdo = $db->getConnection();
$stmt = $pdo->prepare("SELECT name FROM excel_templates WHERE id = ?");
@@ -124,52 +155,74 @@ $templateName = $stmt->fetchColumn() ?: ('Template ' . $templateId);
- | Campo (template_mapping) |
+ |
Valore JSON |
Valore LIMS |
Azioni |
- $item): ?>
-
- | = htmlspecialchars($item['field_label']) ?> |
- = htmlspecialchars($item['json_value']) ?> |
-
-
- |
-
-
- In attesa
+
+ |
-
-
-
- | = htmlspecialchars($res['field_label']) ?> |
- = htmlspecialchars($res['json_value']) ?> |
-
-
- |
-
-
- = $res['badge'] ?>
- |
-
+
+
+ data-fixed-key="= htmlspecialchars($item['fixed_field_key'], ENT_QUOTES) ?>"
+
+ data-mapping-id="= (int) ($item['mapping_id'] ?? 0) ?>"
+ data-field-id="= (int) ($item['field_id'] ?? 0) ?>"
+
+ data-json-value="= htmlspecialchars($item['json_value'], ENT_QUOTES) ?>"
+ data-datadb-ids="= htmlspecialchars(json_encode($item['datadb_ids']), ENT_QUOTES) ?>">
+ | › |
+ = htmlspecialchars($item['json_value']) ?> |
+
+
+ |
+
+
+ In attesa
+ |
+
+
+
+
+
+ data-fixed-key="= htmlspecialchars($res['fixed_field_key'], ENT_QUOTES) ?>"
+
+ data-mapping-id="= (int) ($res['mapping_id'] ?? 0) ?>"
+ data-field-id="= (int) ($res['field_id'] ?? 0) ?>"
+
+ data-json-value="= htmlspecialchars($res['json_value'], ENT_QUOTES) ?>"
+ data-datadb-ids="= htmlspecialchars(json_encode($res['datadb_ids']), ENT_QUOTES) ?>">
+ | › |
+ = htmlspecialchars($res['json_value']) ?> |
+
+
+ |
+
+
+ = $res['badge'] ?>
+ |
+
+
@@ -206,21 +259,29 @@ $templateName = $stmt->fetchColumn() ?: ('Template ' . $templateId);
const $btn = $('#confirmBindingsBtn');
const $error = $('#bindingError');
- // Dropdown valori LIMS per riga (search_customfield_values.php).
+ // Dropdown valori LIMS per riga: sorgente custom vs fixed.
$('.binding-select').each(function() {
- const fieldId = $(this).data('field-id');
+ const $row = $(this).closest('.binding-row');
+ const kind = $row.data('kind');
+ const isFixed = kind === 'fixed';
+
$(this).select2({
placeholder: 'Seleziona valore LIMS...',
width: '100%',
ajax: {
- url: 'search_customfield_values.php',
+ url: isFixed ? 'search_fixed_field_values.php' : 'search_customfield_values.php',
dataType: 'json',
delay: 200,
- data: params => ({
- field_id: fieldId,
+ data: params => isFixed ? {
+ field_key: $row.data('fixed-key'),
+ template_id: TEMPLATE_ID,
q: params.term || '',
limit: 50
- }),
+ } : {
+ field_id: $row.data('field-id'),
+ q: params.term || '',
+ limit: 50
+ },
processResults: data => ({
results: data.results || []
})
@@ -262,13 +323,20 @@ $templateName = $stmt->fetchColumn() ?: ('Template ' . $templateId);
const tasks = $('.binding-row').toArray().map(row => {
const $row = $(row);
const $status = $row.find('.binding-status');
+ const kind = $row.data('kind');
+ const isFixed = kind === 'fixed';
const datadbIds = JSON.stringify($row.data('datadb-ids') || []);
const jsonValue = String($row.data('json-value'));
+ const targetFields = isFixed
+ ? { kind: 'fixed', fixed_field_key: $row.data('fixed-key') }
+ : { kind: 'custom', mapping_id: $row.data('mapping-id'), field_id: $row.data('field-id') };
+
// Riga senza corrispondenza: azzera il valore, niente binding.
if ($row.hasClass('is-skipped')) {
return $.post('skip_binding.php', {
- mapping_id: $row.data('mapping-id'),
+ ...targetFields,
+ template_id: TEMPLATE_ID,
json_value: jsonValue,
datadb_ids: datadbIds
}).then(resp => {
@@ -285,8 +353,7 @@ $templateName = $stmt->fetchColumn() ?: ('Template ' . $templateId);
const selectedData = $select.select2('data')[0] || {};
return $.post('save_binding.php', {
- mapping_id: $row.data('mapping-id'),
- field_id: $row.data('field-id'),
+ ...targetFields,
template_id: TEMPLATE_ID,
json_value: jsonValue,
lims_value_id: $select.val(),
diff --git a/public/userarea/save_binding.php b/public/userarea/save_binding.php
index 1ec6a9c..2de9055 100644
--- a/public/userarea/save_binding.php
+++ b/public/userarea/save_binding.php
@@ -1,6 +1,6 @@
LIMS e lo applica ai record appena importati. Ritorna JSON.
+// Salva un binding JSON -> LIMS (custom o fixed) e lo applica ai record appena importati. Ritorna JSON.
require_once dirname(__DIR__, 2) . '/vendor/autoload.php';
require_once __DIR__ . '/class/db-functions.php';
@@ -23,8 +23,7 @@ if ($_SERVER['REQUEST_METHOD'] !== 'POST') {
exit;
}
-$mappingId = intval($_POST['mapping_id'] ?? 0);
-$fieldId = intval($_POST['field_id'] ?? 0);
+$kind = ($_POST['kind'] ?? 'custom') === 'fixed' ? 'fixed' : 'custom';
$templateId = intval($_POST['template_id'] ?? 0);
$jsonValue = (string) ($_POST['json_value'] ?? '');
$limsValueId = intval($_POST['lims_value_id'] ?? 0);
@@ -38,7 +37,7 @@ if (isset($_POST['datadb_ids'])) {
}
}
-if ($mappingId <= 0 || $templateId <= 0 || $jsonValue === '' || $limsValueId <= 0) {
+if ($templateId <= 0 || $jsonValue === '' || $limsValueId <= 0) {
http_response_code(422);
echo json_encode(['success' => false, 'error' => 'Missing required parameters']);
exit;
@@ -47,31 +46,44 @@ if ($mappingId <= 0 || $templateId <= 0 || $jsonValue === '' || $limsValueId <=
try {
$pdo = DBHandlerSelect::getInstance()->getConnection();
- $createdBy = null;
- if (Auth::user()) {
- $createdBy = (int) Auth::user()->present()->id;
- }
+ $createdBy = Auth::user() ? (int) Auth::user()->present()->id : null;
- if ($fieldId <= 0) {
- $stmt = $pdo->prepare("SELECT field_id FROM template_mapping WHERE id = ?");
- $stmt->execute([$mappingId]);
- $fieldId = (int) ($stmt->fetchColumn() ?: 0);
- }
+ if ($kind === 'fixed') {
+ $fixedKey = trim($_POST['fixed_field_key'] ?? '');
+ $column = binding_fixed_column($fixedKey);
+ if ($fixedKey === '' || !binding_fixed_is_list($fixedKey) || !$column) {
+ http_response_code(422);
+ echo json_encode(['success' => false, 'error' => 'Invalid fixed field']);
+ exit;
+ }
- binding_upsert($pdo, $templateId, $mappingId, $fieldId, $jsonValue, $limsValueId, $limsValue, $createdBy);
+ binding_upsert_fixed($pdo, $templateId, $fixedKey, $jsonValue, $limsValueId, $limsValue, $createdBy);
+ $applied = !empty($datadbIds) ? binding_apply_to_datadb($pdo, $column, $limsValueId, $datadbIds) : 0;
+ } else {
+ $mappingId = intval($_POST['mapping_id'] ?? 0);
+ $fieldId = intval($_POST['field_id'] ?? 0);
+ if ($mappingId <= 0) {
+ http_response_code(422);
+ echo json_encode(['success' => false, 'error' => 'Missing mapping_id']);
+ exit;
+ }
+ if ($fieldId <= 0) {
+ $stmt = $pdo->prepare("SELECT field_id FROM template_mapping WHERE id = ?");
+ $stmt->execute([$mappingId]);
+ $fieldId = (int) ($stmt->fetchColumn() ?: 0);
+ }
- $applied = 0;
- if (!empty($datadbIds)) {
- $applied = binding_apply_to_details($pdo, $mappingId, $limsValue, $datadbIds);
+ binding_upsert($pdo, $templateId, $mappingId, $fieldId, $jsonValue, $limsValueId, $limsValue, $createdBy);
+ $applied = !empty($datadbIds) ? binding_apply_to_details($pdo, $mappingId, $limsValue, $datadbIds) : 0;
}
echo json_encode([
- 'success' => true,
- 'applied_rows' => $applied,
- 'mapping_id' => $mappingId,
- 'json_value' => $jsonValue,
- 'lims_value' => $limsValue,
- 'lims_value_id' => $limsValueId,
+ 'success' => true,
+ 'applied_rows' => $applied,
+ 'kind' => $kind,
+ 'json_value' => $jsonValue,
+ 'lims_value' => $limsValue,
+ 'lims_value_id' => $limsValueId,
]);
} catch (Exception $e) {
http_response_code(500);
diff --git a/public/userarea/search_fixed_field_values.php b/public/userarea/search_fixed_field_values.php
new file mode 100644
index 0000000..5eea49a
--- /dev/null
+++ b/public/userarea/search_fixed_field_values.php
@@ -0,0 +1,65 @@
+ 'Unauthorized']);
+ exit;
+}
+
+$fieldKey = trim($_GET['field_key'] ?? '');
+$templateId = intval($_GET['template_id'] ?? 0);
+$q = mb_strtolower(trim($_GET['q'] ?? ''));
+$id = isset($_GET['id']) ? intval($_GET['id']) : null;
+$rawLimit = intval($_GET['limit'] ?? 30);
+$limit = $rawLimit <= 0 ? 0 : max(1, min(500, $rawLimit));
+
+if ($fieldKey === '' || !binding_fixed_is_list($fieldKey)) {
+ echo json_encode(['results' => []]);
+ exit;
+}
+
+try {
+ $pdo = DBHandlerSelect::getInstance()->getConnection();
+ $values = binding_get_fixed_values($pdo, $fieldKey, $templateId);
+
+ if ($id !== null) {
+ foreach ($values as $v) {
+ if ((int) $v['id'] === $id) {
+ echo json_encode(['results' => [['id' => $v['id'], 'text' => $v['text']]]]);
+ exit;
+ }
+ }
+ echo json_encode(['results' => []]);
+ exit;
+ }
+
+ $results = [];
+ foreach ($values as $v) {
+ $text = (string) $v['text'];
+ if ($q === '' || mb_strpos(mb_strtolower($text), $q) !== false) {
+ $results[] = ['id' => $v['id'], 'text' => $text];
+ if ($limit > 0 && count($results) >= $limit) {
+ break;
+ }
+ }
+ }
+
+ usort($results, fn($a, $b) => strcasecmp($a['text'], $b['text']));
+ echo json_encode(['results' => $results]);
+} catch (Exception $e) {
+ http_response_code(500);
+ echo json_encode(['error' => $e->getMessage()]);
+}
diff --git a/public/userarea/skip_binding.php b/public/userarea/skip_binding.php
index 53efe2e..c33d06b 100644
--- a/public/userarea/skip_binding.php
+++ b/public/userarea/skip_binding.php
@@ -1,6 +1,6 @@
false, 'error' => 'Missing required parameters']);
+ echo json_encode(['success' => false, 'error' => 'Missing json_value']);
exit;
}
try {
$pdo = DBHandlerSelect::getInstance()->getConnection();
- $cleared = binding_apply_to_details($pdo, $mappingId, '', $datadbIds);
- // Rimuovo un eventuale binding gia' salvato (es. auto-collegato) per coerenza.
- $del = $pdo->prepare("DELETE FROM json_lims_binding WHERE mapping_id = ? AND json_value = ?");
- $del->execute([$mappingId, $jsonValue]);
+ if ($kind === 'fixed') {
+ $fixedKey = trim($_POST['fixed_field_key'] ?? '');
+ $column = binding_fixed_column($fixedKey);
+ if ($fixedKey === '' || !binding_fixed_is_list($fixedKey) || !$column || $templateId <= 0) {
+ http_response_code(422);
+ echo json_encode(['success' => false, 'error' => 'Invalid fixed field']);
+ exit;
+ }
+ $cleared = !empty($datadbIds) ? binding_apply_to_datadb($pdo, $column, null, $datadbIds) : 0;
+ binding_delete_target($pdo, binding_target_fixed($templateId, $fixedKey), $jsonValue);
+ } else {
+ $mappingId = intval($_POST['mapping_id'] ?? 0);
+ if ($mappingId <= 0) {
+ http_response_code(422);
+ echo json_encode(['success' => false, 'error' => 'Missing mapping_id']);
+ exit;
+ }
+ $cleared = !empty($datadbIds) ? binding_apply_to_details($pdo, $mappingId, '', $datadbIds) : 0;
+ binding_delete_target($pdo, binding_target_custom($mappingId), $jsonValue);
+ }
echo json_encode(['success' => true, 'cleared_rows' => $cleared]);
} catch (Exception $e) {