Compare commits
10 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| dab8d9aebf | |||
| 375a10a678 | |||
| 15990be884 | |||
| c3a6dd73b6 | |||
| 44ed1186e0 | |||
| 9050cb1006 | |||
| e6820fdb62 | |||
| 5da37a7836 | |||
| c5f27cb69a | |||
| 1d81d6c996 |
@@ -47,6 +47,7 @@ yarn-error.log
|
||||
/public/userarea/class/curl_auth_debug.log
|
||||
/public/userarea/class/curl_request_debug.log
|
||||
/public/userarea/schema_dettagli_response.json
|
||||
public/userarea/schemi_base_response.json
|
||||
|
||||
# File XLSX temporanei importati
|
||||
/public/userarea/imported_trf/*.xlsx
|
||||
|
||||
@@ -431,7 +431,7 @@
|
||||
const emptyEl = modal.querySelector("#analysisEmptyBox");
|
||||
const errorEl = modal.querySelector("#analysisErrorBox");
|
||||
|
||||
const webOnly = webOnlyEl ? webOnlyEl.checked : false;
|
||||
const webOnly = true;
|
||||
const searchValue = searchEl ? searchEl.value.trim().toLowerCase() : "";
|
||||
|
||||
let visibleCount = 0;
|
||||
@@ -496,8 +496,10 @@
|
||||
emptyEl.classList.add("d-none");
|
||||
}
|
||||
|
||||
if (analysisLoadedCache[String(matrixId)]) {
|
||||
renderAnalysesList(analysisLoadedCache[String(matrixId)]);
|
||||
const cacheKey = String(matrixId) + "_WEB_ONLY";
|
||||
|
||||
if (analysisLoadedCache[cacheKey]) {
|
||||
renderAnalysesList(analysisLoadedCache[cacheKey]);
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -509,13 +511,21 @@
|
||||
dataType: "json",
|
||||
data: {
|
||||
id_matrice: matrixId,
|
||||
web_only: 1,
|
||||
},
|
||||
})
|
||||
.done(function (response) {
|
||||
const analyses = Array.isArray(response.value)
|
||||
? response.value
|
||||
? response.value.filter(function (item) {
|
||||
return (
|
||||
item.SelezionabileSuWeb === true ||
|
||||
item.SelezionabileSuWeb === 1 ||
|
||||
item.SelezionabileSuWeb === "1"
|
||||
);
|
||||
})
|
||||
: [];
|
||||
analysisLoadedCache[String(matrixId)] = analyses;
|
||||
|
||||
analysisLoadedCache[cacheKey] = analyses;
|
||||
renderAnalysesList(analyses);
|
||||
})
|
||||
.fail(function (xhr) {
|
||||
@@ -674,12 +684,7 @@
|
||||
});
|
||||
}
|
||||
|
||||
const webOnlyEl = modal.querySelector("#analysisWebOnly");
|
||||
if (webOnlyEl) {
|
||||
webOnlyEl.addEventListener("change", function () {
|
||||
filterAnalysisList();
|
||||
});
|
||||
}
|
||||
// WEB only is now fixed by default
|
||||
|
||||
const searchEl = modal.querySelector("#analysisSearchInput");
|
||||
if (searchEl) {
|
||||
|
||||
@@ -59,6 +59,49 @@ function formatDateToExport($value)
|
||||
return null; // Imposta null se non è una data valida
|
||||
}
|
||||
|
||||
// ImportaCommessa con retry: la chiamata è asincrona lato LIMS e a volte
|
||||
// risponde 200 senza importare (StatoCommessaWeb resta "Inviata"/"Nuova").
|
||||
// Riprova con backoff esponenziale finché non passa a "Elaborata".
|
||||
function importaCommessaWithRetry($api, $commessaId, array $payload, $maxRetries = 3, $initialBackoff = 1)
|
||||
{
|
||||
$result = null;
|
||||
$stato = null;
|
||||
$succeeded = false;
|
||||
$log = "";
|
||||
$backoff = $initialBackoff;
|
||||
|
||||
set_time_limit(120); // i backoff non devono far scadere il timeout della richiesta
|
||||
|
||||
for ($attempt = 1; $attempt <= $maxRetries + 1; $attempt++) {
|
||||
try {
|
||||
$result = $api->post("CommessaWeb({$commessaId})/ImportaCommessa", $payload);
|
||||
$stato = $result['StatoCommessaWeb'] ?? null;
|
||||
$log .= "Attempt {$attempt}: HTTP 200, StatoCommessaWeb=" . ($stato ?? 'null') . "\n";
|
||||
} catch (Exception $e) {
|
||||
$stato = null;
|
||||
$log .= "Attempt {$attempt}: EXCEPTION " . $e->getMessage() . "\n";
|
||||
}
|
||||
|
||||
if ($stato === 'Elaborata') {
|
||||
$succeeded = true;
|
||||
break;
|
||||
}
|
||||
|
||||
if ($attempt <= $maxRetries) {
|
||||
$log .= " -> not Elaborata, waiting {$backoff}s before retry\n";
|
||||
sleep($backoff);
|
||||
$backoff *= 2;
|
||||
}
|
||||
}
|
||||
|
||||
return [
|
||||
'succeeded' => $succeeded,
|
||||
'stato' => $stato,
|
||||
'result' => $result,
|
||||
'log' => $log,
|
||||
];
|
||||
}
|
||||
|
||||
try {
|
||||
$iddatadb = $_POST['iddatadb'] ?? null;
|
||||
if (!$iddatadb) {
|
||||
@@ -512,9 +555,8 @@ try {
|
||||
$writeLog($logFileStep9, $logContentStep9, "STEP 9 - InviaCommessa (commessa={$commessaId})");
|
||||
|
||||
|
||||
// 🔹 STEP 9.5: Importazione da CommessaWeb a Commessa (commentato come richiesto)
|
||||
// 🔹 STEP 9.5: Importazione da CommessaWeb a Commessa (con retry)
|
||||
// Supplier call: POST api/odata/CommessaWeb(XXX)/ImportaCommessa
|
||||
|
||||
$importUserId = (!empty($lims_global_user_id) && is_numeric($lims_global_user_id))
|
||||
? (int) $lims_global_user_id
|
||||
: 285;
|
||||
@@ -522,17 +564,23 @@ try {
|
||||
$importPayload = [
|
||||
"IdUtente" => $importUserId
|
||||
];
|
||||
$importResult = $api->post("CommessaWeb({$commessaId})/ImportaCommessa", $importPayload);
|
||||
|
||||
$importPayloadLog = json_encode($importPayload, JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES);
|
||||
// Logga il POST
|
||||
|
||||
$importOutcome = importaCommessaWithRetry($api, $commessaId, $importPayload);
|
||||
$importResult = $importOutcome['result'];
|
||||
$importStato = $importOutcome['stato'];
|
||||
$importSucceeded = $importOutcome['succeeded'];
|
||||
|
||||
// Logga il POST (tutti i tentativi)
|
||||
$logContentStep91 = "curl --location --request POST '{$apiBaseUrl}CommessaWeb({$commessaId})/ImportaCommessa' \\\n" .
|
||||
"--header 'Content-Type: application/json' \\\n" .
|
||||
"--header 'Authorization: Bearer ••••••' \\\n" .
|
||||
"--data '{$importPayloadLog}'\n\n" .
|
||||
"RESPONSE:\n" . json_encode($importResult, JSON_PRETTY_PRINT);
|
||||
"ATTEMPTS:\n" . $importOutcome['log'] . "\n" .
|
||||
"SUCCEEDED: " . ($importSucceeded ? 'yes' : 'NO') . "\n\n" .
|
||||
"LAST RESPONSE:\n" . json_encode($importResult, JSON_PRETTY_PRINT);
|
||||
$logFileStep91 = $logDir . "commessa_{$commessaId}_importa_step91_" . time() . ".txt";
|
||||
$writeLog($logFileStep91, $logContentStep91, "STEP 9.5 - ImportaCommessa (commessa={$commessaId})");
|
||||
$writeLog($logFileStep91, $logContentStep91, "STEP 9.5 - ImportaCommessa (commessa={$commessaId}, succeeded=" . ($importSucceeded ? '1' : '0') . ")");
|
||||
|
||||
// 🔹 STEP 10: GET di controllo post-PATCH
|
||||
$expand = "CommesseCustomFields(\$expand=CustomField)";
|
||||
|
||||
@@ -18,7 +18,15 @@ try {
|
||||
|
||||
$api = VisualLimsApiClient::getInstance();
|
||||
|
||||
$filter = rawurlencode("Matrice/IdMatrice eq $idMatrice");
|
||||
$webOnly = isset($_GET['web_only']) ? (int)$_GET['web_only'] : 1;
|
||||
|
||||
$filterString = "Matrice/IdMatrice eq $idMatrice";
|
||||
|
||||
if ($webOnly === 1) {
|
||||
$filterString .= " and SelezionabileSuWeb eq true";
|
||||
}
|
||||
|
||||
$filter = rawurlencode($filterString);
|
||||
$endpoint = "Analisi?\$filter={$filter}";
|
||||
|
||||
$base_url = 'https://93.43.5.102/limsapi/api/odata/';
|
||||
|
||||
@@ -20,9 +20,10 @@ $db = DBHandlerSelect::getInstance();
|
||||
$pdo = $db->getConnection();
|
||||
|
||||
// Recupera tutti i mapping dal template, includendo is_visible_import
|
||||
$stmt = $pdo->prepare("SELECT id, excel_column, data_type, is_required, manual_default, is_manual, field_label, field_id, main_field, is_visible_import, auto_value
|
||||
$stmt = $pdo->prepare("SELECT id, excel_column, data_type, is_required, manual_default, is_manual, field_label, field_id, field_order, main_field, is_visible_import, auto_value
|
||||
FROM template_mapping
|
||||
WHERE template_id = ?");
|
||||
WHERE template_id = ?
|
||||
ORDER BY field_order ASC, id ASC");
|
||||
$stmt->execute([$template_id]);
|
||||
$allMappings = $stmt->fetchAll(PDO::FETCH_ASSOC);
|
||||
|
||||
@@ -252,19 +253,28 @@ foreach ($importedData as $index => $row) {
|
||||
// Build columns in display order
|
||||
$gridColumns = [];
|
||||
|
||||
// 1. Main fields, maximum 2
|
||||
foreach ($mainFieldMappings as $mainMapping) {
|
||||
// 1. Main fields first, immediately after buttons
|
||||
foreach ($allMappings as $mapping) {
|
||||
if (
|
||||
(int)$mapping['is_visible_import'] === 1
|
||||
&& (string)$mapping['main_field'] === '1'
|
||||
&& trim((string)$mapping['field_label']) !== 'Tested Component:'
|
||||
) {
|
||||
$gridColumns[] = [
|
||||
'type' => 'main_field',
|
||||
'key' => (string)$mainMapping['id'],
|
||||
'label' => $mainMapping['field_label'],
|
||||
'dataType' => $mainMapping['data_type'],
|
||||
'isManual' => (bool)$mainMapping['is_manual'],
|
||||
'isRequired' => (bool)$mainMapping['is_required'],
|
||||
'fieldId' => $mainMapping['field_id'] ?? null,
|
||||
'key' => (string)$mapping['id'],
|
||||
'label' => $mapping['field_label'],
|
||||
'dataType' => $mapping['data_type'],
|
||||
'isManual' => (bool)$mapping['is_manual'],
|
||||
'isRequired' => (bool)$mapping['is_required'],
|
||||
'fieldId' => $mapping['field_id'] ?? null,
|
||||
'fieldOrder' => (int)($mapping['field_order'] ?? 9999),
|
||||
'manualDefault' => $mapping['manual_default'] ?? '',
|
||||
'autoValue' => $mapping['auto_value'] ?? 'none',
|
||||
'width' => 150,
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
// 2. Status
|
||||
$gridColumns[] = ['type' => 'status', 'key' => 'status', 'label' => 'Status', 'width' => 150, 'editable' => false];
|
||||
@@ -275,50 +285,30 @@ $gridColumns[] = ['type' => 'idclient', 'key' => 'idclient', 'label' => 'Client'
|
||||
// 4. Cliente Fornitore
|
||||
$gridColumns[] = ['type' => 'cliente_fornitore_id', 'key' => 'cliente_fornitore_id', 'label' => $slugMapping['ClienteFornitore'] ?? 'ClienteFornitore', 'width' => 300];
|
||||
|
||||
// 5. Auto fields
|
||||
// 5. Other custom fields in schema order
|
||||
foreach ($allMappings as $mapping) {
|
||||
if (
|
||||
!$mapping['is_manual']
|
||||
&& $mapping['main_field'] != 1
|
||||
&& $mapping['is_visible_import'] == 1
|
||||
(int)$mapping['is_visible_import'] === 1
|
||||
&& (string)$mapping['main_field'] !== '1'
|
||||
&& trim((string)$mapping['field_label']) !== 'Tested Component:'
|
||||
) {
|
||||
$isMainField = ((string)$mapping['main_field'] === '1');
|
||||
|
||||
$gridColumns[] = [
|
||||
'type' => 'detail',
|
||||
'type' => $isMainField ? 'main_field' : 'detail',
|
||||
'key' => (string)$mapping['id'],
|
||||
'label' => $mapping['field_label'],
|
||||
'dataType' => $mapping['data_type'],
|
||||
'isManual' => false,
|
||||
'isManual' => (bool)$mapping['is_manual'],
|
||||
'isRequired' => (bool)$mapping['is_required'],
|
||||
'fieldId' => $mapping['field_id'] ?? null,
|
||||
'fieldOrder' => (int)($mapping['field_order'] ?? 9999),
|
||||
'manualDefault' => $mapping['manual_default'] ?? '',
|
||||
'autoValue' => $mapping['auto_value'] ?? 'none',
|
||||
'width' => 150,
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
// 6. Manual fields
|
||||
foreach ($allMappings as $mapping) {
|
||||
if (
|
||||
$mapping['is_manual']
|
||||
&& $mapping['main_field'] != 1
|
||||
&& $mapping['is_visible_import'] == 1
|
||||
&& trim((string)$mapping['field_label']) !== 'Tested Component:'
|
||||
) {
|
||||
$gridColumns[] = [
|
||||
'type' => 'detail',
|
||||
'key' => (string)$mapping['id'],
|
||||
'label' => $mapping['field_label'],
|
||||
'dataType' => $mapping['data_type'],
|
||||
'isManual' => true,
|
||||
'isRequired' => (bool)$mapping['is_required'],
|
||||
'fieldId' => $mapping['field_id'] ?? null,
|
||||
'manualDefault' => $mapping['manual_default'] ?? '',
|
||||
'width' => 150,
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
// 7. Tested Component
|
||||
$gridColumns[] = ['type' => 'tested_component', 'key' => 'tested_component', 'label' => 'Tested Component', 'width' => 150];
|
||||
|
||||
|
||||
@@ -58,6 +58,7 @@ $stmt = $pdo->prepare("
|
||||
SELECT
|
||||
id,
|
||||
field_id,
|
||||
field_order,
|
||||
excel_column,
|
||||
json_node,
|
||||
is_manual,
|
||||
@@ -79,6 +80,7 @@ $stmt = $pdo->prepare("
|
||||
is_visible_parts
|
||||
FROM template_mapping
|
||||
WHERE template_id = ?
|
||||
ORDER BY field_order ASC, id ASC
|
||||
");
|
||||
$stmt->execute([$id]);
|
||||
$mappings = $stmt->fetchAll(PDO::FETCH_ASSOC);
|
||||
@@ -205,28 +207,37 @@ $apiSampleJson = $template['api_sample_json'] ?? '';
|
||||
}
|
||||
|
||||
/* Type */
|
||||
/* Order */
|
||||
#schemaFieldsTable th:nth-child(5),
|
||||
#schemaFieldsTable td:nth-child(5) {
|
||||
width: 70px;
|
||||
text-align: center;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
/* Type */
|
||||
#schemaFieldsTable th:nth-child(6),
|
||||
#schemaFieldsTable td:nth-child(6) {
|
||||
width: 120px;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
/* Mapping = wide but NOT insane */
|
||||
#schemaFieldsTable th:nth-child(6),
|
||||
#schemaFieldsTable td:nth-child(6) {
|
||||
#schemaFieldsTable th:nth-child(7),
|
||||
#schemaFieldsTable td:nth-child(7) {
|
||||
width: 380px;
|
||||
}
|
||||
|
||||
/* Default Value = wider */
|
||||
#schemaFieldsTable th:nth-child(7),
|
||||
#schemaFieldsTable td:nth-child(7) {
|
||||
#schemaFieldsTable th:nth-child(8),
|
||||
#schemaFieldsTable td:nth-child(8) {
|
||||
width: 320px;
|
||||
}
|
||||
|
||||
/* selects fill the cell */
|
||||
#schemaFieldsTable td:nth-child(6) .form-select,
|
||||
#schemaFieldsTable td:nth-child(7) .form-control,
|
||||
#schemaFieldsTable td:nth-child(7) .form-select {
|
||||
#schemaFieldsTable td:nth-child(7) .form-select,
|
||||
#schemaFieldsTable td:nth-child(8) .form-control,
|
||||
#schemaFieldsTable td:nth-child(8) .form-select {
|
||||
width: 100% !important;
|
||||
}
|
||||
|
||||
@@ -254,6 +265,47 @@ $apiSampleJson = $template['api_sample_json'] ?? '';
|
||||
font-family: Consolas, Monaco, monospace;
|
||||
font-size: 13px;
|
||||
}
|
||||
|
||||
.select2-container--default .select2-results__option {
|
||||
white-space: nowrap;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
max-width: 520px;
|
||||
}
|
||||
|
||||
.select2-json-node {
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
.select2-json-value {
|
||||
color: #6c757d;
|
||||
font-size: 12px;
|
||||
margin-left: 8px;
|
||||
}
|
||||
|
||||
.select2-json-row {
|
||||
display: block;
|
||||
margin: -6px;
|
||||
padding: 6px;
|
||||
}
|
||||
|
||||
.select2-results__option:has(.select2-json-row.used-option) {
|
||||
background-color: #fff3cd !important;
|
||||
color: #856404 !important;
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
.select2-results__option:has(.select2-json-row.used-option).select2-results__option--highlighted {
|
||||
background-color: #ffe69c !important;
|
||||
color: #856404 !important;
|
||||
}
|
||||
|
||||
.select2-json-row.used-option .select2-json-node::after {
|
||||
content: " (already used)";
|
||||
color: #856404;
|
||||
font-size: 12px;
|
||||
font-weight: 600;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
|
||||
@@ -347,6 +399,7 @@ $apiSampleJson = $template['api_sample_json'] ?? '';
|
||||
<th style="width:45px; text-align:center;">Import</th>
|
||||
<th style="width:45px; text-align:center;">Parts</th>
|
||||
<th style="width:320px;">Title</th>
|
||||
<th style="width:70px; text-align:center;">Order</th>
|
||||
<th style="width:120px;">Type</th>
|
||||
<th><?php echo $sourceType === 'API' ? 'JSON Mapping' : 'Mapping'; ?></th>
|
||||
<th>Default Value</th>
|
||||
@@ -383,6 +436,12 @@ $apiSampleJson = $template['api_sample_json'] ?? '';
|
||||
<?php endif; ?>
|
||||
</td>
|
||||
|
||||
<td class="text-center">
|
||||
<?php echo (int)($mapping['field_order'] ?? 9999); ?>
|
||||
</td>
|
||||
|
||||
<td><?php echo htmlspecialchars($mapping['data_type'] ?? 'N/A'); ?></td>
|
||||
|
||||
<td><?php echo htmlspecialchars($mapping['data_type'] ?? 'N/A'); ?></td>
|
||||
|
||||
<td>
|
||||
@@ -1000,6 +1059,42 @@ $apiSampleJson = $template['api_sample_json'] ?? '';
|
||||
return escapeHtmlText(str);
|
||||
}
|
||||
|
||||
let jsonNodeLabels = {};
|
||||
|
||||
function getLastJsonNodeName(path) {
|
||||
return String(path || '')
|
||||
.replace(/\[\]/g, '')
|
||||
.split('.')
|
||||
.pop();
|
||||
}
|
||||
|
||||
function formatJsonSampleValue(value) {
|
||||
if (value === null || value === undefined) return '';
|
||||
|
||||
let text = '';
|
||||
|
||||
if (Array.isArray(value)) {
|
||||
text = '[Array]';
|
||||
} else if (typeof value === 'object') {
|
||||
text = '{Object}';
|
||||
} else {
|
||||
text = String(value);
|
||||
}
|
||||
|
||||
text = text
|
||||
.replace(/[\r\n\t]+/g, ' ')
|
||||
.replace(/\s+/g, ' ')
|
||||
.trim();
|
||||
|
||||
if (text === '/' || text === '\\/') return '';
|
||||
|
||||
if (text.length > 38) {
|
||||
text = text.substring(0, 38) + '...';
|
||||
}
|
||||
|
||||
return text;
|
||||
}
|
||||
|
||||
function flattenJsonNodes(obj, prefix = '') {
|
||||
let nodes = [];
|
||||
|
||||
@@ -1018,6 +1113,14 @@ $apiSampleJson = $template['api_sample_json'] ?? '';
|
||||
if (value !== null && typeof value === 'object') {
|
||||
nodes = nodes.concat(flattenJsonNodes(value, path));
|
||||
} else {
|
||||
const sample = formatJsonSampleValue(value);
|
||||
const shortName = getLastJsonNodeName(path);
|
||||
|
||||
jsonNodeLabels[path] = {
|
||||
shortName: shortName,
|
||||
sample: sample
|
||||
};
|
||||
|
||||
nodes.push(path);
|
||||
}
|
||||
});
|
||||
@@ -1043,18 +1146,128 @@ $apiSampleJson = $template['api_sample_json'] ?? '';
|
||||
if (!clean) return '';
|
||||
|
||||
const isUsed = uniqueUsedNodes.includes(clean) && clean !== currentValue;
|
||||
const label = isUsed ? `⚠ ${clean} (already used)` : clean;
|
||||
const info = jsonNodeLabels[clean] || {};
|
||||
const shortName = info.shortName || getLastJsonNodeName(clean);
|
||||
const sample = info.sample || '';
|
||||
|
||||
let label = shortName;
|
||||
if (sample) {
|
||||
label += ` — ${sample}`;
|
||||
}
|
||||
|
||||
if (isUsed) {
|
||||
label = `⚠ ${label} (already used)`;
|
||||
}
|
||||
|
||||
const isSelected = clean === currentValue ? 'selected' : '';
|
||||
|
||||
return `<option value="${escapeHtmlAttr(clean)}" class="${isUsed ? 'used-option' : ''}" ${isSelected}>${escapeHtmlText(label)}</option>`;
|
||||
return `
|
||||
<option
|
||||
value="${escapeHtmlAttr(clean)}"
|
||||
data-short-name="${escapeHtmlAttr(shortName)}"
|
||||
data-sample="${escapeHtmlAttr(sample)}"
|
||||
data-full-path="${escapeHtmlAttr(clean)}"
|
||||
class="${isUsed ? 'used-option' : ''}"
|
||||
data-used="${isUsed ? '1' : '0'}"
|
||||
${isSelected}>
|
||||
${escapeHtmlText(label)}
|
||||
</option>
|
||||
`;
|
||||
})
|
||||
.join('');
|
||||
|
||||
select.innerHTML = '<option value="">Select JSON Node</option>' + options;
|
||||
select.dataset.currentJson = currentValue;
|
||||
});
|
||||
|
||||
initSelect2ForJsonDropdowns();
|
||||
}
|
||||
|
||||
function initSelect2ForJsonDropdowns() {
|
||||
if (!(window.jQuery && $.fn.select2)) return;
|
||||
|
||||
$('.json-nodes').filter(function() {
|
||||
const tr = this.closest('tr');
|
||||
const mappingSelect = tr ? tr.querySelector('.mapping-select') : null;
|
||||
return mappingSelect && mappingSelect.value === 'json';
|
||||
}).each(function() {
|
||||
const $el = $(this);
|
||||
if (this.style.display === 'none') {
|
||||
return;
|
||||
}
|
||||
if ($el.hasClass('select2-hidden-accessible')) {
|
||||
$el.select2('destroy');
|
||||
}
|
||||
|
||||
$el.select2({
|
||||
width: '100%',
|
||||
placeholder: 'Select JSON Node',
|
||||
allowClear: true,
|
||||
dropdownAutoWidth: false,
|
||||
|
||||
templateResult: function(data) {
|
||||
if (!data.id) return data.text;
|
||||
|
||||
const option = data.element;
|
||||
const shortName = option.getAttribute('data-short-name') || data.text;
|
||||
const sample = option.getAttribute('data-sample') || '';
|
||||
const isUsed = option.getAttribute('data-used') === '1';
|
||||
|
||||
const $row = $('<span class="select2-json-row"></span>');
|
||||
if (isUsed) {
|
||||
$row.addClass('used-option');
|
||||
}
|
||||
$row.append(`<span class="select2-json-node">${escapeHtmlText(shortName)}</span>`);
|
||||
|
||||
if (sample) {
|
||||
$row.append(`<span class="select2-json-value">${escapeHtmlText(sample)}</span>`);
|
||||
}
|
||||
|
||||
return $row;
|
||||
},
|
||||
|
||||
templateSelection: function(data) {
|
||||
if (!data.id) return data.text;
|
||||
|
||||
const option = data.element;
|
||||
const shortName = option.getAttribute('data-short-name') || data.text;
|
||||
const sample = option.getAttribute('data-sample') || '';
|
||||
|
||||
if (sample) {
|
||||
return `${shortName} — ${sample}`;
|
||||
}
|
||||
|
||||
return shortName;
|
||||
},
|
||||
|
||||
matcher: function(params, data) {
|
||||
if ($.trim(params.term) === '') {
|
||||
return data;
|
||||
}
|
||||
|
||||
const term = params.term.toLowerCase();
|
||||
const option = data.element;
|
||||
|
||||
const fullPath = option?.getAttribute('data-full-path')?.toLowerCase() || '';
|
||||
const shortName = option?.getAttribute('data-short-name')?.toLowerCase() || '';
|
||||
const sample = option?.getAttribute('data-sample')?.toLowerCase() || '';
|
||||
|
||||
if (
|
||||
fullPath.includes(term) ||
|
||||
shortName.includes(term) ||
|
||||
sample.includes(term)
|
||||
) {
|
||||
return data;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
|
||||
function saveJsonNodes(sampleJson, nodes) {
|
||||
return fetch('update_api_json_nodes.php', {
|
||||
method: 'POST',
|
||||
@@ -1445,25 +1658,38 @@ $apiSampleJson = $template['api_sample_json'] ?? '';
|
||||
document.getElementById('updateSchemaButton').addEventListener('click', updateSchemaDetails);
|
||||
|
||||
document.getElementById('schemaFieldsBody').addEventListener('change', function(event) {
|
||||
if (!event.target.classList.contains('mapping-select')) return;
|
||||
|
||||
if (event.target.classList.contains('mapping-select')) {
|
||||
let tr = event.target.closest('tr');
|
||||
let mappingId = event.target.getAttribute('data-id');
|
||||
const mappingSelect = event.target;
|
||||
const tr = mappingSelect.closest('tr');
|
||||
const mappingId = mappingSelect.getAttribute('data-id');
|
||||
|
||||
let xlsSelect = tr.querySelector('.xls-columns');
|
||||
let jsonSelect = tr.querySelector('.json-nodes');
|
||||
let manualInput = tr.querySelector('.manual-default');
|
||||
let autoSelect = tr.querySelector('.auto-value-select');
|
||||
const xlsSelect = tr.querySelector('.xls-columns');
|
||||
const jsonSelect = tr.querySelector('.json-nodes');
|
||||
const manualInput = tr.querySelector('.manual-default');
|
||||
const autoSelect = tr.querySelector('.auto-value-select');
|
||||
|
||||
let mappedColumn = tr.querySelector('.mapped-column');
|
||||
let mappedJsonNode = tr.querySelector('.mapped-json-node');
|
||||
const mappedColumn = tr.querySelector('.mapped-column');
|
||||
const mappedJsonNode = tr.querySelector('.mapped-json-node');
|
||||
|
||||
let removeBtn = tr.querySelector('.remove-xls');
|
||||
let removeJsonBtn = tr.querySelector('.remove-json');
|
||||
const removeBtn = tr.querySelector('.remove-xls');
|
||||
const removeJsonBtn = tr.querySelector('.remove-json');
|
||||
|
||||
if (event.target.value === 'xls') {
|
||||
function destroyJsonSelect2() {
|
||||
if (jsonSelect && window.jQuery && $(jsonSelect).hasClass('select2-hidden-accessible')) {
|
||||
$(jsonSelect).select2('destroy');
|
||||
}
|
||||
}
|
||||
|
||||
if (mappingSelect.value === 'xls') {
|
||||
if (xlsSelect) xlsSelect.style.display = 'block';
|
||||
if (jsonSelect) jsonSelect.style.display = 'none';
|
||||
|
||||
destroyJsonSelect2();
|
||||
if (jsonSelect) {
|
||||
jsonSelect.style.display = 'none';
|
||||
jsonSelect.value = '';
|
||||
}
|
||||
|
||||
if (autoSelect) autoSelect.style.display = 'none';
|
||||
|
||||
if (manualInput) {
|
||||
@@ -1477,9 +1703,16 @@ $apiSampleJson = $template['api_sample_json'] ?? '';
|
||||
if (removeBtn) removeBtn.style.display = xlsSelect && xlsSelect.value ? 'inline-block' : 'none';
|
||||
if (removeJsonBtn) removeJsonBtn.style.display = 'none';
|
||||
|
||||
} else if (event.target.value === 'json') {
|
||||
if (xlsSelect) xlsSelect.style.display = 'none';
|
||||
if (jsonSelect) jsonSelect.style.display = 'block';
|
||||
} else if (mappingSelect.value === 'json') {
|
||||
if (xlsSelect) {
|
||||
xlsSelect.style.display = 'none';
|
||||
xlsSelect.value = '';
|
||||
}
|
||||
|
||||
if (jsonSelect) {
|
||||
jsonSelect.style.display = 'block';
|
||||
}
|
||||
|
||||
if (autoSelect) autoSelect.style.display = 'none';
|
||||
|
||||
if (manualInput) {
|
||||
@@ -1493,9 +1726,17 @@ $apiSampleJson = $template['api_sample_json'] ?? '';
|
||||
if (removeBtn) removeBtn.style.display = 'none';
|
||||
if (removeJsonBtn) removeJsonBtn.style.display = jsonSelect && jsonSelect.value ? 'inline-block' : 'none';
|
||||
|
||||
} else if (event.target.value === 'manual') {
|
||||
updateJsonDropdowns();
|
||||
|
||||
} else if (mappingSelect.value === 'manual') {
|
||||
if (xlsSelect) xlsSelect.style.display = 'none';
|
||||
if (jsonSelect) jsonSelect.style.display = 'none';
|
||||
|
||||
destroyJsonSelect2();
|
||||
if (jsonSelect) {
|
||||
jsonSelect.style.display = 'none';
|
||||
jsonSelect.value = '';
|
||||
}
|
||||
|
||||
if (autoSelect) autoSelect.style.display = 'none';
|
||||
|
||||
if (manualInput) {
|
||||
@@ -1508,12 +1749,13 @@ $apiSampleJson = $template['api_sample_json'] ?? '';
|
||||
if (removeBtn) removeBtn.style.display = 'none';
|
||||
if (removeJsonBtn) removeJsonBtn.style.display = 'none';
|
||||
|
||||
} else if (event.target.value === 'auto') {
|
||||
} else if (mappingSelect.value === 'auto') {
|
||||
if (xlsSelect) {
|
||||
xlsSelect.style.display = 'none';
|
||||
xlsSelect.value = '';
|
||||
}
|
||||
|
||||
destroyJsonSelect2();
|
||||
if (jsonSelect) {
|
||||
jsonSelect.style.display = 'none';
|
||||
jsonSelect.value = '';
|
||||
@@ -1534,7 +1776,13 @@ $apiSampleJson = $template['api_sample_json'] ?? '';
|
||||
|
||||
} else {
|
||||
if (xlsSelect) xlsSelect.style.display = 'none';
|
||||
if (jsonSelect) jsonSelect.style.display = 'none';
|
||||
|
||||
destroyJsonSelect2();
|
||||
if (jsonSelect) {
|
||||
jsonSelect.style.display = 'none';
|
||||
jsonSelect.value = '';
|
||||
}
|
||||
|
||||
if (autoSelect) autoSelect.style.display = 'none';
|
||||
|
||||
if (manualInput) {
|
||||
@@ -1551,7 +1799,7 @@ $apiSampleJson = $template['api_sample_json'] ?? '';
|
||||
|
||||
saveMapping(
|
||||
mappingId,
|
||||
event.target.value,
|
||||
mappingSelect.value,
|
||||
manualInput ? manualInput.value : '',
|
||||
xlsSelect ? xlsSelect.value : null,
|
||||
autoSelect ? autoSelect.value : null,
|
||||
@@ -1559,111 +1807,67 @@ $apiSampleJson = $template['api_sample_json'] ?? '';
|
||||
);
|
||||
|
||||
if (sourceType === 'XLS') updateXlsDropdowns();
|
||||
if (sourceType === 'API') updateJsonDropdowns();
|
||||
});
|
||||
|
||||
function saveJsonNodeSelection(jsonSelect) {
|
||||
if (!jsonSelect) return;
|
||||
|
||||
let tr = jsonSelect.closest('tr');
|
||||
let mappingId = jsonSelect.getAttribute('data-id');
|
||||
let manualInput = tr.querySelector('.manual-default');
|
||||
let mappedJsonNode = tr.querySelector('.mapped-json-node');
|
||||
let removeJsonBtn = tr.querySelector('.remove-json');
|
||||
let mappingSelect = tr.querySelector('.mapping-select');
|
||||
|
||||
if (mappingSelect && mappingSelect.value !== 'json') {
|
||||
return;
|
||||
}
|
||||
|
||||
if (event.target.classList.contains('main-field-checkbox')) {
|
||||
const checkbox = event.target;
|
||||
const mappingId = checkbox.dataset.mappingId;
|
||||
const value = checkbox.checked ? 1 : 0;
|
||||
|
||||
// Count only the other Main fields already checked in this table
|
||||
const otherCheckedMainFields = Array.from(
|
||||
document.querySelectorAll('#schemaFieldsBody .main-field-checkbox')
|
||||
).filter(cb => cb !== checkbox && cb.checked);
|
||||
|
||||
// If I am checking this one, I can have max 2 total:
|
||||
// this checkbox + max 1 other already checked
|
||||
if (checkbox.checked && otherCheckedMainFields.length >= 2) {
|
||||
checkbox.checked = false;
|
||||
alert('Puoi selezionare al massimo 2 campi Main.');
|
||||
return;
|
||||
if (!mappedJsonNode) {
|
||||
mappedJsonNode = document.createElement('span');
|
||||
mappedJsonNode.className = 'mapped-json-node';
|
||||
mappedJsonNode.style.marginLeft = '5px';
|
||||
tr.querySelector('td:nth-child(7)').appendChild(mappedJsonNode);
|
||||
}
|
||||
|
||||
fetch('update_main_field.php', {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'application/json'
|
||||
},
|
||||
body: JSON.stringify({
|
||||
template_id: <?php echo $id; ?>,
|
||||
mapping_id: mappingId,
|
||||
value: value
|
||||
})
|
||||
})
|
||||
.then(response => response.json())
|
||||
.then(data => {
|
||||
if (!data.success) {
|
||||
console.error("❌ Error updating main_field:", data.message);
|
||||
|
||||
document.querySelectorAll('#schemaFieldsBody .main-field-checkbox').forEach(cb => {
|
||||
cb.checked = cb.dataset.originalChecked === 'true';
|
||||
});
|
||||
|
||||
alert(data.message || 'Errore durante il salvataggio del campo Main.');
|
||||
return;
|
||||
if (!removeJsonBtn) {
|
||||
removeJsonBtn = document.createElement('button');
|
||||
removeJsonBtn.className = 'btn btn-danger btn-sm remove-json';
|
||||
removeJsonBtn.textContent = 'X';
|
||||
removeJsonBtn.style.marginLeft = '5px';
|
||||
removeJsonBtn.setAttribute('data-id', mappingId);
|
||||
tr.querySelector('td:nth-child(7)').appendChild(removeJsonBtn);
|
||||
}
|
||||
|
||||
document.querySelectorAll('#schemaFieldsBody .main-field-checkbox').forEach(cb => {
|
||||
cb.dataset.originalChecked = cb.checked ? 'true' : 'false';
|
||||
});
|
||||
})
|
||||
.catch(error => {
|
||||
console.error("❌ Fetch error:", error);
|
||||
mappedJsonNode.textContent = jsonSelect.value ? `(${jsonSelect.value})` : '';
|
||||
mappedJsonNode.style.display = jsonSelect.value ? 'inline' : 'none';
|
||||
removeJsonBtn.style.display = jsonSelect.value ? 'inline-block' : 'none';
|
||||
|
||||
document.querySelectorAll('#schemaFieldsBody .main-field-checkbox').forEach(cb => {
|
||||
cb.checked = cb.dataset.originalChecked === 'true';
|
||||
console.log('[JSON NODE SAVE]', {
|
||||
mappingId: mappingId,
|
||||
value: jsonSelect.value
|
||||
});
|
||||
|
||||
alert('Errore di comunicazione durante il salvataggio del campo Main.');
|
||||
});
|
||||
|
||||
return;
|
||||
saveMapping(
|
||||
mappingId,
|
||||
'json',
|
||||
manualInput ? manualInput.value : '',
|
||||
null,
|
||||
null,
|
||||
jsonSelect.value
|
||||
);
|
||||
}
|
||||
|
||||
if (event.target.classList.contains('visible-parts-checkbox')) {
|
||||
const checkbox = event.target;
|
||||
const mappingId = checkbox.dataset.mappingId;
|
||||
const value = checkbox.checked ? 1 : 0;
|
||||
const prevChecked = checkbox.checked;
|
||||
|
||||
if (value === 1) {
|
||||
document.querySelectorAll('.visible-parts-checkbox').forEach(cb => {
|
||||
if (cb !== checkbox) cb.checked = false;
|
||||
});
|
||||
}
|
||||
|
||||
fetch('update_visible_parts.php', {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'application/json'
|
||||
},
|
||||
body: JSON.stringify({
|
||||
template_id: <?php echo $id; ?>,
|
||||
mapping_id: mappingId,
|
||||
value: value
|
||||
})
|
||||
})
|
||||
.then(r => r.json())
|
||||
.then(data => {
|
||||
if (!data.success) {
|
||||
console.error("❌ Error updating is_visible_parts:", data.message);
|
||||
checkbox.checked = !prevChecked;
|
||||
location.reload();
|
||||
}
|
||||
})
|
||||
.catch(error => {
|
||||
console.error("❌ Fetch error:", error);
|
||||
checkbox.checked = !prevChecked;
|
||||
location.reload();
|
||||
document.getElementById('schemaFieldsBody').addEventListener('change', function(event) {
|
||||
if (!event.target.classList.contains('json-nodes')) return;
|
||||
saveJsonNodeSelection(event.target);
|
||||
});
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
if (window.jQuery) {
|
||||
$(document).on('select2:select select2:clear', 'select.json-nodes', function() {
|
||||
saveJsonNodeSelection(this);
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
// Save original Main checkbox state
|
||||
@@ -1705,7 +1909,7 @@ $apiSampleJson = $template['api_sample_json'] ?? '';
|
||||
mappedColumn = document.createElement('span');
|
||||
mappedColumn.className = 'mapped-column';
|
||||
mappedColumn.style.marginLeft = '5px';
|
||||
tr.querySelector('td:nth-child(6)').appendChild(mappedColumn);
|
||||
tr.querySelector('td:nth-child(7)').appendChild(mappedColumn);
|
||||
}
|
||||
if (!removeBtn) {
|
||||
removeBtn = document.createElement('button');
|
||||
@@ -1713,7 +1917,7 @@ $apiSampleJson = $template['api_sample_json'] ?? '';
|
||||
removeBtn.textContent = 'X';
|
||||
removeBtn.style.marginLeft = '5px';
|
||||
removeBtn.setAttribute('data-id', mappingId);
|
||||
tr.querySelector('td:nth-child(6)').appendChild(removeBtn);
|
||||
tr.querySelector('td:nth-child(7)').appendChild(removeBtn);
|
||||
|
||||
removeBtn.addEventListener('click', function(e) {
|
||||
let tr = e.target.closest('tr');
|
||||
@@ -1747,51 +1951,6 @@ $apiSampleJson = $template['api_sample_json'] ?? '';
|
||||
}
|
||||
});
|
||||
|
||||
document.getElementById('schemaFieldsBody').addEventListener('change', function(event) {
|
||||
if (event.target.classList.contains('json-nodes')) {
|
||||
let tr = event.target.closest('tr');
|
||||
let mappingId = event.target.getAttribute('data-id');
|
||||
let manualInput = tr.querySelector('.manual-default');
|
||||
let mappedJsonNode = tr.querySelector('.mapped-json-node');
|
||||
let removeJsonBtn = tr.querySelector('.remove-json');
|
||||
|
||||
if (!mappedJsonNode) {
|
||||
mappedJsonNode = document.createElement('span');
|
||||
mappedJsonNode.className = 'mapped-json-node';
|
||||
mappedJsonNode.style.marginLeft = '5px';
|
||||
tr.querySelector('td:nth-child(6)').appendChild(mappedJsonNode);
|
||||
}
|
||||
|
||||
if (!removeJsonBtn) {
|
||||
removeJsonBtn = document.createElement('button');
|
||||
removeJsonBtn.className = 'btn btn-danger btn-sm remove-json';
|
||||
removeJsonBtn.textContent = 'X';
|
||||
removeJsonBtn.style.marginLeft = '5px';
|
||||
removeJsonBtn.setAttribute('data-id', mappingId);
|
||||
tr.querySelector('td:nth-child(6)').appendChild(removeJsonBtn);
|
||||
}
|
||||
|
||||
mappedJsonNode.textContent = event.target.value ? `(${event.target.value})` : '';
|
||||
mappedJsonNode.style.display = event.target.value ? 'inline' : 'none';
|
||||
removeJsonBtn.style.display = event.target.value ? 'inline-block' : 'none';
|
||||
|
||||
const mappingSelect = tr.querySelector('.mapping-select');
|
||||
if (mappingSelect && mappingSelect.value !== 'json') {
|
||||
return;
|
||||
}
|
||||
|
||||
saveMapping(
|
||||
mappingId,
|
||||
'json',
|
||||
manualInput ? manualInput.value : '',
|
||||
null,
|
||||
null,
|
||||
event.target.value
|
||||
);
|
||||
|
||||
updateJsonDropdowns();
|
||||
}
|
||||
});
|
||||
|
||||
document.getElementById('schemaFieldsBody').addEventListener('change', function(event) {
|
||||
if (event.target.classList.contains('manual-default') && event.target.tagName === 'SELECT') {
|
||||
@@ -1918,9 +2077,11 @@ $apiSampleJson = $template['api_sample_json'] ?? '';
|
||||
updateXlsDropdowns();
|
||||
}
|
||||
|
||||
if (data.success && mappingType === 'json' && jsonNode) {
|
||||
usedJsonNodesFromDB = usedJsonNodesFromDB.filter(node => node !== jsonNode);
|
||||
usedJsonNodesFromDB.push(jsonNode);
|
||||
if (data.success && mappingType === 'json') {
|
||||
usedJsonNodesFromDB = Array.from(document.querySelectorAll('select.json-nodes'))
|
||||
.map(select => select.value || select.dataset.currentJson || '')
|
||||
.filter(Boolean);
|
||||
|
||||
updateJsonDropdowns();
|
||||
}
|
||||
})
|
||||
@@ -2155,6 +2316,17 @@ $apiSampleJson = $template['api_sample_json'] ?? '';
|
||||
}
|
||||
|
||||
if (sourceType === 'API' && availableJsonNodes.length) {
|
||||
const rawJson = document.getElementById('apiJsonExample')?.value || '';
|
||||
|
||||
if (rawJson.trim()) {
|
||||
try {
|
||||
jsonNodeLabels = {};
|
||||
flattenJsonNodes(JSON.parse(rawJson));
|
||||
} catch (e) {
|
||||
console.warn('Cannot rebuild JSON labels from sample JSON', e);
|
||||
}
|
||||
}
|
||||
|
||||
updateJsonDropdowns();
|
||||
}
|
||||
|
||||
|
||||
@@ -259,11 +259,9 @@ $matrixGroups = array_values($matrixGroups);
|
||||
</div>
|
||||
|
||||
<div class="d-flex flex-wrap align-items-center gap-2 mb-3">
|
||||
<div class="form-check m-0">
|
||||
<input class="form-check-input" type="checkbox" id="analysisWebOnly">
|
||||
<label class="form-check-label small" for="analysisWebOnly">
|
||||
Web only
|
||||
</label>
|
||||
<input type="hidden" id="analysisWebOnly" value="1">
|
||||
<div class="small text-success fw-semibold">
|
||||
Showing WEB analyses only
|
||||
</div>
|
||||
|
||||
<div class="flex-grow-1" style="min-width: 220px;">
|
||||
|
||||
@@ -0,0 +1,71 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* Routine: burberry
|
||||
*
|
||||
* Purpose:
|
||||
* For each imported XLS row:
|
||||
* - read the value from column S
|
||||
* - read the value from column T
|
||||
* - merge the values
|
||||
* - save the final value into column S
|
||||
*
|
||||
* Target:
|
||||
* Column S must be mapped to the destination field in the template mapping.
|
||||
*/
|
||||
|
||||
function applyRoutine(&$excelData, $routineData = [])
|
||||
{
|
||||
/*
|
||||
* This routine does not require external routine data.
|
||||
* Columns are fixed.
|
||||
*
|
||||
* Excel column indexes are zero-based:
|
||||
*
|
||||
* S = 18
|
||||
* T = 19
|
||||
*/
|
||||
$targetColumnIndex = 18; // S
|
||||
|
||||
$columnSIndex = 18; // S
|
||||
$columnTIndex = 19; // T
|
||||
|
||||
foreach ($excelData as $rowIndex => &$row) {
|
||||
if (!isset($row['data']) || !is_array($row['data'])) {
|
||||
error_log("Routine burberry: invalid row structure at index {$rowIndex}.");
|
||||
continue;
|
||||
}
|
||||
|
||||
$valueS = trim((string)($row['data'][$columnSIndex] ?? ''));
|
||||
$valueT = trim((string)($row['data'][$columnTIndex] ?? ''));
|
||||
|
||||
/*
|
||||
* Merge values, ignoring empty values.
|
||||
*/
|
||||
$mergedValues = [];
|
||||
|
||||
if ($valueS !== '') {
|
||||
$mergedValues[] = $valueS;
|
||||
}
|
||||
|
||||
if ($valueT !== '') {
|
||||
$mergedValues[] = $valueT;
|
||||
}
|
||||
|
||||
/*
|
||||
* Save final value into column S.
|
||||
*/
|
||||
$row['data'][$targetColumnIndex] = implode(' ', $mergedValues);
|
||||
|
||||
error_log(
|
||||
"Routine burberry: row " .
|
||||
($row['excelrow'] ?? $rowIndex) .
|
||||
" generated value in column S: " .
|
||||
$row['data'][$targetColumnIndex]
|
||||
);
|
||||
}
|
||||
|
||||
unset($row);
|
||||
|
||||
error_log("Routine burberry completed.");
|
||||
}
|
||||
@@ -0,0 +1,67 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* Routine: merge_column_T_and_U_into_T
|
||||
*
|
||||
* Purpose:
|
||||
* For each imported XLS row:
|
||||
* - read the value from column T
|
||||
* - read the value from column U
|
||||
* - merge both values
|
||||
* - save the final value into column T
|
||||
*
|
||||
* Target:
|
||||
* Column T must be mapped to the destination field in the template mapping.
|
||||
*/
|
||||
|
||||
function applyRoutine(&$excelData, $routineData = [])
|
||||
{
|
||||
/*
|
||||
* Excel column indexes are zero-based:
|
||||
*
|
||||
* T = 19
|
||||
* U = 20
|
||||
*/
|
||||
$targetColumnIndex = 19; // T
|
||||
$firstColumnIndex = 19; // T
|
||||
$secondColumnIndex = 20; // U
|
||||
|
||||
foreach ($excelData as $rowIndex => &$row) {
|
||||
if (!isset($row['data']) || !is_array($row['data'])) {
|
||||
error_log("Routine merge T+U: invalid row structure at index {$rowIndex}.");
|
||||
continue;
|
||||
}
|
||||
|
||||
$valueT = trim((string)($row['data'][$firstColumnIndex] ?? ''));
|
||||
$valueU = trim((string)($row['data'][$secondColumnIndex] ?? ''));
|
||||
|
||||
/*
|
||||
* Merge values, ignoring empty values.
|
||||
*/
|
||||
$mergedValues = [];
|
||||
|
||||
if ($valueT !== '') {
|
||||
$mergedValues[] = $valueT;
|
||||
}
|
||||
|
||||
if ($valueU !== '') {
|
||||
$mergedValues[] = $valueU;
|
||||
}
|
||||
|
||||
/*
|
||||
* Save final value into column T.
|
||||
*/
|
||||
$row['data'][$targetColumnIndex] = implode(' ', $mergedValues);
|
||||
|
||||
error_log(
|
||||
"Routine merge T+U: row " .
|
||||
($row['excelrow'] ?? $rowIndex) .
|
||||
" generated value in column T: " .
|
||||
$row['data'][$targetColumnIndex]
|
||||
);
|
||||
}
|
||||
|
||||
unset($row);
|
||||
|
||||
error_log("Routine merge T+U completed.");
|
||||
}
|
||||
@@ -0,0 +1,76 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* Routine: paulshark
|
||||
*
|
||||
* Purpose:
|
||||
* For each imported XLS row:
|
||||
* - read the value from column D
|
||||
* - read the value from column E
|
||||
* - read the value from column J
|
||||
* - merge the values
|
||||
* - save the final value into column D
|
||||
*
|
||||
* Target:
|
||||
* Column D must be mapped to the destination field in the template mapping.
|
||||
*/
|
||||
|
||||
function applyRoutine(&$excelData, $routineData = [])
|
||||
{
|
||||
/*
|
||||
* Excel column indexes are zero-based:
|
||||
*
|
||||
* D = 3
|
||||
* E = 4
|
||||
* J = 9
|
||||
*/
|
||||
$targetColumnIndex = 3; // D
|
||||
|
||||
$columnDIndex = 3; // D
|
||||
$columnEIndex = 4; // E
|
||||
$columnJIndex = 9; // J
|
||||
|
||||
foreach ($excelData as $rowIndex => &$row) {
|
||||
if (!isset($row['data']) || !is_array($row['data'])) {
|
||||
error_log("Routine paulshark: invalid row structure at index {$rowIndex}.");
|
||||
continue;
|
||||
}
|
||||
|
||||
$valueD = trim((string)($row['data'][$columnDIndex] ?? ''));
|
||||
$valueE = trim((string)($row['data'][$columnEIndex] ?? ''));
|
||||
$valueJ = trim((string)($row['data'][$columnJIndex] ?? ''));
|
||||
|
||||
/*
|
||||
* Merge values, ignoring empty values.
|
||||
*/
|
||||
$mergedValues = [];
|
||||
|
||||
if ($valueD !== '') {
|
||||
$mergedValues[] = $valueD;
|
||||
}
|
||||
|
||||
if ($valueE !== '') {
|
||||
$mergedValues[] = $valueE;
|
||||
}
|
||||
|
||||
if ($valueJ !== '') {
|
||||
$mergedValues[] = $valueJ;
|
||||
}
|
||||
|
||||
/*
|
||||
* Save final value into column D.
|
||||
*/
|
||||
$row['data'][$targetColumnIndex] = implode(' ', $mergedValues);
|
||||
|
||||
error_log(
|
||||
"Routine paulshark: row " .
|
||||
($row['excelrow'] ?? $rowIndex) .
|
||||
" generated value in column D: " .
|
||||
$row['data'][$targetColumnIndex]
|
||||
);
|
||||
}
|
||||
|
||||
unset($row);
|
||||
|
||||
error_log("Routine paulshark completed.");
|
||||
}
|
||||
@@ -0,0 +1,71 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* Routine: Richemont Pelletteria
|
||||
*
|
||||
* Purpose:
|
||||
* For each imported XLS row:
|
||||
* - read the value from column D
|
||||
* - read the value from column E
|
||||
* - merge the values
|
||||
* - save the final value into column D
|
||||
*
|
||||
* Target:
|
||||
* Column D must be mapped to the destination field in the template mapping.
|
||||
*/
|
||||
|
||||
function applyRoutine(&$excelData, $routineData = [])
|
||||
{
|
||||
/*
|
||||
* This routine does not require external routine data.
|
||||
* Columns are fixed.
|
||||
*
|
||||
* Excel column indexes are zero-based:
|
||||
*
|
||||
* D = 3
|
||||
* E = 4
|
||||
*/
|
||||
$targetColumnIndex = 3; // D
|
||||
|
||||
$columnDIndex = 3; // D
|
||||
$columnEIndex = 4; // E
|
||||
|
||||
foreach ($excelData as $rowIndex => &$row) {
|
||||
if (!isset($row['data']) || !is_array($row['data'])) {
|
||||
error_log("Routine Richemont Pelletteria: invalid row structure at index {$rowIndex}.");
|
||||
continue;
|
||||
}
|
||||
|
||||
$valueD = trim((string)($row['data'][$columnDIndex] ?? ''));
|
||||
$valueE = trim((string)($row['data'][$columnEIndex] ?? ''));
|
||||
|
||||
/*
|
||||
* Merge values, ignoring empty values.
|
||||
*/
|
||||
$mergedValues = [];
|
||||
|
||||
if ($valueD !== '') {
|
||||
$mergedValues[] = $valueD;
|
||||
}
|
||||
|
||||
if ($valueE !== '') {
|
||||
$mergedValues[] = $valueE;
|
||||
}
|
||||
|
||||
/*
|
||||
* Save final value into column D.
|
||||
*/
|
||||
$row['data'][$targetColumnIndex] = implode(' ', $mergedValues);
|
||||
|
||||
error_log(
|
||||
"Routine Richemont Pelletteria: row " .
|
||||
($row['excelrow'] ?? $rowIndex) .
|
||||
" generated value in column D: " .
|
||||
$row['data'][$targetColumnIndex]
|
||||
);
|
||||
}
|
||||
|
||||
unset($row);
|
||||
|
||||
error_log("Routine Richemont Pelletteria completed.");
|
||||
}
|
||||
@@ -46,8 +46,8 @@
|
||||
{
|
||||
"IdSchemaCustomFields": 48,
|
||||
"ConteggioClienti": 0,
|
||||
"Nome": "Standard Generico \/ Generic Standard",
|
||||
"Descrizione": "Schema per tutti i campioni di qualsiasi matrice escluso cuoio\/pelle\r\n\r\n"
|
||||
"Nome": "Standard \/ Generico",
|
||||
"Descrizione": "\r\n"
|
||||
},
|
||||
{
|
||||
"IdSchemaCustomFields": 49,
|
||||
@@ -882,6 +882,24 @@
|
||||
"ConteggioClienti": 0,
|
||||
"Nome": "LIMS-CIM - MAX MARA",
|
||||
"Descrizione": "Schema per MAX MARA scambio dati Database"
|
||||
},
|
||||
{
|
||||
"IdSchemaCustomFields": 203,
|
||||
"ConteggioClienti": 0,
|
||||
"Nome": "Vince",
|
||||
"Descrizione": "Schema per tutti i campioni di VINCE\r\n\r\n"
|
||||
},
|
||||
{
|
||||
"IdSchemaCustomFields": 204,
|
||||
"ConteggioClienti": 0,
|
||||
"Nome": "Max Mara",
|
||||
"Descrizione": "Schema da usare per Max Mara\r\n"
|
||||
},
|
||||
{
|
||||
"IdSchemaCustomFields": 205,
|
||||
"ConteggioClienti": 0,
|
||||
"Nome": "Chanel Flammability",
|
||||
"Descrizione": "Schema per Chanel Flammability\r\n"
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1,107 @@
|
||||
<?php
|
||||
require_once dirname(__DIR__, 2) . '/vendor/autoload.php';
|
||||
require_once dirname(__FILE__) . '/class/VisualLimsApiClient.class.php';
|
||||
require_once dirname(__FILE__) . '/include/headscript.php';
|
||||
|
||||
header('Content-Type: application/json');
|
||||
ini_set('display_errors', '0');
|
||||
error_reporting(E_ALL);
|
||||
|
||||
try {
|
||||
$db = DBHandlerSelect::getInstance();
|
||||
$pdo = $db->getConnection();
|
||||
|
||||
$api = VisualLimsApiClient::getInstance();
|
||||
|
||||
// Get all schemas currently used in template_mapping
|
||||
$stmtSchemas = $pdo->query("
|
||||
SELECT DISTINCT schema_id
|
||||
FROM template_mapping
|
||||
WHERE schema_id IS NOT NULL
|
||||
AND schema_id > 0
|
||||
ORDER BY schema_id ASC
|
||||
");
|
||||
|
||||
$schemaIds = $stmtSchemas->fetchAll(PDO::FETCH_COLUMN);
|
||||
|
||||
if (empty($schemaIds)) {
|
||||
throw new Exception('No schema_id found in template_mapping');
|
||||
}
|
||||
|
||||
$stmtUpdate = $pdo->prepare("
|
||||
UPDATE template_mapping
|
||||
SET field_order = ?
|
||||
WHERE schema_id = ?
|
||||
AND field_id = ?
|
||||
");
|
||||
|
||||
$summary = [];
|
||||
$totalUpdated = 0;
|
||||
|
||||
foreach ($schemaIds as $schemaId) {
|
||||
$schemaId = (int)$schemaId;
|
||||
|
||||
$endpoint = "SchemaCustomField($schemaId)?\$expand=SchemiCustomFieldsDettagli(\$expand=CustomField)";
|
||||
$data = $api->get($endpoint);
|
||||
|
||||
if (empty($data['SchemiCustomFieldsDettagli']) || !is_array($data['SchemiCustomFieldsDettagli'])) {
|
||||
$summary[] = [
|
||||
'schema_id' => $schemaId,
|
||||
'success' => false,
|
||||
'message' => 'No SchemiCustomFieldsDettagli found'
|
||||
];
|
||||
continue;
|
||||
}
|
||||
|
||||
$schemaUpdated = 0;
|
||||
$notFound = [];
|
||||
|
||||
foreach ($data['SchemiCustomFieldsDettagli'] as $detail) {
|
||||
$order = intval($detail['Ordine'] ?? 9999);
|
||||
$fieldId = intval($detail['CustomField']['IdCustomField'] ?? 0);
|
||||
|
||||
if ($fieldId <= 0) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$stmtUpdate->execute([
|
||||
$order,
|
||||
$schemaId,
|
||||
$fieldId
|
||||
]);
|
||||
|
||||
if ($stmtUpdate->rowCount() > 0) {
|
||||
$schemaUpdated++;
|
||||
$totalUpdated++;
|
||||
} else {
|
||||
$notFound[] = [
|
||||
'field_id' => $fieldId,
|
||||
'order' => $order,
|
||||
'label' => $detail['CustomField']['Titolo'] ?? ''
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
$summary[] = [
|
||||
'schema_id' => $schemaId,
|
||||
'success' => true,
|
||||
'updated' => $schemaUpdated,
|
||||
'not_found_count' => count($notFound),
|
||||
'not_found' => $notFound
|
||||
];
|
||||
}
|
||||
|
||||
echo json_encode([
|
||||
'success' => true,
|
||||
'schemas_processed' => count($schemaIds),
|
||||
'total_updated' => $totalUpdated,
|
||||
'summary' => $summary
|
||||
], JSON_PRETTY_PRINT);
|
||||
} catch (Exception $e) {
|
||||
http_response_code(500);
|
||||
|
||||
echo json_encode([
|
||||
'success' => false,
|
||||
'message' => $e->getMessage()
|
||||
], JSON_PRETTY_PRINT);
|
||||
}
|
||||
@@ -82,6 +82,7 @@ try {
|
||||
template_id,
|
||||
schema_id,
|
||||
field_id,
|
||||
field_order,
|
||||
data_type,
|
||||
is_required,
|
||||
default_value,
|
||||
@@ -97,6 +98,7 @@ try {
|
||||
:template_id,
|
||||
:schema_id,
|
||||
:field_id,
|
||||
:field_order,
|
||||
:data_type,
|
||||
:is_required,
|
||||
:default_value,
|
||||
@@ -116,6 +118,7 @@ try {
|
||||
UPDATE template_mapping
|
||||
SET
|
||||
schema_id = :schema_id,
|
||||
field_order = :field_order,
|
||||
data_type = :data_type,
|
||||
is_required = :is_required,
|
||||
default_value = :default_value,
|
||||
@@ -172,6 +175,7 @@ try {
|
||||
|
||||
$data = [
|
||||
':schema_id' => $schema_id,
|
||||
':field_order' => (int)($field['Ordine'] ?? 9999),
|
||||
':data_type' => $newDataType,
|
||||
':is_required' => !empty($custom_field['ObbligatorioWeb']) ? 1 : 0,
|
||||
':default_value' => $custom_field['ValoreDefault'] ?? null,
|
||||
@@ -234,7 +238,6 @@ try {
|
||||
|
||||
$response["success"] = true;
|
||||
$response["message"] = "Schema JSON updated, mappings synchronized, removed fields deleted, and changed fields updated successfully.";
|
||||
|
||||
} catch (Exception $e) {
|
||||
if (isset($pdo) && $pdo->inTransaction()) {
|
||||
$pdo->rollback();
|
||||
|
||||
@@ -0,0 +1,79 @@
|
||||
<?php
|
||||
require_once dirname(__DIR__, 2) . '/vendor/autoload.php';
|
||||
require_once dirname(__FILE__) . '/class/VisualLimsApiClient.class.php';
|
||||
require_once dirname(__FILE__) . '/include/headscript.php';
|
||||
|
||||
header('Content-Type: application/json');
|
||||
ini_set('display_errors', '0');
|
||||
error_reporting(E_ALL);
|
||||
|
||||
try {
|
||||
$schemaId = isset($_GET['schema_id']) && is_numeric($_GET['schema_id'])
|
||||
? intval($_GET['schema_id'])
|
||||
: 0;
|
||||
|
||||
if ($schemaId <= 0) {
|
||||
throw new Exception('Missing or invalid schema_id');
|
||||
}
|
||||
|
||||
$api = VisualLimsApiClient::getInstance();
|
||||
|
||||
$endpoint = "SchemaCustomField($schemaId)?\$expand=SchemiCustomFieldsDettagli(\$expand=CustomField)";
|
||||
$data = $api->get($endpoint);
|
||||
|
||||
if (empty($data['SchemiCustomFieldsDettagli']) || !is_array($data['SchemiCustomFieldsDettagli'])) {
|
||||
throw new Exception('No SchemiCustomFieldsDettagli found');
|
||||
}
|
||||
|
||||
$db = DBHandlerSelect::getInstance();
|
||||
$pdo = $db->getConnection();
|
||||
|
||||
$updated = 0;
|
||||
$notFound = [];
|
||||
|
||||
$stmt = $pdo->prepare("
|
||||
UPDATE template_mapping
|
||||
SET field_order = ?
|
||||
WHERE schema_id = ?
|
||||
AND field_id = ?
|
||||
");
|
||||
|
||||
foreach ($data['SchemiCustomFieldsDettagli'] as $detail) {
|
||||
$order = intval($detail['Ordine'] ?? 9999);
|
||||
$fieldId = intval($detail['CustomField']['IdCustomField'] ?? 0);
|
||||
|
||||
if ($fieldId <= 0) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$stmt->execute([
|
||||
$order,
|
||||
$schemaId,
|
||||
$fieldId
|
||||
]);
|
||||
|
||||
if ($stmt->rowCount() > 0) {
|
||||
$updated++;
|
||||
} else {
|
||||
$notFound[] = [
|
||||
'field_id' => $fieldId,
|
||||
'order' => $order,
|
||||
'label' => $detail['CustomField']['Titolo'] ?? ''
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
echo json_encode([
|
||||
'success' => true,
|
||||
'schema_id' => $schemaId,
|
||||
'updated' => $updated,
|
||||
'not_found' => $notFound
|
||||
]);
|
||||
} catch (Exception $e) {
|
||||
http_response_code(500);
|
||||
|
||||
echo json_encode([
|
||||
'success' => false,
|
||||
'message' => $e->getMessage()
|
||||
]);
|
||||
}
|
||||
Reference in New Issue
Block a user