added clienti in template, export

This commit is contained in:
Claudio 2026-03-19 10:08:29 +01:00
parent eb21910ef3
commit 5a58decd40
15 changed files with 3497 additions and 235 deletions

View File

@ -30,6 +30,8 @@ if ((int)$stmt->fetchColumn() > 0) {
$fixedFields = [
// fixed_field_key, data_type
['ClienteResponsabile', 'INT'],
['ClienteFornitore', 'INT'],
['ClienteAnalisi', 'INT'],
['MoltiplicatorePrezzo', 'INT'],
['AnagraficaCertestObject', 'INT'],
['AnagraficaCertestService', 'INT'],

View File

@ -0,0 +1,290 @@
<?php
require_once "class/VisualLimsApiClient.class.php";
include('include/headscript.php');
// Force HTML response
header('Content-Type: text/html; charset=UTF-8');
// Initialize variables
$error = null;
$result = null;
$entityType = $_GET['entity_type'] ?? 'CommessaWeb';
$entityId = isset($_GET['entity_id']) ? (int)$_GET['entity_id'] : 0;
$customExpand = trim($_GET['expand'] ?? '');
$rawEndpoint = null;
function h($value)
{
return htmlspecialchars((string)$value, ENT_QUOTES, 'UTF-8');
}
function renderArrayAsTable(array $items)
{
if (empty($items)) {
echo '<div class="alert alert-secondary">No records found</div>';
return;
}
$firstRow = reset($items);
if (!is_array($firstRow)) {
echo '<pre class="bg-light p-3 border rounded">' . h(json_encode($items, JSON_PRETTY_PRINT | JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES)) . '</pre>';
return;
}
$headers = [];
foreach ($items as $row) {
if (is_array($row)) {
$headers = array_unique(array_merge($headers, array_keys($row)));
}
}
echo '<div class="table-responsive">';
echo '<table class="table table-striped table-bordered table-sm align-middle">';
echo '<thead class="table-dark"><tr>';
foreach ($headers as $header) {
echo '<th>' . h($header) . '</th>';
}
echo '</tr></thead><tbody>';
foreach ($items as $row) {
echo '<tr>';
foreach ($headers as $header) {
$value = $row[$header] ?? '';
if (is_array($value) || is_object($value)) {
$value = json_encode($value, JSON_PRETTY_PRINT | JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES);
echo '<td><pre style="white-space:pre-wrap; margin:0;">' . h($value) . '</pre></td>';
} else {
echo '<td>' . h($value) . '</td>';
}
}
echo '</tr>';
}
echo '</tbody></table>';
echo '</div>';
}
try {
if ($entityId > 0) {
$api = VisualLimsApiClient::getInstance();
// Default expands for better debugging
if ($customExpand !== '') {
$expand = $customExpand;
} else {
if ($entityType === 'CommessaWeb') {
$expand = implode(',', [
'CommesseCustomFields($expand=CustomField)',
'Campioni'
]);
} else {
$expand = implode(',', [
'CommesseCustomFields($expand=CustomField)',
'Campioni'
]);
}
}
$rawEndpoint = $entityType . '(' . $entityId . ')?$expand=' . $expand;
$result = $api->get($rawEndpoint);
}
} catch (Exception $e) {
$error = $e->getMessage();
}
?>
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Debug Commessa API</title>
<meta name="viewport" content="width=device-width, initial-scale=1">
<style>
body {
background: #f5f7fb;
font-family: Arial, sans-serif;
}
.debug-card {
background: #fff;
border-radius: 12px;
box-shadow: 0 4px 18px rgba(0, 0, 0, 0.08);
padding: 20px;
margin-bottom: 20px;
}
.section-title {
font-size: 18px;
font-weight: 700;
margin-bottom: 15px;
}
pre {
background: #f8f9fa;
border: 1px solid #ddd;
border-radius: 8px;
padding: 12px;
white-space: pre-wrap;
word-break: break-word;
}
.label-box {
display: inline-block;
padding: 6px 10px;
border-radius: 8px;
background: #eef3ff;
color: #2446a8;
font-weight: 600;
margin-right: 8px;
margin-bottom: 8px;
}
.mini-grid {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(220px, 1fr));
gap: 12px;
}
.mini-box {
background: #f8fafc;
border: 1px solid #e4e7eb;
border-radius: 10px;
padding: 12px;
}
.mini-box strong {
display: block;
margin-bottom: 6px;
color: #1f2937;
}
</style>
</head>
<body>
<div class="container py-4">
<div class="debug-card">
<h2 class="mb-3">Commessa / CommessaWeb API Inspector</h2>
<form method="GET" class="row g-3">
<div class="col-md-3">
<label class="form-label">Entity Type</label>
<select name="entity_type" class="form-select">
<option value="CommessaWeb" <?= $entityType === 'CommessaWeb' ? 'selected' : '' ?>>CommessaWeb</option>
<option value="Commessa" <?= $entityType === 'Commessa' ? 'selected' : '' ?>>Commessa</option>
</select>
</div>
<div class="col-md-2">
<label class="form-label">Entity ID</label>
<input type="number" name="entity_id" class="form-control" value="<?= h($entityId) ?>" required>
</div>
<div class="col-md-5">
<label class="form-label">Expand</label>
<input type="text" name="expand" class="form-control" value="<?= h($customExpand) ?>" placeholder="CommesseCustomFields($expand=CustomField),Campioni">
</div>
<div class="col-md-2 d-flex align-items-end">
<button type="submit" class="btn btn-primary w-100">Load Data</button>
</div>
</form>
</div>
<?php if ($rawEndpoint): ?>
<div class="debug-card">
<div class="section-title">Requested Endpoint</div>
<pre><?= h($rawEndpoint) ?></pre>
</div>
<?php endif; ?>
<?php if ($error): ?>
<div class="debug-card">
<div class="alert alert-danger mb-0">
<strong>API Error:</strong><br>
<?= h($error) ?>
</div>
</div>
<?php endif; ?>
<?php if ($result && is_array($result)): ?>
<div class="debug-card">
<div class="section-title">Main Information</div>
<div class="mini-grid">
<div class="mini-box">
<strong>ID</strong>
<?= h($result['IdCommessa'] ?? $result['IdCommessaWeb'] ?? '') ?>
</div>
<div class="mini-box">
<strong>Code</strong>
<?= h($result['CodiceCommessa'] ?? '') ?>
</div>
<div class="mini-box">
<strong>Cliente</strong>
<?= h($result['Cliente'] ?? '') ?>
</div>
<div class="mini-box">
<strong>SchemaCustomField</strong>
<?= h($result['SchemaCustomField'] ?? '') ?>
</div>
<div class="mini-box">
<strong>Richiedente</strong>
<?= h($result['Richiedente'] ?? '') ?>
</div>
<div class="mini-box">
<strong>Descrizione</strong>
<?= h($result['Descrizione'] ?? '') ?>
</div>
</div>
</div>
<div class="debug-card">
<div class="section-title">Direct Properties</div>
<pre><?= h(json_encode($result, JSON_PRETTY_PRINT | JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES)) ?></pre>
</div>
<?php if (!empty($result['CommesseCustomFields']) && is_array($result['CommesseCustomFields'])): ?>
<div class="debug-card">
<div class="section-title">CommesseCustomFields</div>
<?php
$fieldsRows = [];
foreach ($result['CommesseCustomFields'] as $field) {
$fieldsRows[] = [
'IdCommesseCustomFields' => $field['IdCommesseCustomFields'] ?? '',
'Valore' => $field['Valore'] ?? '',
'CustomFieldId' => $field['CustomField']['IdCustomField'] ?? '',
'Label' => $field['CustomField']['Descrizione'] ?? ($field['CustomField']['Name'] ?? ''),
'Tipo' => $field['CustomField']['Tipo'] ?? '',
];
}
renderArrayAsTable($fieldsRows);
?>
</div>
<?php endif; ?>
<?php if (!empty($result['Campioni']) && is_array($result['Campioni'])): ?>
<div class="debug-card">
<div class="section-title">Campioni</div>
<?php renderArrayAsTable($result['Campioni']); ?>
</div>
<?php endif; ?>
<div class="debug-card">
<div class="section-title">Raw JSON</div>
<pre><?= h(json_encode($result, JSON_PRETTY_PRINT | JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES)) ?></pre>
</div>
<?php elseif ($entityId > 0 && !$error): ?>
<div class="debug-card">
<div class="alert alert-warning mb-0">No data returned from API</div>
</div>
<?php endif; ?>
</div>
</body>
</html>
<!-- Example of use
debug_commessa_api.php?entity_type=CommessaWeb&entity_id=564779
debug_commessa_api.php?entity_type=Commessa&entity_id=12345
debug_commessa_api.php?entity_type=CommessaWeb&entity_id=564779&expand=CommesseCustomFields($expand=CustomField),Campioni
-->

View File

@ -315,3 +315,4 @@
2026-02-28 21:01:27 [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-03-01 19:11:58 [AnagraficaCertestObject] 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-03-18 15:51:45 [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-03-19 09:50:34 [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"}

View File

@ -101,6 +101,7 @@ try {
$anagraficaObject = !empty($result['anagrafica_certest_object_id']) ? (int) $result['anagrafica_certest_object_id'] : null;
$anagraficaService = !empty($result['anagrafica_certest_service_id']) ? (int) $result['anagrafica_certest_service_id'] : null;
$clienteFornitore = !empty($result['cliente_fornitore_id']) ? (int) $result['cliente_fornitore_id'] : null;
$clienteAnalisi = !empty($result['clienteAnalisi']) ? (int) $result['clienteAnalisi'] : null;
$consegnaRichiesta = !empty($result['consegna_richiesta']) ? $result['consegna_richiesta'] : null;
// 🔹 STEP 3: Fetch Parts (including idmatrice)
@ -175,6 +176,7 @@ try {
"AnagraficaCertestObject" => $anagraficaObject,
"AnagraficaCertestService" => $anagraficaService,
"ClienteFornitore" => $clienteFornitore, // PLACEHOLDER — to be implemented
"ClienteAnalisi" => $clienteAnalisi, // PLACEHOLDER — to be implemented
// DeliveryRequest goes to Campione, not CommessaWeb
];

View File

@ -167,6 +167,8 @@ $fixedFieldsRaw = $fixedStmt->fetchAll(PDO::FETCH_ASSOC);
// Ordine desiderato: Cliente Responsabile prima, poi gli altri, ConsegnaRichiesta prima delle 3 speciali
$desiredOrder = [
'ClienteResponsabile',
'ClienteFornitore',
'ClienteAnalisi',
'AnagraficaCertestObject',
'AnagraficaCertestService',
'MoltiplicatorePrezzo',
@ -174,8 +176,8 @@ $desiredOrder = [
// se ci sono altri campi fixed li mettiamo dopo
];
// ClienteFornitore is rendered as a standalone column (like idclient), skip it in fixed fields
$excludeFromFixed = ['ClienteFornitore'];
// No exclusions: fixed fields will be rendered together at the end
$excludeFromFixed = [];
$fixedFields = [];
$tempMap = [];
@ -199,10 +201,11 @@ foreach ($tempMap as $f) {
// Maps logical fixed_field_key → real datadb column name (mirrors JS fixedAliasMap)
$fixedAliasMap = [
'ClienteResponsabile' => 'cliente_responsabile_id',
'ClienteFornitore' => 'cliente_fornitore_id',
'ClienteAnalisi' => 'clienteAnalisi',
'MoltiplicatorePrezzo' => 'moltiplicatore_prezzo_id',
'AnagraficaCertestObject' => 'anagrafica_certest_object_id',
'AnagraficaCertestService' => 'anagrafica_certest_service_id',
'ClienteFornitore' => 'cliente_fornitore_id',
'ConsegnaRichiesta' => 'consegna_richiesta',
];
@ -966,15 +969,9 @@ function fixedDefaultValue(array $f): string
<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 grid-top-cell" style="flex: 0 0 300px;" data-index="<?= $mainFieldMapping ? 4 : 3 ?>">
<select class="custom-field dropdown-select client-select searchable-client" data-column="cliente_fornitore_id" id="clienteFornitoreSelect" name="cliente_fornitore_id">
<option value="">Select a supplier...</option>
</select>
<button type="button" class="propagate-btn" data-column="cliente_fornitore_id"><i class="fas fa-arrow-down"></i></button>
</div>
<?php
$topColIndex = $mainFieldMapping ? 5 : 4;
$topColIndex = $mainFieldMapping ? 4 : 3;
$autoIndex = 0;
foreach ($allMappings as $mapping) {
@ -1083,6 +1080,8 @@ function fixedDefaultValue(array $f): string
$isApiField = in_array($key, [
'MoltiplicatorePrezzo',
'ClienteResponsabile',
'ClienteFornitore',
'ClienteAnalisi',
'AnagraficaCertestObject',
'AnagraficaCertestService'
], true);
@ -1140,10 +1139,8 @@ function fixedDefaultValue(array $f): string
</div>
<div class="grid-header" data-index="<?= $mainFieldMapping ? 3 : 2 ?>" style="flex: 0 0 300px; position: relative;">Client<div class="resizer"></div>
</div>
<div class="grid-header" data-index="<?= $mainFieldMapping ? 4 : 3 ?>" style="flex: 0 0 300px; position: relative;">Cliente Fornitore<div class="resizer"></div>
</div>
<?php
$headerIndex = $mainFieldMapping ? 5 : 4;
$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>";
@ -1245,13 +1242,9 @@ function fixedDefaultValue(array $f): string
<option value="">Select a client...</option>
</select>
</div>
<div class="grid-cell editable-cell" data-col="cliente_fornitore_id" data-row="<?= $index ?>" data-index="<?= $mainFieldMapping ? 4 : 3 ?>" style="flex: 0 0 300px;">
<select name="rows[<?= $index ?>][cliente_fornitore_id]" class="cell-input dropdown-select client-select searchable-client fornitore-select" data-current-value="<?= htmlspecialchars($row['cliente_fornitore_id'] ?? '') ?>">
<option value="">Select a supplier...</option>
</select>
</div>
<?php
$cellIndex = $mainFieldMapping ? 5 : 4;
$cellIndex = $mainFieldMapping ? 4 : 3;
$rowDetails = array_filter($manualDetails, fn($d) => $d['datadb_id'] == $row['iddatadb']);
$autoIndex = 0;
foreach ($allMappings as $mapping) {
@ -1376,7 +1369,7 @@ function fixedDefaultValue(array $f): string
. (((int)$f['is_required'] === 1) ? "required" : "")
. ">";
} else {
$isApiField = in_array($key, ['MoltiplicatorePrezzo', 'ClienteResponsabile', 'AnagraficaCertestObject', 'AnagraficaCertestService'], true);
$isApiField = in_array($key, ['MoltiplicatorePrezzo', 'ClienteResponsabile', 'ClienteFornitore', 'ClienteAnalisi', 'AnagraficaCertestObject', 'AnagraficaCertestService'], true);
$selectClass = $isApiField ? 'api-fixed-select' : '';
echo "<select
name='rows[$index][$key]'
@ -1443,6 +1436,60 @@ function fixedDefaultValue(array $f): string
<script src="partsTable.js"></script>
<script src="tracking.js"></script>
<script src="export_to_lims.js"></script>
<script>
let globalClientData = [];
let globalClientPromise = null;
function formatClientLabel(client) {
const nome = client.Nominativo || "Nome non disponibile";
const id = client.IdCliente || "";
const codiceCliente = (client.CodiceCliente ?? client.codiceCliente ?? '').toString().trim();
const suffix = (codiceCliente.split('_')[1] || '').trim();
const shortCode = suffix || (codiceCliente ? codiceCliente.charAt(0) : '--');
return `${nome.trim()} - ${shortCode} (ID: ${id})`;
}
async function ensureClientsLoaded(retryCount = 0, maxRetries = 3) {
if (globalClientData.length > 0) {
return globalClientData;
}
if (globalClientPromise) {
return globalClientPromise;
}
globalClientPromise = (async () => {
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 &&
data.error.includes('Cannot persist the object') &&
retryCount < maxRetries
) {
await new Promise(resolve => setTimeout(resolve, 1000));
globalClientPromise = null;
return ensureClientsLoaded(retryCount + 1, maxRetries);
}
throw new Error(data.error || `Errore HTTP: ${response.status}`);
}
globalClientData = data.value || [];
return globalClientData;
})();
return globalClientPromise;
}
</script>
<script>
function expandColumn(cell, expand) {
const columnIndex = cell.getAttribute('data-index');
@ -1577,11 +1624,6 @@ function fixedDefaultValue(array $f): string
formData.append('idclient', idclientSelect.value);
}
const fornitoreSelect = row.querySelector(`select[name="rows[${rowIndex}][cliente_fornitore_id]"]`);
if (fornitoreSelect) {
formData.append('cliente_fornitore_id', fornitoreSelect.value);
}
// ---- FIXED FIELDS (NEW) ----
const fixedInputs = row.querySelectorAll(`input[name^="rows[${rowIndex}]["], select[name^="rows[${rowIndex}]["]`);
fixedInputs.forEach(inp => {
@ -1593,6 +1635,8 @@ function fixedDefaultValue(array $f): string
// Map: fixed key (logical) -> datadb real column
const fixedAliasMap = {
ClienteResponsabile: 'cliente_responsabile_id',
ClienteFornitore: 'cliente_fornitore_id',
ClienteAnalisi: 'clienteAnalisi',
MoltiplicatorePrezzo: 'moltiplicatore_prezzo_id',
AnagraficaCertestObject: 'anagrafica_certest_object_id',
AnagraficaCertestService: 'anagrafica_certest_service_id',
@ -1746,10 +1790,6 @@ function fixedDefaultValue(array $f): string
if (idclientSelect) {
formData.append('idclient', idclientSelect.value);
}
const fornitoreSelect = row.querySelector(`select[name="rows[${rowIndex}][cliente_fornitore_id]"]`);
if (fornitoreSelect) {
formData.append('cliente_fornitore_id', fornitoreSelect.value);
}
// ---- FIXED FIELDS ----
const fixedInputs = row.querySelectorAll(`input[name^="rows[${rowIndex}]["], select[name^="rows[${rowIndex}]["]`);
fixedInputs.forEach(inp => {
@ -1757,6 +1797,8 @@ function fixedDefaultValue(array $f): string
const m = inp.name.match(/rows\[\d+\]\[([^\]]+)\]/);
const fixedAliasMap = {
ClienteResponsabile: 'cliente_responsabile_id',
ClienteFornitore: 'cliente_fornitore_id',
ClienteAnalisi: 'clienteAnalisi',
MoltiplicatorePrezzo: 'moltiplicatore_prezzo_id',
AnagraficaCertestObject: 'anagrafica_certest_object_id',
AnagraficaCertestService: 'anagrafica_certest_service_id',
@ -1884,51 +1926,31 @@ function fixedDefaultValue(array $f): string
<script>
document.addEventListener("DOMContentLoaded", function() {
const inputs = document.querySelectorAll('.cell-input');
let clientData = []; // Dichiarazione di clientData qui
let clientData = globalClientData;
// Funzione per caricare i client
async function loadClients(retryCount = 0, maxRetries = 3) {
async function loadClients() {
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 || [];
clientData = await ensureClientsLoaded();
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 codiceCliente = (client.CodiceCliente || '').toString().trim();
const suffix = (codiceCliente.split('_')[1] || '').trim();
const shortCode = suffix || (codiceCliente ? codiceCliente.charAt(0) : '--');
const option = new Option(`${nome.trim()} - ${shortCode} (ID: ${id})`, id);
if (parseInt(id) === parseInt(<?php echo json_encode($default_idclient ?? 0); ?>)) {
clientData.forEach(client => {
const option = new Option(formatClientLabel(client), client.IdCliente);
if (parseInt(client.IdCliente) === parseInt(<?php echo json_encode($default_idclient ?? 0); ?>)) {
option.selected = true;
}
select.add(option);
});
populateClientDropdowns();
populateFornitoreDropdowns();
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');
@ -1979,45 +2001,6 @@ function fixedDefaultValue(array $f): string
});
}
// Funzione per popolare i dropdown ClienteFornitore (header + rows)
function populateFornitoreDropdowns() {
// Header
const headerSelect = document.getElementById("clienteFornitoreSelect");
if (headerSelect) {
headerSelect.innerHTML = '<option value="">Select a supplier...</option>';
clientData.forEach(client => {
const nome = client.Nominativo || "Nome non disponibile";
const id = client.IdCliente || "ID non disponibile";
const codiceCliente = (client.CodiceCliente || '').toString().trim();
const suffix = (codiceCliente.split('_')[1] || '').trim();
const shortCode = suffix || (codiceCliente ? codiceCliente.charAt(0) : '--');
headerSelect.add(new Option(`${nome.trim()} - ${shortCode} (ID: ${id})`, id));
});
}
// Row dropdowns
const fornitoreDropdowns = document.querySelectorAll('select[name^="rows"][name$="[cliente_fornitore_id]"]');
fornitoreDropdowns.forEach(dropdown => {
const currentValue = dropdown.getAttribute('data-current-value') || '';
dropdown.innerHTML = '<option value="">Select a supplier...</option>';
clientData.forEach(client => {
const nome = client.Nominativo || "Nome non disponibile";
const id = client.IdCliente || "ID non disponibile";
const codiceCliente = (client.CodiceCliente || '').toString().trim();
const suffix = (codiceCliente.split('_')[1] || '').trim();
const shortCode = suffix || (codiceCliente ? codiceCliente.charAt(0) : '--');
const option = new Option(`${nome.trim()} - ${shortCode} (ID: ${id})`, id);
if (String(id) === String(currentValue)) {
option.selected = true;
}
dropdown.add(option);
});
if (currentValue) {
dropdown.value = currentValue;
}
});
}
// Carica i client all'avvio
loadClients();
@ -2355,121 +2338,6 @@ function fixedDefaultValue(array $f): string
populateDropdowns();
});
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}`);
}
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";
// CodiceCliente es: "Bl01858_E" -> vogliamo "E"
const codiceCliente = (client.CodiceCliente ?? client.codiceCliente ?? '').toString().trim();
const suffix = (codiceCliente.split('_')[1] || '').trim(); // parte dopo "_"
const shortCode = suffix || '--';
const option = new Option(`${nome.trim()} - ${shortCode} (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.";
Swal.fire({
title: "Errore!",
text: "Impossibile caricare i clienti: " + error.message,
icon: "error",
confirmButtonText: "OK"
});
} finally {
setTimeout(() => clientLoadingStatus.style.display = 'none', 2000);
}
}
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";
// CodiceCliente es: "Bl01858_E" -> vogliamo "E"
const codiceCliente = (client.CodiceCliente || '').toString().trim();
const suffix = (codiceCliente.split('_')[1] || '').trim(); // parte dopo "_"
const shortCode = suffix || '--';
const option = new Option(`${nome.trim()} - ${shortCode} (ID: ${id})`, id);
if (String(id) === String(currentValue)) {
option.selected = true;
}
dropdown.add(option);
});
// Ripristina il valore corrente
if (currentValue) {
dropdown.value = currentValue;
dropdown.setAttribute('data-restoring', '');
const event = new Event('change', {
bubbles: true
});
dropdown.dispatchEvent(event);
dropdown.removeAttribute('data-restoring');
}
});
}
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() {
@ -2538,22 +2406,6 @@ function fixedDefaultValue(array $f): string
});
});
// Propagate ClienteFornitore
$('.propagate-btn[data-column="cliente_fornitore_id"]').on('click', function() {
const value = $('#clienteFornitoreSelect').val();
const rows = document.querySelectorAll('.grid-row');
rows.forEach(row => {
const fornitoreSelect = row.querySelector('select[name$="[cliente_fornitore_id]"]');
if (fornitoreSelect) {
$(fornitoreSelect).val(value).trigger('change.select2');
const event = new Event('change', {
bubbles: true
});
fornitoreSelect.dispatchEvent(event);
}
});
});
// Quick add part from "Tested Component" (+ button)
$(document).on('click', '.add-part-btn', async function() {
const rowIndex = $(this).data('row');
@ -2757,6 +2609,12 @@ function fixedDefaultValue(array $f): string
getParams: (clientId) => ({
id_cliente: clientId
})
},
'ClienteFornitore': {
source: 'clients'
},
'ClienteAnalisi': {
source: 'clients'
}
};
@ -2780,6 +2638,21 @@ function fixedDefaultValue(array $f): string
const config = apiFields[fieldKey];
if (!config) return [];
if (config.source === 'clients') {
const clients = await ensureClientsLoaded();
const results = clients.map(client => ({
id: client.IdCliente,
text: formatClientLabel(client)
}));
results.sort((a, b) => String(a.text || '').localeCompare(String(b.text || ''), 'it', {
sensitivity: 'base'
}));
fixedFieldDataCache[fieldKey] = results;
return results;
}
const cacheKey = fieldKey + (clientId ? '_' + clientId : '');
if (fixedFieldDataCache[cacheKey]) {
return fixedFieldDataCache[cacheKey];

View File

@ -0,0 +1,90 @@
CAMPIONE #0
curl --location --request POST 'https://93.43.5.102/limsapi/api/odata/Campione' \
--header 'Content-Type: application/json' \
--header 'Authorization: Bearer ••••••' \
--data '{
"Commessa": 565085,
"Matrice": 7714,
"SottoMatrice": null,
"SchemaCustomField": 82,
"NoteWeb": "Parte AAA",
"ConsegnaRichiesta": "2026-04-08"
}'
RESPONSE:
{
"@odata.context": "https:\/\/bvcpsitaly-elims.com\/limsapi\/api\/odata\/$metadata#Campione\/$entity",
"IdCampione": 736899,
"CodiceCampione": "11038",
"CodiceCampioneWeb": "11038",
"StatoCampione": "Nuovo",
"DataCreazioneWeb": "2026-03-19T10:03:19.8547279+01:00",
"RiferimentoWeb": "",
"ConsegnaRichiesta": "2026-04-08T00:00:00+02:00",
"DataAccettazioneLims": null,
"Riferimento": null,
"NoteWeb": "Parte AAA",
"GruppiRicercati": null
}
---
CAMPIONE #1
curl --location --request POST 'https://93.43.5.102/limsapi/api/odata/Campione' \
--header 'Content-Type: application/json' \
--header 'Authorization: Bearer ••••••' \
--data '{
"Commessa": 565085,
"Matrice": 7714,
"SottoMatrice": null,
"SchemaCustomField": 82,
"NoteWeb": "Parte BBB",
"ConsegnaRichiesta": "2026-04-08"
}'
RESPONSE:
{
"@odata.context": "https:\/\/bvcpsitaly-elims.com\/limsapi\/api\/odata\/$metadata#Campione\/$entity",
"IdCampione": 736900,
"CodiceCampione": "11039",
"CodiceCampioneWeb": "11039",
"StatoCampione": "Nuovo",
"DataCreazioneWeb": "2026-03-19T10:03:22.0431135+01:00",
"RiferimentoWeb": "",
"ConsegnaRichiesta": "2026-04-08T00:00:00+02:00",
"DataAccettazioneLims": null,
"Riferimento": null,
"NoteWeb": "Parte BBB",
"GruppiRicercati": null
}
---
CAMPIONE #2
curl --location --request POST 'https://93.43.5.102/limsapi/api/odata/Campione' \
--header 'Content-Type: application/json' \
--header 'Authorization: Bearer ••••••' \
--data '{
"Commessa": 565085,
"Matrice": 7714,
"SottoMatrice": null,
"SchemaCustomField": 82,
"NoteWeb": "Parte CCC",
"ConsegnaRichiesta": "2026-04-08"
}'
RESPONSE:
{
"@odata.context": "https:\/\/bvcpsitaly-elims.com\/limsapi\/api\/odata\/$metadata#Campione\/$entity",
"IdCampione": 736901,
"CodiceCampione": "11040",
"CodiceCampioneWeb": "11040",
"StatoCampione": "Nuovo",
"DataCreazioneWeb": "2026-03-19T10:03:23.6170597+01:00",
"RiferimentoWeb": "",
"ConsegnaRichiesta": "2026-04-08T00:00:00+02:00",
"DataAccettazioneLims": null,
"Riferimento": null,
"NoteWeb": "Parte CCC",
"GruppiRicercati": null
}
---

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,20 @@
curl --location --request POST 'https://93.43.5.102/limsapi/api/odata/CommessaWeb(565085)/ImportaCommessa' \
--header 'Content-Type: application/json' \
--header 'Authorization: Bearer ••••••' \
--data '{
"IdUtente": 285
}'
RESPONSE:
{
"@odata.context": "https:\/\/bvcpsitaly-elims.com\/limsapi\/api\/odata\/$metadata#CommessaWeb\/$entity",
"IdCommessa": 565085,
"CodiceCommessa": "26C0089",
"RiferimentoCertificato": null,
"StatoCommessaWeb": "Elaborata",
"DataCreazioneWeb": "2026-03-19T10:03:18.653+01:00",
"CodiceCommessaWeb": "26C0089",
"DataInviatoWeb": "2026-03-19T10:03:30.603+01:00",
"Richiedente": "From TRFSmart Application",
"Descrizione": "From TRFSmart Application"
}

