added fixed fields
This commit is contained in:
parent
8838edf3a1
commit
4e4cae1df8
@ -16,3 +16,14 @@
|
|||||||
2026-01-27 15:34:06 - Errore nel recupero dati: HTTP 404, Risposta:
|
2026-01-27 15:34:06 - Errore nel recupero dati: HTTP 404, Risposta:
|
||||||
2026-01-27 15:34:10 - Errore nel recupero dati: HTTP 404, Risposta:
|
2026-01-27 15:34:10 - Errore nel recupero dati: HTTP 404, Risposta:
|
||||||
2026-01-27 15:35:13 - Errore nel recupero dati: HTTP 404, Risposta:
|
2026-01-27 15:35:13 - Errore nel recupero dati: HTTP 404, Risposta:
|
||||||
|
2026-01-29 14:33:38 [ClienteResponsabile] Errore nel recupero dati: HTTP 404, Risposta:
|
||||||
|
2026-01-29 14:33:39 [MoltiplicatorePrezzo] Autenticazione fallita: HTTP 400, Errore cURL: , Risposta: {"title":"Bad Request","status":400,"detail":"Cannot persist the object. It was modified or deleted (purged) by another application.","instance":"POST /api/authentication/authenticate","errorCode":"96bfc1252b"}
|
||||||
|
2026-01-29 14:34:04 [ClienteResponsabile] Errore nel recupero dati: HTTP 404, Risposta:
|
||||||
|
2026-01-29 14:37:29 [ClienteResponsabile] Errore nel recupero dati: HTTP 404, Risposta:
|
||||||
|
2026-01-29 14:41:55 [ClienteResponsabile] Errore nel recupero dati: HTTP 404, Risposta:
|
||||||
|
2026-01-29 14:42:03 [ClienteResponsabile] Errore nel recupero dati: HTTP 404, Risposta:
|
||||||
|
2026-01-29 14:42:52 [ClienteResponsabile] Errore nel recupero dati: HTTP 404, Risposta:
|
||||||
|
2026-01-29 14:43:00 [ClienteResponsabile] Errore nel recupero dati: HTTP 404, Risposta:
|
||||||
|
2026-01-30 10:50:43 [AnagraficaCertestService] Autenticazione fallita: HTTP 400, Errore cURL: , Risposta: {"title":"Bad Request","status":400,"detail":"Cannot persist the object. It was modified or deleted (purged) by another application.","instance":"POST /api/authentication/authenticate","errorCode":"96bfc1252b"}
|
||||||
|
2026-01-30 10:50:43 [MoltiplicatorePrezzo] Autenticazione fallita: HTTP 400, Errore cURL: , Risposta: {"title":"Bad Request","status":400,"detail":"Cannot persist the object. It was modified or deleted (purged) by another application.","instance":"POST /api/authentication/authenticate","errorCode":"96bfc1252b"}
|
||||||
|
2026-01-30 11:09:22 [MoltiplicatorePrezzo] Autenticazione fallita: HTTP 400, Errore cURL: , Risposta: {"title":"Bad Request","status":400,"detail":"Cannot persist the object. It was modified or deleted (purged) by another application.","instance":"POST /api/authentication/authenticate","errorCode":"96bfc1252b"}
|
||||||
|
|||||||
89
public/userarea/get_fixed_field_data.php
Normal file
89
public/userarea/get_fixed_field_data.php
Normal file
@ -0,0 +1,89 @@
|
|||||||
|
<?php
|
||||||
|
// get_fixed_field_data.php
|
||||||
|
|
||||||
|
require_once dirname(__DIR__, 2) . '/vendor/autoload.php';
|
||||||
|
require_once dirname(__FILE__) . '/class/VisualLimsApiClient.class.php';
|
||||||
|
|
||||||
|
header('Content-Type: application/json');
|
||||||
|
ini_set('display_errors', '0');
|
||||||
|
error_reporting(E_ALL);
|
||||||
|
|
||||||
|
$field = $_GET['field'] ?? ''; // es: MoltiplicatorePrezzo, AnagraficaCertestObject, ...
|
||||||
|
|
||||||
|
if (!$field) {
|
||||||
|
http_response_code(400);
|
||||||
|
echo json_encode(['error' => 'Parametro "field" mancante']);
|
||||||
|
exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
$api = VisualLimsApiClient::getInstance();
|
||||||
|
$base_url = 'https://93.43.5.102/limsapi/api/odata/';
|
||||||
|
|
||||||
|
$data = null;
|
||||||
|
$endpoint = null;
|
||||||
|
$cache_file = null;
|
||||||
|
$options = []; // qui puoi aggiungere filtri/ordering per campo se serve
|
||||||
|
|
||||||
|
try {
|
||||||
|
switch ($field) {
|
||||||
|
case 'MoltiplicatorePrezzo':
|
||||||
|
$endpoint = 'MoltiplicatorePrezzi';
|
||||||
|
$cache_file = __DIR__ . '/cache/moltiplicatori_prezzo.json';
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 'AnagraficaCertestObject':
|
||||||
|
$endpoint = 'AnagraficaCertestObject';
|
||||||
|
$cache_file = __DIR__ . '/cache/anagrafica_certest_object.json';
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 'AnagraficaCertestService':
|
||||||
|
$endpoint = 'AnagraficaCertestService';
|
||||||
|
$cache_file = __DIR__ . '/cache/anagrafica_certest_service.json';
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 'ClienteResponsabile':
|
||||||
|
$id_cliente = (int)($_GET['id_cliente'] ?? 0);
|
||||||
|
if ($id_cliente <= 0) {
|
||||||
|
http_response_code(400);
|
||||||
|
echo json_encode(['error' => 'Manca o invalido id_cliente']);
|
||||||
|
exit;
|
||||||
|
}
|
||||||
|
$endpoint = "Cliente($id_cliente)?\$expand=Responsabili";
|
||||||
|
$cache_file = __DIR__ . '/cache/cliente_responsabili_' . $id_cliente . '.json';
|
||||||
|
break;
|
||||||
|
|
||||||
|
// case 'FuturoCampo5':
|
||||||
|
// $endpoint = 'QualcosaElse';
|
||||||
|
// break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
http_response_code(400);
|
||||||
|
echo json_encode(['error' => "Campo non supportato: $field"]);
|
||||||
|
exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Opzionale: caching semplice (molto utile se i dati cambiano poco)
|
||||||
|
if ($cache_file && file_exists($cache_file) && (time() - filemtime($cache_file) < 3600)) { // 1 ora
|
||||||
|
$data = json_decode(file_get_contents($cache_file), true);
|
||||||
|
} else {
|
||||||
|
$data = $api->get($endpoint, $options);
|
||||||
|
|
||||||
|
if ($cache_file) {
|
||||||
|
file_put_contents($cache_file, json_encode($data, JSON_PRETTY_PRINT));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Log ultimo URL (opzionale, per debug)
|
||||||
|
$full_url = $base_url . $endpoint . ($options ? '?' . http_build_query($options) : '');
|
||||||
|
file_put_contents(__DIR__ . '/last_urls.log', date('c') . " - $field - $full_url\n", FILE_APPEND);
|
||||||
|
|
||||||
|
echo json_encode($data);
|
||||||
|
} catch (Exception $e) {
|
||||||
|
file_put_contents(
|
||||||
|
__DIR__ . '/error_log.txt',
|
||||||
|
date('Y-m-d H:i:s') . " [$field] " . $e->getMessage() . PHP_EOL,
|
||||||
|
FILE_APPEND
|
||||||
|
);
|
||||||
|
http_response_code(500);
|
||||||
|
echo json_encode(['error' => $e->getMessage()]);
|
||||||
|
}
|
||||||
@ -74,6 +74,25 @@ $slugMapping = [];
|
|||||||
foreach ($stmt->fetchAll(PDO::FETCH_ASSOC) as $row) {
|
foreach ($stmt->fetchAll(PDO::FETCH_ASSOC) as $row) {
|
||||||
$slugMapping[$row['mysql_column_name']] = $row['user_friendly_slug'];
|
$slugMapping[$row['mysql_column_name']] = $row['user_friendly_slug'];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ---------------- FIXED FIELDS (from template_fixed_mapping) ----------------
|
||||||
|
$fixedStmt = $pdo->prepare("
|
||||||
|
SELECT id, fixed_field_key, is_manual, data_type, is_required, default_value, is_visible_import
|
||||||
|
FROM template_fixed_mapping
|
||||||
|
WHERE template_id = ? AND is_visible_import = 1
|
||||||
|
ORDER BY id
|
||||||
|
");
|
||||||
|
$fixedStmt->execute([$template_id]);
|
||||||
|
$fixedFields = $fixedStmt->fetchAll(PDO::FETCH_ASSOC);
|
||||||
|
|
||||||
|
// helper default (DATE can use 'today' if you already use it elsewhere)
|
||||||
|
function fixedDefaultValue(array $f): string
|
||||||
|
{
|
||||||
|
$v = $f['default_value'] ?? '';
|
||||||
|
if ($f['data_type'] === 'DATE' && $v === 'today') return date('Y-m-d');
|
||||||
|
return (string)$v;
|
||||||
|
}
|
||||||
|
|
||||||
?>
|
?>
|
||||||
|
|
||||||
<!doctype html>
|
<!doctype html>
|
||||||
@ -456,6 +475,21 @@ foreach ($stmt->fetchAll(PDO::FETCH_ASSOC) as $row) {
|
|||||||
.select2-container--default .select2-selection--single .select2-selection__arrow {
|
.select2-container--default .select2-selection--single .select2-selection__arrow {
|
||||||
height: 31px;
|
height: 31px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.api-fixed-select+.select2-container .select2-selection__rendered {
|
||||||
|
color: #333;
|
||||||
|
}
|
||||||
|
|
||||||
|
.api-fixed-select option[value=""] {
|
||||||
|
color: #999;
|
||||||
|
font-style: italic;
|
||||||
|
}
|
||||||
|
|
||||||
|
.api-fixed-select.required-input:invalid,
|
||||||
|
.api-fixed-select[required]:not([value]):not([data-select2-id]) {
|
||||||
|
background-color: #f8d7da !important;
|
||||||
|
border-color: #dc3545 !important;
|
||||||
|
}
|
||||||
</style>
|
</style>
|
||||||
<title>Edit Imported Data - <?= htmlspecialchars($titlewebsite, ENT_QUOTES, 'UTF-8'); ?></title>
|
<title>Edit Imported Data - <?= htmlspecialchars($titlewebsite, ENT_QUOTES, 'UTF-8'); ?></title>
|
||||||
</head>
|
</head>
|
||||||
@ -577,6 +611,68 @@ foreach ($stmt->fetchAll(PDO::FETCH_ASSOC) as $row) {
|
|||||||
echo "<div class='grid-cell' style='flex: 0 0 150px;'></div>";
|
echo "<div class='grid-cell' style='flex: 0 0 150px;'></div>";
|
||||||
echo "<div class='grid-cell' style='flex: 0 0 150px;'></div>";
|
echo "<div class='grid-cell' style='flex: 0 0 150px;'></div>";
|
||||||
echo "<div class='grid-cell' style='flex: 0 0 150px;'></div>";
|
echo "<div class='grid-cell' style='flex: 0 0 150px;'></div>";
|
||||||
|
// ---------------- FIXED FIELDS TOP (propagate) ----------------
|
||||||
|
if (!empty($fixedFields)) {
|
||||||
|
foreach ($fixedFields as $fx => $f) {
|
||||||
|
|
||||||
|
$key = $f['fixed_field_key']; // datadb column
|
||||||
|
|
||||||
|
$val = fixedDefaultValue($f);
|
||||||
|
|
||||||
|
$isRequired = ((int)$f['is_required'] === 1);
|
||||||
|
$inputClass = 'manual-input' . ($isRequired ? ' required-input' : '');
|
||||||
|
$topRequiredClass = ($isRequired && ($val === '' || $val === null)) ? 'missing-required' : '';
|
||||||
|
|
||||||
|
echo "<div class='grid-cell {$topRequiredClass}' style='flex: 0 0 180px;'>";
|
||||||
|
|
||||||
|
// Forza DATE anche se per errore nel DB è diversa
|
||||||
|
$isDate = ($f['data_type'] === 'DATE' || $key === 'ConsegnaRichiesta');
|
||||||
|
|
||||||
|
if ($isDate) {
|
||||||
|
|
||||||
|
echo "<input type='text'
|
||||||
|
class='custom-field date-picker {$inputClass} fixed-top'
|
||||||
|
data-column='fixed_{$fx}'
|
||||||
|
data-fixed-key='" . htmlspecialchars($key, ENT_QUOTES) . "'
|
||||||
|
value='" . htmlspecialchars((string)$val, ENT_QUOTES) . "'
|
||||||
|
" . ($isRequired ? "required" : "") . ">";
|
||||||
|
} else {
|
||||||
|
|
||||||
|
$isApiField = in_array($key, [
|
||||||
|
'MoltiplicatorePrezzo',
|
||||||
|
'ClienteResponsabile',
|
||||||
|
'AnagraficaCertestObject',
|
||||||
|
'AnagraficaCertestService'
|
||||||
|
], true);
|
||||||
|
|
||||||
|
if ($isApiField) {
|
||||||
|
$placeholder = ($key === 'ClienteResponsabile') ? 'Seleziona cliente prima...' : 'Seleziona...';
|
||||||
|
|
||||||
|
echo "<select
|
||||||
|
class='custom-field dropdown-select api-fixed-select {$inputClass} fixed-top'
|
||||||
|
data-column='fixed_{$fx}'
|
||||||
|
data-fixed-key='" . htmlspecialchars($key, ENT_QUOTES) . "'
|
||||||
|
" . ($isRequired ? "required" : "") . ">
|
||||||
|
<option value=''>{$placeholder}</option>
|
||||||
|
</select>";
|
||||||
|
} else {
|
||||||
|
|
||||||
|
echo "<input type='number'
|
||||||
|
class='custom-field {$inputClass} fixed-top'
|
||||||
|
data-column='fixed_{$fx}'
|
||||||
|
data-fixed-key='" . htmlspecialchars($key, ENT_QUOTES) . "'
|
||||||
|
value='" . htmlspecialchars((string)$val, ENT_QUOTES) . "'
|
||||||
|
" . ($isRequired ? "required" : "") . ">";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// UNA SOLA freccia
|
||||||
|
echo "<button type='button' class='propagate-btn' data-column='fixed_{$fx}'><i class='fas fa-arrow-down'></i></button>";
|
||||||
|
echo "</div>";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
?>
|
?>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
@ -619,6 +715,22 @@ foreach ($stmt->fetchAll(PDO::FETCH_ASSOC) as $row) {
|
|||||||
$headerIndex++;
|
$headerIndex++;
|
||||||
echo "<div class='grid-header' data-index='$headerIndex' style='flex: 0 0 150px; position: relative;'>" . ($slugMapping['importdate'] ?? 'importdate') . "<div class='resizer'></div></div>";
|
echo "<div class='grid-header' data-index='$headerIndex' style='flex: 0 0 150px; position: relative;'>" . ($slugMapping['importdate'] ?? 'importdate') . "<div class='resizer'></div></div>";
|
||||||
?>
|
?>
|
||||||
|
<?php
|
||||||
|
// ---------------- FIXED FIELDS HEADERS ----------------
|
||||||
|
$headerIndex++; // IMPORTANT: advance index after importdate
|
||||||
|
|
||||||
|
if (!empty($fixedFields)) {
|
||||||
|
foreach ($fixedFields as $f) {
|
||||||
|
$key = $f['fixed_field_key'];
|
||||||
|
$label = $slugMapping[$key] ?? $key; // if slug exists use it, else key
|
||||||
|
echo "<div class='grid-header' data-index='$headerIndex' style='flex: 0 0 180px; position: relative;'>"
|
||||||
|
. htmlspecialchars($label) .
|
||||||
|
"<div class='resizer'></div></div>";
|
||||||
|
$headerIndex++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
?>
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<?php foreach ($importedData as $index => $row): ?>
|
<?php foreach ($importedData as $index => $row): ?>
|
||||||
@ -768,6 +880,56 @@ foreach ($stmt->fetchAll(PDO::FETCH_ASSOC) as $row) {
|
|||||||
<span><?= htmlspecialchars($row['importdate']) ?></span>
|
<span><?= htmlspecialchars($row['importdate']) ?></span>
|
||||||
<input type="hidden" name="rows[<?= $index ?>][importdate]" value="<?= htmlspecialchars($row['importdate']) ?>">
|
<input type="hidden" name="rows[<?= $index ?>][importdate]" value="<?= htmlspecialchars($row['importdate']) ?>">
|
||||||
</div>
|
</div>
|
||||||
|
<?php
|
||||||
|
// ---------------- FIXED FIELDS CELLS ----------------
|
||||||
|
$cellIndex++; // IMPORTANT: move to next data-index after importdate cell
|
||||||
|
|
||||||
|
if (!empty($fixedFields)) {
|
||||||
|
foreach ($fixedFields as $f) {
|
||||||
|
$key = $f['fixed_field_key']; // datadb column name
|
||||||
|
$val = $row[$key] ?? '';
|
||||||
|
if ($val === '' || $val === null) {
|
||||||
|
$val = fixedDefaultValue($f);
|
||||||
|
}
|
||||||
|
|
||||||
|
$requiredClass = ((int)$f['is_required'] === 1 && ($val === '' || $val === null)) ? 'missing-required' : '';
|
||||||
|
$inputClass = 'manual-input';
|
||||||
|
if ((int)$f['is_required'] === 1) $inputClass .= ' required-input';
|
||||||
|
|
||||||
|
echo "<div class='grid-cell editable-cell $requiredClass'
|
||||||
|
data-col='" . htmlspecialchars($key, ENT_QUOTES) . "'
|
||||||
|
data-row='$index'
|
||||||
|
data-index='$cellIndex'
|
||||||
|
style='flex: 0 0 180px;'>";
|
||||||
|
|
||||||
|
if ($f['data_type'] === 'DATE') {
|
||||||
|
echo "<input type='text'
|
||||||
|
name='rows[$index][$key]'
|
||||||
|
value='" . htmlspecialchars((string)$val, ENT_QUOTES) . "'
|
||||||
|
class='cell-input date-picker $inputClass fixed-input'
|
||||||
|
data-fixed-key='" . htmlspecialchars($key, ENT_QUOTES) . "' "
|
||||||
|
. (((int)$f['is_required'] === 1) ? "required" : "")
|
||||||
|
. ">";
|
||||||
|
} else { // INT → diventa select se è uno dei campi API
|
||||||
|
$isApiField = in_array($key, ['MoltiplicatorePrezzo', 'ClienteResponsabile', 'AnagraficaCertestObject', 'AnagraficaCertestService']);
|
||||||
|
$selectClass = $isApiField ? 'api-fixed-select' : '';
|
||||||
|
|
||||||
|
echo "<select
|
||||||
|
name='rows[$index][$key]'
|
||||||
|
class='cell-input $inputClass fixed-input $selectClass'
|
||||||
|
data-fixed-key='" . htmlspecialchars($key, ENT_QUOTES) . "'
|
||||||
|
data-current-value='" . htmlspecialchars((string)$val, ENT_QUOTES) . "'
|
||||||
|
" . (((int)$f['is_required'] === 1) ? "required" : "") . ">
|
||||||
|
<option value=''>Caricamento...</option>
|
||||||
|
</select>";
|
||||||
|
}
|
||||||
|
|
||||||
|
echo "</div>";
|
||||||
|
$cellIndex++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
?>
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
<?php endforeach; ?>
|
<?php endforeach; ?>
|
||||||
</div>
|
</div>
|
||||||
@ -910,6 +1072,20 @@ foreach ($stmt->fetchAll(PDO::FETCH_ASSOC) as $row) {
|
|||||||
if (idclientSelect) {
|
if (idclientSelect) {
|
||||||
formData.append('idclient', idclientSelect.value);
|
formData.append('idclient', idclientSelect.value);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ---- FIXED FIELDS (NEW) ----
|
||||||
|
const fixedInputs = row.querySelectorAll(`input[name^="rows[${rowIndex}]["], select[name^="rows[${rowIndex}]["]`);
|
||||||
|
fixedInputs.forEach(inp => {
|
||||||
|
// prendo solo quelli che NON sono details e NON idclient/status/importdate/filename/importreferencecode ecc.
|
||||||
|
// filtro tramite classe fixed-input (che abbiamo messo)
|
||||||
|
if (!inp.classList.contains('fixed-input')) return;
|
||||||
|
|
||||||
|
const m = inp.name.match(/rows\[\d+\]\[([^\]]+)\]/);
|
||||||
|
if (m && m[1]) {
|
||||||
|
formData.append(m[1], inp.value);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
formData.append('iddatadb', iddatadb);
|
formData.append('iddatadb', iddatadb);
|
||||||
|
|
||||||
fetch('save_edited_row.php', {
|
fetch('save_edited_row.php', {
|
||||||
@ -982,6 +1158,16 @@ foreach ($stmt->fetchAll(PDO::FETCH_ASSOC) as $row) {
|
|||||||
if (idclientSelect) {
|
if (idclientSelect) {
|
||||||
formData.append('idclient', idclientSelect.value);
|
formData.append('idclient', idclientSelect.value);
|
||||||
}
|
}
|
||||||
|
// ---- FIXED FIELDS (NEW) ----
|
||||||
|
const fixedInputs = row.querySelectorAll(`input[name^="rows[${rowIndex}]["], select[name^="rows[${rowIndex}]["]`);
|
||||||
|
fixedInputs.forEach(inp => {
|
||||||
|
if (!inp.classList.contains('fixed-input')) return;
|
||||||
|
const m = inp.name.match(/rows\[\d+\]\[([^\]]+)\]/);
|
||||||
|
if (m && m[1]) {
|
||||||
|
formData.append(m[1], inp.value);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
formData.append('iddatadb', iddatadb);
|
formData.append('iddatadb', iddatadb);
|
||||||
|
|
||||||
try {
|
try {
|
||||||
@ -1093,6 +1279,10 @@ foreach ($stmt->fetchAll(PDO::FETCH_ASSOC) as $row) {
|
|||||||
|
|
||||||
populateClientDropdowns();
|
populateClientDropdowns();
|
||||||
clientLoadingStatus.textContent = "Clienti caricati.";
|
clientLoadingStatus.textContent = "Clienti caricati.";
|
||||||
|
// ✅ force refresh of header dependent fixed fields
|
||||||
|
$('#clientSelect').trigger('change');
|
||||||
|
$('.grid-top .api-fixed-select[data-fixed-key="ClienteResponsabile"]').trigger('fixed:reload');
|
||||||
|
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
clientLoadingStatus.textContent = "Errore nel caricamento.";
|
clientLoadingStatus.textContent = "Errore nel caricamento.";
|
||||||
console.error("Errore nel caricamento dei client:", error);
|
console.error("Errore nel caricamento dei client:", error);
|
||||||
@ -1251,7 +1441,7 @@ foreach ($stmt->fetchAll(PDO::FETCH_ASSOC) as $row) {
|
|||||||
const dropdownData = {};
|
const dropdownData = {};
|
||||||
|
|
||||||
async function populateDropdowns() {
|
async function populateDropdowns() {
|
||||||
const dropdowns = document.querySelectorAll('.dropdown-select:not(.client-select)');
|
const dropdowns = document.querySelectorAll('.dropdown-select:not(.client-select):not(.api-fixed-select)');
|
||||||
if (dropdowns.length === 0) {
|
if (dropdowns.length === 0) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@ -1564,6 +1754,227 @@ foreach ($stmt->fetchAll(PDO::FETCH_ASSOC) as $row) {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
</script>
|
</script>
|
||||||
|
<script>
|
||||||
|
// Dati globali caricati una volta sola
|
||||||
|
let fixedFieldDataCache = {}; // { 'MoltiplicatorePrezzo': [...], 'ClienteResponsabile_4202': [...], ... }
|
||||||
|
|
||||||
|
document.addEventListener("DOMContentLoaded", function() {
|
||||||
|
const apiFields = {
|
||||||
|
'MoltiplicatorePrezzo': {
|
||||||
|
endpoint: 'MoltiplicatorePrezzo',
|
||||||
|
idKey: 'IdMoltiplicatorePrezzo',
|
||||||
|
textKey: 'Descrizione'
|
||||||
|
},
|
||||||
|
'AnagraficaCertestObject': {
|
||||||
|
endpoint: 'AnagraficaCertestObject',
|
||||||
|
idKey: 'IdAnagrafica',
|
||||||
|
textKey: 'NomeAnagrafica'
|
||||||
|
},
|
||||||
|
'AnagraficaCertestService': {
|
||||||
|
endpoint: 'AnagraficaCertestService',
|
||||||
|
idKey: 'IdAnagrafica',
|
||||||
|
textKey: 'NomeAnagrafica'
|
||||||
|
},
|
||||||
|
'ClienteResponsabile': {
|
||||||
|
endpoint: 'ClienteResponsabile',
|
||||||
|
idKey: 'IdClienteResponsabile',
|
||||||
|
textKey: 'Nominativo',
|
||||||
|
dependsOn: 'idclient',
|
||||||
|
getParams: (clientId) => ({
|
||||||
|
id_cliente: clientId
|
||||||
|
})
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// ✅ NEW: returns clientId for both row selects and header (grid-top)
|
||||||
|
function getClientIdForSelect($select) {
|
||||||
|
const $row = $select.closest('.grid-row');
|
||||||
|
|
||||||
|
// If the select is inside a row, read the row client dropdown
|
||||||
|
if ($row.length) {
|
||||||
|
return $row.find('select[name$="[idclient]"]').val() || '';
|
||||||
|
}
|
||||||
|
|
||||||
|
// Otherwise it's in the header (grid-top): read the top client select
|
||||||
|
const topClientId = $('#clientSelect').val();
|
||||||
|
return topClientId || '';
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// Funzione per caricare i dati di un campo (una volta sola)
|
||||||
|
async function loadFixedFieldData(fieldKey, clientId = null) {
|
||||||
|
const config = apiFields[fieldKey];
|
||||||
|
if (!config) return [];
|
||||||
|
|
||||||
|
const cacheKey = fieldKey + (clientId ? '_' + clientId : '');
|
||||||
|
if (fixedFieldDataCache[cacheKey]) {
|
||||||
|
return fixedFieldDataCache[cacheKey];
|
||||||
|
}
|
||||||
|
|
||||||
|
let urlParams = {
|
||||||
|
field: config.endpoint
|
||||||
|
};
|
||||||
|
if (config.dependsOn && clientId) {
|
||||||
|
Object.assign(urlParams, config.getParams(clientId));
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
const response = await fetch("get_fixed_field_data.php?" + new URLSearchParams(urlParams));
|
||||||
|
if (!response.ok) throw new Error('HTTP ' + response.status);
|
||||||
|
const data = await response.json();
|
||||||
|
|
||||||
|
let items = [];
|
||||||
|
if (fieldKey === 'ClienteResponsabile') {
|
||||||
|
items = data.Responsabili || [];
|
||||||
|
} else {
|
||||||
|
items = data.value || data.d?.results || data || [];
|
||||||
|
}
|
||||||
|
let results = items.map(item => ({
|
||||||
|
id: item[config.idKey],
|
||||||
|
text: (item.Codice ? item.Codice + ' - ' : '') + (item[config.textKey] || 'Senza nome')
|
||||||
|
}));
|
||||||
|
fixedFieldDataCache[cacheKey] = results;
|
||||||
|
return results;
|
||||||
|
} catch (err) {
|
||||||
|
console.error('Errore caricamento ' + fieldKey + ':', err);
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Carica tutti i campi NON dipendenti all'apertura pagina
|
||||||
|
async function preloadNonDependentFields() {
|
||||||
|
for (const [fieldKey, config] of Object.entries(apiFields)) {
|
||||||
|
if (!config.dependsOn) {
|
||||||
|
await loadFixedFieldData(fieldKey);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Inizializza tutte le select
|
||||||
|
$('.api-fixed-select').each(function() {
|
||||||
|
const $select = $(this);
|
||||||
|
const fieldKey = $select.data('fixed-key');
|
||||||
|
const currentVal = $select.data('current-value') || '';
|
||||||
|
const config = apiFields[fieldKey];
|
||||||
|
if (!config) return;
|
||||||
|
|
||||||
|
$select.select2({
|
||||||
|
placeholder: config.dependsOn ? "Seleziona cliente prima..." : "Seleziona...",
|
||||||
|
allowClear: true,
|
||||||
|
width: '100%',
|
||||||
|
data: [], // inizialmente vuota, la riempiamo dopo
|
||||||
|
minimumInputLength: 0
|
||||||
|
});
|
||||||
|
|
||||||
|
// Funzione per popolare la select con dati già caricati o caricandoli
|
||||||
|
async function populateSelect() {
|
||||||
|
let results = [];
|
||||||
|
if (config.dependsOn) {
|
||||||
|
const clientId = getClientIdForSelect($select);
|
||||||
|
if (!clientId) {
|
||||||
|
// ✅ reset select completely if no client
|
||||||
|
$select.empty().append(new Option('Seleziona cliente prima...', '', true, true)).trigger('change');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
let rawData = await fetch("get_fixed_field_data.php?" + new URLSearchParams({
|
||||||
|
field: config.endpoint,
|
||||||
|
...config.getParams(clientId)
|
||||||
|
})).then(r => r.json());
|
||||||
|
|
||||||
|
let items = [];
|
||||||
|
if (fieldKey === 'ClienteResponsabile') {
|
||||||
|
items = rawData.Responsabili || [];
|
||||||
|
} else {
|
||||||
|
items = rawData.value || [];
|
||||||
|
}
|
||||||
|
results = items.map(item => ({
|
||||||
|
id: item[config.idKey],
|
||||||
|
text: (item.Codice ? item.Codice + ' - ' : '') + (item[config.textKey] || 'Senza nome')
|
||||||
|
}));
|
||||||
|
} else {
|
||||||
|
results = await loadFixedFieldData(fieldKey);
|
||||||
|
}
|
||||||
|
|
||||||
|
$select.select2('destroy').empty().select2({
|
||||||
|
data: [{
|
||||||
|
id: '',
|
||||||
|
text: 'Seleziona...'
|
||||||
|
}, ...results],
|
||||||
|
placeholder: "Seleziona...",
|
||||||
|
allowClear: true,
|
||||||
|
width: '100%'
|
||||||
|
});
|
||||||
|
|
||||||
|
// Imposta valore iniziale
|
||||||
|
if (currentVal) {
|
||||||
|
const found = results.find(r => String(r.id) === String(currentVal));
|
||||||
|
if (found) {
|
||||||
|
$select.val(currentVal).trigger('change');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// ✅ NEW: allow external reload (used by header client change)
|
||||||
|
$select.on('fixed:reload', function() {
|
||||||
|
populateSelect();
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
// Per campi dipendenti: popola quando cambia cliente (riga o header)
|
||||||
|
if (config.dependsOn) {
|
||||||
|
const $row = $select.closest('.grid-row');
|
||||||
|
|
||||||
|
if ($row.length) {
|
||||||
|
// ✅ ROW: bind on row client select
|
||||||
|
$row.find('select[name$="[idclient]"]').on('change', populateSelect);
|
||||||
|
|
||||||
|
// Popola iniziale se cliente già selezionato nella riga
|
||||||
|
if ($row.find('select[name$="[idclient]"]').val()) {
|
||||||
|
populateSelect();
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// ✅ HEADER (grid-top): bind on top client select
|
||||||
|
$('#clientSelect').on('change', populateSelect);
|
||||||
|
|
||||||
|
// Popola iniziale se cliente già selezionato nell’header
|
||||||
|
if ($('#clientSelect').val()) {
|
||||||
|
populateSelect();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// Campi non dipendenti: popola subito
|
||||||
|
populateSelect();
|
||||||
|
}
|
||||||
|
|
||||||
|
});
|
||||||
|
|
||||||
|
// ✅ NEW: when top client changes, reload only the header ClienteResponsabile select
|
||||||
|
$('#clientSelect').on('change', function() {
|
||||||
|
$('.grid-top .api-fixed-select[data-fixed-key="ClienteResponsabile"]').trigger('fixed:reload');
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
// Precarica i campi indipendenti all'avvio
|
||||||
|
preloadNonDependentFields();
|
||||||
|
|
||||||
|
// Propaga anche per i fixed select
|
||||||
|
$('.propagate-btn[data-column^="fixed_"]').on('click', function() {
|
||||||
|
const column = $(this).data('column');
|
||||||
|
const $topSelect = $(this).siblings('select.api-fixed-select');
|
||||||
|
if (!$topSelect.length) return;
|
||||||
|
const value = $topSelect.val();
|
||||||
|
const fieldKey = $topSelect.data('fixed-key');
|
||||||
|
|
||||||
|
// Propaga solo se il valore è valido
|
||||||
|
if (value) {
|
||||||
|
$(`.api-fixed-select[data-fixed-key="${fieldKey}"]`).each(function() {
|
||||||
|
$(this).val(value).trigger('change');
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
</script>
|
||||||
<!-- Modale di conferma per l'esportazione -->
|
<!-- Modale di conferma per l'esportazione -->
|
||||||
<div class="modal fade" id="exportConfirmModal" tabindex="-1" aria-labelledby="exportConfirmModalLabel" aria-hidden="true">
|
<div class="modal fade" id="exportConfirmModal" tabindex="-1" aria-labelledby="exportConfirmModalLabel" aria-hidden="true">
|
||||||
<div class="modal-dialog">
|
<div class="modal-dialog">
|
||||||
|
|||||||
@ -15,6 +15,44 @@ try {
|
|||||||
$db = DBHandlerSelect::getInstance();
|
$db = DBHandlerSelect::getInstance();
|
||||||
$pdo = $db->getConnection();
|
$pdo = $db->getConnection();
|
||||||
|
|
||||||
|
// ---------------- FIXED FIELDS (template_fixed_mapping) ----------------
|
||||||
|
|
||||||
|
// 1) Recupera templateid dalla riga datadb (serve per sapere quali fixed_field_key sono permessi)
|
||||||
|
$stmtTpl = $pdo->prepare("SELECT templateid FROM datadb WHERE iddatadb = ?");
|
||||||
|
$stmtTpl->execute([$iddatadb]);
|
||||||
|
$tplRow = $stmtTpl->fetch(PDO::FETCH_ASSOC);
|
||||||
|
|
||||||
|
$templateId = isset($tplRow['templateid']) ? (int)$tplRow['templateid'] : 0;
|
||||||
|
if ($templateId <= 0) {
|
||||||
|
throw new Exception("Template non trovato per iddatadb=$iddatadb");
|
||||||
|
}
|
||||||
|
|
||||||
|
// 2) Recupera elenco fixed fields visibili per quel template
|
||||||
|
$fxStmt = $pdo->prepare("
|
||||||
|
SELECT fixed_field_key, data_type, is_required, default_value
|
||||||
|
FROM template_fixed_mapping
|
||||||
|
WHERE template_id = ? AND is_visible_import = 1
|
||||||
|
");
|
||||||
|
$fxStmt->execute([$templateId]);
|
||||||
|
$fixedList = $fxStmt->fetchAll(PDO::FETCH_ASSOC);
|
||||||
|
|
||||||
|
// 3) Crea whitelist: key => metadata
|
||||||
|
$fixedWhitelist = [];
|
||||||
|
foreach ($fixedList as $fx) {
|
||||||
|
$k = (string)$fx['fixed_field_key'];
|
||||||
|
|
||||||
|
// sicurezza: nome colonna ammesso solo se "safe" (no spazi, no caratteri strani)
|
||||||
|
if (!preg_match('/^[a-zA-Z0-9_]+$/', $k)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
$fixedWhitelist[$k] = [
|
||||||
|
'data_type' => (string)$fx['data_type'], // INT | DATE
|
||||||
|
'is_required' => (int)$fx['is_required'],
|
||||||
|
'default_value' => $fx['default_value'] ?? null
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
$data = $_POST;
|
$data = $_POST;
|
||||||
$details = [];
|
$details = [];
|
||||||
|
|
||||||
@ -63,22 +101,57 @@ try {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// 5. Aggiorna idclient in datadb
|
// 5. Aggiorna datadb: idclient + FIXED FIELDS (whitelisted)
|
||||||
|
$setParts = [];
|
||||||
|
$params = [];
|
||||||
|
|
||||||
|
// 5a) idclient (se presente)
|
||||||
if (isset($idclient)) {
|
if (isset($idclient)) {
|
||||||
$updateStmt = $pdo->prepare("
|
$setParts[] = "idclient = ?";
|
||||||
UPDATE datadb
|
$params[] = $idclient;
|
||||||
SET idclient = :idclient
|
|
||||||
WHERE iddatadb = :iddatadb
|
|
||||||
");
|
|
||||||
$updateStmt->execute([
|
|
||||||
':idclient' => $idclient,
|
|
||||||
':iddatadb' => $iddatadb
|
|
||||||
]);
|
|
||||||
$response['message'] = !empty($changed) ? "Updated details and idclient successfully" : "Updated idclient successfully";
|
|
||||||
} else {
|
|
||||||
$response['message'] = !empty($changed) ? "Updated details successfully" : "No changes found";
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 5b) fixed fields dal POST (solo quelli presenti nella whitelist del template)
|
||||||
|
foreach ($fixedWhitelist as $col => $meta) {
|
||||||
|
if (!array_key_exists($col, $_POST)) {
|
||||||
|
continue; // non inviato dal form
|
||||||
|
}
|
||||||
|
|
||||||
|
$val = $_POST[$col];
|
||||||
|
|
||||||
|
// Normalizzazione per tipo
|
||||||
|
if ($meta['data_type'] === 'DATE') {
|
||||||
|
$val = trim((string)$val);
|
||||||
|
$val = ($val === '') ? null : $val; // atteso formato Y-m-d
|
||||||
|
} else { // INT
|
||||||
|
$val = trim((string)$val);
|
||||||
|
$val = ($val === '') ? null : (int)$val;
|
||||||
|
}
|
||||||
|
|
||||||
|
$setParts[] = "`$col` = ?";
|
||||||
|
$params[] = $val;
|
||||||
|
}
|
||||||
|
|
||||||
|
// esegui update solo se c'è qualcosa da aggiornare
|
||||||
|
if (!empty($setParts)) {
|
||||||
|
$params[] = $iddatadb;
|
||||||
|
$sqlUpd = "UPDATE datadb SET " . implode(", ", $setParts) . " WHERE iddatadb = ?";
|
||||||
|
$updStmt = $pdo->prepare($sqlUpd);
|
||||||
|
$updStmt->execute($params);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Messaggio risposta (mantengo la tua logica ma includo fixed)
|
||||||
|
if (!empty($setParts) && !empty($changed)) {
|
||||||
|
$response['message'] = "Updated details and datadb fields successfully";
|
||||||
|
} elseif (!empty($setParts)) {
|
||||||
|
$response['message'] = "Updated datadb fields successfully";
|
||||||
|
} elseif (!empty($changed)) {
|
||||||
|
$response['message'] = "Updated details successfully";
|
||||||
|
} else {
|
||||||
|
$response['message'] = "No changes found";
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
$response['success'] = true;
|
$response['success'] = true;
|
||||||
$response['changed'] = $changed; // Debug / optional
|
$response['changed'] = $changed; // Debug / optional
|
||||||
|
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user