xls binding
This commit is contained in:
@@ -25,6 +25,7 @@ $templatedashboard = "Template Dashboard";
|
||||
$newtemplate = "New Template";
|
||||
$dashtemplate = "Dashboard Templates";
|
||||
$insertnewtemplatexls = "Insert New Template XLS";
|
||||
$bindingsmanage = "Value Bindings";
|
||||
// Datatables
|
||||
$langdatatables = [
|
||||
"paginate_first" => "First",
|
||||
|
||||
@@ -0,0 +1,299 @@
|
||||
<?php
|
||||
|
||||
include('include/headscript.php');
|
||||
require_once __DIR__ . '/class/binding-functions.php';
|
||||
|
||||
$db = DBHandlerSelect::getInstance();
|
||||
$pdo = $db->getConnection();
|
||||
|
||||
$templateFilter = isset($_GET['template_id']) ? (int) $_GET['template_id'] : 0;
|
||||
$search = trim($_GET['q'] ?? '');
|
||||
|
||||
$templates = $pdo->query("SELECT id, name FROM excel_templates ORDER BY name")->fetchAll(PDO::FETCH_ASSOC);
|
||||
|
||||
$conds = [];
|
||||
$params = [];
|
||||
|
||||
if ($templateFilter > 0) {
|
||||
$conds[] = 'b.template_id = ?';
|
||||
$params[] = $templateFilter;
|
||||
}
|
||||
if ($search !== '') {
|
||||
$conds[] = '(b.source_value LIKE ? OR b.lims_value LIKE ? OR m.field_label LIKE ? OR f.fixed_field_key LIKE ?)';
|
||||
$like = '%' . $search . '%';
|
||||
array_push($params, $like, $like, $like, $like);
|
||||
}
|
||||
$where = $conds ? ('WHERE ' . implode(' AND ', $conds)) : '';
|
||||
|
||||
$stmt = $pdo->prepare(
|
||||
"SELECT b.id, b.template_id, b.binding_kind, b.mapping_id, b.field_id, b.source_value,
|
||||
b.lims_value_id, b.lims_value, b.updated_at,
|
||||
t.name AS template_name,
|
||||
COALESCE(m.field_label, f.fixed_field_key) AS field_label,
|
||||
COALESCE(m.excel_column, f.excel_column) AS excel_column,
|
||||
f.fixed_field_key
|
||||
FROM lims_value_binding b
|
||||
LEFT JOIN excel_templates t ON t.id = b.template_id
|
||||
LEFT JOIN template_mapping m ON m.id = b.mapping_id AND b.binding_kind = 'custom'
|
||||
LEFT JOIN template_fixed_mapping f ON f.id = b.mapping_id AND b.binding_kind = 'fixed'
|
||||
$where
|
||||
ORDER BY t.name ASC, field_label ASC, b.source_value ASC"
|
||||
);
|
||||
$stmt->execute($params);
|
||||
$rows = $stmt->fetchAll(PDO::FETCH_ASSOC);
|
||||
|
||||
$groups = [];
|
||||
foreach ($rows as $r) {
|
||||
$kind = $r['binding_kind'] ?? 'custom';
|
||||
$gkey = $kind . ':' . (int) $r['mapping_id'];
|
||||
if (!isset($groups[$gkey])) {
|
||||
$groups[$gkey] = [
|
||||
'kind' => $kind,
|
||||
'label' => $kind === 'fixed'
|
||||
? binding_fixed_label((string) $r['fixed_field_key'])
|
||||
: ($r['field_label'] ?: ('Mapping ' . $gkey)),
|
||||
'excel_column' => $r['excel_column'] ?: '',
|
||||
'template_name' => $r['template_name'] ?: ('Template ' . $r['template_id']),
|
||||
'template_id' => (int) $r['template_id'],
|
||||
'rows' => [],
|
||||
];
|
||||
}
|
||||
$groups[$gkey]['rows'][] = $r;
|
||||
}
|
||||
?>
|
||||
<!doctype html>
|
||||
<html lang="en">
|
||||
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<link rel="icon" href="assets/images/favicon-32x32.png" type="image/png" />
|
||||
<?php include('cssinclude.php'); ?>
|
||||
<link href="https://cdn.jsdelivr.net/npm/select2@4.1.0-rc.0/dist/css/select2.min.css" rel="stylesheet">
|
||||
<style>
|
||||
.source-value {
|
||||
font-family: Consolas, Monaco, monospace;
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
td .select2-container {
|
||||
min-width: 240px;
|
||||
}
|
||||
|
||||
.select2-container--default .select2-selection--single {
|
||||
height: 38px;
|
||||
border-color: #ced4da;
|
||||
}
|
||||
|
||||
.select2-container--default .select2-selection--single .select2-selection__rendered {
|
||||
line-height: 36px;
|
||||
}
|
||||
|
||||
.select2-container--default .select2-selection--single .select2-selection__arrow {
|
||||
height: 36px;
|
||||
}
|
||||
</style>
|
||||
<title>Gestione binding - <?= htmlspecialchars($titlewebsite ?? '', ENT_QUOTES, 'UTF-8'); ?></title>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<div class="wrapper">
|
||||
<?php include('include/navbar.php'); ?>
|
||||
<?php include('include/topbar.php'); ?>
|
||||
<div class="page-wrapper">
|
||||
<div class="page-content">
|
||||
|
||||
<div class="card radius-10">
|
||||
<div class="card-header">
|
||||
<h6 class="mb-0">Binding valori importati → LIMS</h6>
|
||||
<small class="text-muted">
|
||||
Le modifiche valgono dalla prossima importazione: i record gia' importati non vengono aggiornati.
|
||||
</small>
|
||||
</div>
|
||||
|
||||
<div class="card-body">
|
||||
<form class="row g-2 align-items-end mb-3" method="get">
|
||||
<div class="col-md-4">
|
||||
<label class="form-label mb-1">Template</label>
|
||||
<select class="form-select" name="template_id" onchange="this.form.submit()">
|
||||
<option value="">Tutti</option>
|
||||
<?php foreach ($templates as $t): ?>
|
||||
<option value="<?= (int) $t['id'] ?>" <?= $templateFilter === (int) $t['id'] ? 'selected' : '' ?>>
|
||||
<?= htmlspecialchars($t['name']) ?>
|
||||
</option>
|
||||
<?php endforeach; ?>
|
||||
</select>
|
||||
</div>
|
||||
<div class="col-md-4">
|
||||
<label class="form-label mb-1">Cerca</label>
|
||||
<input type="text" class="form-control" name="q" value="<?= htmlspecialchars($search) ?>"
|
||||
placeholder="valore importato, valore LIMS, campo...">
|
||||
</div>
|
||||
<div class="col-md-4">
|
||||
<button type="submit" class="btn btn-primary">Filtra</button>
|
||||
<a href="bindings_manage.php" class="btn btn-outline-secondary">Azzera</a>
|
||||
</div>
|
||||
</form>
|
||||
|
||||
<div id="bindingError" class="alert alert-danger" style="display:none;"></div>
|
||||
|
||||
<?php if (empty($rows)): ?>
|
||||
<div class="alert alert-info mb-0">Nessun binding salvato per i filtri selezionati.</div>
|
||||
<?php else: ?>
|
||||
<div class="table-responsive">
|
||||
<table class="table table-bordered align-middle">
|
||||
<thead>
|
||||
<tr>
|
||||
<th style="width:32px;"></th>
|
||||
<th>Valore importato</th>
|
||||
<th>Valore LIMS</th>
|
||||
<th style="width:150px;">Aggiornato</th>
|
||||
<th style="width:110px;">Azioni</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<?php foreach ($groups as $gkey => $g): ?>
|
||||
<tr class="table-light">
|
||||
<td colspan="5">
|
||||
<strong><?= htmlspecialchars($g['label']) ?></strong>
|
||||
<?php if (($g['kind'] ?? 'custom') === 'fixed'): ?><span class="badge bg-light text-dark border">campo fisso</span><?php endif; ?>
|
||||
<span class="text-muted">
|
||||
· <?= htmlspecialchars($g['template_name']) ?>
|
||||
<?php if ($g['excel_column'] !== ''): ?>
|
||||
· colonna «<?= htmlspecialchars($g['excel_column']) ?>»
|
||||
<?php endif; ?>
|
||||
· <?= count($g['rows']) ?> valore/i
|
||||
</span>
|
||||
</td>
|
||||
</tr>
|
||||
<?php foreach ($g['rows'] as $r): ?>
|
||||
<tr class="binding-row"
|
||||
data-binding-id="<?= (int) $r['id'] ?>"
|
||||
data-kind="<?= htmlspecialchars($r['binding_kind'] ?? 'custom', ENT_QUOTES) ?>"
|
||||
data-fixed-key="<?= htmlspecialchars($r['fixed_field_key'] ?? '', ENT_QUOTES) ?>"
|
||||
data-template-id="<?= (int) $r['template_id'] ?>"
|
||||
data-mapping-id="<?= (int) $r['mapping_id'] ?>"
|
||||
data-field-id="<?= (int) $r['field_id'] ?>"
|
||||
data-source-value="<?= htmlspecialchars($r['source_value'], ENT_QUOTES) ?>">
|
||||
<td class="text-muted ps-4">›</td>
|
||||
<td class="source-value"><?= htmlspecialchars($r['source_value']) ?></td>
|
||||
<td>
|
||||
<select class="form-select binding-select">
|
||||
<option value="<?= (int) $r['lims_value_id'] ?>" selected>
|
||||
<?= htmlspecialchars($r['lims_value'] ?: ('#' . $r['lims_value_id'])) ?>
|
||||
</option>
|
||||
</select>
|
||||
<div class="binding-status small mt-1"></div>
|
||||
</td>
|
||||
<td class="small text-muted"><?= htmlspecialchars((string) $r['updated_at']) ?></td>
|
||||
<td>
|
||||
<button type="button" class="btn btn-sm btn-outline-danger delete-binding-btn">
|
||||
<i class="fas fa-trash"></i>
|
||||
</button>
|
||||
</td>
|
||||
</tr>
|
||||
<?php endforeach; ?>
|
||||
<?php endforeach; ?>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
<?php endif; ?>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
<div class="overlay toggle-icon"></div>
|
||||
<a href="javaScript:;" class="back-to-top"><i class='bx bxs-up-arrow-alt'></i></a>
|
||||
<?php include('include/footer.php'); ?>
|
||||
</div>
|
||||
|
||||
<?php include('jsinclude.php'); ?>
|
||||
<script src="https://cdn.jsdelivr.net/npm/select2@4.1.0-rc.0/dist/js/select2.min.js"></script>
|
||||
|
||||
<script>
|
||||
$(function() {
|
||||
const $error = $('#bindingError');
|
||||
|
||||
$('.binding-select').each(function() {
|
||||
const $row = $(this).closest('.binding-row');
|
||||
const isFixed = $row.data('kind') === 'fixed';
|
||||
|
||||
$(this).select2({
|
||||
placeholder: 'Seleziona valore LIMS...',
|
||||
width: '100%',
|
||||
ajax: {
|
||||
url: isFixed ? 'search_fixed_field_values.php' : 'search_customfield_values.php',
|
||||
dataType: 'json',
|
||||
delay: 200,
|
||||
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 || []
|
||||
})
|
||||
},
|
||||
minimumInputLength: 0
|
||||
});
|
||||
});
|
||||
|
||||
$('.binding-select').on('change', function() {
|
||||
const $select = $(this);
|
||||
const $row = $select.closest('.binding-row');
|
||||
const $status = $row.find('.binding-status');
|
||||
const selected = $select.select2('data')[0] || {};
|
||||
|
||||
if (!$select.val()) {
|
||||
return;
|
||||
}
|
||||
|
||||
$status.removeClass('text-success text-danger').addClass('text-muted').text('Salvataggio...');
|
||||
|
||||
$.post('save_binding.php', {
|
||||
kind: $row.data('kind') || 'custom',
|
||||
template_id: $row.data('template-id'),
|
||||
mapping_id: $row.data('mapping-id'),
|
||||
field_id: $row.data('field-id'),
|
||||
source_value: String($row.data('source-value')),
|
||||
lims_value_id: $select.val(),
|
||||
lims_value: selected.text || ''
|
||||
}).then(resp => {
|
||||
if (resp && resp.success) {
|
||||
$status.removeClass('text-muted').addClass('text-success').text('Salvato');
|
||||
} else {
|
||||
$status.removeClass('text-muted').addClass('text-danger').text((resp && resp.error) || 'Errore');
|
||||
}
|
||||
}).fail(() => {
|
||||
$status.removeClass('text-muted').addClass('text-danger').text('Errore di rete');
|
||||
});
|
||||
});
|
||||
|
||||
$('.delete-binding-btn').on('click', function() {
|
||||
const $row = $(this).closest('.binding-row');
|
||||
|
||||
$error.hide();
|
||||
|
||||
$.post('delete_binding.php', {
|
||||
binding_id: $row.data('binding-id')
|
||||
}).then(resp => {
|
||||
if (resp && resp.success) {
|
||||
$row.remove();
|
||||
} else {
|
||||
$error.text((resp && resp.error) || 'Errore durante l\'eliminazione.').show();
|
||||
}
|
||||
}).fail(() => {
|
||||
$error.text('Errore di rete durante l\'eliminazione.').show();
|
||||
});
|
||||
});
|
||||
});
|
||||
</script>
|
||||
</body>
|
||||
|
||||
</html>
|
||||
@@ -0,0 +1,716 @@
|
||||
<?php
|
||||
|
||||
function binding_normalize_value($value): string
|
||||
{
|
||||
$s = (string) $value;
|
||||
$s = str_replace(
|
||||
["\xC2\xA0", "\xE2\x80\xAF", "\xE2\x80\x87", "\xE2\x80\x89", "\xE2\x80\x8A", "\xEF\xBB\xBF"],
|
||||
' ',
|
||||
$s
|
||||
);
|
||||
$s = preg_replace('/\s+/u', ' ', $s);
|
||||
|
||||
return mb_strtolower(trim((string) $s));
|
||||
}
|
||||
|
||||
function binding_is_list_field(array $mapping): bool
|
||||
{
|
||||
return strcasecmp(trim((string) ($mapping['data_type'] ?? '')), 'SceltaMultipla') === 0;
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
function binding_lims_value_index(int $fieldId): array
|
||||
{
|
||||
static $memo = [];
|
||||
|
||||
if (array_key_exists($fieldId, $memo)) {
|
||||
return $memo[$fieldId];
|
||||
}
|
||||
|
||||
$index = [];
|
||||
foreach (binding_get_lims_values($fieldId) as $v) {
|
||||
$id = (int) ($v['IdCustomFieldsValue'] ?? 0);
|
||||
if ($id > 0) {
|
||||
$index[$id] = (string) ($v['Valore'] ?? '');
|
||||
}
|
||||
}
|
||||
|
||||
return $memo[$fieldId] = $index;
|
||||
}
|
||||
|
||||
function binding_has_lims_values(int $fieldId): bool
|
||||
{
|
||||
return binding_get_lims_values($fieldId) !== [];
|
||||
}
|
||||
|
||||
function binding_is_lims_id(int $fieldId, $value): bool
|
||||
{
|
||||
$raw = trim((string) $value);
|
||||
if ($raw === '' || !ctype_digit($raw)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$index = binding_lims_value_index($fieldId);
|
||||
|
||||
return $index === [] ? false : isset($index[(int) $raw]);
|
||||
}
|
||||
|
||||
function binding_auto_match(int $fieldId, string $sourceValue): ?array
|
||||
{
|
||||
$needle = binding_normalize_value($sourceValue);
|
||||
if ($needle === '') {
|
||||
return null;
|
||||
}
|
||||
|
||||
$matches = [];
|
||||
foreach (binding_get_lims_values($fieldId) as $v) {
|
||||
if (binding_normalize_value($v['Valore'] ?? '') === $needle) {
|
||||
$matches[] = [
|
||||
'id' => (int) ($v['IdCustomFieldsValue'] ?? 0),
|
||||
'text' => (string) ($v['Valore'] ?? ''),
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
return count($matches) === 1 && $matches[0]['id'] > 0 ? $matches[0] : null;
|
||||
}
|
||||
|
||||
function binding_lookup(PDO $pdo, int $mappingId, string $sourceValue, string $kind = 'custom'): ?array
|
||||
{
|
||||
$stmt = $pdo->prepare(
|
||||
"SELECT id, lims_value_id, lims_value, source_value
|
||||
FROM lims_value_binding
|
||||
WHERE binding_kind = ? AND mapping_id = ? AND source_key = ?
|
||||
LIMIT 1"
|
||||
);
|
||||
$stmt->execute([$kind, $mappingId, binding_normalize_value($sourceValue)]);
|
||||
$row = $stmt->fetch(PDO::FETCH_ASSOC);
|
||||
|
||||
return $row ?: null;
|
||||
}
|
||||
|
||||
function binding_upsert(
|
||||
PDO $pdo,
|
||||
int $templateId,
|
||||
int $mappingId,
|
||||
int $fieldId,
|
||||
string $sourceValue,
|
||||
int $limsValueId,
|
||||
string $limsValue,
|
||||
?int $createdBy,
|
||||
string $kind = 'custom'
|
||||
): void {
|
||||
$stmt = $pdo->prepare(
|
||||
"INSERT INTO lims_value_binding
|
||||
(template_id, binding_kind, mapping_id, field_id, source_value, source_key, 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)"
|
||||
);
|
||||
$stmt->execute([
|
||||
$templateId,
|
||||
$kind,
|
||||
$mappingId,
|
||||
$fieldId,
|
||||
mb_substr(trim($sourceValue), 0, 500),
|
||||
mb_substr(binding_normalize_value($sourceValue), 0, 500),
|
||||
$limsValueId,
|
||||
mb_substr($limsValue, 0, 500),
|
||||
$createdBy,
|
||||
]);
|
||||
}
|
||||
|
||||
function binding_delete(PDO $pdo, int $mappingId, string $sourceValue, string $kind = 'custom'): int
|
||||
{
|
||||
$stmt = $pdo->prepare("DELETE FROM lims_value_binding WHERE binding_kind = ? AND mapping_id = ? AND source_key = ?");
|
||||
$stmt->execute([$kind, $mappingId, binding_normalize_value($sourceValue)]);
|
||||
|
||||
return $stmt->rowCount();
|
||||
}
|
||||
|
||||
function binding_delete_by_id(PDO $pdo, int $bindingId): int
|
||||
{
|
||||
$stmt = $pdo->prepare("DELETE FROM lims_value_binding WHERE id = ?");
|
||||
$stmt->execute([$bindingId]);
|
||||
|
||||
return $stmt->rowCount();
|
||||
}
|
||||
|
||||
function binding_suggest(PDO $pdo, int $fieldId, string $sourceValue, int $excludeMappingId): ?array
|
||||
{
|
||||
if ($fieldId <= 0) {
|
||||
return null;
|
||||
}
|
||||
|
||||
$stmt = $pdo->prepare(
|
||||
"SELECT b.lims_value_id, b.lims_value, b.template_id, t.name AS template_name
|
||||
FROM lims_value_binding b
|
||||
LEFT JOIN excel_templates t ON t.id = b.template_id
|
||||
WHERE b.binding_kind = 'custom' AND b.field_id = ? AND b.source_key = ? AND b.mapping_id <> ?
|
||||
ORDER BY b.updated_at DESC
|
||||
LIMIT 1"
|
||||
);
|
||||
$stmt->execute([$fieldId, binding_normalize_value($sourceValue), $excludeMappingId]);
|
||||
$row = $stmt->fetch(PDO::FETCH_ASSOC);
|
||||
|
||||
return $row ?: null;
|
||||
}
|
||||
|
||||
function binding_apply_to_details(PDO $pdo, int $mappingId, string $value, array $datadbIds): int
|
||||
{
|
||||
$datadbIds = array_values(array_filter(array_map('intval', $datadbIds)));
|
||||
if (empty($datadbIds)) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
$placeholders = implode(',', array_fill(0, count($datadbIds), '?'));
|
||||
$stmt = $pdo->prepare(
|
||||
"UPDATE import_data_details
|
||||
SET field_value = ?
|
||||
WHERE mapping_id = ? AND id IN ($placeholders)"
|
||||
);
|
||||
$stmt->execute(array_merge([$value, $mappingId], $datadbIds));
|
||||
|
||||
return $stmt->rowCount();
|
||||
}
|
||||
|
||||
function binding_find_column_index(string $column, array $columns)
|
||||
{
|
||||
$needle = binding_normalize_value($column);
|
||||
if ($needle === '') {
|
||||
return false;
|
||||
}
|
||||
|
||||
foreach ($columns as $i => $c) {
|
||||
if (binding_normalize_value($c) === $needle) {
|
||||
return $i;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
function binding_fixed_alias_map(): array
|
||||
{
|
||||
return [
|
||||
'ClienteResponsabile' => 'cliente_responsabile_id',
|
||||
'ClienteFornitore' => 'cliente_fornitore_id',
|
||||
'ClienteAnalisi' => 'clienteAnalisi',
|
||||
'ClienteFatturazione' => 'ClienteFatturazione',
|
||||
'MoltiplicatorePrezzo' => 'moltiplicatore_prezzo_id',
|
||||
'AnagraficaCertestObject' => 'anagrafica_certest_object_id',
|
||||
'AnagraficaCertestService' => 'anagrafica_certest_service_id',
|
||||
'ConsegnaRichiesta' => 'consegna_richiesta',
|
||||
];
|
||||
}
|
||||
|
||||
function binding_fixed_column(string $key): ?string
|
||||
{
|
||||
return binding_fixed_alias_map()[$key] ?? null;
|
||||
}
|
||||
|
||||
function binding_fixed_is_list(string $key): bool
|
||||
{
|
||||
return in_array($key, [
|
||||
'ClienteResponsabile',
|
||||
'ClienteFornitore',
|
||||
'ClienteAnalisi',
|
||||
'ClienteFatturazione',
|
||||
'MoltiplicatorePrezzo',
|
||||
'AnagraficaCertestObject',
|
||||
'AnagraficaCertestService',
|
||||
], true);
|
||||
}
|
||||
|
||||
function binding_fixed_is_date(string $key): bool
|
||||
{
|
||||
return $key === 'ConsegnaRichiesta';
|
||||
}
|
||||
|
||||
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' => 'Buyer',
|
||||
'ClienteFatturazione' => 'Cliente Fatturazione',
|
||||
'ConsegnaRichiesta' => 'Consegna Richiesta',
|
||||
];
|
||||
|
||||
return $labels[$key] ?? $key;
|
||||
}
|
||||
|
||||
function binding_parse_date($value): ?string
|
||||
{
|
||||
$raw = trim((string) $value);
|
||||
if ($raw === '') {
|
||||
return null;
|
||||
}
|
||||
|
||||
foreach (['Y-m-d', 'd/m/Y', 'd-m-Y', 'd.m.Y', 'Y/m/d', 'd/m/y', 'Y-m-d H:i:s'] as $format) {
|
||||
$date = DateTime::createFromFormat($format, $raw);
|
||||
if ($date && $date->format($format) === $raw) {
|
||||
return $date->format('Y-m-d');
|
||||
}
|
||||
}
|
||||
|
||||
$ts = strtotime($raw);
|
||||
|
||||
return $ts ? date('Y-m-d', $ts) : null;
|
||||
}
|
||||
|
||||
function binding_fixed_default(array $fixedMapping): string
|
||||
{
|
||||
$value = trim((string) ($fixedMapping['default_value'] ?? ''));
|
||||
if ($value === '') {
|
||||
return '';
|
||||
}
|
||||
|
||||
$isDate = strcasecmp((string) ($fixedMapping['data_type'] ?? ''), 'DATE') === 0
|
||||
|| binding_fixed_is_date((string) ($fixedMapping['fixed_field_key'] ?? ''));
|
||||
|
||||
if ($isDate && preg_match('/^today\s*(?:\+\s*(\d+))?$/i', $value, $m)) {
|
||||
return date('Y-m-d', strtotime('+' . (int) ($m[1] ?? 0) . ' days'));
|
||||
}
|
||||
|
||||
return $value;
|
||||
}
|
||||
|
||||
function binding_cached_get($api, string $cacheFile, string $endpoint, int $ttl = 3600): array
|
||||
{
|
||||
if (file_exists($cacheFile) && (time() - filemtime($cacheFile) < $ttl)) {
|
||||
$data = json_decode(file_get_contents($cacheFile), true);
|
||||
if (is_array($data)) {
|
||||
return $data;
|
||||
}
|
||||
}
|
||||
|
||||
$data = $api->get($endpoint);
|
||||
if (is_array($data)) {
|
||||
$dir = dirname($cacheFile);
|
||||
if (!is_dir($dir)) {
|
||||
mkdir($dir, 0777, true);
|
||||
}
|
||||
file_put_contents($cacheFile, json_encode($data));
|
||||
}
|
||||
|
||||
return is_array($data) ? $data : [];
|
||||
}
|
||||
|
||||
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((string) ($client['Nominativo'] ?? ''));
|
||||
$code = trim((string) ($client['CodiceCliente'] ?? ''));
|
||||
|
||||
return $code !== '' ? ($name . ' - ' . $code) : $name;
|
||||
}
|
||||
|
||||
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'] ?? ''));
|
||||
$name = (string) ($r['NomeAnagrafica'] ?? '');
|
||||
$out[] = [
|
||||
'id' => (int) ($r['IdAnagrafica'] ?? 0),
|
||||
'text' => $name,
|
||||
'aliases' => array_values(array_filter([$code, $code !== '' ? "$code - $name" : ''])),
|
||||
];
|
||||
}
|
||||
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':
|
||||
case 'ClienteFatturazione':
|
||||
$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] = array_values(array_filter($out, fn($v) => $v['id'] > 0));
|
||||
}
|
||||
|
||||
function binding_fixed_has_values(PDO $pdo, string $fieldKey, int $templateId): bool
|
||||
{
|
||||
return binding_get_fixed_values($pdo, $fieldKey, $templateId) !== [];
|
||||
}
|
||||
|
||||
function binding_fixed_is_lims_id(PDO $pdo, string $fieldKey, int $templateId, $value): bool
|
||||
{
|
||||
$raw = trim((string) $value);
|
||||
if ($raw === '' || !ctype_digit($raw)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
foreach (binding_get_fixed_values($pdo, $fieldKey, $templateId) as $v) {
|
||||
if ((int) $v['id'] === (int) $raw) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
function binding_auto_match_fixed(array $values, string $sourceValue): ?array
|
||||
{
|
||||
$needle = binding_normalize_value($sourceValue);
|
||||
if ($needle === '') {
|
||||
return null;
|
||||
}
|
||||
|
||||
$matches = [];
|
||||
foreach ($values as $v) {
|
||||
$candidates = array_merge([$v['text'] ?? ''], $v['aliases'] ?? []);
|
||||
foreach ($candidates as $candidate) {
|
||||
if (binding_normalize_value($candidate) === $needle) {
|
||||
$matches[] = $v;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return count($matches) === 1 ? $matches[0] : null;
|
||||
}
|
||||
|
||||
function binding_suggest_fixed(PDO $pdo, string $fieldKey, string $sourceValue, int $excludeMappingId): ?array
|
||||
{
|
||||
$stmt = $pdo->prepare(
|
||||
"SELECT b.lims_value_id, b.lims_value, b.template_id, t.name AS template_name
|
||||
FROM lims_value_binding b
|
||||
JOIN template_fixed_mapping f ON f.id = b.mapping_id
|
||||
LEFT JOIN excel_templates t ON t.id = b.template_id
|
||||
WHERE b.binding_kind = 'fixed' AND f.fixed_field_key = ?
|
||||
AND b.source_key = ? AND b.mapping_id <> ?
|
||||
ORDER BY b.updated_at DESC
|
||||
LIMIT 1"
|
||||
);
|
||||
$stmt->execute([$fieldKey, binding_normalize_value($sourceValue), $excludeMappingId]);
|
||||
$row = $stmt->fetch(PDO::FETCH_ASSOC);
|
||||
|
||||
return $row ?: null;
|
||||
}
|
||||
|
||||
function binding_apply_to_datadb(PDO $pdo, string $fieldKey, $value, array $datadbIds): int
|
||||
{
|
||||
$column = binding_fixed_column($fieldKey);
|
||||
if ($column === null) {
|
||||
throw new InvalidArgumentException("Campo fisso sconosciuto: $fieldKey");
|
||||
}
|
||||
|
||||
$datadbIds = array_values(array_filter(array_map('intval', $datadbIds)));
|
||||
if (empty($datadbIds)) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
$placeholders = implode(',', array_fill(0, count($datadbIds), '?'));
|
||||
$stmt = $pdo->prepare("UPDATE datadb SET `$column` = ? WHERE iddatadb IN ($placeholders)");
|
||||
$stmt->execute(array_merge([$value === '' ? null : $value], $datadbIds));
|
||||
|
||||
return $stmt->rowCount();
|
||||
}
|
||||
|
||||
function binding_write_fixed_columns(PDO $pdo, int $iddatadb, array $valuesByKey): int
|
||||
{
|
||||
$map = binding_fixed_alias_map();
|
||||
$sets = [];
|
||||
$params = [];
|
||||
|
||||
foreach ($valuesByKey as $key => $value) {
|
||||
if (!isset($map[$key]) || $value === null || $value === '') {
|
||||
continue;
|
||||
}
|
||||
$sets[] = "`{$map[$key]}` = ?";
|
||||
$params[] = $value;
|
||||
}
|
||||
|
||||
if (empty($sets)) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
$params[] = $iddatadb;
|
||||
$stmt = $pdo->prepare("UPDATE datadb SET " . implode(', ', $sets) . " WHERE iddatadb = ?");
|
||||
$stmt->execute($params);
|
||||
|
||||
return $stmt->rowCount();
|
||||
}
|
||||
|
||||
function binding_resolve_for_import(
|
||||
PDO $pdo,
|
||||
array $mapping,
|
||||
int $templateId,
|
||||
string $sourceValue,
|
||||
int $iddatadb,
|
||||
?int $userId,
|
||||
array &$saved,
|
||||
array &$auto,
|
||||
array &$pending
|
||||
): string {
|
||||
$mappingId = (int) $mapping['id'];
|
||||
$fieldId = (int) $mapping['field_id'];
|
||||
$key = 'cf:' . $mappingId . '|' . binding_normalize_value($sourceValue);
|
||||
|
||||
$collect = function (array &$bucket, array $extra = []) use ($key, $mapping, $mappingId, $fieldId, $sourceValue, $iddatadb) {
|
||||
if (!isset($bucket[$key])) {
|
||||
$bucket[$key] = array_merge([
|
||||
'kind' => 'custom',
|
||||
'mapping_id' => $mappingId,
|
||||
'field_id' => $fieldId,
|
||||
'field_label' => $mapping['field_label'] ?? ('Mapping ' . $mappingId),
|
||||
'source_value' => $sourceValue,
|
||||
'datadb_ids' => [],
|
||||
], $extra);
|
||||
}
|
||||
$bucket[$key]['datadb_ids'][] = $iddatadb;
|
||||
};
|
||||
|
||||
if (!binding_has_lims_values($fieldId)) {
|
||||
return $sourceValue;
|
||||
}
|
||||
|
||||
if (binding_is_lims_id($fieldId, $sourceValue)) {
|
||||
return $sourceValue;
|
||||
}
|
||||
|
||||
if ($existing = binding_lookup($pdo, $mappingId, $sourceValue)) {
|
||||
$collect($saved, [
|
||||
'lims_value_id' => (int) $existing['lims_value_id'],
|
||||
'lims_value' => (string) $existing['lims_value'],
|
||||
]);
|
||||
|
||||
return (string) $existing['lims_value_id'];
|
||||
}
|
||||
|
||||
if ($match = binding_auto_match($fieldId, $sourceValue)) {
|
||||
binding_upsert($pdo, $templateId, $mappingId, $fieldId, $sourceValue, $match['id'], $match['text'], $userId);
|
||||
$collect($auto, [
|
||||
'lims_value_id' => $match['id'],
|
||||
'lims_value' => $match['text'],
|
||||
]);
|
||||
|
||||
return (string) $match['id'];
|
||||
}
|
||||
|
||||
$collect($pending);
|
||||
|
||||
return $sourceValue;
|
||||
}
|
||||
|
||||
function binding_resolve_fixed_for_import(
|
||||
PDO $pdo,
|
||||
array $fixedMapping,
|
||||
int $templateId,
|
||||
string $sourceValue,
|
||||
int $iddatadb,
|
||||
?int $userId,
|
||||
array &$saved,
|
||||
array &$auto,
|
||||
array &$pending
|
||||
): ?string {
|
||||
$fieldKey = (string) $fixedMapping['fixed_field_key'];
|
||||
$mappingId = (int) $fixedMapping['id'];
|
||||
$key = 'fx:' . $mappingId . '|' . binding_normalize_value($sourceValue);
|
||||
|
||||
$collect = function (array &$bucket, array $extra = []) use ($key, $fieldKey, $mappingId, $sourceValue, $iddatadb) {
|
||||
if (!isset($bucket[$key])) {
|
||||
$bucket[$key] = array_merge([
|
||||
'kind' => 'fixed',
|
||||
'mapping_id' => $mappingId,
|
||||
'fixed_field_key' => $fieldKey,
|
||||
'field_id' => 0,
|
||||
'field_label' => binding_fixed_label($fieldKey),
|
||||
'source_value' => $sourceValue,
|
||||
'datadb_ids' => [],
|
||||
], $extra);
|
||||
}
|
||||
$bucket[$key]['datadb_ids'][] = $iddatadb;
|
||||
};
|
||||
|
||||
if (binding_fixed_is_date($fieldKey)) {
|
||||
return binding_parse_date($sourceValue);
|
||||
}
|
||||
|
||||
if (!binding_fixed_is_list($fieldKey)) {
|
||||
return $sourceValue;
|
||||
}
|
||||
|
||||
if (!binding_fixed_has_values($pdo, $fieldKey, $templateId)) {
|
||||
error_log("[BINDING] campo fisso $fieldKey: lista LIMS non disponibile, valore non scritto");
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
if (binding_fixed_is_lims_id($pdo, $fieldKey, $templateId, $sourceValue)) {
|
||||
return (string) (int) $sourceValue;
|
||||
}
|
||||
|
||||
if ($existing = binding_lookup($pdo, $mappingId, $sourceValue, 'fixed')) {
|
||||
$collect($saved, [
|
||||
'lims_value_id' => (int) $existing['lims_value_id'],
|
||||
'lims_value' => (string) $existing['lims_value'],
|
||||
]);
|
||||
|
||||
return (string) $existing['lims_value_id'];
|
||||
}
|
||||
|
||||
if (binding_fixed_auto_matchable($fieldKey)) {
|
||||
$match = binding_auto_match_fixed(binding_get_fixed_values($pdo, $fieldKey, $templateId), $sourceValue);
|
||||
if ($match) {
|
||||
binding_upsert($pdo, $templateId, $mappingId, 0, $sourceValue, (int) $match['id'], (string) $match['text'], $userId, 'fixed');
|
||||
$collect($auto, [
|
||||
'lims_value_id' => (int) $match['id'],
|
||||
'lims_value' => (string) $match['text'],
|
||||
]);
|
||||
|
||||
return (string) $match['id'];
|
||||
}
|
||||
}
|
||||
|
||||
$collect($pending);
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
function binding_merge_buckets(array $bucket, array $items): array
|
||||
{
|
||||
foreach ($items as $key => $item) {
|
||||
if (!isset($bucket[$key])) {
|
||||
$bucket[$key] = $item;
|
||||
continue;
|
||||
}
|
||||
$bucket[$key]['datadb_ids'] = array_values(array_unique(array_merge(
|
||||
$bucket[$key]['datadb_ids'] ?? [],
|
||||
$item['datadb_ids'] ?? []
|
||||
)));
|
||||
}
|
||||
|
||||
return $bucket;
|
||||
}
|
||||
|
||||
function binding_list_for_template(PDO $pdo, int $templateId): array
|
||||
{
|
||||
$stmt = $pdo->prepare(
|
||||
"SELECT b.*,
|
||||
COALESCE(m.field_label, f.fixed_field_key) AS field_label,
|
||||
COALESCE(m.excel_column, f.excel_column) AS excel_column,
|
||||
f.fixed_field_key
|
||||
FROM lims_value_binding b
|
||||
LEFT JOIN template_mapping m ON m.id = b.mapping_id AND b.binding_kind = 'custom'
|
||||
LEFT JOIN template_fixed_mapping f ON f.id = b.mapping_id AND b.binding_kind = 'fixed'
|
||||
WHERE b.template_id = ?
|
||||
ORDER BY field_label ASC, b.source_value ASC"
|
||||
);
|
||||
$stmt->execute([$templateId]);
|
||||
|
||||
return $stmt->fetchAll(PDO::FETCH_ASSOC);
|
||||
}
|
||||
@@ -0,0 +1,40 @@
|
||||
<?php
|
||||
|
||||
require_once dirname(__DIR__, 2) . '/vendor/autoload.php';
|
||||
require_once __DIR__ . '/class/db-functions.php';
|
||||
require_once __DIR__ . '/class/binding-functions.php';
|
||||
include dirname(__DIR__) . '/../extra/auth.php';
|
||||
|
||||
header('Content-Type: application/json');
|
||||
ini_set('display_errors', '0');
|
||||
error_reporting(E_ALL);
|
||||
|
||||
if (!Auth::check()) {
|
||||
http_response_code(401);
|
||||
echo json_encode(['success' => false, 'error' => 'Unauthorized']);
|
||||
exit;
|
||||
}
|
||||
|
||||
if ($_SERVER['REQUEST_METHOD'] !== 'POST') {
|
||||
http_response_code(405);
|
||||
echo json_encode(['success' => false, 'error' => 'Method not allowed']);
|
||||
exit;
|
||||
}
|
||||
|
||||
$bindingId = intval($_POST['binding_id'] ?? 0);
|
||||
|
||||
if ($bindingId <= 0) {
|
||||
http_response_code(422);
|
||||
echo json_encode(['success' => false, 'error' => 'Missing binding_id']);
|
||||
exit;
|
||||
}
|
||||
|
||||
try {
|
||||
$pdo = DBHandlerSelect::getInstance()->getConnection();
|
||||
$deleted = binding_delete_by_id($pdo, $bindingId);
|
||||
|
||||
echo json_encode(['success' => $deleted > 0, 'deleted' => $deleted]);
|
||||
} catch (Exception $e) {
|
||||
http_response_code(500);
|
||||
echo json_encode(['success' => false, 'error' => $e->getMessage()]);
|
||||
}
|
||||
@@ -12,6 +12,7 @@ if (!file_exists(__DIR__ . '/import_debug.log')) {
|
||||
error_log("Inizio importazione alle " . date('Y-m-d H:i:s'));
|
||||
|
||||
include('include/headscript.php');
|
||||
require_once __DIR__ . '/class/binding-functions.php';
|
||||
|
||||
if ($_SERVER['REQUEST_METHOD'] !== 'POST' || !isset($_POST['template_id']) || !isset($_POST['selected_rows']) || !isset($_POST['filename'])) {
|
||||
header("Location: xlstemplates_grid.php?status=error&message=" . urlencode("Richiesta non valida"));
|
||||
@@ -70,7 +71,7 @@ $pdo = $db->getConnection();
|
||||
$importReferenceCode = date('YmdHis') . '-' . uniqid();
|
||||
|
||||
// Recupera tutti i mapping dal template
|
||||
$stmt = $pdo->prepare("SELECT id, excel_column, data_type, is_required, manual_default, is_manual, field_label, field_id, main_field, auto_value FROM template_mapping WHERE template_id = ?");
|
||||
$stmt = $pdo->prepare("SELECT id, excel_column, data_type, has_list, is_required, manual_default, is_manual, field_label, field_id, main_field, auto_value FROM template_mapping WHERE template_id = ?");
|
||||
$stmt->execute([$template_id]);
|
||||
$allMappings = $stmt->fetchAll(PDO::FETCH_ASSOC);
|
||||
|
||||
@@ -88,6 +89,23 @@ foreach ($allMappings as $mapping) {
|
||||
}
|
||||
}
|
||||
|
||||
$fixedMappings = [];
|
||||
try {
|
||||
$fxStmt = $pdo->prepare(
|
||||
"SELECT id, fixed_field_key, data_type, default_value, default_source, excel_column
|
||||
FROM template_fixed_mapping
|
||||
WHERE template_id = ?"
|
||||
);
|
||||
$fxStmt->execute([$template_id]);
|
||||
$fixedMappings = $fxStmt->fetchAll(PDO::FETCH_ASSOC);
|
||||
} catch (Exception $e) {
|
||||
error_log("[FIXED] lettura template_fixed_mapping fallita: " . $e->getMessage());
|
||||
}
|
||||
|
||||
$savedBindings = []; // risolti con un binding gia' salvato
|
||||
$autoBindings = []; // collegati in automatico (corrispondenza esatta)
|
||||
$pendingBindings = []; // senza corrispondenza: da risolvere a mano
|
||||
|
||||
// Inserisci le righe selezionate in datadb
|
||||
$insertedIds = [];
|
||||
foreach ($selected_rows as $rowIndex) {
|
||||
@@ -172,6 +190,24 @@ foreach ($selected_rows as $rowIndex) {
|
||||
}
|
||||
}
|
||||
|
||||
if (
|
||||
binding_is_list_field($mapping)
|
||||
&& $fieldValue !== null
|
||||
&& trim((string) $fieldValue) !== ''
|
||||
) {
|
||||
$fieldValue = binding_resolve_for_import(
|
||||
$pdo,
|
||||
$mapping,
|
||||
(int) $template_id,
|
||||
(string) $fieldValue,
|
||||
(int) $iddatadb,
|
||||
(int) $user_id,
|
||||
$savedBindings,
|
||||
$autoBindings,
|
||||
$pendingBindings
|
||||
);
|
||||
}
|
||||
|
||||
if ($mapping['is_required'] && (is_null($fieldValue) || $fieldValue === '')) {
|
||||
error_log("Required field missing for mapping ID: " . $mapping['id'] . ", field: " . $mapping['field_label']);
|
||||
}
|
||||
@@ -180,6 +216,43 @@ foreach ($selected_rows as $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));
|
||||
}
|
||||
$fixedValues = [];
|
||||
foreach ($fixedMappings as $fm) {
|
||||
$fixedKey = (string) $fm['fixed_field_key'];
|
||||
$value = null;
|
||||
|
||||
if (($fm['default_source'] ?? 'manual') === 'xls' && trim((string) ($fm['excel_column'] ?? '')) !== '') {
|
||||
$idx = binding_find_column_index((string) $fm['excel_column'], $columns);
|
||||
$raw = ($idx !== false && isset($row[$idx])) ? trim((string) $row[$idx]) : '';
|
||||
|
||||
if ($raw !== '') {
|
||||
$value = binding_resolve_fixed_for_import(
|
||||
$pdo,
|
||||
$fm,
|
||||
(int) $template_id,
|
||||
$raw,
|
||||
(int) $iddatadb,
|
||||
(int) $user_id,
|
||||
$savedBindings,
|
||||
$autoBindings,
|
||||
$pendingBindings
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
if ($value === null || $value === '') {
|
||||
$default = binding_fixed_default($fm);
|
||||
$value = $default !== '' ? $default : null;
|
||||
}
|
||||
|
||||
if ($value !== null && $value !== '') {
|
||||
$fixedValues[$fixedKey] = $value;
|
||||
}
|
||||
}
|
||||
|
||||
if (!empty($fixedValues)) {
|
||||
binding_write_fixed_columns($pdo, (int) $iddatadb, $fixedValues);
|
||||
}
|
||||
}
|
||||
|
||||
// ------------------------------------------------------------
|
||||
@@ -237,5 +310,20 @@ try {
|
||||
|
||||
$_SESSION['inserted_ids'] = $insertedIds;
|
||||
|
||||
if (!empty($pendingBindings)) {
|
||||
$_SESSION['pending_bindings'] = [
|
||||
'template_id' => $template_id,
|
||||
'importref' => $importReferenceCode,
|
||||
'items' => array_values($pendingBindings),
|
||||
'auto' => array_values($autoBindings),
|
||||
'saved' => array_values($savedBindings),
|
||||
];
|
||||
|
||||
header("Location: resolve_bindings.php");
|
||||
exit;
|
||||
}
|
||||
|
||||
unset($_SESSION['pending_bindings']);
|
||||
|
||||
header("Location: imported.php?id=" . urlencode($template_id) . "&importref=" . urlencode($importReferenceCode));
|
||||
exit;
|
||||
|
||||
@@ -5,6 +5,7 @@ ini_set('log_errors', 1);
|
||||
ini_set('error_log', __DIR__ . '/import_debug.log');
|
||||
|
||||
include('include/headscript.php');
|
||||
require_once __DIR__ . '/class/binding-functions.php';
|
||||
|
||||
header('Content-Type: application/json');
|
||||
|
||||
@@ -49,7 +50,7 @@ $db = DBHandlerSelect::getInstance();
|
||||
$pdo = $db->getConnection();
|
||||
|
||||
// Recupera i mapping
|
||||
$stmt = $pdo->prepare("SELECT id, excel_column, data_type, is_required, manual_default, is_manual, field_label, field_id, main_field, auto_value FROM template_mapping WHERE template_id = ?");
|
||||
$stmt = $pdo->prepare("SELECT id, excel_column, data_type, has_list, is_required, manual_default, is_manual, field_label, field_id, main_field, auto_value FROM template_mapping WHERE template_id = ?");
|
||||
$stmt->execute([$template_id]);
|
||||
$allMappings = $stmt->fetchAll(PDO::FETCH_ASSOC);
|
||||
|
||||
@@ -76,6 +77,23 @@ $mappingId244 = (int)$stmtMap->fetchColumn();
|
||||
|
||||
$insertedIds = [];
|
||||
|
||||
$fixedMappings = [];
|
||||
try {
|
||||
$fxStmt = $pdo->prepare(
|
||||
"SELECT id, fixed_field_key, data_type, default_value, default_source, excel_column
|
||||
FROM template_fixed_mapping
|
||||
WHERE template_id = ?"
|
||||
);
|
||||
$fxStmt->execute([$template_id]);
|
||||
$fixedMappings = $fxStmt->fetchAll(PDO::FETCH_ASSOC);
|
||||
} catch (Exception $e) {
|
||||
error_log("[FIXED] lettura template_fixed_mapping fallita: " . $e->getMessage());
|
||||
}
|
||||
|
||||
$savedBindings = [];
|
||||
$autoBindings = [];
|
||||
$pendingBindings = [];
|
||||
|
||||
try {
|
||||
$pdo->beginTransaction();
|
||||
|
||||
@@ -146,9 +164,65 @@ try {
|
||||
}
|
||||
}
|
||||
|
||||
if (
|
||||
binding_is_list_field($mapping)
|
||||
&& $fieldValue !== null
|
||||
&& trim((string) $fieldValue) !== ''
|
||||
) {
|
||||
$fieldValue = binding_resolve_for_import(
|
||||
$pdo,
|
||||
$mapping,
|
||||
$template_id,
|
||||
(string) $fieldValue,
|
||||
(int) $iddatadb,
|
||||
(int) $user_id,
|
||||
$savedBindings,
|
||||
$autoBindings,
|
||||
$pendingBindings
|
||||
);
|
||||
}
|
||||
|
||||
$insDetail->execute([$iddatadb, $mapping['id'], $fieldValue]);
|
||||
}
|
||||
|
||||
$fixedValues = [];
|
||||
foreach ($fixedMappings as $fm) {
|
||||
$fixedKey = (string) $fm['fixed_field_key'];
|
||||
$value = null;
|
||||
|
||||
if (($fm['default_source'] ?? 'manual') === 'xls' && trim((string) ($fm['excel_column'] ?? '')) !== '') {
|
||||
$idx = binding_find_column_index((string) $fm['excel_column'], $columns);
|
||||
$raw = ($idx !== false && isset($row[$idx])) ? trim((string) $row[$idx]) : '';
|
||||
|
||||
if ($raw !== '') {
|
||||
$value = binding_resolve_fixed_for_import(
|
||||
$pdo,
|
||||
$fm,
|
||||
(int) $template_id,
|
||||
$raw,
|
||||
(int) $iddatadb,
|
||||
(int) $user_id,
|
||||
$savedBindings,
|
||||
$autoBindings,
|
||||
$pendingBindings
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
if ($value === null || $value === '') {
|
||||
$default = binding_fixed_default($fm);
|
||||
$value = $default !== '' ? $default : null;
|
||||
}
|
||||
|
||||
if ($value !== null && $value !== '') {
|
||||
$fixedValues[$fixedKey] = $value;
|
||||
}
|
||||
}
|
||||
|
||||
if (!empty($fixedValues)) {
|
||||
binding_write_fixed_columns($pdo, (int) $iddatadb, $fixedValues);
|
||||
}
|
||||
|
||||
// Campo 244 (Accettatore) con lims_user_id
|
||||
if ($limsUserId !== '' && $mappingId244 > 0) {
|
||||
$insDetail->execute([$iddatadb, $mappingId244, $limsUserId]);
|
||||
@@ -157,9 +231,27 @@ try {
|
||||
|
||||
$pdo->commit();
|
||||
|
||||
$state = $_SESSION['pending_bindings'] ?? null;
|
||||
if (!is_array($state) || ($state['importref'] ?? null) !== $importReferenceCode) {
|
||||
$state = [
|
||||
'template_id' => $template_id,
|
||||
'importref' => $importReferenceCode,
|
||||
'items' => [],
|
||||
'auto' => [],
|
||||
'saved' => [],
|
||||
];
|
||||
}
|
||||
|
||||
$state['items'] = binding_merge_buckets($state['items'], $pendingBindings);
|
||||
$state['auto'] = binding_merge_buckets($state['auto'], $autoBindings);
|
||||
$state['saved'] = binding_merge_buckets($state['saved'], $savedBindings);
|
||||
|
||||
$_SESSION['pending_bindings'] = $state;
|
||||
|
||||
echo json_encode([
|
||||
'ok' => true,
|
||||
'inserted' => count($insertedIds)
|
||||
'inserted' => count($insertedIds),
|
||||
'pending_bindings' => count($state['items'])
|
||||
]);
|
||||
exit;
|
||||
} catch (Exception $e) {
|
||||
|
||||
@@ -492,6 +492,7 @@ error_log("Loaded template: " . print_r($template, true));
|
||||
const progressErr = document.getElementById('importProgressError');
|
||||
|
||||
let insertedTotal = 0;
|
||||
let pendingBindings = 0; // valori a lista senza corrispondenza LIMS
|
||||
|
||||
// Invia un batch e ritorna la risposta JSON
|
||||
async function sendBatch(startIndex) {
|
||||
@@ -550,6 +551,7 @@ error_log("Loaded template: " . print_r($template, true));
|
||||
}
|
||||
|
||||
insertedTotal += (result.inserted || 0);
|
||||
pendingBindings = result.pending_bindings || pendingBindings;
|
||||
const done = Math.min(start + BATCH_SIZE, total);
|
||||
const pct = Math.round((done / total) * 100);
|
||||
progressBar.style.width = pct + '%';
|
||||
@@ -565,7 +567,9 @@ error_log("Loaded template: " . print_r($template, true));
|
||||
// PAUSA temporanea di 2 secondi per vedere la barra completa (da rimuovere dopo il test)
|
||||
await new Promise(r => setTimeout(r, 2000));
|
||||
|
||||
window.location.href = 'imported.php?id=' +
|
||||
window.location.href = pendingBindings > 0
|
||||
? 'resolve_bindings.php'
|
||||
: 'imported.php?id=' +
|
||||
encodeURIComponent(data.template_id) +
|
||||
'&importref=' + encodeURIComponent(importReferenceCode);
|
||||
});
|
||||
|
||||
@@ -64,6 +64,11 @@
|
||||
<i class='bx bx-radio-circle'></i><?= htmlspecialchars($insertnewtemplatexls, ENT_QUOTES, 'UTF-8'); ?>
|
||||
</a>
|
||||
</li>
|
||||
<li>
|
||||
<a href="bindings_manage.php">
|
||||
<i class='bx bx-radio-circle'></i><?= htmlspecialchars($bindingsmanage ?? 'Value Bindings', ENT_QUOTES, 'UTF-8'); ?>
|
||||
</a>
|
||||
</li>
|
||||
</ul>
|
||||
</li>
|
||||
<?php endif; ?>
|
||||
|
||||
@@ -88,7 +88,8 @@ $mappings = $stmt->fetchAll(PDO::FETCH_ASSOC);
|
||||
|
||||
// Recupera i fixed fields dalla tabella template_fixed_mapping
|
||||
$stmt = $pdo->prepare("
|
||||
SELECT id, fixed_field_key, is_manual, data_type, is_required, default_value, is_visible_import
|
||||
SELECT id, fixed_field_key, is_manual, data_type, is_required, default_value, is_visible_import,
|
||||
default_source, excel_column
|
||||
FROM template_fixed_mapping
|
||||
WHERE template_id = ?
|
||||
ORDER BY id ASC
|
||||
@@ -594,6 +595,7 @@ $apiSampleJson = $template['api_sample_json'] ?? '';
|
||||
<th style="width:120px; text-align:center;">Required</th>
|
||||
<th>Field</th>
|
||||
<th style="width:140px;">Type</th>
|
||||
<th style="width:230px;">Source</th>
|
||||
<th>Default Value</th>
|
||||
</tr>
|
||||
</thead>
|
||||
@@ -626,6 +628,28 @@ $apiSampleJson = $template['api_sample_json'] ?? '';
|
||||
|
||||
<td><?php echo htmlspecialchars($fm['data_type']); ?></td>
|
||||
|
||||
<td>
|
||||
<?php $fixedSource = $fm['default_source'] ?? 'manual'; ?>
|
||||
<select class="form-select fixed-source-select"
|
||||
data-fixed-id="<?php echo (int)$fm['id']; ?>">
|
||||
<option value="manual" <?php echo $fixedSource === 'manual' ? 'selected' : ''; ?>>Default value</option>
|
||||
<?php if ($sourceType === 'XLS'): ?>
|
||||
<option value="xls" <?php echo $fixedSource === 'xls' ? 'selected' : ''; ?>>Map to XLS Column</option>
|
||||
<?php endif; ?>
|
||||
<?php if ($sourceType === 'API'): ?>
|
||||
<option value="json" <?php echo $fixedSource === 'json' ? 'selected' : ''; ?>>Map to JSON Node</option>
|
||||
<?php endif; ?>
|
||||
</select>
|
||||
|
||||
<?php if ($sourceType === 'XLS'): ?>
|
||||
<select class="form-select xls-columns fixed-xls-column mt-1"
|
||||
style="display:<?php echo $fixedSource === 'xls' ? 'block' : 'none'; ?>"
|
||||
data-fixed-id="<?php echo (int)$fm['id']; ?>"
|
||||
<?php echo !empty($fm['excel_column']) ? 'data-current-xls="' . htmlspecialchars($fm['excel_column'], ENT_QUOTES, 'UTF-8') . '"' : ''; ?>>
|
||||
</select>
|
||||
<?php endif; ?>
|
||||
</td>
|
||||
|
||||
<td>
|
||||
|
||||
<?php if ($fm['data_type'] === 'DATE'): ?>
|
||||
@@ -1279,8 +1303,6 @@ $apiSampleJson = $template['api_sample_json'] ?? '';
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
|
||||
function saveJsonNodes(sampleJson, nodes) {
|
||||
return fetch('update_api_json_nodes.php', {
|
||||
method: 'POST',
|
||||
@@ -1363,6 +1385,41 @@ $apiSampleJson = $template['api_sample_json'] ?? '';
|
||||
});
|
||||
|
||||
// fallback (nel caso cambi senza select2)
|
||||
$(document).on('change', '.fixed-source-select', function() {
|
||||
const id = this.dataset.fixedId;
|
||||
const value = this.value;
|
||||
const $colSelect = $(this).closest('td').find('.fixed-xls-column');
|
||||
|
||||
$colSelect.toggle(value === 'xls');
|
||||
if (value !== 'xls') {
|
||||
$colSelect.val('');
|
||||
}
|
||||
|
||||
saveFixedField(id, 'default_source', value);
|
||||
});
|
||||
|
||||
$(document).on('change', '.fixed-xls-column', function() {
|
||||
saveFixedField(this.dataset.fixedId, 'excel_column', this.value || null);
|
||||
});
|
||||
|
||||
function saveFixedField(id, field, value) {
|
||||
const status = document.getElementById('fixedSaveStatus');
|
||||
if (status) status.textContent = 'Salvataggio...';
|
||||
|
||||
fetch('update_fixed_field.php', {
|
||||
method: 'POST',
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
body: JSON.stringify({ id: parseInt(id, 10), field: field, value: value })
|
||||
})
|
||||
.then(r => r.json())
|
||||
.then(d => {
|
||||
if (status) status.textContent = d.success ? 'Salvato' : ('Errore: ' + (d.message || ''));
|
||||
})
|
||||
.catch(() => {
|
||||
if (status) status.textContent = 'Errore di rete';
|
||||
});
|
||||
}
|
||||
|
||||
$(document).on('change', '.fixed-default-select', function() {
|
||||
saveFixedDefaultSelect(this);
|
||||
});
|
||||
@@ -1882,7 +1939,6 @@ $apiSampleJson = $template['api_sample_json'] ?? '';
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
// Save original Main checkbox state
|
||||
document.querySelectorAll('#schemaFieldsBody .main-field-checkbox').forEach(cb => {
|
||||
cb.dataset.originalChecked = cb.checked ? 'true' : 'false';
|
||||
@@ -2031,7 +2087,6 @@ $apiSampleJson = $template['api_sample_json'] ?? '';
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
document.getElementById('schemaFieldsBody').addEventListener('change', function(event) {
|
||||
if (event.target.classList.contains('manual-default') && event.target.tagName === 'SELECT') {
|
||||
let tr = event.target.closest('tr');
|
||||
@@ -2233,7 +2288,6 @@ $apiSampleJson = $template['api_sample_json'] ?? '';
|
||||
|
||||
<td><strong>${escapeHtml(fixedFieldLabels[r.fixed_field_key] || r.fixed_field_key)}</strong></td>
|
||||
|
||||
|
||||
<td>${escapeHtml(r.data_type || '')}</td>
|
||||
|
||||
<td>
|
||||
|
||||
@@ -0,0 +1,417 @@
|
||||
<?php
|
||||
|
||||
include('include/headscript.php');
|
||||
require_once __DIR__ . '/class/binding-functions.php';
|
||||
|
||||
$pending = $_SESSION['pending_bindings'] ?? null;
|
||||
|
||||
if (isset($_GET['done'])) {
|
||||
$tid = $pending['template_id'] ?? ($_SESSION['template_id'] ?? null);
|
||||
$ref = $pending['importref'] ?? '';
|
||||
unset($_SESSION['pending_bindings']);
|
||||
|
||||
header($tid
|
||||
? "Location: imported.php?id=" . urlencode($tid) . "&importref=" . urlencode($ref)
|
||||
: "Location: xlstemplates_grid.php");
|
||||
exit;
|
||||
}
|
||||
|
||||
if (empty($pending) || empty($pending['items'])) {
|
||||
$tid = $pending['template_id'] ?? ($_SESSION['template_id'] ?? null);
|
||||
$ref = $pending['importref'] ?? '';
|
||||
unset($_SESSION['pending_bindings']);
|
||||
|
||||
if ($tid) {
|
||||
header("Location: imported.php?id=" . urlencode($tid) . "&importref=" . urlencode($ref));
|
||||
} else {
|
||||
header("Location: xlstemplates_grid.php");
|
||||
}
|
||||
exit;
|
||||
}
|
||||
|
||||
$templateId = (int) $pending['template_id'];
|
||||
$importRef = (string) $pending['importref'];
|
||||
$items = array_values($pending['items'] ?? []);
|
||||
$autoItems = array_values($pending['auto'] ?? []);
|
||||
$savedItems = array_values($pending['saved'] ?? []);
|
||||
|
||||
$db = DBHandlerSelect::getInstance();
|
||||
$pdo = $db->getConnection();
|
||||
|
||||
$stmt = $pdo->prepare("SELECT name FROM excel_templates WHERE id = ?");
|
||||
$stmt->execute([$templateId]);
|
||||
$templateName = $stmt->fetchColumn() ?: ('Template ' . $templateId);
|
||||
|
||||
foreach ($items as $i => $item) {
|
||||
$items[$i]['suggestion'] = ($item['kind'] ?? 'custom') === 'fixed'
|
||||
? binding_suggest_fixed(
|
||||
$pdo,
|
||||
(string) ($item['fixed_field_key'] ?? ''),
|
||||
(string) $item['source_value'],
|
||||
(int) $item['mapping_id']
|
||||
)
|
||||
: binding_suggest(
|
||||
$pdo,
|
||||
(int) $item['field_id'],
|
||||
(string) $item['source_value'],
|
||||
(int) $item['mapping_id']
|
||||
);
|
||||
}
|
||||
|
||||
$resolvedItems = [];
|
||||
foreach ($autoItems as $a) {
|
||||
$a['badge'] = 'automatico';
|
||||
$a['badge_class'] = 'bg-success';
|
||||
$resolvedItems[] = $a;
|
||||
}
|
||||
foreach ($savedItems as $s) {
|
||||
$s['badge'] = 'binding salvato';
|
||||
$s['badge_class'] = 'bg-secondary';
|
||||
$resolvedItems[] = $s;
|
||||
}
|
||||
|
||||
$groups = [];
|
||||
$groupOrder = [];
|
||||
$pushToGroup = function (array $row, string $type, $idx = null) use (&$groups, &$groupOrder) {
|
||||
$kind = $row['kind'] ?? 'custom';
|
||||
$gkey = $kind . ':' . (int) ($row['mapping_id'] ?? 0);
|
||||
if (!isset($groups[$gkey])) {
|
||||
$groups[$gkey] = [
|
||||
'label' => $row['field_label'] ?? ('Mapping ' . $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');
|
||||
}
|
||||
|
||||
$pendingRecords = 0;
|
||||
foreach ($items as $item) {
|
||||
$pendingRecords += count($item['datadb_ids'] ?? []);
|
||||
}
|
||||
?>
|
||||
<!doctype html>
|
||||
<html lang="en">
|
||||
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<link rel="icon" href="assets/images/favicon-32x32.png" type="image/png" />
|
||||
<?php include('cssinclude.php'); ?>
|
||||
<link href="https://cdn.jsdelivr.net/npm/select2@4.1.0-rc.0/dist/css/select2.min.css" rel="stylesheet">
|
||||
<style>
|
||||
.source-value {
|
||||
font-family: Consolas, Monaco, monospace;
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
.binding-status {
|
||||
font-size: 12px;
|
||||
}
|
||||
|
||||
td .select2-container {
|
||||
min-width: 240px;
|
||||
}
|
||||
|
||||
/* Allinea l'altezza di select2 a form-select/btn di Bootstrap */
|
||||
.select2-container--default .select2-selection--single {
|
||||
height: 38px;
|
||||
border-color: #ced4da;
|
||||
}
|
||||
|
||||
.select2-container--default .select2-selection--single .select2-selection__rendered {
|
||||
line-height: 36px;
|
||||
}
|
||||
|
||||
.select2-container--default .select2-selection--single .select2-selection__arrow {
|
||||
height: 36px;
|
||||
}
|
||||
</style>
|
||||
<title>Binding valori → LIMS - <?= htmlspecialchars($titlewebsite ?? '', ENT_QUOTES, 'UTF-8'); ?></title>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<div class="wrapper">
|
||||
<?php include('include/navbar.php'); ?>
|
||||
<?php include('include/topbar.php'); ?>
|
||||
<div class="page-wrapper">
|
||||
<div class="page-content">
|
||||
|
||||
<div class="card radius-10">
|
||||
<div class="card-header">
|
||||
<div class="d-flex align-items-center justify-content-between flex-wrap gap-2">
|
||||
<div>
|
||||
<h6 class="mb-0">Binding valori importati → LIMS</h6>
|
||||
<small><?= htmlspecialchars($templateName) ?> · Template ID: <?= $templateId ?></small>
|
||||
</div>
|
||||
<a href="bindings_manage.php?template_id=<?= $templateId ?>" class="btn btn-sm btn-outline-secondary">
|
||||
<i class="fas fa-cog"></i> Gestione binding
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="card-body">
|
||||
<div class="alert alert-info">
|
||||
Alcuni valori importati dal file non corrispondono a nessun valore delle liste LIMS.
|
||||
Finche' non vengono collegati, questi campi non possono essere esportati.
|
||||
Scegli il valore LIMS corretto per ciascuno: il binding viene salvato e
|
||||
riutilizzato nelle importazioni successive.
|
||||
<div class="mt-1">
|
||||
<strong><?= count($items) ?></strong> valore/i da risolvere su
|
||||
<strong><?= $pendingRecords ?></strong> record importati.
|
||||
</div>
|
||||
</div>
|
||||
<?php if (!empty($autoItems)): ?>
|
||||
<div class="alert alert-success">
|
||||
<?= count($autoItems) ?> valore/i collegato/i automaticamente (corrispondenza esatta con il LIMS).
|
||||
</div>
|
||||
<?php endif; ?>
|
||||
|
||||
<div id="bindingError" class="alert alert-danger" style="display:none;"></div>
|
||||
|
||||
<form id="bindingForm">
|
||||
<div class="table-responsive">
|
||||
<table class="table table-bordered align-middle">
|
||||
<thead>
|
||||
<tr>
|
||||
<th style="width:32px;"></th>
|
||||
<th>Valore importato</th>
|
||||
<th style="width:90px;">Record</th>
|
||||
<th>Valore LIMS</th>
|
||||
<th style="width:210px;">Azioni</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<?php foreach ($groupOrder as $gkey): $g = $groups[$gkey];
|
||||
$pendCount = count($g['pending']);
|
||||
$totalCount = $pendCount + count($g['resolved']); ?>
|
||||
<tr class="binding-group-header table-light">
|
||||
<td colspan="5">
|
||||
<strong><?= htmlspecialchars($g['label']) ?></strong>
|
||||
<?php if (($g['kind'] ?? 'custom') === 'fixed'): ?><span class="badge bg-light text-dark border">campo fisso</span><?php endif; ?>
|
||||
<span class="text-muted">· <?= $totalCount ?> valore/i<?php if ($pendCount): ?>, <?= $pendCount ?> da risolvere<?php endif; ?></span>
|
||||
</td>
|
||||
</tr>
|
||||
|
||||
<?php foreach ($g['pending'] as $p): $idx = $p['idx']; $item = $p['data'];
|
||||
$suggestion = $item['suggestion'] ?? null; ?>
|
||||
<tr class="binding-row"
|
||||
data-index="<?= $idx ?>"
|
||||
data-kind="<?= htmlspecialchars($item['kind'] ?? 'custom', ENT_QUOTES) ?>"
|
||||
data-fixed-key="<?= htmlspecialchars($item['fixed_field_key'] ?? '', ENT_QUOTES) ?>"
|
||||
data-mapping-id="<?= (int) $item['mapping_id'] ?>"
|
||||
data-field-id="<?= (int) $item['field_id'] ?>"
|
||||
data-source-value="<?= htmlspecialchars($item['source_value'], ENT_QUOTES) ?>"
|
||||
data-datadb-ids="<?= htmlspecialchars(json_encode($item['datadb_ids']), ENT_QUOTES) ?>">
|
||||
<td class="text-muted ps-4">›</td>
|
||||
<td class="source-value"><?= htmlspecialchars($item['source_value']) ?></td>
|
||||
<td><?= count($item['datadb_ids'] ?? []) ?></td>
|
||||
<td>
|
||||
<select class="form-select binding-select">
|
||||
<?php if ($suggestion): ?>
|
||||
<option value="<?= (int) $suggestion['lims_value_id'] ?>" selected><?= htmlspecialchars($suggestion['lims_value']) ?></option>
|
||||
<?php else: ?>
|
||||
<option value="">Seleziona valore LIMS...</option>
|
||||
<?php endif; ?>
|
||||
</select>
|
||||
</td>
|
||||
<td>
|
||||
<button type="button" class="btn btn-sm btn-outline-secondary skip-binding-btn">Nessuna corrispondenza</button>
|
||||
<div class="binding-status mt-1">
|
||||
<?php if ($suggestion): ?>
|
||||
<span class="badge bg-warning text-dark" title="Stesso valore gia' collegato in un altro template">
|
||||
suggerito · <?= htmlspecialchars($suggestion['template_name'] ?: ('template ' . $suggestion['template_id'])) ?>
|
||||
</span>
|
||||
<?php else: ?>
|
||||
<span class="text-muted">In attesa</span>
|
||||
<?php endif; ?>
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
<?php endforeach; ?>
|
||||
|
||||
<?php foreach ($g['resolved'] as $r): $res = $r['data']; ?>
|
||||
<tr class="binding-row"
|
||||
data-kind="<?= htmlspecialchars($res['kind'] ?? 'custom', ENT_QUOTES) ?>"
|
||||
data-fixed-key="<?= htmlspecialchars($res['fixed_field_key'] ?? '', ENT_QUOTES) ?>"
|
||||
data-mapping-id="<?= (int) $res['mapping_id'] ?>"
|
||||
data-field-id="<?= (int) $res['field_id'] ?>"
|
||||
data-source-value="<?= htmlspecialchars($res['source_value'], ENT_QUOTES) ?>"
|
||||
data-datadb-ids="<?= htmlspecialchars(json_encode($res['datadb_ids']), ENT_QUOTES) ?>">
|
||||
<td class="text-muted ps-4">›</td>
|
||||
<td class="source-value"><?= htmlspecialchars($res['source_value']) ?></td>
|
||||
<td><?= count($res['datadb_ids'] ?? []) ?></td>
|
||||
<td>
|
||||
<select class="form-select binding-select">
|
||||
<option value="<?= (int) $res['lims_value_id'] ?>" selected><?= htmlspecialchars($res['lims_value']) ?></option>
|
||||
</select>
|
||||
</td>
|
||||
<td>
|
||||
<button type="button" class="btn btn-sm btn-outline-secondary skip-binding-btn">Nessuna corrispondenza</button>
|
||||
<div class="binding-status mt-1"><span class="badge <?= $res['badge_class'] ?>"><?= $res['badge'] ?></span></div>
|
||||
</td>
|
||||
</tr>
|
||||
<?php endforeach; ?>
|
||||
<?php endforeach; ?>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
|
||||
<div class="d-flex justify-content-end gap-2 mt-3">
|
||||
<a href="resolve_bindings.php?done=1"
|
||||
class="btn btn-outline-secondary">Salta per ora</a>
|
||||
<button type="submit" class="btn btn-primary" id="confirmBindingsBtn" disabled>
|
||||
Conferma e prosegui
|
||||
</button>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
<div class="overlay toggle-icon"></div>
|
||||
<a href="javaScript:;" class="back-to-top"><i class='bx bxs-up-arrow-alt'></i></a>
|
||||
<?php include('include/footer.php'); ?>
|
||||
</div>
|
||||
|
||||
<?php include('jsinclude.php'); ?>
|
||||
<script src="https://cdn.jsdelivr.net/npm/select2@4.1.0-rc.0/dist/js/select2.min.js"></script>
|
||||
|
||||
<script>
|
||||
$(function() {
|
||||
const TEMPLATE_ID = <?= $templateId ?>;
|
||||
const IMPORT_REF = <?= json_encode($importRef) ?>;
|
||||
const CONTINUE_URL = 'resolve_bindings.php?done=1';
|
||||
|
||||
const $form = $('#bindingForm');
|
||||
const $btn = $('#confirmBindingsBtn');
|
||||
const $error = $('#bindingError');
|
||||
|
||||
$('.binding-select').each(function() {
|
||||
const $row = $(this).closest('.binding-row');
|
||||
const isFixed = $row.data('kind') === 'fixed';
|
||||
|
||||
$(this).select2({
|
||||
placeholder: 'Seleziona valore LIMS...',
|
||||
width: '100%',
|
||||
ajax: {
|
||||
url: isFixed ? 'search_fixed_field_values.php' : 'search_customfield_values.php',
|
||||
dataType: 'json',
|
||||
delay: 200,
|
||||
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 || []
|
||||
})
|
||||
},
|
||||
minimumInputLength: 0
|
||||
});
|
||||
});
|
||||
|
||||
function refreshButton() {
|
||||
const allReady = $('.binding-row').toArray().every(row => {
|
||||
const $row = $(row);
|
||||
return $row.hasClass('is-skipped') || $row.find('.binding-select').val();
|
||||
});
|
||||
$btn.prop('disabled', !allReady);
|
||||
}
|
||||
|
||||
$('.binding-select').on('change', refreshButton);
|
||||
refreshButton();
|
||||
|
||||
$('.skip-binding-btn').on('click', function() {
|
||||
const $row = $(this).closest('.binding-row');
|
||||
const $select = $row.find('.binding-select');
|
||||
const $status = $row.find('.binding-status');
|
||||
const skipped = $row.toggleClass('is-skipped').hasClass('is-skipped');
|
||||
|
||||
$(this).toggleClass('btn-outline-secondary', !skipped).toggleClass('btn-secondary', skipped);
|
||||
$select.val(null).trigger('change').prop('disabled', skipped);
|
||||
$status.html(skipped ? 'Nessuna corrispondenza' : '<span class="text-muted">In attesa</span>');
|
||||
refreshButton();
|
||||
});
|
||||
|
||||
$form.on('submit', function(e) {
|
||||
e.preventDefault();
|
||||
$error.hide();
|
||||
$btn.prop('disabled', true).text('Salvataggio...');
|
||||
|
||||
const tasks = $('.binding-row').toArray().map(row => {
|
||||
const $row = $(row);
|
||||
const $status = $row.find('.binding-status');
|
||||
const datadbIds = JSON.stringify($row.data('datadb-ids') || []);
|
||||
const sourceValue = String($row.data('source-value'));
|
||||
const mappingId = $row.data('mapping-id');
|
||||
|
||||
if ($row.hasClass('is-skipped')) {
|
||||
return $.post('skip_binding.php', {
|
||||
kind: $row.data('kind') || 'custom',
|
||||
mapping_id: mappingId,
|
||||
template_id: TEMPLATE_ID,
|
||||
source_value: sourceValue,
|
||||
datadb_ids: datadbIds
|
||||
}).then(resp => {
|
||||
if (resp && resp.success) {
|
||||
$status.html('<span class="text-success">Azzerato</span>');
|
||||
return true;
|
||||
}
|
||||
throw new Error((resp && resp.error) || 'Errore azzeramento valore');
|
||||
});
|
||||
}
|
||||
|
||||
const $select = $row.find('.binding-select');
|
||||
const selectedData = $select.select2('data')[0] || {};
|
||||
|
||||
return $.post('save_binding.php', {
|
||||
kind: $row.data('kind') || 'custom',
|
||||
mapping_id: mappingId,
|
||||
field_id: $row.data('field-id'),
|
||||
template_id: TEMPLATE_ID,
|
||||
source_value: sourceValue,
|
||||
lims_value_id: $select.val(),
|
||||
lims_value: selectedData.text || '',
|
||||
datadb_ids: datadbIds
|
||||
}).then(resp => {
|
||||
if (resp && resp.success) {
|
||||
$status.html('<span class="text-success">Salvato</span>');
|
||||
return true;
|
||||
}
|
||||
throw new Error((resp && resp.error) || 'Errore salvataggio binding');
|
||||
});
|
||||
});
|
||||
|
||||
Promise.all(tasks)
|
||||
.then(() => {
|
||||
window.location.href = CONTINUE_URL;
|
||||
})
|
||||
.catch(err => {
|
||||
$error.text(err.message || 'Errore durante il salvataggio dei binding.').show();
|
||||
$btn.prop('disabled', false).text('Conferma e prosegui');
|
||||
});
|
||||
});
|
||||
});
|
||||
</script>
|
||||
</body>
|
||||
|
||||
</html>
|
||||
@@ -0,0 +1,101 @@
|
||||
<?php
|
||||
|
||||
require_once dirname(__DIR__, 2) . '/vendor/autoload.php';
|
||||
require_once __DIR__ . '/class/db-functions.php';
|
||||
require_once __DIR__ . '/class/binding-functions.php';
|
||||
include dirname(__DIR__) . '/../extra/auth.php';
|
||||
|
||||
header('Content-Type: application/json');
|
||||
ini_set('display_errors', '0');
|
||||
error_reporting(E_ALL);
|
||||
|
||||
if (!Auth::check()) {
|
||||
http_response_code(401);
|
||||
echo json_encode(['success' => false, 'error' => 'Unauthorized']);
|
||||
exit;
|
||||
}
|
||||
|
||||
if ($_SERVER['REQUEST_METHOD'] !== 'POST') {
|
||||
http_response_code(405);
|
||||
echo json_encode(['success' => false, 'error' => 'Method not allowed']);
|
||||
exit;
|
||||
}
|
||||
|
||||
$kind = ($_POST['kind'] ?? 'custom') === 'fixed' ? 'fixed' : 'custom';
|
||||
$templateId = intval($_POST['template_id'] ?? 0);
|
||||
$mappingId = intval($_POST['mapping_id'] ?? 0);
|
||||
$fieldId = intval($_POST['field_id'] ?? 0);
|
||||
$sourceValue = (string) ($_POST['source_value'] ?? '');
|
||||
$limsValueId = intval($_POST['lims_value_id'] ?? 0);
|
||||
$limsValue = (string) ($_POST['lims_value'] ?? '');
|
||||
|
||||
$datadbIds = [];
|
||||
if (isset($_POST['datadb_ids'])) {
|
||||
$decoded = json_decode($_POST['datadb_ids'], true);
|
||||
if (is_array($decoded)) {
|
||||
$datadbIds = $decoded;
|
||||
}
|
||||
}
|
||||
|
||||
if ($templateId <= 0 || $mappingId <= 0 || trim($sourceValue) === '' || $limsValueId <= 0) {
|
||||
http_response_code(422);
|
||||
echo json_encode(['success' => false, 'error' => 'Missing required parameters']);
|
||||
exit;
|
||||
}
|
||||
|
||||
try {
|
||||
$pdo = DBHandlerSelect::getInstance()->getConnection();
|
||||
$createdBy = Auth::user() ? (int) Auth::user()->present()->id : null;
|
||||
|
||||
if ($kind === 'fixed') {
|
||||
$stmt = $pdo->prepare("SELECT fixed_field_key FROM template_fixed_mapping WHERE id = ?");
|
||||
$stmt->execute([$mappingId]);
|
||||
$fixedKey = (string) ($stmt->fetchColumn() ?: '');
|
||||
|
||||
if ($fixedKey === '' || !binding_fixed_is_list($fixedKey)) {
|
||||
http_response_code(422);
|
||||
echo json_encode(['success' => false, 'error' => 'Invalid fixed field']);
|
||||
exit;
|
||||
}
|
||||
|
||||
binding_upsert($pdo, $templateId, $mappingId, 0, $sourceValue, $limsValueId, $limsValue, $createdBy, 'fixed');
|
||||
|
||||
$applied = !empty($datadbIds)
|
||||
? binding_apply_to_datadb($pdo, $fixedKey, (string) $limsValueId, $datadbIds)
|
||||
: 0;
|
||||
|
||||
echo json_encode([
|
||||
'success' => true,
|
||||
'applied_rows' => $applied,
|
||||
'kind' => 'fixed',
|
||||
'source_value' => $sourceValue,
|
||||
'lims_value' => $limsValue,
|
||||
'lims_value_id' => $limsValueId,
|
||||
]);
|
||||
exit;
|
||||
}
|
||||
|
||||
if ($fieldId <= 0) {
|
||||
$stmt = $pdo->prepare("SELECT field_id FROM template_mapping WHERE id = ?");
|
||||
$stmt->execute([$mappingId]);
|
||||
$fieldId = (int) ($stmt->fetchColumn() ?: 0);
|
||||
}
|
||||
|
||||
binding_upsert($pdo, $templateId, $mappingId, $fieldId, $sourceValue, $limsValueId, $limsValue, $createdBy);
|
||||
|
||||
$applied = !empty($datadbIds)
|
||||
? binding_apply_to_details($pdo, $mappingId, (string) $limsValueId, $datadbIds)
|
||||
: 0;
|
||||
|
||||
echo json_encode([
|
||||
'success' => true,
|
||||
'applied_rows' => $applied,
|
||||
'kind' => 'custom',
|
||||
'source_value' => $sourceValue,
|
||||
'lims_value' => $limsValue,
|
||||
'lims_value_id' => $limsValueId,
|
||||
]);
|
||||
} catch (Exception $e) {
|
||||
http_response_code(500);
|
||||
echo json_encode(['success' => false, 'error' => $e->getMessage()]);
|
||||
}
|
||||
@@ -0,0 +1,54 @@
|
||||
<?php
|
||||
|
||||
require_once dirname(__DIR__, 2) . '/vendor/autoload.php';
|
||||
require_once __DIR__ . '/class/db-functions.php';
|
||||
require_once __DIR__ . '/class/binding-functions.php';
|
||||
include dirname(__DIR__) . '/../extra/auth.php';
|
||||
|
||||
header('Content-Type: application/json');
|
||||
ini_set('display_errors', '0');
|
||||
error_reporting(E_ALL);
|
||||
|
||||
if (!Auth::check()) {
|
||||
http_response_code(401);
|
||||
echo json_encode(['error' => 'Unauthorized']);
|
||||
exit;
|
||||
}
|
||||
|
||||
$fieldKey = trim($_GET['field_key'] ?? '');
|
||||
$templateId = (int) ($_GET['template_id'] ?? 0);
|
||||
$q = binding_normalize_value($_GET['q'] ?? '');
|
||||
$rawLimit = (int) ($_GET['limit'] ?? 50);
|
||||
$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 ($q !== '') {
|
||||
$terms = array_values(array_filter(preg_split('/\s+/u', $q, -1, PREG_SPLIT_NO_EMPTY)));
|
||||
$values = array_values(array_filter($values, function ($v) use ($terms) {
|
||||
$hay = binding_normalize_value($v['text'] ?? '');
|
||||
foreach ($terms as $t) {
|
||||
if (mb_strpos($hay, $t) === false) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}));
|
||||
}
|
||||
|
||||
if ($limit > 0) {
|
||||
$values = array_slice($values, 0, $limit);
|
||||
}
|
||||
|
||||
echo json_encode(['results' => $values]);
|
||||
} catch (Exception $e) {
|
||||
http_response_code(500);
|
||||
echo json_encode(['error' => $e->getMessage(), 'results' => []]);
|
||||
}
|
||||
@@ -0,0 +1,71 @@
|
||||
<?php
|
||||
|
||||
require_once dirname(__DIR__, 2) . '/vendor/autoload.php';
|
||||
require_once __DIR__ . '/class/db-functions.php';
|
||||
require_once __DIR__ . '/class/binding-functions.php';
|
||||
include dirname(__DIR__) . '/../extra/auth.php';
|
||||
|
||||
header('Content-Type: application/json');
|
||||
ini_set('display_errors', '0');
|
||||
error_reporting(E_ALL);
|
||||
|
||||
if (!Auth::check()) {
|
||||
http_response_code(401);
|
||||
echo json_encode(['success' => false, 'error' => 'Unauthorized']);
|
||||
exit;
|
||||
}
|
||||
|
||||
if ($_SERVER['REQUEST_METHOD'] !== 'POST') {
|
||||
http_response_code(405);
|
||||
echo json_encode(['success' => false, 'error' => 'Method not allowed']);
|
||||
exit;
|
||||
}
|
||||
|
||||
$kind = ($_POST['kind'] ?? 'custom') === 'fixed' ? 'fixed' : 'custom';
|
||||
$mappingId = intval($_POST['mapping_id'] ?? 0);
|
||||
$sourceValue = (string) ($_POST['source_value'] ?? '');
|
||||
|
||||
$datadbIds = [];
|
||||
if (isset($_POST['datadb_ids'])) {
|
||||
$decoded = json_decode($_POST['datadb_ids'], true);
|
||||
if (is_array($decoded)) {
|
||||
$datadbIds = $decoded;
|
||||
}
|
||||
}
|
||||
|
||||
if ($mappingId <= 0 || trim($sourceValue) === '') {
|
||||
http_response_code(422);
|
||||
echo json_encode(['success' => false, 'error' => 'Missing required parameters']);
|
||||
exit;
|
||||
}
|
||||
|
||||
try {
|
||||
$pdo = DBHandlerSelect::getInstance()->getConnection();
|
||||
|
||||
if ($kind === 'fixed') {
|
||||
$stmt = $pdo->prepare("SELECT fixed_field_key FROM template_fixed_mapping WHERE id = ?");
|
||||
$stmt->execute([$mappingId]);
|
||||
$fixedKey = (string) ($stmt->fetchColumn() ?: '');
|
||||
|
||||
if ($fixedKey === '') {
|
||||
http_response_code(422);
|
||||
echo json_encode(['success' => false, 'error' => 'Invalid fixed field']);
|
||||
exit;
|
||||
}
|
||||
|
||||
$cleared = !empty($datadbIds)
|
||||
? binding_apply_to_datadb($pdo, $fixedKey, null, $datadbIds)
|
||||
: 0;
|
||||
} else {
|
||||
$cleared = !empty($datadbIds)
|
||||
? binding_apply_to_details($pdo, $mappingId, '', $datadbIds)
|
||||
: 0;
|
||||
}
|
||||
|
||||
binding_delete($pdo, $mappingId, $sourceValue, $kind);
|
||||
|
||||
echo json_encode(['success' => true, 'cleared_rows' => $cleared]);
|
||||
} catch (Exception $e) {
|
||||
http_response_code(500);
|
||||
echo json_encode(['success' => false, 'error' => $e->getMessage()]);
|
||||
}
|
||||
@@ -0,0 +1,19 @@
|
||||
-- json_node e default_source possono gia' esistere: in quel caso saltare le ADD COLUMN.
|
||||
|
||||
ALTER TABLE `template_fixed_mapping`
|
||||
ADD COLUMN `json_node` varchar(500) DEFAULT NULL,
|
||||
ADD COLUMN `default_source` enum('manual','xls','json') NOT NULL DEFAULT 'manual';
|
||||
|
||||
ALTER TABLE `template_fixed_mapping`
|
||||
ADD COLUMN `excel_column` varchar(255) DEFAULT NULL AFTER `json_node`;
|
||||
|
||||
ALTER TABLE `template_fixed_mapping`
|
||||
MODIFY COLUMN `default_source` enum('manual','xls','json') NOT NULL DEFAULT 'manual';
|
||||
|
||||
ALTER TABLE `lims_value_binding`
|
||||
ADD COLUMN `binding_kind` enum('custom','fixed') NOT NULL DEFAULT 'custom' AFTER `template_id`,
|
||||
MODIFY COLUMN `field_id` int(11) NOT NULL DEFAULT 0;
|
||||
|
||||
ALTER TABLE `lims_value_binding` DROP INDEX `uq_mapping_source`;
|
||||
ALTER TABLE `lims_value_binding`
|
||||
ADD UNIQUE KEY `uq_kind_mapping_source` (`binding_kind`, `mapping_id`, `source_key`);
|
||||
@@ -0,0 +1,17 @@
|
||||
CREATE TABLE IF NOT EXISTS `lims_value_binding` (
|
||||
`id` int(11) NOT NULL AUTO_INCREMENT,
|
||||
`template_id` int(11) NOT NULL,
|
||||
`mapping_id` int(11) NOT NULL,
|
||||
`field_id` int(11) NOT NULL,
|
||||
`source_value` varchar(500) NOT NULL,
|
||||
`source_key` varchar(500) NOT NULL,
|
||||
`lims_value_id` int(11) NOT NULL,
|
||||
`lims_value` varchar(500) DEFAULT NULL,
|
||||
`created_by` int(11) DEFAULT NULL,
|
||||
`created_at` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
`updated_at` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
|
||||
PRIMARY KEY (`id`),
|
||||
UNIQUE KEY `uq_mapping_source` (`mapping_id`, `source_key`),
|
||||
KEY `idx_template` (`template_id`),
|
||||
KEY `idx_field_source` (`field_id`, `source_key`)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci;
|
||||
@@ -17,7 +17,7 @@ if ($id <= 0) {
|
||||
exit;
|
||||
}
|
||||
|
||||
$allowed = ['default_value', 'is_visible_import', 'is_required'];
|
||||
$allowed = ['default_value', 'is_visible_import', 'is_required', 'default_source', 'excel_column'];
|
||||
if (!in_array($field, $allowed, true)) {
|
||||
echo json_encode(['success' => false, 'message' => 'Invalid field']);
|
||||
exit;
|
||||
@@ -27,6 +27,21 @@ if ($field === 'is_visible_import' || $field === 'is_required') {
|
||||
$value = ((int)$value === 1) ? 1 : 0;
|
||||
}
|
||||
|
||||
if ($field === 'default_source') {
|
||||
$value = in_array($value, ['manual', 'xls', 'json'], true) ? $value : 'manual';
|
||||
|
||||
if ($value !== 'xls') {
|
||||
$pdo->prepare("UPDATE template_fixed_mapping SET excel_column = NULL WHERE id = :id")
|
||||
->execute([':id' => $id]);
|
||||
}
|
||||
}
|
||||
|
||||
if ($field === 'excel_column') {
|
||||
$value = trim((string)$value);
|
||||
if ($value === '') {
|
||||
$value = null;
|
||||
}
|
||||
}
|
||||
|
||||
$sql = "UPDATE template_fixed_mapping SET {$field} = :val WHERE id = :id";
|
||||
$stmt = $pdo->prepare($sql);
|
||||
|
||||
@@ -22,6 +22,7 @@
|
||||
* To add a new rule — just append another closure to $validators.
|
||||
*/
|
||||
include('include/headscript.php');
|
||||
require_once __DIR__ . '/class/binding-functions.php';
|
||||
|
||||
$dbHandler = DBHandlerSelect::getInstance();
|
||||
$pdo = $dbHandler->getConnection();
|
||||
@@ -124,6 +125,29 @@ $validators[] = function (int $iddatadb, array $ctx): array {
|
||||
return $errors;
|
||||
};
|
||||
|
||||
$validators[] = function (int $iddatadb, array $ctx): array {
|
||||
$errors = [];
|
||||
|
||||
foreach (($ctx['listCustom'] ?? []) as $cf) {
|
||||
$val = trim((string) ($ctx['customValues'][(int) $cf['mapping_id']] ?? ''));
|
||||
if ($val === '' || ctype_digit($val)) {
|
||||
continue;
|
||||
}
|
||||
if (!binding_has_lims_values((int) $cf['field_id'])) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$label = rtrim($cf['field_label'], ': ');
|
||||
$errors[] = [
|
||||
'field' => 'field_label:' . $cf['field_label'],
|
||||
'message' => $label . ': il valore importato "' . $val . '" non e\' collegato a nessun valore '
|
||||
. 'della lista LIMS. Aprire Gestione binding o ri-importare per collegarlo.',
|
||||
];
|
||||
}
|
||||
|
||||
return $errors;
|
||||
};
|
||||
|
||||
// Logical fixed_field_key - real datadb column (mirrors imported.php $fixedAliasMap)
|
||||
$fixedAliasMap = [
|
||||
'ClienteResponsabile' => 'cliente_responsabile_id',
|
||||
@@ -197,6 +221,7 @@ try {
|
||||
|
||||
$requiredFixedByTemplate = []; // template_id => [ fixed_field_key => label ]
|
||||
$requiredCustomByTemplate = []; // template_id => [ { mapping_id, field_label }, ... ]
|
||||
$listCustomByTemplate = []; // template_id => [ { mapping_id, field_label }, ... ]
|
||||
|
||||
if (!empty($templateIds)) {
|
||||
$tplPlaceholders = implode(',', array_fill(0, count($templateIds), '?'));
|
||||
@@ -237,6 +262,21 @@ try {
|
||||
'field_label' => $r['field_label'],
|
||||
];
|
||||
}
|
||||
|
||||
$stmt = $pdo->prepare("
|
||||
SELECT id AS mapping_id, template_id, field_id, field_label
|
||||
FROM template_mapping
|
||||
WHERE template_id IN ($tplPlaceholders)
|
||||
AND data_type = 'SceltaMultipla'
|
||||
");
|
||||
$stmt->execute($templateIds);
|
||||
foreach ($stmt->fetchAll(PDO::FETCH_ASSOC) as $r) {
|
||||
$listCustomByTemplate[(int)$r['template_id']][] = [
|
||||
'mapping_id' => (int)$r['mapping_id'],
|
||||
'field_id' => (int)$r['field_id'],
|
||||
'field_label' => $r['field_label'],
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
// Custom field values per record (import_data_details.id is the FK to datadb)
|
||||
@@ -268,6 +308,7 @@ try {
|
||||
'fixedAliasMap' => $fixedAliasMap,
|
||||
'requiredFixed' => $requiredFixedByTemplate[$templateId] ?? [],
|
||||
'requiredCustom' => $requiredCustomByTemplate[$templateId] ?? [],
|
||||
'listCustom' => $listCustomByTemplate[$templateId] ?? [],
|
||||
'customValues' => $customValuesByRecord[$iddatadb] ?? [],
|
||||
];
|
||||
|
||||
|
||||
Reference in New Issue
Block a user