View File

@ -0,0 +1,48 @@
Photos for CommessaWeb 565085 (iddatadb=1279):
Total photos found: 2, campioni: 3
=== Campione 736899 (main) ===
curl --location --request POST 'https://93.43.5.102/limsapi/api/odata/Campione(736899)/UploadCampioneFile' \
--header 'Authorization: Bearer ••••••' \
--form 'file=@C:\xampp\htdocs\trf_certest\public\photostrf\1279-20260319090037-1e672dd9-5420-4432-b422-02d8d271c178.jpg' \
--form 'StampaNelRapporto=true' \
--form 'PrimaPagina=true'
RESPONSE:
{
"@odata.context": "https:\/\/bvcpsitaly-elims.com\/limsapi\/api\/odata\/$metadata#CampioneFiles\/$entity",
"IdCampioneFile": 1786602,
"FileName": "1279-20260319090037-1e672dd9-5420-4432-b422-02d8d271c178.jpg",
"Web": false,
"AllegaAlRapporto": false,
"StampaNelRapporto": false,
"ReportGermania": false,
"PrimaPagina": false,
"Duplica": false,
"LastUpdate": "2026-03-19T10:03:25.2757049+01:00",
"Titolo": null
}
---
curl --location --request POST 'https://93.43.5.102/limsapi/api/odata/Campione(736899)/UploadCampioneFile' \
--header 'Authorization: Bearer ••••••' \
--form 'file=@C:\xampp\htdocs\trf_certest\public\photostrf\1279-20260319090037-104a2ca5-8a4c-4df8-b2ca-a28f518b7b25.jpg' \
--form 'StampaNelRapporto=false' \
--form 'PrimaPagina=false'
RESPONSE:
{
"@odata.context": "https:\/\/bvcpsitaly-elims.com\/limsapi\/api\/odata\/$metadata#CampioneFiles\/$entity",
"IdCampioneFile": 1786603,
"FileName": "1279-20260319090037-104a2ca5-8a4c-4df8-b2ca-a28f518b7b25.jpg",
"Web": false,
"AllegaAlRapporto": false,
"StampaNelRapporto": false,
"ReportGermania": false,
"PrimaPagina": false,
"Duplica": false,
"LastUpdate": "2026-03-19T10:03:26.7566613+01:00",
"Titolo": null
}
---

