Compare commits

...

7 Commits

Author SHA1 Message Date
solocla 9969cc9edc partstable fixed matrici dropdown 2025-10-28 09:31:13 +01:00
solocla 8d6fe92481 fixed multiple parts mix and single line 2025-10-28 08:58:39 +01:00
solocla dbc66723a6 fixed color save 2025-10-27 15:53:12 +01:00
solocla 218fc14462 fixed country client and parts column matrice 2025-10-27 14:38:20 +01:00
solocla 29e4b41874 update export to lims 2025-10-11 20:19:43 +02:00
solocla eef9ae8d36 added note to export 2025-10-10 11:31:49 +02:00
solocla 68c867a3f4 change clienti to datadb and fixed column pages 2025-10-09 15:30:44 +02:00
12 changed files with 915 additions and 285 deletions
@@ -29,7 +29,7 @@ class VisualLimsApiClient
return self::$instance;
}
private function authenticate()
private function authenticate($retryCount = 0, $maxRetries = 3)
{
$ch = curl_init("{$this->baseUrl}/api/authentication/authenticate");
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
@@ -45,16 +45,22 @@ class VisualLimsApiClient
curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);
curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, false);
curl_setopt($ch, CURLOPT_VERBOSE, true);
$log = fopen(__DIR__ . '/curl_auth_debug.log', 'w') ?: fopen('php://stderr', 'w');
$log = fopen(__DIR__ . '/curl_auth_debug.log', 'a') ?: fopen('php://stderr', 'w');
curl_setopt($ch, CURLOPT_STDERR, $log);
$response = curl_exec($ch);
$http_code = curl_getinfo($ch, CURLINFO_HTTP_CODE);
$curl_error = curl_error($ch);
$log_message = date('Y-m-d H:i:s') . " - Auth attempt {$retryCount}: HTTP {$http_code}, Error: {$curl_error}, Response: " . substr($response, 0, 1000) . "\n";
fwrite($log, $log_message);
fclose($log);
curl_close($ch);
if ($response === false || $http_code != 200) {
if ($http_code === 400 && strpos($response, 'Cannot persist the object') !== false && $retryCount < $maxRetries) {
usleep(500000); // Ritardo di 500ms
return $this->authenticate($retryCount + 1, $maxRetries); // Riprova
}
throw new Exception("Autenticazione fallita: HTTP {$http_code}, Errore cURL: {$curl_error}, Risposta: " . substr($response, 0, 1000));
}
@@ -191,5 +197,4 @@ class VisualLimsApiClient
{
return $this->baseUrl;
}
}
File diff suppressed because one or more lines are too long
+2 -2
View File
@@ -33,9 +33,9 @@ try {
throw new Exception("Missing iddatadb");
}
// 🔹 STEP 1+2: Fetch Cliente ID + Schema ID
// 🔹 STEP 1+2: Fetch Cliente ID from datadb and Schema ID from excel_templates
$stmt = $pdo->prepare("
SELECT et.idclient AS clienteId, et.idschema AS schemaId
SELECT d.idclient AS clienteId, et.idschema AS schemaId
FROM datadb as d
INNER JOIN excel_templates as et ON d.templateid = et.id
WHERE d.iddatadb = :iddatadb
+41 -5
View File
@@ -23,16 +23,52 @@ try {
// Componi endpoint finale
$endpoint = "Cliente?$queryString";
// Richiama API
// Funzione per eseguire la chiamata con retry
function makeApiRequest($api, $endpoint, $maxRetries = 3)
{
for ($retry = 0; $retry < $maxRetries; $retry++) {
try {
// Tenta la chiamata API
$data = $api->get($endpoint);
// Salva risposta per debug
file_put_contents(__DIR__ . '/clienti_response.json', json_encode($data, JSON_PRETTY_PRINT));
return $data;
} catch (Exception $e) {
$errorMessage = $e->getMessage();
// Controlla se l'errore è legato all'autenticazione (HTTP 400 con messaggio specifico)
if (strpos($errorMessage, 'HTTP 400') !== false && strpos($errorMessage, 'Cannot persist the object') !== false) {
// Forza il refresh del token
try {
// Assumi che VisualLimsApiClient abbia un metodo per il refresh del token
$api->refreshToken(); // Da implementare in VisualLimsApiClient se non esiste
error_log("Tentativo $retry: Refresh token eseguito per endpoint $endpoint");
} catch (Exception $refreshEx) {
error_log("Errore durante il refresh del token: " . $refreshEx->getMessage());
throw new Exception("Impossibile eseguire il refresh del token: " . $refreshEx->getMessage());
}
// Ritarda leggermente prima del retry
usleep(500000); // 500ms
continue;
}
// Altri errori non gestiti dal retry
throw $e;
}
}
throw new Exception("Massimo numero di tentativi raggiunto per $endpoint");
}
// Esegui la chiamata con retry
$data = makeApiRequest($api, $endpoint);
echo json_encode($data);
} catch (Exception $e) {
http_response_code(500);
echo json_encode([
'error' => $e->getMessage()
]);
$errorResponse = [
'error' => $e->getMessage(),
'file' => $e->getFile(),
'line' => $e->getLine(),
'trace' => $e->getTraceAsString()
];
error_log("Errore in get_clienti.php: " . json_encode($errorResponse));
echo json_encode($errorResponse);
}
+350 -56
View File
@@ -40,6 +40,12 @@ foreach ($allMappings as $mapping) {
}
}
// Recupera l'idclient di default dal template (se presente)
$template_stmt = $pdo->prepare("SELECT idclient FROM excel_templates WHERE id = ?");
$template_stmt->execute([$template_id]);
$template = $template_stmt->fetch(PDO::FETCH_ASSOC);
$default_idclient = $template['idclient'] ?? null;
$insertedIds = $_POST['inserted_ids'] ?? $_SESSION['inserted_ids'];
// Recupera i dati appena inseriti con i nomi degli utenti
@@ -410,6 +416,46 @@ foreach ($stmt->fetchAll(PDO::FETCH_ASSOC) as $row) {
.flatpickr-input {
cursor: pointer;
}
.client-input {
width: 100%;
box-sizing: border-box;
border: 1px solid #ced4da;
border-radius: 4px;
padding: 5px;
font-size: 14px;
color: #333;
}
.client-input:focus {
outline: none;
border-color: #80bdff;
box-shadow: 0 0 5px rgba(0, 123, 255, 0.5);
}
.select2-container {
width: 100% !important;
}
.select2-dropdown-smaller {
font-size: 14px;
}
.select2-container--default .select2-selection--single {
height: 31px;
border: 1px solid #ced4da;
border-radius: 4px;
padding: 5px;
}
.select2-container--default .select2-selection--single .select2-selection__rendered {
line-height: 20px;
}
.select2-container--default .select2-selection--single .select2-selection__arrow {
height: 31px;
}
</style>
<title>Edit Imported Data - <?= htmlspecialchars($titlewebsite, ENT_QUOTES, 'UTF-8'); ?></title>
</head>
@@ -471,6 +517,13 @@ foreach ($stmt->fetchAll(PDO::FETCH_ASSOC) as $row) {
?>
</div>
<?php endif; ?>
<div class="grid-cell" style="flex: 0 0 300px;">
<select class="custom-field dropdown-select client-select searchable-client" data-column="idclient" id="clientSelect" name="idclient">
<option value="">Select a client...</option>
</select>
<button type="button" class="propagate-btn" data-column="idclient"><i class="fas fa-arrow-down"></i></button>
<span id="clientLoadingStatus" class="text-muted" style="margin-left: 10px; display: none;">Recupero clienti in corso...</span>
</div>
<div class="grid-cell" style="flex: 0 0 150px;"></div>
<?php
$autoIndex = 0;
@@ -535,10 +588,12 @@ foreach ($stmt->fetchAll(PDO::FETCH_ASSOC) as $row) {
<div class="resizer"></div>
</div>
<?php endif; ?>
<div class="grid-header" data-index="<?= $mainFieldMapping ? 2 : 1 ?>" style="flex: 0 0 150px; position: relative;">Status<div class="resizer"></div>
<div class="grid-header" data-index="<?= $mainFieldMapping ? 2 : 1 ?>" style="flex: 0 0 300px; position: relative;">Client<div class="resizer"></div>
</div>
<div class="grid-header" data-index="<?= $mainFieldMapping ? 3 : 2 ?>" style="flex: 0 0 150px; position: relative;">Status<div class="resizer"></div>
</div>
<?php
$headerIndex = $mainFieldMapping ? 3 : 2;
$headerIndex = $mainFieldMapping ? 4 : 3;
foreach ($allMappings as $mapping) {
if (!$mapping['is_manual'] && $mapping['main_field'] != 1 && $mapping['is_visible_import'] == 1) {
echo "<div class='grid-header' data-index='$headerIndex' style='flex: 0 0 150px; position: relative;'>" . htmlspecialchars($mapping['field_label']) . "<div class='resizer'></div></div>";
@@ -604,14 +659,19 @@ foreach ($stmt->fetchAll(PDO::FETCH_ASSOC) as $row) {
<?php endif; ?>
</div>
<?php endif; ?>
<div class="grid-cell editable-cell" data-col="status" data-row="<?= $index ?>" data-index="<?= $mainFieldMapping ? 2 : 1 ?>" style="flex: 0 0 150px;">
<div class="grid-cell editable-cell" data-col="idclient" data-row="<?= $index ?>" data-index="<?= $mainFieldMapping ? 2 : 1 ?>" style="flex: 0 0 300px;">
<select name="rows[<?= $index ?>][idclient]" class="cell-input dropdown-select client-select searchable-client" data-current-value="<?= htmlspecialchars($row['idclient'] ?? $default_idclient) ?>">
<option value="">Select a client...</option>
</select>
</div>
<div class="grid-cell editable-cell" data-col="status" data-row="<?= $index ?>" data-index="<?= $mainFieldMapping ? 3 : 2 ?>" style="flex: 0 0 150px;">
<span class="status-badge status-<?= htmlspecialchars($row['status'] ?? 'i') ?>">
<?= htmlspecialchars($row['status'] === 'i' ? 'Imported' : ($row['status'] === 'P' ? 'In Progress' : 'To LIMS')) ?>
</span>
<input type="hidden" name="rows[<?= $index ?>][status]" value="<?= htmlspecialchars($row['status'] ?? 'i') ?>">
</div>
<?php
$cellIndex = $mainFieldMapping ? 3 : 2;
$cellIndex = $mainFieldMapping ? 4 : 3;
$rowDetails = array_filter($manualDetails, fn($d) => $d['datadb_id'] == $row['iddatadb']);
$autoIndex = 0;
foreach ($allMappings as $mapping) {
@@ -840,11 +900,16 @@ foreach ($stmt->fetchAll(PDO::FETCH_ASSOC) as $row) {
if (matches) {
const mappingId = matches[1];
formData.append(`details${mappingId}field_value`, input.value);
if (input.tagName === 'SELECT' && input.classList.contains('dropdown-select')) {
if (input.tagName === 'SELECT') {
input.setAttribute('data-selected-value', input.value);
}
}
});
const idclientSelect = row.querySelector(`select[name="rows[${rowIndex}][idclient]"]`);
if (idclientSelect) {
formData.append('idclient', idclientSelect.value);
}
formData.append('iddatadb', iddatadb);
fetch('save_edited_row.php', {
@@ -912,6 +977,11 @@ foreach ($stmt->fetchAll(PDO::FETCH_ASSOC) as $row) {
}
}
});
const idclientSelect = row.querySelector(`select[name="rows[${rowIndex}][idclient]"]`);
if (idclientSelect) {
formData.append('idclient', idclientSelect.value);
}
formData.append('iddatadb', iddatadb);
try {
@@ -984,6 +1054,90 @@ foreach ($stmt->fetchAll(PDO::FETCH_ASSOC) as $row) {
<script>
document.addEventListener("DOMContentLoaded", function() {
const inputs = document.querySelectorAll('.cell-input');
let clientData = []; // Dichiarazione di clientData qui
// Funzione per caricare i client
async function loadClients(retryCount = 0, maxRetries = 3) {
const clientLoadingStatus = document.getElementById("clientLoadingStatus");
try {
clientLoadingStatus.style.display = 'inline';
clientLoadingStatus.textContent = 'Recupero clienti in corso...';
const response = await fetch("get_clienti.php", {
method: "GET",
headers: {
"Content-Type": "application/json"
}
});
const data = await response.json();
if (!response.ok) {
if (response.status === 500 && data.error.includes('Cannot persist the object') && retryCount < maxRetries) {
console.log(`Tentativo ${retryCount + 1}/${maxRetries}: Riprovo a caricare i clienti...`);
await new Promise(resolve => setTimeout(resolve, 1000));
return loadClients(retryCount + 1, maxRetries);
}
throw new Error(data.error || `Errore HTTP: ${response.status}`);
}
clientData = data.value || [];
const select = document.getElementById("clientSelect");
select.innerHTML = '<option value="">Select a client...</option>';
clientData.forEach(client => {
const nome = client.Nominativo || "Nome non disponibile";
const id = client.IdCliente || "ID non disponibile";
const option = new Option(`${nome.trim()} - ${client.CodiceNazioneFatturazione} (ID: ${id})`, id);
if (parseInt(id) === parseInt(<?php echo json_encode($default_idclient ?? 0); ?>)) {
option.selected = true;
}
select.add(option);
});
populateClientDropdowns();
clientLoadingStatus.textContent = "Clienti caricati.";
} catch (error) {
clientLoadingStatus.textContent = "Errore nel caricamento.";
console.error("Errore nel caricamento dei client:", error);
Swal.fire({
title: "Errore!",
text: "Impossibile caricare i clienti: " + error.message,
icon: "error",
confirmButtonText: "OK"
});
} finally {
setTimeout(() => clientLoadingStatus.style.display = 'none', 2000);
}
}
// Funzione per popolare i dropdown dei client
function populateClientDropdowns() {
const clientDropdowns = document.querySelectorAll('select[name^="rows"][name$="[idclient]"]');
clientDropdowns.forEach(dropdown => {
const currentValue = dropdown.getAttribute('data-current-value') || '';
dropdown.innerHTML = '<option value="">Select a client...</option>';
clientData.forEach(client => {
const nome = client.Nominativo || "Nome non disponibile";
const id = client.IdCliente || "ID non disponibile";
const option = new Option(`${nome.trim()} - ${client.CodiceNazioneFatturazione} (ID: ${id})`, id);
if (String(id) === String(currentValue)) {
option.selected = true;
}
dropdown.add(option);
});
// Ripristina il valore corrente
if (currentValue) {
dropdown.value = currentValue;
const event = new Event('change', {
bubbles: true
});
dropdown.dispatchEvent(event);
}
});
}
// Carica i client all'avvio
loadClients();
// Gestione degli input
inputs.forEach(input => {
input.addEventListener('focus', function() {
this.closest('.grid-cell').classList.add('expanded');
@@ -993,42 +1147,60 @@ foreach ($stmt->fetchAll(PDO::FETCH_ASSOC) as $row) {
});
});
// Gestione della propagazione
const propagateButtons = document.querySelectorAll('.propagate-btn');
propagateButtons.forEach(button => {
button.addEventListener('click', function() {
button.addEventListener('click', async function() {
const column = this.getAttribute('data-column');
const input = this.previousElementSibling;
const value = input.value;
const value = input.tagName === 'SELECT' ? input.value : input.value;
console.log('Propagate clicked for column:', column, 'with value:', value); // Debug
// Assicurati che i dropdown dei client siano popolati
if (column === 'idclient' && clientData.length === 0) {
await loadClients(); // Carica i client se non ancora caricati
}
const gridTopCells = document.querySelector('.grid-top').querySelectorAll('.grid-cell');
const targetTopIndex = Array.from(gridTopCells).findIndex(cell =>
cell.querySelector('.propagate-btn[data-column="' + column + '"]')
);
console.log('Target index found:', targetTopIndex); // Debug
if (targetTopIndex !== -1) {
const rows = document.querySelectorAll('.grid-row');
rows.forEach(row => {
const cells = row.querySelectorAll('.grid-cell');
if (cells.length > targetTopIndex) {
const targetInput = cells[targetTopIndex].querySelector('input, select');
const targetInput = cells[targetTopIndex].querySelector('select, input');
if (targetInput) {
targetInput.value = value;
console.log('Setting value on target input:', targetInput, 'with value:', value); // Debug
if (targetInput.tagName === 'SELECT') {
targetInput.setAttribute('data-selected-value', value);
const event = new Event('change');
targetInput.value = value;
const event = new Event('change', {
bubbles: true
});
targetInput.dispatchEvent(event);
} else if (targetInput.classList.contains('date-picker')) {
// Update Flatpickr instance
const flatpickrInstance = targetInput._flatpickr;
if (flatpickrInstance && value) {
flatpickrInstance.setDate(value, true);
}
const event = new Event('change');
const event = new Event('change', {
bubbles: true
});
targetInput.dispatchEvent(event);
} else {
const event = new Event('change');
targetInput.value = value;
const event = new Event('change', {
bubbles: true
});
targetInput.dispatchEvent(event);
}
} else {
console.warn('No target input found in cell'); // Debug
}
}
});
@@ -1036,6 +1208,7 @@ foreach ($stmt->fetchAll(PDO::FETCH_ASSOC) as $row) {
});
});
// Gestione del ridimensionamento delle colonne
const resizers = document.querySelectorAll('.resizer');
let currentResizer = null;
let startX = 0;
@@ -1078,7 +1251,7 @@ foreach ($stmt->fetchAll(PDO::FETCH_ASSOC) as $row) {
const dropdownData = {};
async function populateDropdowns() {
const dropdowns = document.querySelectorAll('.dropdown-select');
const dropdowns = document.querySelectorAll('.dropdown-select:not(.client-select)');
if (dropdowns.length === 0) {
return;
}
@@ -1102,13 +1275,15 @@ foreach ($stmt->fetchAll(PDO::FETCH_ASSOC) as $row) {
dropdownData[fieldId] = data[fieldId] || [];
}
}
} catch (error) {}
} catch (error) {
console.error('Errore nel caricamento dei valori per dropdown:', error);
}
}
dropdowns.forEach(dropdown => {
const fieldId = dropdown.getAttribute('data-field-id');
const selectedValue = dropdown.getAttribute('data-selected-value') || '';
const currentValue = dropdown.value;
const currentValue = dropdown.value || '';
if (!fieldId || !dropdownData[fieldId]) {
dropdown.innerHTML = '<option value="">Errore nel caricamento</option>';
@@ -1128,63 +1303,182 @@ foreach ($stmt->fetchAll(PDO::FETCH_ASSOC) as $row) {
dropdown.appendChild(option);
});
if ((currentValue || selectedValue) && dropdown.value !== (currentValue || selectedValue)) {
dropdown.value = '';
// Ripristina il valore corrente
if (currentValue || selectedValue) {
dropdown.value = currentValue || selectedValue;
const event = new Event('change', {
bubbles: true
});
dropdown.dispatchEvent(event);
}
});
}
populateDropdowns();
});
</script>
<script>
$(document).on('click', '.add-part-btn', function() {
const rowIndex = $(this).data('row');
const row = $(this).closest('.grid-row');
const iddatadb = row.data('id');
const input = row.find(`input[name="rows[${rowIndex}][tested_component]"]`);
const description = input.val().trim();
if (!description) {
alert('Inserisci un valore per Tested Component');
return;
document.addEventListener("DOMContentLoaded", function() {
let clientData = [];
async function loadClients(retryCount = 0, maxRetries = 3) {
const clientLoadingStatus = document.getElementById("clientLoadingStatus");
try {
clientLoadingStatus.style.display = 'inline';
clientLoadingStatus.textContent = 'Recupero clienti in corso...';
const response = await fetch("get_clienti.php", {
method: "GET",
headers: {
"Content-Type": "application/json"
}
});
const data = await response.json();
if (!response.ok) {
if (response.status === 500 && data.error.includes('Cannot persist the object') && retryCount < maxRetries) {
console.log(`Tentativo ${retryCount + 1}/${maxRetries}: Riprovo a caricare i clienti...`);
await new Promise(resolve => setTimeout(resolve, 1000));
return loadClients(retryCount + 1, maxRetries);
}
throw new Error(data.error || `Errore HTTP: ${response.status}`);
}
const data = {
iddatadb: iddatadb,
parts: [{
part_number: '1', // Imposta part_number a '1'
part_description: description,
mix: 'N'
}]
};
clientData = data.value || [];
const select = document.getElementById("clientSelect");
select.innerHTML = '<option value="">Select a client...</option>';
clientData.forEach(client => {
const nome = client.Nominativo || "Nome non disponibile";
const id = client.IdCliente || "ID non disponibile";
const codice = (client.CodiceNazioneFatturazione || '').trim();
const option = new Option(`${nome.trim()} - ${codice} (ID: ${id})`, id);
$.ajax({
url: 'save_parts.php',
method: 'POST',
contentType: 'application/json',
data: JSON.stringify(data),
success: function(response) {
if (response.success) {
alert('Parte aggiunta con successo!');
input.val(''); // Pulisci l'input dopo l'aggiunta
// Opzionale: aggiorna la tabella delle parti se il modal è aperto
const partsModal = $('#partsModal');
if (partsModal.hasClass('show')) {
loadParts(iddatadb);
if (parseInt(id) === parseInt(<?php echo json_encode($default_idclient ?? 0); ?>)) {
option.selected = true;
}
} else {
alert('Errore: ' + response.message);
select.add(option);
});
populateClientDropdowns();
clientLoadingStatus.textContent = "Clienti caricati.";
} catch (error) {
clientLoadingStatus.textContent = "Errore nel caricamento.";
Swal.fire({
title: "Errore!",
text: "Impossibile caricare i clienti: " + error.message,
icon: "error",
confirmButtonText: "OK"
});
} finally {
setTimeout(() => clientLoadingStatus.style.display = 'none', 2000);
}
},
error: function() {
alert('Errore durante la richiesta AJAX');
}
function populateClientDropdowns() {
const clientDropdowns = document.querySelectorAll('select[name^="rows"][name$="[idclient]"]');
clientDropdowns.forEach(dropdown => {
const currentValue = dropdown.getAttribute('data-current-value') || '';
dropdown.innerHTML = '<option value="">Select a client...</option>';
clientData.forEach(client => {
const nome = client.Nominativo || "Nome non disponibile";
const id = client.IdCliente || "ID non disponibile";
const codice = (client.CodiceNazioneFatturazione || '').trim();
const option = new Option(`${nome.trim()} - ${codice} (ID: ${id})`, id);
if (String(id) === String(currentValue)) {
option.selected = true;
}
dropdown.add(option);
});
// Ripristina il valore corrente
if (currentValue) {
dropdown.value = currentValue;
const event = new Event('change', {
bubbles: true
});
dropdown.dispatchEvent(event);
}
});
}
loadClients();
document.getElementById('clientSelect').addEventListener('change', function() {
const gridCell = this.closest('.grid-cell');
const event = new Event('change', {
bubbles: true
});
gridCell.dispatchEvent(event);
});
document.addEventListener('change', function(e) {
if (e.target.matches('select[name^="rows"][name$="[idclient]"]')) {
const gridCell = e.target.closest('.grid-cell');
const event = new Event('change', {
bubbles: true
});
gridCell.dispatchEvent(event);
}
});
});
</script>
<script>
document.addEventListener("DOMContentLoaded", function() {
// Gestione del cambio valore per il dropdown principale dei client
document.getElementById('clientSelect').addEventListener('change', function() {
const gridCell = this.closest('.grid-cell');
const event = new Event('change', {
bubbles: true
});
gridCell.dispatchEvent(event);
});
// Gestione del cambio valore per i dropdown dei client nelle righe
document.addEventListener('change', function(e) {
if (e.target.matches('select[name^="rows"][name$="[idclient]"]')) {
const gridCell = e.target.closest('.grid-cell');
const event = new Event('change', {
bubbles: true
});
gridCell.dispatchEvent(event);
}
});
});
</script>
<script>
document.addEventListener("DOMContentLoaded", function() {
// Initialize Select2 for searchable client dropdowns
$('.searchable-client').select2({
placeholder: "Select a client...",
allowClear: true,
width: '100%',
dropdownCssClass: 'select2-dropdown-smaller',
minimumInputLength: 1
});
// Ensure Select2 dropdowns trigger change events for unsaved changes tracking
$('.searchable-client').on('select2:select select2:clear', function(e) {
const gridCell = this.closest('.grid-cell');
const event = new Event('change', {
bubbles: true
});
gridCell.dispatchEvent(event);
});
// Update propagate functionality for client dropdown
$('.propagate-btn[data-column="idclient"]').on('click', async function() {
const value = $('#clientSelect').val();
const rows = document.querySelectorAll('.grid-row');
rows.forEach(row => {
const clientSelect = row.querySelector('select[name$="[idclient]"]');
if (clientSelect) {
$(clientSelect).val(value).trigger('change.select2');
const event = new Event('change', {
bubbles: true
});
clientSelect.closest('.grid-cell').dispatchEvent(event);
}
});
});
$(document).on('click', '.parts-btn', function() {
const iddatadb = $(this).data('iddatadb') || null;
const idquotations = $(this).data('idquotations') || null;
+9 -2
View File
@@ -75,6 +75,12 @@ foreach ($selected_rows as $rowIndex) {
continue;
}
// Recupera l'idclient di default dal template
$template_stmt = $pdo->prepare("SELECT idclient FROM excel_templates WHERE id = ?");
$template_stmt->execute([$template_id]);
$template = $template_stmt->fetch(PDO::FETCH_ASSOC);
$default_idclient = $template['idclient'] ?? null;
$values = [
$template_id,
$importReferenceCode,
@@ -83,9 +89,10 @@ foreach ($selected_rows as $rowIndex) {
$user_id,
null,
date('Y-m-d'),
$excelrow // Aggiunto excelrow per la colonna excelrow
$excelrow,
$default_idclient // Aggiungi idclient
];
$sql = "INSERT INTO datadb (templateid, importreferencecode, filename_import, status, user_id, limscode, importdate, excelrow) VALUES (?, ?, ?, ?, ?, ?, ?, ?)";
$sql = "INSERT INTO datadb (templateid, importreferencecode, filename_import, status, user_id, limscode, importdate, excelrow, idclient) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)";
$stmt = $pdo->prepare($sql);
$stmt->execute($values);
@@ -13,13 +13,13 @@ if (session_status() == PHP_SESSION_NONE) {
}
// Imposta variabili di sessione di default per evitare errori
$_SESSION['iduserlogin'] = null; // Nessun utente loggato
$_SESSION['iduserlogin'] = '1'; // Nessun utente loggato
$_SESSION['nameuser'] = 'Ospite';
$_SESSION['surnameuser'] = '';
$_SESSION['emailuser'] = '';
$_SESSION['photouser'] = '';
$photouser = $_SESSION['photouser'];
$photousername = '';
$iduserlogin = $_SESSION['iduserlogin'];
// Include file di lingua, se necessario
require_once(__DIR__ . '/../../languages/en/general.php');
+222 -71
View File
@@ -143,172 +143,323 @@
</div>
<style>
/* --- Base --- */
#partsModal {
z-index: 1060 !important;
z-index: 1060 !important
}
#partsModal .modal-backdrop {
z-index: 1055 !important;
z-index: 1055 !important
}
#partsModal .modal-content {
width: 100% !important;
max-width: 100% !important;
max-width: 100% !important
}
/* Tabelle */
#partsTable tr {
display: table-row !important;
display: table-row !important
}
#partsTable tr:hover {
background-color: #f5f5f5;
display: table-row !important;
background: #f5f5f5
}
#partsTable td,
#partsTable th {
padding: 0.2rem;
vertical-align: middle;
padding: .2rem;
vertical-align: middle
}
#partsTable input,
#partsTable select {
height: 24px;
padding: 0.1rem 0.3rem;
padding: .1rem .3rem
}
#partsTable button {
padding: 0.1rem 0.3rem;
margin: 0 2px;
padding: .1rem .3rem;
margin: 0 2px
}
#partsTable i {
font-size: 0.6rem !important;
font-size: .6rem !important
}
#global-matrice,
.part-matrice,
/* --- Larghezze fisse header --- */
/* MacroMatrici = 250px */
#macro-matrice-filter {
width: 100% !important;
min-width: 100% !important;
width: 250px !important;
min-width: 250px !important;
max-width: 250px !important;
flex: 0 0 250px !important;
box-sizing: border-box;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
.select2-container--default #global-matrice {
width: 350px !important;
#macro-matrice-filter.select2-hidden-accessible+.select2 {
width: 250px !important;
min-width: 250px !important;
max-width: 250px !important;
flex: 0 0 250px !important;
}
#macro-matrice-filter.select2-hidden-accessible+.select2 .select2-selection__rendered {
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
/* Matrice globale = 450px */
#global-matrice {
width: 450px !important;
min-width: 450px !important;
max-width: 450px !important;
flex: 0 0 450px !important;
box-sizing: border-box;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
#global-matrice.select2-hidden-accessible+.select2 {
width: 450px !important;
min-width: 450px !important;
max-width: 450px !important;
flex: 0 0 450px !important;
}
#global-matrice.select2-hidden-accessible+.select2 .select2-selection__rendered {
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
/* Select delle righe (colonna Matrice) = 150px */
.part-matrice {
width: 300px !important;
min-width: 300px !important;
max-width: 300px !important;
flex: 0 0 300px !important;
}
.part-matrice.select2-hidden-accessible+.select2 {
width: 300px !important;
min-width: 300px !important;
max-width: 300px !important;
flex: 0 0 300px !important;
}
/* Colonna Descrizione (2ª colonna) = 420px */
#partsTable th:nth-child(2),
#partsTable td:nth-child(2) {
width: 350 !important;
min-width: 350px !important;
max-width: 350px !important;
}
.select2-container--default #macro-matrice-filter {
width: 200px !important;
min-width: 200px !important;
}
.select2-container--default .part-matrice {
width: 150px !important;
min-width: 150px !important;
#partsTable td:nth-child(2) .part-description {
width: 100% !important;
max-width: 100% !important;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
/* Aspetto Select2 */
.select2-container--default .select2-selection--single {
height: 24px !important;
padding: 0.1rem 0.3rem !important;
font-size: 0.8rem !important;
padding: .1rem .3rem !important;
font-size: .8rem !important;
border: 1px solid #ced4da !important;
}
.select2-container--default .select2-selection__rendered {
line-height: 22px !important;
overflow: hidden !important;
text-overflow: ellipsis !important;
white-space: nowrap !important;
}
.select2-container--default .select2-selection__arrow {
height: 24px !important;
height: 24px !important
}
.select2-container--open .select2-dropdown {
z-index: 1061 !important;
border: 1px solid #aaa !important;
border-radius: 4px !important;
background: white !important;
overflow-y: auto !important;
background: #fff !important;
max-height: 200px !important;
overflow-y: auto !important;
}
/* Evita stretching del flex nella riga dei filtri */
#partsModal .modal-body>.row .col-md-9>div[style*="display: flex"]>* {
flex: 0 0 auto;
}
/* Altri modali e pulsanti */
.propagate-matrice-btn,
.propagate-all-btn {
padding: 0.1rem 0.3rem !important;
font-size: 0.8rem !important;
padding: .1rem .3rem !important;
font-size: .8rem !important
}
.save-status,
.save-loading {
margin-left: 5px;
margin-left: 5px
}
#confirmDeleteModal {
z-index: 1070 !important;
z-index: 1070 !important
}
#confirmDeleteModal .modal-backdrop {
z-index: 1065 !important;
z-index: 1065 !important
}
.note-btn {
padding: 0.2rem 0.4rem !important;
/* Aumentato leggermente il padding per un pulsante più grande */
font-size: 0.9rem !important;
/* Aumentato il font-size per un'icona più grande */
padding: .2rem .4rem !important;
font-size: .9rem !important
}
.note-btn.has-note {
color: #dc3545 !important;
/* Rosso quando la nota è presente */
color: #dc3545 !important
}
#noteModal {
z-index: 1090 !important;
z-index: 1090 !important
}
#noteModal .modal-backdrop {
z-index: 1085 !important;
z-index: 1085 !important
}
#noteModal .modal-dialog {
position: relative;
z-index: 1090 !important;
z-index: 1090 !important
}
#noteModal textarea {
resize: vertical;
}
.propagate-date-btn {
padding: 0.2rem 0.4rem !important;
font-size: 0.9rem !important;
}
.propagate-note-btn {
padding: 0.2rem 0.4rem !important;
font-size: 0.9rem !important;
#noteModal textarea,
#commonNoteModal textarea {
resize: vertical
}
#commonNoteModal {
z-index: 1095 !important;
/* Sopra #noteModal (1090) */
z-index: 1095 !important
}
#commonNoteModal .modal-backdrop {
z-index: 1090 !important;
/* Sopra il backdrop di #noteModal (1085) */
z-index: 1090 !important
}
#commonNoteModal .modal-dialog {
position: relative;
z-index: 1095 !important;
z-index: 1095 !important
}
#commonNoteModal textarea {
resize: vertical;
/* Evidenza salvataggio riga nel parts table */
/* Aumenta la specificità per le classi di flash */
table#partsTable tr.row-saving {
background-color: #f0ad4e !important;
/* Arancione per salvataggio in corso */
transition: background-color 0.3s ease;
}
table#partsTable tr.row-success {
background-color: #5cb85c !important;
/* Verde per successo */
transition: background-color 0.3s ease;
}
table#partsTable tr.row-error {
background-color: #d9534f !important;
/* Rosso per errore */
transition: background-color 0.3s ease;
}
/* Stato base: nascosti (verrà sovrascritto dallo style inline di jQuery) */
#partsModal .save-loading,
#partsModal .save-status {
display: none;
align-items: center;
gap: 6px;
padding: 2px 8px;
border-radius: 999px;
font-weight: 700;
font-size: 12px;
line-height: 1.2;
margin-left: 5px;
}
/* Quando NON sono nascosti via style inline (jQuery .show()), forzali a inline-flex */
#partsModal .save-loading:not([style*="display: none"]),
#partsModal .save-status:not([style*="display: none"]) {
display: inline-flex;
}
/* Loading (giallo) */
/* Loading (giallo) */
#partsModal .save-loading {
background: #ffd753ff;
border: 1px solid #ffd042ff;
color: #111;
/* testo nero */
}
#partsModal .save-loading i {
color: #111;
}
/* icona nera */
#partsModal .save-loading::after {
content: " Salvataggio…";
color: #111;
}
/* Salvato (verde) */
#partsModal .save-status {
background: #5dff83ff;
border: 1px solid #4effafff;
color: #111;
/* testo nero */
}
#partsModal .save-status i {
color: #111;
}
/* icona nera */
#partsModal .save-status::after {
content: " Salvato";
color: #111;
}
/* Animazioni */
@keyframes pulse {
0%,
100% {
transform: scale(1);
opacity: .9
}
50% {
transform: scale(1.05);
opacity: 1
}
}
@keyframes pop {
0% {
transform: scale(.85);
opacity: 0
}
100% {
transform: scale(1);
opacity: 1
}
}
/* rosso */
</style>
+242 -120
View File
@@ -7,6 +7,35 @@ $(document).ready(function () {
let matrici = [];
let macroMatrici = [];
// --- ROW ID helpers: niente più cache impazzita di jQuery .data() ---
function getPartId($row) {
// Legge in ordine: cache jQuery, nostra cache, attributo
const d = $row.data("part-id");
const ours = $row.data("__pid");
const attr = $row.attr("data-part-id");
// Scegli il primo definito
const id =
d !== undefined
? d
: ours !== undefined
? ours
: attr !== undefined
? attr
: null;
// Normalizza: "new" o "" NON sono ID validi
return id === "new" || id === "" || id === null ? null : id;
}
function setPartId($row, id) {
if (!id) return;
// Sincronizza TUTTO: attributo + cache jQuery + nostra cache
$row.attr("data-part-id", id);
$row.data("part-id", id); // <<< fondamentale per invalidare la cache di jQuery
$row.data("__pid", id);
}
// ===================
// VOICE RECOGNITION SETUP
// ===================
@@ -439,6 +468,7 @@ $(document).ready(function () {
$(document).on("click", ".add-mix-global", function (e) {
e.preventDefault();
const maxPartNumber = Math.max(
...$("#partsTableBody tr")
.map(function () {
@@ -446,36 +476,177 @@ $(document).ready(function () {
})
.get(),
);
addNewRow(maxPartNumber + 1, true); // Riga Mix
// Crea la riga Mix
addNewRow(maxPartNumber + 1, true);
const $mixRow = $("#partsTableBody tr:last");
// Consenti SOLO ora la creazione (INSERT) della riga Mix
$mixRow.data("allowCreateMix", true);
// esegue SUBITO l'INSERT così ottieni part-id
saveRow($mixRow);
});
function extractPartId(response) {
// prova i campi più comuni
if (response == null) return null;
if (response.part_id) return response.part_id;
if (response.id) return response.id;
if (response.insert_id) return response.insert_id;
if (response.lastId) return response.lastId;
// array di id
if (Array.isArray(response.part_ids) && response.part_ids[0]) {
return response.part_ids[0];
}
// oggetti annidati
if (response.parts && response.parts[0]) {
if (response.parts[0].id) return response.parts[0].id;
if (response.parts[0].part_id) return response.parts[0].part_id;
if (response.parts[0].insert_id) return response.parts[0].insert_id;
}
// altri possibili nomi dal backend
if (response.new_id) return response.new_id;
if (response.partId) return response.partId;
return null;
}
function saveRow($row) {
const partNumber = $row.find(".part-number").val();
const partDescription = $row.find(".part-description").val().trim();
const dateexpiry = $row.find(".part-dateexpiry").val();
const note = $row.data("note") || null;
const $saveStatus = $row.find(".save-status");
const $saveLoading = $row.find(".save-loading");
const iddatadb = $("#partsModal").data("iddatadb");
const idquotations = $("#partsModal").data("idquotations");
const isMix = partDescription.startsWith("Mix") ? "Y" : "N";
let partId = getPartId($row);
if (partId === "new") partId = null; // difesa extra (non dovrebbe più servire, ma sicura)
const endpoint = idquotations
? "save_parts_quotation.php"
: "save_parts.php";
const data = idquotations ? { idquotations } : { iddatadb };
// Evita salvataggi concorrenti
if ($row.data("saving") === true) return;
// Blocca INSERT del Mix se non esplicitamente richiesta
if (isMix === "Y" && !partId && $row.data("allowCreateMix") !== true) {
return;
}
$row.data("saving", true);
if (partDescription && (iddatadb || idquotations)) {
$saveLoading.show();
$saveStatus.hide();
$.ajax({
url: endpoint,
method: "POST",
data: JSON.stringify({
...data,
parts: [
{
id: partId,
part_number: partNumber,
part_description: partDescription,
mix: isMix,
dateexpiry: dateexpiry || null,
note: note,
},
],
}),
contentType: "application/json",
success: function (response) {
// assegna ID appena arriva (robusto)
if (response.success) {
const newId = extractPartId(response);
if (newId) {
setPartId($row, newId);
} else {
console.warn(
"Parte salvata ma ID non presente nella risposta. Ricarico parti per sincronizzare gli ID.",
);
loadExistingParts(iddatadb, idquotations); // <<< ORA ANCHE PER RIGHE NORMALI
}
}
$row.data("saving", false);
$row.removeData("allowCreateMix");
// ora che l'ID c'è di sicuro, sblocco gli update pendenti (es. M+)
$row.trigger("row:saved");
$saveLoading.hide();
if (response.success) {
$saveStatus.show();
setTimeout(() => $saveStatus.hide(), 2000);
} else {
const errorMsg = $(
'<div class="alert alert-danger temp-alert" role="alert">Errore nel salvataggio: ' +
response.message +
"</div>",
);
$("#partsModal .modal-body").prepend(errorMsg);
setTimeout(() => {
errorMsg.fadeOut(500, function () {
$(this).remove();
});
}, 5000);
}
},
error: function (xhr, status, error) {
$row.data("saving", false);
$row.removeData("allowCreateMix");
$saveLoading.hide();
const errorMsg = $(
'<div class="alert alert-danger temp-alert" role="alert">Errore nel salvataggio delle parti: ' +
error +
" (" +
xhr.status +
")</div>",
);
$("#partsModal .modal-body").prepend(errorMsg);
setTimeout(() => {
errorMsg.fadeOut(500, function () {
$(this).remove();
});
}, 5000);
},
});
}
}
$(document).on("click", ".add-mix-row", function (e) {
e.preventDefault();
const $row = $(this).closest("tr");
const partDescription = $row.find(".part-description").val().trim();
const $srcRow = $(this).closest("tr");
const partDescription = $srcRow.find(".part-description").val().trim();
if (!partDescription) {
const errorMsg = $(
'<div class="alert alert-danger temp-alert" role="alert">Inserisci una descrizione valida prima di aggiungerla al Mix.</div>',
);
$("#partsModal .modal-body").prepend(errorMsg);
setTimeout(function () {
setTimeout(
() =>
errorMsg.fadeOut(500, function () {
$(this).remove();
});
}, 5000);
}),
5000,
);
return;
}
const maxPartNumber = Math.max(
...$("#partsTableBody tr")
.map(function () {
return parseInt($(this).find(".part-number").val()) || 0;
})
.get(),
);
let mixDescription = `Mix ${partDescription}`;
const $mixRow = $("#partsTableBody tr")
let $mixRow = $("#partsTableBody tr")
.filter(function () {
return $(this)
.find(".part-description")
@@ -485,31 +656,59 @@ $(document).ready(function () {
})
.last();
if ($mixRow.length > 0) {
let currentMix = $mixRow.find(".part-description").val().trim();
if (currentMix === "Mix") {
mixDescription = currentMix + " " + partDescription;
} else if (!currentMix.includes(partDescription)) {
mixDescription = currentMix + " + " + partDescription;
// Se non esiste una riga Mix, ne creo una e la INSERISCO SUBITO (come fa il bottone in header)
if ($mixRow.length === 0) {
const maxPartNumber = Math.max(
...$("#partsTableBody tr")
.map(function () {
return (
parseInt($(this).find(".part-number").val()) || 0
);
})
.get(),
);
addNewRow(maxPartNumber + 1, true);
$mixRow = $("#partsTableBody tr:last");
$mixRow.find(".part-description").val(`Mix ${partDescription}`);
// Consenti la creazione (INSERT) della riga Mix e salvala subito
$mixRow.data("allowCreateMix", true);
saveRow($mixRow); // -> INSERT
return; // la descrizione include già l'elemento appena aggiunto
}
// Aggiorna la descrizione del Mix esistente
const currentMix = $mixRow.find(".part-description").val().trim();
let newDesc = currentMix;
if (currentMix === "Mix") newDesc = currentMix + " " + partDescription;
else if (!currentMix.includes(partDescription))
newDesc = currentMix + " + " + partDescription;
$mixRow.find(".part-description").val(newDesc);
// Se il Mix è già in salvataggio (INSERT o UPDATE in corso), accodiamo un solo UPDATE
if ($mixRow.data("saving") === true) {
// evita più code accumulate
if (!$mixRow.data("pendingUpdate")) {
$mixRow.data("pendingUpdate", true);
$mixRow.one("row:saved", function () {
$mixRow.removeData("pendingUpdate");
// ora che saving è false, salviamo l'ultima descrizione impostata
saveRow($mixRow);
});
}
$mixRow
.find(".part-description")
.val(mixDescription)
.trigger("blur");
} else {
addNewRow(maxPartNumber + 1, true); // Crea nuova riga Mix
const $newMixRow = $("#partsTableBody tr:last");
$newMixRow
.find(".part-description")
.val(mixDescription)
.trigger("blur");
// libero: salva subito
saveRow($mixRow);
}
});
function addNewRow(nextPartNumber, isMix = false) {
const description = isMix ? "Mix" : "";
const newRow = `
<tr data-part-id="">
<tr data-part-id="new">
<td><input type="number" class="form-control form-control-sm part-number" value="${nextPartNumber || 1}" style="width: 80px;"></td>
<td><input type="text" class="form-control form-control-sm part-description" value="${description}" placeholder="Inserisci descrizione"></td>
<td>
@@ -540,7 +739,7 @@ $(document).ready(function () {
// ===================
$(document).on("click", ".note-btn", function () {
const $row = $(this).closest("tr");
const partId = $row.data("part-id");
const partId = getPartId($row);
const note = $row.data("note") || "";
const $noteModal = $("#noteModal");
$noteModal.find(".part-note").val(note);
@@ -655,7 +854,7 @@ $(document).ready(function () {
$(document).on("change", ".part-dateexpiry", function () {
const $input = $(this);
const $row = $input.closest("tr");
const partId = $row.data("part-id");
const partId = getPartId($row);
const dateexpiry = $input.val();
const iddatadb = $("#partsModal").data("iddatadb");
const idquotations = $("#partsModal").data("idquotations");
@@ -953,88 +1152,7 @@ $(document).ready(function () {
});
$(document).on("blur", ".part-description, .part-number", function () {
const $input = $(this);
const $row = $input.closest("tr");
const partNumber = $row.find(".part-number").val();
const partDescription = $row.find(".part-description").val().trim();
const dateexpiry = $row.find(".part-dateexpiry").val();
const note = $row.data("note") || null;
const $saveStatus = $row.find(".save-status");
const $saveLoading = $row.find(".save-loading");
const iddatadb = $("#partsModal").data("iddatadb");
const idquotations = $("#partsModal").data("idquotations");
const isMix = partDescription.startsWith("Mix") ? "Y" : "N";
const partId = $row.data("part-id") || null;
const endpoint = idquotations
? "save_parts_quotation.php"
: "save_parts.php";
const data = idquotations
? { idquotations: idquotations }
: { iddatadb: iddatadb };
if (partDescription && (iddatadb || idquotations)) {
$saveLoading.show();
$saveStatus.hide();
$.ajax({
url: endpoint,
method: "POST",
data: JSON.stringify({
...data,
parts: [
{
id: partId,
part_number: partNumber,
part_description: partDescription,
mix: isMix,
dateexpiry: dateexpiry || null,
note: note,
},
],
}),
contentType: "application/json",
success: function (response) {
$saveLoading.hide();
if (response.success) {
$saveStatus.show();
if (response.part_id) {
$row.attr("data-part-id", response.part_id).data(
"part-id",
response.part_id,
);
}
setTimeout(() => $saveStatus.hide(), 2000);
} else {
const errorMsg = $(
'<div class="alert alert-danger temp-alert" role="alert">Errore nel salvataggio: ' +
response.message +
"</div>",
);
$("#partsModal .modal-body").prepend(errorMsg);
setTimeout(function () {
errorMsg.fadeOut(500, function () {
$(this).remove();
});
}, 5000);
}
},
error: function (xhr, status, error) {
$saveLoading.hide();
const errorMsg = $(
'<div class="alert alert-danger temp-alert" role="alert">Errore nel salvataggio delle parti: ' +
error +
" (" +
xhr.status +
")</div>",
);
$("#partsModal .modal-body").prepend(errorMsg);
setTimeout(function () {
errorMsg.fadeOut(500, function () {
$(this).remove();
});
}, 5000);
},
});
}
saveRow($(this).closest("tr"));
});
function loadExistingParts(iddatadb, idquotations) {
@@ -1356,8 +1474,8 @@ $(document).ready(function () {
},
});
// Ripristina il valore se valido
if (partId && partId !== "new" && currentValue) {
// === MODIFICA CHIRURGICA: solo qui ===
if (currentValue && currentValue !== "new") {
const matrice = matrici.find((m) => m.IdMatrice == currentValue);
if (matrice) {
const option = new Option(
@@ -1369,11 +1487,15 @@ $(document).ready(function () {
$select.append(option).trigger("change");
partMatrice[partNumber] = matrice.IdMatrice;
} else {
// Aggiusta valore non valido
$select.val(null).trigger("change");
partMatrice[partNumber] = null;
}
} else {
// Nessun valore: assicurati che sia vuoto
$select.val(null).trigger("change");
partMatrice[partNumber] = null;
}
// === FINE MODIFICA ===
$select.on("change", function () {
const idmatrice = $(this).val();
+4 -1
View File
@@ -108,7 +108,7 @@ try {
}
// Recupera routine dal template
$stmt = $pdo->prepare("SELECT idroutine FROM excel_templates WHERE id = ?");
$stmt = $pdo->prepare("SELECT idroutine, idclient FROM excel_templates WHERE id = ?");
$stmt->execute([$template_id]);
$template = $stmt->fetch(PDO::FETCH_ASSOC);
@@ -133,6 +133,9 @@ try {
error_log("Nessuna routine associata al template {$template_id}");
}
// Aggiungi idclient alla risposta
$response['idclient'] = $template['idclient'] ?? null;
// Salva i dati in sessione
$_SESSION['excel_data'] = $excelData;
$_SESSION['template_id'] = $template_id;
+22 -10
View File
@@ -11,13 +11,14 @@ try {
}
$iddatadb = intval($_POST['iddatadb']);
$idclient = isset($_POST['idclient']) ? (is_numeric($_POST['idclient']) ? intval($_POST['idclient']) : null) : null;
$db = DBHandlerSelect::getInstance();
$pdo = $db->getConnection();
$data = $_POST;
$details = [];
// 1. POST-დან ამოვიღოთ მხოლოდ details
// 1. Estrarre i dettagli da POST
foreach ($data as $key => $value) {
if (preg_match('/^details(\d+)field_value$/', $key, $matches)) {
$id = $matches[1];
@@ -25,16 +26,15 @@ try {
}
}
// 2. DB-დან წამოვიღოთ არსებული მნიშვნელობები
// 2. Recupera i valori esistenti da import_data_details
$stmt = $pdo->prepare("SELECT mapping_id, field_value FROM import_data_details WHERE id = ?");
$stmt->execute([$iddatadb]);
$currentValues = [];
while ($row = $stmt->fetch(PDO::FETCH_ASSOC)) {
$currentValues[$row['mapping_id']] = $row['field_value'];
}
// 3. შევადაროთ POST-ს და DB-ს
// 3. Confronta i valori nuovi con quelli esistenti
$changed = [];
foreach ($details as $id => $newValue) {
$oldValue = $currentValues[$id] ?? null;
@@ -46,7 +46,7 @@ try {
}
}
// 4. თუ არის ცვლილებები → UPDATE
// 4. Aggiorna i dettagli se ci sono modifiche
if (!empty($changed)) {
$updateStmt = $pdo->prepare("
UPDATE import_data_details
@@ -61,14 +61,26 @@ try {
':mappingId' => $mappingId
]);
}
}
// 5. Aggiorna idclient in datadb
if (isset($idclient)) {
$updateStmt = $pdo->prepare("
UPDATE datadb
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";
}
$response['success'] = true;
$response['message'] = "Updated successfully";
$response['changed'] = $changed; // Debug / optional
} else {
$response['success'] = true;
$response['message'] = "No changes found";
}
} catch (Exception $e) {
$response['success'] = false;
+1 -1
View File
@@ -1,6 +1,6 @@
<?php
// upload_photos_mobile.php
include('include/headscript.php');
include('include/headscriptnologin.php');
$db = DBHandlerSelect::getInstance();
$pdo = $db->getConnection();