View File

@ -0,0 +1,18 @@
curl --location --request POST 'https://93.43.5.102/limsapi/api/odata/CommessaWeb(565085)/InviaCommessa' \
--header 'Content-Type: application/json' \
--header 'Authorization: Bearer ••••••' \
--data '{}'
RESPONSE:
{
"@odata.context": "https:\/\/bvcpsitaly-elims.com\/limsapi\/api\/odata\/$metadata#CommessaWeb\/$entity",
"IdCommessa": 565085,
"CodiceCommessa": "26C0089",
"RiferimentoCertificato": null,
"StatoCommessaWeb": "Nuova",
"DataCreazioneWeb": "2026-03-19T10:03:18.653+01:00",
"CodiceCommessaWeb": "26C0089",
"DataInviatoWeb": "2026-03-19T10:03:30.6022436+01:00",
"Richiedente": "From TRFSmart Application",
"Descrizione": "From TRFSmart Application"
}

View File

@ -0,0 +1,226 @@
curl --location --request PATCH 'https://93.43.5.102/limsapi/api/odata/CommessaWeb(565085)' \
--header 'Content-Type: application/json' \
--header 'Authorization: Bearer ••••••' \
--data '{
"CommesseCustomFields": [
{
"IdCommesseCustomFields": 23328248,
"Valore": "18/03/2026"
},
{
"IdCommesseCustomFields": 23328249,
"Valore": "09:00"
},
{
"IdCommesseCustomFields": 23328250,
"Valore": "9299"
},
{
"IdCommesseCustomFields": 23328251,
"Valore": "17/03/2026"
},
{
"IdCommesseCustomFields": 23328252,
"Valore": "/"
},
{
"IdCommesseCustomFields": 23328253,
"Valore": "5"
},
{
"IdCommesseCustomFields": 23328259,
"Valore": "673"
},
{
"IdCommesseCustomFields": 23328260,
"Valore": "674"
},
{
"IdCommesseCustomFields": 23328261,
"Valore": "669"
},
{
"IdCommesseCustomFields": 23328263,
"Valore": "1469"
},
{
"IdCommesseCustomFields": 23328264,
"Valore": "1604"
},
{
"IdCommesseCustomFields": 23328265,
"Valore": "12875"
},
{
"IdCommesseCustomFields": 23328266,
"Valore": "8755"
},
{
"IdCommesseCustomFields": 23328267,
"Valore": ""
},
{
"IdCommesseCustomFields": 23328227,
"Valore": "13497"
},
{
"IdCommesseCustomFields": 23328228,
"Valore": "20262"
},
{
"IdCommesseCustomFields": 23328229,
"Valore": "ART. PEACH"
},
{
"IdCommesseCustomFields": 23328268,
"Valore": "1892"
},
{
"IdCommesseCustomFields": 23328269,
"Valore": ""
},
{
"IdCommesseCustomFields": 23328270,
"Valore": ""
},
{
"IdCommesseCustomFields": 23328271,
"Valore": "19/03/2026"
},
{
"IdCommesseCustomFields": 23328272,
"Valore": "18/03/2026"
},
{
"IdCommesseCustomFields": 23328273,
"Valore": ""
},
{
"IdCommesseCustomFields": 23328274,
"Valore": "09:00"
},
{
"IdCommesseCustomFields": 23328275,
"Valore": "09:03"
},
{
"IdCommesseCustomFields": 23328276,
"Valore": "09:58"
},
{
"IdCommesseCustomFields": 23328277,
"Valore": "19/03/2026"
},
{
"IdCommesseCustomFields": 23328278,
"Valore": ""
},
{
"IdCommesseCustomFields": 23328279,
"Valore": ""
},
{
"IdCommesseCustomFields": 23328280,
"Valore": ""
},
{
"IdCommesseCustomFields": 23328230,
"Valore": "L209B4M00110M7280"
},
{
"IdCommesseCustomFields": 23328231,
"Valore": "BLACK"
},
{
"IdCommesseCustomFields": 23328232,
"Valore": "PE007J"
},
{
"IdCommesseCustomFields": 23328233,
"Valore": "264"
},
{
"IdCommesseCustomFields": 23328234,
"Valore": "420"
},
{
"IdCommesseCustomFields": 23328235,
"Valore": "AAA"
},
{
"IdCommesseCustomFields": 23328236,
"Valore": "3055"
},
{
"IdCommesseCustomFields": 23328237,
"Valore": "AAA"
},
{
"IdCommesseCustomFields": 23328238,
"Valore": "AAA"
},
{
"IdCommesseCustomFields": 23328239,
"Valore": "P"
},
{
"IdCommesseCustomFields": 23328240,
"Valore": "RM-ACC"
},
{
"IdCommesseCustomFields": 23328241,
"Valore": "\u00a0CONCERIA M2 SRL"
},
{
"IdCommesseCustomFields": 23328242,
"Valore": "344"
},
{
"IdCommesseCustomFields": 23328243,
"Valore": "1208"
},
{
"IdCommesseCustomFields": 23328244,
"Valore": "AAA"
},
{
"IdCommesseCustomFields": 23328245,
"Valore": "AAA"
},
{
"IdCommesseCustomFields": 23328246,
"Valore": "Not provided"
},
{
"IdCommesseCustomFields": 23328247,
"Valore": "AAA"
},
{
"IdCommesseCustomFields": 23328254,
"Valore": "aaa"
},
{
"IdCommesseCustomFields": 23328255,
"Valore": "4678"
},
{
"IdCommesseCustomFields": 23328256,
"Valore": "Leather&amp;amp;Fur"
},
{
"IdCommesseCustomFields": 23328257,
"Valore": "278"
},
{
"IdCommesseCustomFields": 23328258,
"Valore": "2089"
},
{
"IdCommesseCustomFields": 23328262,
"Valore": "745"
}
]
}'
RESPONSE:
null

View File

@ -0,0 +1,29 @@
curl --location --request POST 'https://93.43.5.102/limsapi/api/odata/CommessaWeb' \
--header 'Content-Type: application/json' \
--header 'Authorization: Bearer ••••••' \
--data '{
"Cliente": 3378,
"SchemaCustomField": 82,
"Richiedente": "From TRFSmart Application",
"Descrizione": "From TRFSmart Application",
"ClienteResponsabile": 7586,
"MoltiplicatorePrezzo": 2,
"AnagraficaCertestObject": 8973,
"AnagraficaCertestService": 9007,
"ClienteFornitore": 4505,
"ClienteAnalisi": null
}'
RESPONSE:
{
"@odata.context": "https:\/\/bvcpsitaly-elims.com\/limsapi\/api\/odata\/$metadata#CommessaWeb\/$entity",
"IdCommessa": 565085,
"CodiceCommessa": "26C0089",
"RiferimentoCertificato": null,
"StatoCommessaWeb": "Nuova",
"DataCreazioneWeb": "2026-03-19T10:03:18.6527414+01:00",
"CodiceCommessaWeb": "26C0089",
"DataInviatoWeb": null,
"Richiedente": "From TRFSmart Application",
"Descrizione": "From TRFSmart Application"
}

View File

@ -414,6 +414,8 @@ $xlsHeaders = $template['xls_headers'] ? json_decode($template['xls_headers'], t
<?php elseif (in_array($fm['fixed_field_key'], [
'ClienteResponsabile',
'ClienteFornitore',
'ClienteAnalisi',
'MoltiplicatorePrezzo',
'AnagraficaCertestObject',
'AnagraficaCertestService'
@ -1230,6 +1232,8 @@ $xlsHeaders = $template['xls_headers'] ? json_decode($template['xls_headers'], t
const keysWithDropdown = [
'ClienteResponsabile',
'ClienteFornitore',
'ClienteAnalisi',
'MoltiplicatorePrezzo',
'AnagraficaCertestObject',
'AnagraficaCertestService'
@ -1443,15 +1447,25 @@ $xlsHeaders = $template['xls_headers'] ? json_decode($template['xls_headers'], t
const clientId = <?php echo (int)($template['idclient'] ?? 0); ?>;
const requestCache = {};
async function fetchJson(url) {
const r = await fetch(url);
const text = await r.text();
if (!r.ok) throw new Error(`HTTP ${r.status} on ${url}: ${text.slice(0, 200)}`);
try {
return JSON.parse(text);
} catch (e) {
throw new Error(`Invalid JSON from ${url}: ${text.slice(0, 200)}`);
if (requestCache[url]) {
return requestCache[url];
}
requestCache[url] = (async () => {
const r = await fetch(url);
const text = await r.text();
if (!r.ok) throw new Error(`HTTP ${r.status} on ${url}: ${text.slice(0, 200)}`);
try {
return JSON.parse(text);
} catch (e) {
throw new Error(`Invalid JSON from ${url}: ${text.slice(0, 200)}`);
}
})();
return requestCache[url];
}
function normalizeList(j, key) {
@ -1465,6 +1479,16 @@ $xlsHeaders = $template['xls_headers'] ? json_decode($template['xls_headers'], t
return [];
}
function formatClientLabel(x) {
const nome = x.Nominativo ?? x.Nome ?? x.name ?? 'Nome non disponibile';
const id = x.IdCliente ?? x.Id ?? x.id ?? '';
const codiceCliente = (x.CodiceCliente ?? x.codiceCliente ?? '').toString().trim();
const suffix = (codiceCliente.split('_')[1] || '').trim();
const shortCode = suffix || (codiceCliente ? codiceCliente.charAt(0) : '--');
return `${nome.trim()} - ${shortCode} (ID: ${id})`;
}
async function loadData(key) {
if (key === 'MoltiplicatorePrezzo') {
const j = await fetchJson('get_moltiplicatoreprezzo.php');
@ -1507,6 +1531,16 @@ $xlsHeaders = $template['xls_headers'] ? json_decode($template['xls_headers'], t
})).filter(x => x.id != null && x.label);
}
if (key === 'ClienteFornitore' || key === 'ClienteAnalisi') {
const j = await fetchJson('get_clienti.php');
const list = normalizeList(j, key);
return list.map(x => ({
id: x.IdCliente ?? x.Id ?? x.id,
label: formatClientLabel(x)
})).filter(x => x.id != null && x.label);
}
return [];
}

View File

@ -23,10 +23,11 @@ try {
// (NON tocchiamo MySQL, gestiamo solo qui la traduzione)
$fixedAliasMap = [
'ClienteResponsabile' => 'cliente_responsabile_id',
'ClienteFornitore' => 'cliente_fornitore_id',
'ClienteAnalisi' => 'clienteAnalisi',
'MoltiplicatorePrezzo' => 'moltiplicatore_prezzo_id',
'AnagraficaCertestObject' => 'anagrafica_certest_object_id',
'AnagraficaCertestService' => 'anagrafica_certest_service_id',
'ClienteFornitore' => 'cliente_fornitore_id',
'ConsegnaRichiesta' => 'consegna_richiesta',
];