Compare commits
72 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 56eee99a67 | |||
| f514b3d2c7 | |||
| a3eb0f0a57 | |||
| aa355905d7 | |||
| b38f3e1240 | |||
| cbd0c5b68a | |||
| f3e5cb4ffd | |||
| 0f0c6a04b7 | |||
| 813bd66f96 | |||
| ce00247d1c | |||
| 3a7dd266c8 | |||
| ba8dc4c721 | |||
| cfbbc36116 | |||
| 50d578eea1 | |||
| 6b0d2aa9b9 | |||
| fa7997c980 | |||
| 66be442eb6 | |||
| 19a2d6e3f7 | |||
| a15ab08576 | |||
| f71e8a56b5 | |||
| cb38bfb75a | |||
| 28a708dad3 | |||
| 6b9cf20ab9 | |||
| d40fc7d177 | |||
| b812563023 | |||
| 39a821357e | |||
| 53c223ea5f | |||
| 9775a12d4a | |||
| 7dfb935e33 | |||
| abb4200215 | |||
| 0be7109df4 | |||
| 578671e013 | |||
| d24836e2b1 | |||
| d983659000 | |||
| 7c5aa7734f | |||
| 4f0dbc7e91 | |||
| fb09f033ae | |||
| 8bae2d7008 | |||
| 7463fc6726 | |||
| 0bc2ff7e9d | |||
| aa1c32b7ed | |||
| c573a46318 | |||
| bf18a904bd | |||
| b3ce489348 | |||
| 08b89e01cc | |||
| 3e66d67dc5 | |||
| 490786731a | |||
| e96f538a47 | |||
| cc96ecb67f | |||
| 4cf03ae742 | |||
| 7a486e9dcf | |||
| 2a7b1fae17 | |||
| a05d9fed2b | |||
| 65170a0a7c | |||
| 223688c372 | |||
| b562eb4033 | |||
| 0645a0c675 | |||
| a02a6b2c4c | |||
| 45dd8d6907 | |||
| 755f6812d4 | |||
| 5e59ae2162 | |||
| 71a19144c8 | |||
| 381a05341b | |||
| 5a58decd40 | |||
| eb21910ef3 | |||
| 5dedc779df | |||
| f4e0074a73 | |||
| 9c850e4ea6 | |||
| f300811341 | |||
| 48387a9945 | |||
| 0e90db8219 | |||
| eaf70d5a46 |
+8
-1
@@ -33,15 +33,20 @@ yarn-error.log
|
|||||||
/public/userarea/*.log
|
/public/userarea/*.log
|
||||||
/public/userarea/*.txt
|
/public/userarea/*.txt
|
||||||
/public/userarea/*_response.json
|
/public/userarea/*_response.json
|
||||||
|
/public/userarea/customfield_values_response.json
|
||||||
/public/userarea/error_log.txt
|
/public/userarea/error_log.txt
|
||||||
/public/userarea/import_debug.log
|
/public/userarea/import_debug.log
|
||||||
/public/userarea/last_url.txt
|
/public/userarea/last_url.txt
|
||||||
/public/userarea/logaspi/
|
/public/userarea/logaspi/
|
||||||
/public/userarea/logsapi/
|
/public/userarea/logs/api/
|
||||||
|
/public/userarea/logs/api/**
|
||||||
|
/public/userarea/logs/
|
||||||
|
/public/userarea/logs/**
|
||||||
/public/userarea/photostrf/
|
/public/userarea/photostrf/
|
||||||
/public/userarea/class/*.log
|
/public/userarea/class/*.log
|
||||||
/public/userarea/class/curl_auth_debug.log
|
/public/userarea/class/curl_auth_debug.log
|
||||||
/public/userarea/class/curl_request_debug.log
|
/public/userarea/class/curl_request_debug.log
|
||||||
|
/public/userarea/schema_dettagli_response.json
|
||||||
|
|
||||||
# File XLSX temporanei importati
|
# File XLSX temporanei importati
|
||||||
/public/userarea/imported_trf/*.xlsx
|
/public/userarea/imported_trf/*.xlsx
|
||||||
@@ -53,3 +58,5 @@ yarn-error.log
|
|||||||
|
|
||||||
# Ignora tutti i log ovunque
|
# Ignora tutti i log ovunque
|
||||||
*.log
|
*.log
|
||||||
|
|
||||||
|
public/userarea/cache/
|
||||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,6 @@
|
|||||||
|
# DB LOCALE (Windows 11)
|
||||||
|
url=jdbc:mysql://localhost:3306/trfcertest
|
||||||
|
username=solocla
|
||||||
|
password=!Massarosa2
|
||||||
|
driver=com.mysql.cj.jdbc.Driver
|
||||||
|
changeLogFile=liquibase/changelog/db.changelog-master.yaml
|
||||||
@@ -0,0 +1,73 @@
|
|||||||
|
<?php
|
||||||
|
include('include/headscript.php');
|
||||||
|
header('Content-Type: application/json');
|
||||||
|
|
||||||
|
try {
|
||||||
|
$input = json_decode(file_get_contents('php://input'), true);
|
||||||
|
$templateId = intval($input['template_id'] ?? 0);
|
||||||
|
$importRefFromClient = trim($input['importreferencecode'] ?? '');
|
||||||
|
|
||||||
|
if (!$templateId) {
|
||||||
|
throw new Exception('Template ID missing');
|
||||||
|
}
|
||||||
|
|
||||||
|
$userId = $iduserlogin ?? 1;
|
||||||
|
|
||||||
|
$db = DBHandlerSelect::getInstance();
|
||||||
|
$pdo = $db->getConnection();
|
||||||
|
|
||||||
|
// Get default idclient from template
|
||||||
|
$stmt = $pdo->prepare("SELECT idclient FROM excel_templates WHERE id = ?");
|
||||||
|
$stmt->execute([$templateId]);
|
||||||
|
$template = $stmt->fetch(PDO::FETCH_ASSOC);
|
||||||
|
$idclient = $template['idclient'] ?? null;
|
||||||
|
|
||||||
|
// Use provided importreferencecode (from filtered page) or generate new
|
||||||
|
$importReferenceCode = $importRefFromClient !== '' ? $importRefFromClient : date('YmdHis') . '-' . uniqid();
|
||||||
|
|
||||||
|
// Insert empty record
|
||||||
|
$stmt = $pdo->prepare("
|
||||||
|
INSERT INTO datadb (templateid, user_id, status, idclient, importreferencecode, importdate)
|
||||||
|
VALUES (?, ?, 'i', ?, ?, NOW())
|
||||||
|
");
|
||||||
|
$stmt->execute([$templateId, $userId, $idclient, $importReferenceCode]);
|
||||||
|
$iddatadb = (int)$pdo->lastInsertId();
|
||||||
|
|
||||||
|
// Create import_data_details for all mappings (with auto_value support)
|
||||||
|
$mappingStmt = $pdo->prepare("SELECT id, auto_value, manual_default, data_type FROM template_mapping WHERE template_id = ?");
|
||||||
|
$mappingStmt->execute([$templateId]);
|
||||||
|
$mappings = $mappingStmt->fetchAll(PDO::FETCH_ASSOC);
|
||||||
|
|
||||||
|
if (!empty($mappings)) {
|
||||||
|
$insertStmt = $pdo->prepare("INSERT INTO import_data_details (id, mapping_id, field_value) VALUES (?, ?, ?)");
|
||||||
|
foreach ($mappings as $m) {
|
||||||
|
$val = '';
|
||||||
|
$auto = $m['auto_value'] ?? 'none';
|
||||||
|
if ($auto === 'import_date') {
|
||||||
|
$val = date('Y-m-d');
|
||||||
|
} elseif ($auto === 'import_time') {
|
||||||
|
$val = date('H:i');
|
||||||
|
} elseif ($m['data_type'] === 'DATE' && ($m['manual_default'] ?? '') === 'today') {
|
||||||
|
$val = date('Y-m-d');
|
||||||
|
} elseif (!empty($m['manual_default'])) {
|
||||||
|
$val = $m['manual_default'];
|
||||||
|
}
|
||||||
|
$insertStmt->execute([$iddatadb, $m['id'], $val]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get user name
|
||||||
|
$userStmt = $pdo->prepare("SELECT CONCAT(first_name, ' ', last_name) AS user_name FROM auth_users WHERE id = ?");
|
||||||
|
$userStmt->execute([$userId]);
|
||||||
|
$userName = $userStmt->fetchColumn() ?: '';
|
||||||
|
|
||||||
|
echo json_encode([
|
||||||
|
'success' => true,
|
||||||
|
'iddatadb' => $iddatadb,
|
||||||
|
'importreferencecode' => $importReferenceCode,
|
||||||
|
'user_name' => $userName,
|
||||||
|
]);
|
||||||
|
} catch (Exception $e) {
|
||||||
|
http_response_code(500);
|
||||||
|
echo json_encode(['success' => false, 'message' => $e->getMessage()]);
|
||||||
|
}
|
||||||
@@ -0,0 +1,849 @@
|
|||||||
|
(function () {
|
||||||
|
"use strict";
|
||||||
|
let analysisMatriciMap = {};
|
||||||
|
let analysisLoadedCache = {};
|
||||||
|
let analysisAssignedState = {};
|
||||||
|
let analysisSelectedState = {};
|
||||||
|
|
||||||
|
function loadAnalysisMatrixNames() {
|
||||||
|
return $.ajax({
|
||||||
|
url: "get_matrici_db.php",
|
||||||
|
method: "GET",
|
||||||
|
dataType: "json",
|
||||||
|
})
|
||||||
|
.done(function (data) {
|
||||||
|
analysisMatriciMap = {};
|
||||||
|
|
||||||
|
(data.value || []).forEach(function (matrice) {
|
||||||
|
analysisMatriciMap[String(matrice.IdMatrice)] =
|
||||||
|
matrice.NomeMatrice || "#" + matrice.IdMatrice;
|
||||||
|
});
|
||||||
|
|
||||||
|
applyAnalysisMatrixNames();
|
||||||
|
})
|
||||||
|
.fail(function () {
|
||||||
|
analysisMatriciMap = {};
|
||||||
|
applyAnalysisMatrixNames();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function applyAnalysisMatrixNames() {
|
||||||
|
const modal = document.getElementById("analysisModal");
|
||||||
|
if (!modal) return;
|
||||||
|
|
||||||
|
modal.querySelectorAll(".analysis-matrix-item").forEach((item) => {
|
||||||
|
const matrixId = item.getAttribute("data-matrix-id");
|
||||||
|
const nameEl = item.querySelector(".analysis-matrix-name");
|
||||||
|
|
||||||
|
let matrixName = "No Matrix";
|
||||||
|
if (matrixId && matrixId !== "NO_MATRIX") {
|
||||||
|
matrixName =
|
||||||
|
analysisMatriciMap[String(matrixId)] || "#" + matrixId;
|
||||||
|
}
|
||||||
|
|
||||||
|
item.setAttribute("data-matrix-name", matrixName);
|
||||||
|
|
||||||
|
if (nameEl) {
|
||||||
|
nameEl.textContent = matrixName;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
modal.querySelectorAll(".analysis-part-matrix-name").forEach((el) => {
|
||||||
|
const matrixId = el.getAttribute("data-matrix-id");
|
||||||
|
|
||||||
|
if (!matrixId || matrixId === "NO_MATRIX") {
|
||||||
|
el.textContent = "No Matrix";
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
el.textContent =
|
||||||
|
analysisMatriciMap[String(matrixId)] || "#" + matrixId;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
function readInitialAssignedAnalyses() {
|
||||||
|
const jsonEl = document.getElementById("analysisAssignedInitialData");
|
||||||
|
if (!jsonEl) {
|
||||||
|
analysisAssignedState = {};
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
analysisAssignedState =
|
||||||
|
JSON.parse(jsonEl.textContent || "{}") || {};
|
||||||
|
} catch (e) {
|
||||||
|
analysisAssignedState = {};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function getSelectedPartIds() {
|
||||||
|
const modal = document.getElementById("analysisModal");
|
||||||
|
if (!modal) return [];
|
||||||
|
|
||||||
|
return Array.from(
|
||||||
|
modal.querySelectorAll(".analysis-part-checkbox:checked"),
|
||||||
|
).map((el) => String(el.value));
|
||||||
|
}
|
||||||
|
|
||||||
|
function getCurrentSelectedAnalysisRecordKeys() {
|
||||||
|
return Object.keys(analysisSelectedState).filter(function (key) {
|
||||||
|
return analysisSelectedState[key] === true;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function renderAssignedAnalysesForPart(partId) {
|
||||||
|
const container = document.getElementById(
|
||||||
|
"analysisAssignedListPart" + partId,
|
||||||
|
);
|
||||||
|
if (!container) return;
|
||||||
|
|
||||||
|
const items = Array.isArray(analysisAssignedState[String(partId)])
|
||||||
|
? analysisAssignedState[String(partId)]
|
||||||
|
: [];
|
||||||
|
|
||||||
|
if (!items.length) {
|
||||||
|
container.innerHTML = "";
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
container.innerHTML = items
|
||||||
|
.map(function (item) {
|
||||||
|
const recordKey = item.analysis_recordkey || "";
|
||||||
|
const title = item.analysis_name || "Unnamed analysis";
|
||||||
|
const method = item.analysis_method || "";
|
||||||
|
|
||||||
|
return `
|
||||||
|
<div class="analysis-assigned-chip" data-recordkey="${escapeHtml(recordKey)}">
|
||||||
|
<div class="analysis-assigned-chip-text">
|
||||||
|
<div class="analysis-assigned-chip-title">${escapeHtml(title)}</div>
|
||||||
|
${method ? `<div class="analysis-assigned-chip-method">${escapeHtml(method)}</div>` : ""}
|
||||||
|
</div>
|
||||||
|
<button type="button"
|
||||||
|
class="analysis-remove-btn"
|
||||||
|
data-part-id="${escapeHtml(partId)}"
|
||||||
|
data-recordkey="${escapeHtml(recordKey)}"
|
||||||
|
title="Remove analysis">×</button>
|
||||||
|
</div>
|
||||||
|
`;
|
||||||
|
})
|
||||||
|
.join("");
|
||||||
|
}
|
||||||
|
|
||||||
|
function renderAssignedAnalysesForSelectedParts() {
|
||||||
|
const modal = document.getElementById("analysisModal");
|
||||||
|
if (!modal) return;
|
||||||
|
|
||||||
|
modal.querySelectorAll(".analysis-part-item").forEach(function (item) {
|
||||||
|
const partId = item.getAttribute("data-part-id");
|
||||||
|
renderAssignedAnalysesForPart(partId);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function syncSelectedAnalysisRows() {
|
||||||
|
const modal = document.getElementById("analysisModal");
|
||||||
|
if (!modal) return;
|
||||||
|
|
||||||
|
modal
|
||||||
|
.querySelectorAll(".analysis-analysis-item")
|
||||||
|
.forEach(function (item) {
|
||||||
|
const recordKey = item.getAttribute("data-recordkey") || "";
|
||||||
|
if (recordKey && analysisSelectedState[recordKey]) {
|
||||||
|
item.classList.add("analysis-selected");
|
||||||
|
} else {
|
||||||
|
item.classList.remove("analysis-selected");
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function buildAnalysisPayloadFromRow(rowEl) {
|
||||||
|
return {
|
||||||
|
analysis_recordkey: rowEl.getAttribute("data-recordkey") || "",
|
||||||
|
analysis_name: rowEl.getAttribute("data-analysis-name") || "",
|
||||||
|
analysis_method: rowEl.getAttribute("data-analysis-method") || "",
|
||||||
|
analysis_level: rowEl.getAttribute("data-analysis-level") || "",
|
||||||
|
is_web_selectable: rowEl.getAttribute("data-web") === "1" ? 1 : 0,
|
||||||
|
is_accredited:
|
||||||
|
rowEl.getAttribute("data-accredited") === "1" ? 1 : 0,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
function addAnalysisToLocalState(partId, payload, iddatadb, idmatrice) {
|
||||||
|
const key = String(partId);
|
||||||
|
if (!Array.isArray(analysisAssignedState[key])) {
|
||||||
|
analysisAssignedState[key] = [];
|
||||||
|
}
|
||||||
|
|
||||||
|
const exists = analysisAssignedState[key].some(function (item) {
|
||||||
|
return item.analysis_recordkey === payload.analysis_recordkey;
|
||||||
|
});
|
||||||
|
|
||||||
|
if (!exists) {
|
||||||
|
analysisAssignedState[key].push({
|
||||||
|
id: null,
|
||||||
|
part_id: parseInt(partId, 10),
|
||||||
|
iddatadb: iddatadb || null,
|
||||||
|
idmatrice: idmatrice || null,
|
||||||
|
analysis_recordkey: payload.analysis_recordkey,
|
||||||
|
analysis_name: payload.analysis_name,
|
||||||
|
analysis_method: payload.analysis_method,
|
||||||
|
analysis_level:
|
||||||
|
payload.analysis_level !== ""
|
||||||
|
? parseInt(payload.analysis_level, 10)
|
||||||
|
: null,
|
||||||
|
is_web_selectable: payload.is_web_selectable,
|
||||||
|
is_accredited: payload.is_accredited,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function removeAnalysisFromLocalState(partId, recordKey) {
|
||||||
|
const key = String(partId);
|
||||||
|
if (!Array.isArray(analysisAssignedState[key])) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
analysisAssignedState[key] = analysisAssignedState[key].filter(
|
||||||
|
function (item) {
|
||||||
|
return item.analysis_recordkey !== recordKey;
|
||||||
|
},
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
function saveAnalysisAssociation(partId, payload, callback) {
|
||||||
|
const modal = document.getElementById("analysisModal");
|
||||||
|
if (!modal) return;
|
||||||
|
|
||||||
|
const iddatadb =
|
||||||
|
modal.querySelector("#analysisModalIddatadb")?.value || "";
|
||||||
|
const partItem = modal.querySelector(
|
||||||
|
'.analysis-part-item[data-part-id="' + partId + '"]',
|
||||||
|
);
|
||||||
|
const idmatrice = partItem
|
||||||
|
? partItem.getAttribute("data-matrix-id") || ""
|
||||||
|
: "";
|
||||||
|
|
||||||
|
$.ajax({
|
||||||
|
url: "save_part_analysis.php",
|
||||||
|
method: "POST",
|
||||||
|
dataType: "json",
|
||||||
|
data: {
|
||||||
|
part_id: partId,
|
||||||
|
iddatadb: iddatadb,
|
||||||
|
idmatrice: idmatrice !== "NO_MATRIX" ? idmatrice : "",
|
||||||
|
analysis_recordkey: payload.analysis_recordkey,
|
||||||
|
analysis_name: payload.analysis_name,
|
||||||
|
analysis_method: payload.analysis_method,
|
||||||
|
analysis_level: payload.analysis_level,
|
||||||
|
is_web_selectable: payload.is_web_selectable,
|
||||||
|
is_accredited: payload.is_accredited,
|
||||||
|
},
|
||||||
|
})
|
||||||
|
.done(function () {
|
||||||
|
addAnalysisToLocalState(
|
||||||
|
partId,
|
||||||
|
payload,
|
||||||
|
iddatadb,
|
||||||
|
idmatrice !== "NO_MATRIX" ? idmatrice : null,
|
||||||
|
);
|
||||||
|
renderAssignedAnalysesForPart(partId);
|
||||||
|
if (typeof callback === "function") callback(true);
|
||||||
|
})
|
||||||
|
.fail(function (xhr) {
|
||||||
|
let message = "Error saving analysis association";
|
||||||
|
if (xhr && xhr.responseJSON && xhr.responseJSON.message) {
|
||||||
|
message = xhr.responseJSON.message;
|
||||||
|
}
|
||||||
|
alert(message);
|
||||||
|
if (typeof callback === "function") callback(false);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function deleteAnalysisAssociation(partId, recordKey, callback) {
|
||||||
|
$.ajax({
|
||||||
|
url: "delete_part_analysis.php",
|
||||||
|
method: "POST",
|
||||||
|
dataType: "json",
|
||||||
|
data: {
|
||||||
|
part_id: partId,
|
||||||
|
analysis_recordkey: recordKey,
|
||||||
|
},
|
||||||
|
})
|
||||||
|
.done(function () {
|
||||||
|
removeAnalysisFromLocalState(partId, recordKey);
|
||||||
|
renderAssignedAnalysesForPart(partId);
|
||||||
|
if (typeof callback === "function") callback(true);
|
||||||
|
})
|
||||||
|
.fail(function (xhr) {
|
||||||
|
let message = "Error deleting analysis association";
|
||||||
|
if (xhr && xhr.responseJSON && xhr.responseJSON.message) {
|
||||||
|
message = xhr.responseJSON.message;
|
||||||
|
}
|
||||||
|
alert(message);
|
||||||
|
if (typeof callback === "function") callback(false);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
function setAnalysisLoadingState(isLoading) {
|
||||||
|
const modal = document.getElementById("analysisModal");
|
||||||
|
if (!modal) return;
|
||||||
|
|
||||||
|
const loadingEl = modal.querySelector("#analysisLoadingBox");
|
||||||
|
if (!loadingEl) return;
|
||||||
|
|
||||||
|
if (isLoading) {
|
||||||
|
loadingEl.classList.remove("d-none");
|
||||||
|
} else {
|
||||||
|
loadingEl.classList.add("d-none");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function showAnalysisError(message) {
|
||||||
|
const modal = document.getElementById("analysisModal");
|
||||||
|
if (!modal) return;
|
||||||
|
|
||||||
|
const errorEl = modal.querySelector("#analysisErrorBox");
|
||||||
|
const emptyEl = modal.querySelector("#analysisEmptyBox");
|
||||||
|
const listEl = modal.querySelector("#analysisList");
|
||||||
|
|
||||||
|
if (listEl) listEl.innerHTML = "";
|
||||||
|
if (emptyEl) emptyEl.classList.add("d-none");
|
||||||
|
|
||||||
|
if (errorEl) {
|
||||||
|
errorEl.textContent = message || "Error loading analyses";
|
||||||
|
errorEl.classList.remove("d-none");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function showAnalysisEmpty(message) {
|
||||||
|
const modal = document.getElementById("analysisModal");
|
||||||
|
if (!modal) return;
|
||||||
|
|
||||||
|
const errorEl = modal.querySelector("#analysisErrorBox");
|
||||||
|
const emptyEl = modal.querySelector("#analysisEmptyBox");
|
||||||
|
const listEl = modal.querySelector("#analysisList");
|
||||||
|
|
||||||
|
if (listEl) listEl.innerHTML = "";
|
||||||
|
if (errorEl) errorEl.classList.add("d-none");
|
||||||
|
|
||||||
|
if (emptyEl) {
|
||||||
|
emptyEl.textContent =
|
||||||
|
message || "No analyses found for this matrix";
|
||||||
|
emptyEl.classList.remove("d-none");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function escapeHtml(value) {
|
||||||
|
return String(value ?? "")
|
||||||
|
.replace(/&/g, "&")
|
||||||
|
.replace(/</g, "<")
|
||||||
|
.replace(/>/g, ">")
|
||||||
|
.replace(/"/g, """)
|
||||||
|
.replace(/'/g, "'");
|
||||||
|
}
|
||||||
|
|
||||||
|
function renderAnalysesList(analyses) {
|
||||||
|
const modal = document.getElementById("analysisModal");
|
||||||
|
if (!modal) return;
|
||||||
|
|
||||||
|
const errorEl = modal.querySelector("#analysisErrorBox");
|
||||||
|
const emptyEl = modal.querySelector("#analysisEmptyBox");
|
||||||
|
const listEl = modal.querySelector("#analysisList");
|
||||||
|
|
||||||
|
if (!listEl) return;
|
||||||
|
|
||||||
|
if (errorEl) errorEl.classList.add("d-none");
|
||||||
|
|
||||||
|
if (!Array.isArray(analyses) || analyses.length === 0) {
|
||||||
|
showAnalysisEmpty("No analyses found for this matrix");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (emptyEl) emptyEl.classList.add("d-none");
|
||||||
|
|
||||||
|
listEl.innerHTML = analyses
|
||||||
|
.map(function (item) {
|
||||||
|
const recordKey = item.RecordKey || "";
|
||||||
|
const analysisName =
|
||||||
|
item.NomeAnalisiTraduzione ||
|
||||||
|
item.NomeAnalisi ||
|
||||||
|
"Unnamed analysis";
|
||||||
|
const methodName = item.MetodoNome || "";
|
||||||
|
const selectable = item.SelezionabileSuWeb === true;
|
||||||
|
const accredited = item.Accreditato === true;
|
||||||
|
const level = item.Livello ?? "";
|
||||||
|
const searchText = (
|
||||||
|
analysisName +
|
||||||
|
" " +
|
||||||
|
methodName
|
||||||
|
).toLowerCase();
|
||||||
|
|
||||||
|
let badges = "";
|
||||||
|
if (selectable) {
|
||||||
|
badges += '<span class="badge bg-success">Web</span>';
|
||||||
|
} else {
|
||||||
|
badges += '<span class="badge bg-secondary">Not web</span>';
|
||||||
|
}
|
||||||
|
|
||||||
|
if (accredited) {
|
||||||
|
badges +=
|
||||||
|
'<span class="badge bg-info text-dark">Accredited</span>';
|
||||||
|
}
|
||||||
|
|
||||||
|
if (level !== "") {
|
||||||
|
badges +=
|
||||||
|
'<span class="badge bg-light text-dark border">Level ' +
|
||||||
|
escapeHtml(level) +
|
||||||
|
"</span>";
|
||||||
|
}
|
||||||
|
|
||||||
|
return `
|
||||||
|
<div
|
||||||
|
class="list-group-item analysis-analysis-item"
|
||||||
|
data-recordkey="${escapeHtml(recordKey)}"
|
||||||
|
data-analysis-name="${escapeHtml(analysisName)}"
|
||||||
|
data-analysis-method="${escapeHtml(methodName)}"
|
||||||
|
data-analysis-level="${escapeHtml(level)}"
|
||||||
|
data-web="${selectable ? "1" : "0"}"
|
||||||
|
data-accredited="${accredited ? "1" : "0"}"
|
||||||
|
data-search="${escapeHtml(searchText)}"
|
||||||
|
>
|
||||||
|
<div class="fw-semibold">${escapeHtml(analysisName)}</div>
|
||||||
|
|
||||||
|
${methodName ? `<div class="analysis-analysis-meta mt-1">${escapeHtml(methodName)}</div>` : ""}
|
||||||
|
|
||||||
|
<div class="analysis-analysis-badges mt-2">
|
||||||
|
${badges}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
`;
|
||||||
|
})
|
||||||
|
.join("");
|
||||||
|
|
||||||
|
filterAnalysisList();
|
||||||
|
syncSelectedAnalysisRows();
|
||||||
|
}
|
||||||
|
|
||||||
|
function filterAnalysisList() {
|
||||||
|
const modal = document.getElementById("analysisModal");
|
||||||
|
if (!modal) return;
|
||||||
|
|
||||||
|
const webOnlyEl = modal.querySelector("#analysisWebOnly");
|
||||||
|
const searchEl = modal.querySelector("#analysisSearchInput");
|
||||||
|
const items = modal.querySelectorAll(".analysis-analysis-item");
|
||||||
|
const emptyEl = modal.querySelector("#analysisEmptyBox");
|
||||||
|
const errorEl = modal.querySelector("#analysisErrorBox");
|
||||||
|
|
||||||
|
const webOnly = webOnlyEl ? webOnlyEl.checked : false;
|
||||||
|
const searchValue = searchEl ? searchEl.value.trim().toLowerCase() : "";
|
||||||
|
|
||||||
|
let visibleCount = 0;
|
||||||
|
|
||||||
|
items.forEach((item) => {
|
||||||
|
const isWeb = item.getAttribute("data-web") === "1";
|
||||||
|
const searchText = (
|
||||||
|
item.getAttribute("data-search") || ""
|
||||||
|
).toLowerCase();
|
||||||
|
|
||||||
|
let visible = true;
|
||||||
|
|
||||||
|
if (webOnly && !isWeb) {
|
||||||
|
visible = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (visible && searchValue && !searchText.includes(searchValue)) {
|
||||||
|
visible = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
item.style.display = visible ? "" : "none";
|
||||||
|
|
||||||
|
if (visible) {
|
||||||
|
visibleCount++;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
if (errorEl) {
|
||||||
|
errorEl.classList.add("d-none");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (emptyEl) {
|
||||||
|
if (items.length === 0) {
|
||||||
|
emptyEl.textContent = "No analyses found for this matrix";
|
||||||
|
emptyEl.classList.remove("d-none");
|
||||||
|
} else if (visibleCount === 0) {
|
||||||
|
emptyEl.textContent = "No analyses match the current filters";
|
||||||
|
emptyEl.classList.remove("d-none");
|
||||||
|
} else {
|
||||||
|
emptyEl.classList.add("d-none");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function loadAnalysesByMatrix(matrixId) {
|
||||||
|
const modal = document.getElementById("analysisModal");
|
||||||
|
if (!modal) return;
|
||||||
|
|
||||||
|
if (!matrixId || matrixId === "NO_MATRIX") {
|
||||||
|
showAnalysisEmpty("No matrix selected");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const listEl = modal.querySelector("#analysisList");
|
||||||
|
const errorEl = modal.querySelector("#analysisErrorBox");
|
||||||
|
const emptyEl = modal.querySelector("#analysisEmptyBox");
|
||||||
|
|
||||||
|
if (listEl) listEl.innerHTML = "";
|
||||||
|
if (errorEl) errorEl.classList.add("d-none");
|
||||||
|
if (emptyEl) {
|
||||||
|
emptyEl.textContent = "";
|
||||||
|
emptyEl.classList.add("d-none");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (analysisLoadedCache[String(matrixId)]) {
|
||||||
|
renderAnalysesList(analysisLoadedCache[String(matrixId)]);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
setAnalysisLoadingState(true);
|
||||||
|
|
||||||
|
$.ajax({
|
||||||
|
url: "get_analisi_matrice_filter.php",
|
||||||
|
method: "GET",
|
||||||
|
dataType: "json",
|
||||||
|
data: {
|
||||||
|
id_matrice: matrixId,
|
||||||
|
},
|
||||||
|
})
|
||||||
|
.done(function (response) {
|
||||||
|
const analyses = Array.isArray(response.value)
|
||||||
|
? response.value
|
||||||
|
: [];
|
||||||
|
analysisLoadedCache[String(matrixId)] = analyses;
|
||||||
|
renderAnalysesList(analyses);
|
||||||
|
})
|
||||||
|
.fail(function (xhr) {
|
||||||
|
let message = "Error loading analyses";
|
||||||
|
if (xhr && xhr.responseJSON && xhr.responseJSON.error) {
|
||||||
|
message = xhr.responseJSON.error;
|
||||||
|
}
|
||||||
|
showAnalysisError(message);
|
||||||
|
})
|
||||||
|
.always(function () {
|
||||||
|
setAnalysisLoadingState(false);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function updateSelectedPartsInfo() {
|
||||||
|
const modal = document.getElementById("analysisModal");
|
||||||
|
if (!modal) return;
|
||||||
|
|
||||||
|
const checked = modal.querySelectorAll(
|
||||||
|
".analysis-part-checkbox:checked",
|
||||||
|
);
|
||||||
|
const ids = Array.from(checked).map((el) => el.value);
|
||||||
|
|
||||||
|
const countEl = modal.querySelector("#analysisSelectedPartsCount");
|
||||||
|
const idsEl = modal.querySelector("#analysisSelectedPartsIds");
|
||||||
|
|
||||||
|
if (countEl) {
|
||||||
|
countEl.textContent = `${ids.length} selected`;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (idsEl) {
|
||||||
|
idsEl.textContent = ids.length ? ids.join(", ") : "-";
|
||||||
|
}
|
||||||
|
|
||||||
|
modal.querySelectorAll(".analysis-part-item").forEach((item) => {
|
||||||
|
const checkbox = item.querySelector(".analysis-part-checkbox");
|
||||||
|
if (checkbox && checkbox.checked) {
|
||||||
|
item.classList.add("part-checked");
|
||||||
|
} else {
|
||||||
|
item.classList.remove("part-checked");
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function selectPartsByMatrix(matrixId, matrixLabel) {
|
||||||
|
const modal = document.getElementById("analysisModal");
|
||||||
|
if (!modal) return;
|
||||||
|
|
||||||
|
const partItems = modal.querySelectorAll(".analysis-part-item");
|
||||||
|
const matrixLabelEl = modal.querySelector("#analysisCurrentMatrix");
|
||||||
|
|
||||||
|
partItems.forEach((item) => {
|
||||||
|
const itemMatrixId = item.getAttribute("data-matrix-id");
|
||||||
|
const checkbox = item.querySelector(".analysis-part-checkbox");
|
||||||
|
|
||||||
|
item.classList.remove("matrix-active");
|
||||||
|
|
||||||
|
if (itemMatrixId === String(matrixId)) {
|
||||||
|
item.classList.add("matrix-active");
|
||||||
|
if (checkbox) checkbox.checked = true;
|
||||||
|
} else {
|
||||||
|
if (checkbox) checkbox.checked = false;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
if (matrixLabelEl) {
|
||||||
|
matrixLabelEl.textContent = matrixLabel || "-";
|
||||||
|
}
|
||||||
|
analysisSelectedState = {};
|
||||||
|
|
||||||
|
const selectedPartIds = getSelectedPartIds();
|
||||||
|
selectedPartIds.forEach(function (partId) {
|
||||||
|
const assigned = Array.isArray(
|
||||||
|
analysisAssignedState[String(partId)],
|
||||||
|
)
|
||||||
|
? analysisAssignedState[String(partId)]
|
||||||
|
: [];
|
||||||
|
|
||||||
|
assigned.forEach(function (item) {
|
||||||
|
if (item.analysis_recordkey) {
|
||||||
|
analysisSelectedState[item.analysis_recordkey] = true;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
updateSelectedPartsInfo();
|
||||||
|
loadAnalysesByMatrix(matrixId);
|
||||||
|
}
|
||||||
|
|
||||||
|
function clearAnalysisSelection() {
|
||||||
|
const modal = document.getElementById("analysisModal");
|
||||||
|
if (!modal) return;
|
||||||
|
|
||||||
|
modal.querySelectorAll(".analysis-matrix-item").forEach((item) => {
|
||||||
|
item.classList.remove("active");
|
||||||
|
});
|
||||||
|
|
||||||
|
modal.querySelectorAll(".analysis-part-item").forEach((item) => {
|
||||||
|
item.classList.remove("matrix-active", "part-checked");
|
||||||
|
});
|
||||||
|
|
||||||
|
modal.querySelectorAll(".analysis-part-checkbox").forEach((cb) => {
|
||||||
|
cb.checked = false;
|
||||||
|
});
|
||||||
|
|
||||||
|
const matrixLabelEl = modal.querySelector("#analysisCurrentMatrix");
|
||||||
|
if (matrixLabelEl) matrixLabelEl.textContent = "-";
|
||||||
|
|
||||||
|
const webOnlyEl = modal.querySelector("#analysisWebOnly");
|
||||||
|
if (webOnlyEl) webOnlyEl.checked = false;
|
||||||
|
|
||||||
|
const searchEl = modal.querySelector("#analysisSearchInput");
|
||||||
|
if (searchEl) searchEl.value = "";
|
||||||
|
|
||||||
|
const listEl = modal.querySelector("#analysisList");
|
||||||
|
if (listEl) listEl.innerHTML = "";
|
||||||
|
|
||||||
|
showAnalysisEmpty("Select a matrix to load analyses");
|
||||||
|
updateSelectedPartsInfo();
|
||||||
|
}
|
||||||
|
|
||||||
|
function initAnalysisModal() {
|
||||||
|
const modal = document.getElementById("analysisModal");
|
||||||
|
if (!modal) return;
|
||||||
|
|
||||||
|
analysisLoadedCache = {};
|
||||||
|
analysisSelectedState = {};
|
||||||
|
readInitialAssignedAnalyses();
|
||||||
|
renderAssignedAnalysesForSelectedParts();
|
||||||
|
|
||||||
|
modal.querySelectorAll(".analysis-matrix-item").forEach((btn) => {
|
||||||
|
btn.addEventListener("click", function () {
|
||||||
|
modal
|
||||||
|
.querySelectorAll(".analysis-matrix-item")
|
||||||
|
.forEach((x) => x.classList.remove("active"));
|
||||||
|
this.classList.add("active");
|
||||||
|
|
||||||
|
const matrixId = this.getAttribute("data-matrix-id");
|
||||||
|
const matrixLabel =
|
||||||
|
this.getAttribute("data-matrix-name") ||
|
||||||
|
this.textContent.trim();
|
||||||
|
|
||||||
|
selectPartsByMatrix(matrixId, matrixLabel);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
modal.querySelectorAll(".analysis-part-checkbox").forEach((cb) => {
|
||||||
|
cb.addEventListener("change", function () {
|
||||||
|
updateSelectedPartsInfo();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
const clearBtn = modal.querySelector("#analysisClearSelectionBtn");
|
||||||
|
if (clearBtn) {
|
||||||
|
clearBtn.addEventListener("click", function () {
|
||||||
|
clearAnalysisSelection();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
const webOnlyEl = modal.querySelector("#analysisWebOnly");
|
||||||
|
if (webOnlyEl) {
|
||||||
|
webOnlyEl.addEventListener("change", function () {
|
||||||
|
filterAnalysisList();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
const searchEl = modal.querySelector("#analysisSearchInput");
|
||||||
|
if (searchEl) {
|
||||||
|
searchEl.addEventListener("input", function () {
|
||||||
|
filterAnalysisList();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
modal.addEventListener("click", function (e) {
|
||||||
|
const removeBtn = e.target.closest(".analysis-remove-btn");
|
||||||
|
if (removeBtn) {
|
||||||
|
e.preventDefault();
|
||||||
|
e.stopPropagation();
|
||||||
|
|
||||||
|
const partId = removeBtn.getAttribute("data-part-id");
|
||||||
|
const recordKey = removeBtn.getAttribute("data-recordkey");
|
||||||
|
|
||||||
|
deleteAnalysisAssociation(partId, recordKey, function () {
|
||||||
|
const stillUsed = Object.keys(analysisAssignedState).some(
|
||||||
|
function (pid) {
|
||||||
|
return (
|
||||||
|
Array.isArray(analysisAssignedState[pid]) &&
|
||||||
|
analysisAssignedState[pid].some(
|
||||||
|
function (item) {
|
||||||
|
return (
|
||||||
|
item.analysis_recordkey ===
|
||||||
|
recordKey
|
||||||
|
);
|
||||||
|
},
|
||||||
|
)
|
||||||
|
);
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
|
if (!stillUsed) {
|
||||||
|
delete analysisSelectedState[recordKey];
|
||||||
|
}
|
||||||
|
|
||||||
|
syncSelectedAnalysisRows();
|
||||||
|
});
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const row = e.target.closest(".analysis-analysis-item");
|
||||||
|
if (!row) return;
|
||||||
|
|
||||||
|
const selectedPartIds = getSelectedPartIds();
|
||||||
|
if (!selectedPartIds.length) {
|
||||||
|
alert("Select at least one part first");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const payload = buildAnalysisPayloadFromRow(row);
|
||||||
|
if (!payload.analysis_recordkey) {
|
||||||
|
alert("Invalid analysis record key");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const recordKey = payload.analysis_recordkey;
|
||||||
|
const alreadySelected = !!analysisSelectedState[recordKey];
|
||||||
|
|
||||||
|
if (alreadySelected) {
|
||||||
|
selectedPartIds.forEach(function (partId) {
|
||||||
|
deleteAnalysisAssociation(partId, recordKey);
|
||||||
|
});
|
||||||
|
|
||||||
|
delete analysisSelectedState[recordKey];
|
||||||
|
syncSelectedAnalysisRows();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
let pending = selectedPartIds.length;
|
||||||
|
let atLeastOneSaved = false;
|
||||||
|
|
||||||
|
selectedPartIds.forEach(function (partId) {
|
||||||
|
saveAnalysisAssociation(partId, payload, function (ok) {
|
||||||
|
if (ok) atLeastOneSaved = true;
|
||||||
|
|
||||||
|
pending--;
|
||||||
|
if (pending <= 0 && atLeastOneSaved) {
|
||||||
|
analysisSelectedState[recordKey] = true;
|
||||||
|
syncSelectedAnalysisRows();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
const firstActiveMatrix = modal.querySelector(
|
||||||
|
".analysis-matrix-item.active",
|
||||||
|
);
|
||||||
|
if (firstActiveMatrix) {
|
||||||
|
const matrixId = firstActiveMatrix.getAttribute("data-matrix-id");
|
||||||
|
const matrixLabel =
|
||||||
|
firstActiveMatrix.getAttribute("data-matrix-name") ||
|
||||||
|
firstActiveMatrix.textContent.trim();
|
||||||
|
|
||||||
|
selectPartsByMatrix(matrixId, matrixLabel);
|
||||||
|
} else {
|
||||||
|
updateSelectedPartsInfo();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// OPEN ANALYSIS MODAL FROM PARTS MODAL BUTTON
|
||||||
|
$(document).on("click", ".open-analysis-modal-btn", function () {
|
||||||
|
const partsModal = document.getElementById("partsModal");
|
||||||
|
const iddatadb = $("#partsModal").data("iddatadb");
|
||||||
|
|
||||||
|
if (!iddatadb) {
|
||||||
|
console.error("iddatadb not found on #partsModal");
|
||||||
|
alert("iddatadb not found");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
$.ajax({
|
||||||
|
url: "modal_analysis.php",
|
||||||
|
method: "GET",
|
||||||
|
data: { iddatadb: iddatadb },
|
||||||
|
success: function (response) {
|
||||||
|
$("#analysisModalContainer").html(response);
|
||||||
|
|
||||||
|
const modalElement = document.getElementById("analysisModal");
|
||||||
|
if (!modalElement) {
|
||||||
|
console.error("Analysis modal not found: #analysisModal");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
let modal = bootstrap.Modal.getInstance(modalElement);
|
||||||
|
if (!modal) {
|
||||||
|
modal = new bootstrap.Modal(modalElement, {
|
||||||
|
backdrop: true,
|
||||||
|
keyboard: true,
|
||||||
|
focus: true,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
loadAnalysisMatrixNames().always(function () {
|
||||||
|
initAnalysisModal();
|
||||||
|
modal.show();
|
||||||
|
});
|
||||||
|
},
|
||||||
|
error: function (xhr, status, error) {
|
||||||
|
console.error("Error loading analysis modal:", error);
|
||||||
|
alert("Error loading analysis modal: " + error);
|
||||||
|
},
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
// CLEANUP ON CLOSE
|
||||||
|
$(document).on("hidden.bs.modal", "#analysisModal", function () {
|
||||||
|
const modalElement = document.getElementById("analysisModal");
|
||||||
|
if (modalElement) {
|
||||||
|
const modal = bootstrap.Modal.getInstance(modalElement);
|
||||||
|
if (modal) modal.dispose();
|
||||||
|
}
|
||||||
|
|
||||||
|
$("#analysisModalContainer").empty();
|
||||||
|
|
||||||
|
// keep parts modal alive, but remove extra backdrop leftovers
|
||||||
|
$(".modal-backdrop").last().remove();
|
||||||
|
|
||||||
|
if ($("#partsModal").hasClass("show")) {
|
||||||
|
$("body").addClass("modal-open");
|
||||||
|
} else {
|
||||||
|
$("body").removeClass("modal-open").css("padding-right", "");
|
||||||
|
}
|
||||||
|
});
|
||||||
|
})();
|
||||||
@@ -22,6 +22,8 @@ $(document).ready(function () {
|
|||||||
let partsListData = [];
|
let partsListData = [];
|
||||||
// DIMENSIONE GLOBALE MARKER
|
// DIMENSIONE GLOBALE MARKER
|
||||||
let globalMarkerSize = 16;
|
let globalMarkerSize = 16;
|
||||||
|
// COLORE TESTO LISTA DESCRIZIONI
|
||||||
|
let globalDescriptionColor = "#000000";
|
||||||
|
|
||||||
// ===================
|
// ===================
|
||||||
// MODAL INITIALIZATION
|
// MODAL INITIALIZATION
|
||||||
@@ -138,6 +140,7 @@ $(document).ready(function () {
|
|||||||
descriptionTextbox = null;
|
descriptionTextbox = null;
|
||||||
markerObjects = {};
|
markerObjects = {};
|
||||||
globalMarkerSize = 16;
|
globalMarkerSize = 16;
|
||||||
|
globalDescriptionColor = "#000000";
|
||||||
$("#photoSelectorContainerAnnotations").empty().hide();
|
$("#photoSelectorContainerAnnotations").empty().hide();
|
||||||
$("#samplePhotoAnnotations").attr("src", "");
|
$("#samplePhotoAnnotations").attr("src", "");
|
||||||
$("#partsListAnnotations").empty();
|
$("#partsListAnnotations").empty();
|
||||||
@@ -177,7 +180,25 @@ $(document).ready(function () {
|
|||||||
updateMarkers();
|
updateMarkers();
|
||||||
markUnsaved();
|
markUnsaved();
|
||||||
});
|
});
|
||||||
|
// ===================
|
||||||
|
// COLORE LISTA DESCRIZIONI
|
||||||
|
// ===================
|
||||||
|
$(document)
|
||||||
|
.off("input.descriptionColor", "#descriptionColorPickerAnnotations")
|
||||||
|
.on(
|
||||||
|
"input.descriptionColor",
|
||||||
|
"#descriptionColorPickerAnnotations",
|
||||||
|
function () {
|
||||||
|
globalDescriptionColor = $(this).val();
|
||||||
|
|
||||||
|
if (descriptionTextbox && fabricCanvas) {
|
||||||
|
descriptionTextbox.set("fill", globalDescriptionColor);
|
||||||
|
fabricCanvas.renderAll();
|
||||||
|
}
|
||||||
|
|
||||||
|
markUnsaved();
|
||||||
|
},
|
||||||
|
);
|
||||||
// ===================
|
// ===================
|
||||||
// PHOTO LOADERS
|
// PHOTO LOADERS
|
||||||
// ===================
|
// ===================
|
||||||
@@ -614,17 +635,11 @@ $(document).ready(function () {
|
|||||||
partsListData.forEach((part) => {
|
partsListData.forEach((part) => {
|
||||||
const partNumber = part.part_number;
|
const partNumber = part.part_number;
|
||||||
const partDescription = part.part_description;
|
const partDescription = part.part_description;
|
||||||
|
const isMixPart = String(part.mix || "N").toUpperCase() === "Y";
|
||||||
|
|
||||||
const partColor =
|
const partColor =
|
||||||
partColors[partNumber] ||
|
partColors[partNumber] || (isMixPart ? "#0000ff" : "#ff0000");
|
||||||
(partDescription.toLowerCase().startsWith("mix")
|
if (partNumber && partDescription && (showMixParts || !isMixPart)) {
|
||||||
? "#0000ff"
|
|
||||||
: "#ff0000");
|
|
||||||
if (
|
|
||||||
partNumber &&
|
|
||||||
partDescription &&
|
|
||||||
(showMixParts ||
|
|
||||||
!partDescription.toLowerCase().startsWith("mix"))
|
|
||||||
) {
|
|
||||||
const colorOptions = predefinedColors
|
const colorOptions = predefinedColors
|
||||||
.map(
|
.map(
|
||||||
(color) =>
|
(color) =>
|
||||||
@@ -922,11 +937,10 @@ $(document).ready(function () {
|
|||||||
) {
|
) {
|
||||||
partsListData = response.parts;
|
partsListData = response.parts;
|
||||||
response.parts.forEach((part) => {
|
response.parts.forEach((part) => {
|
||||||
const defaultColor = part.part_description
|
const isMixPart =
|
||||||
.toLowerCase()
|
String(part.mix || "N").toUpperCase() === "Y";
|
||||||
.startsWith("mix")
|
const defaultColor = isMixPart ? "#0000ff" : "#ff0000";
|
||||||
? "#0000ff"
|
|
||||||
: "#ff0000";
|
|
||||||
partColors[part.part_number] = defaultColor;
|
partColors[part.part_number] = defaultColor;
|
||||||
});
|
});
|
||||||
updatePartsList();
|
updatePartsList();
|
||||||
@@ -1003,11 +1017,10 @@ $(document).ready(function () {
|
|||||||
(p) => p.part_number == marker.partNumber,
|
(p) => p.part_number == marker.partNumber,
|
||||||
);
|
);
|
||||||
const partDescription = part ? part.part_description : "";
|
const partDescription = part ? part.part_description : "";
|
||||||
if (
|
const isMixPart =
|
||||||
!showMixParts &&
|
part && String(part.mix || "N").toUpperCase() === "Y";
|
||||||
partDescription &&
|
|
||||||
partDescription.toLowerCase().startsWith("mix")
|
if (!showMixParts && isMixPart) {
|
||||||
) {
|
|
||||||
console.log("Ignoro marker per parte Mix:", marker.partNumber);
|
console.log("Ignoro marker per parte Mix:", marker.partNumber);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -1115,11 +1128,10 @@ $(document).ready(function () {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const partsList = partsListData
|
const partsList = partsListData
|
||||||
.filter(
|
.filter((part) => {
|
||||||
(part) =>
|
const isMixPart = String(part.mix || "N").toUpperCase() === "Y";
|
||||||
showMixParts ||
|
return showMixParts || !isMixPart;
|
||||||
!part.part_description.toLowerCase().startsWith("mix"),
|
})
|
||||||
)
|
|
||||||
.map((part) => `${part.part_number} ${part.part_description}`);
|
.map((part) => `${part.part_number} ${part.part_description}`);
|
||||||
|
|
||||||
const text = partsList.join("\n");
|
const text = partsList.join("\n");
|
||||||
@@ -1139,7 +1151,7 @@ $(document).ready(function () {
|
|||||||
backgroundColor: "transparent",
|
backgroundColor: "transparent",
|
||||||
fontFamily: "Arial",
|
fontFamily: "Arial",
|
||||||
fontSize: Math.max(16, Math.round(globalMarkerSize * 0.8)),
|
fontSize: Math.max(16, Math.round(globalMarkerSize * 0.8)),
|
||||||
fill: "#000000",
|
fill: globalDescriptionColor,
|
||||||
padding: 10,
|
padding: 10,
|
||||||
editable: false,
|
editable: false,
|
||||||
selectable: true,
|
selectable: true,
|
||||||
|
|||||||
@@ -3,6 +3,9 @@ ob_start();
|
|||||||
session_start();
|
session_start();
|
||||||
require_once '../../vendor/autoload.php';
|
require_once '../../vendor/autoload.php';
|
||||||
|
|
||||||
|
Dotenv\Dotenv::createImmutable(dirname(__DIR__, 2))->safeLoad();
|
||||||
|
date_default_timezone_set($_ENV['APP_TIMEZONE'] ?? 'Europe/Rome');
|
||||||
|
|
||||||
$response = ['error' => '', 'rows' => [], 'columns' => [], 'template_id' => 0, 'filename' => '', 'excel_data' => []];
|
$response = ['error' => '', 'rows' => [], 'columns' => [], 'template_id' => 0, 'filename' => '', 'excel_data' => []];
|
||||||
|
|
||||||
try {
|
try {
|
||||||
|
|||||||
@@ -255,4 +255,39 @@ class VisualLimsApiClient
|
|||||||
{
|
{
|
||||||
return $this->baseUrl;
|
return $this->baseUrl;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Recupera contenuto binario - Adattato per https://bvcpsitaly-elims.com/limsapi
|
||||||
|
*/
|
||||||
|
public function getRaw($endpoint)
|
||||||
|
{
|
||||||
|
$token = $this->getToken();
|
||||||
|
|
||||||
|
// IMPORTANTE: usa /odata/ e NON /api/odata/
|
||||||
|
$url = "{$this->baseUrl}/odata/{$endpoint}";
|
||||||
|
|
||||||
|
$ch = curl_init($url);
|
||||||
|
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
|
||||||
|
curl_setopt($ch, CURLOPT_HTTPHEADER, [
|
||||||
|
"Authorization: Bearer {$token}",
|
||||||
|
"Accept: */*"
|
||||||
|
]);
|
||||||
|
curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);
|
||||||
|
curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, false);
|
||||||
|
|
||||||
|
$response = curl_exec($ch);
|
||||||
|
$http_code = curl_getinfo($ch, CURLINFO_HTTP_CODE);
|
||||||
|
$curl_error = curl_error($ch);
|
||||||
|
curl_close($ch);
|
||||||
|
|
||||||
|
if ($response === false) {
|
||||||
|
throw new Exception("Errore cURL: " . $curl_error);
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($http_code !== 200) {
|
||||||
|
throw new Exception("HTTP {$http_code} su endpoint: " . $url);
|
||||||
|
}
|
||||||
|
|
||||||
|
return $response;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,6 +3,9 @@ require_once dirname(__DIR__, 3) . '/vendor/autoload.php';
|
|||||||
|
|
||||||
use Dotenv\Dotenv;
|
use Dotenv\Dotenv;
|
||||||
|
|
||||||
|
Dotenv::createImmutable(dirname(__DIR__, 3))->safeLoad();
|
||||||
|
date_default_timezone_set($_ENV['APP_TIMEZONE'] ?? 'Europe/Rome');
|
||||||
|
|
||||||
class DBHandlerSelect
|
class DBHandlerSelect
|
||||||
{
|
{
|
||||||
private static $instance = null;
|
private static $instance = null;
|
||||||
|
|||||||
@@ -0,0 +1,131 @@
|
|||||||
|
<?php
|
||||||
|
header('Content-Type: application/json');
|
||||||
|
include('include/headscript.php');
|
||||||
|
|
||||||
|
$dbHandler = DBHandlerSelect::getInstance();
|
||||||
|
$pdo = $dbHandler->getConnection();
|
||||||
|
$pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
|
||||||
|
|
||||||
|
$data = json_decode(file_get_contents('php://input'), true);
|
||||||
|
|
||||||
|
$sourceIddatadb = isset($data['source_iddatadb']) ? (int)$data['source_iddatadb'] : 0;
|
||||||
|
$targetList = $data['target_iddatadb_list'] ?? [];
|
||||||
|
|
||||||
|
$targetIds = array_values(array_unique(array_filter(array_map('intval', (array)$targetList), function ($v) use ($sourceIddatadb) {
|
||||||
|
return $v > 0 && $v !== $sourceIddatadb;
|
||||||
|
})));
|
||||||
|
|
||||||
|
if ($sourceIddatadb <= 0 || empty($targetIds)) {
|
||||||
|
echo json_encode([
|
||||||
|
'success' => false,
|
||||||
|
'message' => 'Missing source or target records'
|
||||||
|
]);
|
||||||
|
exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
$pdo->beginTransaction();
|
||||||
|
|
||||||
|
// 1. Load source parts
|
||||||
|
$stmtParts = $pdo->prepare("
|
||||||
|
SELECT id, part_number, part_description, mix, idmatrice, note, dateexpiry
|
||||||
|
FROM identification_parts
|
||||||
|
WHERE iddatadb = ?
|
||||||
|
ORDER BY part_number ASC, id ASC
|
||||||
|
");
|
||||||
|
$stmtParts->execute([$sourceIddatadb]);
|
||||||
|
$sourceParts = $stmtParts->fetchAll(PDO::FETCH_ASSOC);
|
||||||
|
|
||||||
|
if (empty($sourceParts)) {
|
||||||
|
$pdo->rollBack();
|
||||||
|
echo json_encode([
|
||||||
|
'success' => false,
|
||||||
|
'message' => 'No parts found for source record'
|
||||||
|
]);
|
||||||
|
exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 2. Prepare statements
|
||||||
|
$stmtInsertPart = $pdo->prepare("
|
||||||
|
INSERT INTO identification_parts
|
||||||
|
(iddatadb, part_number, part_description, mix, idmatrice, note, dateexpiry, created_at, updated_at)
|
||||||
|
VALUES
|
||||||
|
(:iddatadb, :part_number, :part_description, :mix, :idmatrice, :note, :dateexpiry, NOW(), NOW())
|
||||||
|
");
|
||||||
|
|
||||||
|
$stmtLoadCF = $pdo->prepare("
|
||||||
|
SELECT field_id, value_id, value_text
|
||||||
|
FROM identification_parts_customfields
|
||||||
|
WHERE part_id = ?
|
||||||
|
ORDER BY id ASC
|
||||||
|
");
|
||||||
|
|
||||||
|
$stmtInsertCF = $pdo->prepare("
|
||||||
|
INSERT INTO identification_parts_customfields
|
||||||
|
(part_id, field_id, value_id, value_text, created_at, updated_at)
|
||||||
|
VALUES
|
||||||
|
(:part_id, :field_id, :value_id, :value_text, NOW(), NOW())
|
||||||
|
");
|
||||||
|
|
||||||
|
$details = [];
|
||||||
|
$totalClonedParts = 0;
|
||||||
|
|
||||||
|
// 3. Clone source parts to each target record
|
||||||
|
foreach ($targetIds as $targetIddatadb) {
|
||||||
|
$clonedCountForTarget = 0;
|
||||||
|
|
||||||
|
foreach ($sourceParts as $part) {
|
||||||
|
$stmtInsertPart->execute([
|
||||||
|
':iddatadb' => $targetIddatadb,
|
||||||
|
':part_number' => $part['part_number'],
|
||||||
|
':part_description' => $part['part_description'],
|
||||||
|
':mix' => $part['mix'] ?? 'N',
|
||||||
|
':idmatrice' => $part['idmatrice'] !== '' ? $part['idmatrice'] : null,
|
||||||
|
':note' => $part['note'] ?? null,
|
||||||
|
':dateexpiry' => $part['dateexpiry'] ?? null,
|
||||||
|
]);
|
||||||
|
|
||||||
|
$newPartId = (int)$pdo->lastInsertId();
|
||||||
|
|
||||||
|
// Load source custom fields for this part
|
||||||
|
$stmtLoadCF->execute([(int)$part['id']]);
|
||||||
|
$customFields = $stmtLoadCF->fetchAll(PDO::FETCH_ASSOC);
|
||||||
|
|
||||||
|
foreach ($customFields as $cf) {
|
||||||
|
$stmtInsertCF->execute([
|
||||||
|
':part_id' => $newPartId,
|
||||||
|
':field_id' => (int)$cf['field_id'],
|
||||||
|
':value_id' => ($cf['value_id'] !== null && $cf['value_id'] !== '') ? (int)$cf['value_id'] : null,
|
||||||
|
':value_text' => $cf['value_text'] !== '' ? $cf['value_text'] : null,
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
|
||||||
|
$clonedCountForTarget++;
|
||||||
|
$totalClonedParts++;
|
||||||
|
}
|
||||||
|
|
||||||
|
$details[] = [
|
||||||
|
'target_iddatadb' => $targetIddatadb,
|
||||||
|
'cloned_parts' => $clonedCountForTarget
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
$pdo->commit();
|
||||||
|
|
||||||
|
echo json_encode([
|
||||||
|
'success' => true,
|
||||||
|
'source_iddatadb' => $sourceIddatadb,
|
||||||
|
'cloned_targets' => count($targetIds),
|
||||||
|
'total_cloned_parts' => $totalClonedParts,
|
||||||
|
'details' => $details
|
||||||
|
]);
|
||||||
|
} catch (Throwable $e) {
|
||||||
|
if ($pdo->inTransaction()) {
|
||||||
|
$pdo->rollBack();
|
||||||
|
}
|
||||||
|
|
||||||
|
echo json_encode([
|
||||||
|
'success' => false,
|
||||||
|
'message' => 'Clone failed: ' . $e->getMessage()
|
||||||
|
]);
|
||||||
|
}
|
||||||
@@ -0,0 +1,228 @@
|
|||||||
|
<?php
|
||||||
|
require_once 'class/db-functions.php';
|
||||||
|
|
||||||
|
ini_set('display_errors', 1);
|
||||||
|
ini_set('display_startup_errors', 1);
|
||||||
|
error_reporting(E_ALL);
|
||||||
|
|
||||||
|
if (!isset($_GET['id']) || !is_numeric($_GET['id'])) {
|
||||||
|
die("Invalid template ID");
|
||||||
|
}
|
||||||
|
|
||||||
|
$sourceTemplateId = (int)$_GET['id'];
|
||||||
|
|
||||||
|
try {
|
||||||
|
$db = DBHandlerSelect::getInstance();
|
||||||
|
$pdo = $db->getConnection();
|
||||||
|
|
||||||
|
$pdo->beginTransaction();
|
||||||
|
|
||||||
|
// 1. Get source template
|
||||||
|
$stmt = $pdo->prepare("
|
||||||
|
SELECT
|
||||||
|
name,
|
||||||
|
source_type,
|
||||||
|
header_row,
|
||||||
|
start_column,
|
||||||
|
description,
|
||||||
|
target_table,
|
||||||
|
sample_xlsx,
|
||||||
|
button_size,
|
||||||
|
button_bg_color,
|
||||||
|
button_text_color,
|
||||||
|
button_label,
|
||||||
|
status,
|
||||||
|
client_specific_fields,
|
||||||
|
idclient,
|
||||||
|
clientname,
|
||||||
|
schemaname,
|
||||||
|
idschema,
|
||||||
|
schemajson,
|
||||||
|
xls_headers,
|
||||||
|
api_sample_json,
|
||||||
|
json_nodes,
|
||||||
|
idroutine
|
||||||
|
FROM excel_templates
|
||||||
|
WHERE id = ?
|
||||||
|
LIMIT 1
|
||||||
|
");
|
||||||
|
$stmt->execute([$sourceTemplateId]);
|
||||||
|
$template = $stmt->fetch(PDO::FETCH_ASSOC);
|
||||||
|
|
||||||
|
if (!$template) {
|
||||||
|
throw new Exception("Template not found.");
|
||||||
|
}
|
||||||
|
|
||||||
|
// 2. Generate unique clone name
|
||||||
|
$baseName = 'Copia di ' . $template['name'];
|
||||||
|
$newName = $baseName;
|
||||||
|
|
||||||
|
$checkStmt = $pdo->prepare("SELECT COUNT(*) FROM excel_templates WHERE name = ?");
|
||||||
|
$checkStmt->execute([$newName]);
|
||||||
|
|
||||||
|
if ((int)$checkStmt->fetchColumn() > 0) {
|
||||||
|
$counter = 2;
|
||||||
|
|
||||||
|
do {
|
||||||
|
$newName = $baseName . ' (' . $counter . ')';
|
||||||
|
$checkStmt->execute([$newName]);
|
||||||
|
$exists = (int)$checkStmt->fetchColumn() > 0;
|
||||||
|
$counter++;
|
||||||
|
} while ($exists);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 3. Insert cloned template
|
||||||
|
$insertTemplate = $pdo->prepare("
|
||||||
|
INSERT INTO excel_templates (
|
||||||
|
name,
|
||||||
|
source_type,
|
||||||
|
created_at,
|
||||||
|
updated_at,
|
||||||
|
header_row,
|
||||||
|
start_column,
|
||||||
|
description,
|
||||||
|
target_table,
|
||||||
|
sample_xlsx,
|
||||||
|
button_size,
|
||||||
|
button_bg_color,
|
||||||
|
button_text_color,
|
||||||
|
button_label,
|
||||||
|
status,
|
||||||
|
client_specific_fields,
|
||||||
|
idclient,
|
||||||
|
clientname,
|
||||||
|
schemaname,
|
||||||
|
idschema,
|
||||||
|
schemajson,
|
||||||
|
xls_headers,
|
||||||
|
api_sample_json,
|
||||||
|
json_nodes,
|
||||||
|
idroutine
|
||||||
|
) VALUES (
|
||||||
|
:name,
|
||||||
|
:source_type,
|
||||||
|
NOW(),
|
||||||
|
NOW(),
|
||||||
|
:header_row,
|
||||||
|
:start_column,
|
||||||
|
:description,
|
||||||
|
:target_table,
|
||||||
|
:sample_xlsx,
|
||||||
|
:button_size,
|
||||||
|
:button_bg_color,
|
||||||
|
:button_text_color,
|
||||||
|
:button_label,
|
||||||
|
:status,
|
||||||
|
:client_specific_fields,
|
||||||
|
:idclient,
|
||||||
|
:clientname,
|
||||||
|
:schemaname,
|
||||||
|
:idschema,
|
||||||
|
:schemajson,
|
||||||
|
:xls_headers,
|
||||||
|
:api_sample_json,
|
||||||
|
:json_nodes,
|
||||||
|
:idroutine
|
||||||
|
)
|
||||||
|
");
|
||||||
|
|
||||||
|
$insertTemplate->execute([
|
||||||
|
':name' => $newName,
|
||||||
|
':source_type' => $template['source_type'],
|
||||||
|
':header_row' => $template['header_row'],
|
||||||
|
':start_column' => $template['start_column'],
|
||||||
|
':description' => $template['description'],
|
||||||
|
':target_table' => $template['target_table'],
|
||||||
|
':sample_xlsx' => $template['sample_xlsx'],
|
||||||
|
':button_size' => $template['button_size'],
|
||||||
|
':button_bg_color' => $template['button_bg_color'],
|
||||||
|
':button_text_color' => $template['button_text_color'],
|
||||||
|
':button_label' => $template['button_label'],
|
||||||
|
':status' => $template['status'],
|
||||||
|
':client_specific_fields' => $template['client_specific_fields'],
|
||||||
|
':idclient' => $template['idclient'],
|
||||||
|
':clientname' => $template['clientname'],
|
||||||
|
':schemaname' => $template['schemaname'],
|
||||||
|
':idschema' => $template['idschema'],
|
||||||
|
':schemajson' => $template['schemajson'],
|
||||||
|
':xls_headers' => $template['xls_headers'],
|
||||||
|
':api_sample_json' => $template['api_sample_json'],
|
||||||
|
':json_nodes' => $template['json_nodes'],
|
||||||
|
':idroutine' => $template['idroutine']
|
||||||
|
]);
|
||||||
|
|
||||||
|
$newTemplateId = (int)$pdo->lastInsertId();
|
||||||
|
|
||||||
|
if ($newTemplateId <= 0) {
|
||||||
|
throw new Exception("Unable to create cloned template.");
|
||||||
|
}
|
||||||
|
|
||||||
|
// 4. Clone template_mapping rows
|
||||||
|
$cloneMappings = $pdo->prepare("
|
||||||
|
INSERT INTO template_mapping (
|
||||||
|
template_id,
|
||||||
|
schema_id,
|
||||||
|
field_id,
|
||||||
|
excel_column,
|
||||||
|
json_node,
|
||||||
|
is_manual,
|
||||||
|
manual_default,
|
||||||
|
auto_value,
|
||||||
|
data_type,
|
||||||
|
is_required,
|
||||||
|
default_value,
|
||||||
|
has_list,
|
||||||
|
length,
|
||||||
|
decimals,
|
||||||
|
min_value,
|
||||||
|
max_value,
|
||||||
|
default_curr_date,
|
||||||
|
tablename,
|
||||||
|
field_label,
|
||||||
|
main_field,
|
||||||
|
is_visible_import,
|
||||||
|
is_visible_parts
|
||||||
|
)
|
||||||
|
SELECT
|
||||||
|
:new_template_id,
|
||||||
|
schema_id,
|
||||||
|
field_id,
|
||||||
|
excel_column,
|
||||||
|
json_node,
|
||||||
|
is_manual,
|
||||||
|
manual_default,
|
||||||
|
auto_value,
|
||||||
|
data_type,
|
||||||
|
is_required,
|
||||||
|
default_value,
|
||||||
|
has_list,
|
||||||
|
length,
|
||||||
|
decimals,
|
||||||
|
min_value,
|
||||||
|
max_value,
|
||||||
|
default_curr_date,
|
||||||
|
tablename,
|
||||||
|
field_label,
|
||||||
|
main_field,
|
||||||
|
is_visible_import,
|
||||||
|
is_visible_parts
|
||||||
|
FROM template_mapping
|
||||||
|
WHERE template_id = :source_template_id
|
||||||
|
");
|
||||||
|
|
||||||
|
$cloneMappings->execute([
|
||||||
|
':new_template_id' => $newTemplateId,
|
||||||
|
':source_template_id' => $sourceTemplateId
|
||||||
|
]);
|
||||||
|
|
||||||
|
$pdo->commit();
|
||||||
|
|
||||||
|
header("Location: templates_dashboard.php?cloned=1&new_id=" . $newTemplateId);
|
||||||
|
exit;
|
||||||
|
} catch (Exception $e) {
|
||||||
|
if (isset($pdo) && $pdo->inTransaction()) {
|
||||||
|
$pdo->rollBack();
|
||||||
|
}
|
||||||
|
|
||||||
|
die("Clone error: " . htmlspecialchars($e->getMessage(), ENT_QUOTES, 'UTF-8'));
|
||||||
|
}
|
||||||
@@ -30,6 +30,8 @@ if ((int)$stmt->fetchColumn() > 0) {
|
|||||||
$fixedFields = [
|
$fixedFields = [
|
||||||
// fixed_field_key, data_type
|
// fixed_field_key, data_type
|
||||||
['ClienteResponsabile', 'INT'],
|
['ClienteResponsabile', 'INT'],
|
||||||
|
['ClienteFornitore', 'INT'],
|
||||||
|
['ClienteAnalisi', 'INT'],
|
||||||
['MoltiplicatorePrezzo', 'INT'],
|
['MoltiplicatorePrezzo', 'INT'],
|
||||||
['AnagraficaCertestObject', 'INT'],
|
['AnagraficaCertestObject', 'INT'],
|
||||||
['AnagraficaCertestService', 'INT'],
|
['AnagraficaCertestService', 'INT'],
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
@@ -17,6 +17,8 @@ try {
|
|||||||
exit(1);
|
exit(1);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
date_default_timezone_set($_ENV['APP_TIMEZONE'] ?? 'Europe/Rome');
|
||||||
|
|
||||||
// Recupera le variabili d'ambiente
|
// Recupera le variabili d'ambiente
|
||||||
$dbHost = $_ENV['DB_HOST'];
|
$dbHost = $_ENV['DB_HOST'];
|
||||||
$dbName = $_ENV['DB_DATABASE'];
|
$dbName = $_ENV['DB_DATABASE'];
|
||||||
|
|||||||
@@ -0,0 +1 @@
|
|||||||
|
https://93.43.5.102/limsapi/api/odata/Matrice
|
||||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1 @@
|
|||||||
|
2026-04-03 10:07:01 - Aggiornamento completato: 3198 record inseriti.
|
||||||
File diff suppressed because one or more lines are too long
@@ -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
|
||||||
|
-->
|
||||||
@@ -0,0 +1,48 @@
|
|||||||
|
<?php
|
||||||
|
require_once dirname(__DIR__, 2) . '/vendor/autoload.php';
|
||||||
|
require_once __DIR__ . '/class/db-functions.php';
|
||||||
|
|
||||||
|
header('Content-Type: application/json');
|
||||||
|
ini_set('display_errors', '0');
|
||||||
|
error_reporting(E_ALL);
|
||||||
|
|
||||||
|
try {
|
||||||
|
if ($_SERVER['REQUEST_METHOD'] !== 'POST') {
|
||||||
|
http_response_code(405);
|
||||||
|
echo json_encode(['success' => false, 'message' => 'Method not allowed']);
|
||||||
|
exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
$partId = isset($_POST['part_id']) ? (int)$_POST['part_id'] : 0;
|
||||||
|
$analysisRecordkey = trim($_POST['analysis_recordkey'] ?? '');
|
||||||
|
|
||||||
|
if ($partId <= 0 || $analysisRecordkey === '') {
|
||||||
|
http_response_code(400);
|
||||||
|
echo json_encode(['success' => false, 'message' => 'Missing required data']);
|
||||||
|
exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
$db = DBHandlerSelect::getInstance();
|
||||||
|
$pdo = $db->getConnection();
|
||||||
|
|
||||||
|
$stmt = $pdo->prepare("
|
||||||
|
DELETE FROM identification_parts_analyses
|
||||||
|
WHERE part_id = :part_id
|
||||||
|
AND analysis_recordkey = :analysis_recordkey
|
||||||
|
");
|
||||||
|
$stmt->execute([
|
||||||
|
':part_id' => $partId,
|
||||||
|
':analysis_recordkey' => $analysisRecordkey,
|
||||||
|
]);
|
||||||
|
|
||||||
|
echo json_encode([
|
||||||
|
'success' => true,
|
||||||
|
'message' => 'Association deleted'
|
||||||
|
]);
|
||||||
|
} catch (Throwable $e) {
|
||||||
|
http_response_code(500);
|
||||||
|
echo json_encode([
|
||||||
|
'success' => false,
|
||||||
|
'message' => $e->getMessage()
|
||||||
|
]);
|
||||||
|
}
|
||||||
@@ -0,0 +1,112 @@
|
|||||||
|
<?php
|
||||||
|
require_once dirname(__DIR__, 2) . '/vendor/autoload.php';
|
||||||
|
require_once dirname(__FILE__) . '/class/VisualLimsApiClient.class.php';
|
||||||
|
|
||||||
|
ini_set('display_errors', '0');
|
||||||
|
error_reporting(E_ALL);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Uso:
|
||||||
|
* download_rapporto_file.php?id=123
|
||||||
|
*/
|
||||||
|
|
||||||
|
try {
|
||||||
|
$api = VisualLimsApiClient::getInstance();
|
||||||
|
|
||||||
|
$id = isset($_GET['id']) ? intval($_GET['id']) : 0;
|
||||||
|
|
||||||
|
if (!$id) {
|
||||||
|
throw new Exception("ID RapportoFile mancante");
|
||||||
|
}
|
||||||
|
|
||||||
|
$debugDir = __DIR__ . '/logs/lims_get_commessa/';
|
||||||
|
|
||||||
|
if (!is_dir($debugDir)) {
|
||||||
|
mkdir($debugDir, 0755, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Recupero il record RapportoFile.
|
||||||
|
*/
|
||||||
|
$data = $api->get("RapportoFile({$id})", []);
|
||||||
|
|
||||||
|
file_put_contents(
|
||||||
|
$debugDir . "rapporto_file_{$id}.json",
|
||||||
|
json_encode($data, JSON_PRETTY_PRINT | JSON_UNESCAPED_UNICODE)
|
||||||
|
);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Provo vari nomi campo possibili per il contenuto PDF.
|
||||||
|
* Dopo il primo test potremo adattarlo al nome esatto.
|
||||||
|
*/
|
||||||
|
$possibleContentFields = [
|
||||||
|
'File',
|
||||||
|
'file',
|
||||||
|
'Content',
|
||||||
|
'content',
|
||||||
|
'FileContent',
|
||||||
|
'fileContent',
|
||||||
|
'Contenuto',
|
||||||
|
'contenuto',
|
||||||
|
'Bytes',
|
||||||
|
'bytes'
|
||||||
|
];
|
||||||
|
|
||||||
|
$base64 = null;
|
||||||
|
|
||||||
|
foreach ($possibleContentFields as $field) {
|
||||||
|
if (!empty($data[$field])) {
|
||||||
|
$base64 = $data[$field];
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!$base64) {
|
||||||
|
header('Content-Type: application/json; charset=utf-8');
|
||||||
|
echo json_encode([
|
||||||
|
'success' => false,
|
||||||
|
'message' => 'Record RapportoFile recuperato, ma non ho trovato un campo base64 del PDF.',
|
||||||
|
'hint' => 'Apri logs/lims_get_commessa/rapporto_file_' . $id . '.json e controlla il nome reale del campo file.',
|
||||||
|
'data' => $data
|
||||||
|
], JSON_PRETTY_PRINT | JSON_UNESCAPED_UNICODE);
|
||||||
|
exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Se il contenuto ha prefisso data URI, lo pulisco.
|
||||||
|
*/
|
||||||
|
if (strpos($base64, 'base64,') !== false) {
|
||||||
|
$parts = explode('base64,', $base64);
|
||||||
|
$base64 = end($parts);
|
||||||
|
}
|
||||||
|
|
||||||
|
$pdfBinary = base64_decode($base64, true);
|
||||||
|
|
||||||
|
if ($pdfBinary === false) {
|
||||||
|
throw new Exception("Il contenuto trovato non è un base64 valido");
|
||||||
|
}
|
||||||
|
|
||||||
|
$filename = $data['NomeFile']
|
||||||
|
?? $data['nomeFile']
|
||||||
|
?? $data['FileName']
|
||||||
|
?? $data['fileName']
|
||||||
|
?? "rapporto_{$id}.pdf";
|
||||||
|
|
||||||
|
if (!str_ends_with(strtolower($filename), '.pdf')) {
|
||||||
|
$filename .= '.pdf';
|
||||||
|
}
|
||||||
|
|
||||||
|
header('Content-Type: application/pdf');
|
||||||
|
header('Content-Disposition: inline; filename="' . basename($filename) . '"');
|
||||||
|
header('Content-Length: ' . strlen($pdfBinary));
|
||||||
|
|
||||||
|
echo $pdfBinary;
|
||||||
|
} catch (Exception $e) {
|
||||||
|
http_response_code(500);
|
||||||
|
header('Content-Type: application/json; charset=utf-8');
|
||||||
|
|
||||||
|
echo json_encode([
|
||||||
|
'success' => false,
|
||||||
|
'error' => $e->getMessage()
|
||||||
|
], JSON_PRETTY_PRINT | JSON_UNESCAPED_UNICODE);
|
||||||
|
}
|
||||||
@@ -1,29 +1,75 @@
|
|||||||
<?php include('include/headscript.php');
|
<?php include('include/headscript.php');
|
||||||
|
|
||||||
// Controlla se è stato passato un ID valido
|
// Check if a valid ID was provided
|
||||||
if (!isset($_GET['id']) || !is_numeric($_GET['id'])) {
|
if (!isset($_GET['id']) || !is_numeric($_GET['id'])) {
|
||||||
header("Location: xlstemplates_grid.php?status=error&message=" . urlencode("Invalid ID"));
|
header("Location: templates_dashboard.php?status=error&message=" . urlencode("Invalid ID"));
|
||||||
exit;
|
exit;
|
||||||
}
|
}
|
||||||
|
|
||||||
$id = intval($_GET['id']); // Sanifica l'ID
|
$id = intval($_GET['id']);
|
||||||
|
|
||||||
// Recupera il template dal database
|
// Retrieve template from database
|
||||||
$db = DBHandlerSelect::getInstance();
|
$db = DBHandlerSelect::getInstance();
|
||||||
$pdo = $db->getConnection();
|
$pdo = $db->getConnection();
|
||||||
|
|
||||||
$stmt = $pdo->prepare("SELECT * FROM excel_templates WHERE id = ?");
|
$stmt = $pdo->prepare("SELECT * FROM excel_templates WHERE id = ?");
|
||||||
$stmt->execute([$id]);
|
$stmt->execute([$id]);
|
||||||
$template = $stmt->fetch(PDO::FETCH_ASSOC);
|
$template = $stmt->fetch(PDO::FETCH_ASSOC);
|
||||||
|
|
||||||
if (!$template) {
|
if (!$template) {
|
||||||
header("Location: template_dashboard.php?status=error&message=" . urlencode("Template not found"));
|
header("Location: templates_dashboard.php?status=error&message=" . urlencode("Template not found"));
|
||||||
exit;
|
exit;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Recupera tutte le routine dal database
|
// Retrieve all routines
|
||||||
$stmt = $pdo->prepare("SELECT * FROM routine");
|
$stmt = $pdo->prepare("SELECT * FROM routine");
|
||||||
$stmt->execute();
|
$stmt->execute();
|
||||||
$routines = $stmt->fetchAll(PDO::FETCH_ASSOC);
|
$routines = $stmt->fetchAll(PDO::FETCH_ASSOC);
|
||||||
|
|
||||||
|
// Retrieve active API/JSON configurations
|
||||||
|
$stmt = $pdo->prepare("
|
||||||
|
SELECT id, name, provider_code, api_type, php_class_name
|
||||||
|
FROM api_configurations
|
||||||
|
WHERE is_active = 1
|
||||||
|
ORDER BY name ASC
|
||||||
|
");
|
||||||
|
$stmt->execute();
|
||||||
|
$apiConfigurations = $stmt->fetchAll(PDO::FETCH_ASSOC);
|
||||||
|
|
||||||
|
$buttonBgPalette = [
|
||||||
|
'#0d6efd' => 'Blue',
|
||||||
|
'#6610f2' => 'Indigo',
|
||||||
|
'#6f42c1' => 'Purple',
|
||||||
|
'#d63384' => 'Pink',
|
||||||
|
'#dc3545' => 'Red',
|
||||||
|
'#fd7e14' => 'Orange',
|
||||||
|
'#ffc107' => 'Yellow',
|
||||||
|
'#198754' => 'Green',
|
||||||
|
'#20c997' => 'Teal',
|
||||||
|
'#0dcaf0' => 'Cyan',
|
||||||
|
'#212529' => 'Dark',
|
||||||
|
'#6c757d' => 'Gray',
|
||||||
|
'#495057' => 'Slate',
|
||||||
|
'#795548' => 'Brown',
|
||||||
|
'#2f5d50' => 'Sage Green',
|
||||||
|
];
|
||||||
|
|
||||||
|
$buttonTextPalette = [
|
||||||
|
'#ffffff' => 'White',
|
||||||
|
'#6c757d' => 'Gray',
|
||||||
|
'#000000' => 'Black',
|
||||||
|
];
|
||||||
|
|
||||||
|
$currentButtonBgColor = strtolower($template['button_bg_color'] ?? '#007bff');
|
||||||
|
$currentButtonTextColor = strtolower($template['button_text_color'] ?? '#ffffff');
|
||||||
|
|
||||||
|
if (!array_key_exists($currentButtonBgColor, array_change_key_case($buttonBgPalette, CASE_LOWER))) {
|
||||||
|
$currentButtonBgColor = '#0d6efd';
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!array_key_exists($currentButtonTextColor, array_change_key_case($buttonTextPalette, CASE_LOWER))) {
|
||||||
|
$currentButtonTextColor = '#ffffff';
|
||||||
|
}
|
||||||
?>
|
?>
|
||||||
<!doctype html>
|
<!doctype html>
|
||||||
<html lang="en">
|
<html lang="en">
|
||||||
@@ -34,7 +80,64 @@ $routines = $stmt->fetchAll(PDO::FETCH_ASSOC);
|
|||||||
<link rel="icon" href="assets/images/favicon-32x32.png" type="image/png" />
|
<link rel="icon" href="assets/images/favicon-32x32.png" type="image/png" />
|
||||||
<link href="https://cdn.jsdelivr.net/npm/select2@4.1.0-rc.0/dist/css/select2.min.css" rel="stylesheet" />
|
<link href="https://cdn.jsdelivr.net/npm/select2@4.1.0-rc.0/dist/css/select2.min.css" rel="stylesheet" />
|
||||||
<?php include('cssinclude.php'); ?>
|
<?php include('cssinclude.php'); ?>
|
||||||
<!-- Include jQuery prima di Select2 -->
|
<style>
|
||||||
|
.color-palette {
|
||||||
|
display: flex;
|
||||||
|
flex-wrap: wrap;
|
||||||
|
gap: 10px;
|
||||||
|
margin-top: 8px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.color-option {
|
||||||
|
position: relative;
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
|
||||||
|
.color-option input {
|
||||||
|
position: absolute;
|
||||||
|
opacity: 0;
|
||||||
|
pointer-events: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.color-swatch {
|
||||||
|
width: 42px;
|
||||||
|
height: 42px;
|
||||||
|
border-radius: 10px;
|
||||||
|
border: 2px solid #d6dbe0;
|
||||||
|
display: inline-flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
box-shadow: 0 2px 6px rgba(0, 0, 0, 0.12);
|
||||||
|
transition: all 0.18s ease;
|
||||||
|
}
|
||||||
|
|
||||||
|
.color-option input:checked+.color-swatch {
|
||||||
|
border: 3px solid #111827;
|
||||||
|
transform: scale(1.08);
|
||||||
|
box-shadow: 0 0 0 4px rgba(13, 110, 253, 0.18);
|
||||||
|
}
|
||||||
|
|
||||||
|
.color-swatch .check-mark {
|
||||||
|
display: none;
|
||||||
|
font-size: 18px;
|
||||||
|
font-weight: 700;
|
||||||
|
color: #ffffff;
|
||||||
|
text-shadow: 0 1px 3px rgba(0, 0, 0, 0.7);
|
||||||
|
}
|
||||||
|
|
||||||
|
.color-option input:checked+.color-swatch .check-mark {
|
||||||
|
display: inline-block;
|
||||||
|
}
|
||||||
|
|
||||||
|
.color-name {
|
||||||
|
display: block;
|
||||||
|
font-size: 11px;
|
||||||
|
text-align: center;
|
||||||
|
margin-top: 4px;
|
||||||
|
color: #555;
|
||||||
|
max-width: 55px;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
<script src="https://code.jquery.com/jquery-3.6.0.min.js" integrity="sha256-/xUj+3OJU5yExlq6GSYGSHk7tPXikynS7ogEvDej/m4=" crossorigin="anonymous"></script>
|
<script src="https://code.jquery.com/jquery-3.6.0.min.js" integrity="sha256-/xUj+3OJU5yExlq6GSYGSHk7tPXikynS7ogEvDej/m4=" crossorigin="anonymous"></script>
|
||||||
<script src="https://cdn.jsdelivr.net/npm/select2@4.1.0-rc.0/dist/js/select2.min.js"></script>
|
<script src="https://cdn.jsdelivr.net/npm/select2@4.1.0-rc.0/dist/js/select2.min.js"></script>
|
||||||
<title>Edit Template <?= htmlspecialchars($titlewebsite, ENT_QUOTES, 'UTF-8'); ?></title>
|
<title>Edit Template <?= htmlspecialchars($titlewebsite, ENT_QUOTES, 'UTF-8'); ?></title>
|
||||||
@@ -44,19 +147,22 @@ $routines = $stmt->fetchAll(PDO::FETCH_ASSOC);
|
|||||||
<div class="wrapper">
|
<div class="wrapper">
|
||||||
<?php include('include/navbar.php'); ?>
|
<?php include('include/navbar.php'); ?>
|
||||||
<?php include('include/topbar.php'); ?>
|
<?php include('include/topbar.php'); ?>
|
||||||
|
|
||||||
<div class="page-wrapper">
|
<div class="page-wrapper">
|
||||||
<div class="page-content">
|
<div class="page-content">
|
||||||
|
|
||||||
<div class="card mb-4">
|
<div class="card mb-4">
|
||||||
<div class="card-header">
|
<div class="card-header">
|
||||||
<h5 class="mb-0">Update XLS Template</h5>
|
<h5 class="mb-0">Update Template</h5>
|
||||||
</div>
|
</div>
|
||||||
<div class="card-body">
|
<div class="card-body">
|
||||||
<p class="mb-2">Edit the following form in order to update the selected import XLS template</p>
|
<p class="mb-2">Edit the following form in order to update the selected import template</p>
|
||||||
<p class="mb-2">Mandatory Fields</p>
|
<p class="mb-2">Mandatory Fields</p>
|
||||||
<ul class="mb-0">
|
<ul class="mb-0">
|
||||||
<li>Template Name</li>
|
<li>Template Name</li>
|
||||||
<li>Row Header and Column Header: where the title of the excel starts</li>
|
<li>Source Type</li>
|
||||||
<li>Schema</li>
|
<li>Schema and Client</li>
|
||||||
|
<li>Row Header and Column Header only for XLS templates</li>
|
||||||
</ul>
|
</ul>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -69,34 +175,100 @@ $routines = $stmt->fetchAll(PDO::FETCH_ASSOC);
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="card-body">
|
<div class="card-body">
|
||||||
<div class="col-12">
|
<div class="col-12">
|
||||||
<form id="editTemplateForm" method="POST">
|
<form id="editTemplateForm" method="POST">
|
||||||
<input type="hidden" name="id" value="<?php echo $template['id']; ?>">
|
<input type="hidden" name="id" value="<?php echo (int)$template['id']; ?>">
|
||||||
|
|
||||||
<div class="mb-3">
|
<div class="mb-3">
|
||||||
<label class="form-label"><?= htmlspecialchars($templatename, ENT_QUOTES, 'UTF-8'); ?> *</label>
|
<label class="form-label"><?= htmlspecialchars($templatename, ENT_QUOTES, 'UTF-8'); ?> *</label>
|
||||||
<input type="text" name="name" class="form-control" value="<?php echo htmlspecialchars($template['name']); ?>" required>
|
<input type="text" name="name" class="form-control" value="<?php echo htmlspecialchars($template['name'] ?? ''); ?>" required>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="mb-3">
|
<div class="mb-3">
|
||||||
|
<label class="form-label">Source Type *</label>
|
||||||
|
<select name="source_type" id="sourceType" class="form-control" required>
|
||||||
|
<option value="XLS" <?php echo (($template['source_type'] ?? 'XLS') === 'XLS') ? 'selected' : ''; ?>>XLS</option>
|
||||||
|
<option value="API" <?php echo (($template['source_type'] ?? 'XLS') === 'API') ? 'selected' : ''; ?>>API</option>
|
||||||
|
<option value="JSON" <?php echo (($template['source_type'] ?? 'XLS') === 'JSON') ? 'selected' : ''; ?>>JSON</option>
|
||||||
|
<option value="PDF" <?php echo (($template['source_type'] ?? 'XLS') === 'PDF') ? 'selected' : ''; ?>>PDF</option>
|
||||||
|
</select>
|
||||||
|
<small class="text-muted">Choose the source used by this template</small>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="mb-3" id="headerRowWrapper">
|
||||||
<label class="form-label"><?= htmlspecialchars($rowheader, ENT_QUOTES, 'UTF-8'); ?> *</label>
|
<label class="form-label"><?= htmlspecialchars($rowheader, ENT_QUOTES, 'UTF-8'); ?> *</label>
|
||||||
<input type="number" name="header_row" class="form-control" value="<?php echo $template['header_row']; ?>" required>
|
<input type="number" name="header_row" id="headerRow" class="form-control" value="<?php echo htmlspecialchars($template['header_row'] ?? ''); ?>">
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="mb-3">
|
<div class="mb-3" id="startColumnWrapper">
|
||||||
<label class="form-label"><?= htmlspecialchars($columnheader, ENT_QUOTES, 'UTF-8'); ?>*</label>
|
<label class="form-label"><?= htmlspecialchars($columnheader, ENT_QUOTES, 'UTF-8'); ?> *</label>
|
||||||
<input type="text" name="start_column" class="form-control" value="<?php echo htmlspecialchars($template['start_column']); ?>" required>
|
<input type="text" name="start_column" id="startColumn" class="form-control" value="<?php echo htmlspecialchars($template['start_column'] ?? ''); ?>">
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="mb-3" id="xlsSheetNumberWrapper">
|
||||||
|
<label class="form-label">XLS Sheet Number</label>
|
||||||
|
<input
|
||||||
|
type="number"
|
||||||
|
name="xls_sheet_index"
|
||||||
|
id="xlsSheetIndex"
|
||||||
|
class="form-control"
|
||||||
|
min="0"
|
||||||
|
value="<?php echo htmlspecialchars($template['xls_sheet_index'] ?? 0); ?>">
|
||||||
|
<small class="text-muted">
|
||||||
|
Use 0 for the first sheet, 1 for the second sheet, 2 for the third sheet, and so on.
|
||||||
|
</small>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="mb-3" id="apiConfigWrapper" style="display: none;">
|
||||||
|
<label class="form-label">API / JSON Configuration *</label>
|
||||||
|
<select name="api_config_id" id="apiConfigSelect" class="form-control">
|
||||||
|
<option value="">Select an API configuration...</option>
|
||||||
|
|
||||||
|
<?php foreach ($apiConfigurations as $apiConfig): ?>
|
||||||
|
<?php
|
||||||
|
$apiLabelParts = [];
|
||||||
|
|
||||||
|
if (!empty($apiConfig['name'])) {
|
||||||
|
$apiLabelParts[] = $apiConfig['name'];
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!empty($apiConfig['provider_code'])) {
|
||||||
|
$apiLabelParts[] = '[' . $apiConfig['provider_code'] . ']';
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!empty($apiConfig['api_type'])) {
|
||||||
|
$apiLabelParts[] = '(' . $apiConfig['api_type'] . ')';
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!empty($apiConfig['php_class_name'])) {
|
||||||
|
$apiLabelParts[] = '- ' . $apiConfig['php_class_name'];
|
||||||
|
}
|
||||||
|
|
||||||
|
$apiLabel = implode(' ', $apiLabelParts);
|
||||||
|
?>
|
||||||
|
|
||||||
|
<option
|
||||||
|
value="<?php echo (int)$apiConfig['id']; ?>"
|
||||||
|
<?php echo ((int)($template['api_config_id'] ?? 0) === (int)$apiConfig['id']) ? 'selected' : ''; ?>>
|
||||||
|
<?php echo htmlspecialchars($apiLabel, ENT_QUOTES, 'UTF-8'); ?>
|
||||||
|
</option>
|
||||||
|
<?php endforeach; ?>
|
||||||
|
</select>
|
||||||
|
<small class="text-muted">
|
||||||
|
Select the API/JSON configuration linked to this template.
|
||||||
|
</small>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="mb-3">
|
<div class="mb-3">
|
||||||
<label class="form-label"><?= htmlspecialchars($desctemplate, ENT_QUOTES, 'UTF-8'); ?></label>
|
<label class="form-label"><?= htmlspecialchars($desctemplate, ENT_QUOTES, 'UTF-8'); ?></label>
|
||||||
<textarea name="description" class="form-control"><?php echo htmlspecialchars($template['description']); ?></textarea>
|
<textarea name="description" class="form-control"><?php echo htmlspecialchars($template['description'] ?? ''); ?></textarea>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="mb-3">
|
<div class="mb-3">
|
||||||
<label class="form-label"><?= htmlspecialchars($desttable, ENT_QUOTES, 'UTF-8'); ?>*</label>
|
<label class="form-label"><?= htmlspecialchars($desttable, ENT_QUOTES, 'UTF-8'); ?> *</label>
|
||||||
<input type="text" name="target_table" class="form-control" value="<?php echo htmlspecialchars($template['target_table']); ?>" readonly required>
|
<input type="text" name="target_table" class="form-control" value="<?php echo htmlspecialchars($template['target_table'] ?? 'datadb'); ?>" readonly required>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="mb-3">
|
<div class="mb-3">
|
||||||
@@ -110,12 +282,46 @@ $routines = $stmt->fetchAll(PDO::FETCH_ASSOC);
|
|||||||
|
|
||||||
<div class="mb-3">
|
<div class="mb-3">
|
||||||
<label class="form-label">Button Background Color</label>
|
<label class="form-label">Button Background Color</label>
|
||||||
<input type="color" name="button_bg_color" class="form-control" value="<?php echo htmlspecialchars($template['button_bg_color'] ?? '#007bff'); ?>">
|
|
||||||
|
<div class="color-palette">
|
||||||
|
<?php foreach ($buttonBgPalette as $colorValue => $colorLabel): ?>
|
||||||
|
<?php $isChecked = strtolower($colorValue) === $currentButtonBgColor; ?>
|
||||||
|
|
||||||
|
<label class="color-option" title="<?= htmlspecialchars($colorLabel, ENT_QUOTES, 'UTF-8'); ?>">
|
||||||
|
<input
|
||||||
|
type="radio"
|
||||||
|
name="button_bg_color"
|
||||||
|
value="<?= htmlspecialchars($colorValue, ENT_QUOTES, 'UTF-8'); ?>"
|
||||||
|
<?= $isChecked ? 'checked' : ''; ?>>
|
||||||
|
<span class="color-swatch" style="background-color: <?= htmlspecialchars($colorValue, ENT_QUOTES, 'UTF-8'); ?>;">
|
||||||
|
<span class="check-mark">✓</span>
|
||||||
|
</span>
|
||||||
|
<span class="color-name"><?= htmlspecialchars($colorLabel, ENT_QUOTES, 'UTF-8'); ?></span>
|
||||||
|
</label>
|
||||||
|
<?php endforeach; ?>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="mb-3">
|
<div class="mb-3">
|
||||||
<label class="form-label">Button Text Color</label>
|
<label class="form-label">Button Text Color</label>
|
||||||
<input type="color" name="button_text_color" class="form-control" value="<?php echo htmlspecialchars($template['button_text_color'] ?? '#ffffff'); ?>">
|
|
||||||
|
<div class="color-palette">
|
||||||
|
<?php foreach ($buttonTextPalette as $colorValue => $colorLabel): ?>
|
||||||
|
<?php $isChecked = strtolower($colorValue) === $currentButtonTextColor; ?>
|
||||||
|
|
||||||
|
<label class="color-option" title="<?= htmlspecialchars($colorLabel, ENT_QUOTES, 'UTF-8'); ?>">
|
||||||
|
<input
|
||||||
|
type="radio"
|
||||||
|
name="button_text_color"
|
||||||
|
value="<?= htmlspecialchars($colorValue, ENT_QUOTES, 'UTF-8'); ?>"
|
||||||
|
<?= $isChecked ? 'checked' : ''; ?>>
|
||||||
|
<span class="color-swatch" style="background-color: <?= htmlspecialchars($colorValue, ENT_QUOTES, 'UTF-8'); ?>;">
|
||||||
|
<span class="check-mark">✓</span>
|
||||||
|
</span>
|
||||||
|
<span class="color-name"><?= htmlspecialchars($colorLabel, ENT_QUOTES, 'UTF-8'); ?></span>
|
||||||
|
</label>
|
||||||
|
<?php endforeach; ?>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="mb-3">
|
<div class="mb-3">
|
||||||
@@ -128,7 +334,7 @@ $routines = $stmt->fetchAll(PDO::FETCH_ASSOC);
|
|||||||
<select name="client_id" id="clientSelect" class="form-control" required>
|
<select name="client_id" id="clientSelect" class="form-control" required>
|
||||||
<option value="">Select a client...</option>
|
<option value="">Select a client...</option>
|
||||||
</select>
|
</select>
|
||||||
<span id="clientLoadingStatus" class="text-muted" style="margin-left: 10px; display: none;">Recupero clienti in corso...</span>
|
<span id="clientLoadingStatus" class="text-muted" style="margin-left: 10px; display: none;">Loading clients...</span>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="mb-3">
|
<div class="mb-3">
|
||||||
@@ -136,7 +342,7 @@ $routines = $stmt->fetchAll(PDO::FETCH_ASSOC);
|
|||||||
<select name="schema_id" id="schemaSelect" class="form-control" required>
|
<select name="schema_id" id="schemaSelect" class="form-control" required>
|
||||||
<option value="">Select a schema...</option>
|
<option value="">Select a schema...</option>
|
||||||
</select>
|
</select>
|
||||||
<span id="schemaLoadingStatus" class="text-muted" style="margin-left: 10px; display: none;">Caricamento schemi in corso...</span>
|
<span id="schemaLoadingStatus" class="text-muted" style="margin-left: 10px; display: none;">Loading schemas...</span>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="mb-3">
|
<div class="mb-3">
|
||||||
@@ -144,11 +350,12 @@ $routines = $stmt->fetchAll(PDO::FETCH_ASSOC);
|
|||||||
<select name="idroutine" id="routineSelect" class="form-control">
|
<select name="idroutine" id="routineSelect" class="form-control">
|
||||||
<option value="">Select a routine...</option>
|
<option value="">Select a routine...</option>
|
||||||
<?php foreach ($routines as $routine): ?>
|
<?php foreach ($routines as $routine): ?>
|
||||||
<option value="<?php echo $routine['idroutine']; ?>" <?php echo ($template['idroutine'] ?? '') == $routine['idroutine'] ? 'selected' : ''; ?>>
|
<option value="<?php echo $routine['idroutine']; ?>" <?php echo (($template['idroutine'] ?? '') == $routine['idroutine']) ? 'selected' : ''; ?>>
|
||||||
<?php echo htmlspecialchars($routine['name']); ?>
|
<?php echo htmlspecialchars($routine['name']); ?>
|
||||||
</option>
|
</option>
|
||||||
<?php endforeach; ?>
|
<?php endforeach; ?>
|
||||||
</select>
|
</select>
|
||||||
|
|
||||||
<div id="routineDetails" class="mt-2" style="display: none;">
|
<div id="routineDetails" class="mt-2" style="display: none;">
|
||||||
<h6>Routine Details</h6>
|
<h6>Routine Details</h6>
|
||||||
<p><strong>Name:</strong> <span id="routineName"></span></p>
|
<p><strong>Name:</strong> <span id="routineName"></span></p>
|
||||||
@@ -166,8 +373,10 @@ $routines = $stmt->fetchAll(PDO::FETCH_ASSOC);
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="overlay toggle-icon"></div>
|
<div class="overlay toggle-icon"></div>
|
||||||
<a href="javaScript:;" class="back-to-top"><i class='bx bxs-up-arrow-alt'></i></a>
|
<a href="javaScript:;" class="back-to-top"><i class='bx bxs-up-arrow-alt'></i></a>
|
||||||
<?php include('include/footer.php'); ?>
|
<?php include('include/footer.php'); ?>
|
||||||
@@ -175,9 +384,8 @@ $routines = $stmt->fetchAll(PDO::FETCH_ASSOC);
|
|||||||
|
|
||||||
<script>
|
<script>
|
||||||
document.addEventListener("DOMContentLoaded", function() {
|
document.addEventListener("DOMContentLoaded", function() {
|
||||||
// Verifica che jQuery sia caricato
|
|
||||||
if (typeof jQuery === 'undefined') {
|
if (typeof jQuery === 'undefined') {
|
||||||
alert("Errore: jQuery non è caricato. Contatta l'amministratore.");
|
alert("Error: jQuery is not loaded.");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -192,12 +400,21 @@ $routines = $stmt->fetchAll(PDO::FETCH_ASSOC);
|
|||||||
const routineAction2 = document.getElementById("routineAction2");
|
const routineAction2 = document.getElementById("routineAction2");
|
||||||
const routineAction3 = document.getElementById("routineAction3");
|
const routineAction3 = document.getElementById("routineAction3");
|
||||||
|
|
||||||
if (!form || !clientLoadingStatus || !schemaLoadingStatus || !routineSelect || !routineDetails) {
|
const sourceType = document.getElementById("sourceType");
|
||||||
alert("Errore: Uno o più elementi della pagina non sono stati trovati. Contatta l'amministratore.");
|
|
||||||
return;
|
const headerRowWrapper = document.getElementById("headerRowWrapper");
|
||||||
}
|
const startColumnWrapper = document.getElementById("startColumnWrapper");
|
||||||
|
const xlsSheetNumberWrapper = document.getElementById("xlsSheetNumberWrapper");
|
||||||
|
const apiConfigWrapper = document.getElementById("apiConfigWrapper");
|
||||||
|
|
||||||
|
const headerRow = document.getElementById("headerRow");
|
||||||
|
const startColumn = document.getElementById("startColumn");
|
||||||
|
const xlsSheetIndex = document.getElementById("xlsSheetIndex");
|
||||||
|
const apiConfigSelect = document.getElementById("apiConfigSelect");
|
||||||
|
|
||||||
|
const selectedClientId = <?php echo json_encode((int)($template['idclient'] ?? 0)); ?>;
|
||||||
|
const selectedSchemaId = <?php echo json_encode((int)($template['idschema'] ?? 0)); ?>;
|
||||||
|
|
||||||
// Inizializza Select2
|
|
||||||
$('#clientSelect').select2({
|
$('#clientSelect').select2({
|
||||||
placeholder: "Search for a client...",
|
placeholder: "Search for a client...",
|
||||||
allowClear: true
|
allowClear: true
|
||||||
@@ -213,108 +430,201 @@ $routines = $stmt->fetchAll(PDO::FETCH_ASSOC);
|
|||||||
allowClear: true
|
allowClear: true
|
||||||
});
|
});
|
||||||
|
|
||||||
// Carica i clienti
|
$('#apiConfigSelect').select2({
|
||||||
|
placeholder: "Select an API configuration...",
|
||||||
|
allowClear: true
|
||||||
|
});
|
||||||
|
|
||||||
|
function updateSourceFields() {
|
||||||
|
const selectedSource = sourceType.value;
|
||||||
|
|
||||||
|
const isXls = selectedSource === 'XLS';
|
||||||
|
const isApiOrJson = selectedSource === 'API' || selectedSource === 'JSON';
|
||||||
|
|
||||||
|
if (isXls) {
|
||||||
|
headerRowWrapper.style.display = 'block';
|
||||||
|
startColumnWrapper.style.display = 'block';
|
||||||
|
xlsSheetNumberWrapper.style.display = 'block';
|
||||||
|
|
||||||
|
headerRow.required = true;
|
||||||
|
startColumn.required = true;
|
||||||
|
|
||||||
|
headerRow.disabled = false;
|
||||||
|
startColumn.disabled = false;
|
||||||
|
xlsSheetIndex.disabled = false;
|
||||||
|
|
||||||
|
apiConfigWrapper.style.display = 'none';
|
||||||
|
apiConfigSelect.required = false;
|
||||||
|
apiConfigSelect.disabled = true;
|
||||||
|
$('#apiConfigSelect').val(null).trigger('change');
|
||||||
|
} else {
|
||||||
|
headerRowWrapper.style.display = 'none';
|
||||||
|
startColumnWrapper.style.display = 'none';
|
||||||
|
xlsSheetNumberWrapper.style.display = 'none';
|
||||||
|
|
||||||
|
headerRow.required = false;
|
||||||
|
startColumn.required = false;
|
||||||
|
|
||||||
|
headerRow.disabled = true;
|
||||||
|
startColumn.disabled = true;
|
||||||
|
xlsSheetIndex.disabled = true;
|
||||||
|
|
||||||
|
if (isApiOrJson) {
|
||||||
|
apiConfigWrapper.style.display = 'block';
|
||||||
|
apiConfigSelect.required = true;
|
||||||
|
apiConfigSelect.disabled = false;
|
||||||
|
} else {
|
||||||
|
apiConfigWrapper.style.display = 'none';
|
||||||
|
apiConfigSelect.required = false;
|
||||||
|
apiConfigSelect.disabled = true;
|
||||||
|
$('#apiConfigSelect').val(null).trigger('change');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
sourceType.addEventListener('change', updateSourceFields);
|
||||||
|
updateSourceFields();
|
||||||
|
|
||||||
|
function formatClientLabel(client) {
|
||||||
|
const nome = client.Nominativo || "Name not available";
|
||||||
|
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 loadClients() {
|
async function loadClients() {
|
||||||
try {
|
try {
|
||||||
clientLoadingStatus.style.display = 'inline';
|
clientLoadingStatus.style.display = 'inline';
|
||||||
clientLoadingStatus.textContent = 'Recupero clienti in corso...';
|
clientLoadingStatus.textContent = 'Loading clients...';
|
||||||
|
|
||||||
const response = await fetch("get_clienti.php", {
|
const response = await fetch("get_clienti.php", {
|
||||||
method: "GET",
|
method: "GET",
|
||||||
headers: {
|
headers: {
|
||||||
"Content-Type": "application/json"
|
"Content-Type": "application/json"
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
const data = await response.json();
|
const data = await response.json();
|
||||||
if (!response.ok) throw new Error(data.error || `Errore HTTP: ${response.status}`);
|
|
||||||
|
if (!response.ok) {
|
||||||
|
throw new Error(data.error || `HTTP error: ${response.status}`);
|
||||||
|
}
|
||||||
|
|
||||||
const select = document.getElementById("clientSelect");
|
const select = document.getElementById("clientSelect");
|
||||||
select.innerHTML = '<option value="">Select a client...</option>';
|
select.innerHTML = '<option value="">Select a client...</option>';
|
||||||
|
|
||||||
data.value.forEach(client => {
|
data.value.forEach(client => {
|
||||||
const nome = client.Nominativo || "Nome non disponibile";
|
const id = client.IdCliente || "";
|
||||||
const id = client.IdCliente || "ID non disponibile";
|
const option = new Option(formatClientLabel(client), id);
|
||||||
const option = new Option(`${nome.trim()} (ID: ${id})`, id);
|
|
||||||
if (parseInt(id) === parseInt(<?php echo json_encode($template['idclient'] ?? 0); ?>)) {
|
if (parseInt(id) === parseInt(selectedClientId)) {
|
||||||
option.selected = true;
|
option.selected = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
select.add(option);
|
select.add(option);
|
||||||
});
|
});
|
||||||
|
|
||||||
$(select).trigger('change');
|
$(select).trigger('change');
|
||||||
clientLoadingStatus.textContent = "Clienti caricati.";
|
clientLoadingStatus.textContent = "Clients loaded.";
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
clientLoadingStatus.textContent = "Errore nel caricamento.";
|
clientLoadingStatus.textContent = "Loading error.";
|
||||||
|
|
||||||
Swal.fire({
|
Swal.fire({
|
||||||
title: "Errore!",
|
title: "Error!",
|
||||||
text: "Impossibile caricare i clienti: " + error.message,
|
text: "Unable to load clients: " + error.message,
|
||||||
icon: "error",
|
icon: "error",
|
||||||
confirmButtonText: "OK"
|
confirmButtonText: "OK"
|
||||||
});
|
});
|
||||||
} finally {
|
} finally {
|
||||||
setTimeout(() => clientLoadingStatus.style.display = 'none', 2000);
|
setTimeout(() => clientLoadingStatus.style.display = 'none', 1500);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Carica gli schemi
|
|
||||||
async function loadSchemas() {
|
async function loadSchemas() {
|
||||||
try {
|
try {
|
||||||
schemaLoadingStatus.style.display = 'inline';
|
schemaLoadingStatus.style.display = 'inline';
|
||||||
schemaLoadingStatus.textContent = 'Caricamento schemi in corso...';
|
schemaLoadingStatus.textContent = 'Loading schemas...';
|
||||||
|
|
||||||
const response = await fetch("get_schemi.php", {
|
const response = await fetch("get_schemi.php", {
|
||||||
method: "GET",
|
method: "GET",
|
||||||
headers: {
|
headers: {
|
||||||
"Content-Type": "application/json"
|
"Content-Type": "application/json"
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
const data = await response.json();
|
const data = await response.json();
|
||||||
if (!response.ok) throw new Error(data.error || `Errore HTTP: ${response.status}`);
|
|
||||||
|
if (!response.ok) {
|
||||||
|
throw new Error(data.error || `HTTP error: ${response.status}`);
|
||||||
|
}
|
||||||
|
|
||||||
const select = document.getElementById("schemaSelect");
|
const select = document.getElementById("schemaSelect");
|
||||||
select.innerHTML = '<option value="">Select a schema...</option>';
|
select.innerHTML = '<option value="">Select a schema...</option>';
|
||||||
data.value.forEach(schema => {
|
|
||||||
const nome = schema.Nome || "Nome non disponibile";
|
const sortedSchemas = [...data.value].sort((a, b) => {
|
||||||
const id = schema.IdSchemaCustomFields || "ID non disponibile";
|
const nomeA = (a.Nome || "").trim().toLowerCase();
|
||||||
|
const nomeB = (b.Nome || "").trim().toLowerCase();
|
||||||
|
return nomeA.localeCompare(nomeB, 'it', {
|
||||||
|
sensitivity: 'base'
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
sortedSchemas.forEach(schema => {
|
||||||
|
const nome = schema.Nome || "Name not available";
|
||||||
|
const id = schema.IdSchemaCustomFields || "";
|
||||||
const option = new Option(`${nome.trim()} (ID: ${id})`, id);
|
const option = new Option(`${nome.trim()} (ID: ${id})`, id);
|
||||||
if (parseInt(id) === parseInt(<?php echo json_encode($template['idschema'] ?? 0); ?>)) {
|
|
||||||
|
if (parseInt(id) === parseInt(selectedSchemaId)) {
|
||||||
option.selected = true;
|
option.selected = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
select.add(option);
|
select.add(option);
|
||||||
});
|
});
|
||||||
|
|
||||||
$(select).trigger('change');
|
$(select).trigger('change');
|
||||||
schemaLoadingStatus.textContent = "Schemi caricati.";
|
schemaLoadingStatus.textContent = "Schemas loaded.";
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
schemaLoadingStatus.textContent = "Errore nel caricamento.";
|
schemaLoadingStatus.textContent = "Loading error.";
|
||||||
|
|
||||||
Swal.fire({
|
Swal.fire({
|
||||||
title: "Errore!",
|
title: "Error!",
|
||||||
text: "Impossibile caricare gli schemi: " + error.message,
|
text: "Unable to load schemas: " + error.message,
|
||||||
icon: "error",
|
icon: "error",
|
||||||
confirmButtonText: "OK"
|
confirmButtonText: "OK"
|
||||||
});
|
});
|
||||||
} finally {
|
} finally {
|
||||||
setTimeout(() => schemaLoadingStatus.style.display = 'none', 2000);
|
setTimeout(() => schemaLoadingStatus.style.display = 'none', 1500);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Carica i dati
|
|
||||||
async function loadData() {
|
async function loadData() {
|
||||||
try {
|
try {
|
||||||
await loadClients();
|
await loadClients();
|
||||||
await loadSchemas();
|
await loadSchemas();
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
Swal.fire({
|
Swal.fire({
|
||||||
title: "Errore!",
|
title: "Error!",
|
||||||
text: "Errore nel caricamento dei dati: " + error.message,
|
text: "Error while loading data: " + error.message,
|
||||||
icon: "error",
|
icon: "error",
|
||||||
confirmButtonText: "OK"
|
confirmButtonText: "OK"
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
loadData();
|
loadData();
|
||||||
|
|
||||||
// Routine dettagli
|
|
||||||
const routines = <?php echo json_encode($routines); ?>;
|
const routines = <?php echo json_encode($routines); ?>;
|
||||||
|
|
||||||
function updateRoutineDetails() {
|
function updateRoutineDetails() {
|
||||||
const selectedId = routineSelect.value;
|
const selectedId = routineSelect.value;
|
||||||
routineDetails.style.display = selectedId ? 'block' : 'none';
|
routineDetails.style.display = selectedId ? 'block' : 'none';
|
||||||
|
|
||||||
if (selectedId) {
|
if (selectedId) {
|
||||||
const routine = routines.find(r => r.idroutine == selectedId);
|
const routine = routines.find(r => r.idroutine == selectedId);
|
||||||
|
|
||||||
if (routine) {
|
if (routine) {
|
||||||
routineName.textContent = routine.name || 'N/A';
|
routineName.textContent = routine.name || 'N/A';
|
||||||
routineDescription.textContent = routine.description || 'N/A';
|
routineDescription.textContent = routine.description || 'N/A';
|
||||||
@@ -336,10 +646,10 @@ $routines = $stmt->fetchAll(PDO::FETCH_ASSOC);
|
|||||||
routineAction3.textContent = '';
|
routineAction3.textContent = '';
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
routineSelect.addEventListener('change', updateRoutineDetails);
|
|
||||||
updateRoutineDetails(); // Inizializza dettagli se una routine è preselezionata
|
|
||||||
|
|
||||||
// Submit del form
|
routineSelect.addEventListener('change', updateRoutineDetails);
|
||||||
|
updateRoutineDetails();
|
||||||
|
|
||||||
form.addEventListener("submit", function(e) {
|
form.addEventListener("submit", function(e) {
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
|
|
||||||
@@ -351,8 +661,8 @@ $routines = $stmt->fetchAll(PDO::FETCH_ASSOC);
|
|||||||
|
|
||||||
if (!clientId) {
|
if (!clientId) {
|
||||||
Swal.fire({
|
Swal.fire({
|
||||||
title: "Errore!",
|
title: "Error!",
|
||||||
text: "Per favore seleziona un cliente.",
|
text: "Please select a client.",
|
||||||
icon: "error",
|
icon: "error",
|
||||||
confirmButtonText: "OK"
|
confirmButtonText: "OK"
|
||||||
});
|
});
|
||||||
@@ -373,8 +683,8 @@ $routines = $stmt->fetchAll(PDO::FETCH_ASSOC);
|
|||||||
|
|
||||||
if (!schemaId) {
|
if (!schemaId) {
|
||||||
Swal.fire({
|
Swal.fire({
|
||||||
title: "Errore!",
|
title: "Error!",
|
||||||
text: "Per favore seleziona uno schema.",
|
text: "Please select a schema.",
|
||||||
icon: "error",
|
icon: "error",
|
||||||
confirmButtonText: "OK"
|
confirmButtonText: "OK"
|
||||||
});
|
});
|
||||||
@@ -387,13 +697,35 @@ $routines = $stmt->fetchAll(PDO::FETCH_ASSOC);
|
|||||||
const nameMatch = optionText.match(/^(.+?)(?:\s*\(ID:\s*\d+\))?$/);
|
const nameMatch = optionText.match(/^(.+?)(?:\s*\(ID:\s*\d+\))?$/);
|
||||||
schemaName = nameMatch ? nameMatch[1].trim() : optionText;
|
schemaName = nameMatch ? nameMatch[1].trim() : optionText;
|
||||||
}
|
}
|
||||||
|
|
||||||
formData.append("idschema", schemaId);
|
formData.append("idschema", schemaId);
|
||||||
formData.append("schemaname", schemaName);
|
formData.append("schemaname", schemaName);
|
||||||
|
|
||||||
// Aggiungi idroutine
|
|
||||||
const routineId = routineSelect.value;
|
const routineId = routineSelect.value;
|
||||||
formData.append("idroutine", routineId);
|
formData.append("idroutine", routineId);
|
||||||
|
|
||||||
|
const selectedSource = sourceType.value;
|
||||||
|
|
||||||
|
if ((selectedSource === 'API' || selectedSource === 'JSON') && !apiConfigSelect.value) {
|
||||||
|
Swal.fire({
|
||||||
|
title: "Error!",
|
||||||
|
text: "Please select an API/JSON configuration.",
|
||||||
|
icon: "error",
|
||||||
|
confirmButtonText: "OK"
|
||||||
|
});
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (selectedSource === 'XLS' && xlsSheetIndex.value === '') {
|
||||||
|
Swal.fire({
|
||||||
|
title: "Error!",
|
||||||
|
text: "Please enter the XLS sheet number.",
|
||||||
|
icon: "error",
|
||||||
|
confirmButtonText: "OK"
|
||||||
|
});
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
fetch("process_edit_template_xls.php", {
|
fetch("process_edit_template_xls.php", {
|
||||||
method: "POST",
|
method: "POST",
|
||||||
body: formData
|
body: formData
|
||||||
@@ -402,8 +734,8 @@ $routines = $stmt->fetchAll(PDO::FETCH_ASSOC);
|
|||||||
.then(data => {
|
.then(data => {
|
||||||
if (data.success) {
|
if (data.success) {
|
||||||
Swal.fire({
|
Swal.fire({
|
||||||
title: "Successo!",
|
title: "Success!",
|
||||||
text: "Template aggiornato con successo!",
|
text: "Template updated successfully!",
|
||||||
icon: "success",
|
icon: "success",
|
||||||
confirmButtonText: "OK"
|
confirmButtonText: "OK"
|
||||||
}).then(() => {
|
}).then(() => {
|
||||||
@@ -411,17 +743,17 @@ $routines = $stmt->fetchAll(PDO::FETCH_ASSOC);
|
|||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
Swal.fire({
|
Swal.fire({
|
||||||
title: "Errore!",
|
title: "Error!",
|
||||||
text: data.message,
|
text: data.message,
|
||||||
icon: "error",
|
icon: "error",
|
||||||
confirmButtonText: "OK"
|
confirmButtonText: "OK"
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
.catch(error => {
|
.catch(() => {
|
||||||
Swal.fire({
|
Swal.fire({
|
||||||
title: "Errore!",
|
title: "Error!",
|
||||||
text: "Si è verificato un errore imprevisto.",
|
text: "An unexpected error occurred.",
|
||||||
icon: "error",
|
icon: "error",
|
||||||
confirmButtonText: "OK"
|
confirmButtonText: "OK"
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -314,3 +314,20 @@
|
|||||||
2026-02-26 15:01:32 [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-02-26 15:01:32 [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-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-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-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"}
|
||||||
|
2026-03-25 14:13:34 - Autenticazione fallita: HTTP 503, Errore cURL: , Risposta: The service is unavailable.
|
||||||
|
2026-03-25 14:13:34 - Autenticazione fallita: HTTP 503, Errore cURL: , Risposta: The service is unavailable.
|
||||||
|
2026-03-26 10:57:18 [AnagraficaCertestObject] Autenticazione fallita: HTTP 500, Errore cURL: , Risposta: {"title":"Internal Server Error","status":500,"detail":"Timeout expired. The timeout period elapsed prior to obtaining a connection from the pool. This may have occurred because all pooled connections were in use and max pool size was reached.","instance":"POST /api/authentication/authenticate","errorCode":"63ab532c6"}
|
||||||
|
2026-03-26 10:57:21 [AnagraficaCertestObject] Autenticazione fallita: HTTP 500, Errore cURL: , Risposta: {"title":"Internal Server Error","status":500,"detail":"Timeout expired. The timeout period elapsed prior to obtaining a connection from the pool. This may have occurred because all pooled connections were in use and max pool size was reached.","instance":"POST /api/authentication/authenticate","errorCode":"63ab532c6"}
|
||||||
|
2026-03-26 10:57:21 [AnagraficaCertestService] Autenticazione fallita: HTTP 500, Errore cURL: , Risposta: {"title":"Internal Server Error","status":500,"detail":"Timeout expired. The timeout period elapsed prior to obtaining a connection from the pool. This may have occurred because all pooled connections were in use and max pool size was reached.","instance":"POST /api/authentication/authenticate","errorCode":"63ab532c6"}
|
||||||
|
2026-03-26 10:57:21 [MoltiplicatorePrezzo] Autenticazione fallita: HTTP 500, Errore cURL: , Risposta: {"title":"Internal Server Error","status":500,"detail":"Timeout expired. The timeout period elapsed prior to obtaining a connection from the pool. This may have occurred because all pooled connections were in use and max pool size was reached.","instance":"POST /api/authentication/authenticate","errorCode":"63ab532c6"}
|
||||||
|
2026-03-26 10:57:36 [AnagraficaCertestObject] Autenticazione fallita: HTTP 500, Errore cURL: , Risposta: {"title":"Internal Server Error","status":500,"detail":"Timeout expired. The timeout period elapsed prior to obtaining a connection from the pool. This may have occurred because all pooled connections were in use and max pool size was reached.","instance":"POST /api/authentication/authenticate","errorCode":"63ab532c6"}
|
||||||
|
2026-03-26 10:57:36 [AnagraficaCertestService] Autenticazione fallita: HTTP 500, Errore cURL: , Risposta: {"title":"Internal Server Error","status":500,"detail":"Timeout expired. The timeout period elapsed prior to obtaining a connection from the pool. This may have occurred because all pooled connections were in use and max pool size was reached.","instance":"POST /api/authentication/authenticate","errorCode":"63ab532c6"}
|
||||||
|
2026-03-26 10:57:36 [MoltiplicatorePrezzo] Autenticazione fallita: HTTP 500, Errore cURL: , Risposta: {"title":"Internal Server Error","status":500,"detail":"Timeout expired. The timeout period elapsed prior to obtaining a connection from the pool. This may have occurred because all pooled connections were in use and max pool size was reached.","instance":"POST /api/authentication/authenticate","errorCode":"63ab532c6"}
|
||||||
|
2026-03-26 10:57:41 [MoltiplicatorePrezzo] Autenticazione fallita: HTTP 500, Errore cURL: , Risposta: {"title":"Internal Server Error","status":500,"detail":"Timeout expired. The timeout period elapsed prior to obtaining a connection from the pool. This may have occurred because all pooled connections were in use and max pool size was reached.","instance":"POST /api/authentication/authenticate","errorCode":"63ab532c6"}
|
||||||
|
2026-03-26 10:57:56 [AnagraficaCertestObject] Autenticazione fallita: HTTP 500, Errore cURL: , Risposta: {"title":"Internal Server Error","status":500,"detail":"Timeout expired. The timeout period elapsed prior to obtaining a connection from the pool. This may have occurred because all pooled connections were in use and max pool size was reached.","instance":"POST /api/authentication/authenticate","errorCode":"63ab532c6"}
|
||||||
|
2026-03-26 10:58:11 [AnagraficaCertestService] Autenticazione fallita: HTTP 500, Errore cURL: , Risposta: {"title":"Internal Server Error","status":500,"detail":"Timeout expired. The timeout period elapsed prior to obtaining a connection from the pool. This may have occurred because all pooled connections were in use and max pool size was reached.","instance":"POST /api/authentication/authenticate","errorCode":"63ab532c6"}
|
||||||
|
2026-04-30 15:01:25 - Errore nel recupero dati: HTTP 404, Risposta: {"title":"Not Found","status":404,"detail":"Not Found","instance":"GET /api/odata/Rapporto(2621521)","errorCode":"d25cbd678"}
|
||||||
|
2026-04-30 15:02:04 - Errore nel recupero dati: HTTP 404, Risposta:
|
||||||
|
2026-04-30 15:03:19 - Errore nella richiesta: URL rejected: Malformed input to a URL function
|
||||||
|
|||||||
@@ -0,0 +1,555 @@
|
|||||||
|
/**
|
||||||
|
* exportLims_gridData.js — Export to LIMS using gridData (for imported.php)
|
||||||
|
*
|
||||||
|
* Replaces export_to_lims.js for pages that use gridData instead of DOM-rendered rows.
|
||||||
|
* Single export + batch export (Export All) with validation.
|
||||||
|
*/
|
||||||
|
(function () {
|
||||||
|
'use strict';
|
||||||
|
|
||||||
|
let pendingConfirmHandler = null;
|
||||||
|
let batchRunning = false;
|
||||||
|
Object.defineProperty(window, "batchRunning", { get: () => batchRunning });
|
||||||
|
|
||||||
|
let batchCancelled = false;
|
||||||
|
let pendingBatchConfirmHandler = null;
|
||||||
|
|
||||||
|
// ── Helpers ──────────────────────────────────────────────────────────
|
||||||
|
|
||||||
|
function cleanupBackdrop() {
|
||||||
|
document.querySelectorAll(".modal-backdrop").forEach(b => b.remove());
|
||||||
|
document.body.classList.remove("modal-open");
|
||||||
|
document.body.style.paddingRight = "";
|
||||||
|
const overlay = document.querySelector(".overlay.toggle-icon");
|
||||||
|
if (overlay) overlay.style.display = "none";
|
||||||
|
}
|
||||||
|
|
||||||
|
function getGridRow(iddatadb) {
|
||||||
|
return document.querySelector(`.grid-row[data-id="${iddatadb}"]`);
|
||||||
|
}
|
||||||
|
|
||||||
|
function getRowIndexByIddatadb(iddatadb) {
|
||||||
|
return (window.gridData || []).findIndex(r => String(r.iddatadb) === String(iddatadb));
|
||||||
|
}
|
||||||
|
|
||||||
|
// ── Validation ──────────────────────────────────────────────────────
|
||||||
|
|
||||||
|
async function validateRows(rowsToValidate) {
|
||||||
|
const response = await fetch("validate_export.php", {
|
||||||
|
method: "POST",
|
||||||
|
headers: { "Content-Type": "application/json" },
|
||||||
|
body: JSON.stringify({ rows: rowsToValidate }),
|
||||||
|
});
|
||||||
|
if (!response.ok) throw new Error(`Validation HTTP error: ${response.status}`);
|
||||||
|
return response.json();
|
||||||
|
}
|
||||||
|
|
||||||
|
function clearValidationErrors() {
|
||||||
|
// Clear from gridData
|
||||||
|
(window.gridData || []).forEach(row => { delete row._validationErrors; delete row._exportError; });
|
||||||
|
|
||||||
|
document.querySelectorAll(".grid-cell.validation-error").forEach(cell => {
|
||||||
|
cell.classList.remove("validation-error");
|
||||||
|
cell.querySelectorAll(".input-validation-error").forEach(el => el.classList.remove("input-validation-error"));
|
||||||
|
const tooltip = cell.querySelector(".validation-tooltip");
|
||||||
|
if (tooltip) tooltip.remove();
|
||||||
|
});
|
||||||
|
document.querySelectorAll(".grid-row.validation-row-error").forEach(row => row.classList.remove("validation-row-error"));
|
||||||
|
clearAllRowErrors();
|
||||||
|
}
|
||||||
|
|
||||||
|
function showValidationErrors(gridRow, iddatadb, errors) {
|
||||||
|
// Store in gridData for re-render persistence
|
||||||
|
const idx = getRowIndexByIddatadb(iddatadb);
|
||||||
|
if (idx >= 0) window.gridData[idx]._validationErrors = errors;
|
||||||
|
|
||||||
|
if (!gridRow) return;
|
||||||
|
gridRow.classList.add("validation-row-error");
|
||||||
|
const messages = [];
|
||||||
|
|
||||||
|
errors.forEach(err => {
|
||||||
|
messages.push(err.message);
|
||||||
|
if (!err.field) return;
|
||||||
|
|
||||||
|
let cell = null;
|
||||||
|
if (err.field.startsWith("field_label:")) {
|
||||||
|
const label = err.field.substring("field_label:".length);
|
||||||
|
const headers = document.querySelectorAll(".grid-header");
|
||||||
|
let targetIndex = null;
|
||||||
|
headers.forEach(h => {
|
||||||
|
if (h.textContent.trim() === label) targetIndex = h.getAttribute("data-index");
|
||||||
|
});
|
||||||
|
if (targetIndex) {
|
||||||
|
cell = gridRow.querySelector(`.grid-cell[data-index="${targetIndex}"]`);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
cell = gridRow.querySelector(`.grid-cell[data-col="${err.field}"]`);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (cell) {
|
||||||
|
cell.classList.add("validation-error");
|
||||||
|
cell.querySelectorAll("input, select").forEach(el => el.classList.add("input-validation-error"));
|
||||||
|
let tooltip = cell.querySelector(".validation-tooltip");
|
||||||
|
if (!tooltip) {
|
||||||
|
tooltip = document.createElement("div");
|
||||||
|
tooltip.className = "validation-tooltip";
|
||||||
|
cell.appendChild(tooltip);
|
||||||
|
}
|
||||||
|
tooltip.textContent = err.message;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
showRowError(gridRow, iddatadb, messages.join("\n"));
|
||||||
|
}
|
||||||
|
|
||||||
|
// ── Send export ─────────────────────────────────────────────────────
|
||||||
|
|
||||||
|
async function sendExport(iddatadb, batchUuid) {
|
||||||
|
const formData = new FormData();
|
||||||
|
formData.append("iddatadb", iddatadb);
|
||||||
|
if (batchUuid) formData.append("batch_uuid", batchUuid);
|
||||||
|
|
||||||
|
const response = await fetch("export_to_lims.php", { method: "POST", body: formData });
|
||||||
|
if (!response.ok) throw new Error(`HTTP error! status: ${response.status}`);
|
||||||
|
const data = await response.json();
|
||||||
|
|
||||||
|
if (data.success) {
|
||||||
|
// Update gridData
|
||||||
|
const idx = getRowIndexByIddatadb(iddatadb);
|
||||||
|
if (idx >= 0) {
|
||||||
|
window.gridData[idx].status = 'l';
|
||||||
|
window.gridData[idx].commessaweb = data.commessaweb;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Update visible DOM row
|
||||||
|
const gridRow = getGridRow(iddatadb);
|
||||||
|
if (gridRow) {
|
||||||
|
const statusBadge = gridRow.querySelector('.grid-cell[data-col="status"] .status-badge');
|
||||||
|
if (statusBadge) {
|
||||||
|
statusBadge.classList.remove("status-i", "status-P");
|
||||||
|
statusBadge.classList.add("status-l");
|
||||||
|
statusBadge.textContent = "To LIMS";
|
||||||
|
}
|
||||||
|
const statusCell = gridRow.querySelector('.grid-cell[data-col="status"]');
|
||||||
|
if (statusCell && data.commessaweb) {
|
||||||
|
let cwSpan = statusCell.querySelector(".commessaweb-code");
|
||||||
|
if (!cwSpan) {
|
||||||
|
cwSpan = document.createElement("span");
|
||||||
|
cwSpan.className = "commessaweb-code";
|
||||||
|
cwSpan.style.cssText = "display:block; font-size:0.75em; color:#555; margin-top:2px;";
|
||||||
|
statusCell.appendChild(cwSpan);
|
||||||
|
}
|
||||||
|
cwSpan.textContent = data.commessaweb;
|
||||||
|
}
|
||||||
|
const exportBtn = gridRow.querySelector(".export-lims-btn");
|
||||||
|
if (exportBtn) {
|
||||||
|
exportBtn.disabled = true;
|
||||||
|
exportBtn.style.background = "#ccc";
|
||||||
|
exportBtn.style.cursor = "not-allowed";
|
||||||
|
exportBtn.style.opacity = "0.5";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return data;
|
||||||
|
}
|
||||||
|
|
||||||
|
// ── Show result modal ───────────────────────────────────────────────
|
||||||
|
|
||||||
|
function showExportResult(data) {
|
||||||
|
const el = document.getElementById("exportResponseModal");
|
||||||
|
if (!el) return;
|
||||||
|
const modal = new bootstrap.Modal(el, { keyboard: false });
|
||||||
|
const msg = document.getElementById("exportResponseMessage");
|
||||||
|
const label = document.getElementById("exportResponseModalLabel");
|
||||||
|
|
||||||
|
if (data.success) {
|
||||||
|
msg.innerHTML = `${data.message.replace(/\n/g, "<br>")}` +
|
||||||
|
`<br>ID CommessaWeb: ${data.idcommessaweb}` +
|
||||||
|
`<br>Codice CommessaWeb: ${data.commessaweb}` +
|
||||||
|
(data.totalPhotos > 0 ? `<br>Foto: ${data.totalPhotos}` : "");
|
||||||
|
label.textContent = "Esportazione Completata";
|
||||||
|
} else {
|
||||||
|
msg.textContent = `Errore: ${data.message}`;
|
||||||
|
label.textContent = "Errore Esportazione";
|
||||||
|
}
|
||||||
|
modal.show();
|
||||||
|
el.addEventListener("hidden.bs.modal", cleanupBackdrop, { once: true });
|
||||||
|
}
|
||||||
|
|
||||||
|
// ── Row UI helpers ──────────────────────────────────────────────────
|
||||||
|
|
||||||
|
function setRowExporting(row, active) {
|
||||||
|
if (!row) return;
|
||||||
|
const btnCell = row.querySelector(".button-cell");
|
||||||
|
if (active) {
|
||||||
|
row.classList.remove("batch-disabled");
|
||||||
|
row.classList.add("batch-exporting");
|
||||||
|
if (btnCell) {
|
||||||
|
btnCell.querySelectorAll(".action-btn").forEach(b => { b.dataset.prevDisplay = b.style.display; b.style.display = "none"; });
|
||||||
|
const spinner = document.createElement("span");
|
||||||
|
spinner.className = "batch-row-spinner";
|
||||||
|
spinner.innerHTML = '<i class="fas fa-spinner fa-spin"></i> Exporting...';
|
||||||
|
btnCell.appendChild(spinner);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
row.classList.remove("batch-exporting");
|
||||||
|
if (btnCell) {
|
||||||
|
const spinner = btnCell.querySelector(".batch-row-spinner");
|
||||||
|
if (spinner) spinner.remove();
|
||||||
|
btnCell.querySelectorAll(".action-btn").forEach(b => { b.style.display = b.dataset.prevDisplay || ""; delete b.dataset.prevDisplay; });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function showRowError(row, iddatadb, message) {
|
||||||
|
if (!row) return;
|
||||||
|
row.classList.add("batch-row-error");
|
||||||
|
const btnCell = row.querySelector(".button-cell");
|
||||||
|
if (btnCell) {
|
||||||
|
const old = btnCell.querySelector(".batch-error-msg");
|
||||||
|
if (old) old.remove();
|
||||||
|
const errorEl = document.createElement("div");
|
||||||
|
errorEl.className = "batch-error-msg";
|
||||||
|
errorEl.textContent = "Warning — click for details";
|
||||||
|
errorEl.addEventListener("click", () => {
|
||||||
|
document.getElementById("exportResponseMessage").innerHTML = message.replace(/\n/g, "<br>");
|
||||||
|
document.getElementById("exportResponseModalLabel").textContent = "Error (id: " + iddatadb + ")";
|
||||||
|
new bootstrap.Modal(document.getElementById("exportResponseModal"), { keyboard: false }).show();
|
||||||
|
});
|
||||||
|
btnCell.appendChild(errorEl);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function clearAllRowErrors() {
|
||||||
|
document.querySelectorAll(".grid-row.batch-row-error").forEach(row => {
|
||||||
|
row.classList.remove("batch-row-error");
|
||||||
|
const msg = row.querySelector(".batch-error-msg");
|
||||||
|
if (msg) msg.remove();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function disableAllRowButtons() {
|
||||||
|
document.querySelectorAll(".grid-row[data-id]").forEach(row => row.classList.add("batch-disabled"));
|
||||||
|
const toggle = document.querySelector(".actions-dropdown .dropdown-toggle");
|
||||||
|
if (toggle) { toggle.disabled = true; toggle.style.opacity = "0.5"; toggle.style.pointerEvents = "none"; }
|
||||||
|
}
|
||||||
|
|
||||||
|
function enableAllRowButtons() {
|
||||||
|
document.querySelectorAll(".grid-row[data-id]").forEach(row => row.classList.remove("batch-disabled"));
|
||||||
|
const toggle = document.querySelector(".actions-dropdown .dropdown-toggle");
|
||||||
|
if (toggle) { toggle.disabled = false; toggle.style.opacity = ""; toggle.style.pointerEvents = ""; }
|
||||||
|
}
|
||||||
|
|
||||||
|
// ── Single row export: validate → confirm → send ────────────────────
|
||||||
|
|
||||||
|
function startExportConfirmFlow(iddatadb, rowIndex) {
|
||||||
|
const gridRow = getGridRow(iddatadb);
|
||||||
|
clearValidationErrors();
|
||||||
|
|
||||||
|
if (gridRow) {
|
||||||
|
setRowExporting(gridRow, true);
|
||||||
|
const spinner = gridRow.querySelector(".batch-row-spinner");
|
||||||
|
if (spinner) spinner.innerHTML = '<i class="fas fa-spinner fa-spin"></i> Validating...';
|
||||||
|
}
|
||||||
|
|
||||||
|
validateRows([{ iddatadb: parseInt(iddatadb), index: rowIndex }])
|
||||||
|
.then(validationData => {
|
||||||
|
if (gridRow) { setRowExporting(gridRow, false); gridRow.classList.remove("batch-disabled"); }
|
||||||
|
|
||||||
|
if (!validationData.success) {
|
||||||
|
showExportResult({ success: false, message: validationData.message || "Validation error" });
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const result = validationData.results[rowIndex];
|
||||||
|
if (result && !result.valid) {
|
||||||
|
if (gridRow) showValidationErrors(gridRow, iddatadb, result.errors);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
showConfirmAndExport(iddatadb, rowIndex);
|
||||||
|
})
|
||||||
|
.catch(error => {
|
||||||
|
if (gridRow) { setRowExporting(gridRow, false); gridRow.classList.remove("batch-disabled"); }
|
||||||
|
showExportResult({ success: false, message: "Validation error: " + error.message });
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function showConfirmAndExport(iddatadb, rowIndex) {
|
||||||
|
const confirmModalElement = document.getElementById("exportConfirmModal");
|
||||||
|
if (!confirmModalElement) return;
|
||||||
|
|
||||||
|
const confirmModal = new bootstrap.Modal(confirmModalElement, { keyboard: false });
|
||||||
|
document.getElementById("exportIddatadb").textContent = iddatadb;
|
||||||
|
confirmModal.show();
|
||||||
|
|
||||||
|
const confirmBtn = document.getElementById("exportConfirmBtn");
|
||||||
|
if (!confirmBtn) { confirmModal.hide(); return; }
|
||||||
|
|
||||||
|
const confirmHandler = async () => {
|
||||||
|
pendingConfirmHandler = null;
|
||||||
|
confirmModal.hide();
|
||||||
|
|
||||||
|
const gridRow = getGridRow(iddatadb);
|
||||||
|
if (gridRow) setRowExporting(gridRow, true);
|
||||||
|
|
||||||
|
try {
|
||||||
|
const data = await sendExport(iddatadb);
|
||||||
|
if (gridRow) { setRowExporting(gridRow, false); gridRow.classList.remove("batch-disabled"); }
|
||||||
|
if (!data.success) showRowError(gridRow, iddatadb, data.message || "Unknown error");
|
||||||
|
showExportResult(data);
|
||||||
|
} catch (error) {
|
||||||
|
if (gridRow) { setRowExporting(gridRow, false); gridRow.classList.remove("batch-disabled"); }
|
||||||
|
showRowError(gridRow, iddatadb, error.message);
|
||||||
|
showExportResult({ success: false, message: error.message });
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
if (pendingConfirmHandler) confirmBtn.removeEventListener("click", pendingConfirmHandler);
|
||||||
|
pendingConfirmHandler = confirmHandler;
|
||||||
|
confirmBtn.addEventListener("click", confirmHandler, { once: true });
|
||||||
|
}
|
||||||
|
|
||||||
|
// ── Single row click (event delegation) ─────────────────────────────
|
||||||
|
|
||||||
|
$(document).on('click', '.export-lims-btn', function (e) {
|
||||||
|
e.preventDefault();
|
||||||
|
if (batchRunning) return;
|
||||||
|
|
||||||
|
const iddatadb = this.dataset.iddatadb;
|
||||||
|
const rowIndex = parseInt(this.dataset.row);
|
||||||
|
const gridRow = getGridRow(iddatadb);
|
||||||
|
|
||||||
|
// Check unsaved changes for this row
|
||||||
|
const dataRow = window.gridData?.[rowIndex];
|
||||||
|
if (dataRow && dataRow._dirty) {
|
||||||
|
const unsavedModal = new bootstrap.Modal(document.getElementById("exportUnsavedModal"), { keyboard: false });
|
||||||
|
unsavedModal.show();
|
||||||
|
|
||||||
|
document.getElementById("saveAndExportBtn")?.addEventListener("click", () => {
|
||||||
|
unsavedModal.hide();
|
||||||
|
// Save first, then export
|
||||||
|
const formData = window.buildSavePayload(rowIndex);
|
||||||
|
fetch('save_edited_row.php', { method: 'POST', body: formData })
|
||||||
|
.then(r => r.json())
|
||||||
|
.then(result => {
|
||||||
|
if (result.success) {
|
||||||
|
dataRow._dirty = false;
|
||||||
|
startExportConfirmFlow(iddatadb, rowIndex);
|
||||||
|
} else {
|
||||||
|
alert('Save failed: ' + result.message);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}, { once: true });
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
startExportConfirmFlow(iddatadb, rowIndex);
|
||||||
|
});
|
||||||
|
|
||||||
|
// ── Batch export (Export All) ───────────────────────────────────────
|
||||||
|
|
||||||
|
function collectEligibleRows() {
|
||||||
|
// Read from gridData, not DOM
|
||||||
|
const eligible = [];
|
||||||
|
(window.gridData || []).forEach((row, index) => {
|
||||||
|
if (row.status !== 'l') {
|
||||||
|
eligible.push({ iddatadb: row.iddatadb, index, row: getGridRow(row.iddatadb) });
|
||||||
|
}
|
||||||
|
});
|
||||||
|
return eligible;
|
||||||
|
}
|
||||||
|
|
||||||
|
function hasUnsavedChanges() {
|
||||||
|
return (window.gridData || []).some(r => r._dirty);
|
||||||
|
}
|
||||||
|
|
||||||
|
async function validateAndFilter(eligibleRows) {
|
||||||
|
const rowsToValidate = eligibleRows.map(({ iddatadb, index }) => ({
|
||||||
|
iddatadb: parseInt(iddatadb),
|
||||||
|
index,
|
||||||
|
}));
|
||||||
|
const validationData = await validateRows(rowsToValidate);
|
||||||
|
if (!validationData.success) throw new Error(validationData.message || "Validation error");
|
||||||
|
|
||||||
|
const validRows = [];
|
||||||
|
let invalidCount = 0;
|
||||||
|
|
||||||
|
for (const item of eligibleRows) {
|
||||||
|
const result = validationData.results[item.index];
|
||||||
|
if (result && !result.valid) {
|
||||||
|
if (item.row) showValidationErrors(item.row, item.iddatadb, result.errors);
|
||||||
|
invalidCount++;
|
||||||
|
} else {
|
||||||
|
validRows.push(item);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return { validRows, invalidCount };
|
||||||
|
}
|
||||||
|
|
||||||
|
function showValidationSpinner(show) {
|
||||||
|
const bar = document.getElementById("batchExportBar");
|
||||||
|
const statusEl = document.getElementById("batchExportStatus");
|
||||||
|
const cancelBtn = document.getElementById("exportBatchCancelBtn");
|
||||||
|
if (show) {
|
||||||
|
if (bar) bar.style.display = "";
|
||||||
|
if (statusEl) statusEl.textContent = "Validating...";
|
||||||
|
if (cancelBtn) cancelBtn.style.display = "none";
|
||||||
|
} else {
|
||||||
|
if (bar) bar.style.display = "none";
|
||||||
|
if (cancelBtn) cancelBtn.style.display = "";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function showBatchConfirm(eligibleRows) {
|
||||||
|
clearValidationErrors();
|
||||||
|
showValidationSpinner(true);
|
||||||
|
|
||||||
|
validateAndFilter(eligibleRows)
|
||||||
|
.then(({ validRows, invalidCount }) => {
|
||||||
|
showValidationSpinner(false);
|
||||||
|
if (validRows.length === 0) {
|
||||||
|
document.getElementById("exportResponseMessage").innerHTML =
|
||||||
|
`No valid rows for export.<br><strong>${invalidCount}</strong> rows with validation errors.`;
|
||||||
|
document.getElementById("exportResponseModalLabel").textContent = "Validation Failed";
|
||||||
|
new bootstrap.Modal(document.getElementById("exportResponseModal"), { keyboard: false }).show();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const confirmModal = new bootstrap.Modal(document.getElementById("exportBatchConfirmModal"), { keyboard: false });
|
||||||
|
let countText = String(validRows.length);
|
||||||
|
if (invalidCount > 0) countText += ` (${invalidCount} excluded due to errors)`;
|
||||||
|
document.getElementById("exportBatchCount").textContent = countText;
|
||||||
|
confirmModal.show();
|
||||||
|
|
||||||
|
const confirmBtn = document.getElementById("exportBatchConfirmBtn");
|
||||||
|
if (pendingBatchConfirmHandler) confirmBtn.removeEventListener("click", pendingBatchConfirmHandler);
|
||||||
|
pendingBatchConfirmHandler = () => {
|
||||||
|
pendingBatchConfirmHandler = null;
|
||||||
|
confirmModal.hide();
|
||||||
|
startBatchExport(validRows);
|
||||||
|
};
|
||||||
|
confirmBtn.addEventListener("click", pendingBatchConfirmHandler, { once: true });
|
||||||
|
})
|
||||||
|
.catch(error => {
|
||||||
|
showValidationSpinner(false);
|
||||||
|
document.getElementById("exportResponseMessage").textContent = "Validation error: " + error.message;
|
||||||
|
document.getElementById("exportResponseModalLabel").textContent = "Validation Error";
|
||||||
|
new bootstrap.Modal(document.getElementById("exportResponseModal"), { keyboard: false }).show();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
$(document).on('click', '.export-all-lims-btn', function (e) {
|
||||||
|
e.preventDefault();
|
||||||
|
if (batchRunning) return;
|
||||||
|
|
||||||
|
if (hasUnsavedChanges()) {
|
||||||
|
const unsavedModal = new bootstrap.Modal(document.getElementById("exportBatchUnsavedModal"), { keyboard: false });
|
||||||
|
unsavedModal.show();
|
||||||
|
document.getElementById("batchSaveAndExportBtn")?.addEventListener("click", () => {
|
||||||
|
unsavedModal.hide();
|
||||||
|
// Trigger save all first — listen for completion
|
||||||
|
alert("Please Save All first, then Export All.");
|
||||||
|
}, { once: true });
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const eligibleRows = collectEligibleRows();
|
||||||
|
if (eligibleRows.length === 0) {
|
||||||
|
document.getElementById("exportResponseMessage").textContent = "All rows already exported to LIMS.";
|
||||||
|
document.getElementById("exportResponseModalLabel").textContent = "Export All";
|
||||||
|
new bootstrap.Modal(document.getElementById("exportResponseModal"), { keyboard: false }).show();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
showBatchConfirm(eligibleRows);
|
||||||
|
});
|
||||||
|
|
||||||
|
function startBatchExport(eligibleRows) {
|
||||||
|
batchCancelled = false;
|
||||||
|
batchRunning = true;
|
||||||
|
const batchUuid = crypto.randomUUID();
|
||||||
|
const total = eligibleRows.length;
|
||||||
|
let processed = 0, succeeded = 0, failed = 0;
|
||||||
|
|
||||||
|
disableAllRowButtons();
|
||||||
|
|
||||||
|
const bar = document.getElementById("batchExportBar");
|
||||||
|
const statusEl = document.getElementById("batchExportStatus");
|
||||||
|
const cancelBtn = document.getElementById("exportBatchCancelBtn");
|
||||||
|
if (bar) bar.style.display = "";
|
||||||
|
if (cancelBtn) cancelBtn.disabled = false;
|
||||||
|
if (statusEl) statusEl.textContent = `Exporting 0 / ${total}...`;
|
||||||
|
|
||||||
|
cancelBtn?.addEventListener("click", () => {
|
||||||
|
batchCancelled = true;
|
||||||
|
if (statusEl) statusEl.textContent = "Cancelling... (waiting for current row)";
|
||||||
|
if (cancelBtn) cancelBtn.disabled = true;
|
||||||
|
}, { once: true });
|
||||||
|
|
||||||
|
(async () => {
|
||||||
|
for (let i = 0; i < eligibleRows.length; i++) {
|
||||||
|
if (batchCancelled) break;
|
||||||
|
|
||||||
|
const { iddatadb, row } = eligibleRows[i];
|
||||||
|
if (statusEl) statusEl.textContent = `Exporting ${processed + 1} / ${total} (id: ${iddatadb})...`;
|
||||||
|
|
||||||
|
const gridRow = row || getGridRow(iddatadb);
|
||||||
|
if (gridRow) setRowExporting(gridRow, true);
|
||||||
|
|
||||||
|
try {
|
||||||
|
const data = await sendExport(iddatadb, batchUuid);
|
||||||
|
processed++;
|
||||||
|
if (data.success) {
|
||||||
|
succeeded++;
|
||||||
|
} else {
|
||||||
|
failed++;
|
||||||
|
const errIdx = getRowIndexByIddatadb(iddatadb);
|
||||||
|
if (errIdx >= 0) window.gridData[errIdx]._exportError = data.message || "Unknown error";
|
||||||
|
if (gridRow) showRowError(gridRow, iddatadb, data.message || "Unknown error");
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
processed++;
|
||||||
|
failed++;
|
||||||
|
const errIdx = getRowIndexByIddatadb(iddatadb);
|
||||||
|
if (errIdx >= 0) window.gridData[errIdx]._exportError = error.message;
|
||||||
|
if (gridRow) showRowError(gridRow, iddatadb, error.message);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (gridRow) { setRowExporting(gridRow, false); gridRow.classList.remove("batch-disabled"); }
|
||||||
|
}
|
||||||
|
|
||||||
|
batchRunning = false;
|
||||||
|
enableAllRowButtons();
|
||||||
|
if (bar) bar.style.display = "none";
|
||||||
|
|
||||||
|
// Re-render to reflect status changes, then restore error indicators
|
||||||
|
const gr = window.gridRenderer;
|
||||||
|
if (gr) gr.renderVisibleRows();
|
||||||
|
|
||||||
|
// Restore error state from gridData._exportError
|
||||||
|
(window.gridData || []).forEach((row, idx) => {
|
||||||
|
if (row._exportError) {
|
||||||
|
const gridRow = getGridRow(row.iddatadb);
|
||||||
|
if (gridRow) showRowError(gridRow, row.iddatadb, row._exportError);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
const msgEl = document.getElementById("exportResponseMessage");
|
||||||
|
const labelEl = document.getElementById("exportResponseModalLabel");
|
||||||
|
|
||||||
|
if (batchCancelled) {
|
||||||
|
labelEl.textContent = "Export All — Cancelled";
|
||||||
|
msgEl.innerHTML = `Exported: <strong>${succeeded}</strong><br>Errors: <strong>${failed}</strong><br>Not processed: <strong>${total - processed}</strong>`;
|
||||||
|
} else if (failed === 0) {
|
||||||
|
labelEl.textContent = "Export All — Complete";
|
||||||
|
msgEl.innerHTML = `All <strong>${succeeded}</strong> rows exported successfully.`;
|
||||||
|
} else {
|
||||||
|
labelEl.textContent = "Export All — Completed with errors";
|
||||||
|
msgEl.innerHTML = `Exported: <strong>${succeeded}</strong><br>Errors: <strong>${failed}</strong>`;
|
||||||
|
}
|
||||||
|
|
||||||
|
const modalEl = document.getElementById("exportResponseModal");
|
||||||
|
new bootstrap.Modal(modalEl, { keyboard: false }).show();
|
||||||
|
modalEl.addEventListener("hidden.bs.modal", cleanupBackdrop, { once: true });
|
||||||
|
})();
|
||||||
|
}
|
||||||
|
})();
|
||||||
@@ -330,8 +330,16 @@ document.addEventListener("DOMContentLoaded", () => {
|
|||||||
// Validate first
|
// Validate first
|
||||||
clearValidationErrors();
|
clearValidationErrors();
|
||||||
|
|
||||||
|
// Show validating state on the row
|
||||||
|
setRowExporting(gridRow, true);
|
||||||
|
const spinner = gridRow.querySelector(".batch-row-spinner");
|
||||||
|
if (spinner) spinner.innerHTML = '<i class="fas fa-spinner fa-spin"></i> Validating...';
|
||||||
|
|
||||||
validateRows([{ iddatadb: parseInt(iddatadb), index: parseInt(rowIndex) }])
|
validateRows([{ iddatadb: parseInt(iddatadb), index: parseInt(rowIndex) }])
|
||||||
.then((validationData) => {
|
.then((validationData) => {
|
||||||
|
setRowExporting(gridRow, false);
|
||||||
|
gridRow.classList.remove("batch-disabled");
|
||||||
|
|
||||||
if (!validationData.success) {
|
if (!validationData.success) {
|
||||||
showExportResult({ success: false, message: validationData.message || "Errore di validazione" });
|
showExportResult({ success: false, message: validationData.message || "Errore di validazione" });
|
||||||
return;
|
return;
|
||||||
@@ -348,6 +356,9 @@ document.addEventListener("DOMContentLoaded", () => {
|
|||||||
showConfirmAndExport(iddatadb, btn);
|
showConfirmAndExport(iddatadb, btn);
|
||||||
})
|
})
|
||||||
.catch((error) => {
|
.catch((error) => {
|
||||||
|
setRowExporting(gridRow, false);
|
||||||
|
gridRow.classList.remove("batch-disabled");
|
||||||
|
|
||||||
console.error("Validation error:", error);
|
console.error("Validation error:", error);
|
||||||
showExportResult({ success: false, message: "Errore di validazione: " + error.message });
|
showExportResult({ success: false, message: "Errore di validazione: " + error.message });
|
||||||
});
|
});
|
||||||
@@ -379,13 +390,29 @@ document.addEventListener("DOMContentLoaded", () => {
|
|||||||
console.log(`Confirmed export for iddatadb: ${iddatadb}`);
|
console.log(`Confirmed export for iddatadb: ${iddatadb}`);
|
||||||
confirmModal.hide();
|
confirmModal.hide();
|
||||||
|
|
||||||
try {
|
|
||||||
const gridRow = btn.closest(".grid-row");
|
const gridRow = btn.closest(".grid-row");
|
||||||
|
setRowExporting(gridRow, true);
|
||||||
|
|
||||||
|
try {
|
||||||
const data = await sendExport(iddatadb, gridRow);
|
const data = await sendExport(iddatadb, gridRow);
|
||||||
console.log("Export response:", data);
|
console.log("Export response:", data);
|
||||||
|
|
||||||
|
// Stop spinner, fully restore row
|
||||||
|
setRowExporting(gridRow, false);
|
||||||
|
gridRow.classList.remove("batch-disabled");
|
||||||
|
|
||||||
|
if (!data.success) {
|
||||||
|
showRowError(gridRow, iddatadb, data.message || "Errore sconosciuto");
|
||||||
|
}
|
||||||
showExportResult(data);
|
showExportResult(data);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error("Export error:", error);
|
console.error("Export error:", error);
|
||||||
|
|
||||||
|
// Stop spinner, fully restore row so user can retry
|
||||||
|
setRowExporting(gridRow, false);
|
||||||
|
gridRow.classList.remove("batch-disabled");
|
||||||
|
|
||||||
|
showRowError(gridRow, iddatadb, error.message);
|
||||||
showExportResult({
|
showExportResult({
|
||||||
success: false,
|
success: false,
|
||||||
message: error.message,
|
message: error.message,
|
||||||
|
|||||||
@@ -49,6 +49,16 @@ function validateDate($value)
|
|||||||
return null; // Imposta null se non è una data valida
|
return null; // Imposta null se non è una data valida
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 🔹 Funzione per validare e convertire date
|
||||||
|
function formatDateToExport($value)
|
||||||
|
{
|
||||||
|
$date = DateTime::createFromFormat('Y-m-d', $value) ?: DateTime::createFromFormat('Y-m-d H:i:s', $value);
|
||||||
|
if ($date) {
|
||||||
|
return $date->format('d/m/Y');
|
||||||
|
}
|
||||||
|
return null; // Imposta null se non è una data valida
|
||||||
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
$iddatadb = $_POST['iddatadb'] ?? null;
|
$iddatadb = $_POST['iddatadb'] ?? null;
|
||||||
if (!$iddatadb) {
|
if (!$iddatadb) {
|
||||||
@@ -69,6 +79,7 @@ try {
|
|||||||
d.anagrafica_certest_object_id,
|
d.anagrafica_certest_object_id,
|
||||||
d.anagrafica_certest_service_id,
|
d.anagrafica_certest_service_id,
|
||||||
d.cliente_fornitore_id,
|
d.cliente_fornitore_id,
|
||||||
|
d.clienteAnalisi,
|
||||||
d.consegna_richiesta
|
d.consegna_richiesta
|
||||||
FROM datadb as d
|
FROM datadb as d
|
||||||
INNER JOIN excel_templates as et ON d.templateid = et.id
|
INNER JOIN excel_templates as et ON d.templateid = et.id
|
||||||
@@ -91,17 +102,35 @@ try {
|
|||||||
$anagraficaObject = !empty($result['anagrafica_certest_object_id']) ? (int) $result['anagrafica_certest_object_id'] : null;
|
$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;
|
$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;
|
$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;
|
$consegnaRichiesta = !empty($result['consegna_richiesta']) ? $result['consegna_richiesta'] : null;
|
||||||
|
|
||||||
// 🔹 STEP 3: Fetch Parts (including idmatrice)
|
// 🔹 STEP 3: Fetch Parts (including idmatrice and part id for custom fields)
|
||||||
$stmt = $pdo->prepare("
|
$stmt = $pdo->prepare("
|
||||||
SELECT part_number, part_description, material, color, mix, idmatrice
|
SELECT id AS part_id, part_number, part_description, material, color, mix, idmatrice, dateexpiry
|
||||||
FROM identification_parts
|
FROM identification_parts
|
||||||
WHERE iddatadb = :iddatadb
|
WHERE iddatadb = :iddatadb
|
||||||
|
ORDER BY CAST(part_number AS UNSIGNED) ASC, part_number ASC
|
||||||
");
|
");
|
||||||
$stmt->execute(['iddatadb' => $iddatadb]);
|
$stmt->execute(['iddatadb' => $iddatadb]);
|
||||||
$parts = $stmt->fetchAll(PDO::FETCH_ASSOC);
|
$parts = $stmt->fetchAll(PDO::FETCH_ASSOC);
|
||||||
|
|
||||||
|
// 🔹 STEP 3.1: Fetch custom field values per part from identification_parts_customfields
|
||||||
|
$partIds = array_column($parts, 'part_id');
|
||||||
|
$partsCustomFields = []; // part_id => [ { field_id, value_id, value_text }, ... ]
|
||||||
|
if (!empty($partIds)) {
|
||||||
|
$placeholders = implode(',', array_fill(0, count($partIds), '?'));
|
||||||
|
$cfStmt = $pdo->prepare("
|
||||||
|
SELECT part_id, field_id, value_id, value_text
|
||||||
|
FROM identification_parts_customfields
|
||||||
|
WHERE part_id IN ({$placeholders})
|
||||||
|
");
|
||||||
|
$cfStmt->execute($partIds);
|
||||||
|
foreach ($cfStmt->fetchAll(PDO::FETCH_ASSOC) as $cfRow) {
|
||||||
|
$partsCustomFields[(int)$cfRow['part_id']][] = $cfRow;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// 🔹 STEP 4a: Auto-populate export_date / export_time fields
|
// 🔹 STEP 4a: Auto-populate export_date / export_time fields
|
||||||
$stmt = $pdo->prepare("
|
$stmt = $pdo->prepare("
|
||||||
UPDATE import_data_details idd
|
UPDATE import_data_details idd
|
||||||
@@ -165,6 +194,7 @@ try {
|
|||||||
"AnagraficaCertestObject" => $anagraficaObject,
|
"AnagraficaCertestObject" => $anagraficaObject,
|
||||||
"AnagraficaCertestService" => $anagraficaService,
|
"AnagraficaCertestService" => $anagraficaService,
|
||||||
"ClienteFornitore" => $clienteFornitore, // PLACEHOLDER — to be implemented
|
"ClienteFornitore" => $clienteFornitore, // PLACEHOLDER — to be implemented
|
||||||
|
"ClienteAnalisi" => $clienteAnalisi, // PLACEHOLDER — to be implemented
|
||||||
// DeliveryRequest goes to Campione, not CommessaWeb
|
// DeliveryRequest goes to Campione, not CommessaWeb
|
||||||
];
|
];
|
||||||
|
|
||||||
@@ -202,9 +232,9 @@ try {
|
|||||||
"Matrice" => $matriceId,
|
"Matrice" => $matriceId,
|
||||||
"SottoMatrice" => null,
|
"SottoMatrice" => null,
|
||||||
"SchemaCustomField" => $schemaId,
|
"SchemaCustomField" => $schemaId,
|
||||||
"Riferimento" => $part["part_description"] ?? "",
|
// "Riferimento" => $part["part_description"] ?? "",
|
||||||
"NoteWeb" => $part["part_description"] ?? "",
|
"NoteWeb" => $part["part_description"] ?? "",
|
||||||
"ConsegnaRichiesta" => $consegnaRichiesta,
|
"ConsegnaRichiesta" => !empty($part["dateexpiry"]) ? $part["dateexpiry"] : $consegnaRichiesta,
|
||||||
];
|
];
|
||||||
|
|
||||||
// Costruisci curl-like per questo campione
|
// Costruisci curl-like per questo campione
|
||||||
@@ -231,6 +261,83 @@ try {
|
|||||||
$logFileStep6 = $logDir . "commessa_{$commessaId}_campioni_step6_" . time() . ".txt";
|
$logFileStep6 = $logDir . "commessa_{$commessaId}_campioni_step6_" . time() . ".txt";
|
||||||
$writeLog($logFileStep6, $logContentStep6, "STEP 6 - Campioni (commessa={$commessaId})");
|
$writeLog($logFileStep6, $logContentStep6, "STEP 6 - Campioni (commessa={$commessaId})");
|
||||||
|
|
||||||
|
// 🔹 STEP 6.0: PATCH each Campione custom fields:
|
||||||
|
// - field 189 (Tested Component) = part_description
|
||||||
|
// - additional fields from identification_parts_customfields (field_id → value_id/value_text)
|
||||||
|
$logContentStep63 = "";
|
||||||
|
foreach ($campioni as $index => $campione) {
|
||||||
|
$campioneId = (int)($campione['IdCampione'] ?? 0);
|
||||||
|
$partDescription = $parts[$index]['part_description'] ?? '';
|
||||||
|
$partId = (int)($parts[$index]['part_id'] ?? 0);
|
||||||
|
|
||||||
|
if ($campioneId <= 0) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Build a map of overrides: IdCustomField => value
|
||||||
|
$overrides = [];
|
||||||
|
|
||||||
|
// Override 1: Tested Component (field 189) = part_description
|
||||||
|
if ($partDescription !== '') {
|
||||||
|
$overrides[189] = $partDescription;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Override 2: values from identification_parts_customfields
|
||||||
|
$partCFs = $partsCustomFields[$partId] ?? [];
|
||||||
|
foreach ($partCFs as $pcf) {
|
||||||
|
$cfFieldId = (int)$pcf['field_id'];
|
||||||
|
$cfValue = $pcf['value_text'] ?? $pcf['value_id'] ?? null;
|
||||||
|
if ($cfFieldId > 0 && $cfValue !== null && $cfValue !== '') {
|
||||||
|
$overrides[$cfFieldId] = (string)$cfValue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Skip if nothing to override
|
||||||
|
if (empty($overrides)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// GET campione custom fields
|
||||||
|
$campioneWithFields = $api->get("Campione({$campioneId})?\$expand=CampioniCustomFields(\$expand=CustomField)");
|
||||||
|
|
||||||
|
$logContentStep63 .= "GET Campione({$campioneId}) CustomFields:\n" .
|
||||||
|
json_encode($campioneWithFields['CampioniCustomFields'] ?? [], JSON_PRETTY_PRINT) . "\n\n";
|
||||||
|
$logContentStep63 .= "Overrides for part {$partId}: " . json_encode($overrides) . "\n\n";
|
||||||
|
|
||||||
|
// Build PATCH payload — apply overrides where IdCustomField matches
|
||||||
|
$campioniCustomFields = [];
|
||||||
|
foreach ($campioneWithFields["CampioniCustomFields"] ?? [] as $cf) {
|
||||||
|
$definitionId = (int)($cf["CustomField"]["IdCustomField"] ?? 0);
|
||||||
|
$fieldInstanceId = (int)$cf["IdCampioniCustomFields"];
|
||||||
|
$currentValue = $cf["Valore"] ?? '';
|
||||||
|
|
||||||
|
$newValue = $overrides[$definitionId] ?? $currentValue;
|
||||||
|
|
||||||
|
$campioniCustomFields[] = [
|
||||||
|
"IdCampioniCustomFields" => $fieldInstanceId,
|
||||||
|
"Valore" => $newValue
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!empty($campioniCustomFields)) {
|
||||||
|
$patchPayload = ["CampioniCustomFields" => $campioniCustomFields];
|
||||||
|
$patchJson = json_encode($patchPayload, JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES);
|
||||||
|
|
||||||
|
$logContentStep63 .= "PATCH Campione({$campioneId}):\n" .
|
||||||
|
"curl --location --request PATCH '{$apiBaseUrl}Campione({$campioneId})' \\\n" .
|
||||||
|
"--header 'Content-Type: application/json' \\\n" .
|
||||||
|
"--header 'Authorization: Bearer ••••••' \\\n" .
|
||||||
|
"--data '{$patchJson}'\n\n";
|
||||||
|
|
||||||
|
$patchResult = $api->patch("Campione({$campioneId})", $patchPayload);
|
||||||
|
|
||||||
|
$logContentStep63 .= "RESPONSE:\n" . json_encode($patchResult, JSON_PRETTY_PRINT) . "\n\n---\n";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
$logFileStep63 = $logDir . "commessa_{$commessaId}_campioni_customfields_step60_" . time() . ".txt";
|
||||||
|
$writeLog($logFileStep63, $logContentStep63, "STEP 6.0 - Campioni CustomFields (commessa={$commessaId})");
|
||||||
|
|
||||||
// 🔹 STEP 6.1: Fetch photos linked to this iddatadb
|
// 🔹 STEP 6.1: Fetch photos linked to this iddatadb
|
||||||
$stmtPhotos = $pdo->prepare("
|
$stmtPhotos = $pdo->prepare("
|
||||||
SELECT id, file_path, file_name, StampaNelRapporto, PrimaPagina
|
SELECT id, file_path, file_name, StampaNelRapporto, PrimaPagina
|
||||||
@@ -242,15 +349,28 @@ try {
|
|||||||
$photos = $stmtPhotos->fetchAll(PDO::FETCH_ASSOC);
|
$photos = $stmtPhotos->fetchAll(PDO::FETCH_ASSOC);
|
||||||
|
|
||||||
|
|
||||||
// 🔹 STEP 6.2: Upload photos to the first (main) Campione only
|
// 🔹 STEP 6.2: Upload photos to Campione .01 (fetched from API)
|
||||||
$photosUploaded = 0;
|
$photosUploaded = 0;
|
||||||
$logContentPhotos = "Photos for CommessaWeb {$commessaId} (iddatadb={$iddatadb}):\n";
|
$logContentPhotos = "Photos for CommessaWeb {$commessaId} (iddatadb={$iddatadb}):\n";
|
||||||
$logContentPhotos .= "Total photos found: " . count($photos) . ", campioni: " . count($campioni) . "\n\n";
|
$logContentPhotos .= "Total photos found: " . count($photos) . ", campioni: " . count($campioni) . "\n\n";
|
||||||
|
|
||||||
if (!empty($campioni) && !empty($photos)) {
|
if (!empty($campioni) && !empty($photos)) {
|
||||||
$mainCampione = $campioni[0];
|
// Fetch campioni list from API to find the .01 campione
|
||||||
|
$commessaCampioni = $api->get("CommessaWeb({$commessaId})?\$expand=Campioni");
|
||||||
|
$apiCampioni = $commessaCampioni['Campioni'] ?? [];
|
||||||
|
|
||||||
|
// Sort by CodiceCampione to find .01
|
||||||
|
usort($apiCampioni, fn($a, $b) => strcmp($a['CodiceCampione'] ?? '', $b['CodiceCampione'] ?? ''));
|
||||||
|
|
||||||
|
$mainCampione = $apiCampioni[0] ?? null;
|
||||||
$campioneId = (int)($mainCampione['IdCampione'] ?? 0);
|
$campioneId = (int)($mainCampione['IdCampione'] ?? 0);
|
||||||
|
|
||||||
|
$logContentPhotos .= "API Campioni order:\n";
|
||||||
|
foreach ($apiCampioni as $ac) {
|
||||||
|
$logContentPhotos .= " - {$ac['CodiceCampione']} (IdCampione: {$ac['IdCampione']})\n";
|
||||||
|
}
|
||||||
|
$logContentPhotos .= "Selected .01 campione: IdCampione={$campioneId}\n\n";
|
||||||
|
|
||||||
if ($campioneId > 0) {
|
if ($campioneId > 0) {
|
||||||
$logContentPhotos .= "=== Campione {$campioneId} (main) ===\n";
|
$logContentPhotos .= "=== Campione {$campioneId} (main) ===\n";
|
||||||
|
|
||||||
@@ -264,21 +384,42 @@ try {
|
|||||||
}
|
}
|
||||||
|
|
||||||
$photoEndpoint = "Campione({$campioneId})/UploadCampioneFile";
|
$photoEndpoint = "Campione({$campioneId})/UploadCampioneFile";
|
||||||
$stampaNelRapporto = !empty($photo['StampaNelRapporto']) ? 'true' : 'false';
|
$stampaNelRapporto = !empty($photo['StampaNelRapporto']);
|
||||||
$primaPagina = !empty($photo['PrimaPagina']) ? 'true' : 'false';
|
$primaPagina = !empty($photo['PrimaPagina']);
|
||||||
|
|
||||||
$logContentPhotos .= "curl --location --request POST '{$apiBaseUrl}{$photoEndpoint}' \\\n" .
|
$logContentPhotos .= "curl --location --request POST '{$apiBaseUrl}{$photoEndpoint}' \\\n" .
|
||||||
"--header 'Authorization: Bearer ••••••' \\\n" .
|
"--header 'Authorization: Bearer ••••••' \\\n" .
|
||||||
"--form 'file=@{$fullPath}' \\\n" .
|
"--form 'file=@{$fullPath}'\n\n";
|
||||||
"--form 'StampaNelRapporto={$stampaNelRapporto}' \\\n" .
|
|
||||||
"--form 'PrimaPagina={$primaPagina}'\n\n";
|
|
||||||
|
|
||||||
$extraFields = [
|
// Step 1: Upload file (flags are ignored by API during upload)
|
||||||
'StampaNelRapporto' => $stampaNelRapporto,
|
$photoResult = $api->postMultipart($photoEndpoint, $fullPath, $photo['file_name']);
|
||||||
'PrimaPagina' => $primaPagina,
|
$logContentPhotos .= "UPLOAD RESPONSE:\n" . json_encode($photoResult, JSON_PRETTY_PRINT) . "\n\n";
|
||||||
];
|
|
||||||
$photoResult = $api->postMultipart($photoEndpoint, $fullPath, $photo['file_name'], $extraFields);
|
// Step 2: PATCH CampioneFile to set flags (StampaNelRapporto, PrimaPagina)
|
||||||
$logContentPhotos .= "RESPONSE:\n" . json_encode($photoResult, JSON_PRETTY_PRINT) . "\n\n---\n";
|
$campioneFileId = (int)($photoResult['IdCampioneFile'] ?? 0);
|
||||||
|
if ($campioneFileId > 0 && ($stampaNelRapporto || $primaPagina)) {
|
||||||
|
$patchPayload = [];
|
||||||
|
|
||||||
|
if ($stampaNelRapporto) {
|
||||||
|
$patchPayload['StampaNelRapporto'] = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($primaPagina) {
|
||||||
|
$patchPayload['PrimaPagina'] = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
$patchEndpoint = "CampioneFile({$campioneFileId})";
|
||||||
|
$patchJsonLog = json_encode($patchPayload, JSON_PRETTY_PRINT);
|
||||||
|
$logContentPhotos .= "curl --location --request PATCH '{$apiBaseUrl}{$patchEndpoint}' \\\n" .
|
||||||
|
"--header 'Content-Type: application/json' \\\n" .
|
||||||
|
"--header 'Authorization: Bearer ••••••' \\\n" .
|
||||||
|
"--data '{$patchJsonLog}'\n\n";
|
||||||
|
|
||||||
|
$patchResult = $api->patch($patchEndpoint, $patchPayload);
|
||||||
|
$logContentPhotos .= "PATCH RESPONSE:\n" . json_encode($patchResult, JSON_PRETTY_PRINT) . "\n\n";
|
||||||
|
}
|
||||||
|
|
||||||
|
$logContentPhotos .= "---\n";
|
||||||
$photosUploaded++;
|
$photosUploaded++;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
@@ -315,7 +456,7 @@ try {
|
|||||||
|
|
||||||
// Valida se il campo è di tipo Data
|
// Valida se il campo è di tipo Data
|
||||||
if ($fieldType === 'Data' && $newValue !== $currentValue) {
|
if ($fieldType === 'Data' && $newValue !== $currentValue) {
|
||||||
$newValue = validateDate($newValue);
|
$newValue = formatDateToExport($newValue);
|
||||||
}
|
}
|
||||||
|
|
||||||
$commessaCustomFields[] = [
|
$commessaCustomFields[] = [
|
||||||
@@ -372,7 +513,13 @@ try {
|
|||||||
// 🔹 STEP 9.5: Importazione da CommessaWeb a Commessa (commentato come richiesto)
|
// 🔹 STEP 9.5: Importazione da CommessaWeb a Commessa (commentato come richiesto)
|
||||||
// Supplier call: POST api/odata/CommessaWeb(XXX)/ImportaCommessa
|
// Supplier call: POST api/odata/CommessaWeb(XXX)/ImportaCommessa
|
||||||
|
|
||||||
$importPayload = ["IdUtente" => 285]; // user-id
|
$importUserId = (!empty($lims_global_user_id) && is_numeric($lims_global_user_id))
|
||||||
|
? (int) $lims_global_user_id
|
||||||
|
: 285;
|
||||||
|
|
||||||
|
$importPayload = [
|
||||||
|
"IdUtente" => $importUserId
|
||||||
|
];
|
||||||
$importResult = $api->post("CommessaWeb({$commessaId})/ImportaCommessa", $importPayload);
|
$importResult = $api->post("CommessaWeb({$commessaId})/ImportaCommessa", $importPayload);
|
||||||
|
|
||||||
$importPayloadLog = json_encode($importPayload, JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES);
|
$importPayloadLog = json_encode($importPayload, JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES);
|
||||||
@@ -395,7 +542,23 @@ try {
|
|||||||
"RESPONSE:\n" . json_encode($commessaAfterPatch, JSON_PRETTY_PRINT);
|
"RESPONSE:\n" . json_encode($commessaAfterPatch, JSON_PRETTY_PRINT);
|
||||||
$logFileStep10 = $logDir . "commessa_{$commessaId}_get_step10_" . time() . ".txt";
|
$logFileStep10 = $logDir . "commessa_{$commessaId}_get_step10_" . time() . ".txt";
|
||||||
$writeLog($logFileStep10, $logContentStep10, "STEP 10 - GET verify (commessa={$commessaId})");
|
$writeLog($logFileStep10, $logContentStep10, "STEP 10 - GET verify (commessa={$commessaId})");
|
||||||
|
// 🔹 STEP 10.1: Save final CodiceCommessa into datadb.commessaweb
|
||||||
|
// After ImportaCommessa, the API returns the final LIMS job code in CodiceCommessa.
|
||||||
|
// Example: CodiceCommessa = 2614795, CodiceCommessaWeb = 26C0103.
|
||||||
|
$finalCodiceCommessa = trim((string)($commessaAfterPatch['CodiceCommessa'] ?? ''));
|
||||||
|
|
||||||
|
if ($finalCodiceCommessa !== '') {
|
||||||
|
$stmt = $pdo->prepare("
|
||||||
|
UPDATE datadb
|
||||||
|
SET commessaweb = :commessaweb,
|
||||||
|
status = 'l'
|
||||||
|
WHERE iddatadb = :iddatadb
|
||||||
|
");
|
||||||
|
$stmt->execute([
|
||||||
|
'commessaweb' => substr($finalCodiceCommessa, 0, 30),
|
||||||
|
'iddatadb' => $iddatadb
|
||||||
|
]);
|
||||||
|
}
|
||||||
// 🔹 STEP 11: Prepare final response
|
// 🔹 STEP 11: Prepare final response
|
||||||
$finalCommessa = [
|
$finalCommessa = [
|
||||||
"Cliente" => $clienteId,
|
"Cliente" => $clienteId,
|
||||||
@@ -410,7 +573,7 @@ try {
|
|||||||
echo json_encode([
|
echo json_encode([
|
||||||
"success" => true,
|
"success" => true,
|
||||||
"idcommessaweb" => $commessaId,
|
"idcommessaweb" => $commessaId,
|
||||||
"commessaweb" => $commessaWebCode,
|
"commessaweb" => $finalCodiceCommessa ?: $commessaWebCode,
|
||||||
"commessaWeb" => $finalCommessa,
|
"commessaWeb" => $finalCommessa,
|
||||||
"commessaWebApiResponse" => $commessaWeb, // Incluso per debug
|
"commessaWebApiResponse" => $commessaWeb, // Incluso per debug
|
||||||
"totalCampioni" => count($campioni),
|
"totalCampioni" => count($campioni),
|
||||||
|
|||||||
@@ -0,0 +1,50 @@
|
|||||||
|
<?php
|
||||||
|
require_once dirname(__DIR__, 2) . '/vendor/autoload.php';
|
||||||
|
require_once dirname(__FILE__) . '/class/VisualLimsApiClient.class.php';
|
||||||
|
|
||||||
|
header('Content-Type: application/json');
|
||||||
|
|
||||||
|
ini_set('display_errors', '0');
|
||||||
|
error_reporting(E_ALL);
|
||||||
|
|
||||||
|
try {
|
||||||
|
$api = VisualLimsApiClient::getInstance();
|
||||||
|
|
||||||
|
// Endpoint per recuperare le Analisi
|
||||||
|
$endpoint = 'Analisi';
|
||||||
|
|
||||||
|
// Opzioni OData
|
||||||
|
$options = [
|
||||||
|
// Restituisce solo i campi principali utili
|
||||||
|
'$select' => 'IdAnalisi,Codice,NomeAnalisi,ClientiAbilitati,MatriciAbilitate,IsGenerico,Tipo,ParentKey,SelezionabileSuWeb',
|
||||||
|
|
||||||
|
// Solo analisi effettivamente selezionabili sul web
|
||||||
|
'$filter' => 'SelezionabileSuWeb eq true',
|
||||||
|
|
||||||
|
// Ordinamento alfabetico per nome analisi
|
||||||
|
'$orderby' => 'NomeAnalisi asc'
|
||||||
|
];
|
||||||
|
|
||||||
|
// Debug: salva URL usato
|
||||||
|
$base_url = 'https://93.43.5.102/limsapi/api/odata/';
|
||||||
|
$query = http_build_query($options);
|
||||||
|
$full_url = $base_url . $endpoint . ($query ? '?' . $query : '');
|
||||||
|
file_put_contents(__DIR__ . '/last_analisi_url.txt', $full_url . PHP_EOL, FILE_APPEND);
|
||||||
|
|
||||||
|
// Chiamata API
|
||||||
|
$data = $api->get($endpoint, $options);
|
||||||
|
|
||||||
|
// Salva il JSON in locale
|
||||||
|
file_put_contents(__DIR__ . '/analisi_response.json', json_encode($data, JSON_PRETTY_PRINT | JSON_UNESCAPED_UNICODE));
|
||||||
|
|
||||||
|
echo json_encode($data, JSON_UNESCAPED_UNICODE);
|
||||||
|
} catch (Exception $e) {
|
||||||
|
file_put_contents(
|
||||||
|
__DIR__ . '/analisi_error_log.txt',
|
||||||
|
date('Y-m-d H:i:s') . ' - ' . $e->getMessage() . PHP_EOL,
|
||||||
|
FILE_APPEND
|
||||||
|
);
|
||||||
|
|
||||||
|
http_response_code(500);
|
||||||
|
echo json_encode(['error' => $e->getMessage()], JSON_UNESCAPED_UNICODE);
|
||||||
|
}
|
||||||
@@ -0,0 +1,68 @@
|
|||||||
|
<?php
|
||||||
|
require_once dirname(__DIR__, 2) . '/vendor/autoload.php';
|
||||||
|
require_once dirname(__FILE__) . '/class/VisualLimsApiClient.class.php';
|
||||||
|
|
||||||
|
header('Content-Type: application/json');
|
||||||
|
ini_set('display_errors', '0');
|
||||||
|
error_reporting(E_ALL);
|
||||||
|
|
||||||
|
try {
|
||||||
|
$idCliente = isset($_GET['id_cliente']) ? (int)$_GET['id_cliente'] : 0;
|
||||||
|
$idMatrice = isset($_GET['id_matrice']) ? (int)$_GET['id_matrice'] : 0;
|
||||||
|
$debug = isset($_GET['debug']) ? (int)$_GET['debug'] : 0;
|
||||||
|
|
||||||
|
if ($idCliente <= 0) {
|
||||||
|
http_response_code(400);
|
||||||
|
echo json_encode(['error' => 'Missing or invalid id_cliente']);
|
||||||
|
exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
$api = VisualLimsApiClient::getInstance();
|
||||||
|
|
||||||
|
if ($idMatrice > 0) {
|
||||||
|
$expandExpr = "AnalisiAbilitate(\$filter=Matrice/IdMatrice eq $idMatrice)";
|
||||||
|
} else {
|
||||||
|
$expandExpr = "AnalisiAbilitate";
|
||||||
|
}
|
||||||
|
|
||||||
|
// Encode only the $expand expression because it contains spaces, slash and parentheses
|
||||||
|
$expandEncoded = rawurlencode($expandExpr);
|
||||||
|
$endpoint = "Cliente($idCliente)?\$expand={$expandEncoded}";
|
||||||
|
|
||||||
|
$base_url = 'https://93.43.5.102/limsapi/api/odata/';
|
||||||
|
$full_url = $base_url . $endpoint;
|
||||||
|
|
||||||
|
file_put_contents(__DIR__ . '/last_url_analisi.txt', $full_url . PHP_EOL, FILE_APPEND);
|
||||||
|
|
||||||
|
$data = $api->get($endpoint, []);
|
||||||
|
|
||||||
|
file_put_contents(
|
||||||
|
__DIR__ . '/analisi_by_cliente_matrice_response.json',
|
||||||
|
json_encode($data, JSON_PRETTY_PRINT | JSON_UNESCAPED_UNICODE)
|
||||||
|
);
|
||||||
|
|
||||||
|
if ($debug === 1) {
|
||||||
|
echo json_encode([
|
||||||
|
'endpoint' => $endpoint,
|
||||||
|
'full_url' => $full_url,
|
||||||
|
'raw_response' => $data
|
||||||
|
], JSON_PRETTY_PRINT | JSON_UNESCAPED_UNICODE);
|
||||||
|
exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
$analisi = $data['AnalisiAbilitate'] ?? [];
|
||||||
|
|
||||||
|
echo json_encode([
|
||||||
|
'value' => $analisi,
|
||||||
|
'count' => is_array($analisi) ? count($analisi) : 0
|
||||||
|
], JSON_UNESCAPED_UNICODE);
|
||||||
|
} catch (Exception $e) {
|
||||||
|
file_put_contents(
|
||||||
|
__DIR__ . '/error_log_analisi.txt',
|
||||||
|
date('Y-m-d H:i:s') . ' - ' . $e->getMessage() . PHP_EOL,
|
||||||
|
FILE_APPEND
|
||||||
|
);
|
||||||
|
|
||||||
|
http_response_code(500);
|
||||||
|
echo json_encode(['error' => $e->getMessage()]);
|
||||||
|
}
|
||||||
@@ -0,0 +1,56 @@
|
|||||||
|
<?php
|
||||||
|
require_once dirname(__DIR__, 2) . '/vendor/autoload.php';
|
||||||
|
require_once dirname(__FILE__) . '/class/VisualLimsApiClient.class.php';
|
||||||
|
|
||||||
|
header('Content-Type: application/json');
|
||||||
|
ini_set('display_errors', '0');
|
||||||
|
error_reporting(E_ALL);
|
||||||
|
|
||||||
|
try {
|
||||||
|
$idMatrice = isset($_GET['id_matrice']) ? (int)$_GET['id_matrice'] : 0;
|
||||||
|
|
||||||
|
if ($idMatrice <= 0) {
|
||||||
|
http_response_code(400);
|
||||||
|
echo json_encode(['error' => 'Missing or invalid id_matrice']);
|
||||||
|
exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
$api = VisualLimsApiClient::getInstance();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* OData hypothesis:
|
||||||
|
* - Expand enabled matrices
|
||||||
|
* - Return only selectable analyses
|
||||||
|
* - Include generic analyses OR analyses enabled for the given matrix
|
||||||
|
*
|
||||||
|
* This endpoint must be verified against the real VisualLims API metadata.
|
||||||
|
*/
|
||||||
|
$filter = rawurlencode("SelezionabileSuWeb eq true and (IsGenerico eq true or MatriciAbilitate/any(m:m/IdMatrice eq $idMatrice))");
|
||||||
|
$expand = rawurlencode("MatriciAbilitate");
|
||||||
|
$endpoint = "Analisi?\$top=10";
|
||||||
|
|
||||||
|
// Debug URL
|
||||||
|
$base_url = 'https://93.43.5.102/limsapi/api/odata/';
|
||||||
|
$full_url = $base_url . $endpoint;
|
||||||
|
file_put_contents(__DIR__ . '/last_url_analisi.txt', $full_url . PHP_EOL, FILE_APPEND);
|
||||||
|
|
||||||
|
$data = $api->get($endpoint, []);
|
||||||
|
|
||||||
|
file_put_contents(
|
||||||
|
__DIR__ . '/analisi_by_matrice_response.json',
|
||||||
|
json_encode($data, JSON_PRETTY_PRINT | JSON_UNESCAPED_UNICODE)
|
||||||
|
);
|
||||||
|
|
||||||
|
$analisi = $data['value'] ?? [];
|
||||||
|
|
||||||
|
echo json_encode(['value' => $analisi], JSON_UNESCAPED_UNICODE);
|
||||||
|
} catch (Exception $e) {
|
||||||
|
file_put_contents(
|
||||||
|
__DIR__ . '/error_log_analisi.txt',
|
||||||
|
date('Y-m-d H:i:s') . ' - ' . $e->getMessage() . PHP_EOL,
|
||||||
|
FILE_APPEND
|
||||||
|
);
|
||||||
|
|
||||||
|
http_response_code(500);
|
||||||
|
echo json_encode(['error' => $e->getMessage()]);
|
||||||
|
}
|
||||||
@@ -0,0 +1,60 @@
|
|||||||
|
<?php
|
||||||
|
require_once dirname(__DIR__, 2) . '/vendor/autoload.php';
|
||||||
|
require_once dirname(__FILE__) . '/class/VisualLimsApiClient.class.php';
|
||||||
|
|
||||||
|
header('Content-Type: application/json');
|
||||||
|
ini_set('display_errors', '0');
|
||||||
|
error_reporting(E_ALL);
|
||||||
|
|
||||||
|
try {
|
||||||
|
$idMatrice = isset($_GET['id_matrice']) ? (int)$_GET['id_matrice'] : 0;
|
||||||
|
$debug = isset($_GET['debug']) ? (int)$_GET['debug'] : 0;
|
||||||
|
|
||||||
|
if ($idMatrice <= 0) {
|
||||||
|
http_response_code(400);
|
||||||
|
echo json_encode(['error' => 'Missing or invalid id_matrice']);
|
||||||
|
exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
$api = VisualLimsApiClient::getInstance();
|
||||||
|
|
||||||
|
$filter = rawurlencode("Matrice/IdMatrice eq $idMatrice");
|
||||||
|
$endpoint = "Analisi?\$filter={$filter}";
|
||||||
|
|
||||||
|
$base_url = 'https://93.43.5.102/limsapi/api/odata/';
|
||||||
|
$full_url = $base_url . $endpoint;
|
||||||
|
|
||||||
|
file_put_contents(__DIR__ . '/last_url_analisi.txt', $full_url . PHP_EOL, FILE_APPEND);
|
||||||
|
|
||||||
|
$data = $api->get($endpoint, []);
|
||||||
|
|
||||||
|
file_put_contents(
|
||||||
|
__DIR__ . '/analisi_by_matrice_response.json',
|
||||||
|
json_encode($data, JSON_PRETTY_PRINT | JSON_UNESCAPED_UNICODE)
|
||||||
|
);
|
||||||
|
|
||||||
|
if ($debug === 1) {
|
||||||
|
echo json_encode([
|
||||||
|
'endpoint' => $endpoint,
|
||||||
|
'full_url' => $full_url,
|
||||||
|
'raw_response' => $data
|
||||||
|
], JSON_PRETTY_PRINT | JSON_UNESCAPED_UNICODE);
|
||||||
|
exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
$analisi = $data['value'] ?? [];
|
||||||
|
|
||||||
|
echo json_encode([
|
||||||
|
'value' => $analisi,
|
||||||
|
'count' => is_array($analisi) ? count($analisi) : 0
|
||||||
|
], JSON_UNESCAPED_UNICODE);
|
||||||
|
} catch (Exception $e) {
|
||||||
|
file_put_contents(
|
||||||
|
__DIR__ . '/error_log_analisi.txt',
|
||||||
|
date('Y-m-d H:i:s') . ' - ' . $e->getMessage() . PHP_EOL,
|
||||||
|
FILE_APPEND
|
||||||
|
);
|
||||||
|
|
||||||
|
http_response_code(500);
|
||||||
|
echo json_encode(['error' => $e->getMessage()]);
|
||||||
|
}
|
||||||
@@ -0,0 +1,95 @@
|
|||||||
|
<?php
|
||||||
|
require_once dirname(__DIR__, 2) . '/vendor/autoload.php';
|
||||||
|
require_once dirname(__FILE__) . '/class/VisualLimsApiClient.class.php';
|
||||||
|
|
||||||
|
header('Content-Type: application/json');
|
||||||
|
|
||||||
|
ini_set('display_errors', '0');
|
||||||
|
error_reporting(E_ALL);
|
||||||
|
|
||||||
|
try {
|
||||||
|
$api = VisualLimsApiClient::getInstance();
|
||||||
|
|
||||||
|
// Campione hardcoded del tuo esempio
|
||||||
|
$idCampione = 749027;
|
||||||
|
|
||||||
|
// Analisi che stai provando ad assegnare
|
||||||
|
$targetRecordKey = '11;1218320';
|
||||||
|
$targetIdAnalisi = '1218320';
|
||||||
|
|
||||||
|
// Endpoint: recupera il campione espandendo le analisi abilitate
|
||||||
|
$endpoint = "Campione($idCampione)";
|
||||||
|
|
||||||
|
// ATTENZIONE:
|
||||||
|
// nella tua classe normalmente le opzioni vengono trasformate in query string.
|
||||||
|
// Qui serve: ?$expand=AnalisiAbilitate
|
||||||
|
$options = [
|
||||||
|
'$expand' => 'AnalisiAbilitate'
|
||||||
|
];
|
||||||
|
|
||||||
|
// Debug: salva URL usato
|
||||||
|
$base_url = 'https://93.43.5.102/limsapi/api/odata/';
|
||||||
|
$query = http_build_query($options);
|
||||||
|
$query = urldecode($query); // rende leggibile $expand invece di %24expand
|
||||||
|
|
||||||
|
$full_url = $base_url . $endpoint . ($query ? '?' . $query : '');
|
||||||
|
file_put_contents(__DIR__ . '/last_url_analisi_abilitate.txt', $full_url . PHP_EOL, FILE_APPEND);
|
||||||
|
|
||||||
|
// Chiamata API
|
||||||
|
$data = $api->get($endpoint, $options);
|
||||||
|
|
||||||
|
// Recupera AnalisiAbilitate dalla risposta
|
||||||
|
$analisiAbilitate = $data['AnalisiAbilitate'] ?? [];
|
||||||
|
|
||||||
|
// Alcune API OData possono restituire collection dentro "value"
|
||||||
|
if (isset($analisiAbilitate['value']) && is_array($analisiAbilitate['value'])) {
|
||||||
|
$analisiAbilitate = $analisiAbilitate['value'];
|
||||||
|
}
|
||||||
|
|
||||||
|
// Cerca se il RecordKey / IdAnalisi che stai usando è effettivamente assegnabile
|
||||||
|
$matches = [];
|
||||||
|
|
||||||
|
foreach ($analisiAbilitate as $analisi) {
|
||||||
|
$recordKey = isset($analisi['RecordKey']) ? (string)$analisi['RecordKey'] : '';
|
||||||
|
$idAnalisi = isset($analisi['IdAnalisi']) ? (string)$analisi['IdAnalisi'] : '';
|
||||||
|
|
||||||
|
if ($recordKey === $targetRecordKey || $idAnalisi === $targetIdAnalisi) {
|
||||||
|
$matches[] = $analisi;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Output diagnostico
|
||||||
|
$output = [
|
||||||
|
'success' => true,
|
||||||
|
'idCampione' => $idCampione,
|
||||||
|
'request_url' => $full_url,
|
||||||
|
'targetRecordKey' => $targetRecordKey,
|
||||||
|
'targetIdAnalisi' => $targetIdAnalisi,
|
||||||
|
'enabled_analyses_count' => is_array($analisiAbilitate) ? count($analisiAbilitate) : 0,
|
||||||
|
'target_found' => count($matches) > 0,
|
||||||
|
'target_matches' => $matches,
|
||||||
|
'analisi_abilitate' => $analisiAbilitate,
|
||||||
|
'raw_response' => $data
|
||||||
|
];
|
||||||
|
|
||||||
|
// Salva il JSON in locale
|
||||||
|
file_put_contents(
|
||||||
|
__DIR__ . '/analisi_abilitate_campione_749027_response.json',
|
||||||
|
json_encode($output, JSON_PRETTY_PRINT | JSON_UNESCAPED_UNICODE)
|
||||||
|
);
|
||||||
|
|
||||||
|
echo json_encode($output, JSON_PRETTY_PRINT | JSON_UNESCAPED_UNICODE);
|
||||||
|
} catch (Exception $e) {
|
||||||
|
file_put_contents(
|
||||||
|
__DIR__ . '/error_log_analisi_abilitate.txt',
|
||||||
|
date('Y-m-d H:i:s') . ' - ' . $e->getMessage() . PHP_EOL,
|
||||||
|
FILE_APPEND
|
||||||
|
);
|
||||||
|
|
||||||
|
http_response_code(500);
|
||||||
|
|
||||||
|
echo json_encode([
|
||||||
|
'success' => false,
|
||||||
|
'error' => $e->getMessage()
|
||||||
|
], JSON_PRETTY_PRINT | JSON_UNESCAPED_UNICODE);
|
||||||
|
}
|
||||||
@@ -0,0 +1,88 @@
|
|||||||
|
<?php
|
||||||
|
require_once dirname(__DIR__, 2) . '/vendor/autoload.php';
|
||||||
|
require_once dirname(__FILE__) . '/class/VisualLimsApiClient.class.php';
|
||||||
|
|
||||||
|
header('Content-Type: application/json');
|
||||||
|
|
||||||
|
ini_set('display_errors', '0');
|
||||||
|
error_reporting(E_ALL);
|
||||||
|
|
||||||
|
try {
|
||||||
|
$api = VisualLimsApiClient::getInstance();
|
||||||
|
|
||||||
|
// Campione hardcoded del tuo esempio
|
||||||
|
$idCampione = 749027;
|
||||||
|
|
||||||
|
// Matrice attesa dal log STEP 6
|
||||||
|
$expectedMatriceId = 6430;
|
||||||
|
|
||||||
|
// Endpoint con expand della Matrice
|
||||||
|
$endpoint = "Campione($idCampione)";
|
||||||
|
$options = [
|
||||||
|
'$expand' => 'Matrice'
|
||||||
|
];
|
||||||
|
|
||||||
|
// Debug URL
|
||||||
|
$base_url = 'https://93.43.5.102/limsapi/api/odata/';
|
||||||
|
$query = http_build_query($options);
|
||||||
|
$queryReadable = urldecode($query);
|
||||||
|
|
||||||
|
$full_url = $base_url . $endpoint . ($queryReadable ? '?' . $queryReadable : '');
|
||||||
|
|
||||||
|
file_put_contents(
|
||||||
|
__DIR__ . '/last_url_check_matrice.txt',
|
||||||
|
$full_url . PHP_EOL,
|
||||||
|
FILE_APPEND
|
||||||
|
);
|
||||||
|
|
||||||
|
// Chiamata API
|
||||||
|
$data = $api->get($endpoint, $options);
|
||||||
|
|
||||||
|
// Recupero Matrice dalla response
|
||||||
|
$matrice = $data['Matrice'] ?? null;
|
||||||
|
|
||||||
|
$actualMatriceId = null;
|
||||||
|
|
||||||
|
if (is_array($matrice)) {
|
||||||
|
// Provo i nomi più probabili
|
||||||
|
$actualMatriceId = $matrice['IdMatrice']
|
||||||
|
?? $matrice['idMatrice']
|
||||||
|
?? $matrice['Id']
|
||||||
|
?? $matrice['ID']
|
||||||
|
?? null;
|
||||||
|
}
|
||||||
|
|
||||||
|
$matrice_ok = ((int)$actualMatriceId === (int)$expectedMatriceId);
|
||||||
|
|
||||||
|
$output = [
|
||||||
|
'success' => true,
|
||||||
|
'idCampione' => $idCampione,
|
||||||
|
'expectedMatriceId' => $expectedMatriceId,
|
||||||
|
'actualMatriceId' => $actualMatriceId,
|
||||||
|
'matrice_ok' => $matrice_ok,
|
||||||
|
'request_url' => $full_url,
|
||||||
|
'matrice' => $matrice,
|
||||||
|
'raw_response' => $data
|
||||||
|
];
|
||||||
|
|
||||||
|
// Salva JSON completo
|
||||||
|
file_put_contents(
|
||||||
|
__DIR__ . '/check_matrice_campione_749027_response.json',
|
||||||
|
json_encode($output, JSON_PRETTY_PRINT | JSON_UNESCAPED_UNICODE)
|
||||||
|
);
|
||||||
|
|
||||||
|
echo json_encode($output, JSON_PRETTY_PRINT | JSON_UNESCAPED_UNICODE);
|
||||||
|
} catch (Exception $e) {
|
||||||
|
file_put_contents(
|
||||||
|
__DIR__ . '/error_log_check_matrice.txt',
|
||||||
|
date('Y-m-d H:i:s') . ' - ' . $e->getMessage() . PHP_EOL,
|
||||||
|
FILE_APPEND
|
||||||
|
);
|
||||||
|
|
||||||
|
http_response_code(500);
|
||||||
|
|
||||||
|
echo json_encode([
|
||||||
|
'success' => false,
|
||||||
|
'error' => $e->getMessage()
|
||||||
|
], JSON_PRETTY_PRINT | JSON_UNESCAPED_UNICODE);
|
||||||
|
}
|
||||||
@@ -74,10 +74,21 @@ try {
|
|||||||
throw new Exception("Massimo numero di tentativi raggiunto per $endpoint");
|
throw new Exception("Massimo numero di tentativi raggiunto per $endpoint");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Cache file (1 hour TTL)
|
||||||
|
$cacheFile = __DIR__ . '/cache/clienti.json';
|
||||||
|
if (file_exists($cacheFile) && (time() - filemtime($cacheFile) < 3600)) {
|
||||||
|
readfile($cacheFile);
|
||||||
|
exit;
|
||||||
|
}
|
||||||
|
|
||||||
// Esegui la chiamata con retry
|
// Esegui la chiamata con retry
|
||||||
$data = makeApiRequest($api, $endpoint);
|
$data = makeApiRequest($api, $endpoint);
|
||||||
|
|
||||||
echo json_encode($data);
|
$json = json_encode($data);
|
||||||
|
if (!is_dir(__DIR__ . '/cache')) mkdir(__DIR__ . '/cache', 0777, true);
|
||||||
|
file_put_contents($cacheFile, $json);
|
||||||
|
|
||||||
|
echo $json;
|
||||||
} catch (Exception $e) {
|
} catch (Exception $e) {
|
||||||
http_response_code(500);
|
http_response_code(500);
|
||||||
$errorResponse = [
|
$errorResponse = [
|
||||||
|
|||||||
@@ -0,0 +1,103 @@
|
|||||||
|
<?php
|
||||||
|
require_once dirname(__DIR__, 2) . '/vendor/autoload.php';
|
||||||
|
require_once __DIR__ . '/class/VisualLimsApiClient.class.php';
|
||||||
|
|
||||||
|
header('Content-Type: application/json');
|
||||||
|
|
||||||
|
// Disable PHP error display
|
||||||
|
ini_set('display_errors', '0');
|
||||||
|
error_reporting(E_ALL);
|
||||||
|
|
||||||
|
try {
|
||||||
|
$api = VisualLimsApiClient::getInstance(); // also loads dotenv
|
||||||
|
|
||||||
|
// In simulate mode: return fake clients built from idclient values already in datadb.
|
||||||
|
if (($_ENV['SIMULATE_EXPORT_LIMS'] ?? '') === 'true') {
|
||||||
|
require_once __DIR__ . '/class/db-functions.php';
|
||||||
|
$pdo = DBHandlerSelect::getInstance()->getConnection();
|
||||||
|
|
||||||
|
$stmt = $pdo->query("
|
||||||
|
SELECT DISTINCT idclient
|
||||||
|
FROM datadb
|
||||||
|
WHERE idclient IS NOT NULL
|
||||||
|
AND idclient > 0
|
||||||
|
ORDER BY idclient ASC
|
||||||
|
");
|
||||||
|
|
||||||
|
$ids = $stmt->fetchAll(PDO::FETCH_COLUMN);
|
||||||
|
|
||||||
|
$fakeClients = array_map(fn($id) => [
|
||||||
|
'IdCliente' => (int) $id,
|
||||||
|
'Nominativo' => "Cliente Simulato {$id}",
|
||||||
|
'CodiceCliente' => "SIM_{$id}",
|
||||||
|
], $ids);
|
||||||
|
|
||||||
|
echo json_encode(['value' => $fakeClients], JSON_UNESCAPED_UNICODE);
|
||||||
|
exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Endpoint senza filtri: recupera tutto
|
||||||
|
$endpoint = 'Cliente?$top=50';
|
||||||
|
|
||||||
|
// Funzione per eseguire la chiamata con retry
|
||||||
|
function makeApiRequest($api, $endpoint, $maxRetries = 3)
|
||||||
|
{
|
||||||
|
for ($retry = 0; $retry < $maxRetries; $retry++) {
|
||||||
|
try {
|
||||||
|
$data = $api->get($endpoint);
|
||||||
|
|
||||||
|
// Save response for debug
|
||||||
|
file_put_contents(
|
||||||
|
__DIR__ . '/clienti_response.json',
|
||||||
|
json_encode($data, JSON_PRETTY_PRINT | JSON_UNESCAPED_UNICODE)
|
||||||
|
);
|
||||||
|
|
||||||
|
return $data;
|
||||||
|
} catch (Exception $e) {
|
||||||
|
$errorMessage = $e->getMessage();
|
||||||
|
|
||||||
|
// Retry only for specific auth/token related issue
|
||||||
|
if (
|
||||||
|
strpos($errorMessage, 'HTTP 400') !== false &&
|
||||||
|
strpos($errorMessage, 'Cannot persist the object') !== false
|
||||||
|
) {
|
||||||
|
try {
|
||||||
|
if (method_exists($api, 'refreshToken')) {
|
||||||
|
$api->refreshToken();
|
||||||
|
error_log("Tentativo {$retry}: refresh token eseguito per endpoint {$endpoint}");
|
||||||
|
} else {
|
||||||
|
throw new Exception('Il metodo refreshToken() non esiste in VisualLimsApiClient');
|
||||||
|
}
|
||||||
|
} catch (Exception $refreshEx) {
|
||||||
|
error_log("Errore durante il refresh del token: " . $refreshEx->getMessage());
|
||||||
|
throw new Exception("Impossibile eseguire il refresh del token: " . $refreshEx->getMessage());
|
||||||
|
}
|
||||||
|
|
||||||
|
usleep(500000); // 500ms
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
throw $e;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
throw new Exception("Massimo numero di tentativi raggiunto per {$endpoint}");
|
||||||
|
}
|
||||||
|
|
||||||
|
// Esegui la chiamata
|
||||||
|
$data = makeApiRequest($api, $endpoint);
|
||||||
|
|
||||||
|
echo json_encode($data, JSON_UNESCAPED_UNICODE);
|
||||||
|
} catch (Exception $e) {
|
||||||
|
http_response_code(500);
|
||||||
|
|
||||||
|
$errorResponse = [
|
||||||
|
'error' => $e->getMessage(),
|
||||||
|
'file' => $e->getFile(),
|
||||||
|
'line' => $e->getLine(),
|
||||||
|
'trace' => $e->getTraceAsString()
|
||||||
|
];
|
||||||
|
|
||||||
|
error_log("Errore in get_clienti.php: " . json_encode($errorResponse, JSON_UNESCAPED_UNICODE));
|
||||||
|
echo json_encode($errorResponse, JSON_UNESCAPED_UNICODE);
|
||||||
|
}
|
||||||
@@ -21,16 +21,25 @@ try {
|
|||||||
}
|
}
|
||||||
|
|
||||||
$results = [];
|
$results = [];
|
||||||
|
$cacheDir = __DIR__ . '/cache';
|
||||||
|
if (!is_dir($cacheDir)) mkdir($cacheDir, 0777, true);
|
||||||
|
|
||||||
foreach ($fieldIds as $customFieldId) {
|
foreach ($fieldIds as $customFieldId) {
|
||||||
$endpoint = "CustomField($customFieldId)?\$expand=CustomFieldsValues";
|
$cacheFile = $cacheDir . '/customfield_' . $customFieldId . '.json';
|
||||||
$data = $api->get($endpoint);
|
|
||||||
|
|
||||||
$results[$customFieldId] = $data['CustomFieldsValues'] ?? [];
|
// Use cache if fresh (1 hour)
|
||||||
|
if (file_exists($cacheFile) && (time() - filemtime($cacheFile) < 3600)) {
|
||||||
|
$results[$customFieldId] = json_decode(file_get_contents($cacheFile), true);
|
||||||
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Debug ფაილი
|
$endpoint = "CustomField($customFieldId)?\$expand=CustomFieldsValues";
|
||||||
file_put_contents(__DIR__ . '/customfield_values_response.json', json_encode($results));
|
$data = $api->get($endpoint);
|
||||||
|
$values = $data['CustomFieldsValues'] ?? [];
|
||||||
|
$results[$customFieldId] = $values;
|
||||||
|
|
||||||
|
file_put_contents($cacheFile, json_encode($values));
|
||||||
|
}
|
||||||
|
|
||||||
echo json_encode($results);
|
echo json_encode($results);
|
||||||
} catch (Exception $e) {
|
} catch (Exception $e) {
|
||||||
|
|||||||
@@ -1,59 +1,46 @@
|
|||||||
<?php
|
<?php
|
||||||
require_once dirname(__DIR__, 2) . '/vendor/autoload.php'; // Risale a root/vendor/
|
require_once dirname(__DIR__, 2) . '/vendor/autoload.php';
|
||||||
use Dotenv\Dotenv;
|
require_once __DIR__ . '/class/db-functions.php';
|
||||||
|
include dirname(__DIR__) . '/../extra/auth.php';
|
||||||
|
if (!Auth::check()) { http_response_code(401); echo json_encode(['error' => 'Unauthorized']); exit; }
|
||||||
|
|
||||||
// Set JSON header
|
|
||||||
header('Content-Type: application/json');
|
header('Content-Type: application/json');
|
||||||
|
|
||||||
// Debug: Log the path where we expect the .env file
|
|
||||||
$envPath = dirname(__DIR__, 2);
|
|
||||||
file_put_contents(__DIR__ . '/debug_log.txt', date('Y-m-d H:i:s') . ' - Expected .env path: ' . $envPath . PHP_EOL, FILE_APPEND);
|
|
||||||
|
|
||||||
// Carica il file .env dalla root del progetto
|
|
||||||
try {
|
try {
|
||||||
$dotenv = Dotenv::createImmutable($envPath);
|
// Read from matrici cache (populated by get_matrici.php / warm_cache.php)
|
||||||
$dotenv->load();
|
$cacheFile = __DIR__ . '/cache/matrici.json';
|
||||||
} catch (Exception $e) {
|
if (file_exists($cacheFile)) {
|
||||||
file_put_contents(__DIR__ . '/error_log.txt', date('Y-m-d H:i:s') . ' - Errore caricamento .env: ' . $e->getMessage() . PHP_EOL, FILE_APPEND);
|
$data = json_decode(file_get_contents($cacheFile), true);
|
||||||
echo json_encode(['success' => false, 'message' => 'Errore caricamento configurazione: ' . $e->getMessage()]);
|
$matrici = $data['value'] ?? [];
|
||||||
exit(1);
|
} else {
|
||||||
}
|
// Fallback: trigger cache creation
|
||||||
|
require_once __DIR__ . '/class/VisualLimsApiClient.class.php';
|
||||||
|
$api = VisualLimsApiClient::getInstance();
|
||||||
|
$apiData = $api->get('Matrice');
|
||||||
|
$matrici = [];
|
||||||
|
foreach (($apiData['value'] ?? []) as $item) {
|
||||||
|
$nome = $item['NomeMatrice'] ?? '';
|
||||||
|
if ($nome !== '' && substr($nome, 0, 1) !== '*') {
|
||||||
|
$matrici[] = ['IdMatrice' => $item['IdMatrice'], 'NomeMatrice' => $nome, 'MacroMatrice' => $item['MacroMatrice'] ?? null];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!is_dir(__DIR__ . '/cache')) mkdir(__DIR__ . '/cache', 0777, true);
|
||||||
|
file_put_contents($cacheFile, json_encode(['success' => true, 'value' => $matrici]));
|
||||||
|
}
|
||||||
|
|
||||||
// Recupera le variabili d'ambiente
|
// Extract unique MacroMatrice values
|
||||||
$dbHost = $_ENV['DB_HOST'];
|
$macroValues = [];
|
||||||
$dbName = $_ENV['DB_DATABASE'];
|
foreach ($matrici as $m) {
|
||||||
$dbUser = $_ENV['DB_USERNAME'];
|
$macro = $m['MacroMatrice'] ?? null;
|
||||||
$dbPass = $_ENV['DB_PASSWORD'];
|
if ($macro !== null && $macro !== '' && substr($macro, 0, 1) !== '*') {
|
||||||
$dbPrefix = $_ENV['DB_PREFIX'];
|
$macroValues[$macro] = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
$macroMatrici = array_keys($macroValues);
|
||||||
|
sort($macroMatrici);
|
||||||
|
|
||||||
// Debug: Log database connection details (excluding password)
|
|
||||||
file_put_contents(__DIR__ . '/debug_log.txt', date('Y-m-d H:i:s') . " - DB Connection: host=$dbHost, dbname=$dbName, user=$dbUser, prefix=$dbPrefix" . PHP_EOL, FILE_APPEND);
|
|
||||||
|
|
||||||
// Connessione al database MySQL
|
|
||||||
try {
|
|
||||||
$pdo = new PDO("mysql:host=$dbHost;dbname=$dbName;charset=utf8mb4", $dbUser, $dbPass);
|
|
||||||
$pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
|
|
||||||
} catch (PDOException $e) {
|
|
||||||
file_put_contents(__DIR__ . '/error_log.txt', date('Y-m-d H:i:s') . ' - Errore connessione DB: ' . $e->getMessage() . PHP_EOL, FILE_APPEND);
|
|
||||||
echo json_encode(['success' => false, 'message' => 'Errore connessione al database: ' . $e->getMessage()]);
|
|
||||||
exit(1);
|
|
||||||
}
|
|
||||||
|
|
||||||
try {
|
|
||||||
// Query per recuperare i valori distinti di MacroMatrice, escludendo quelli che iniziano con '*' e ordinandoli
|
|
||||||
$query = "SELECT DISTINCT MacroMatrice FROM {$dbPrefix}matrici WHERE MacroMatrice IS NOT NULL AND MacroMatrice NOT LIKE '*%' ORDER BY MacroMatrice ASC";
|
|
||||||
$stmt = $pdo->prepare($query);
|
|
||||||
$stmt->execute();
|
|
||||||
$macroMatrici = $stmt->fetchAll(PDO::FETCH_COLUMN);
|
|
||||||
|
|
||||||
// Debug: Log del numero di MacroMatrice recuperate
|
|
||||||
file_put_contents(__DIR__ . '/debug_log.txt', date('Y-m-d H:i:s') . ' - Retrieved ' . count($macroMatrici) . ' MacroMatrice from database' . PHP_EOL, FILE_APPEND);
|
|
||||||
|
|
||||||
// Restituisci risposta JSON
|
|
||||||
echo json_encode(['success' => true, 'value' => $macroMatrici]);
|
echo json_encode(['success' => true, 'value' => $macroMatrici]);
|
||||||
} catch (PDOException $e) {
|
} catch (Exception $e) {
|
||||||
// Log errore e restituisci risposta di errore
|
http_response_code(500);
|
||||||
file_put_contents(__DIR__ . '/error_log.txt', date('Y-m-d H:i:s') . ' - Errore nel recupero delle MacroMatrice: ' . $e->getMessage() . PHP_EOL, FILE_APPEND);
|
echo json_encode(['success' => false, 'message' => $e->getMessage()]);
|
||||||
echo json_encode(['success' => false, 'message' => 'Errore nel recupero delle MacroMatrice: ' . $e->getMessage()]);
|
|
||||||
exit(1);
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,47 @@
|
|||||||
|
<?php
|
||||||
|
require_once dirname(__DIR__, 2) . '/vendor/autoload.php';
|
||||||
|
require_once __DIR__ . '/class/db-functions.php';
|
||||||
|
include dirname(__DIR__) . '/../extra/auth.php';
|
||||||
|
if (!Auth::check()) { http_response_code(401); echo json_encode(['error' => 'Unauthorized']); exit; }
|
||||||
|
|
||||||
|
require_once __DIR__ . '/class/VisualLimsApiClient.class.php';
|
||||||
|
|
||||||
|
header('Content-Type: application/json');
|
||||||
|
ini_set('display_errors', '0');
|
||||||
|
error_reporting(E_ALL);
|
||||||
|
|
||||||
|
try {
|
||||||
|
// Cache file (1 hour TTL)
|
||||||
|
$cacheFile = __DIR__ . '/cache/matrici.json';
|
||||||
|
if (file_exists($cacheFile) && (time() - filemtime($cacheFile) < 3600)) {
|
||||||
|
readfile($cacheFile);
|
||||||
|
exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
$api = VisualLimsApiClient::getInstance();
|
||||||
|
$data = $api->get('Matrice');
|
||||||
|
|
||||||
|
$matrici = [];
|
||||||
|
if (isset($data['value']) && is_array($data['value'])) {
|
||||||
|
foreach ($data['value'] as $item) {
|
||||||
|
$nome = $item['NomeMatrice'] ?? '';
|
||||||
|
if ($nome !== '' && substr($nome, 0, 1) !== '*') {
|
||||||
|
$matrici[] = [
|
||||||
|
'IdMatrice' => $item['IdMatrice'],
|
||||||
|
'NomeMatrice' => $nome,
|
||||||
|
'MacroMatrice' => $item['MacroMatrice'] ?? null,
|
||||||
|
];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
usort($matrici, fn($a, $b) => strcasecmp($a['NomeMatrice'], $b['NomeMatrice']));
|
||||||
|
}
|
||||||
|
|
||||||
|
$json = json_encode(['success' => true, 'value' => $matrici]);
|
||||||
|
if (!is_dir(__DIR__ . '/cache')) mkdir(__DIR__ . '/cache', 0777, true);
|
||||||
|
file_put_contents($cacheFile, $json);
|
||||||
|
|
||||||
|
echo $json;
|
||||||
|
} catch (Exception $e) {
|
||||||
|
http_response_code(500);
|
||||||
|
echo json_encode(['success' => false, 'message' => $e->getMessage()]);
|
||||||
|
}
|
||||||
@@ -0,0 +1,256 @@
|
|||||||
|
<?php
|
||||||
|
require_once dirname(__DIR__, 2) . '/vendor/autoload.php';
|
||||||
|
require_once dirname(__FILE__) . '/class/VisualLimsApiClient.class.php';
|
||||||
|
|
||||||
|
header('Content-Type: application/json; charset=utf-8');
|
||||||
|
|
||||||
|
ini_set('display_errors', '0');
|
||||||
|
error_reporting(E_ALL);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Uso:
|
||||||
|
* rapporto_full_by_codice.php?codice=2621521
|
||||||
|
* rapporto_full_by_codice.php?codice=2621521&download=1
|
||||||
|
*/
|
||||||
|
|
||||||
|
try {
|
||||||
|
$api = VisualLimsApiClient::getInstance();
|
||||||
|
|
||||||
|
$codiceRapporto = trim($_GET['codice'] ?? '');
|
||||||
|
$downloadPdf = isset($_GET['download']) && $_GET['download'] == '1';
|
||||||
|
|
||||||
|
if ($codiceRapporto === '') {
|
||||||
|
throw new Exception("Parametro codice mancante. Usa ?codice=2621521");
|
||||||
|
}
|
||||||
|
|
||||||
|
$debugDir = __DIR__ . '/logs/lims_rapporti/';
|
||||||
|
$pdfDir = __DIR__ . '/pdf/rapporti/';
|
||||||
|
|
||||||
|
if (!is_dir($debugDir)) mkdir($debugDir, 0755, true);
|
||||||
|
if (!is_dir($pdfDir)) mkdir($pdfDir, 0755, true);
|
||||||
|
|
||||||
|
$codiceRapportoSafe = str_replace("'", "''", $codiceRapporto);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* STEP 1 - Search IdRapporto
|
||||||
|
*/
|
||||||
|
$searchParams = [
|
||||||
|
'$filter' => "CodiceRapporto eq '{$codiceRapportoSafe}'",
|
||||||
|
'$select' => 'IdRapporto,CodiceRapporto,Data,Versione,Firmato,DataStampa',
|
||||||
|
'$top' => 1
|
||||||
|
];
|
||||||
|
|
||||||
|
$searchEndpoint = "Rapporto?" . http_build_query($searchParams);
|
||||||
|
$searchData = $api->get($searchEndpoint);
|
||||||
|
|
||||||
|
$rapporti = $searchData['value'] ?? [];
|
||||||
|
|
||||||
|
if (empty($rapporti)) {
|
||||||
|
echo json_encode([
|
||||||
|
'success' => false,
|
||||||
|
'message' => 'Nessun rapporto trovato per questo CodiceRapporto.',
|
||||||
|
'codice_rapporto' => $codiceRapporto
|
||||||
|
], JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE);
|
||||||
|
exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
$rapportoBase = $rapporti[0];
|
||||||
|
$idRapporto = intval($rapportoBase['IdRapporto'] ?? 0);
|
||||||
|
|
||||||
|
if (!$idRapporto) {
|
||||||
|
throw new Exception("IdRapporto non trovato nella risposta.");
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* STEP 2 - Dettaglio Rapporto
|
||||||
|
*/
|
||||||
|
$clienteExpandError = null;
|
||||||
|
$detailEndpoint = "Rapporto({$idRapporto})?\$expand=Cliente,RapportiFiles,CampioniDatiRapporto";
|
||||||
|
|
||||||
|
try {
|
||||||
|
$detailData = $api->get($detailEndpoint);
|
||||||
|
} catch (Exception $e) {
|
||||||
|
$clienteExpandError = $e->getMessage();
|
||||||
|
$detailEndpoint = "Rapporto({$idRapporto})?\$expand=RapportiFiles,CampioniDatiRapporto";
|
||||||
|
$detailData = $api->get($detailEndpoint);
|
||||||
|
}
|
||||||
|
|
||||||
|
file_put_contents(
|
||||||
|
$debugDir . "rapporto_{$codiceRapportoSafe}_light.json",
|
||||||
|
json_encode($detailData, JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE)
|
||||||
|
);
|
||||||
|
|
||||||
|
$cliente = $detailData['Cliente'] ?? null;
|
||||||
|
$rapportiFiles = $detailData['RapportiFiles'] ?? [];
|
||||||
|
$campioniDatiRapporto = $detailData['CampioniDatiRapporto'] ?? [];
|
||||||
|
|
||||||
|
// ====================== CLIENTE ======================
|
||||||
|
$clienteInfo = [
|
||||||
|
'found' => is_array($cliente),
|
||||||
|
'id_cliente' => $cliente['IdCliente'] ?? $cliente['Id'] ?? null,
|
||||||
|
'codice_cliente' => $cliente['CodiceCliente'] ?? $cliente['Codice'] ?? null,
|
||||||
|
'nome_cliente' => $cliente['Nominativo'] ?? $cliente['RagioneSociale'] ?? $cliente['Nome'] ?? $cliente['Descrizione'] ?? null,
|
||||||
|
'partita_iva' => $cliente['PartitaIva'] ?? null,
|
||||||
|
'codice_fiscale' => $cliente['CodiceFiscale'] ?? null
|
||||||
|
];
|
||||||
|
|
||||||
|
// ====================== FILE PDF ======================
|
||||||
|
$selectedRapportoFile = null;
|
||||||
|
$idRapportoFile = null;
|
||||||
|
$pdfFileName = $codiceRapporto . '.pdf';
|
||||||
|
$pdfFullPath = $pdfDir . $pdfFileName;
|
||||||
|
|
||||||
|
if (is_array($rapportiFiles) && count($rapportiFiles) > 0) {
|
||||||
|
foreach ($rapportiFiles as $file) {
|
||||||
|
$fileName = $file['FileName'] ?? '';
|
||||||
|
$tipoRapporto = $file['TipoRapporto'] ?? '';
|
||||||
|
$categoria = $file['Categoria'] ?? '';
|
||||||
|
|
||||||
|
if (
|
||||||
|
stripos($fileName, '.pdf') !== false ||
|
||||||
|
stripos($tipoRapporto, 'Rapporto') !== false ||
|
||||||
|
stripos($categoria, 'Rapporti') !== false
|
||||||
|
) {
|
||||||
|
$selectedRapportoFile = $file;
|
||||||
|
$idRapportoFile = $file['IdRapportoFile'] ?? null;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!$selectedRapportoFile && count($rapportiFiles) > 0) {
|
||||||
|
$selectedRapportoFile = $rapportiFiles[0];
|
||||||
|
$idRapportoFile = $rapportiFiles[0]['IdRapportoFile'] ?? null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// ====================== DOWNLOAD PDF - DEBUG AVANZATO ======================
|
||||||
|
$pdfDownloaded = false;
|
||||||
|
$pdfError = null;
|
||||||
|
$endpointUsato = null;
|
||||||
|
|
||||||
|
if ($downloadPdf && $idRapportoFile) {
|
||||||
|
|
||||||
|
$tentativi = [
|
||||||
|
"RapportoFile({$idRapportoFile})/\$value",
|
||||||
|
"RapportoFile({$idRapportoFile})/Contenuto/\$value",
|
||||||
|
"RapportoFile({$idRapportoFile})/File/\$value",
|
||||||
|
"Rapporto({$idRapporto})/RapportiFiles({$idRapportoFile})/\$value",
|
||||||
|
"Rapporto({$idRapporto})/RapportoFile/\$value",
|
||||||
|
"GetRapportoFile?IdRapportoFile={$idRapportoFile}",
|
||||||
|
"RapportoFile/Download?Id={$idRapportoFile}",
|
||||||
|
"Rapporto/DownloadFile?IdRapporto={$idRapporto}"
|
||||||
|
];
|
||||||
|
|
||||||
|
foreach ($tentativi as $ep) {
|
||||||
|
try {
|
||||||
|
$pdfContent = $api->getRaw($ep);
|
||||||
|
|
||||||
|
if (strlen($pdfContent) > 2000) { // PDF decente deve essere più grande
|
||||||
|
file_put_contents($pdfFullPath, $pdfContent);
|
||||||
|
$pdfDownloaded = true;
|
||||||
|
$endpointUsato = $ep;
|
||||||
|
$pdfError = "SUCCESSO con: " . $ep;
|
||||||
|
break;
|
||||||
|
} else {
|
||||||
|
$pdfError = "Contenuto troppo piccolo con: " . $ep;
|
||||||
|
}
|
||||||
|
} catch (Exception $e) {
|
||||||
|
$pdfError = "Fallito con " . $ep . " → " . $e->getMessage();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// ====================== CAMPIONI + RATING (ripristinato dal tuo originale) ======================
|
||||||
|
$campioniSintesi = [];
|
||||||
|
$ratingFinale = null;
|
||||||
|
$ratingSource = null;
|
||||||
|
$hasIrregolare = false;
|
||||||
|
$hasPositiveResults = false;
|
||||||
|
|
||||||
|
if (is_array($campioniDatiRapporto)) {
|
||||||
|
foreach ($campioniDatiRapporto as $campione) {
|
||||||
|
$giudizioRapporto = $campione['GiudizioRapporto'] ?? null;
|
||||||
|
$giudizioCertificato = $campione['GiudizioCertificato'] ?? null;
|
||||||
|
$esitoGiudizioLMR = $campione['EsitoGiudizioLMR'] ?? null;
|
||||||
|
$esitoGiudizioImpiego = $campione['EsitoGiudizioImpiego'] ?? null;
|
||||||
|
$risultatiIrregolari = ($campione['RisultatiIrregolari'] ?? false) === true;
|
||||||
|
$risultatiPositivi = ($campione['RisultatiPositivi'] ?? false) === true;
|
||||||
|
|
||||||
|
$campioniSintesi[] = [
|
||||||
|
'id_campione_dati_rapporto' => $campione['IdCampioneDatiRapporto'] ?? null,
|
||||||
|
'codice_campione' => $campione['CodiceCampione'] ?? null,
|
||||||
|
'stato_campione' => $campione['StatoCampione'] ?? null,
|
||||||
|
'stato_campione_web' => $campione['StatoCampioneWeb'] ?? null,
|
||||||
|
'matrice' => $campione['Matrice'] ?? null,
|
||||||
|
'macro_matrice' => $campione['MacroMatrice'] ?? null,
|
||||||
|
'giudizio_rapporto' => $giudizioRapporto,
|
||||||
|
'giudizio_certificato' => $giudizioCertificato,
|
||||||
|
'esito_giudizio_lmr' => $esitoGiudizioLMR,
|
||||||
|
'esito_giudizio_impiego' => $esitoGiudizioImpiego,
|
||||||
|
'risultati_positivi' => $risultatiPositivi,
|
||||||
|
'risultati_irregolari' => $risultatiIrregolari
|
||||||
|
];
|
||||||
|
|
||||||
|
if (!$ratingFinale && !empty($giudizioRapporto)) {
|
||||||
|
$ratingFinale = $giudizioRapporto;
|
||||||
|
$ratingSource = 'CampioniDatiRapporto.GiudizioRapporto';
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!$ratingFinale && !empty($giudizioCertificato)) {
|
||||||
|
$ratingFinale = $giudizioCertificato;
|
||||||
|
$ratingSource = 'CampioniDatiRapporto.GiudizioCertificato';
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($risultatiIrregolari) $hasIrregolare = true;
|
||||||
|
if ($risultatiPositivi) $hasPositiveResults = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Fallback rating
|
||||||
|
if (!$ratingFinale) {
|
||||||
|
if ($hasIrregolare) {
|
||||||
|
$ratingFinale = 'Irregolare';
|
||||||
|
$ratingSource = 'RisultatiIrregolari';
|
||||||
|
} elseif ($hasPositiveResults || count($campioniSintesi) > 0) {
|
||||||
|
$ratingFinale = 'Regolare';
|
||||||
|
$ratingSource = 'fallback';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// ====================== RISPOSTA FINALE ======================
|
||||||
|
echo json_encode([
|
||||||
|
'success' => true,
|
||||||
|
|
||||||
|
'codice_rapporto' => $codiceRapporto,
|
||||||
|
'id_rapporto' => $idRapporto,
|
||||||
|
|
||||||
|
'search_endpoint' => $searchEndpoint,
|
||||||
|
'detail_endpoint' => $detailEndpoint,
|
||||||
|
'cliente_expand_error' => $clienteExpandError,
|
||||||
|
|
||||||
|
'rapporto_base' => $rapportoBase,
|
||||||
|
'cliente' => $clienteInfo,
|
||||||
|
|
||||||
|
'rating_finale' => $ratingFinale,
|
||||||
|
'rating_source' => $ratingSource,
|
||||||
|
|
||||||
|
'rapporti_files_count' => count($rapportiFiles),
|
||||||
|
'selected_rapporto_file' => $selectedRapportoFile,
|
||||||
|
|
||||||
|
'pdf_file_name' => $pdfFileName,
|
||||||
|
'pdf_path' => $pdfDownloaded ? $pdfFullPath : null,
|
||||||
|
'pdf_downloaded' => $pdfDownloaded,
|
||||||
|
'pdf_error' => $pdfError,
|
||||||
|
|
||||||
|
'campioni_count' => count($campioniSintesi),
|
||||||
|
'campioni_sintesi' => $campioniSintesi,
|
||||||
|
|
||||||
|
'download_requested' => $downloadPdf
|
||||||
|
], JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE);
|
||||||
|
} catch (Exception $e) {
|
||||||
|
http_response_code(500);
|
||||||
|
echo json_encode([
|
||||||
|
'success' => false,
|
||||||
|
'error' => $e->getMessage()
|
||||||
|
], JSON_PRETTY_PRINT | JSON_UNESCAPED_UNICODE);
|
||||||
|
}
|
||||||
@@ -0,0 +1,118 @@
|
|||||||
|
<?php
|
||||||
|
require_once dirname(__DIR__, 2) . '/vendor/autoload.php';
|
||||||
|
require_once dirname(__FILE__) . '/class/VisualLimsApiClient.class.php';
|
||||||
|
|
||||||
|
header('Content-Type: application/json; charset=utf-8');
|
||||||
|
|
||||||
|
ini_set('display_errors', '0');
|
||||||
|
error_reporting(E_ALL);
|
||||||
|
|
||||||
|
try {
|
||||||
|
$api = VisualLimsApiClient::getInstance();
|
||||||
|
|
||||||
|
$commessa = trim($_GET['commessa'] ?? '');
|
||||||
|
|
||||||
|
if ($commessa === '') {
|
||||||
|
throw new Exception("Parametro commessa mancante");
|
||||||
|
}
|
||||||
|
|
||||||
|
$debugDir = __DIR__ . '/logs/lims_rapporto/';
|
||||||
|
if (!is_dir($debugDir)) {
|
||||||
|
mkdir($debugDir, 0755, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* STEP 1
|
||||||
|
* Provo a cercare il rapporto con filtri leggeri.
|
||||||
|
* NIENTE expand pesanti.
|
||||||
|
*/
|
||||||
|
$attempts = [];
|
||||||
|
|
||||||
|
$filters = [
|
||||||
|
'CodiceCommessa' => "CodiceCommessa eq '{$commessa}'",
|
||||||
|
'Commessa_CodiceCommessa' => "Commessa/CodiceCommessa eq '{$commessa}'",
|
||||||
|
'Commessa_IdCommessa' => is_numeric($commessa) ? "Commessa/IdCommessa eq {$commessa}" : null,
|
||||||
|
'Codice' => "Codice eq '{$commessa}'"
|
||||||
|
];
|
||||||
|
|
||||||
|
foreach ($filters as $label => $filter) {
|
||||||
|
if (!$filter) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
$options = [
|
||||||
|
'$filter' => $filter,
|
||||||
|
'$top' => 10
|
||||||
|
];
|
||||||
|
|
||||||
|
$data = $api->get('Rapporto', $options);
|
||||||
|
|
||||||
|
$attempts[$label] = [
|
||||||
|
'success' => true,
|
||||||
|
'filter' => $filter,
|
||||||
|
'records' => isset($data['value']) && is_array($data['value']) ? count($data['value']) : null,
|
||||||
|
'data' => $data
|
||||||
|
];
|
||||||
|
|
||||||
|
file_put_contents(
|
||||||
|
$debugDir . "rapporto_{$commessa}_{$label}.json",
|
||||||
|
json_encode($data, JSON_PRETTY_PRINT | JSON_UNESCAPED_UNICODE)
|
||||||
|
);
|
||||||
|
} catch (Exception $e) {
|
||||||
|
$attempts[$label] = [
|
||||||
|
'success' => false,
|
||||||
|
'filter' => $filter,
|
||||||
|
'error' => $e->getMessage()
|
||||||
|
];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* STEP 2
|
||||||
|
* Prendo solo eventuali rapporti trovati.
|
||||||
|
*/
|
||||||
|
$rapportiFound = [];
|
||||||
|
|
||||||
|
foreach ($attempts as $label => $attempt) {
|
||||||
|
if (!$attempt['success']) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
$items = $attempt['data']['value'] ?? [];
|
||||||
|
|
||||||
|
if (!is_array($items)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach ($items as $item) {
|
||||||
|
$rapportiFound[] = [
|
||||||
|
'matched_by' => $label,
|
||||||
|
'rapporto' => $item
|
||||||
|
];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
echo json_encode([
|
||||||
|
'success' => true,
|
||||||
|
'input_commessa' => $commessa,
|
||||||
|
'message' => 'Ricerca leggera su Rapporto completata. Se trovi un rapporto, poi recuperiamo RapportiFiles solo per quello.',
|
||||||
|
'rapporti_found_count' => count($rapportiFound),
|
||||||
|
'rapporti_found' => $rapportiFound,
|
||||||
|
'attempts_summary' => array_map(function ($a) {
|
||||||
|
return [
|
||||||
|
'success' => $a['success'],
|
||||||
|
'filter' => $a['filter'],
|
||||||
|
'records' => $a['records'] ?? null,
|
||||||
|
'error' => $a['error'] ?? null
|
||||||
|
];
|
||||||
|
}, $attempts)
|
||||||
|
], JSON_PRETTY_PRINT | JSON_UNESCAPED_UNICODE);
|
||||||
|
} catch (Exception $e) {
|
||||||
|
http_response_code(500);
|
||||||
|
|
||||||
|
echo json_encode([
|
||||||
|
'success' => false,
|
||||||
|
'error' => $e->getMessage()
|
||||||
|
], JSON_PRETTY_PRINT | JSON_UNESCAPED_UNICODE);
|
||||||
|
}
|
||||||
@@ -2,25 +2,140 @@
|
|||||||
require_once dirname(__DIR__, 2) . '/vendor/autoload.php';
|
require_once dirname(__DIR__, 2) . '/vendor/autoload.php';
|
||||||
require_once dirname(__FILE__) . '/class/VisualLimsApiClient.class.php';
|
require_once dirname(__FILE__) . '/class/VisualLimsApiClient.class.php';
|
||||||
|
|
||||||
header('Content-Type: application/json');
|
header('Content-Type: application/json; charset=utf-8');
|
||||||
|
|
||||||
ini_set('display_errors', '0');
|
ini_set('display_errors', '0');
|
||||||
error_reporting(E_ALL);
|
error_reporting(E_ALL);
|
||||||
|
|
||||||
try {
|
try {
|
||||||
$api = VisualLimsApiClient::getInstance();
|
$api = VisualLimsApiClient::getInstance();
|
||||||
$rapporto_id = 533329;
|
|
||||||
|
|
||||||
// Costruzione manuale dell'endpoint con espansione annidata
|
// Esempi:
|
||||||
$endpoint = "Rapporto($rapporto_id)?\$expand=CampioniDatiRapporto(\$expand=AnalisiDatiRapporto,CustomFieldsDatiRapporto)";
|
// rapporto_by_codice_expand_step.php?codice=2541111&step=base
|
||||||
|
// rapporto_by_codice_expand_step.php?codice=2541111&step=files
|
||||||
|
// rapporto_by_codice_expand_step.php?codice=2541111&step=campioni
|
||||||
|
// rapporto_by_codice_expand_step.php?codice=2541111&step=files_campioni
|
||||||
|
|
||||||
// Non passiamo options, già incluso nell'endpoint
|
$codiceRapporto = trim($_GET['codice'] ?? '');
|
||||||
$data = $api->get($endpoint);
|
$step = trim($_GET['step'] ?? 'base');
|
||||||
|
|
||||||
file_put_contents(__DIR__ . '/rapporto_expanded.json', json_encode($data, JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES));
|
if ($codiceRapporto === '') {
|
||||||
echo json_encode($data);
|
throw new Exception("Parametro codice mancante. Usa ?codice=2541111");
|
||||||
|
}
|
||||||
|
|
||||||
|
$allowedSteps = [
|
||||||
|
'base' => '',
|
||||||
|
'files' => 'RapportiFiles',
|
||||||
|
'allegati' => 'RapportiAllegati',
|
||||||
|
'campioni' => 'CampioniDatiRapporto',
|
||||||
|
'files_campioni' => 'RapportiFiles,CampioniDatiRapporto'
|
||||||
|
];
|
||||||
|
|
||||||
|
if (!array_key_exists($step, $allowedSteps)) {
|
||||||
|
throw new Exception("Step non valido. Usa: " . implode(', ', array_keys($allowedSteps)));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Escape OData per eventuali apostrofi
|
||||||
|
$codiceRapportoSafe = str_replace("'", "''", $codiceRapporto);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* STEP 1 - Trova IdRapporto partendo da CodiceRapporto.
|
||||||
|
* Query leggera, con $select e $top=1.
|
||||||
|
*/
|
||||||
|
$searchParams = [
|
||||||
|
'$filter' => "CodiceRapporto eq '{$codiceRapportoSafe}'",
|
||||||
|
'$select' => 'IdRapporto,CodiceRapporto,Data,Versione,Firmato,DataStampa',
|
||||||
|
'$top' => 1
|
||||||
|
];
|
||||||
|
|
||||||
|
$searchEndpoint = "Rapporto?" . http_build_query($searchParams);
|
||||||
|
|
||||||
|
$searchData = $api->get($searchEndpoint);
|
||||||
|
|
||||||
|
$items = $searchData['value'] ?? [];
|
||||||
|
|
||||||
|
if (!is_array($items) || count($items) === 0) {
|
||||||
|
echo json_encode([
|
||||||
|
'success' => false,
|
||||||
|
'message' => 'Nessun rapporto trovato per questo CodiceRapporto.',
|
||||||
|
'codice_rapporto' => $codiceRapporto,
|
||||||
|
'search_endpoint' => $searchEndpoint,
|
||||||
|
'search_data' => $searchData
|
||||||
|
], JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE);
|
||||||
|
exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
$rapportoBase = $items[0];
|
||||||
|
$rapportoId = intval($rapportoBase['IdRapporto'] ?? 0);
|
||||||
|
|
||||||
|
if (!$rapportoId) {
|
||||||
|
throw new Exception("IdRapporto non trovato nella risposta.");
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* STEP 2 - Se step=base, restituisco solo la ricerca base.
|
||||||
|
*/
|
||||||
|
if ($step === 'base') {
|
||||||
|
echo json_encode([
|
||||||
|
'success' => true,
|
||||||
|
'codice_rapporto' => $codiceRapporto,
|
||||||
|
'id_rapporto' => $rapportoId,
|
||||||
|
'step' => $step,
|
||||||
|
'search_endpoint' => $searchEndpoint,
|
||||||
|
'rapporto_base' => $rapportoBase
|
||||||
|
], JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE);
|
||||||
|
exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* STEP 3 - Espande SOLO il rapporto trovato.
|
||||||
|
*/
|
||||||
|
$expandValue = $allowedSteps[$step];
|
||||||
|
|
||||||
|
$detailParams = [
|
||||||
|
'$expand' => $expandValue
|
||||||
|
];
|
||||||
|
|
||||||
|
$detailEndpoint = "Rapporto({$rapportoId})?" . http_build_query($detailParams);
|
||||||
|
|
||||||
|
file_put_contents(
|
||||||
|
__DIR__ . '/last_rapporto_by_codice_expand_endpoint.txt',
|
||||||
|
'[' . date('Y-m-d H:i:s') . '] SEARCH: ' . $searchEndpoint . PHP_EOL .
|
||||||
|
'[' . date('Y-m-d H:i:s') . '] DETAIL: ' . $detailEndpoint . PHP_EOL,
|
||||||
|
FILE_APPEND
|
||||||
|
);
|
||||||
|
|
||||||
|
$detailData = $api->get($detailEndpoint);
|
||||||
|
|
||||||
|
file_put_contents(
|
||||||
|
__DIR__ . "/rapporto_codice_{$codiceRapportoSafe}_{$step}.json",
|
||||||
|
json_encode([
|
||||||
|
'search' => $searchData,
|
||||||
|
'detail' => $detailData
|
||||||
|
], JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE)
|
||||||
|
);
|
||||||
|
|
||||||
|
echo json_encode([
|
||||||
|
'success' => true,
|
||||||
|
'codice_rapporto' => $codiceRapporto,
|
||||||
|
'id_rapporto' => $rapportoId,
|
||||||
|
'step' => $step,
|
||||||
|
'search_endpoint' => $searchEndpoint,
|
||||||
|
'detail_endpoint' => $detailEndpoint,
|
||||||
|
'rapporto_base' => $rapportoBase,
|
||||||
|
'data' => $detailData
|
||||||
|
], JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE);
|
||||||
} catch (Exception $e) {
|
} catch (Exception $e) {
|
||||||
file_put_contents(__DIR__ . '/error_log.txt', date('Y-m-d H:i:s') . ' - ' . $e->getMessage() . PHP_EOL, FILE_APPEND);
|
file_put_contents(
|
||||||
|
__DIR__ . '/error_log.txt',
|
||||||
|
date('Y-m-d H:i:s') . ' - ' . $e->getMessage() . PHP_EOL,
|
||||||
|
FILE_APPEND
|
||||||
|
);
|
||||||
|
|
||||||
http_response_code(500);
|
http_response_code(500);
|
||||||
echo json_encode(['error' => $e->getMessage()]);
|
|
||||||
|
echo json_encode([
|
||||||
|
'success' => false,
|
||||||
|
'error' => $e->getMessage()
|
||||||
|
], JSON_PRETTY_PRINT | JSON_UNESCAPED_UNICODE);
|
||||||
}
|
}
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
@@ -10,65 +10,125 @@
|
|||||||
<title>Template Buttons - <?= htmlspecialchars($titlewebsite, ENT_QUOTES, 'UTF-8'); ?></title>
|
<title>Template Buttons - <?= htmlspecialchars($titlewebsite, ENT_QUOTES, 'UTF-8'); ?></title>
|
||||||
|
|
||||||
<style>
|
<style>
|
||||||
/* Layout flessibile per gestire dimensioni diverse */
|
/* Main buttons container */
|
||||||
#templateButtons {
|
.template-buttons {
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-wrap: wrap;
|
flex-wrap: wrap;
|
||||||
gap: 10px;
|
gap: 12px;
|
||||||
justify-content: flex-start;
|
justify-content: flex-start;
|
||||||
/* Allinea a sinistra */
|
padding: 10px 0;
|
||||||
padding: 20px;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Definizione delle dimensioni */
|
/* Button sizes */
|
||||||
/* Definizione delle dimensioni */
|
|
||||||
.btn-small {
|
.btn-small {
|
||||||
font-size: 12px;
|
font-size: 12px;
|
||||||
padding: 6px 12px;
|
padding: 6px 12px;
|
||||||
min-width: 100px;
|
min-width: 100px;
|
||||||
min-height: 30px;
|
min-height: 36px;
|
||||||
display: flex;
|
display: inline-flex;
|
||||||
/* Aggiunto */
|
|
||||||
justify-content: center;
|
justify-content: center;
|
||||||
/* Aggiunto */
|
|
||||||
align-items: center;
|
align-items: center;
|
||||||
/* Aggiunto */
|
gap: 8px;
|
||||||
|
border-radius: 8px;
|
||||||
|
text-align: center;
|
||||||
}
|
}
|
||||||
|
|
||||||
.btn-medium {
|
.btn-medium {
|
||||||
font-size: 16px;
|
font-size: 16px;
|
||||||
padding: 10px 20px;
|
padding: 10px 20px;
|
||||||
min-width: 130px;
|
min-width: 140px;
|
||||||
min-height: 45px;
|
min-height: 48px;
|
||||||
display: flex;
|
display: inline-flex;
|
||||||
/* Aggiunto */
|
|
||||||
justify-content: center;
|
justify-content: center;
|
||||||
/* Aggiunto */
|
|
||||||
align-items: center;
|
align-items: center;
|
||||||
/* Aggiunto */
|
gap: 8px;
|
||||||
|
border-radius: 10px;
|
||||||
|
text-align: center;
|
||||||
}
|
}
|
||||||
|
|
||||||
.btn-large {
|
.btn-large {
|
||||||
font-size: 20px;
|
font-size: 20px;
|
||||||
padding: 14px 28px;
|
padding: 14px 28px;
|
||||||
min-width: 180px;
|
min-width: 190px;
|
||||||
min-height: 60px;
|
min-height: 64px;
|
||||||
display: flex;
|
display: inline-flex;
|
||||||
/* Aggiunto */
|
|
||||||
justify-content: center;
|
justify-content: center;
|
||||||
/* Aggiunto */
|
|
||||||
align-items: center;
|
align-items: center;
|
||||||
/* Aggiunto */
|
gap: 10px;
|
||||||
|
border-radius: 12px;
|
||||||
|
text-align: center;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Stile della barra di ricerca */
|
.template-icon {
|
||||||
|
font-size: 18px;
|
||||||
|
line-height: 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
.btn-large .template-icon {
|
||||||
|
font-size: 22px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.btn-small .template-icon {
|
||||||
|
font-size: 15px;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Search box */
|
||||||
#searchInput {
|
#searchInput {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
padding: 10px;
|
padding: 10px 14px;
|
||||||
font-size: 16px;
|
font-size: 16px;
|
||||||
margin-bottom: 15px;
|
margin-bottom: 18px;
|
||||||
border: 1px solid #ccc;
|
border: 1px solid #d9d9d9;
|
||||||
border-radius: 5px;
|
border-radius: 8px;
|
||||||
|
outline: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
#searchInput:focus {
|
||||||
|
border-color: #0d6efd;
|
||||||
|
box-shadow: 0 0 0 0.15rem rgba(13, 110, 253, 0.15);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Tabs */
|
||||||
|
.custom-tabs {
|
||||||
|
border-bottom: 1px solid #e5e5e5;
|
||||||
|
margin-bottom: 20px;
|
||||||
|
gap: 6px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.custom-tabs .nav-link {
|
||||||
|
border: none;
|
||||||
|
border-radius: 10px 10px 0 0;
|
||||||
|
color: #555;
|
||||||
|
font-weight: 500;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 8px;
|
||||||
|
padding: 10px 18px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.custom-tabs .nav-link:hover {
|
||||||
|
background: #f8f9fa;
|
||||||
|
color: #0d6efd;
|
||||||
|
}
|
||||||
|
|
||||||
|
.custom-tabs .nav-link.active {
|
||||||
|
background: #0d6efd;
|
||||||
|
color: #fff;
|
||||||
|
}
|
||||||
|
|
||||||
|
.tab-pane {
|
||||||
|
min-height: 140px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.empty-message {
|
||||||
|
color: #6c757d;
|
||||||
|
font-style: italic;
|
||||||
|
padding: 10px 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.loading-message {
|
||||||
|
color: #6c757d;
|
||||||
|
padding: 10px 0;
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
</head>
|
</head>
|
||||||
@@ -87,14 +147,54 @@
|
|||||||
<h6 class="mb-0">Active Templates</h6>
|
<h6 class="mb-0">Active Templates</h6>
|
||||||
</div>
|
</div>
|
||||||
<div class="card-body">
|
<div class="card-body">
|
||||||
<!-- Barra di ricerca -->
|
|
||||||
<input type="text" id="searchInput" placeholder="Search template...">
|
<input type="text" id="searchInput" placeholder="Search template...">
|
||||||
<div id="templateButtons"></div>
|
|
||||||
|
<ul class="nav nav-tabs custom-tabs" id="templateTabs" role="tablist">
|
||||||
|
<li class="nav-item" role="presentation">
|
||||||
|
<button class="nav-link active" id="xls-tab" data-bs-toggle="tab" data-bs-target="#xls-pane" type="button" role="tab" aria-controls="xls-pane" aria-selected="true">
|
||||||
|
<i class="bx bx-spreadsheet template-icon"></i>
|
||||||
|
XLS
|
||||||
|
</button>
|
||||||
|
</li>
|
||||||
|
<li class="nav-item" role="presentation">
|
||||||
|
<button class="nav-link" id="api-tab" data-bs-toggle="tab" data-bs-target="#api-pane" type="button" role="tab" aria-controls="api-pane" aria-selected="false">
|
||||||
|
<i class="bx bx-transfer-alt template-icon"></i>
|
||||||
|
JSON/API
|
||||||
|
</button>
|
||||||
|
</li>
|
||||||
|
<li class="nav-item" role="presentation">
|
||||||
|
<button class="nav-link" id="pdf-tab" data-bs-toggle="tab" data-bs-target="#pdf-pane" type="button" role="tab" aria-controls="pdf-pane" aria-selected="false">
|
||||||
|
<i class="bx bx-file-pdf template-icon"></i>
|
||||||
|
PDF
|
||||||
|
</button>
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
|
||||||
|
<div class="tab-content" id="templateTabsContent">
|
||||||
|
<div class="tab-pane fade show active" id="xls-pane" role="tabpanel" aria-labelledby="xls-tab">
|
||||||
|
<div id="templateButtonsXLS" class="template-buttons">
|
||||||
|
<div class="loading-message">Loading XLS templates...</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="tab-pane fade" id="api-pane" role="tabpanel" aria-labelledby="api-tab">
|
||||||
|
<div id="templateButtonsAPI" class="template-buttons">
|
||||||
|
<div class="loading-message">Loading API templates...</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="tab-pane fade" id="pdf-pane" role="tabpanel" aria-labelledby="pdf-tab">
|
||||||
|
<div id="templateButtonsPDF" class="template-buttons">
|
||||||
|
<div class="loading-message">Loading PDF templates...</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="overlay toggle-icon"></div>
|
<div class="overlay toggle-icon"></div>
|
||||||
<a href="javaScript:;" class="back-to-top"><i class='bx bxs-up-arrow-alt'></i></a>
|
<a href="javaScript:;" class="back-to-top"><i class='bx bxs-up-arrow-alt'></i></a>
|
||||||
<?php include('include/footer.php'); ?>
|
<?php include('include/footer.php'); ?>
|
||||||
@@ -104,47 +204,138 @@
|
|||||||
|
|
||||||
<script>
|
<script>
|
||||||
document.addEventListener("DOMContentLoaded", function() {
|
document.addEventListener("DOMContentLoaded", function() {
|
||||||
|
const allTemplates = [];
|
||||||
|
|
||||||
|
const containers = {
|
||||||
|
XLS: document.getElementById("templateButtonsXLS"),
|
||||||
|
API: document.getElementById("templateButtonsAPI"),
|
||||||
|
PDF: document.getElementById("templateButtonsPDF")
|
||||||
|
};
|
||||||
|
|
||||||
|
function getSizeClass(buttonSize) {
|
||||||
|
if (buttonSize === "small") return "btn-small";
|
||||||
|
if (buttonSize === "large") return "btn-large";
|
||||||
|
return "btn-medium";
|
||||||
|
}
|
||||||
|
|
||||||
|
function getTemplateIcon(sourceType) {
|
||||||
|
switch ((sourceType || '').toUpperCase()) {
|
||||||
|
case 'XLS':
|
||||||
|
return 'bx bx-spreadsheet';
|
||||||
|
case 'API':
|
||||||
|
return 'bx bx-transfer-alt';
|
||||||
|
case 'PDF':
|
||||||
|
return 'bx bx-file-pdf';
|
||||||
|
default:
|
||||||
|
return 'bx bx-file';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function createButton(template) {
|
||||||
|
const sizeClass = getSizeClass(template.button_size);
|
||||||
|
const sourceType = (template.source_type || '').toUpperCase();
|
||||||
|
const iconClass = getTemplateIcon(sourceType);
|
||||||
|
|
||||||
|
const btn = document.createElement("a");
|
||||||
|
btn.href = `import_xls2.php?id=${template.id}`;
|
||||||
|
btn.className = `btn ${sizeClass}`;
|
||||||
|
btn.style.backgroundColor = template.button_bg_color || '#0d6efd';
|
||||||
|
btn.style.color = template.button_text_color || '#ffffff';
|
||||||
|
btn.setAttribute("data-label", (template.button_label || '').toLowerCase());
|
||||||
|
btn.setAttribute("data-source-type", sourceType);
|
||||||
|
|
||||||
|
btn.innerHTML = `
|
||||||
|
<i class="${iconClass} template-icon"></i>
|
||||||
|
<span>${escapeHtml(template.button_label || 'Unnamed')}</span>
|
||||||
|
`;
|
||||||
|
|
||||||
|
return btn;
|
||||||
|
}
|
||||||
|
|
||||||
|
function escapeHtml(text) {
|
||||||
|
const div = document.createElement('div');
|
||||||
|
div.textContent = text;
|
||||||
|
return div.innerHTML;
|
||||||
|
}
|
||||||
|
|
||||||
|
function clearContainers() {
|
||||||
|
Object.values(containers).forEach(container => {
|
||||||
|
container.innerHTML = '';
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function renderTemplates(searchValue = '') {
|
||||||
|
clearContainers();
|
||||||
|
|
||||||
|
const grouped = {
|
||||||
|
XLS: [],
|
||||||
|
API: [],
|
||||||
|
PDF: []
|
||||||
|
};
|
||||||
|
|
||||||
|
allTemplates.forEach(template => {
|
||||||
|
const sourceType = (template.source_type || '').toUpperCase();
|
||||||
|
const label = (template.button_label || '').toLowerCase();
|
||||||
|
|
||||||
|
if (searchValue && !label.includes(searchValue)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (grouped[sourceType]) {
|
||||||
|
grouped[sourceType].push(template);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
Object.keys(grouped).forEach(type => {
|
||||||
|
const container = containers[type];
|
||||||
|
if (!container) return;
|
||||||
|
|
||||||
|
if (grouped[type].length === 0) {
|
||||||
|
container.innerHTML = `<div class="empty-message">No templates found in this section.</div>`;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
grouped[type].forEach(template => {
|
||||||
|
container.appendChild(createButton(template));
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
fetch("load_active_templates.php")
|
fetch("load_active_templates.php")
|
||||||
.then(response => response.json())
|
.then(response => response.json())
|
||||||
.then(data => {
|
.then(data => {
|
||||||
if (!data.success) {
|
if (!data.success) {
|
||||||
console.error("Error loading templates:", data.message);
|
console.error("Error loading templates:", data.message);
|
||||||
|
clearContainers();
|
||||||
|
Object.values(containers).forEach(container => {
|
||||||
|
container.innerHTML = `<div class="empty-message">Error loading templates.</div>`;
|
||||||
|
});
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
let templateButtons = document.getElementById("templateButtons");
|
if (!Array.isArray(data.data)) {
|
||||||
templateButtons.innerHTML = '';
|
clearContainers();
|
||||||
|
Object.values(containers).forEach(container => {
|
||||||
data.data.forEach(template => {
|
container.innerHTML = `<div class="empty-message">Invalid response format.</div>`;
|
||||||
let sizeClass = "btn-medium"; // Default
|
|
||||||
if (template.button_size === "small") sizeClass = "btn-small";
|
|
||||||
if (template.button_size === "large") sizeClass = "btn-large";
|
|
||||||
|
|
||||||
let btn = document.createElement("a");
|
|
||||||
btn.href = `import_xls2.php?id=${template.id}`;
|
|
||||||
btn.className = `btn ${sizeClass}`;
|
|
||||||
btn.style.backgroundColor = template.button_bg_color;
|
|
||||||
btn.style.color = template.button_text_color;
|
|
||||||
btn.textContent = template.button_label;
|
|
||||||
btn.setAttribute("data-label", template.button_label.toLowerCase()); // Attributo per ricerca
|
|
||||||
|
|
||||||
templateButtons.appendChild(btn);
|
|
||||||
});
|
});
|
||||||
})
|
return;
|
||||||
.catch(error => console.error("Fetch error:", error));
|
|
||||||
|
|
||||||
// Funzione per la ricerca live
|
|
||||||
document.getElementById("searchInput").addEventListener("input", function() {
|
|
||||||
let searchValue = this.value.toLowerCase();
|
|
||||||
document.querySelectorAll("#templateButtons a").forEach(btn => {
|
|
||||||
let label = btn.getAttribute("data-label");
|
|
||||||
if (label.includes(searchValue)) {
|
|
||||||
btn.style.display = "inline-block";
|
|
||||||
} else {
|
|
||||||
btn.style.display = "none";
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
allTemplates.push(...data.data);
|
||||||
|
renderTemplates();
|
||||||
|
})
|
||||||
|
.catch(error => {
|
||||||
|
console.error("Fetch error:", error);
|
||||||
|
clearContainers();
|
||||||
|
Object.values(containers).forEach(container => {
|
||||||
|
container.innerHTML = `<div class="empty-message">Fetch error while loading templates.</div>`;
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
document.getElementById("searchInput").addEventListener("input", function() {
|
||||||
|
const searchValue = this.value.toLowerCase().trim();
|
||||||
|
renderTemplates(searchValue);
|
||||||
|
});
|
||||||
});
|
});
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
|||||||
+197
-186
@@ -1,10 +1,6 @@
|
|||||||
<?php
|
<?php
|
||||||
include('include/headscript.php');
|
include('include/headscript.php');
|
||||||
|
|
||||||
// ✅ FIX timezone (Rome)
|
|
||||||
ini_set('date.timezone', 'Europe/Rome');
|
|
||||||
date_default_timezone_set('Europe/Rome');
|
|
||||||
|
|
||||||
if ($_SERVER['REQUEST_METHOD'] !== 'POST' || !isset($_POST['template_id']) || !isset($_POST['selected_rows']) || !isset($_POST['filename'])) {
|
if ($_SERVER['REQUEST_METHOD'] !== 'POST' || !isset($_POST['template_id']) || !isset($_POST['selected_rows']) || !isset($_POST['filename'])) {
|
||||||
header("Location: xlstemplates_grid.php?status=error&message=" . urlencode("Richiesta non valida"));
|
header("Location: xlstemplates_grid.php?status=error&message=" . urlencode("Richiesta non valida"));
|
||||||
exit;
|
exit;
|
||||||
@@ -167,6 +163,8 @@ $fixedFieldsRaw = $fixedStmt->fetchAll(PDO::FETCH_ASSOC);
|
|||||||
// Ordine desiderato: Cliente Responsabile prima, poi gli altri, ConsegnaRichiesta prima delle 3 speciali
|
// Ordine desiderato: Cliente Responsabile prima, poi gli altri, ConsegnaRichiesta prima delle 3 speciali
|
||||||
$desiredOrder = [
|
$desiredOrder = [
|
||||||
'ClienteResponsabile',
|
'ClienteResponsabile',
|
||||||
|
'ClienteFornitore',
|
||||||
|
'ClienteAnalisi',
|
||||||
'AnagraficaCertestObject',
|
'AnagraficaCertestObject',
|
||||||
'AnagraficaCertestService',
|
'AnagraficaCertestService',
|
||||||
'MoltiplicatorePrezzo',
|
'MoltiplicatorePrezzo',
|
||||||
@@ -174,9 +172,13 @@ $desiredOrder = [
|
|||||||
// se ci sono altri campi fixed li mettiamo dopo
|
// se ci sono altri campi fixed li mettiamo dopo
|
||||||
];
|
];
|
||||||
|
|
||||||
|
// No exclusions: fixed fields will be rendered together at the end
|
||||||
|
$excludeFromFixed = [];
|
||||||
|
|
||||||
$fixedFields = [];
|
$fixedFields = [];
|
||||||
$tempMap = [];
|
$tempMap = [];
|
||||||
foreach ($fixedFieldsRaw as $f) {
|
foreach ($fixedFieldsRaw as $f) {
|
||||||
|
if (in_array($f['fixed_field_key'], $excludeFromFixed, true)) continue;
|
||||||
$tempMap[$f['fixed_field_key']] = $f;
|
$tempMap[$f['fixed_field_key']] = $f;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -195,10 +197,11 @@ foreach ($tempMap as $f) {
|
|||||||
// Maps logical fixed_field_key → real datadb column name (mirrors JS fixedAliasMap)
|
// Maps logical fixed_field_key → real datadb column name (mirrors JS fixedAliasMap)
|
||||||
$fixedAliasMap = [
|
$fixedAliasMap = [
|
||||||
'ClienteResponsabile' => 'cliente_responsabile_id',
|
'ClienteResponsabile' => 'cliente_responsabile_id',
|
||||||
|
'ClienteFornitore' => 'cliente_fornitore_id',
|
||||||
|
'ClienteAnalisi' => 'clienteAnalisi',
|
||||||
'MoltiplicatorePrezzo' => 'moltiplicatore_prezzo_id',
|
'MoltiplicatorePrezzo' => 'moltiplicatore_prezzo_id',
|
||||||
'AnagraficaCertestObject' => 'anagrafica_certest_object_id',
|
'AnagraficaCertestObject' => 'anagrafica_certest_object_id',
|
||||||
'AnagraficaCertestService' => 'anagrafica_certest_service_id',
|
'AnagraficaCertestService' => 'anagrafica_certest_service_id',
|
||||||
'ClienteFornitore' => 'cliente_fornitore_id',
|
|
||||||
'ConsegnaRichiesta' => 'consegna_richiesta',
|
'ConsegnaRichiesta' => 'consegna_richiesta',
|
||||||
];
|
];
|
||||||
|
|
||||||
@@ -333,6 +336,7 @@ function fixedDefaultValue(array $f): string
|
|||||||
align-items: center;
|
align-items: center;
|
||||||
padding: 0;
|
padding: 0;
|
||||||
border-bottom: 1px solid #dee2e6;
|
border-bottom: 1px solid #dee2e6;
|
||||||
|
min-width: fit-content;
|
||||||
}
|
}
|
||||||
|
|
||||||
.grid-row:last-child {
|
.grid-row:last-child {
|
||||||
@@ -360,8 +364,15 @@ function fixedDefaultValue(array $f): string
|
|||||||
}
|
}
|
||||||
|
|
||||||
@keyframes batch-pulse {
|
@keyframes batch-pulse {
|
||||||
0%, 100% { background-position: 0% 50%; }
|
|
||||||
50% { background-position: 100% 50%; }
|
0%,
|
||||||
|
100% {
|
||||||
|
background-position: 0% 50%;
|
||||||
|
}
|
||||||
|
|
||||||
|
50% {
|
||||||
|
background-position: 100% 50%;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.batch-row-spinner {
|
.batch-row-spinner {
|
||||||
@@ -876,9 +887,8 @@ function fixedDefaultValue(array $f): string
|
|||||||
<div class="page-wrapper">
|
<div class="page-wrapper">
|
||||||
<div class="page-content">
|
<div class="page-content">
|
||||||
<div class="mb-3 text">
|
<div class="mb-3 text">
|
||||||
<a href="historical_trf.php?id=<?= $template_id ?>&status=i" class="btn btn-warning me-2">Imported (i)</a>
|
<a href="imported.php?id=<?= $template_id ?>" class="btn btn-warning me-2">Imported (i)</a>
|
||||||
<a href="historical_trf.php?id=<?= $template_id ?>&status=P" class="btn btn-primary me-2">In Progress (P)</a>
|
<a href="tolims.php?id=<?= $template_id ?>" class="btn btn-success">To LIMS (l)</a>
|
||||||
<a href="historical_trf.php?id=<?= $template_id ?>&status=l" class="btn btn-success">To LIMS (l)</a>
|
|
||||||
</div>
|
</div>
|
||||||
<div class="card radius-10">
|
<div class="card radius-10">
|
||||||
<div class="card-header">
|
<div class="card-header">
|
||||||
@@ -946,14 +956,15 @@ function fixedDefaultValue(array $f): string
|
|||||||
|
|
||||||
$topColIndex = $mainFieldMapping ? 2 : 1; ?>
|
$topColIndex = $mainFieldMapping ? 2 : 1; ?>
|
||||||
|
|
||||||
<div class="grid-cell grid-top-cell" style="flex: 0 0 300px;" data-index="<?= $mainFieldMapping ? 2 : 1 ?>">
|
<div class="grid-cell grid-top-cell" style="flex: 0 0 150px;" data-index="<?= $mainFieldMapping ? 2 : 1 ?>"></div>
|
||||||
|
|
||||||
|
<div class="grid-cell grid-top-cell" style="flex: 0 0 300px;" data-index="<?= $mainFieldMapping ? 3 : 2 ?>">
|
||||||
<select class="custom-field dropdown-select client-select searchable-client" data-column="idclient" id="clientSelect" name="idclient">
|
<select class="custom-field dropdown-select client-select searchable-client" data-column="idclient" id="clientSelect" name="idclient">
|
||||||
<option value="">Select a client...</option>
|
<option value="">Select a client...</option>
|
||||||
</select>
|
</select>
|
||||||
<button type="button" class="propagate-btn" data-column="idclient"><i class="fas fa-arrow-down"></i></button>
|
<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>
|
<span id="clientLoadingStatus" class="text-muted" style="margin-left: 10px; display: none;">Recupero clienti in corso...</span>
|
||||||
</div>
|
</div>
|
||||||
<div class="grid-cell grid-top-cell" style="flex: 0 0 150px;" data-index="<?= $mainFieldMapping ? 3 : 2 ?>"></div>
|
|
||||||
<?php
|
<?php
|
||||||
|
|
||||||
$topColIndex = $mainFieldMapping ? 4 : 3;
|
$topColIndex = $mainFieldMapping ? 4 : 3;
|
||||||
@@ -1065,6 +1076,8 @@ function fixedDefaultValue(array $f): string
|
|||||||
$isApiField = in_array($key, [
|
$isApiField = in_array($key, [
|
||||||
'MoltiplicatorePrezzo',
|
'MoltiplicatorePrezzo',
|
||||||
'ClienteResponsabile',
|
'ClienteResponsabile',
|
||||||
|
'ClienteFornitore',
|
||||||
|
'ClienteAnalisi',
|
||||||
'AnagraficaCertestObject',
|
'AnagraficaCertestObject',
|
||||||
'AnagraficaCertestService'
|
'AnagraficaCertestService'
|
||||||
], true);
|
], true);
|
||||||
@@ -1118,9 +1131,9 @@ function fixedDefaultValue(array $f): string
|
|||||||
<div class="resizer"></div>
|
<div class="resizer"></div>
|
||||||
</div>
|
</div>
|
||||||
<?php endif; ?>
|
<?php endif; ?>
|
||||||
<div class="grid-header" data-index="<?= $mainFieldMapping ? 2 : 1 ?>" style="flex: 0 0 300px; position: relative;">Client<div class="resizer"></div>
|
<div class="grid-header" data-index="<?= $mainFieldMapping ? 2 : 1 ?>" style="flex: 0 0 150px; position: relative;">Status<div class="resizer"></div>
|
||||||
</div>
|
</div>
|
||||||
<div class="grid-header" data-index="<?= $mainFieldMapping ? 3 : 2 ?>" style="flex: 0 0 150px; position: relative;">Status<div class="resizer"></div>
|
<div class="grid-header" data-index="<?= $mainFieldMapping ? 3 : 2 ?>" style="flex: 0 0 300px; position: relative;">Client<div class="resizer"></div>
|
||||||
</div>
|
</div>
|
||||||
<?php
|
<?php
|
||||||
$headerIndex = $mainFieldMapping ? 4 : 3;
|
$headerIndex = $mainFieldMapping ? 4 : 3;
|
||||||
@@ -1151,6 +1164,16 @@ function fixedDefaultValue(array $f): string
|
|||||||
$key = $f['fixed_field_key'];
|
$key = $f['fixed_field_key'];
|
||||||
$label = $slugMapping[$key] ?? $key;
|
$label = $slugMapping[$key] ?? $key;
|
||||||
|
|
||||||
|
if ($key === 'ClienteFornitore') {
|
||||||
|
$label = 'Fornitore';
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($key === 'ClienteAnalisi') {
|
||||||
|
$label = 'Buyer';
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
echo "<div class='grid-header' data-index='$headerIndex' style='flex: 0 0 180px; position: relative;'>"
|
echo "<div class='grid-header' data-index='$headerIndex' style='flex: 0 0 180px; position: relative;'>"
|
||||||
. htmlspecialchars($label) .
|
. htmlspecialchars($label) .
|
||||||
"<div class='resizer'></div></div>";
|
"<div class='resizer'></div></div>";
|
||||||
@@ -1178,7 +1201,7 @@ function fixedDefaultValue(array $f): string
|
|||||||
<!-- commented only for admin roles -->
|
<!-- commented only for admin roles -->
|
||||||
<?php if ((Auth::user()->hasRole('Admin'))) : ?>
|
<?php if ((Auth::user()->hasRole('Admin'))) : ?>
|
||||||
<?php $isExported = (($row['status'] ?? '') === 'l'); ?>
|
<?php $isExported = (($row['status'] ?? '') === 'l'); ?>
|
||||||
<button type="button" class="export-lims-btn action-btn" data-row="<?= $index ?>" data-iddatadb="<?= $row['iddatadb'] ?>" title="<?= $isExported ? 'Già esportato' : 'Export to LIMS' ?>" style="background: <?= $isExported ? '#ccc' : '#eb0b0b' ?>; color: white; border: none; border-radius: 5px; cursor: <?= $isExported ? 'not-allowed' : 'pointer' ?>; <?= $isExported ? 'opacity: 0.5;' : '' ?>"<?= $isExported ? ' disabled' : '' ?>><i class="fas fa-upload"></i></button>
|
<button type="button" class="export-lims-btn action-btn" data-row="<?= $index ?>" data-iddatadb="<?= $row['iddatadb'] ?>" title="<?= $isExported ? 'Già esportato' : 'Export to LIMS' ?>" style="background: <?= $isExported ? '#ccc' : '#eb0b0b' ?>; color: white; border: none; border-radius: 5px; cursor: <?= $isExported ? 'not-allowed' : 'pointer' ?>; <?= $isExported ? 'opacity: 0.5;' : '' ?>" <?= $isExported ? ' disabled' : '' ?>><i class="fas fa-upload"></i></button>
|
||||||
<?php endif; ?>
|
<?php endif; ?>
|
||||||
|
|
||||||
<button type="button" class="save-btn action-btn" data-row="<?= $index ?>" title="Save" style="background: #28a745; color: white; border: none; border-radius: 5px; cursor: pointer;"><i class="fas fa-save"></i></button>
|
<button type="button" class="save-btn action-btn" data-row="<?= $index ?>" title="Save" style="background: #28a745; color: white; border: none; border-radius: 5px; cursor: pointer;"><i class="fas fa-save"></i></button>
|
||||||
@@ -1211,12 +1234,7 @@ function fixedDefaultValue(array $f): string
|
|||||||
<?php endif; ?>
|
<?php endif; ?>
|
||||||
</div>
|
</div>
|
||||||
<?php endif; ?>
|
<?php endif; ?>
|
||||||
<div class="grid-cell editable-cell" data-col="idclient" data-row="<?= $index ?>" data-index="<?= $mainFieldMapping ? 2 : 1 ?>" style="flex: 0 0 300px;">
|
<div class="grid-cell editable-cell" data-col="status" data-row="<?= $index ?>" data-index="<?= $mainFieldMapping ? 2 : 1 ?>" style="flex: 0 0 150px;">
|
||||||
<select name="rows[<?= $index ?>][idclient]" class="cell-input dropdown-select client-select searchable-client" data-current-value="<?= htmlspecialchars($row['idclient'] ?? $default_idclient) ?>">
|
|
||||||
<option value="">Select a client...</option>
|
|
||||||
</select>
|
|
||||||
</div>
|
|
||||||
<div class="grid-cell editable-cell" data-col="status" data-row="<?= $index ?>" data-index="<?= $mainFieldMapping ? 3 : 2 ?>" style="flex: 0 0 150px;">
|
|
||||||
<span class="status-badge status-<?= htmlspecialchars($row['status'] ?? 'i') ?>">
|
<span class="status-badge status-<?= htmlspecialchars($row['status'] ?? 'i') ?>">
|
||||||
<?= htmlspecialchars($row['status'] === 'i' ? 'Imported' : ($row['status'] === 'P' ? 'In Progress' : 'To LIMS')) ?>
|
<?= htmlspecialchars($row['status'] === 'i' ? 'Imported' : ($row['status'] === 'P' ? 'In Progress' : 'To LIMS')) ?>
|
||||||
</span>
|
</span>
|
||||||
@@ -1225,6 +1243,12 @@ function fixedDefaultValue(array $f): string
|
|||||||
<?php endif; ?>
|
<?php endif; ?>
|
||||||
<input type="hidden" name="rows[<?= $index ?>][status]" value="<?= htmlspecialchars($row['status'] ?? 'i') ?>">
|
<input type="hidden" name="rows[<?= $index ?>][status]" value="<?= htmlspecialchars($row['status'] ?? 'i') ?>">
|
||||||
</div>
|
</div>
|
||||||
|
<div class="grid-cell editable-cell" data-col="idclient" data-row="<?= $index ?>" data-index="<?= $mainFieldMapping ? 3 : 2 ?>" style="flex: 0 0 300px;">
|
||||||
|
<select name="rows[<?= $index ?>][idclient]" class="cell-input dropdown-select client-select searchable-client" data-current-value="<?= htmlspecialchars($row['idclient'] ?? $default_idclient) ?>">
|
||||||
|
<option value="">Select a client...</option>
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
|
||||||
<?php
|
<?php
|
||||||
$cellIndex = $mainFieldMapping ? 4 : 3;
|
$cellIndex = $mainFieldMapping ? 4 : 3;
|
||||||
$rowDetails = array_filter($manualDetails, fn($d) => $d['datadb_id'] == $row['iddatadb']);
|
$rowDetails = array_filter($manualDetails, fn($d) => $d['datadb_id'] == $row['iddatadb']);
|
||||||
@@ -1351,7 +1375,7 @@ function fixedDefaultValue(array $f): string
|
|||||||
. (((int)$f['is_required'] === 1) ? "required" : "")
|
. (((int)$f['is_required'] === 1) ? "required" : "")
|
||||||
. ">";
|
. ">";
|
||||||
} else {
|
} 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' : '';
|
$selectClass = $isApiField ? 'api-fixed-select' : '';
|
||||||
echo "<select
|
echo "<select
|
||||||
name='rows[$index][$key]'
|
name='rows[$index][$key]'
|
||||||
@@ -1398,6 +1422,7 @@ function fixedDefaultValue(array $f): string
|
|||||||
</div>
|
</div>
|
||||||
</form>
|
</form>
|
||||||
<div id="partsModalContainer"></div>
|
<div id="partsModalContainer"></div>
|
||||||
|
<div id="analysisModalContainer"></div>
|
||||||
<div id="annotationsModalContainer"></div>
|
<div id="annotationsModalContainer"></div>
|
||||||
<?php include 'photos_functions.php'; ?>
|
<?php include 'photos_functions.php'; ?>
|
||||||
</div>
|
</div>
|
||||||
@@ -1416,8 +1441,63 @@ function fixedDefaultValue(array $f): string
|
|||||||
<script src="photos.js"></script>
|
<script src="photos.js"></script>
|
||||||
<script src="annotationsModal.js"></script>
|
<script src="annotationsModal.js"></script>
|
||||||
<script src="partsTable.js"></script>
|
<script src="partsTable.js"></script>
|
||||||
|
<script src="analysisModal.js"></script>
|
||||||
<script src="tracking.js"></script>
|
<script src="tracking.js"></script>
|
||||||
<script src="export_to_lims.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>
|
<script>
|
||||||
function expandColumn(cell, expand) {
|
function expandColumn(cell, expand) {
|
||||||
const columnIndex = cell.getAttribute('data-index');
|
const columnIndex = cell.getAttribute('data-index');
|
||||||
@@ -1525,6 +1605,14 @@ function fixedDefaultValue(array $f): string
|
|||||||
const rowIndex = btn.dataset.row;
|
const rowIndex = btn.dataset.row;
|
||||||
const row = btn.closest('.grid-row');
|
const row = btn.closest('.grid-row');
|
||||||
const iddatadb = row.getAttribute('data-id');
|
const iddatadb = row.getAttribute('data-id');
|
||||||
|
|
||||||
|
// Show spinner on save button
|
||||||
|
const origHtml = btn.innerHTML;
|
||||||
|
btn.innerHTML = '<i class="fas fa-spinner fa-spin"></i>';
|
||||||
|
btn.disabled = true;
|
||||||
|
row.style.opacity = '0.6';
|
||||||
|
row.style.pointerEvents = 'none';
|
||||||
|
|
||||||
const formData = new FormData();
|
const formData = new FormData();
|
||||||
|
|
||||||
const inputs = row.querySelectorAll(`input[name^="rows[${rowIndex}][details]"], select[name^="rows[${rowIndex}][details]"]`);
|
const inputs = row.querySelectorAll(`input[name^="rows[${rowIndex}][details]"], select[name^="rows[${rowIndex}][details]"]`);
|
||||||
@@ -1555,6 +1643,8 @@ function fixedDefaultValue(array $f): string
|
|||||||
// Map: fixed key (logical) -> datadb real column
|
// Map: fixed key (logical) -> datadb real column
|
||||||
const fixedAliasMap = {
|
const fixedAliasMap = {
|
||||||
ClienteResponsabile: 'cliente_responsabile_id',
|
ClienteResponsabile: 'cliente_responsabile_id',
|
||||||
|
ClienteFornitore: 'cliente_fornitore_id',
|
||||||
|
ClienteAnalisi: 'clienteAnalisi',
|
||||||
MoltiplicatorePrezzo: 'moltiplicatore_prezzo_id',
|
MoltiplicatorePrezzo: 'moltiplicatore_prezzo_id',
|
||||||
AnagraficaCertestObject: 'anagrafica_certest_object_id',
|
AnagraficaCertestObject: 'anagrafica_certest_object_id',
|
||||||
AnagraficaCertestService: 'anagrafica_certest_service_id',
|
AnagraficaCertestService: 'anagrafica_certest_service_id',
|
||||||
@@ -1608,6 +1698,12 @@ function fixedDefaultValue(array $f): string
|
|||||||
})
|
})
|
||||||
.catch(error => {
|
.catch(error => {
|
||||||
alert('Errore durante il salvataggio: ' + error.message);
|
alert('Errore durante il salvataggio: ' + error.message);
|
||||||
|
})
|
||||||
|
.finally(() => {
|
||||||
|
btn.innerHTML = origHtml;
|
||||||
|
btn.disabled = false;
|
||||||
|
row.style.opacity = '';
|
||||||
|
row.style.pointerEvents = '';
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
@@ -1616,7 +1712,9 @@ function fixedDefaultValue(array $f): string
|
|||||||
document.querySelector('.save-all-btn').addEventListener('click', (e) => {
|
document.querySelector('.save-all-btn').addEventListener('click', (e) => {
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
if (saveAllRunning || window.batchRunning) return;
|
if (saveAllRunning || window.batchRunning) return;
|
||||||
const confirmModal = new bootstrap.Modal(document.getElementById('saveAllConfirmModal'), { keyboard: false });
|
const confirmModal = new bootstrap.Modal(document.getElementById('saveAllConfirmModal'), {
|
||||||
|
keyboard: false
|
||||||
|
});
|
||||||
confirmModal.show();
|
confirmModal.show();
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -1634,7 +1732,11 @@ function fixedDefaultValue(array $f): string
|
|||||||
// Disable all row buttons
|
// Disable all row buttons
|
||||||
document.querySelectorAll('.grid-row[data-id]').forEach(r => r.classList.add('batch-disabled'));
|
document.querySelectorAll('.grid-row[data-id]').forEach(r => r.classList.add('batch-disabled'));
|
||||||
const toggle = document.querySelector('.actions-dropdown .dropdown-toggle');
|
const toggle = document.querySelector('.actions-dropdown .dropdown-toggle');
|
||||||
if (toggle) { toggle.disabled = true; toggle.style.opacity = '0.5'; toggle.style.pointerEvents = 'none'; }
|
if (toggle) {
|
||||||
|
toggle.disabled = true;
|
||||||
|
toggle.style.opacity = '0.5';
|
||||||
|
toggle.style.pointerEvents = 'none';
|
||||||
|
}
|
||||||
|
|
||||||
// Show status bar
|
// Show status bar
|
||||||
const bar = document.getElementById('batchExportBar');
|
const bar = document.getElementById('batchExportBar');
|
||||||
@@ -1651,10 +1753,14 @@ function fixedDefaultValue(array $f): string
|
|||||||
|
|
||||||
for (const row of rows) {
|
for (const row of rows) {
|
||||||
const saveBtn = row.querySelector('.save-btn');
|
const saveBtn = row.querySelector('.save-btn');
|
||||||
if (!saveBtn) { continue; }
|
if (!saveBtn) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
const rowIndex = saveBtn.dataset.row;
|
const rowIndex = saveBtn.dataset.row;
|
||||||
const iddatadb = row.getAttribute('data-id');
|
const iddatadb = row.getAttribute('data-id');
|
||||||
if (!rowIndex || !iddatadb) { continue; }
|
if (!rowIndex || !iddatadb) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
processed++;
|
processed++;
|
||||||
statusEl.textContent = `Salvataggio ${processed} / ${total} (id: ${iddatadb})...`;
|
statusEl.textContent = `Salvataggio ${processed} / ${total} (id: ${iddatadb})...`;
|
||||||
@@ -1664,7 +1770,10 @@ function fixedDefaultValue(array $f): string
|
|||||||
row.classList.add('batch-exporting');
|
row.classList.add('batch-exporting');
|
||||||
const btnCell = row.querySelector('.button-cell');
|
const btnCell = row.querySelector('.button-cell');
|
||||||
if (btnCell) {
|
if (btnCell) {
|
||||||
btnCell.querySelectorAll('.action-btn').forEach(b => { b.dataset.prevDisplay = b.style.display; b.style.display = 'none'; });
|
btnCell.querySelectorAll('.action-btn').forEach(b => {
|
||||||
|
b.dataset.prevDisplay = b.style.display;
|
||||||
|
b.style.display = 'none';
|
||||||
|
});
|
||||||
const spinner = document.createElement('span');
|
const spinner = document.createElement('span');
|
||||||
spinner.className = 'batch-row-spinner';
|
spinner.className = 'batch-row-spinner';
|
||||||
spinner.innerHTML = '<i class="fas fa-spinner fa-spin"></i> Saving...';
|
spinner.innerHTML = '<i class="fas fa-spinner fa-spin"></i> Saving...';
|
||||||
@@ -1696,6 +1805,8 @@ function fixedDefaultValue(array $f): string
|
|||||||
const m = inp.name.match(/rows\[\d+\]\[([^\]]+)\]/);
|
const m = inp.name.match(/rows\[\d+\]\[([^\]]+)\]/);
|
||||||
const fixedAliasMap = {
|
const fixedAliasMap = {
|
||||||
ClienteResponsabile: 'cliente_responsabile_id',
|
ClienteResponsabile: 'cliente_responsabile_id',
|
||||||
|
ClienteFornitore: 'cliente_fornitore_id',
|
||||||
|
ClienteAnalisi: 'clienteAnalisi',
|
||||||
MoltiplicatorePrezzo: 'moltiplicatore_prezzo_id',
|
MoltiplicatorePrezzo: 'moltiplicatore_prezzo_id',
|
||||||
AnagraficaCertestObject: 'anagrafica_certest_object_id',
|
AnagraficaCertestObject: 'anagrafica_certest_object_id',
|
||||||
AnagraficaCertestService: 'anagrafica_certest_service_id',
|
AnagraficaCertestService: 'anagrafica_certest_service_id',
|
||||||
@@ -1758,7 +1869,10 @@ function fixedDefaultValue(array $f): string
|
|||||||
if (btnCell) {
|
if (btnCell) {
|
||||||
const sp = btnCell.querySelector('.batch-row-spinner');
|
const sp = btnCell.querySelector('.batch-row-spinner');
|
||||||
if (sp) sp.remove();
|
if (sp) sp.remove();
|
||||||
btnCell.querySelectorAll('.action-btn').forEach(b => { b.style.display = b.dataset.prevDisplay || ''; delete b.dataset.prevDisplay; });
|
btnCell.querySelectorAll('.action-btn').forEach(b => {
|
||||||
|
b.style.display = b.dataset.prevDisplay || '';
|
||||||
|
delete b.dataset.prevDisplay;
|
||||||
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1771,7 +1885,11 @@ function fixedDefaultValue(array $f): string
|
|||||||
|
|
||||||
// Re-enable all row buttons
|
// Re-enable all row buttons
|
||||||
document.querySelectorAll('.grid-row[data-id]').forEach(r => r.classList.remove('batch-disabled'));
|
document.querySelectorAll('.grid-row[data-id]').forEach(r => r.classList.remove('batch-disabled'));
|
||||||
if (toggle) { toggle.disabled = false; toggle.style.opacity = ''; toggle.style.pointerEvents = ''; }
|
if (toggle) {
|
||||||
|
toggle.disabled = false;
|
||||||
|
toggle.style.opacity = '';
|
||||||
|
toggle.style.pointerEvents = '';
|
||||||
|
}
|
||||||
|
|
||||||
const resultEl = document.getElementById('saveAllResultMessage');
|
const resultEl = document.getElementById('saveAllResultMessage');
|
||||||
const resultLabel = document.getElementById('saveAllResultModalLabel');
|
const resultLabel = document.getElementById('saveAllResultModalLabel');
|
||||||
@@ -1782,7 +1900,9 @@ function fixedDefaultValue(array $f): string
|
|||||||
resultLabel.textContent = 'Salvataggio con Errori';
|
resultLabel.textContent = 'Salvataggio con Errori';
|
||||||
resultEl.innerHTML = `<i class="fas fa-exclamation-triangle" style="color:#ffc107;"></i> Salvate <strong>${successCount}</strong> righe con successo.<br><br><strong>Errori:</strong><br>` + errorMessages.join('<br>');
|
resultEl.innerHTML = `<i class="fas fa-exclamation-triangle" style="color:#ffc107;"></i> Salvate <strong>${successCount}</strong> righe con successo.<br><br><strong>Errori:</strong><br>` + errorMessages.join('<br>');
|
||||||
}
|
}
|
||||||
new bootstrap.Modal(document.getElementById('saveAllResultModal'), { keyboard: false }).show();
|
new bootstrap.Modal(document.getElementById('saveAllResultModal'), {
|
||||||
|
keyboard: false
|
||||||
|
}).show();
|
||||||
});
|
});
|
||||||
|
|
||||||
window.addEventListener("beforeunload", function(e) {
|
window.addEventListener("beforeunload", function(e) {
|
||||||
@@ -1814,50 +1934,31 @@ function fixedDefaultValue(array $f): string
|
|||||||
<script>
|
<script>
|
||||||
document.addEventListener("DOMContentLoaded", function() {
|
document.addEventListener("DOMContentLoaded", function() {
|
||||||
const inputs = document.querySelectorAll('.cell-input');
|
const inputs = document.querySelectorAll('.cell-input');
|
||||||
let clientData = []; // Dichiarazione di clientData qui
|
let clientData = globalClientData;
|
||||||
|
|
||||||
// Funzione per caricare i client
|
// Funzione per caricare i client
|
||||||
async function loadClients(retryCount = 0, maxRetries = 3) {
|
async function loadClients() {
|
||||||
const clientLoadingStatus = document.getElementById("clientLoadingStatus");
|
const clientLoadingStatus = document.getElementById("clientLoadingStatus");
|
||||||
try {
|
try {
|
||||||
clientLoadingStatus.style.display = 'inline';
|
clientLoadingStatus.style.display = 'inline';
|
||||||
clientLoadingStatus.textContent = 'Recupero clienti in corso...';
|
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");
|
const select = document.getElementById("clientSelect");
|
||||||
select.innerHTML = '<option value="">Select a client...</option>';
|
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);
|
clientData.forEach(client => {
|
||||||
if (parseInt(id) === parseInt(<?php echo json_encode($default_idclient ?? 0); ?>)) {
|
const option = new Option(formatClientLabel(client), client.IdCliente);
|
||||||
|
if (parseInt(client.IdCliente) === parseInt(<?php echo json_encode($default_idclient ?? 0); ?>)) {
|
||||||
option.selected = true;
|
option.selected = true;
|
||||||
}
|
}
|
||||||
select.add(option);
|
select.add(option);
|
||||||
});
|
});
|
||||||
|
|
||||||
populateClientDropdowns();
|
populateClientDropdowns();
|
||||||
|
|
||||||
clientLoadingStatus.textContent = "Clienti caricati.";
|
clientLoadingStatus.textContent = "Clienti caricati.";
|
||||||
// ✅ force refresh of header dependent fixed fields
|
|
||||||
$('#clientSelect').trigger('change');
|
$('#clientSelect').trigger('change');
|
||||||
$('.grid-top .api-fixed-select[data-fixed-key="ClienteResponsabile"]').trigger('fixed:reload');
|
$('.grid-top .api-fixed-select[data-fixed-key="ClienteResponsabile"]').trigger('fixed:reload');
|
||||||
|
|
||||||
@@ -2219,7 +2320,7 @@ function fixedDefaultValue(array $f): string
|
|||||||
} else {
|
} else {
|
||||||
// Select nativa
|
// Select nativa
|
||||||
try {
|
try {
|
||||||
$(dropdown).select2('destroy');
|
if ($(dropdown).hasClass('select2-hidden-accessible')) $(dropdown).select2('destroy');
|
||||||
} catch (e) {}
|
} catch (e) {}
|
||||||
dropdown.innerHTML = '<option value="">Seleziona...</option>';
|
dropdown.innerHTML = '<option value="">Seleziona...</option>';
|
||||||
items.forEach(value => {
|
items.forEach(value => {
|
||||||
@@ -2245,121 +2346,6 @@ function fixedDefaultValue(array $f): string
|
|||||||
|
|
||||||
populateDropdowns();
|
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>
|
||||||
<script>
|
<script>
|
||||||
document.addEventListener("DOMContentLoaded", function() {
|
document.addEventListener("DOMContentLoaded", function() {
|
||||||
@@ -2381,6 +2367,13 @@ function fixedDefaultValue(array $f): string
|
|||||||
});
|
});
|
||||||
gridCell.dispatchEvent(event);
|
gridCell.dispatchEvent(event);
|
||||||
}
|
}
|
||||||
|
if (e.target.matches('select[name^="rows"][name$="[cliente_fornitore_id]"]')) {
|
||||||
|
const gridCell = e.target.closest('.grid-cell');
|
||||||
|
const event = new Event('change', {
|
||||||
|
bubbles: true
|
||||||
|
});
|
||||||
|
gridCell.dispatchEvent(event);
|
||||||
|
}
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
</script>
|
</script>
|
||||||
@@ -2397,11 +2390,12 @@ function fixedDefaultValue(array $f): string
|
|||||||
|
|
||||||
// Ensure Select2 dropdowns trigger change events for unsaved changes tracking
|
// Ensure Select2 dropdowns trigger change events for unsaved changes tracking
|
||||||
$('.searchable-client').on('select2:select select2:clear', function(e) {
|
$('.searchable-client').on('select2:select select2:clear', function(e) {
|
||||||
const gridCell = this.closest('.grid-cell');
|
// Trigger native change on the real <select> element so
|
||||||
const event = new Event('change', {
|
// addEventListener('change') handlers can detect modifications.
|
||||||
|
const nativeEvent = new Event('change', {
|
||||||
bubbles: true
|
bubbles: true
|
||||||
});
|
});
|
||||||
gridCell.dispatchEvent(event);
|
this.dispatchEvent(nativeEvent);
|
||||||
});
|
});
|
||||||
|
|
||||||
// Update propagate functionality for client dropdown
|
// Update propagate functionality for client dropdown
|
||||||
@@ -2415,7 +2409,7 @@ function fixedDefaultValue(array $f): string
|
|||||||
const event = new Event('change', {
|
const event = new Event('change', {
|
||||||
bubbles: true
|
bubbles: true
|
||||||
});
|
});
|
||||||
clientSelect.closest('.grid-cell').dispatchEvent(event);
|
clientSelect.dispatchEvent(event);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
@@ -2438,10 +2432,7 @@ function fixedDefaultValue(array $f): string
|
|||||||
.map(s => s.trim())
|
.map(s => s.trim())
|
||||||
.filter(s => s.length > 0);
|
.filter(s => s.length > 0);
|
||||||
|
|
||||||
// (opzionale) dedup per evitare doppioni tipo "Cap | Cap"
|
if (!parts.length) {
|
||||||
const uniqueParts = [...new Set(parts)];
|
|
||||||
|
|
||||||
if (!uniqueParts.length) {
|
|
||||||
alert('⚠️ Inserisci prima una descrizione nel campo Tested Component.');
|
alert('⚠️ Inserisci prima una descrizione nel campo Tested Component.');
|
||||||
$input.focus();
|
$input.focus();
|
||||||
return;
|
return;
|
||||||
@@ -2452,7 +2443,7 @@ function fixedDefaultValue(array $f): string
|
|||||||
const errors = [];
|
const errors = [];
|
||||||
|
|
||||||
// ✅ esegue una chiamata per ogni parte (compatibile con l’attuale add_part_quick.php)
|
// ✅ esegue una chiamata per ogni parte (compatibile con l’attuale add_part_quick.php)
|
||||||
for (const p of uniqueParts) {
|
for (const p of parts) {
|
||||||
const formData = new FormData();
|
const formData = new FormData();
|
||||||
formData.append('iddatadb', iddatadb);
|
formData.append('iddatadb', iddatadb);
|
||||||
formData.append('part_description', p);
|
formData.append('part_description', p);
|
||||||
@@ -2626,6 +2617,12 @@ function fixedDefaultValue(array $f): string
|
|||||||
getParams: (clientId) => ({
|
getParams: (clientId) => ({
|
||||||
id_cliente: clientId
|
id_cliente: clientId
|
||||||
})
|
})
|
||||||
|
},
|
||||||
|
'ClienteFornitore': {
|
||||||
|
source: 'clients'
|
||||||
|
},
|
||||||
|
'ClienteAnalisi': {
|
||||||
|
source: 'clients'
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -2649,6 +2646,21 @@ function fixedDefaultValue(array $f): string
|
|||||||
const config = apiFields[fieldKey];
|
const config = apiFields[fieldKey];
|
||||||
if (!config) return [];
|
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 : '');
|
const cacheKey = fieldKey + (clientId ? '_' + clientId : '');
|
||||||
if (fixedFieldDataCache[cacheKey]) {
|
if (fixedFieldDataCache[cacheKey]) {
|
||||||
return fixedFieldDataCache[cacheKey];
|
return fixedFieldDataCache[cacheKey];
|
||||||
@@ -2751,7 +2763,8 @@ function fixedDefaultValue(array $f): string
|
|||||||
|
|
||||||
if (results.length > 12) {
|
if (results.length > 12) {
|
||||||
// Select2 con ricerca
|
// Select2 con ricerca
|
||||||
$select.select2('destroy').empty().select2({
|
if ($select.hasClass('select2-hidden-accessible')) $select.select2('destroy');
|
||||||
|
$select.empty().select2({
|
||||||
data: [{
|
data: [{
|
||||||
id: '',
|
id: '',
|
||||||
text: 'Seleziona...'
|
text: 'Seleziona...'
|
||||||
@@ -2762,9 +2775,7 @@ function fixedDefaultValue(array $f): string
|
|||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
// Select nativa senza Select2
|
// Select nativa senza Select2
|
||||||
try {
|
if ($select.hasClass('select2-hidden-accessible')) $select.select2('destroy');
|
||||||
$select.select2('destroy');
|
|
||||||
} catch (e) {}
|
|
||||||
$select.empty();
|
$select.empty();
|
||||||
$select.append(new Option('Seleziona...', '', true, false));
|
$select.append(new Option('Seleziona...', '', true, false));
|
||||||
results.forEach(r => {
|
results.forEach(r => {
|
||||||
|
|||||||
@@ -24,7 +24,7 @@ $columns = json_decode(urldecode($_POST['columns'] ?? '[]'), true);
|
|||||||
$rows = json_decode(urldecode($_POST['rows'] ?? '[]'), true);
|
$rows = json_decode(urldecode($_POST['rows'] ?? '[]'), true);
|
||||||
$excelrows = json_decode(urldecode($_POST['excelrows'] ?? '[]'), true);
|
$excelrows = json_decode(urldecode($_POST['excelrows'] ?? '[]'), true);
|
||||||
|
|
||||||
$newFilename = htmlspecialchars($_POST['filename']);
|
$newFilename = $_POST['filename'];
|
||||||
|
|
||||||
$_SESSION['template_id'] = $template_id;
|
$_SESSION['template_id'] = $template_id;
|
||||||
$_SESSION['selected_rows'] = $selected_rows;
|
$_SESSION['selected_rows'] = $selected_rows;
|
||||||
@@ -47,7 +47,7 @@ $pdo = $db->getConnection();
|
|||||||
$importReferenceCode = date('YmdHis') . '-' . uniqid();
|
$importReferenceCode = date('YmdHis') . '-' . uniqid();
|
||||||
|
|
||||||
// Recupera tutti i mapping dal template
|
// Recupera tutti i mapping dal template
|
||||||
$stmt = $pdo->prepare("SELECT id, excel_column, data_type, is_required, manual_default, is_manual, field_label, field_id, main_field FROM template_mapping WHERE template_id = ?");
|
$stmt = $pdo->prepare("SELECT id, excel_column, data_type, is_required, manual_default, is_manual, field_label, field_id, main_field, auto_value FROM template_mapping WHERE template_id = ?");
|
||||||
$stmt->execute([$template_id]);
|
$stmt->execute([$template_id]);
|
||||||
$allMappings = $stmt->fetchAll(PDO::FETCH_ASSOC);
|
$allMappings = $stmt->fetchAll(PDO::FETCH_ASSOC);
|
||||||
|
|
||||||
@@ -126,7 +126,7 @@ foreach ($selected_rows as $rowIndex) {
|
|||||||
case 'Testo':
|
case 'Testo':
|
||||||
case 'VARCHAR':
|
case 'VARCHAR':
|
||||||
default:
|
default:
|
||||||
$fieldValue = !empty($fieldValue) ? htmlspecialchars((string)$fieldValue) : ($mapping['manual_default'] ?? '');
|
$fieldValue = !empty($fieldValue) ? (string)$fieldValue : ($mapping['manual_default'] ?? '');
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
@@ -135,6 +135,15 @@ foreach ($selected_rows as $rowIndex) {
|
|||||||
$fieldValue = date('Y-m-d');
|
$fieldValue = date('Y-m-d');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
// Apply auto_value if field is still empty
|
||||||
|
if (($fieldValue === null || $fieldValue === '') && !empty($mapping['auto_value']) && $mapping['auto_value'] !== 'none') {
|
||||||
|
if ($mapping['auto_value'] === 'import_date') {
|
||||||
|
$fieldValue = date('Y-m-d');
|
||||||
|
} elseif ($mapping['auto_value'] === 'import_time') {
|
||||||
|
$fieldValue = date('H:i');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if ($mapping['is_required'] && (is_null($fieldValue) || $fieldValue === '')) {
|
if ($mapping['is_required'] && (is_null($fieldValue) || $fieldValue === '')) {
|
||||||
error_log("Required field missing for mapping ID: " . $mapping['id'] . ", field: " . $mapping['field_label']);
|
error_log("Required field missing for mapping ID: " . $mapping['id'] . ", field: " . $mapping['field_label']);
|
||||||
}
|
}
|
||||||
@@ -147,28 +156,6 @@ foreach ($selected_rows as $rowIndex) {
|
|||||||
|
|
||||||
$_SESSION['inserted_ids'] = $insertedIds;
|
$_SESSION['inserted_ids'] = $insertedIds;
|
||||||
|
|
||||||
$params = [
|
header("Location: imported.php?id=" . urlencode($template_id) . "&importref=" . urlencode($importReferenceCode));
|
||||||
'template_id' => $template_id,
|
|
||||||
'filename' => $newFilename,
|
|
||||||
];
|
|
||||||
|
|
||||||
?>
|
|
||||||
<form id="redirectForm" action="import_edit2.php" method="post">
|
|
||||||
<input type="hidden" name="template_id" value="<?= htmlspecialchars($template_id) ?>">
|
|
||||||
<input type="hidden" name="filename" value="<?= htmlspecialchars($newFilename) ?>">
|
|
||||||
<?php foreach ($selected_rows as $row): ?>
|
|
||||||
<input type="hidden" name="selected_rows[]" value="<?= htmlspecialchars($row) ?>">
|
|
||||||
<?php endforeach; ?>
|
|
||||||
<?php foreach ($insertedIds as $id): ?>
|
|
||||||
<input type="hidden" name="inserted_ids[]" value="<?= htmlspecialchars($id) ?>">
|
|
||||||
<?php endforeach; ?>
|
|
||||||
<input type="hidden" name="columns" value='<?= json_encode($columns) ?>'>
|
|
||||||
<input type="hidden" name="rows" value='<?= json_encode($rows) ?>'>
|
|
||||||
<input type="hidden" name="excelrows" value='<?= json_encode($excelrows) ?>'>
|
|
||||||
</form>
|
|
||||||
<script>
|
|
||||||
document.getElementById('redirectForm').submit();
|
|
||||||
</script>
|
|
||||||
<?php
|
|
||||||
exit;
|
exit;
|
||||||
?>
|
?>
|
||||||
+109
-14
@@ -40,6 +40,22 @@ error_log("Loaded template: " . print_r($template, true));
|
|||||||
<?php include('cssinclude.php'); ?>
|
<?php include('cssinclude.php'); ?>
|
||||||
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/css/bootstrap.min.css" rel="stylesheet">
|
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/css/bootstrap.min.css" rel="stylesheet">
|
||||||
<style>
|
<style>
|
||||||
|
.top-scrollbar {
|
||||||
|
overflow-x: auto;
|
||||||
|
overflow-y: hidden;
|
||||||
|
width: 100%;
|
||||||
|
height: 18px;
|
||||||
|
margin-bottom: 8px;
|
||||||
|
border: 1px solid #dee2e6;
|
||||||
|
border-radius: 0.25rem;
|
||||||
|
background: #f8f9fa;
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.top-scrollbar-inner {
|
||||||
|
height: 1px;
|
||||||
|
}
|
||||||
|
|
||||||
.table-container {
|
.table-container {
|
||||||
overflow-x: auto;
|
overflow-x: auto;
|
||||||
max-width: 100%;
|
max-width: 100%;
|
||||||
@@ -143,16 +159,20 @@ error_log("Loaded template: " . print_r($template, true));
|
|||||||
<div class="page-content">
|
<div class="page-content">
|
||||||
<?php include('top_stat_widget.php'); ?>
|
<?php include('top_stat_widget.php'); ?>
|
||||||
<div class="mb-3 text">
|
<div class="mb-3 text">
|
||||||
<a href="historical_trf.php?id=<?= $id ?>&status=i" class="btn btn-warning me-2">Imported (i)</a>
|
<a href="imported.php?id=<?= $id ?>" class="btn btn-warning me-2">Imported (i)</a>
|
||||||
<a href="historical_trf.php?id=<?= $id ?>&status=P" class="btn btn-primary me-2">In Progress (P)</a>
|
<a href="tolims.php?id=<?= $id ?>" class="btn btn-success">To LIMS (l)</a>
|
||||||
<a href="historical_trf.php?id=<?= $id ?>&status=l" class="btn btn-success">To LIMS (l)</a>
|
|
||||||
</div>
|
</div>
|
||||||
<div class="card radius-10">
|
<div class="card radius-10">
|
||||||
<div class="card-header">
|
<div class="card-header">
|
||||||
<div class="d-flex align-items-center">
|
<div class="d-flex align-items-center">
|
||||||
<div>
|
<div>
|
||||||
<h6 class="mb-0"><?= htmlspecialchars($template['name']) ?></h6>
|
<h6 class="mb-0"><?= htmlspecialchars($template['name']) ?></h6>
|
||||||
<small>Template ID: <?= $id ?>, Start Row: <?= $template['header_row'] ?>, Start Column: <?= $template['start_column'] ?></small>
|
<small>
|
||||||
|
Template ID: <?= $id ?>,
|
||||||
|
Sheet Number: <?= (int)($template['xls_sheet_index'] ?? 0) ?>,
|
||||||
|
Start Row: <?= $template['header_row'] ?>,
|
||||||
|
Start Column: <?= $template['start_column'] ?>
|
||||||
|
</small>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -229,8 +249,9 @@ error_log("Loaded template: " . print_r($template, true));
|
|||||||
const templateId = <?= $id ?>;
|
const templateId = <?= $id ?>;
|
||||||
console.log('Template ID passed to formData:', templateId);
|
console.log('Template ID passed to formData:', templateId);
|
||||||
formData.append('template_id', templateId);
|
formData.append('template_id', templateId);
|
||||||
formData.append('header_row', <?= $template['header_row'] ?>);
|
formData.append('header_row', <?= (int)$template['header_row'] ?>);
|
||||||
formData.append('start_column', <?= $template['start_column'] ?>);
|
formData.append('start_column', <?= json_encode($template['start_column']) ?>);
|
||||||
|
formData.append('xls_sheet_index', <?= (int)($template['xls_sheet_index'] ?? 0) ?>);
|
||||||
|
|
||||||
fetch('process_import_xls2.php', {
|
fetch('process_import_xls2.php', {
|
||||||
method: 'POST',
|
method: 'POST',
|
||||||
@@ -325,12 +346,19 @@ error_log("Loaded template: " . print_r($template, true));
|
|||||||
<button type="submit" class="btn btn-primary" id="proceedButtonTop" disabled>Prosegui</button>
|
<button type="submit" class="btn btn-primary" id="proceedButtonTop" disabled>Prosegui</button>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="table-container">
|
<div class="top-scrollbar" id="topTableScrollbar">
|
||||||
<table class="table table-striped table-bordered">
|
<div class="top-scrollbar-inner" id="topTableScrollbarInner"></div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="table-container" id="mainTableContainer">
|
||||||
|
<table class="table table-striped table-bordered" id="importPreviewTable">
|
||||||
<thead>
|
<thead>
|
||||||
<tr>
|
<tr>
|
||||||
<th><input type="checkbox" id="selectAll"> Seleziona</th>
|
<th><input type="checkbox" id="selectAll"> Seleziona</th>
|
||||||
${data.columns.map(col => `<th>${col || 'Colonna senza nome'}<div class="resize-handle"></div></th>`).join('')}
|
${data.columns.map(col => {
|
||||||
|
const label = !col ? 'Colonna senza nome' : (col.match(/^__empty_\d+__$/) ? 'Colonna senza nome' : col);
|
||||||
|
return `<th>${label}<div class="resize-handle"></div></th>`;
|
||||||
|
}).join('')}
|
||||||
</tr>
|
</tr>
|
||||||
<tr class="column-filters">
|
<tr class="column-filters">
|
||||||
<th></th>
|
<th></th>
|
||||||
@@ -361,6 +389,47 @@ error_log("Loaded template: " . print_r($template, true));
|
|||||||
`;
|
`;
|
||||||
tableContainer.innerHTML = html;
|
tableContainer.innerHTML = html;
|
||||||
|
|
||||||
|
const topTableScrollbar = document.getElementById('topTableScrollbar');
|
||||||
|
const topTableScrollbarInner = document.getElementById('topTableScrollbarInner');
|
||||||
|
const mainTableContainer = document.getElementById('mainTableContainer');
|
||||||
|
const importPreviewTable = document.getElementById('importPreviewTable');
|
||||||
|
|
||||||
|
function updateTopTableScrollbar() {
|
||||||
|
if (!topTableScrollbar || !topTableScrollbarInner || !mainTableContainer || !importPreviewTable) return;
|
||||||
|
|
||||||
|
topTableScrollbarInner.style.width = importPreviewTable.scrollWidth + 'px';
|
||||||
|
|
||||||
|
if (mainTableContainer.scrollWidth > mainTableContainer.clientWidth) {
|
||||||
|
topTableScrollbar.style.display = 'block';
|
||||||
|
} else {
|
||||||
|
topTableScrollbar.style.display = 'none';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let syncingTop = false;
|
||||||
|
let syncingBottom = false;
|
||||||
|
|
||||||
|
if (topTableScrollbar && mainTableContainer) {
|
||||||
|
topTableScrollbar.addEventListener('scroll', function() {
|
||||||
|
if (syncingBottom) return;
|
||||||
|
syncingTop = true;
|
||||||
|
mainTableContainer.scrollLeft = topTableScrollbar.scrollLeft;
|
||||||
|
syncingTop = false;
|
||||||
|
});
|
||||||
|
|
||||||
|
mainTableContainer.addEventListener('scroll', function() {
|
||||||
|
if (syncingTop) return;
|
||||||
|
syncingBottom = true;
|
||||||
|
topTableScrollbar.scrollLeft = mainTableContainer.scrollLeft;
|
||||||
|
syncingBottom = false;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
updateTopTableScrollbar();
|
||||||
|
setTimeout(updateTopTableScrollbar, 100);
|
||||||
|
setTimeout(updateTopTableScrollbar, 300);
|
||||||
|
window.addEventListener('resize', updateTopTableScrollbar);
|
||||||
|
|
||||||
const proceedButtonTop = document.getElementById('proceedButtonTop');
|
const proceedButtonTop = document.getElementById('proceedButtonTop');
|
||||||
const proceedButtonBottom = document.getElementById('proceedButtonBottom');
|
const proceedButtonBottom = document.getElementById('proceedButtonBottom');
|
||||||
const selectAllCheckbox = document.getElementById('selectAll');
|
const selectAllCheckbox = document.getElementById('selectAll');
|
||||||
@@ -374,16 +443,32 @@ error_log("Loaded template: " . print_r($template, true));
|
|||||||
}
|
}
|
||||||
|
|
||||||
selectAllCheckbox.addEventListener('change', function() {
|
selectAllCheckbox.addEventListener('change', function() {
|
||||||
checkboxes.forEach(checkbox => {
|
const visibleRows = Array.from(document.querySelectorAll('.table tbody tr'))
|
||||||
|
.filter(row => row.style.display !== 'none');
|
||||||
|
|
||||||
|
visibleRows.forEach(row => {
|
||||||
|
const checkbox = row.querySelector('.row-checkbox');
|
||||||
|
if (checkbox) {
|
||||||
checkbox.checked = this.checked;
|
checkbox.checked = this.checked;
|
||||||
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
updateProceedButton();
|
updateProceedButton();
|
||||||
});
|
});
|
||||||
|
|
||||||
checkboxes.forEach(checkbox => {
|
checkboxes.forEach(checkbox => {
|
||||||
checkbox.addEventListener('change', function() {
|
checkbox.addEventListener('change', function() {
|
||||||
console.log('Checkbox changed, checked:', this.checked, 'excelrow:', this.dataset.excelrow);
|
console.log('Checkbox changed, checked:', this.checked, 'excelrow:', this.dataset.excelrow);
|
||||||
selectAllCheckbox.checked = Array.from(checkboxes).every(cb => cb.checked);
|
|
||||||
|
const visibleCheckboxes = Array.from(document.querySelectorAll('.table tbody tr'))
|
||||||
|
.filter(row => row.style.display !== 'none')
|
||||||
|
.map(row => row.querySelector('.row-checkbox'))
|
||||||
|
.filter(cb => cb !== null);
|
||||||
|
|
||||||
|
selectAllCheckbox.checked =
|
||||||
|
visibleCheckboxes.length > 0 &&
|
||||||
|
visibleCheckboxes.every(cb => cb.checked);
|
||||||
|
|
||||||
updateProceedButton();
|
updateProceedButton();
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
@@ -431,12 +516,11 @@ error_log("Loaded template: " . print_r($template, true));
|
|||||||
|
|
||||||
function applyColumnFilters() {
|
function applyColumnFilters() {
|
||||||
rows.forEach(row => {
|
rows.forEach(row => {
|
||||||
// Le celle di data partono da index 1 (perché index 0 è checkbox)
|
|
||||||
let visible = true;
|
let visible = true;
|
||||||
|
|
||||||
for (const [colIndexStr, filterValue] of Object.entries(activeFilters)) {
|
for (const [colIndexStr, filterValue] of Object.entries(activeFilters)) {
|
||||||
const colIndex = parseInt(colIndexStr, 10); // 0..N-1 sulle colonne dati
|
const colIndex = parseInt(colIndexStr, 10);
|
||||||
const cell = row.cells[colIndex + 1]; // +1 per saltare la colonna checkbox
|
const cell = row.cells[colIndex + 1];
|
||||||
|
|
||||||
const cellText = (cell?.textContent || '').toLowerCase();
|
const cellText = (cell?.textContent || '').toLowerCase();
|
||||||
const searchText = (filterValue || '').toLowerCase().trim();
|
const searchText = (filterValue || '').toLowerCase().trim();
|
||||||
@@ -449,6 +533,17 @@ error_log("Loaded template: " . print_r($template, true));
|
|||||||
|
|
||||||
row.style.display = visible ? '' : 'none';
|
row.style.display = visible ? '' : 'none';
|
||||||
});
|
});
|
||||||
|
|
||||||
|
const visibleCheckboxes = Array.from(document.querySelectorAll('.table tbody tr'))
|
||||||
|
.filter(row => row.style.display !== 'none')
|
||||||
|
.map(row => row.querySelector('.row-checkbox'))
|
||||||
|
.filter(cb => cb !== null);
|
||||||
|
|
||||||
|
selectAllCheckbox.checked =
|
||||||
|
visibleCheckboxes.length > 0 &&
|
||||||
|
visibleCheckboxes.every(cb => cb.checked);
|
||||||
|
|
||||||
|
updateProceedButton();
|
||||||
}
|
}
|
||||||
|
|
||||||
filterInputs.forEach(input => {
|
filterInputs.forEach(input => {
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
@@ -10,6 +10,9 @@ error_reporting(E_ALL | E_STRICT);
|
|||||||
include('../../extra/auth.php');
|
include('../../extra/auth.php');
|
||||||
//require_once __DIR__ . '/extra/auth.php';
|
//require_once __DIR__ . '/extra/auth.php';
|
||||||
|
|
||||||
|
// Laravel bootstrap (loaded by auth.php) forces UTC via config/app.php — re-apply our TZ
|
||||||
|
date_default_timezone_set($_ENV['APP_TIMEZONE'] ?? 'Europe/Rome');
|
||||||
|
|
||||||
// Here we just check if user is not
|
// Here we just check if user is not
|
||||||
// logged in, and in that case we redirect
|
// logged in, and in that case we redirect
|
||||||
// the user to vanguard login page.
|
// the user to vanguard login page.
|
||||||
@@ -26,6 +29,8 @@ $nameuser = $user->present()->first_name;
|
|||||||
$surnameuser = $user->present()->last_name;
|
$surnameuser = $user->present()->last_name;
|
||||||
$emailuser = $user->present()->email;
|
$emailuser = $user->present()->email;
|
||||||
$avatar = $user->present()->avatar;
|
$avatar = $user->present()->avatar;
|
||||||
|
$lims_user_id = $user->lims_user_id ?? '';
|
||||||
|
$lims_global_user_id = $user->lims_global_user_id ?? '';
|
||||||
|
|
||||||
$kindofrole = $user->present()->role_id;
|
$kindofrole = $user->present()->role_id;
|
||||||
|
|
||||||
|
|||||||
@@ -1,11 +1,22 @@
|
|||||||
<?php include('include/headscript.php');
|
<?php include('include/headscript.php');
|
||||||
|
|
||||||
// Recupera tutte le routine dal database
|
// Retrieve all routines from database
|
||||||
$db = DBHandlerSelect::getInstance();
|
$db = DBHandlerSelect::getInstance();
|
||||||
$pdo = $db->getConnection();
|
$pdo = $db->getConnection();
|
||||||
|
|
||||||
$stmt = $pdo->prepare("SELECT * FROM routine");
|
$stmt = $pdo->prepare("SELECT * FROM routine");
|
||||||
$stmt->execute();
|
$stmt->execute();
|
||||||
$routines = $stmt->fetchAll(PDO::FETCH_ASSOC);
|
$routines = $stmt->fetchAll(PDO::FETCH_ASSOC);
|
||||||
|
|
||||||
|
// Retrieve active API/JSON configurations
|
||||||
|
$stmt = $pdo->prepare("
|
||||||
|
SELECT id, name, provider_code, api_type, php_class_name
|
||||||
|
FROM api_configurations
|
||||||
|
WHERE is_active = 1
|
||||||
|
ORDER BY name ASC
|
||||||
|
");
|
||||||
|
$stmt->execute();
|
||||||
|
$apiConfigurations = $stmt->fetchAll(PDO::FETCH_ASSOC);
|
||||||
?>
|
?>
|
||||||
<!doctype html>
|
<!doctype html>
|
||||||
<html lang="en">
|
<html lang="en">
|
||||||
@@ -18,26 +29,30 @@ $routines = $stmt->fetchAll(PDO::FETCH_ASSOC);
|
|||||||
<?php include('cssinclude.php'); ?>
|
<?php include('cssinclude.php'); ?>
|
||||||
<script src="https://code.jquery.com/jquery-3.6.0.min.js" integrity="sha256-/xUj+3OJU5yExlq6GSYGSHk7tPXikynS7ogEvDej/m4=" crossorigin="anonymous"></script>
|
<script src="https://code.jquery.com/jquery-3.6.0.min.js" integrity="sha256-/xUj+3OJU5yExlq6GSYGSHk7tPXikynS7ogEvDej/m4=" crossorigin="anonymous"></script>
|
||||||
<script src="https://cdn.jsdelivr.net/npm/select2@4.1.0-rc.0/dist/js/select2.min.js"></script>
|
<script src="https://cdn.jsdelivr.net/npm/select2@4.1.0-rc.0/dist/js/select2.min.js"></script>
|
||||||
<title>Insert XLS Template <?= htmlspecialchars($titlewebsite, ENT_QUOTES, 'UTF-8'); ?></title>
|
<title>Insert Template <?= htmlspecialchars($titlewebsite, ENT_QUOTES, 'UTF-8'); ?></title>
|
||||||
</head>
|
</head>
|
||||||
|
|
||||||
<body>
|
<body>
|
||||||
<div class="wrapper">
|
<div class="wrapper">
|
||||||
<?php include('include/navbar.php'); ?>
|
<?php include('include/navbar.php'); ?>
|
||||||
<?php include('include/topbar.php'); ?>
|
<?php include('include/topbar.php'); ?>
|
||||||
|
|
||||||
<div class="page-wrapper">
|
<div class="page-wrapper">
|
||||||
<div class="page-content">
|
<div class="page-content">
|
||||||
|
|
||||||
<div class="card mb-4">
|
<div class="card mb-4">
|
||||||
<div class="card-header">
|
<div class="card-header">
|
||||||
<h5 class="mb-0">Insert new XLS Template</h5>
|
<h5 class="mb-0">Insert New Template</h5>
|
||||||
</div>
|
</div>
|
||||||
<div class="card-body">
|
<div class="card-body">
|
||||||
<p class="mb-2">Fill the following form in order to create a new import XLS template</p>
|
<p class="mb-2">Fill the following form in order to create a new import template</p>
|
||||||
<p class="mb-2">Mandatory Fields</p>
|
<p class="mb-2">Mandatory Fields</p>
|
||||||
<ul class="mb-0">
|
<ul class="mb-0">
|
||||||
<li>Template Name</li>
|
<li>Template Name</li>
|
||||||
<li>Row Header and Column Header: where the title of the excel starts</li>
|
<li>Source Type</li>
|
||||||
<li>Schema and client</li>
|
<li>Schema and Client</li>
|
||||||
|
<li>Row Header, Column Header and Sheet Number only for XLS templates</li>
|
||||||
|
<li>API / JSON Configuration only for API / JSON templates</li>
|
||||||
</ul>
|
</ul>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -50,22 +65,86 @@ $routines = $stmt->fetchAll(PDO::FETCH_ASSOC);
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="card-body">
|
<div class="card-body">
|
||||||
<div class="col-12">
|
<div class="col-12">
|
||||||
<form id="insertTemplateForm" method="POST">
|
<form id="insertTemplateForm" method="POST">
|
||||||
|
|
||||||
<div class="mb-3">
|
<div class="mb-3">
|
||||||
<label class="form-label"><?= htmlspecialchars($templatename, ENT_QUOTES, 'UTF-8'); ?> *</label>
|
<label class="form-label"><?= htmlspecialchars($templatename, ENT_QUOTES, 'UTF-8'); ?> *</label>
|
||||||
<input type="text" name="name" class="form-control" required>
|
<input type="text" name="name" class="form-control" required>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="mb-3">
|
<div class="mb-3">
|
||||||
<label class="form-label"><?= htmlspecialchars($rowheader, ENT_QUOTES, 'UTF-8'); ?> *</label>
|
<label class="form-label">Source Type *</label>
|
||||||
<input type="number" name="header_row" class="form-control" required>
|
<select name="source_type" id="sourceType" class="form-control" required>
|
||||||
|
<option value="XLS" selected>XLS</option>
|
||||||
|
<option value="API">API / JSON</option>
|
||||||
|
<option value="PDF">PDF</option>
|
||||||
|
</select>
|
||||||
|
<small class="text-muted">Choose the source used by this template</small>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="mb-3">
|
<div class="mb-3" id="headerRowWrapper">
|
||||||
|
<label class="form-label"><?= htmlspecialchars($rowheader, ENT_QUOTES, 'UTF-8'); ?> *</label>
|
||||||
|
<input type="number" name="header_row" id="headerRow" class="form-control" value="1" required>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="mb-3" id="startColumnWrapper">
|
||||||
<label class="form-label"><?= htmlspecialchars($columnheader, ENT_QUOTES, 'UTF-8'); ?> *</label>
|
<label class="form-label"><?= htmlspecialchars($columnheader, ENT_QUOTES, 'UTF-8'); ?> *</label>
|
||||||
<input type="text" name="start_column" class="form-control" required>
|
<input type="text" name="start_column" id="startColumn" class="form-control" value="A" required>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="mb-3" id="xlsSheetNumberWrapper">
|
||||||
|
<label class="form-label">XLS Sheet Number</label>
|
||||||
|
<input
|
||||||
|
type="number"
|
||||||
|
name="xls_sheet_index"
|
||||||
|
id="xlsSheetIndex"
|
||||||
|
class="form-control"
|
||||||
|
min="0"
|
||||||
|
value="0">
|
||||||
|
<small class="text-muted">
|
||||||
|
Use 0 for the first sheet, 1 for the second sheet, 2 for the third sheet, and so on.
|
||||||
|
</small>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="mb-3" id="apiConfigWrapper" style="display: none;">
|
||||||
|
<label class="form-label">API / JSON Configuration *</label>
|
||||||
|
<select name="api_config_id" id="apiConfigSelect" class="form-control">
|
||||||
|
<option value="">Select an API configuration...</option>
|
||||||
|
|
||||||
|
<?php foreach ($apiConfigurations as $apiConfig): ?>
|
||||||
|
<?php
|
||||||
|
$apiLabelParts = [];
|
||||||
|
|
||||||
|
if (!empty($apiConfig['name'])) {
|
||||||
|
$apiLabelParts[] = $apiConfig['name'];
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!empty($apiConfig['provider_code'])) {
|
||||||
|
$apiLabelParts[] = '[' . $apiConfig['provider_code'] . ']';
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!empty($apiConfig['api_type'])) {
|
||||||
|
$apiLabelParts[] = '(' . $apiConfig['api_type'] . ')';
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!empty($apiConfig['php_class_name'])) {
|
||||||
|
$apiLabelParts[] = '- ' . $apiConfig['php_class_name'];
|
||||||
|
}
|
||||||
|
|
||||||
|
$apiLabel = implode(' ', $apiLabelParts);
|
||||||
|
?>
|
||||||
|
|
||||||
|
<option value="<?php echo (int)$apiConfig['id']; ?>">
|
||||||
|
<?php echo htmlspecialchars($apiLabel, ENT_QUOTES, 'UTF-8'); ?>
|
||||||
|
</option>
|
||||||
|
<?php endforeach; ?>
|
||||||
|
</select>
|
||||||
|
<small class="text-muted">
|
||||||
|
Select the API/JSON configuration linked to this template.
|
||||||
|
</small>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="mb-3">
|
<div class="mb-3">
|
||||||
@@ -86,12 +165,12 @@ $routines = $stmt->fetchAll(PDO::FETCH_ASSOC);
|
|||||||
|
|
||||||
<div class="mb-3">
|
<div class="mb-3">
|
||||||
<label class="form-label">Button Background Color</label>
|
<label class="form-label">Button Background Color</label>
|
||||||
<input type="color" name="button_bg_color" class="form-control" value="#007bff">
|
<input type="color" name="button_bg_color" class="form-control form-control-color" value="#007bff">
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="mb-3">
|
<div class="mb-3">
|
||||||
<label class="form-label">Button Text Color</label>
|
<label class="form-label">Button Text Color</label>
|
||||||
<input type="color" name="button_text_color" class="form-control" value="#ffffff">
|
<input type="color" name="button_text_color" class="form-control form-control-color" value="#ffffff">
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="mb-3">
|
<div class="mb-3">
|
||||||
@@ -125,6 +204,7 @@ $routines = $stmt->fetchAll(PDO::FETCH_ASSOC);
|
|||||||
</option>
|
</option>
|
||||||
<?php endforeach; ?>
|
<?php endforeach; ?>
|
||||||
</select>
|
</select>
|
||||||
|
|
||||||
<div id="routineDetails" class="mt-2" style="display: none;">
|
<div id="routineDetails" class="mt-2" style="display: none;">
|
||||||
<h6>Routine Details</h6>
|
<h6>Routine Details</h6>
|
||||||
<p><strong>Name:</strong> <span id="routineName"></span></p>
|
<p><strong>Name:</strong> <span id="routineName"></span></p>
|
||||||
@@ -142,8 +222,10 @@ $routines = $stmt->fetchAll(PDO::FETCH_ASSOC);
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="overlay toggle-icon"></div>
|
<div class="overlay toggle-icon"></div>
|
||||||
<a href="javaScript:;" class="back-to-top"><i class='bx bxs-up-arrow-alt'></i></a>
|
<a href="javaScript:;" class="back-to-top"><i class='bx bxs-up-arrow-alt'></i></a>
|
||||||
<?php include('include/footer.php'); ?>
|
<?php include('include/footer.php'); ?>
|
||||||
@@ -167,6 +249,18 @@ $routines = $stmt->fetchAll(PDO::FETCH_ASSOC);
|
|||||||
const routineAction2 = document.getElementById("routineAction2");
|
const routineAction2 = document.getElementById("routineAction2");
|
||||||
const routineAction3 = document.getElementById("routineAction3");
|
const routineAction3 = document.getElementById("routineAction3");
|
||||||
|
|
||||||
|
const sourceType = document.getElementById("sourceType");
|
||||||
|
|
||||||
|
const headerRowWrapper = document.getElementById("headerRowWrapper");
|
||||||
|
const startColumnWrapper = document.getElementById("startColumnWrapper");
|
||||||
|
const xlsSheetNumberWrapper = document.getElementById("xlsSheetNumberWrapper");
|
||||||
|
const apiConfigWrapper = document.getElementById("apiConfigWrapper");
|
||||||
|
|
||||||
|
const headerRow = document.getElementById("headerRow");
|
||||||
|
const startColumn = document.getElementById("startColumn");
|
||||||
|
const xlsSheetIndex = document.getElementById("xlsSheetIndex");
|
||||||
|
const apiConfigSelect = document.getElementById("apiConfigSelect");
|
||||||
|
|
||||||
if (!form || !clientLoadingStatus || !schemaLoadingStatus || !routineSelect || !routineDetails) {
|
if (!form || !clientLoadingStatus || !schemaLoadingStatus || !routineSelect || !routineDetails) {
|
||||||
alert("Errore: Uno o più elementi della pagina non sono stati trovati. Contatta l'amministratore.");
|
alert("Errore: Uno o più elementi della pagina non sono stati trovati. Contatta l'amministratore.");
|
||||||
return;
|
return;
|
||||||
@@ -187,30 +281,101 @@ $routines = $stmt->fetchAll(PDO::FETCH_ASSOC);
|
|||||||
allowClear: true
|
allowClear: true
|
||||||
});
|
});
|
||||||
|
|
||||||
|
$('#apiConfigSelect').select2({
|
||||||
|
placeholder: "Select an API configuration...",
|
||||||
|
allowClear: true
|
||||||
|
});
|
||||||
|
|
||||||
|
function updateSourceFields() {
|
||||||
|
const selectedSource = sourceType.value;
|
||||||
|
|
||||||
|
const isXls = selectedSource === 'XLS';
|
||||||
|
const isApiJson = selectedSource === 'API';
|
||||||
|
|
||||||
|
if (isXls) {
|
||||||
|
headerRowWrapper.style.display = 'block';
|
||||||
|
startColumnWrapper.style.display = 'block';
|
||||||
|
xlsSheetNumberWrapper.style.display = 'block';
|
||||||
|
|
||||||
|
headerRow.required = true;
|
||||||
|
startColumn.required = true;
|
||||||
|
xlsSheetIndex.required = true;
|
||||||
|
|
||||||
|
headerRow.disabled = false;
|
||||||
|
startColumn.disabled = false;
|
||||||
|
xlsSheetIndex.disabled = false;
|
||||||
|
|
||||||
|
apiConfigWrapper.style.display = 'none';
|
||||||
|
apiConfigSelect.required = false;
|
||||||
|
apiConfigSelect.disabled = true;
|
||||||
|
$('#apiConfigSelect').val(null).trigger('change');
|
||||||
|
} else {
|
||||||
|
headerRowWrapper.style.display = 'none';
|
||||||
|
startColumnWrapper.style.display = 'none';
|
||||||
|
xlsSheetNumberWrapper.style.display = 'none';
|
||||||
|
|
||||||
|
headerRow.required = false;
|
||||||
|
startColumn.required = false;
|
||||||
|
xlsSheetIndex.required = false;
|
||||||
|
|
||||||
|
headerRow.disabled = true;
|
||||||
|
startColumn.disabled = true;
|
||||||
|
xlsSheetIndex.disabled = true;
|
||||||
|
|
||||||
|
if (isApiJson) {
|
||||||
|
apiConfigWrapper.style.display = 'block';
|
||||||
|
apiConfigSelect.required = true;
|
||||||
|
apiConfigSelect.disabled = false;
|
||||||
|
} else {
|
||||||
|
apiConfigWrapper.style.display = 'none';
|
||||||
|
apiConfigSelect.required = false;
|
||||||
|
apiConfigSelect.disabled = true;
|
||||||
|
$('#apiConfigSelect').val(null).trigger('change');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
sourceType.addEventListener('change', updateSourceFields);
|
||||||
|
updateSourceFields();
|
||||||
|
|
||||||
async function loadClients() {
|
async function loadClients() {
|
||||||
try {
|
try {
|
||||||
clientLoadingStatus.style.display = 'inline';
|
clientLoadingStatus.style.display = 'inline';
|
||||||
clientLoadingStatus.textContent = 'Recupero clienti in corso...';
|
clientLoadingStatus.textContent = 'Recupero clienti in corso...';
|
||||||
|
|
||||||
const response = await fetch("get_clienti.php", {
|
const response = await fetch("get_clienti.php", {
|
||||||
method: "GET",
|
method: "GET",
|
||||||
headers: {
|
headers: {
|
||||||
"Content-Type": "application/json"
|
"Content-Type": "application/json"
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
const data = await response.json();
|
const data = await response.json();
|
||||||
if (!response.ok) throw new Error(data.error || `Errore HTTP: ${response.status}`);
|
|
||||||
|
if (!response.ok) {
|
||||||
|
throw new Error(data.error || `Errore HTTP: ${response.status}`);
|
||||||
|
}
|
||||||
|
|
||||||
const select = document.getElementById("clientSelect");
|
const select = document.getElementById("clientSelect");
|
||||||
select.innerHTML = '<option value="">Select a client...</option>';
|
select.innerHTML = '<option value="">Select a client...</option>';
|
||||||
|
|
||||||
data.value.forEach(client => {
|
data.value.forEach(client => {
|
||||||
const nome = client.Nominativo || "Nome non disponibile";
|
const nome = client.Nominativo || "Nome non disponibile";
|
||||||
const id = client.IdCliente || "ID non disponibile";
|
const id = client.IdCliente || "ID non disponibile";
|
||||||
const option = new Option(`${nome.trim()} (ID: ${id})`, id);
|
|
||||||
|
const codiceCliente = (client.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);
|
||||||
select.add(option);
|
select.add(option);
|
||||||
});
|
});
|
||||||
|
|
||||||
$(select).trigger('change');
|
$(select).trigger('change');
|
||||||
clientLoadingStatus.textContent = "Clienti caricati.";
|
clientLoadingStatus.textContent = "Clienti caricati.";
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
clientLoadingStatus.textContent = "Errore nel caricamento.";
|
clientLoadingStatus.textContent = "Errore nel caricamento.";
|
||||||
|
|
||||||
Swal.fire({
|
Swal.fire({
|
||||||
title: "Errore!",
|
title: "Errore!",
|
||||||
text: "Impossibile caricare i clienti: " + error.message,
|
text: "Impossibile caricare i clienti: " + error.message,
|
||||||
@@ -226,26 +391,43 @@ $routines = $stmt->fetchAll(PDO::FETCH_ASSOC);
|
|||||||
try {
|
try {
|
||||||
schemaLoadingStatus.style.display = 'inline';
|
schemaLoadingStatus.style.display = 'inline';
|
||||||
schemaLoadingStatus.textContent = 'Caricamento schemi in corso...';
|
schemaLoadingStatus.textContent = 'Caricamento schemi in corso...';
|
||||||
|
|
||||||
const response = await fetch("get_schemi.php", {
|
const response = await fetch("get_schemi.php", {
|
||||||
method: "GET",
|
method: "GET",
|
||||||
headers: {
|
headers: {
|
||||||
"Content-Type": "application/json"
|
"Content-Type": "application/json"
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
const data = await response.json();
|
const data = await response.json();
|
||||||
if (!response.ok) throw new Error(data.error || `Errore HTTP: ${response.status}`);
|
|
||||||
|
if (!response.ok) {
|
||||||
|
throw new Error(data.error || `Errore HTTP: ${response.status}`);
|
||||||
|
}
|
||||||
|
|
||||||
const select = document.getElementById("schemaSelect");
|
const select = document.getElementById("schemaSelect");
|
||||||
select.innerHTML = '<option value="">Select a schema...</option>';
|
select.innerHTML = '<option value="">Select a schema...</option>';
|
||||||
data.value.forEach(schema => {
|
|
||||||
|
const sortedSchemas = [...data.value].sort((a, b) => {
|
||||||
|
const nomeA = (a.Nome || "").trim().toLowerCase();
|
||||||
|
const nomeB = (b.Nome || "").trim().toLowerCase();
|
||||||
|
return nomeA.localeCompare(nomeB, 'it', {
|
||||||
|
sensitivity: 'base'
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
sortedSchemas.forEach(schema => {
|
||||||
const nome = schema.Nome || "Nome non disponibile";
|
const nome = schema.Nome || "Nome non disponibile";
|
||||||
const id = schema.IdSchemaCustomFields || "ID non disponibile";
|
const id = schema.IdSchemaCustomFields || "ID non disponibile";
|
||||||
const option = new Option(`${nome.trim()} (ID: ${id})`, id);
|
const option = new Option(`${nome.trim()} (ID: ${id})`, id);
|
||||||
select.add(option);
|
select.add(option);
|
||||||
});
|
});
|
||||||
|
|
||||||
$(select).trigger('change');
|
$(select).trigger('change');
|
||||||
schemaLoadingStatus.textContent = "Schemi caricati.";
|
schemaLoadingStatus.textContent = "Schemi caricati.";
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
schemaLoadingStatus.textContent = "Errore nel caricamento.";
|
schemaLoadingStatus.textContent = "Errore nel caricamento.";
|
||||||
|
|
||||||
Swal.fire({
|
Swal.fire({
|
||||||
title: "Errore!",
|
title: "Errore!",
|
||||||
text: "Impossibile caricare gli schemi: " + error.message,
|
text: "Impossibile caricare gli schemi: " + error.message,
|
||||||
@@ -270,6 +452,7 @@ $routines = $stmt->fetchAll(PDO::FETCH_ASSOC);
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
loadData();
|
loadData();
|
||||||
|
|
||||||
const routines = <?php echo json_encode($routines); ?>;
|
const routines = <?php echo json_encode($routines); ?>;
|
||||||
@@ -277,8 +460,10 @@ $routines = $stmt->fetchAll(PDO::FETCH_ASSOC);
|
|||||||
function updateRoutineDetails() {
|
function updateRoutineDetails() {
|
||||||
const selectedId = routineSelect.value;
|
const selectedId = routineSelect.value;
|
||||||
routineDetails.style.display = selectedId ? 'block' : 'none';
|
routineDetails.style.display = selectedId ? 'block' : 'none';
|
||||||
|
|
||||||
if (selectedId) {
|
if (selectedId) {
|
||||||
const routine = routines.find(r => r.idroutine == selectedId);
|
const routine = routines.find(r => r.idroutine == selectedId);
|
||||||
|
|
||||||
if (routine) {
|
if (routine) {
|
||||||
routineName.textContent = routine.name || 'N/A';
|
routineName.textContent = routine.name || 'N/A';
|
||||||
routineDescription.textContent = routine.description || 'N/A';
|
routineDescription.textContent = routine.description || 'N/A';
|
||||||
@@ -300,6 +485,7 @@ $routines = $stmt->fetchAll(PDO::FETCH_ASSOC);
|
|||||||
routineAction3.textContent = '';
|
routineAction3.textContent = '';
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
routineSelect.addEventListener('change', updateRoutineDetails);
|
routineSelect.addEventListener('change', updateRoutineDetails);
|
||||||
updateRoutineDetails();
|
updateRoutineDetails();
|
||||||
|
|
||||||
@@ -308,6 +494,28 @@ $routines = $stmt->fetchAll(PDO::FETCH_ASSOC);
|
|||||||
|
|
||||||
let formData = new FormData(this);
|
let formData = new FormData(this);
|
||||||
|
|
||||||
|
const selectedSource = sourceType.value;
|
||||||
|
|
||||||
|
if (selectedSource === 'XLS' && xlsSheetIndex.value === '') {
|
||||||
|
Swal.fire({
|
||||||
|
title: "Errore!",
|
||||||
|
text: "Inserisci il numero del foglio XLS.",
|
||||||
|
icon: "error",
|
||||||
|
confirmButtonText: "OK"
|
||||||
|
});
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (selectedSource === 'API' && !apiConfigSelect.value) {
|
||||||
|
Swal.fire({
|
||||||
|
title: "Errore!",
|
||||||
|
text: "Seleziona una configurazione API / JSON.",
|
||||||
|
icon: "error",
|
||||||
|
confirmButtonText: "OK"
|
||||||
|
});
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
const clientSelect = document.getElementById("clientSelect");
|
const clientSelect = document.getElementById("clientSelect");
|
||||||
const clientId = clientSelect.value;
|
const clientId = clientSelect.value;
|
||||||
const selectedClientOption = clientSelect.options[clientSelect.selectedIndex];
|
const selectedClientOption = clientSelect.options[clientSelect.selectedIndex];
|
||||||
@@ -350,6 +558,7 @@ $routines = $stmt->fetchAll(PDO::FETCH_ASSOC);
|
|||||||
const nameMatch = optionText.match(/^(.+?)(?:\s*\(ID:\s*\d+\))?$/);
|
const nameMatch = optionText.match(/^(.+?)(?:\s*\(ID:\s*\d+\))?$/);
|
||||||
schemaName = nameMatch ? nameMatch[1].trim() : optionText;
|
schemaName = nameMatch ? nameMatch[1].trim() : optionText;
|
||||||
}
|
}
|
||||||
|
|
||||||
formData.append("idschema", schemaId);
|
formData.append("idschema", schemaId);
|
||||||
formData.append("schemaname", schemaName);
|
formData.append("schemaname", schemaName);
|
||||||
|
|
||||||
|
|||||||
@@ -13,7 +13,10 @@ try {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Recupera solo i template attivi
|
// Recupera solo i template attivi
|
||||||
$stmt = $pdo->query("SELECT id, button_label, button_bg_color, button_text_color, button_size FROM excel_templates WHERE status = 'active'");
|
$stmt = $pdo->query("SELECT id, button_label, button_size, button_bg_color, button_text_color, source_type
|
||||||
|
FROM excel_templates
|
||||||
|
WHERE status = 'active'
|
||||||
|
ORDER BY button_label ASC");
|
||||||
$templates = $stmt->fetchAll(PDO::FETCH_ASSOC);
|
$templates = $stmt->fetchAll(PDO::FETCH_ASSOC);
|
||||||
|
|
||||||
$response["success"] = true;
|
$response["success"] = true;
|
||||||
|
|||||||
@@ -33,7 +33,7 @@ try {
|
|||||||
if ($extraFieldId) {
|
if ($extraFieldId) {
|
||||||
$stmt = $pdo->prepare("
|
$stmt = $pdo->prepare("
|
||||||
SELECT
|
SELECT
|
||||||
p.id, p.iddatadb, p.part_number, p.part_description, p.idmatrice, p.note, p.dateexpiry,
|
p.id, p.iddatadb, p.part_number, p.part_description, p.mix, p.idmatrice, p.note, p.dateexpiry,
|
||||||
cf.value_id AS extra_value_id,
|
cf.value_id AS extra_value_id,
|
||||||
cf.value_text AS extra_value_text
|
cf.value_text AS extra_value_text
|
||||||
FROM identification_parts p
|
FROM identification_parts p
|
||||||
@@ -48,7 +48,7 @@ try {
|
|||||||
]);
|
]);
|
||||||
} else {
|
} else {
|
||||||
$stmt = $pdo->prepare("
|
$stmt = $pdo->prepare("
|
||||||
SELECT id, iddatadb, part_number, part_description, idmatrice, note, dateexpiry,
|
SELECT id, iddatadb, part_number, part_description, mix, idmatrice, note, dateexpiry,
|
||||||
NULL AS extra_value_id, NULL AS extra_value_text
|
NULL AS extra_value_id, NULL AS extra_value_text
|
||||||
FROM identification_parts
|
FROM identification_parts
|
||||||
WHERE iddatadb = :iddatadb
|
WHERE iddatadb = :iddatadb
|
||||||
|
|||||||
@@ -0,0 +1,60 @@
|
|||||||
|
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": 564663,
|
||||||
|
"Matrice": 3028,
|
||||||
|
"SottoMatrice": null,
|
||||||
|
"SchemaCustomField": 82,
|
||||||
|
"NoteWeb": "Parte 1",
|
||||||
|
"ConsegnaRichiesta": "2026-03-31"
|
||||||
|
}'
|
||||||
|
|
||||||
|
RESPONSE:
|
||||||
|
{
|
||||||
|
"@odata.context": "https:\/\/bvcpsitaly-elims.com\/limsapi\/api\/odata\/$metadata#Campione\/$entity",
|
||||||
|
"IdCampione": 736244,
|
||||||
|
"CodiceCampione": "11004",
|
||||||
|
"CodiceCampioneWeb": "11004",
|
||||||
|
"StatoCampione": "Nuovo",
|
||||||
|
"DataCreazioneWeb": "2026-03-17T14:52:30.3143366+01:00",
|
||||||
|
"RiferimentoWeb": "",
|
||||||
|
"ConsegnaRichiesta": "2026-03-31T00:00:00+02:00",
|
||||||
|
"DataAccettazioneLims": null,
|
||||||
|
"Riferimento": null,
|
||||||
|
"NoteWeb": "Parte 1",
|
||||||
|
"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": 564663,
|
||||||
|
"Matrice": 3028,
|
||||||
|
"SottoMatrice": null,
|
||||||
|
"SchemaCustomField": 82,
|
||||||
|
"NoteWeb": "Parte 2",
|
||||||
|
"ConsegnaRichiesta": "2026-03-31"
|
||||||
|
}'
|
||||||
|
|
||||||
|
RESPONSE:
|
||||||
|
{
|
||||||
|
"@odata.context": "https:\/\/bvcpsitaly-elims.com\/limsapi\/api\/odata\/$metadata#Campione\/$entity",
|
||||||
|
"IdCampione": 736245,
|
||||||
|
"CodiceCampione": "11005",
|
||||||
|
"CodiceCampioneWeb": "11005",
|
||||||
|
"StatoCampione": "Nuovo",
|
||||||
|
"DataCreazioneWeb": "2026-03-17T14:52:31.9631757+01:00",
|
||||||
|
"RiferimentoWeb": "",
|
||||||
|
"ConsegnaRichiesta": "2026-03-31T00:00:00+02:00",
|
||||||
|
"DataAccettazioneLims": null,
|
||||||
|
"Riferimento": null,
|
||||||
|
"NoteWeb": "Parte 2",
|
||||||
|
"GruppiRicercati": null
|
||||||
|
}
|
||||||
|
|
||||||
|
---
|
||||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,20 @@
|
|||||||
|
curl --location --request POST 'https://93.43.5.102/limsapi/api/odata/CommessaWeb(564663)/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": 564663,
|
||||||
|
"CodiceCommessa": "26C0059",
|
||||||
|
"RiferimentoCertificato": null,
|
||||||
|
"StatoCommessaWeb": "Elaborata",
|
||||||
|
"DataCreazioneWeb": "2026-03-17T14:52:29.347+01:00",
|
||||||
|
"CodiceCommessaWeb": "26C0059",
|
||||||
|
"DataInviatoWeb": "2026-03-17T14:52:38.99+01:00",
|
||||||
|
"Richiedente": "From TRFSmart Application",
|
||||||
|
"Descrizione": "From TRFSmart Application"
|
||||||
|
}
|
||||||
@@ -0,0 +1,48 @@
|
|||||||
|
Photos for CommessaWeb 564663 (iddatadb=1268):
|
||||||
|
Total photos found: 2, campioni: 2
|
||||||
|
|
||||||
|
=== Campione 736244 (main) ===
|
||||||
|
curl --location --request POST 'https://93.43.5.102/limsapi/api/odata/Campione(736244)/UploadCampioneFile' \
|
||||||
|
--header 'Authorization: Bearer ••••••' \
|
||||||
|
--form 'file=@C:\xampp\htdocs\trf_certest\public\photostrf\1268-20260317134828-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": 1784768,
|
||||||
|
"FileName": "1268-20260317134828-1e672dd9-5420-4432-b422-02d8d271c178.jpg",
|
||||||
|
"Web": false,
|
||||||
|
"AllegaAlRapporto": false,
|
||||||
|
"StampaNelRapporto": false,
|
||||||
|
"ReportGermania": false,
|
||||||
|
"PrimaPagina": false,
|
||||||
|
"Duplica": false,
|
||||||
|
"LastUpdate": "2026-03-17T14:52:33.9816658+01:00",
|
||||||
|
"Titolo": null
|
||||||
|
}
|
||||||
|
|
||||||
|
---
|
||||||
|
curl --location --request POST 'https://93.43.5.102/limsapi/api/odata/Campione(736244)/UploadCampioneFile' \
|
||||||
|
--header 'Authorization: Bearer ••••••' \
|
||||||
|
--form 'file=@C:\xampp\htdocs\trf_certest\public\photostrf\1268-20260317134828-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": 1784769,
|
||||||
|
"FileName": "1268-20260317134828-104a2ca5-8a4c-4df8-b2ca-a28f518b7b25.jpg",
|
||||||
|
"Web": false,
|
||||||
|
"AllegaAlRapporto": false,
|
||||||
|
"StampaNelRapporto": false,
|
||||||
|
"ReportGermania": false,
|
||||||
|
"PrimaPagina": false,
|
||||||
|
"Duplica": false,
|
||||||
|
"LastUpdate": "2026-03-17T14:52:35.2759882+01:00",
|
||||||
|
"Titolo": null
|
||||||
|
}
|
||||||
|
|
||||||
|
---
|
||||||
@@ -0,0 +1,18 @@
|
|||||||
|
curl --location --request POST 'https://93.43.5.102/limsapi/api/odata/CommessaWeb(564663)/InviaCommessa' \
|
||||||
|
--header 'Content-Type: application/json' \
|
||||||
|
--header 'Authorization: Bearer ••••••' \
|
||||||
|
--data '{}'
|
||||||
|
|
||||||
|
RESPONSE:
|
||||||
|
{
|
||||||
|
"@odata.context": "https:\/\/bvcpsitaly-elims.com\/limsapi\/api\/odata\/$metadata#CommessaWeb\/$entity",
|
||||||
|
"IdCommessa": 564663,
|
||||||
|
"CodiceCommessa": "26C0059",
|
||||||
|
"RiferimentoCertificato": null,
|
||||||
|
"StatoCommessaWeb": "Nuova",
|
||||||
|
"DataCreazioneWeb": "2026-03-17T14:52:29.347+01:00",
|
||||||
|
"CodiceCommessaWeb": "26C0059",
|
||||||
|
"DataInviatoWeb": "2026-03-17T14:52:38.9894528+01:00",
|
||||||
|
"Richiedente": "From TRFSmart Application",
|
||||||
|
"Descrizione": "From TRFSmart Application"
|
||||||
|
}
|
||||||
@@ -0,0 +1,226 @@
|
|||||||
|
curl --location --request PATCH 'https://93.43.5.102/limsapi/api/odata/CommessaWeb(564663)' \
|
||||||
|
--header 'Content-Type: application/json' \
|
||||||
|
--header 'Authorization: Bearer ••••••' \
|
||||||
|
--data '{
|
||||||
|
"CommesseCustomFields": [
|
||||||
|
{
|
||||||
|
"IdCommesseCustomFields": 23308665,
|
||||||
|
"Valore": "2026-03-17T13:52:36+00:00"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"IdCommesseCustomFields": 23308666,
|
||||||
|
"Valore": "13:52"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"IdCommesseCustomFields": 23308667,
|
||||||
|
"Valore": "9299"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"IdCommesseCustomFields": 23308668,
|
||||||
|
"Valore": "2026-03-17T13:52:36+00:00"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"IdCommesseCustomFields": 23308669,
|
||||||
|
"Valore": "aaa"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"IdCommesseCustomFields": 23308670,
|
||||||
|
"Valore": "1"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"IdCommesseCustomFields": 23308676,
|
||||||
|
"Valore": "673"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"IdCommesseCustomFields": 23308677,
|
||||||
|
"Valore": "674"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"IdCommesseCustomFields": 23308678,
|
||||||
|
"Valore": "4410"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"IdCommesseCustomFields": 23308680,
|
||||||
|
"Valore": "1468"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"IdCommesseCustomFields": 23308681,
|
||||||
|
"Valore": "1604"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"IdCommesseCustomFields": 23308682,
|
||||||
|
"Valore": "12875"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"IdCommesseCustomFields": 23308683,
|
||||||
|
"Valore": "8755"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"IdCommesseCustomFields": 23308684,
|
||||||
|
"Valore": ""
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"IdCommesseCustomFields": 23308644,
|
||||||
|
"Valore": "13497"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"IdCommesseCustomFields": 23308645,
|
||||||
|
"Valore": "AAA"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"IdCommesseCustomFields": 23308646,
|
||||||
|
"Valore": "Example Description"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"IdCommesseCustomFields": 23308685,
|
||||||
|
"Valore": "1892"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"IdCommesseCustomFields": 23308686,
|
||||||
|
"Valore": "1111"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"IdCommesseCustomFields": 23308687,
|
||||||
|
"Valore": "2026-03-02T13:52:36+00:00"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"IdCommesseCustomFields": 23308688,
|
||||||
|
"Valore": "2026-03-17T13:52:36+00:00"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"IdCommesseCustomFields": 23308689,
|
||||||
|
"Valore": "2026-03-17T13:52:36+00:00"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"IdCommesseCustomFields": 23308690,
|
||||||
|
"Valore": "1978"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"IdCommesseCustomFields": 23308691,
|
||||||
|
"Valore": "13:52"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"IdCommesseCustomFields": 23308692,
|
||||||
|
"Valore": "14:44"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"IdCommesseCustomFields": 23308693,
|
||||||
|
"Valore": "14:00"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"IdCommesseCustomFields": 23308694,
|
||||||
|
"Valore": "2026-03-17T13:52:36+00:00"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"IdCommesseCustomFields": 23308695,
|
||||||
|
"Valore": "8596"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"IdCommesseCustomFields": 23308696,
|
||||||
|
"Valore": "8600"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"IdCommesseCustomFields": 23308697,
|
||||||
|
"Valore": "8605"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"IdCommesseCustomFields": 23308647,
|
||||||
|
"Valore": "L209A4M00130M7280"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"IdCommesseCustomFields": 23308648,
|
||||||
|
"Valore": "1"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"IdCommesseCustomFields": 23308649,
|
||||||
|
"Valore": "1"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"IdCommesseCustomFields": 23308650,
|
||||||
|
"Valore": "264"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"IdCommesseCustomFields": 23308651,
|
||||||
|
"Valore": "1589"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"IdCommesseCustomFields": 23308652,
|
||||||
|
"Valore": "AAA"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"IdCommesseCustomFields": 23308653,
|
||||||
|
"Valore": "3055"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"IdCommesseCustomFields": 23308654,
|
||||||
|
"Valore": "aaa"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"IdCommesseCustomFields": 23308655,
|
||||||
|
"Valore": "qqq"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"IdCommesseCustomFields": 23308656,
|
||||||
|
"Valore": "1"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"IdCommesseCustomFields": 23308657,
|
||||||
|
"Valore": "1"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"IdCommesseCustomFields": 23308658,
|
||||||
|
"Valore": "qqq"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"IdCommesseCustomFields": 23308659,
|
||||||
|
"Valore": "236"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"IdCommesseCustomFields": 23308660,
|
||||||
|
"Valore": "1208"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"IdCommesseCustomFields": 23308661,
|
||||||
|
"Valore": "ciao"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"IdCommesseCustomFields": 23308662,
|
||||||
|
"Valore": "bbb"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"IdCommesseCustomFields": 23308663,
|
||||||
|
"Valore": "12234"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"IdCommesseCustomFields": 23308664,
|
||||||
|
"Valore": "MONCLER"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"IdCommesseCustomFields": 23308671,
|
||||||
|
"Valore": "L209A4M00130M7280 - PACEY2"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"IdCommesseCustomFields": 23308672,
|
||||||
|
"Valore": "4678"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"IdCommesseCustomFields": 23308673,
|
||||||
|
"Valore": "1"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"IdCommesseCustomFields": 23308674,
|
||||||
|
"Valore": ""
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"IdCommesseCustomFields": 23308675,
|
||||||
|
"Valore": "2450"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"IdCommesseCustomFields": 23308679,
|
||||||
|
"Valore": "744"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}'
|
||||||
|
|
||||||
|
RESPONSE:
|
||||||
|
null
|
||||||
@@ -0,0 +1,120 @@
|
|||||||
|
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": 564817,
|
||||||
|
"Matrice": 3028,
|
||||||
|
"SottoMatrice": null,
|
||||||
|
"SchemaCustomField": 82,
|
||||||
|
"NoteWeb": "AAA",
|
||||||
|
"ConsegnaRichiesta": "2026-01-29"
|
||||||
|
}'
|
||||||
|
|
||||||
|
RESPONSE:
|
||||||
|
{
|
||||||
|
"@odata.context": "https:\/\/bvcpsitaly-elims.com\/limsapi\/api\/odata\/$metadata#Campione\/$entity",
|
||||||
|
"IdCampione": 736487,
|
||||||
|
"CodiceCampione": "11016",
|
||||||
|
"CodiceCampioneWeb": "11016",
|
||||||
|
"StatoCampione": "Nuovo",
|
||||||
|
"DataCreazioneWeb": "2026-03-18T10:54:03.4537462+01:00",
|
||||||
|
"RiferimentoWeb": "",
|
||||||
|
"ConsegnaRichiesta": "2026-01-29T00:00:00+01:00",
|
||||||
|
"DataAccettazioneLims": null,
|
||||||
|
"Riferimento": null,
|
||||||
|
"NoteWeb": "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": 564817,
|
||||||
|
"Matrice": 3028,
|
||||||
|
"SottoMatrice": null,
|
||||||
|
"SchemaCustomField": 82,
|
||||||
|
"NoteWeb": "CCC",
|
||||||
|
"ConsegnaRichiesta": "2026-01-29"
|
||||||
|
}'
|
||||||
|
|
||||||
|
RESPONSE:
|
||||||
|
{
|
||||||
|
"@odata.context": "https:\/\/bvcpsitaly-elims.com\/limsapi\/api\/odata\/$metadata#Campione\/$entity",
|
||||||
|
"IdCampione": 736488,
|
||||||
|
"CodiceCampione": "11017",
|
||||||
|
"CodiceCampioneWeb": "11017",
|
||||||
|
"StatoCampione": "Nuovo",
|
||||||
|
"DataCreazioneWeb": "2026-03-18T10:54:04.8623753+01:00",
|
||||||
|
"RiferimentoWeb": "",
|
||||||
|
"ConsegnaRichiesta": "2026-01-29T00:00:00+01:00",
|
||||||
|
"DataAccettazioneLims": null,
|
||||||
|
"Riferimento": null,
|
||||||
|
"NoteWeb": "CCC",
|
||||||
|
"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": 564817,
|
||||||
|
"Matrice": 3028,
|
||||||
|
"SottoMatrice": null,
|
||||||
|
"SchemaCustomField": 82,
|
||||||
|
"NoteWeb": "DDD",
|
||||||
|
"ConsegnaRichiesta": "2026-01-29"
|
||||||
|
}'
|
||||||
|
|
||||||
|
RESPONSE:
|
||||||
|
{
|
||||||
|
"@odata.context": "https:\/\/bvcpsitaly-elims.com\/limsapi\/api\/odata\/$metadata#Campione\/$entity",
|
||||||
|
"IdCampione": 736489,
|
||||||
|
"CodiceCampione": "11018",
|
||||||
|
"CodiceCampioneWeb": "11018",
|
||||||
|
"StatoCampione": "Nuovo",
|
||||||
|
"DataCreazioneWeb": "2026-03-18T10:54:06.2840533+01:00",
|
||||||
|
"RiferimentoWeb": "",
|
||||||
|
"ConsegnaRichiesta": "2026-01-29T00:00:00+01:00",
|
||||||
|
"DataAccettazioneLims": null,
|
||||||
|
"Riferimento": null,
|
||||||
|
"NoteWeb": "DDD",
|
||||||
|
"GruppiRicercati": null
|
||||||
|
}
|
||||||
|
|
||||||
|
---
|
||||||
|
CAMPIONE #3
|
||||||
|
curl --location --request POST 'https://93.43.5.102/limsapi/api/odata/Campione' \
|
||||||
|
--header 'Content-Type: application/json' \
|
||||||
|
--header 'Authorization: Bearer ••••••' \
|
||||||
|
--data '{
|
||||||
|
"Commessa": 564817,
|
||||||
|
"Matrice": 3028,
|
||||||
|
"SottoMatrice": null,
|
||||||
|
"SchemaCustomField": 82,
|
||||||
|
"NoteWeb": "EEE",
|
||||||
|
"ConsegnaRichiesta": "2026-01-29"
|
||||||
|
}'
|
||||||
|
|
||||||
|
RESPONSE:
|
||||||
|
{
|
||||||
|
"@odata.context": "https:\/\/bvcpsitaly-elims.com\/limsapi\/api\/odata\/$metadata#Campione\/$entity",
|
||||||
|
"IdCampione": 736490,
|
||||||
|
"CodiceCampione": "11019",
|
||||||
|
"CodiceCampioneWeb": "11019",
|
||||||
|
"StatoCampione": "Nuovo",
|
||||||
|
"DataCreazioneWeb": "2026-03-18T10:54:07.8853452+01:00",
|
||||||
|
"RiferimentoWeb": "",
|
||||||
|
"ConsegnaRichiesta": "2026-01-29T00:00:00+01:00",
|
||||||
|
"DataAccettazioneLims": null,
|
||||||
|
"Riferimento": null,
|
||||||
|
"NoteWeb": "EEE",
|
||||||
|
"GruppiRicercati": null
|
||||||
|
}
|
||||||
|
|
||||||
|
---
|
||||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,20 @@
|
|||||||
|
curl --location --request POST 'https://93.43.5.102/limsapi/api/odata/CommessaWeb(564817)/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": 564817,
|
||||||
|
"CodiceCommessa": "26C0071",
|
||||||
|
"RiferimentoCertificato": null,
|
||||||
|
"StatoCommessaWeb": "Elaborata",
|
||||||
|
"DataCreazioneWeb": "2026-03-18T10:54:02.187+01:00",
|
||||||
|
"CodiceCommessaWeb": "26C0071",
|
||||||
|
"DataInviatoWeb": "2026-03-18T10:54:14.727+01:00",
|
||||||
|
"Richiedente": "From TRFSmart Application",
|
||||||
|
"Descrizione": "From TRFSmart Application"
|
||||||
|
}
|
||||||
@@ -0,0 +1,48 @@
|
|||||||
|
Photos for CommessaWeb 564817 (iddatadb=1270):
|
||||||
|
Total photos found: 2, campioni: 4
|
||||||
|
|
||||||
|
=== Campione 736487 (main) ===
|
||||||
|
curl --location --request POST 'https://93.43.5.102/limsapi/api/odata/Campione(736487)/UploadCampioneFile' \
|
||||||
|
--header 'Authorization: Bearer ••••••' \
|
||||||
|
--form 'file=@C:\xampp\htdocs\trf_certest\public\photostrf\1270-20260318095305-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": 1785492,
|
||||||
|
"FileName": "1270-20260318095305-1e672dd9-5420-4432-b422-02d8d271c178.jpg",
|
||||||
|
"Web": false,
|
||||||
|
"AllegaAlRapporto": false,
|
||||||
|
"StampaNelRapporto": false,
|
||||||
|
"ReportGermania": false,
|
||||||
|
"PrimaPagina": false,
|
||||||
|
"Duplica": false,
|
||||||
|
"LastUpdate": "2026-03-18T10:54:09.5438326+01:00",
|
||||||
|
"Titolo": null
|
||||||
|
}
|
||||||
|
|
||||||
|
---
|
||||||
|
curl --location --request POST 'https://93.43.5.102/limsapi/api/odata/Campione(736487)/UploadCampioneFile' \
|
||||||
|
--header 'Authorization: Bearer ••••••' \
|
||||||
|
--form 'file=@C:\xampp\htdocs\trf_certest\public\photostrf\1270-20260318095305-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": 1785493,
|
||||||
|
"FileName": "1270-20260318095305-104a2ca5-8a4c-4df8-b2ca-a28f518b7b25.jpg",
|
||||||
|
"Web": false,
|
||||||
|
"AllegaAlRapporto": false,
|
||||||
|
"StampaNelRapporto": false,
|
||||||
|
"ReportGermania": false,
|
||||||
|
"PrimaPagina": false,
|
||||||
|
"Duplica": false,
|
||||||
|
"LastUpdate": "2026-03-18T10:54:10.8237002+01:00",
|
||||||
|
"Titolo": null
|
||||||
|
}
|
||||||
|
|
||||||
|
---
|
||||||
@@ -0,0 +1,18 @@
|
|||||||
|
curl --location --request POST 'https://93.43.5.102/limsapi/api/odata/CommessaWeb(564817)/InviaCommessa' \
|
||||||
|
--header 'Content-Type: application/json' \
|
||||||
|
--header 'Authorization: Bearer ••••••' \
|
||||||
|
--data '{}'
|
||||||
|
|
||||||
|
RESPONSE:
|
||||||
|
{
|
||||||
|
"@odata.context": "https:\/\/bvcpsitaly-elims.com\/limsapi\/api\/odata\/$metadata#CommessaWeb\/$entity",
|
||||||
|
"IdCommessa": 564817,
|
||||||
|
"CodiceCommessa": "26C0071",
|
||||||
|
"RiferimentoCertificato": null,
|
||||||
|
"StatoCommessaWeb": "Nuova",
|
||||||
|
"DataCreazioneWeb": "2026-03-18T10:54:02.187+01:00",
|
||||||
|
"CodiceCommessaWeb": "26C0071",
|
||||||
|
"DataInviatoWeb": "2026-03-18T10:54:14.7252132+01:00",
|
||||||
|
"Richiedente": "From TRFSmart Application",
|
||||||
|
"Descrizione": "From TRFSmart Application"
|
||||||
|
}
|
||||||
@@ -0,0 +1,226 @@
|
|||||||
|
curl --location --request PATCH 'https://93.43.5.102/limsapi/api/odata/CommessaWeb(564817)' \
|
||||||
|
--header 'Content-Type: application/json' \
|
||||||
|
--header 'Authorization: Bearer ••••••' \
|
||||||
|
--data '{
|
||||||
|
"CommesseCustomFields": [
|
||||||
|
{
|
||||||
|
"IdCommesseCustomFields": 23315543,
|
||||||
|
"Valore": ""
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"IdCommesseCustomFields": 23315544,
|
||||||
|
"Valore": ""
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"IdCommesseCustomFields": 23315545,
|
||||||
|
"Valore": ""
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"IdCommesseCustomFields": 23315546,
|
||||||
|
"Valore": ""
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"IdCommesseCustomFields": 23315547,
|
||||||
|
"Valore": "/"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"IdCommesseCustomFields": 23315548,
|
||||||
|
"Valore": ""
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"IdCommesseCustomFields": 23315554,
|
||||||
|
"Valore": "673"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"IdCommesseCustomFields": 23315555,
|
||||||
|
"Valore": "674"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"IdCommesseCustomFields": 23315556,
|
||||||
|
"Valore": "669"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"IdCommesseCustomFields": 23315558,
|
||||||
|
"Valore": "1469"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"IdCommesseCustomFields": 23315559,
|
||||||
|
"Valore": ""
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"IdCommesseCustomFields": 23315560,
|
||||||
|
"Valore": "12875"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"IdCommesseCustomFields": 23315561,
|
||||||
|
"Valore": ""
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"IdCommesseCustomFields": 23315562,
|
||||||
|
"Valore": ""
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"IdCommesseCustomFields": 23315522,
|
||||||
|
"Valore": "13497"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"IdCommesseCustomFields": 23315523,
|
||||||
|
"Valore": "20262"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"IdCommesseCustomFields": 23315524,
|
||||||
|
"Valore": "ART. PEACH"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"IdCommesseCustomFields": 23315563,
|
||||||
|
"Valore": "1892"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"IdCommesseCustomFields": 23315564,
|
||||||
|
"Valore": ""
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"IdCommesseCustomFields": 23315565,
|
||||||
|
"Valore": ""
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"IdCommesseCustomFields": 23315566,
|
||||||
|
"Valore": "2026-03-18T09:54:11+00:00"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"IdCommesseCustomFields": 23315567,
|
||||||
|
"Valore": ""
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"IdCommesseCustomFields": 23315568,
|
||||||
|
"Valore": ""
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"IdCommesseCustomFields": 23315569,
|
||||||
|
"Valore": ""
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"IdCommesseCustomFields": 23315570,
|
||||||
|
"Valore": "09:53"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"IdCommesseCustomFields": 23315571,
|
||||||
|
"Valore": "10:37"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"IdCommesseCustomFields": 23315572,
|
||||||
|
"Valore": "2026-03-18T09:54:11+00:00"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"IdCommesseCustomFields": 23315573,
|
||||||
|
"Valore": ""
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"IdCommesseCustomFields": 23315574,
|
||||||
|
"Valore": ""
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"IdCommesseCustomFields": 23315575,
|
||||||
|
"Valore": ""
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"IdCommesseCustomFields": 23315525,
|
||||||
|
"Valore": "L209A4M00130M7280"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"IdCommesseCustomFields": 23315526,
|
||||||
|
"Valore": "BLACK"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"IdCommesseCustomFields": 23315527,
|
||||||
|
"Valore": "PE007J"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"IdCommesseCustomFields": 23315528,
|
||||||
|
"Valore": "264"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"IdCommesseCustomFields": 23315529,
|
||||||
|
"Valore": "420"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"IdCommesseCustomFields": 23315530,
|
||||||
|
"Valore": ""
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"IdCommesseCustomFields": 23315531,
|
||||||
|
"Valore": "15357"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"IdCommesseCustomFields": 23315532,
|
||||||
|
"Valore": ""
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"IdCommesseCustomFields": 23315533,
|
||||||
|
"Valore": ""
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"IdCommesseCustomFields": 23315534,
|
||||||
|
"Valore": "P"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"IdCommesseCustomFields": 23315535,
|
||||||
|
"Valore": "RM-ACC"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"IdCommesseCustomFields": 23315536,
|
||||||
|
"Valore": " CONCERIA M2 SRL"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"IdCommesseCustomFields": 23315537,
|
||||||
|
"Valore": "344"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"IdCommesseCustomFields": 23315538,
|
||||||
|
"Valore": "520"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"IdCommesseCustomFields": 23315539,
|
||||||
|
"Valore": ""
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"IdCommesseCustomFields": 23315540,
|
||||||
|
"Valore": ""
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"IdCommesseCustomFields": 23315541,
|
||||||
|
"Valore": "Not provided"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"IdCommesseCustomFields": 23315542,
|
||||||
|
"Valore": ""
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"IdCommesseCustomFields": 23315549,
|
||||||
|
"Valore": ""
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"IdCommesseCustomFields": 23315550,
|
||||||
|
"Valore": "4678"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"IdCommesseCustomFields": 23315551,
|
||||||
|
"Valore": "Leather&amp;Fur"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"IdCommesseCustomFields": 23315552,
|
||||||
|
"Valore": "278"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"IdCommesseCustomFields": 23315553,
|
||||||
|
"Valore": "2089"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"IdCommesseCustomFields": 23315557,
|
||||||
|
"Valore": "745"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}'
|
||||||
|
|
||||||
|
RESPONSE:
|
||||||
|
null
|
||||||
@@ -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
@@ -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"
|
||||||
|
}
|
||||||
@@ -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
|
||||||
|
}
|
||||||
|
|
||||||
|
---
|
||||||
@@ -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"
|
||||||
|
}
|
||||||
@@ -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;Fur"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"IdCommesseCustomFields": 23328257,
|
||||||
|
"Valore": "278"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"IdCommesseCustomFields": 23328258,
|
||||||
|
"Valore": "2089"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"IdCommesseCustomFields": 23328262,
|
||||||
|
"Valore": "745"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}'
|
||||||
|
|
||||||
|
RESPONSE:
|
||||||
|
null
|
||||||
@@ -0,0 +1,28 @@
|
|||||||
|
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": 7598,
|
||||||
|
"MoltiplicatorePrezzo": 2,
|
||||||
|
"AnagraficaCertestObject": 8964,
|
||||||
|
"AnagraficaCertestService": 9012,
|
||||||
|
"ClienteFornitore": 11208
|
||||||
|
}'
|
||||||
|
|
||||||
|
RESPONSE:
|
||||||
|
{
|
||||||
|
"@odata.context": "https:\/\/bvcpsitaly-elims.com\/limsapi\/api\/odata\/$metadata#CommessaWeb\/$entity",
|
||||||
|
"IdCommessa": 564662,
|
||||||
|
"CodiceCommessa": "26C0058",
|
||||||
|
"RiferimentoCertificato": null,
|
||||||
|
"StatoCommessaWeb": "Nuova",
|
||||||
|
"DataCreazioneWeb": "2026-03-17T14:49:39.4933462+01:00",
|
||||||
|
"CodiceCommessaWeb": "26C0058",
|
||||||
|
"DataInviatoWeb": null,
|
||||||
|
"Richiedente": "From TRFSmart Application",
|
||||||
|
"Descrizione": "From TRFSmart Application"
|
||||||
|
}
|
||||||
@@ -0,0 +1,28 @@
|
|||||||
|
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": 7598,
|
||||||
|
"MoltiplicatorePrezzo": 2,
|
||||||
|
"AnagraficaCertestObject": 8964,
|
||||||
|
"AnagraficaCertestService": 9012,
|
||||||
|
"ClienteFornitore": 11208
|
||||||
|
}'
|
||||||
|
|
||||||
|
RESPONSE:
|
||||||
|
{
|
||||||
|
"@odata.context": "https:\/\/bvcpsitaly-elims.com\/limsapi\/api\/odata\/$metadata#CommessaWeb\/$entity",
|
||||||
|
"IdCommessa": 564663,
|
||||||
|
"CodiceCommessa": "26C0059",
|
||||||
|
"RiferimentoCertificato": null,
|
||||||
|
"StatoCommessaWeb": "Nuova",
|
||||||
|
"DataCreazioneWeb": "2026-03-17T14:52:29.3463415+01:00",
|
||||||
|
"CodiceCommessaWeb": "26C0059",
|
||||||
|
"DataInviatoWeb": null,
|
||||||
|
"Richiedente": "From TRFSmart Application",
|
||||||
|
"Descrizione": "From TRFSmart Application"
|
||||||
|
}
|
||||||
@@ -0,0 +1,28 @@
|
|||||||
|
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": null,
|
||||||
|
"AnagraficaCertestObject": 8973,
|
||||||
|
"AnagraficaCertestService": 9007,
|
||||||
|
"ClienteFornitore": 11208
|
||||||
|
}'
|
||||||
|
|
||||||
|
RESPONSE:
|
||||||
|
{
|
||||||
|
"@odata.context": "https:\/\/bvcpsitaly-elims.com\/limsapi\/api\/odata\/$metadata#CommessaWeb\/$entity",
|
||||||
|
"IdCommessa": 564817,
|
||||||
|
"CodiceCommessa": "26C0071",
|
||||||
|
"RiferimentoCertificato": null,
|
||||||
|
"StatoCommessaWeb": "Nuova",
|
||||||
|
"DataCreazioneWeb": "2026-03-18T10:54:02.1867227+01:00",
|
||||||
|
"CodiceCommessaWeb": "26C0071",
|
||||||
|
"DataInviatoWeb": null,
|
||||||
|
"Richiedente": "From TRFSmart Application",
|
||||||
|
"Descrizione": "From TRFSmart Application"
|
||||||
|
}
|
||||||
@@ -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"
|
||||||
|
}
|
||||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,425 @@
|
|||||||
|
<?php
|
||||||
|
include('include/headscript.php');
|
||||||
|
|
||||||
|
$iddatadb = isset($_GET['iddatadb']) ? (int)$_GET['iddatadb'] : 0;
|
||||||
|
|
||||||
|
if ($iddatadb <= 0) {
|
||||||
|
?>
|
||||||
|
<div class="modal fade" id="analysisModal" tabindex="-1" aria-hidden="true">
|
||||||
|
<div class="modal-dialog modal-fullscreen">
|
||||||
|
<div class="modal-content">
|
||||||
|
<div class="modal-header">
|
||||||
|
<h5 class="modal-title">Analysis</h5>
|
||||||
|
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
|
||||||
|
</div>
|
||||||
|
<div class="modal-body">
|
||||||
|
<div class="alert alert-danger mb-0">Invalid iddatadb</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<?php
|
||||||
|
exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
$db = DBHandlerSelect::getInstance();
|
||||||
|
$pdo = $db->getConnection();
|
||||||
|
|
||||||
|
$stmt = $pdo->prepare("
|
||||||
|
SELECT
|
||||||
|
p.id,
|
||||||
|
p.iddatadb,
|
||||||
|
p.part_number,
|
||||||
|
p.part_description,
|
||||||
|
p.material,
|
||||||
|
p.color,
|
||||||
|
p.mix,
|
||||||
|
p.idmatrice,
|
||||||
|
p.note,
|
||||||
|
p.dateexpiry,
|
||||||
|
a.NomeMatriceTraduzione
|
||||||
|
FROM identification_parts p
|
||||||
|
LEFT JOIN auth_matrici a ON a.IdMatrice = p.idmatrice
|
||||||
|
WHERE p.iddatadb = ?
|
||||||
|
ORDER BY
|
||||||
|
CASE WHEN p.part_number IS NULL THEN 999999 ELSE p.part_number END ASC,
|
||||||
|
p.id ASC
|
||||||
|
");
|
||||||
|
$stmt->execute([$iddatadb]);
|
||||||
|
$parts = $stmt->fetchAll(PDO::FETCH_ASSOC);
|
||||||
|
|
||||||
|
$partIds = array_column($parts, 'id');
|
||||||
|
$assignedAnalysesByPart = [];
|
||||||
|
|
||||||
|
if (!empty($partIds)) {
|
||||||
|
$placeholders = implode(',', array_fill(0, count($partIds), '?'));
|
||||||
|
|
||||||
|
$stmtAssigned = $pdo->prepare("
|
||||||
|
SELECT
|
||||||
|
ipa.id,
|
||||||
|
ipa.part_id,
|
||||||
|
ipa.analysis_recordkey,
|
||||||
|
ipa.analysis_name,
|
||||||
|
ipa.analysis_method,
|
||||||
|
ipa.analysis_level,
|
||||||
|
ipa.is_web_selectable,
|
||||||
|
ipa.is_accredited
|
||||||
|
FROM identification_parts_analyses ipa
|
||||||
|
WHERE ipa.part_id IN ($placeholders)
|
||||||
|
ORDER BY ipa.analysis_name ASC, ipa.id ASC
|
||||||
|
");
|
||||||
|
$stmtAssigned->execute($partIds);
|
||||||
|
$assignedRows = $stmtAssigned->fetchAll(PDO::FETCH_ASSOC);
|
||||||
|
|
||||||
|
foreach ($assignedRows as $row) {
|
||||||
|
$partId = (int)$row['part_id'];
|
||||||
|
if (!isset($assignedAnalysesByPart[$partId])) {
|
||||||
|
$assignedAnalysesByPart[$partId] = [];
|
||||||
|
}
|
||||||
|
$assignedAnalysesByPart[$partId][] = $row;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Build matrix groups from parts.
|
||||||
|
* No join for now: we use idmatrice only.
|
||||||
|
*/
|
||||||
|
$matrixGroups = [];
|
||||||
|
foreach ($parts as $part) {
|
||||||
|
$matrixKey = (!empty($part['idmatrice'])) ? (string)$part['idmatrice'] : 'NO_MATRIX';
|
||||||
|
|
||||||
|
if (!isset($matrixGroups[$matrixKey])) {
|
||||||
|
$matrixGroups[$matrixKey] = [
|
||||||
|
'idmatrice' => $part['idmatrice'],
|
||||||
|
'NomeMatriceTraduzione' => $part['NomeMatriceTraduzione'] ?? '',
|
||||||
|
'parts_count' => 0,
|
||||||
|
'parts' => [],
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
$matrixGroups[$matrixKey]['parts_count']++;
|
||||||
|
$matrixGroups[$matrixKey]['parts'][] = $part['id'];
|
||||||
|
}
|
||||||
|
|
||||||
|
$matrixGroups = array_values($matrixGroups);
|
||||||
|
?>
|
||||||
|
|
||||||
|
<div class="modal fade" id="analysisModal" tabindex="-1" aria-labelledby="analysisModalLabel" aria-hidden="true">
|
||||||
|
<div class="modal-dialog" style="max-width: 96vw; width: 96vw; margin: 1.5vh auto;">
|
||||||
|
<div class="modal-content analysis-modal-content">
|
||||||
|
<div class="modal-header">
|
||||||
|
<div>
|
||||||
|
<h5 class="modal-title mb-0" id="analysisModalLabel">Analysis - TRF <?= htmlspecialchars((string)$iddatadb, ENT_QUOTES, 'UTF-8') ?></h5>
|
||||||
|
<small class="text-muted">
|
||||||
|
Parts: <?= count($parts) ?> |
|
||||||
|
Matrices: <?= count($matrixGroups) ?>
|
||||||
|
</small>
|
||||||
|
</div>
|
||||||
|
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="modal-body analysis-modal-body">
|
||||||
|
<div class="row g-3 h-100">
|
||||||
|
<!-- MATRICES -->
|
||||||
|
<div class="col-lg-3 col-md-4">
|
||||||
|
<div class="card h-100 shadow-sm">
|
||||||
|
<div class="card-header py-2 d-flex justify-content-between align-items-center">
|
||||||
|
<strong>Matrices</strong>
|
||||||
|
<span class="badge bg-secondary"><?= count($matrixGroups) ?></span>
|
||||||
|
</div>
|
||||||
|
<div class="card-body p-2 analysis-scroll-area">
|
||||||
|
<div class="list-group analysis-matrix-list" id="analysisMatrixList">
|
||||||
|
<?php if (empty($matrixGroups)): ?>
|
||||||
|
<div class="text-muted small p-2">No matrices found</div>
|
||||||
|
<?php else: ?>
|
||||||
|
<?php foreach ($matrixGroups as $index => $group): ?>
|
||||||
|
<button type="button"
|
||||||
|
class="list-group-item list-group-item-action analysis-matrix-item <?= $index === 0 ? 'active' : '' ?>"
|
||||||
|
data-matrix-id="<?= htmlspecialchars((string)($group['idmatrice'] ?? 'NO_MATRIX'), ENT_QUOTES, 'UTF-8') ?>">
|
||||||
|
<div class="fw-semibold">
|
||||||
|
<?= htmlspecialchars(
|
||||||
|
!empty($group['NomeMatriceTraduzione'])
|
||||||
|
? $group['NomeMatriceTraduzione']
|
||||||
|
: (!empty($group['idmatrice']) ? ('Matrix without translation') : 'No Matrix'),
|
||||||
|
ENT_QUOTES,
|
||||||
|
'UTF-8'
|
||||||
|
) ?>
|
||||||
|
</div>
|
||||||
|
<div class="small text-muted">
|
||||||
|
ID: <?= !empty($group['idmatrice']) ? (int)$group['idmatrice'] : '-' ?>
|
||||||
|
</div>
|
||||||
|
<div class="small text-muted">
|
||||||
|
Parts linked: <?= (int)$group['parts_count'] ?>
|
||||||
|
</div>
|
||||||
|
</button>
|
||||||
|
<?php endforeach; ?>
|
||||||
|
<?php endif; ?>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- PARTS -->
|
||||||
|
<div class="col-lg-4 col-md-8">
|
||||||
|
<div class="card h-100 shadow-sm">
|
||||||
|
<div class="card-header py-2 d-flex justify-content-between align-items-center">
|
||||||
|
<strong>Parts</strong>
|
||||||
|
<div class="d-flex align-items-center gap-2">
|
||||||
|
<button type="button" class="btn btn-outline-secondary btn-sm" id="analysisClearSelectionBtn">Clear</button>
|
||||||
|
<span class="badge bg-primary" id="analysisSelectedPartsCount">0 selected</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="card-body p-2 analysis-scroll-area">
|
||||||
|
<div class="list-group analysis-parts-list" id="analysisPartsList">
|
||||||
|
<?php if (empty($parts)): ?>
|
||||||
|
<div class="text-muted small p-2">No parts found</div>
|
||||||
|
<?php else: ?>
|
||||||
|
<?php foreach ($parts as $part): ?>
|
||||||
|
<?php
|
||||||
|
$matrixId = !empty($part['idmatrice']) ? (string)$part['idmatrice'] : 'NO_MATRIX';
|
||||||
|
$partLabel = trim(($part['part_number'] !== null ? ('Part ' . $part['part_number']) : 'Part') . ' - ' . ($part['part_description'] ?? ''));
|
||||||
|
?>
|
||||||
|
<div class="list-group-item analysis-part-item"
|
||||||
|
data-part-id="<?= (int)$part['id'] ?>"
|
||||||
|
data-matrix-id="<?= htmlspecialchars($matrixId, ENT_QUOTES, 'UTF-8') ?>">
|
||||||
|
<div class="d-flex align-items-start gap-2">
|
||||||
|
<input type="checkbox"
|
||||||
|
class="form-check-input mt-1 analysis-part-checkbox"
|
||||||
|
value="<?= (int)$part['id'] ?>">
|
||||||
|
<div class="flex-grow-1">
|
||||||
|
<div class="fw-semibold">
|
||||||
|
<?= htmlspecialchars($partLabel, ENT_QUOTES, 'UTF-8') ?>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="small text-muted mt-1">
|
||||||
|
Matrix:
|
||||||
|
<strong>
|
||||||
|
<?= htmlspecialchars(
|
||||||
|
!empty($part['NomeMatriceTraduzione'])
|
||||||
|
? $part['NomeMatriceTraduzione']
|
||||||
|
: (!empty($part['idmatrice']) ? 'Matrix without translation' : 'No Matrix'),
|
||||||
|
ENT_QUOTES,
|
||||||
|
'UTF-8'
|
||||||
|
) ?>
|
||||||
|
</strong>
|
||||||
|
<?php if (!empty($part['idmatrice'])): ?>
|
||||||
|
<span class="small text-muted">(ID: <?= (int)$part['idmatrice'] ?>)</span>
|
||||||
|
<?php endif; ?>
|
||||||
|
<?php if (!empty($part['mix']) && strtoupper($part['mix']) === 'Y'): ?>
|
||||||
|
| <span class="badge bg-warning text-dark">Mix</span>
|
||||||
|
<?php endif; ?>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<?php if (!empty($part['material']) || !empty($part['color'])): ?>
|
||||||
|
<div class="small text-muted">
|
||||||
|
<?php if (!empty($part['material'])): ?>
|
||||||
|
Material: <?= htmlspecialchars($part['material'], ENT_QUOTES, 'UTF-8') ?>
|
||||||
|
<?php endif; ?>
|
||||||
|
<?php if (!empty($part['material']) && !empty($part['color'])): ?>
|
||||||
|
|
|
||||||
|
<?php endif; ?>
|
||||||
|
<?php if (!empty($part['color'])): ?>
|
||||||
|
Color: <?= htmlspecialchars($part['color'], ENT_QUOTES, 'UTF-8') ?>
|
||||||
|
<?php endif; ?>
|
||||||
|
</div>
|
||||||
|
<?php endif; ?>
|
||||||
|
|
||||||
|
<?php if (!empty($part['note'])): ?>
|
||||||
|
<div class="small text-muted mt-1">
|
||||||
|
Note: <?= htmlspecialchars($part['note'], ENT_QUOTES, 'UTF-8') ?>
|
||||||
|
</div>
|
||||||
|
<?php endif; ?>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
<div class="analysis-part-assigned-list mt-2" id="analysisAssignedListPart<?= (int)$part['id'] ?>"></div>
|
||||||
|
</div>
|
||||||
|
<?php endforeach; ?>
|
||||||
|
<?php endif; ?>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- ANALYSES PLACEHOLDER -->
|
||||||
|
<div class="col-lg-5">
|
||||||
|
<div class="card h-100 shadow-sm">
|
||||||
|
<div class="card-header py-2">
|
||||||
|
<strong>Analyses</strong>
|
||||||
|
</div>
|
||||||
|
<div class="card-body analysis-scroll-area" id="analysisRightPanel">
|
||||||
|
<div class="mt-1 mb-3">
|
||||||
|
<div class="small text-muted">Selected matrix:</div>
|
||||||
|
<div id="analysisCurrentMatrix" class="fw-semibold">-</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="mb-3">
|
||||||
|
<div class="small text-muted">Selected parts IDs:</div>
|
||||||
|
<div id="analysisSelectedPartsIds" class="fw-semibold">-</div>
|
||||||
|
</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>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="flex-grow-1" style="min-width: 220px;">
|
||||||
|
<input type="text" class="form-control form-control-sm" id="analysisSearchInput" placeholder="Search analyses or method">
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div id="analysisLoadingBox" class="alert alert-info mb-3 d-none">
|
||||||
|
Loading analyses...
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div id="analysisErrorBox" class="alert alert-danger mb-3 d-none"></div>
|
||||||
|
|
||||||
|
<div id="analysisEmptyBox" class="alert alert-secondary mb-3">
|
||||||
|
Select a matrix to load analyses
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="list-group" id="analysisList"></div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<input type="hidden" id="analysisModalIddatadb" value="<?= (int)$iddatadb ?>">
|
||||||
|
|
||||||
|
<script type="application/json" id="analysisAssignedInitialData">
|
||||||
|
<?= json_encode($assignedAnalysesByPart, JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES) ?>
|
||||||
|
</script>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="modal-footer">
|
||||||
|
<button type="button" class="btn btn-secondary btn-sm" data-bs-dismiss="modal">Close</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<style>
|
||||||
|
#analysisModal {
|
||||||
|
z-index: 1080 !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
#analysisModal .modal-content {
|
||||||
|
width: 100% !important;
|
||||||
|
max-width: 100% !important;
|
||||||
|
min-height: 95vh;
|
||||||
|
border-radius: 14px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.analysis-modal-content {
|
||||||
|
overflow: hidden;
|
||||||
|
}
|
||||||
|
|
||||||
|
.analysis-modal-body {
|
||||||
|
height: calc(95vh - 120px);
|
||||||
|
}
|
||||||
|
|
||||||
|
.analysis-scroll-area {
|
||||||
|
overflow-y: auto;
|
||||||
|
max-height: calc(95vh - 180px);
|
||||||
|
}
|
||||||
|
|
||||||
|
.analysis-matrix-item {
|
||||||
|
text-align: left;
|
||||||
|
border-radius: 8px !important;
|
||||||
|
margin-bottom: 8px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.analysis-part-item {
|
||||||
|
border-radius: 8px !important;
|
||||||
|
margin-bottom: 8px;
|
||||||
|
transition: all 0.2s ease-in-out;
|
||||||
|
}
|
||||||
|
|
||||||
|
.analysis-part-item.matrix-active {
|
||||||
|
background-color: #e8f4ff !important;
|
||||||
|
border-color: #86b7fe !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.analysis-part-item.part-checked {
|
||||||
|
background-color: #eaf7ea !important;
|
||||||
|
border-color: #7ac77a !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
#analysisSelectedPartsIds {
|
||||||
|
word-break: break-word;
|
||||||
|
}
|
||||||
|
|
||||||
|
.analysis-analysis-item {
|
||||||
|
border-radius: 8px !important;
|
||||||
|
margin-bottom: 6px;
|
||||||
|
background-color: #ffffff;
|
||||||
|
}
|
||||||
|
|
||||||
|
#analysisList .analysis-analysis-item:nth-child(even) {
|
||||||
|
background-color: #eef4ff;
|
||||||
|
}
|
||||||
|
|
||||||
|
.analysis-analysis-meta {
|
||||||
|
font-size: 0.84rem;
|
||||||
|
color: #6c757d;
|
||||||
|
}
|
||||||
|
|
||||||
|
.analysis-analysis-badges .badge {
|
||||||
|
margin-right: 4px;
|
||||||
|
margin-bottom: 4px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.analysis-assigned-chip {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: space-between;
|
||||||
|
gap: 8px;
|
||||||
|
padding: 4px 8px;
|
||||||
|
border: 1px solid #dbe7f3;
|
||||||
|
background: #f7fbff;
|
||||||
|
border-radius: 8px;
|
||||||
|
margin-bottom: 6px;
|
||||||
|
font-size: 0.84rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.analysis-assigned-chip-text {
|
||||||
|
min-width: 0;
|
||||||
|
flex: 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
.analysis-assigned-chip-title {
|
||||||
|
font-weight: 600;
|
||||||
|
line-height: 1.2;
|
||||||
|
}
|
||||||
|
|
||||||
|
.analysis-assigned-chip-method {
|
||||||
|
color: #6c757d;
|
||||||
|
font-size: 0.78rem;
|
||||||
|
line-height: 1.2;
|
||||||
|
margin-top: 2px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.analysis-remove-btn {
|
||||||
|
border: 0;
|
||||||
|
background: transparent;
|
||||||
|
color: #dc3545;
|
||||||
|
font-weight: 700;
|
||||||
|
font-size: 1rem;
|
||||||
|
line-height: 1;
|
||||||
|
padding: 0 2px;
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
|
||||||
|
.analysis-analysis-item.analysis-selected {
|
||||||
|
border-color: #198754 !important;
|
||||||
|
background-color: #eaf7ea !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.analysis-part-assigned-list {
|
||||||
|
border-top: 1px dashed #dee2e6;
|
||||||
|
padding-top: 8px;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
@@ -46,6 +46,16 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="modal-footer">
|
<div class="modal-footer">
|
||||||
|
<div style="display: flex; align-items: center; gap: 6px; margin-right: auto;">
|
||||||
|
<label for="descriptionColorPickerAnnotations" style="font-size: 0.8rem; margin: 0; white-space: nowrap;">
|
||||||
|
Colore annotazioni:
|
||||||
|
</label>
|
||||||
|
<input
|
||||||
|
type="color"
|
||||||
|
id="descriptionColorPickerAnnotations"
|
||||||
|
value="#000000"
|
||||||
|
style="width: 34px; height: 28px; padding: 1px; border: 1px solid #ccc; border-radius: 4px; cursor: pointer;">
|
||||||
|
</div>
|
||||||
<button type="button" class="btn btn-primary btn-sm" id="addDescriptionsBtnAnnotations" style="padding: 0.1rem 0.5rem; font-size: 0.8rem;">Aggiungi Lista Descrizioni</button>
|
<button type="button" class="btn btn-primary btn-sm" id="addDescriptionsBtnAnnotations" style="padding: 0.1rem 0.5rem; font-size: 0.8rem;">Aggiungi Lista Descrizioni</button>
|
||||||
<button type="button" class="btn btn-danger btn-sm" id="removeAnnotationsBtnAnnotations" style="padding: 0.1rem 0.5rem; font-size: 0.8rem;">Rimuovi Descrizioni</button>
|
<button type="button" class="btn btn-danger btn-sm" id="removeAnnotationsBtnAnnotations" style="padding: 0.1rem 0.5rem; font-size: 0.8rem;">Rimuovi Descrizioni</button>
|
||||||
<button type="button" class="btn btn-warning btn-sm" id="undoMarkerBtnAnnotations" style="padding: 0.1rem 0.5rem; font-size: 0.8rem;">Undo Marker</button>
|
<button type="button" class="btn btn-warning btn-sm" id="undoMarkerBtnAnnotations" style="padding: 0.1rem 0.5rem; font-size: 0.8rem;">Undo Marker</button>
|
||||||
|
|||||||
@@ -307,4 +307,23 @@
|
|||||||
width: 250px !important;
|
width: 250px !important;
|
||||||
min-width: 250px !important;
|
min-width: 250px !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#partsTableBody .extra-field-td .select2-container {
|
||||||
|
width: 100% !important;
|
||||||
|
min-width: 180px !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
#partsTableBody .extra-field-td .select2-selection--single {
|
||||||
|
height: 31px !important;
|
||||||
|
padding: 0.15rem 0.35rem !important;
|
||||||
|
border: 1px solid #ced4da !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
#partsTableBody .extra-field-td .select2-selection__rendered {
|
||||||
|
line-height: 28px !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
#partsTableBody .extra-field-td .select2-selection__arrow {
|
||||||
|
height: 29px !important;
|
||||||
|
}
|
||||||
</style>
|
</style>
|
||||||
@@ -9,13 +9,29 @@
|
|||||||
<div class="row parts-row">
|
<div class="row parts-row">
|
||||||
<div class="col-md-9">
|
<div class="col-md-9">
|
||||||
<!-- Prima riga: Elenco Parti, Rinumera, Voce -->
|
<!-- Prima riga: Elenco Parti, Rinumera, Voce -->
|
||||||
<div style="display: flex; justify-content: space-between; align-items: center; margin-bottom: 10px;">
|
<div style="display: flex; justify-content: space-between; align-items: center; margin-bottom: 10px; min-width: 0;">
|
||||||
<h6 style="margin: 0;">Elenco Parti</h6>
|
<h6 style="margin: 0; white-space: nowrap;">Elenco Parti</h6>
|
||||||
<div style="display: flex; align-items: center;">
|
<div style="display: flex; align-items: center; min-width: 0; gap: 8px;">
|
||||||
<button type="button" class="btn btn-info btn-sm" id="renumberPartsBtn" style="padding: 0.1rem 0.5rem; font-size: 0.8rem;">Rinumera Parti</button>
|
<button type="button"
|
||||||
<button type="button" class="btn btn-secondary btn-sm ms-2" id="toggleVoiceBtn" style="padding: 0.1rem 0.5rem; font-size: 0.8rem;"><i class="fas fa-microphone"></i> Voce</button>
|
class="btn btn-dark btn-sm open-analysis-modal-btn"
|
||||||
<button type="button" class="btn btn-info btn-sm ms-2 d-none" id="quotationeBtn" style="padding: 0.1rem 0.5rem; font-size: 0.8rem;">Add Quotation</button>
|
id="openAnalysisModalBtn"
|
||||||
<button type="button" class="btn btn-primary btn-sm ms-2" id="showHideImageBtn" style="padding: 0.1rem 0.5rem; font-size: 0.8rem;">
|
style="padding: 0.1rem 0.5rem; font-size: 0.8rem;">
|
||||||
|
<i class="fas fa-flask"></i> Analysis
|
||||||
|
</button>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
<button type="button" class="btn btn-info btn-sm" id="renumberPartsBtn" style="padding: 0.1rem 0.5rem; font-size: 0.8rem;">
|
||||||
|
Rinumera Parti
|
||||||
|
</button>
|
||||||
|
<button type="button" class="btn btn-primary btn-sm" id="clonePartsBtn" style="padding: 0.1rem 0.5rem; font-size: 0.8rem;">
|
||||||
|
<i class="fas fa-clone"></i> Clona Parti
|
||||||
|
</button>
|
||||||
|
<button type="button" class="btn btn-secondary btn-sm ms-2" id="toggleVoiceBtn" style="padding: 0.1rem 0.5rem; font-size: 0.8rem;">
|
||||||
|
<i class="fas fa-microphone"></i> Voce
|
||||||
|
</button>
|
||||||
|
<button type="button" class="btn btn-outline-primary btn-sm ms-2" id="showHideImageBtn" style="padding: 0.1rem 0.5rem; font-size: 0.8rem;">
|
||||||
<i class="fas fa-eye-slash" style="font-size: 0.8rem;"></i>
|
<i class="fas fa-eye-slash" style="font-size: 0.8rem;"></i>
|
||||||
<i class="fas fa-image ms-1" style="font-size: 0.8rem;"></i>
|
<i class="fas fa-image ms-1" style="font-size: 0.8rem;"></i>
|
||||||
</button>
|
</button>
|
||||||
@@ -23,15 +39,23 @@
|
|||||||
</div>
|
</div>
|
||||||
<!-- Seconda riga: +, M, MacroMatrice, Matrice globale, Propaga -->
|
<!-- Seconda riga: +, M, MacroMatrice, Matrice globale, Propaga -->
|
||||||
<div style="display: flex; align-items: center; margin-bottom: 10px;">
|
<div style="display: flex; align-items: center; margin-bottom: 10px;">
|
||||||
<button type="button" class="btn btn-success btn-sm add-row-global" style="padding: 0.1rem 0.3rem; font-size: 0.8rem; margin-right: 5px;"><i class="fas fa-plus fa-xs"></i></button>
|
<button type="button" class="btn btn-success btn-sm add-row-global" style="padding: 0.1rem 0.3rem; font-size: 0.8rem; margin-right: 5px;"><i class="fas fa-plus"></i></button>
|
||||||
<button type="button" class="btn btn-primary btn-sm add-mix-global" style="padding: 0.1rem 0.3rem; font-size: 0.8rem; margin-right: 10px;">M</button>
|
<button type="button" class="btn btn-primary btn-sm add-mix-global" style="padding: 0.1rem 0.3rem; font-size: 0.8rem; margin-right: 10px;">M</button>
|
||||||
<select id="macro-matrice-filter" class="form-control form-control-sm ms-2" style="width: 200px !important; min-width: 200px !important; margin-right: 10px;">
|
<select id="macro-matrice-filter" class="form-control form-control-sm ms-2" style="width: 200px !important; min-width: 200px !important; margin-right: 10px;">
|
||||||
<option value="">Tutte le MacroMatrici</option>
|
<option value="">Tutte le MacroMatrici</option>
|
||||||
</select>
|
</select>
|
||||||
<select id="global-matrice" class="form-control form-control-sm" style="width: 350px !important; margin-right: 10px;"></select>
|
<select id="global-matrice" class="form-control form-control-sm" style="width: 350px !important; margin-right: 10px;"></select>
|
||||||
<button type="button" class="btn btn-primary btn-sm propagate-all-btn" style="padding: 0.1rem 0.5rem; font-size: 0.8rem;"><i class="fas fa-arrow-right fa-xs"></i> Propaga a tutte</button>
|
<button type="button" class="btn btn-primary btn-sm propagate-all-btn" style="padding: 0.1rem 0.5rem; font-size: 0.8rem;"><i class="fas fa-arrow-right"></i> Propaga a tutte</button>
|
||||||
</div>
|
</div>
|
||||||
|
<div class="parts-table-scroll">
|
||||||
<table class="table table-striped table-sm" id="partsTable">
|
<table class="table table-striped table-sm" id="partsTable">
|
||||||
|
<colgroup id="partsTableColgroup">
|
||||||
|
<col class="parts-col-num">
|
||||||
|
<col class="parts-col-description">
|
||||||
|
<col class="parts-col-matrice">
|
||||||
|
<col class="parts-col-date">
|
||||||
|
<col class="parts-col-actions">
|
||||||
|
</colgroup>
|
||||||
<thead>
|
<thead>
|
||||||
<tr>
|
<tr>
|
||||||
<th style="width: 55px;">Num</th>
|
<th style="width: 55px;">Num</th>
|
||||||
@@ -55,7 +79,7 @@
|
|||||||
<td><input type="text" class="form-control form-control-sm part-description" placeholder="Inserisci descrizione"></td>
|
<td><input type="text" class="form-control form-control-sm part-description" placeholder="Inserisci descrizione"></td>
|
||||||
<td>
|
<td>
|
||||||
<div style="display: flex; align-items: center;">
|
<div style="display: flex; align-items: center;">
|
||||||
<button type="button" class="btn btn-primary btn-sm propagate-matrice-btn" style="padding: 0.1rem 0.3rem; font-size: 0.8rem; margin-right: 3px;"><i class="fas fa-arrow-right fa-xs"></i></button>
|
<button type="button" class="btn btn-primary btn-sm propagate-matrice-btn" style="padding: 0.1rem 0.3rem; font-size: 0.8rem; margin-right: 3px;"><i class="fas fa-arrow-right"></i></button>
|
||||||
<select class="part-matrice form-control form-control-sm" style="width: 150px;"></select>
|
<select class="part-matrice form-control form-control-sm" style="width: 150px;"></select>
|
||||||
</div>
|
</div>
|
||||||
</td>
|
</td>
|
||||||
@@ -63,14 +87,15 @@
|
|||||||
<td>
|
<td>
|
||||||
<button type="button" class="btn btn-light btn-sm note-btn" style="padding: 0.2rem 0.4rem; font-size: 0.9rem;" title="Aggiungi/Modifica nota"><i class="fas fa-sticky-note"></i></button>
|
<button type="button" class="btn btn-light btn-sm note-btn" style="padding: 0.2rem 0.4rem; font-size: 0.9rem;" title="Aggiungi/Modifica nota"><i class="fas fa-sticky-note"></i></button>
|
||||||
<button type="button" class="btn btn-warning btn-sm add-mix-row" style="padding: 0.1rem 0.3rem; font-size: 0.8rem;">M+</button>
|
<button type="button" class="btn btn-warning btn-sm add-mix-row" style="padding: 0.1rem 0.3rem; font-size: 0.8rem;">M+</button>
|
||||||
<button type="button" class="btn btn-danger btn-sm remove-row" style="padding: 0.1rem 0.3rem; font-size: 0.8rem; display: none;"><i class="fas fa-trash fa-xs"></i></button>
|
<button type="button" class="btn btn-danger btn-sm remove-row" style="padding: 0.1rem 0.3rem; font-size: 0.8rem; display: none;"><i class="fas fa-trash"></i></button>
|
||||||
<span class="save-status text-success" style="display: none; margin-left: 5px;"><i class="fas fa-check fa-xs"></i></span>
|
<span class="save-status text-success" style="display: none; margin-left: 5px;"><i class="fas fa-check"></i></span>
|
||||||
<span class="save-loading text-warning" style="display: none; margin-left: 5px;"><i class="fas fa-spinner fa-spin fa-xs"></i></span>
|
<span class="save-loading text-warning" style="display: none; margin-left: 5px;"><i class="fas fa-spinner fa-spin"></i></span>
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
</tbody>
|
</tbody>
|
||||||
</table>
|
</table>
|
||||||
</div>
|
</div>
|
||||||
|
</div>
|
||||||
<div class="col-md-3">
|
<div class="col-md-3">
|
||||||
<h6>Foto del Campione</h6>
|
<h6>Foto del Campione</h6>
|
||||||
<div style="display: flex; align-items: center; margin-bottom: 10px;">
|
<div style="display: flex; align-items: center; margin-bottom: 10px;">
|
||||||
@@ -188,6 +213,14 @@
|
|||||||
z-index: 1055 !important
|
z-index: 1055 !important
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Fix: app.css sets .btn i { margin-top:-1em; margin-bottom:-1em; font-size:1.3rem } which collapses small icon buttons */
|
||||||
|
#partsModal .btn i {
|
||||||
|
margin-top: 0 !important;
|
||||||
|
margin-bottom: 0 !important;
|
||||||
|
font-size: 0.8rem !important;
|
||||||
|
margin-right: 2px !important;
|
||||||
|
}
|
||||||
|
|
||||||
/* Tabelle */
|
/* Tabelle */
|
||||||
#partsTable tr {
|
#partsTable tr {
|
||||||
display: table-row !important
|
display: table-row !important
|
||||||
@@ -269,28 +302,20 @@
|
|||||||
white-space: nowrap;
|
white-space: nowrap;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Select delle righe (colonna Matrice) = 150px */
|
/* Select delle righe Matrice: si adatta alla colonna */
|
||||||
.part-matrice {
|
.part-matrice {
|
||||||
width: 300px !important;
|
width: 100% !important;
|
||||||
min-width: 300px !important;
|
min-width: 0 !important;
|
||||||
max-width: 300px !important;
|
max-width: 100% !important;
|
||||||
flex: 0 0 300px !important;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.part-matrice.select2-hidden-accessible+.select2 {
|
.part-matrice.select2-hidden-accessible+.select2 {
|
||||||
width: 300px !important;
|
width: 100% !important;
|
||||||
min-width: 300px !important;
|
min-width: 0 !important;
|
||||||
max-width: 300px !important;
|
max-width: 100% !important;
|
||||||
flex: 0 0 300px !important;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Colonna Descrizione (2ª colonna) = 420px */
|
|
||||||
#partsTable th:nth-child(2),
|
|
||||||
#partsTable td:nth-child(2) {
|
|
||||||
width: 300px !important;
|
|
||||||
min-width: 300px !important;
|
|
||||||
max-width: 300px !important;
|
|
||||||
}
|
|
||||||
|
|
||||||
#partsTable .part-number {
|
#partsTable .part-number {
|
||||||
text-align: center;
|
text-align: center;
|
||||||
@@ -495,6 +520,202 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Resizable parts table - stable */
|
||||||
|
.parts-table-scroll {
|
||||||
|
width: 100%;
|
||||||
|
overflow-x: auto;
|
||||||
|
overflow-y: visible;
|
||||||
|
contain: inline-size;
|
||||||
|
}
|
||||||
|
|
||||||
|
#partsTable {
|
||||||
|
table-layout: fixed !important;
|
||||||
|
width: max-content !important;
|
||||||
|
min-width: unset !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
#partsTable col.parts-col-num {
|
||||||
|
width: 55px;
|
||||||
|
}
|
||||||
|
|
||||||
|
#partsTable col.parts-col-description {
|
||||||
|
width: 320px;
|
||||||
|
}
|
||||||
|
|
||||||
|
#partsTable col.parts-col-matrice {
|
||||||
|
width: 360px;
|
||||||
|
}
|
||||||
|
|
||||||
|
#partsTable col.parts-col-date {
|
||||||
|
width: 150px;
|
||||||
|
}
|
||||||
|
|
||||||
|
#partsTable col.parts-col-actions {
|
||||||
|
width: 230px;
|
||||||
|
}
|
||||||
|
|
||||||
|
#partsTable th,
|
||||||
|
#partsTable td {
|
||||||
|
position: relative;
|
||||||
|
box-sizing: border-box;
|
||||||
|
}
|
||||||
|
|
||||||
|
#partsTable th {
|
||||||
|
user-select: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
#partsTable th .parts-resizer {
|
||||||
|
position: absolute;
|
||||||
|
top: 0;
|
||||||
|
right: 0;
|
||||||
|
width: 9px;
|
||||||
|
height: 100%;
|
||||||
|
cursor: col-resize;
|
||||||
|
z-index: 50;
|
||||||
|
background: rgba(108, 117, 125, 0.12);
|
||||||
|
border-right: 1px solid rgba(108, 117, 125, 0.45);
|
||||||
|
}
|
||||||
|
|
||||||
|
#partsTable th .parts-resizer:hover {
|
||||||
|
background: rgba(108, 117, 125, 0.25);
|
||||||
|
border-right: 2px solid rgba(73, 80, 87, 0.7);
|
||||||
|
}
|
||||||
|
|
||||||
|
#partsTable td {
|
||||||
|
overflow: hidden;
|
||||||
|
text-overflow: ellipsis;
|
||||||
|
}
|
||||||
|
|
||||||
|
#partsTable td input,
|
||||||
|
#partsTable td select,
|
||||||
|
#partsTable td .select2-container {
|
||||||
|
max-width: 100% !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Propagation control for dynamic extra field column */
|
||||||
|
#partsTable th.extra-field-th {
|
||||||
|
vertical-align: middle;
|
||||||
|
}
|
||||||
|
|
||||||
|
#partsTable th.extra-field-th .extra-propagate-wrapper {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 4px;
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
#partsTable th.extra-field-th .extra-propagate-label {
|
||||||
|
font-size: 0.75rem;
|
||||||
|
font-weight: 600;
|
||||||
|
white-space: nowrap;
|
||||||
|
overflow: hidden;
|
||||||
|
text-overflow: ellipsis;
|
||||||
|
max-width: 95px;
|
||||||
|
}
|
||||||
|
|
||||||
|
#partsTable th.extra-field-th .extra-propagate-input,
|
||||||
|
#partsTable th.extra-field-th .extra-propagate-select {
|
||||||
|
height: 24px !important;
|
||||||
|
font-size: 0.75rem !important;
|
||||||
|
padding: 0.1rem 0.25rem !important;
|
||||||
|
min-width: 90px;
|
||||||
|
flex: 1 1 auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
#partsTable th.extra-field-th .extra-propagate-btn {
|
||||||
|
height: 24px;
|
||||||
|
min-width: 26px;
|
||||||
|
padding: 0.1rem 0.3rem !important;
|
||||||
|
font-size: 0.75rem !important;
|
||||||
|
flex: 0 0 auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
#partsTable th.extra-field-th .select2-container {
|
||||||
|
flex: 1 1 auto;
|
||||||
|
min-width: 90px;
|
||||||
|
max-width: 100% !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
#partsTable th.extra-field-th .select2-selection--single {
|
||||||
|
height: 24px !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
#partsTable th.extra-field-th .select2-selection__rendered {
|
||||||
|
line-height: 22px !important;
|
||||||
|
font-size: 0.75rem !important;
|
||||||
|
padding-left: 4px !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
#partsTable th.extra-field-th .select2-selection__arrow {
|
||||||
|
height: 22px !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Extra field propagation header - full column width */
|
||||||
|
#partsTable th.extra-field-th {
|
||||||
|
width: auto !important;
|
||||||
|
min-width: 220px !important;
|
||||||
|
max-width: none !important;
|
||||||
|
padding: 0.25rem !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
#partsTable .extra-propagate-wrapper {
|
||||||
|
width: 100% !important;
|
||||||
|
min-width: 100% !important;
|
||||||
|
max-width: 100% !important;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: 4px;
|
||||||
|
box-sizing: border-box;
|
||||||
|
}
|
||||||
|
|
||||||
|
#partsTable .extra-propagate-label {
|
||||||
|
width: 100% !important;
|
||||||
|
max-width: 100% !important;
|
||||||
|
display: block;
|
||||||
|
font-size: 0.82rem;
|
||||||
|
font-weight: 700;
|
||||||
|
line-height: 1.15;
|
||||||
|
color: #222;
|
||||||
|
white-space: nowrap !important;
|
||||||
|
overflow: visible !important;
|
||||||
|
text-overflow: unset !important;
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
#partsTable .extra-propagate-control {
|
||||||
|
width: 100% !important;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 5px;
|
||||||
|
box-sizing: border-box;
|
||||||
|
}
|
||||||
|
|
||||||
|
#partsTable .extra-propagate-control .extra-propagate-input,
|
||||||
|
#partsTable .extra-propagate-control .extra-propagate-select {
|
||||||
|
flex: 1 1 auto !important;
|
||||||
|
width: 100% !important;
|
||||||
|
min-width: 0 !important;
|
||||||
|
max-width: none !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
#partsTable .extra-propagate-control .select2-container {
|
||||||
|
flex: 1 1 auto !important;
|
||||||
|
width: 100% !important;
|
||||||
|
min-width: 0 !important;
|
||||||
|
max-width: none !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
#partsTable .extra-propagate-control .select2-selection {
|
||||||
|
width: 100% !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
#partsTable .extra-propagate-btn {
|
||||||
|
flex: 0 0 30px !important;
|
||||||
|
width: 30px !important;
|
||||||
|
height: 24px !important;
|
||||||
|
min-width: 30px !important;
|
||||||
|
padding: 0.1rem 0.25rem !important;
|
||||||
|
}
|
||||||
|
|
||||||
/* rosso */
|
/* rosso */
|
||||||
</style>
|
</style>
|
||||||
@@ -0,0 +1,264 @@
|
|||||||
|
/**
|
||||||
|
* modals_gridData.js — Photos, Parts, Tested Component handlers for gridData pages
|
||||||
|
*/
|
||||||
|
(function () {
|
||||||
|
"use strict";
|
||||||
|
|
||||||
|
// ── Photos — use photos.js loadPopupContent (exported to window) ──
|
||||||
|
$(document).on("click", ".photos-btn", function () {
|
||||||
|
const iddatadb = $(this).data("iddatadb") || null;
|
||||||
|
const idquotations = $(this).data("idquotations") || null;
|
||||||
|
const modal = document.getElementById("photosModal");
|
||||||
|
if (!modal) return;
|
||||||
|
|
||||||
|
modal.style.display = "block";
|
||||||
|
|
||||||
|
if (typeof window.loadPopupContent === "function") {
|
||||||
|
window.loadPopupContent(iddatadb, idquotations);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// Close photos modal
|
||||||
|
$(document).on("click", ".close-btn", function () {
|
||||||
|
const modal = document.getElementById("photosModal");
|
||||||
|
if (modal) modal.style.display = "none";
|
||||||
|
});
|
||||||
|
|
||||||
|
// Close on backdrop click
|
||||||
|
$(document).on("click", "#photosModal", function (e) {
|
||||||
|
if (e.target === this) {
|
||||||
|
this.style.display = "none";
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// ── Parts (matching import_edit2.php behavior) ─────────────────────
|
||||||
|
$(document).on("click", ".parts-btn", function () {
|
||||||
|
const iddatadb = $(this).data("iddatadb") || null;
|
||||||
|
const idquotations = $(this).data("idquotations") || null;
|
||||||
|
|
||||||
|
$.ajax({
|
||||||
|
url: "modal_partsTable.php",
|
||||||
|
method: "GET",
|
||||||
|
data: { iddatadb: iddatadb },
|
||||||
|
success: function (response) {
|
||||||
|
$("#partsModalContainer").html(response);
|
||||||
|
const modalElement = document.getElementById("partsModal");
|
||||||
|
if (!modalElement) return;
|
||||||
|
|
||||||
|
$("#trfHeader").text(iddatadb || idquotations || "");
|
||||||
|
|
||||||
|
const visibleIddatadbList = Array.isArray(window.gridData)
|
||||||
|
? window.gridData
|
||||||
|
.map((r) => parseInt(r.iddatadb, 10))
|
||||||
|
.filter((v) => !isNaN(v) && v > 0)
|
||||||
|
: [];
|
||||||
|
|
||||||
|
$("#partsModal")
|
||||||
|
.data("iddatadb", iddatadb)
|
||||||
|
.data("idquotations", idquotations)
|
||||||
|
.data("visible-iddatadb-list", visibleIddatadbList);
|
||||||
|
|
||||||
|
let modal = bootstrap.Modal.getInstance(modalElement);
|
||||||
|
if (!modal)
|
||||||
|
modal = new bootstrap.Modal(modalElement, {
|
||||||
|
backdrop: true,
|
||||||
|
keyboard: true,
|
||||||
|
focus: true,
|
||||||
|
});
|
||||||
|
modal.show();
|
||||||
|
|
||||||
|
if (typeof window.loadParts === "function") {
|
||||||
|
window.loadParts(iddatadb, idquotations);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
error: function (xhr, status, error) {
|
||||||
|
console.error("Error loading parts:", error);
|
||||||
|
},
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
$(document).on("hidden.bs.modal", "#partsModal", function () {
|
||||||
|
const modalElement = document.getElementById("partsModal");
|
||||||
|
if (modalElement) {
|
||||||
|
const modal = bootstrap.Modal.getInstance(modalElement);
|
||||||
|
if (modal) modal.dispose();
|
||||||
|
}
|
||||||
|
$("#partsModalContainer").empty();
|
||||||
|
$(".modal-backdrop").remove();
|
||||||
|
$("body").removeClass("modal-open").css("padding-right", "");
|
||||||
|
});
|
||||||
|
|
||||||
|
// ── Tested Component quick add ───────────────────────────────────────
|
||||||
|
$(document).on("click", ".add-part-btn", async function () {
|
||||||
|
const iddatadb = $(this).data("iddatadb") || null;
|
||||||
|
const rowIndex = parseInt($(this).data("row"));
|
||||||
|
const row = window.gridData?.[rowIndex];
|
||||||
|
const id = iddatadb || (row ? row.iddatadb : null);
|
||||||
|
if (!id) return;
|
||||||
|
|
||||||
|
const $cell = $(this).closest(".grid-cell, div");
|
||||||
|
const $input = $cell.find("input");
|
||||||
|
const raw = ($input.val() || "").trim();
|
||||||
|
const parts = raw
|
||||||
|
.split("|")
|
||||||
|
.map((s) => s.trim())
|
||||||
|
.filter((s) => s.length > 0);
|
||||||
|
const uniqueParts = [...new Set(parts)];
|
||||||
|
|
||||||
|
if (!uniqueParts.length) {
|
||||||
|
alert("Insert a description first.");
|
||||||
|
$input.focus();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
for (const p of uniqueParts) {
|
||||||
|
const formData = new FormData();
|
||||||
|
formData.append("iddatadb", id);
|
||||||
|
formData.append("part_description", p);
|
||||||
|
await fetch("add_part_quick.php", {
|
||||||
|
method: "POST",
|
||||||
|
body: formData,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
if (row) {
|
||||||
|
row.tested_component = raw;
|
||||||
|
row._dirty = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
alert(`Added ${uniqueParts.length} part(s).`);
|
||||||
|
$input.val(raw);
|
||||||
|
} catch (e) {
|
||||||
|
alert("Error: " + e.message);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// ── Delete row ───────────────────────────────────────────────────────
|
||||||
|
let deleteIddatadb = null;
|
||||||
|
let deleteRowIndex = null;
|
||||||
|
|
||||||
|
$(document).on("click", ".delete-btn", function () {
|
||||||
|
deleteIddatadb = $(this).data("iddatadb");
|
||||||
|
deleteRowIndex = parseInt($(this).data("row"));
|
||||||
|
const modalEl = document.getElementById("deleteConfirmModal");
|
||||||
|
if (modalEl) {
|
||||||
|
document.getElementById("deleteIddatadbText").textContent =
|
||||||
|
deleteIddatadb;
|
||||||
|
new bootstrap.Modal(modalEl).show();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
$(document).on("click", "#deleteConfirmBtn", async function () {
|
||||||
|
const modalEl = document.getElementById("deleteConfirmModal");
|
||||||
|
const modal = bootstrap.Modal.getInstance(modalEl);
|
||||||
|
if (modal) modal.hide();
|
||||||
|
|
||||||
|
if (!deleteIddatadb) return;
|
||||||
|
|
||||||
|
try {
|
||||||
|
const resp = await fetch("delete_record.php", {
|
||||||
|
method: "POST",
|
||||||
|
headers: { "Content-Type": "application/json" },
|
||||||
|
body: JSON.stringify({ id: deleteIddatadb }),
|
||||||
|
});
|
||||||
|
const result = await resp.json();
|
||||||
|
|
||||||
|
if (result.success) {
|
||||||
|
// Remove from gridData
|
||||||
|
const idx = window.gridData.findIndex(
|
||||||
|
(r) => r.iddatadb === deleteIddatadb,
|
||||||
|
);
|
||||||
|
if (idx >= 0) window.gridData.splice(idx, 1);
|
||||||
|
|
||||||
|
// Re-render
|
||||||
|
const gr = window.gridRenderer;
|
||||||
|
if (gr) gr.renderVisibleRows();
|
||||||
|
} else {
|
||||||
|
alert("Error: " + result.message);
|
||||||
|
}
|
||||||
|
} catch (e) {
|
||||||
|
alert("Error: " + e.message);
|
||||||
|
}
|
||||||
|
|
||||||
|
deleteIddatadb = null;
|
||||||
|
deleteRowIndex = null;
|
||||||
|
});
|
||||||
|
|
||||||
|
// ── Add new row ──────────────────────────────────────────────────────
|
||||||
|
$(document).on("click", "#addRowBtn", async function () {
|
||||||
|
const btn = this;
|
||||||
|
btn.disabled = true;
|
||||||
|
|
||||||
|
try {
|
||||||
|
const templateId = window.gridMeta?.templateId;
|
||||||
|
if (!templateId) {
|
||||||
|
alert("Template ID missing");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const urlParams = new URLSearchParams(window.location.search);
|
||||||
|
const importref = urlParams.get("importref") || "";
|
||||||
|
const resp = await fetch("add_record.php", {
|
||||||
|
method: "POST",
|
||||||
|
headers: { "Content-Type": "application/json" },
|
||||||
|
body: JSON.stringify({
|
||||||
|
template_id: templateId,
|
||||||
|
importreferencecode: importref,
|
||||||
|
}),
|
||||||
|
});
|
||||||
|
const result = await resp.json();
|
||||||
|
|
||||||
|
if (result.success && result.iddatadb) {
|
||||||
|
// Build new row object
|
||||||
|
const newRow = {
|
||||||
|
iddatadb: result.iddatadb,
|
||||||
|
status: "i",
|
||||||
|
idclient: window.gridMeta?.defaultIdclient || "",
|
||||||
|
cliente_fornitore_id: null,
|
||||||
|
commessaweb: null,
|
||||||
|
user_name: result.user_name || "",
|
||||||
|
importreferencecode: result.importreferencecode || "",
|
||||||
|
filename_import: "",
|
||||||
|
importdate: new Date()
|
||||||
|
.toISOString()
|
||||||
|
.slice(0, 19)
|
||||||
|
.replace("T", " "),
|
||||||
|
fixedFields: {},
|
||||||
|
details: {},
|
||||||
|
mainFieldValue: "",
|
||||||
|
_dirty: false,
|
||||||
|
};
|
||||||
|
|
||||||
|
// Add to beginning of gridData
|
||||||
|
window.gridData.unshift(newRow);
|
||||||
|
|
||||||
|
// Re-render
|
||||||
|
const gr = window.gridRenderer;
|
||||||
|
if (gr) gr.renderVisibleRows();
|
||||||
|
|
||||||
|
// Highlight new row briefly
|
||||||
|
const newGridRow = document.querySelector(
|
||||||
|
`.grid-row[data-id="${result.iddatadb}"]`,
|
||||||
|
);
|
||||||
|
if (newGridRow) {
|
||||||
|
newGridRow.classList.add("row-just-created");
|
||||||
|
newGridRow.scrollIntoView({
|
||||||
|
behavior: "smooth",
|
||||||
|
block: "center",
|
||||||
|
});
|
||||||
|
setTimeout(
|
||||||
|
() => newGridRow.classList.remove("row-just-created"),
|
||||||
|
4000,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
alert("Error: " + (result.message || "Unknown error"));
|
||||||
|
}
|
||||||
|
} catch (e) {
|
||||||
|
alert("Error: " + e.message);
|
||||||
|
} finally {
|
||||||
|
btn.disabled = false;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
})();
|
||||||
@@ -143,7 +143,7 @@ $(document).ready(function () {
|
|||||||
if (iddatadb) {
|
if (iddatadb) {
|
||||||
if (matrici.length === 0) {
|
if (matrici.length === 0) {
|
||||||
$.ajax({
|
$.ajax({
|
||||||
url: "get_matrici_db.php",
|
url: "get_matrici.php",
|
||||||
method: "GET",
|
method: "GET",
|
||||||
dataType: "json",
|
dataType: "json",
|
||||||
success: function (data) {
|
success: function (data) {
|
||||||
|
|||||||
+605
-189
File diff suppressed because it is too large
Load Diff
+102
-4
@@ -1,5 +1,5 @@
|
|||||||
document.addEventListener("DOMContentLoaded", function () {
|
document.addEventListener("DOMContentLoaded", function () {
|
||||||
// Funzione per caricare il contenuto del popup
|
// Funzione per caricare il contenuto del popup (exported for external use)
|
||||||
async function loadPopupContent(iddatadb, idquotations) {
|
async function loadPopupContent(iddatadb, idquotations) {
|
||||||
const popupContent = document.getElementById("popupContent");
|
const popupContent = document.getElementById("popupContent");
|
||||||
if (!popupContent) {
|
if (!popupContent) {
|
||||||
@@ -475,6 +475,11 @@ document.addEventListener("DOMContentLoaded", function () {
|
|||||||
);
|
);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (canvas) {
|
||||||
|
canvas.dispose();
|
||||||
|
}
|
||||||
|
|
||||||
canvas = new fabric.Canvas("collageCanvas", {
|
canvas = new fabric.Canvas("collageCanvas", {
|
||||||
backgroundColor: "#fff",
|
backgroundColor: "#fff",
|
||||||
selection: true,
|
selection: true,
|
||||||
@@ -583,6 +588,7 @@ document.addEventListener("DOMContentLoaded", function () {
|
|||||||
if (isCropping || isRemovingBackground) {
|
if (isCropping || isRemovingBackground) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const state = JSON.stringify(
|
const state = JSON.stringify(
|
||||||
canvas.toJSON([
|
canvas.toJSON([
|
||||||
"cornerColor",
|
"cornerColor",
|
||||||
@@ -590,12 +596,16 @@ document.addEventListener("DOMContentLoaded", function () {
|
|||||||
"cornerSize",
|
"cornerSize",
|
||||||
"borderColor",
|
"borderColor",
|
||||||
"transparentCorners",
|
"transparentCorners",
|
||||||
|
"backgroundImage",
|
||||||
]),
|
]),
|
||||||
);
|
);
|
||||||
|
|
||||||
history.push(state);
|
history.push(state);
|
||||||
|
|
||||||
if (history.length > maxHistory) {
|
if (history.length > maxHistory) {
|
||||||
history.shift();
|
history.shift();
|
||||||
}
|
}
|
||||||
|
|
||||||
updateButtons();
|
updateButtons();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -603,10 +613,11 @@ document.addEventListener("DOMContentLoaded", function () {
|
|||||||
if (history.length <= 1) {
|
if (history.length <= 1) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
history.pop();
|
history.pop();
|
||||||
const previousState = history[history.length - 1];
|
const previousState = history[history.length - 1];
|
||||||
|
|
||||||
if (previousState) {
|
if (previousState) {
|
||||||
canvas.clear();
|
|
||||||
canvas.loadFromJSON(previousState, () => {
|
canvas.loadFromJSON(previousState, () => {
|
||||||
canvas.renderAll();
|
canvas.renderAll();
|
||||||
updateLayersPanel();
|
updateLayersPanel();
|
||||||
@@ -615,8 +626,11 @@ document.addEventListener("DOMContentLoaded", function () {
|
|||||||
} else {
|
} else {
|
||||||
console.warn("Nessuno stato precedente disponibile");
|
console.warn("Nessuno stato precedente disponibile");
|
||||||
canvas.clear();
|
canvas.clear();
|
||||||
canvas.setBackgroundColor("#fff");
|
canvas.backgroundImage = null;
|
||||||
canvas.renderAll();
|
canvas.setBackgroundColor(
|
||||||
|
"#fff",
|
||||||
|
canvas.renderAll.bind(canvas),
|
||||||
|
);
|
||||||
updateLayersPanel();
|
updateLayersPanel();
|
||||||
updateButtons();
|
updateButtons();
|
||||||
}
|
}
|
||||||
@@ -909,6 +923,61 @@ document.addEventListener("DOMContentLoaded", function () {
|
|||||||
canvas.renderAll();
|
canvas.renderAll();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function setCanvasBackground(imgPath) {
|
||||||
|
console.log("setCanvasBackground chiamata con:", imgPath);
|
||||||
|
|
||||||
|
if (!canvas) {
|
||||||
|
console.error("Canvas non inizializzato");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
fabric.Image.fromURL(
|
||||||
|
imgPath,
|
||||||
|
(img) => {
|
||||||
|
const canvasWidth = canvas.getWidth();
|
||||||
|
const canvasHeight = canvas.getHeight();
|
||||||
|
|
||||||
|
const imgWidth = img.width;
|
||||||
|
const imgHeight = img.height;
|
||||||
|
|
||||||
|
if (!imgWidth || !imgHeight) {
|
||||||
|
alert(
|
||||||
|
"Impossibile leggere le dimensioni dell'immagine.",
|
||||||
|
);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Scale to cover: l'immagine copre tutto il canvas mantenendo il ratio
|
||||||
|
const scale = Math.max(
|
||||||
|
canvasWidth / imgWidth,
|
||||||
|
canvasHeight / imgHeight,
|
||||||
|
);
|
||||||
|
|
||||||
|
img.set({
|
||||||
|
originX: "left",
|
||||||
|
originY: "top",
|
||||||
|
scaleX: scale,
|
||||||
|
scaleY: scale,
|
||||||
|
left: (canvasWidth - imgWidth * scale) / 2,
|
||||||
|
top: (canvasHeight - imgHeight * scale) / 2,
|
||||||
|
selectable: false,
|
||||||
|
evented: false,
|
||||||
|
hasControls: false,
|
||||||
|
hasBorders: false,
|
||||||
|
excludeFromExport: false,
|
||||||
|
});
|
||||||
|
|
||||||
|
canvas.setBackgroundImage(img, () => {
|
||||||
|
canvas.requestRenderAll();
|
||||||
|
saveCanvasState();
|
||||||
|
updateLayersPanel();
|
||||||
|
updateButtons();
|
||||||
|
});
|
||||||
|
},
|
||||||
|
{ crossOrigin: "anonymous" },
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
const addToCanvasBtn = document.getElementById("addToCanvasBtn");
|
const addToCanvasBtn = document.getElementById("addToCanvasBtn");
|
||||||
if (addToCanvasBtn) {
|
if (addToCanvasBtn) {
|
||||||
addToCanvasBtn.addEventListener("click", () => {
|
addToCanvasBtn.addEventListener("click", () => {
|
||||||
@@ -948,6 +1017,32 @@ document.addEventListener("DOMContentLoaded", function () {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const setBackgroundBtn = document.getElementById("setBackgroundBtn");
|
||||||
|
if (setBackgroundBtn) {
|
||||||
|
setBackgroundBtn.addEventListener("click", () => {
|
||||||
|
const checkboxes = document.querySelectorAll(
|
||||||
|
".photo-checkbox:checked",
|
||||||
|
);
|
||||||
|
|
||||||
|
if (checkboxes.length === 0) {
|
||||||
|
alert("Seleziona una foto da usare come sfondo!");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (checkboxes.length > 1) {
|
||||||
|
alert("Seleziona una sola foto per lo sfondo!");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const imgPath = checkboxes[0].getAttribute("data-path");
|
||||||
|
setCanvasBackground(imgPath);
|
||||||
|
|
||||||
|
checkboxes.forEach((cb) => {
|
||||||
|
cb.checked = false;
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
const saveCollageBtn = document.getElementById("saveCollageBtn");
|
const saveCollageBtn = document.getElementById("saveCollageBtn");
|
||||||
if (saveCollageBtn) {
|
if (saveCollageBtn) {
|
||||||
saveCollageBtn.addEventListener("click", async () => {
|
saveCollageBtn.addEventListener("click", async () => {
|
||||||
@@ -1123,4 +1218,7 @@ document.addEventListener("DOMContentLoaded", function () {
|
|||||||
closeBtn,
|
closeBtn,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Export for external use (gridData pages)
|
||||||
|
window.loadPopupContent = loadPopupContent;
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -298,8 +298,11 @@ $result->saveToFile($qrCodeFile);
|
|||||||
<?php endforeach; ?>
|
<?php endforeach; ?>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- Bottone per aggiungere selezionate al canvas -->
|
<!-- Bottoni azioni collage -->
|
||||||
|
<div style="display: flex; gap: 10px; flex-wrap: wrap; margin-bottom: 10px;">
|
||||||
<button id="addToCanvasBtn">Aggiungi Selezionate al Canvas</button>
|
<button id="addToCanvasBtn">Aggiungi Selezionate al Canvas</button>
|
||||||
|
<button id="setBackgroundBtn" type="button">Imposta Selezionata come Sfondo</button>
|
||||||
|
</div>
|
||||||
|
|
||||||
<!-- Canvas per editing -->
|
<!-- Canvas per editing -->
|
||||||
<canvas id="collageCanvas" width="960" height="720" style="border: 1px solid #ccc; margin-top: 20px;"></canvas>
|
<canvas id="collageCanvas" width="960" height="720" style="border: 1px solid #ccc; margin-top: 20px;"></canvas>
|
||||||
|
|||||||
@@ -0,0 +1,36 @@
|
|||||||
|
<?php
|
||||||
|
require_once dirname(__DIR__, 2) . '/vendor/autoload.php';
|
||||||
|
require_once dirname(__FILE__) . '/class/VisualLimsApiClient.class.php';
|
||||||
|
|
||||||
|
header('Content-Type: application/json; charset=utf-8');
|
||||||
|
|
||||||
|
ini_set('display_errors', '0');
|
||||||
|
error_reporting(E_ALL);
|
||||||
|
set_time_limit(20);
|
||||||
|
|
||||||
|
try {
|
||||||
|
$api = VisualLimsApiClient::getInstance();
|
||||||
|
|
||||||
|
$start = microtime(true);
|
||||||
|
|
||||||
|
// Chiamata minima: 1 solo record, 1 solo campo
|
||||||
|
$data = $api->get('Rapporto', [
|
||||||
|
'$top' => 1,
|
||||||
|
'$select' => 'IdRapporto'
|
||||||
|
]);
|
||||||
|
|
||||||
|
$elapsed = round(microtime(true) - $start, 3);
|
||||||
|
|
||||||
|
echo json_encode([
|
||||||
|
'success' => true,
|
||||||
|
'elapsed_seconds' => $elapsed,
|
||||||
|
'data' => $data
|
||||||
|
], JSON_PRETTY_PRINT | JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES);
|
||||||
|
} catch (Exception $e) {
|
||||||
|
http_response_code(500);
|
||||||
|
|
||||||
|
echo json_encode([
|
||||||
|
'success' => false,
|
||||||
|
'error' => $e->getMessage()
|
||||||
|
], JSON_PRETTY_PRINT | JSON_UNESCAPED_UNICODE);
|
||||||
|
}
|
||||||
@@ -9,48 +9,130 @@ try {
|
|||||||
throw new Exception("Invalid request method.");
|
throw new Exception("Invalid request method.");
|
||||||
}
|
}
|
||||||
|
|
||||||
// Recupera e sanifica i dati
|
// Retrieve and sanitize form data
|
||||||
$id = intval($_POST['id']);
|
$id = intval($_POST['id'] ?? 0);
|
||||||
$name = trim($_POST['name']);
|
$name = trim($_POST['name'] ?? '');
|
||||||
$header_row = intval($_POST['header_row']);
|
$source_type = strtoupper(trim($_POST['source_type'] ?? 'XLS'));
|
||||||
$start_column = trim($_POST['start_column']);
|
|
||||||
|
$header_row = isset($_POST['header_row']) && $_POST['header_row'] !== ''
|
||||||
|
? intval($_POST['header_row'])
|
||||||
|
: null;
|
||||||
|
|
||||||
|
$start_column = trim($_POST['start_column'] ?? '');
|
||||||
|
|
||||||
|
$xls_sheet_index = isset($_POST['xls_sheet_index']) && $_POST['xls_sheet_index'] !== ''
|
||||||
|
? intval($_POST['xls_sheet_index'])
|
||||||
|
: 0;
|
||||||
|
|
||||||
|
$api_config_id = isset($_POST['api_config_id']) && $_POST['api_config_id'] !== ''
|
||||||
|
? intval($_POST['api_config_id'])
|
||||||
|
: null;
|
||||||
|
|
||||||
$description = trim($_POST['description'] ?? '');
|
$description = trim($_POST['description'] ?? '');
|
||||||
$target_table = trim($_POST['target_table']);
|
$target_table = trim($_POST['target_table'] ?? 'datadb');
|
||||||
$idclient = intval($_POST['client_id'] ?? 0); // Usa client_id dal form
|
$idclient = intval($_POST['client_id'] ?? 0);
|
||||||
$clientname = trim($_POST['client_name'] ?? ''); // Usa client_name dal form
|
$clientname = trim($_POST['client_name'] ?? '');
|
||||||
$idschema = intval($_POST['idschema'] ?? 0); // Nuovo campo
|
$idschema = intval($_POST['idschema'] ?? 0);
|
||||||
$schemaname = trim($_POST['schemaname'] ?? ''); // Corretto da schemamaname
|
$schemaname = trim($_POST['schemaname'] ?? '');
|
||||||
$idroutine = isset($_POST['idroutine']) && $_POST['idroutine'] !== '' ? intval($_POST['idroutine']) : null; // Aggiunto idroutine
|
$idroutine = isset($_POST['idroutine']) && $_POST['idroutine'] !== '' ? intval($_POST['idroutine']) : null;
|
||||||
$button_size = trim($_POST['button_size'] ?? 'medium'); // Nuovo campo
|
$button_size = trim($_POST['button_size'] ?? 'medium');
|
||||||
$button_bg_color = trim($_POST['button_bg_color'] ?? '#007bff'); // Nuovo campo
|
$button_bg_color = trim($_POST['button_bg_color'] ?? '#007bff');
|
||||||
$button_text_color = trim($_POST['button_text_color'] ?? '#ffffff'); // Nuovo campo
|
$button_text_color = trim($_POST['button_text_color'] ?? '#ffffff');
|
||||||
$button_label = trim($_POST['button_label'] ?? 'Click Me'); // Nuovo campo
|
$button_label = trim($_POST['button_label'] ?? 'Click Me');
|
||||||
|
|
||||||
// Controllo sui campi obbligatori
|
// Allowed source types
|
||||||
if (empty($id) || empty($name) || empty($header_row) || empty($start_column) || empty($target_table) || $idschema <= 0) {
|
if (!in_array($source_type, ['XLS', 'API', 'JSON', 'PDF'], true)) {
|
||||||
throw new Exception("All fields marked with * are required, including schema.");
|
$source_type = 'XLS';
|
||||||
}
|
}
|
||||||
|
|
||||||
// Validazione del idclient
|
// Required fields validation
|
||||||
if ($idclient <= 0) {
|
if ($id <= 0 || $name === '' || $target_table === '' || $idclient <= 0 || $idschema <= 0) {
|
||||||
throw new Exception("Please select a valid client.");
|
throw new Exception("All fields marked with * are required, including client and schema.");
|
||||||
}
|
}
|
||||||
|
|
||||||
// Connessione al database
|
// XLS-only validation
|
||||||
|
if ($source_type === 'XLS') {
|
||||||
|
if ($header_row === null || $header_row <= 0 || $start_column === '') {
|
||||||
|
throw new Exception("Header Row and Start Column are required for XLS templates.");
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($xls_sheet_index < 0) {
|
||||||
|
throw new Exception("XLS Sheet Number cannot be negative.");
|
||||||
|
}
|
||||||
|
|
||||||
|
$api_config_id = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
// API/JSON validation
|
||||||
|
if ($source_type === 'API' || $source_type === 'JSON') {
|
||||||
|
if (empty($api_config_id)) {
|
||||||
|
throw new Exception("API/JSON configuration is required for API or JSON templates.");
|
||||||
|
}
|
||||||
|
|
||||||
|
$header_row = null;
|
||||||
|
$start_column = null;
|
||||||
|
$xls_sheet_index = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
// PDF currently does not require XLS coordinates or API configuration
|
||||||
|
if ($source_type === 'PDF') {
|
||||||
|
$header_row = null;
|
||||||
|
$start_column = null;
|
||||||
|
$xls_sheet_index = null;
|
||||||
|
$api_config_id = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Database connection
|
||||||
$db = DBHandlerSelect::getInstance();
|
$db = DBHandlerSelect::getInstance();
|
||||||
$pdo = $db->getConnection();
|
$pdo = $db->getConnection();
|
||||||
|
|
||||||
// Aggiorna il database, includendo i nuovi campi
|
// Optional check: verify API configuration exists and is active
|
||||||
$stmt = $pdo->prepare("UPDATE excel_templates
|
if ($api_config_id !== null) {
|
||||||
SET name = ?, header_row = ?, start_column = ?, description = ?, target_table = ?,
|
$stmt = $pdo->prepare("
|
||||||
idclient = ?, clientname = ?, schemaname = ?, idschema = ?, idroutine = ?,
|
SELECT COUNT(*)
|
||||||
button_size = ?, button_bg_color = ?, button_text_color = ?, button_label = ?,
|
FROM api_configurations
|
||||||
|
WHERE id = ?
|
||||||
|
AND is_active = 1
|
||||||
|
");
|
||||||
|
$stmt->execute([$api_config_id]);
|
||||||
|
|
||||||
|
if ((int)$stmt->fetchColumn() === 0) {
|
||||||
|
throw new Exception("Selected API/JSON configuration does not exist or is not active.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Update template
|
||||||
|
$stmt = $pdo->prepare("
|
||||||
|
UPDATE excel_templates
|
||||||
|
SET
|
||||||
|
name = ?,
|
||||||
|
source_type = ?,
|
||||||
|
header_row = ?,
|
||||||
|
start_column = ?,
|
||||||
|
xls_sheet_index = ?,
|
||||||
|
api_config_id = ?,
|
||||||
|
description = ?,
|
||||||
|
target_table = ?,
|
||||||
|
idclient = ?,
|
||||||
|
clientname = ?,
|
||||||
|
schemaname = ?,
|
||||||
|
idschema = ?,
|
||||||
|
idroutine = ?,
|
||||||
|
button_size = ?,
|
||||||
|
button_bg_color = ?,
|
||||||
|
button_text_color = ?,
|
||||||
|
button_label = ?,
|
||||||
updated_at = NOW()
|
updated_at = NOW()
|
||||||
WHERE id = ?");
|
WHERE id = ?
|
||||||
|
");
|
||||||
|
|
||||||
$stmt->execute([
|
$stmt->execute([
|
||||||
$name,
|
$name,
|
||||||
|
$source_type,
|
||||||
$header_row,
|
$header_row,
|
||||||
$start_column,
|
$start_column,
|
||||||
|
$xls_sheet_index,
|
||||||
|
$api_config_id,
|
||||||
$description,
|
$description,
|
||||||
$target_table,
|
$target_table,
|
||||||
$idclient,
|
$idclient,
|
||||||
@@ -65,12 +147,10 @@ try {
|
|||||||
$id
|
$id
|
||||||
]);
|
]);
|
||||||
|
|
||||||
// rowCount potrebbe essere 0 se non ci sono modifiche, quindi consideriamo comunque un successo
|
|
||||||
$response["success"] = true;
|
$response["success"] = true;
|
||||||
$response["message"] = "Template updated successfully!";
|
$response["message"] = "Template updated successfully!";
|
||||||
} catch (Exception $e) {
|
} catch (Exception $e) {
|
||||||
$response["message"] = $e->getMessage();
|
$response["message"] = $e->getMessage();
|
||||||
}
|
}
|
||||||
|
|
||||||
// Restituisce un JSON per il fetch
|
|
||||||
echo json_encode($response);
|
echo json_encode($response);
|
||||||
|
|||||||
@@ -10,6 +10,9 @@ session_start();
|
|||||||
// Includi PHPSpreadsheet
|
// Includi PHPSpreadsheet
|
||||||
require_once '../../vendor/autoload.php';
|
require_once '../../vendor/autoload.php';
|
||||||
|
|
||||||
|
Dotenv\Dotenv::createImmutable(dirname(__DIR__, 2))->safeLoad();
|
||||||
|
date_default_timezone_set($_ENV['APP_TIMEZONE'] ?? 'Europe/Rome');
|
||||||
|
|
||||||
$response = ['error' => '', 'rows' => [], 'columns' => [], 'template_id' => 0, 'filename' => ''];
|
$response = ['error' => '', 'rows' => [], 'columns' => [], 'template_id' => 0, 'filename' => ''];
|
||||||
|
|
||||||
try {
|
try {
|
||||||
@@ -70,7 +73,7 @@ try {
|
|||||||
$columnLetter = \PhpOffice\PhpSpreadsheet\Cell\Coordinate::stringFromColumnIndex($col);
|
$columnLetter = \PhpOffice\PhpSpreadsheet\Cell\Coordinate::stringFromColumnIndex($col);
|
||||||
$cell = $worksheet->getCell($columnLetter . $header_row);
|
$cell = $worksheet->getCell($columnLetter . $header_row);
|
||||||
$cellValue = $cell ? $cell->getCalculatedValue() : ''; // Usa getCalculatedValue per le formule
|
$cellValue = $cell ? $cell->getCalculatedValue() : ''; // Usa getCalculatedValue per le formule
|
||||||
$headerRowData[] = htmlspecialchars($cellValue ?: '');
|
$headerRowData[] = $cellValue ?: '';
|
||||||
}
|
}
|
||||||
|
|
||||||
// Estrai i dati a partire dalla riga successiva
|
// Estrai i dati a partire dalla riga successiva
|
||||||
@@ -80,7 +83,7 @@ try {
|
|||||||
$columnLetter = \PhpOffice\PhpSpreadsheet\Cell\Coordinate::stringFromColumnIndex($col);
|
$columnLetter = \PhpOffice\PhpSpreadsheet\Cell\Coordinate::stringFromColumnIndex($col);
|
||||||
$cell = $worksheet->getCell($columnLetter . $row);
|
$cell = $worksheet->getCell($columnLetter . $row);
|
||||||
$cellValue = $cell ? $cell->getCalculatedValue() : ''; // Usa getCalculatedValue per le formule
|
$cellValue = $cell ? $cell->getCalculatedValue() : ''; // Usa getCalculatedValue per le formule
|
||||||
$rowData[] = htmlspecialchars($cellValue ?: '');
|
$rowData[] = $cellValue ?: '';
|
||||||
}
|
}
|
||||||
if (!empty(array_filter($rowData))) {
|
if (!empty(array_filter($rowData))) {
|
||||||
$excelData[] = $rowData;
|
$excelData[] = $rowData;
|
||||||
|
|||||||
@@ -11,17 +11,105 @@ session_start();
|
|||||||
require_once '../../vendor/autoload.php';
|
require_once '../../vendor/autoload.php';
|
||||||
require_once __DIR__ . '/class/db-functions.php';
|
require_once __DIR__ . '/class/db-functions.php';
|
||||||
|
|
||||||
$response = ['error' => '', 'rows' => [], 'columns' => [], 'template_id' => 0, 'filename' => '', 'apply_routine' => false];
|
use PhpOffice\PhpSpreadsheet\IOFactory;
|
||||||
|
use PhpOffice\PhpSpreadsheet\Cell\Coordinate;
|
||||||
|
|
||||||
|
$response = [
|
||||||
|
'error' => '',
|
||||||
|
'rows' => [],
|
||||||
|
'columns' => [],
|
||||||
|
'template_id' => 0,
|
||||||
|
'filename' => '',
|
||||||
|
'apply_routine' => false
|
||||||
|
];
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Converts a column value to a PhpSpreadsheet 1-based column index.
|
||||||
|
* Accepted values:
|
||||||
|
* - "A" => 1
|
||||||
|
* - "B" => 2
|
||||||
|
* - "AA" => 27
|
||||||
|
* - "1" => 1
|
||||||
|
* - 1 => 1
|
||||||
|
*/
|
||||||
|
function normalizeColumnIndex($value): int
|
||||||
|
{
|
||||||
|
$value = trim((string)$value);
|
||||||
|
|
||||||
|
if ($value === '') {
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ctype_digit($value)) {
|
||||||
|
return max(1, (int)$value);
|
||||||
|
}
|
||||||
|
|
||||||
|
$value = strtoupper($value);
|
||||||
|
|
||||||
|
if (preg_match('/^[A-Z]+$/', $value)) {
|
||||||
|
return Coordinate::columnIndexFromString($value);
|
||||||
|
}
|
||||||
|
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
if ($_SERVER['REQUEST_METHOD'] === 'POST' && isset($_FILES['excel_file'])) {
|
if ($_SERVER['REQUEST_METHOD'] === 'POST' && isset($_FILES['excel_file'])) {
|
||||||
$template_id = isset($_POST['template_id']) ? intval($_POST['template_id']) : 0;
|
$template_id = isset($_POST['template_id']) ? intval($_POST['template_id']) : 0;
|
||||||
$header_row = isset($_POST['header_row']) ? intval($_POST['header_row']) : 1;
|
|
||||||
$start_column = isset($_POST['start_column']) ? intval($_POST['start_column']) : 1;
|
if ($template_id <= 0) {
|
||||||
|
throw new Exception("Template ID non valido.");
|
||||||
|
}
|
||||||
|
|
||||||
|
// Connessione al database
|
||||||
|
$db = DBHandlerSelect::getInstance();
|
||||||
|
$pdo = $db->getConnection();
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Recuperiamo i parametri direttamente dal template.
|
||||||
|
* Così non dipendiamo solo dal form e siamo sicuri di usare i dati salvati.
|
||||||
|
*/
|
||||||
|
$stmt = $pdo->prepare("
|
||||||
|
SELECT
|
||||||
|
id,
|
||||||
|
header_row,
|
||||||
|
start_column,
|
||||||
|
xls_sheet_index,
|
||||||
|
idroutine,
|
||||||
|
idclient
|
||||||
|
FROM excel_templates
|
||||||
|
WHERE id = ?
|
||||||
|
");
|
||||||
|
$stmt->execute([$template_id]);
|
||||||
|
$template = $stmt->fetch(PDO::FETCH_ASSOC);
|
||||||
|
|
||||||
|
if (!$template) {
|
||||||
|
throw new Exception("Template non trovato.");
|
||||||
|
}
|
||||||
|
|
||||||
|
$header_row = isset($template['header_row']) && $template['header_row'] !== null
|
||||||
|
? (int)$template['header_row']
|
||||||
|
: 1;
|
||||||
|
|
||||||
|
$start_column_raw = $template['start_column'] ?? 'A';
|
||||||
|
$start_column = normalizeColumnIndex($start_column_raw);
|
||||||
|
|
||||||
|
$xlsSheetIndex = isset($template['xls_sheet_index']) && $template['xls_sheet_index'] !== null
|
||||||
|
? (int)$template['xls_sheet_index']
|
||||||
|
: 0;
|
||||||
|
|
||||||
|
if ($header_row <= 0) {
|
||||||
|
$header_row = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($xlsSheetIndex < 0) {
|
||||||
|
$xlsSheetIndex = 0;
|
||||||
|
}
|
||||||
|
|
||||||
// Debug del template_id ricevuto
|
// Debug del template_id ricevuto
|
||||||
error_log("Received template_id from POST: " . print_r($_POST['template_id'], true));
|
error_log("Received template_id from POST: " . print_r($_POST['template_id'], true));
|
||||||
error_log("Converted template_id: $template_id");
|
error_log("Converted template_id: $template_id");
|
||||||
|
error_log("Template XLS settings - header_row: $header_row, start_column_raw: $start_column_raw, start_column_index: $start_column, xls_sheet_index: $xlsSheetIndex");
|
||||||
|
|
||||||
$file = $_FILES['excel_file'];
|
$file = $_FILES['excel_file'];
|
||||||
$fileError = $file['error'];
|
$fileError = $file['error'];
|
||||||
@@ -38,23 +126,32 @@ try {
|
|||||||
$originalFilename = basename($file['name']);
|
$originalFilename = basename($file['name']);
|
||||||
$newFilename = "{$iduserlogin}-{$timestamp}-{$originalFilename}";
|
$newFilename = "{$iduserlogin}-{$timestamp}-{$originalFilename}";
|
||||||
$importFolder = __DIR__ . '/imported_trf/';
|
$importFolder = __DIR__ . '/imported_trf/';
|
||||||
|
|
||||||
if (!file_exists($importFolder)) {
|
if (!file_exists($importFolder)) {
|
||||||
mkdir($importFolder, 0777, true);
|
mkdir($importFolder, 0777, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
$destination = $importFolder . $newFilename;
|
$destination = $importFolder . $newFilename;
|
||||||
|
|
||||||
// Sposta il file
|
// Sposta il file
|
||||||
if (!move_uploaded_file($file['tmp_name'], $destination)) {
|
if (!move_uploaded_file($file['tmp_name'], $destination)) {
|
||||||
throw new Exception("Errore durante lo spostamento del file in $destination");
|
throw new Exception("Errore durante lo spostamento del file in $destination");
|
||||||
}
|
}
|
||||||
|
|
||||||
error_log("File spostato con successo in: $destination");
|
error_log("File spostato con successo in: $destination");
|
||||||
|
|
||||||
// Connessione al database
|
|
||||||
$db = DBHandlerSelect::getInstance();
|
|
||||||
$pdo = $db->getConnection();
|
|
||||||
|
|
||||||
// Recupera il mapping da template_mapping
|
// Recupera il mapping da template_mapping
|
||||||
$stmt = $pdo->prepare("SELECT field_id AS excel_column, field_id AS mysql_column, data_type, is_required, default_value, is_manual FROM template_mapping WHERE template_id = ?");
|
$stmt = $pdo->prepare("
|
||||||
|
SELECT
|
||||||
|
field_id AS excel_column,
|
||||||
|
field_id AS mysql_column,
|
||||||
|
data_type,
|
||||||
|
is_required,
|
||||||
|
default_value,
|
||||||
|
is_manual
|
||||||
|
FROM template_mapping
|
||||||
|
WHERE template_id = ?
|
||||||
|
");
|
||||||
$stmt->execute([$template_id]);
|
$stmt->execute([$template_id]);
|
||||||
$mappings = $stmt->fetchAll(PDO::FETCH_ASSOC);
|
$mappings = $stmt->fetchAll(PDO::FETCH_ASSOC);
|
||||||
|
|
||||||
@@ -65,55 +162,175 @@ try {
|
|||||||
$response['error'] = "Nessun mapping trovato per il template con ID $template_id";
|
$response['error'] = "Nessun mapping trovato per il template con ID $template_id";
|
||||||
} else {
|
} else {
|
||||||
// Carica il file rinominato con PHPSpreadsheet
|
// Carica il file rinominato con PHPSpreadsheet
|
||||||
$spreadsheet = \PhpOffice\PhpSpreadsheet\IOFactory::load($destination);
|
$spreadsheet = IOFactory::load($destination);
|
||||||
$worksheet = $spreadsheet->getActiveSheet();
|
|
||||||
|
$sheetCount = $spreadsheet->getSheetCount();
|
||||||
|
$sheetNames = $spreadsheet->getSheetNames();
|
||||||
|
|
||||||
|
if ($sheetCount <= 0) {
|
||||||
|
throw new Exception("Il file XLS non contiene fogli.");
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($xlsSheetIndex >= $sheetCount) {
|
||||||
|
throw new Exception(
|
||||||
|
"Il foglio XLS selezionato non esiste. " .
|
||||||
|
"Sheet Number selezionato: {$xlsSheetIndex}. " .
|
||||||
|
"Fogli disponibili: " . implode(", ", array_map(
|
||||||
|
fn($name, $index) => "{$index}={$name}",
|
||||||
|
$sheetNames,
|
||||||
|
array_keys($sheetNames)
|
||||||
|
))
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Usa il foglio configurato nel template
|
||||||
|
$worksheet = $spreadsheet->getSheet($xlsSheetIndex);
|
||||||
|
$selectedSheetName = $worksheet->getTitle();
|
||||||
|
|
||||||
|
error_log("Selected XLS sheet - index: {$xlsSheetIndex}, name: {$selectedSheetName}");
|
||||||
|
|
||||||
$highestRow = $worksheet->getHighestRow();
|
$highestRow = $worksheet->getHighestRow();
|
||||||
$highestColumn = $worksheet->getHighestColumn();
|
$highestColumn = $worksheet->getHighestColumn();
|
||||||
$highestColumnIndex = \PhpOffice\PhpSpreadsheet\Cell\Coordinate::columnIndexFromString($highestColumn);
|
$highestColumnIndex = Coordinate::columnIndexFromString($highestColumn);
|
||||||
|
|
||||||
$startRow = max(1, $header_row);
|
$startRow = max(1, $header_row);
|
||||||
$startColumn = max(1, $start_column);
|
$startColumn = max(1, $start_column);
|
||||||
|
|
||||||
|
// Advance startColumn to first non-empty cell in header row, matching JS behavior
|
||||||
|
for ($sc = $startColumn; $sc <= $highestColumnIndex; $sc++) {
|
||||||
|
$cl = Coordinate::stringFromColumnIndex($sc);
|
||||||
|
$cv = trim((string)($worksheet->getCell($cl . $header_row)->getCalculatedValue() ?? ''));
|
||||||
|
|
||||||
|
if ($cv !== '') {
|
||||||
|
$startColumn = $sc;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Debug dei parametri
|
// Debug dei parametri
|
||||||
error_log("Processing - template_id: $template_id, startRow: $startRow, startColumn: $startColumn, highestRow: $highestRow, highestColumn: $highestColumn, highestColumnIndex: $highestColumnIndex");
|
error_log(
|
||||||
|
"Processing - template_id: $template_id, " .
|
||||||
|
"sheetIndex: $xlsSheetIndex, sheetName: $selectedSheetName, " .
|
||||||
|
"startRow: $startRow, startColumn: $startColumn, " .
|
||||||
|
"highestRow: $highestRow, highestColumn: $highestColumn, highestColumnIndex: $highestColumnIndex"
|
||||||
|
);
|
||||||
|
|
||||||
// Validazione degli indici
|
// Validazione degli indici
|
||||||
if ($startRow > $highestRow) {
|
if ($startRow > $highestRow) {
|
||||||
$response['error'] = "La riga di partenza ($startRow) supera il numero totale di righe ($highestRow).";
|
$response['error'] = "La riga di partenza ($startRow) supera il numero totale di righe ($highestRow) del foglio '$selectedSheetName'.";
|
||||||
} elseif ($startColumn > $highestColumnIndex) {
|
} elseif ($startColumn > $highestColumnIndex) {
|
||||||
$response['error'] = "La colonna di partenza ($startColumn) supera il numero totale di colonne ($highestColumnIndex).";
|
$response['error'] = "La colonna di partenza ($startColumn) supera il numero totale di colonne ($highestColumnIndex) del foglio '$selectedSheetName'.";
|
||||||
} else {
|
} else {
|
||||||
$excelData = [];
|
$excelData = [];
|
||||||
// Estrai la riga degli header
|
|
||||||
$headerRowData = [];
|
// Build merge map for header row: physCol -> mergeStartCol
|
||||||
for ($col = $startColumn; $col <= $highestColumnIndex; $col++) {
|
$mergeStartMap = [];
|
||||||
$columnLetter = \PhpOffice\PhpSpreadsheet\Cell\Coordinate::stringFromColumnIndex($col);
|
|
||||||
$cell = $worksheet->getCell($columnLetter . $header_row);
|
foreach ($worksheet->getMergeCells() as $range) {
|
||||||
$cellValue = $cell ? $cell->getCalculatedValue() : '';
|
[$startCell, $endCell] = explode(':', $range);
|
||||||
$headerRowData[] = htmlspecialchars($cellValue ?: '');
|
|
||||||
|
$mStartCol = Coordinate::columnIndexFromString(preg_replace('/\d+/', '', $startCell));
|
||||||
|
$mEndCol = Coordinate::columnIndexFromString(preg_replace('/\d+/', '', $endCell));
|
||||||
|
$mStartRow = (int)preg_replace('/[A-Z]+/i', '', $startCell);
|
||||||
|
$mEndRow = (int)preg_replace('/[A-Z]+/i', '', $endCell);
|
||||||
|
|
||||||
|
if ($header_row >= $mStartRow && $header_row <= $mEndRow) {
|
||||||
|
for ($c = $mStartCol; $c <= $mEndCol; $c++) {
|
||||||
|
$mergeStartMap[$c] = $mStartCol;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Estrai i dati a partire dalla riga successiva, includendo excelrow
|
// Build logical columns: each merge = one column
|
||||||
|
$logicalCols = []; // array of physical column indices, one per logical column
|
||||||
|
$seen = [];
|
||||||
|
|
||||||
|
for ($col = $startColumn; $col <= $highestColumnIndex; $col++) {
|
||||||
|
if (isset($mergeStartMap[$col])) {
|
||||||
|
$ms = $mergeStartMap[$col];
|
||||||
|
|
||||||
|
if (in_array($ms, $seen, true)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
$seen[] = $ms;
|
||||||
|
$logicalCols[] = $ms;
|
||||||
|
} else {
|
||||||
|
$logicalCols[] = $col;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Build header row using logical columns
|
||||||
|
$headerRowData = [];
|
||||||
|
$logicalNum = 0;
|
||||||
|
|
||||||
|
foreach ($logicalCols as $physCol) {
|
||||||
|
$logicalNum++;
|
||||||
|
|
||||||
|
$columnLetter = Coordinate::stringFromColumnIndex($physCol);
|
||||||
|
$cell = $worksheet->getCell($columnLetter . $header_row);
|
||||||
|
$cellValue = trim((string)($cell ? $cell->getCalculatedValue() : ''));
|
||||||
|
$cellValue = preg_replace('/[\r\n\t]+/', ' ', $cellValue);
|
||||||
|
|
||||||
|
// Empty headers get __empty_N__ to match mapping page
|
||||||
|
$headerRowData[] = ($cellValue !== '') ? $cellValue : '__empty_' . $logicalNum . '__';
|
||||||
|
}
|
||||||
|
|
||||||
|
error_log("Logical headers: " . json_encode($headerRowData));
|
||||||
|
error_log("Logical cols physical indices: " . json_encode($logicalCols));
|
||||||
|
|
||||||
|
// Find which logical columns have real headers
|
||||||
|
$headerFilledIndices = [];
|
||||||
|
|
||||||
|
foreach ($headerRowData as $idx => $hVal) {
|
||||||
|
if (!str_starts_with($hVal, '__empty_')) {
|
||||||
|
$headerFilledIndices[] = $idx;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
$minFilled = max(1, min(2, count($headerFilledIndices)));
|
||||||
|
|
||||||
|
// Extract data rows using logical columns
|
||||||
for ($row = $startRow + 1; $row <= $highestRow; $row++) {
|
for ($row = $startRow + 1; $row <= $highestRow; $row++) {
|
||||||
$rowData = [];
|
$rowData = [];
|
||||||
for ($col = $startColumn; $col <= $highestColumnIndex; $col++) {
|
|
||||||
$columnLetter = \PhpOffice\PhpSpreadsheet\Cell\Coordinate::stringFromColumnIndex($col);
|
foreach ($logicalCols as $physCol) {
|
||||||
|
$columnLetter = Coordinate::stringFromColumnIndex($physCol);
|
||||||
$cell = $worksheet->getCell($columnLetter . $row);
|
$cell = $worksheet->getCell($columnLetter . $row);
|
||||||
$cellValue = $cell ? $cell->getCalculatedValue() : '';
|
$cellValue = $cell ? $cell->getCalculatedValue() : '';
|
||||||
$rowData[] = htmlspecialchars($cellValue ?: '');
|
|
||||||
|
$rowData[] = $cellValue ?: '';
|
||||||
}
|
}
|
||||||
if (!empty(array_filter($rowData))) {
|
|
||||||
$excelData[] = ['data' => $rowData, 'excelrow' => $row];
|
// Count how many header columns have data in this row
|
||||||
|
$filledCount = 0;
|
||||||
|
|
||||||
|
foreach ($headerFilledIndices as $idx) {
|
||||||
|
if (isset($rowData[$idx]) && trim((string)$rowData[$idx]) !== '') {
|
||||||
|
$filledCount++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($filledCount >= $minFilled) {
|
||||||
|
$excelData[] = [
|
||||||
|
'data' => $rowData,
|
||||||
|
'excelrow' => $row
|
||||||
|
];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Recupera routine dal template
|
// Recupera routine dal template
|
||||||
$stmt = $pdo->prepare("SELECT idroutine, idclient FROM excel_templates WHERE id = ?");
|
if ($template && !empty($template['idroutine'])) {
|
||||||
$stmt->execute([$template_id]);
|
$stmtRoutine = $pdo->prepare("
|
||||||
$template = $stmt->fetch(PDO::FETCH_ASSOC);
|
SELECT
|
||||||
|
idroutine,
|
||||||
if ($template && $template['idroutine']) {
|
name,
|
||||||
$stmtRoutine = $pdo->prepare("SELECT idroutine, name, filename, headerrow, instruction FROM routine WHERE idroutine = ?");
|
filename,
|
||||||
|
headerrow,
|
||||||
|
instruction
|
||||||
|
FROM routine
|
||||||
|
WHERE idroutine = ?
|
||||||
|
");
|
||||||
$stmtRoutine->execute([$template['idroutine']]);
|
$stmtRoutine->execute([$template['idroutine']]);
|
||||||
$routineData = $stmtRoutine->fetch(PDO::FETCH_ASSOC);
|
$routineData = $stmtRoutine->fetch(PDO::FETCH_ASSOC);
|
||||||
|
|
||||||
@@ -125,6 +342,7 @@ try {
|
|||||||
'filename' => $routineData['filename'] ?? '',
|
'filename' => $routineData['filename'] ?? '',
|
||||||
'headerrow' => $routineData['headerrow'] ?? $header_row
|
'headerrow' => $routineData['headerrow'] ?? $header_row
|
||||||
];
|
];
|
||||||
|
|
||||||
error_log("Routine rilevata per template {$template_id}: " . print_r($routineData, true));
|
error_log("Routine rilevata per template {$template_id}: " . print_r($routineData, true));
|
||||||
} else {
|
} else {
|
||||||
error_log("Errore: Nessuna routine trovata per idroutine {$template['idroutine']}");
|
error_log("Errore: Nessuna routine trovata per idroutine {$template['idroutine']}");
|
||||||
@@ -141,6 +359,8 @@ try {
|
|||||||
$_SESSION['template_id'] = $template_id;
|
$_SESSION['template_id'] = $template_id;
|
||||||
$_SESSION['headers'] = $headerRowData;
|
$_SESSION['headers'] = $headerRowData;
|
||||||
$_SESSION['mappings'] = $mappings;
|
$_SESSION['mappings'] = $mappings;
|
||||||
|
$_SESSION['xls_sheet_index'] = $xlsSheetIndex;
|
||||||
|
$_SESSION['xls_sheet_name'] = $selectedSheetName;
|
||||||
|
|
||||||
// Includi excel_data nella risposta JSON in ogni caso
|
// Includi excel_data nella risposta JSON in ogni caso
|
||||||
$response['excel_data'] = $excelData;
|
$response['excel_data'] = $excelData;
|
||||||
@@ -148,6 +368,8 @@ try {
|
|||||||
$response['columns'] = $headerRowData;
|
$response['columns'] = $headerRowData;
|
||||||
$response['template_id'] = $template_id;
|
$response['template_id'] = $template_id;
|
||||||
$response['filename'] = $newFilename;
|
$response['filename'] = $newFilename;
|
||||||
|
$response['xls_sheet_index'] = $xlsSheetIndex;
|
||||||
|
$response['xls_sheet_name'] = $selectedSheetName;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
|
|||||||
@@ -9,42 +9,141 @@ try {
|
|||||||
throw new Exception("Invalid request method.");
|
throw new Exception("Invalid request method.");
|
||||||
}
|
}
|
||||||
|
|
||||||
// Recupera e sanifica i dati
|
// Retrieve and sanitize form data
|
||||||
$name = trim($_POST['name']);
|
$name = trim($_POST['name'] ?? '');
|
||||||
$header_row = intval($_POST['header_row']);
|
$source_type = strtoupper(trim($_POST['source_type'] ?? 'XLS'));
|
||||||
$start_column = trim($_POST['start_column']);
|
|
||||||
|
$header_row = isset($_POST['header_row']) && $_POST['header_row'] !== ''
|
||||||
|
? intval($_POST['header_row'])
|
||||||
|
: null;
|
||||||
|
|
||||||
|
$start_column = trim($_POST['start_column'] ?? '');
|
||||||
|
|
||||||
|
$xls_sheet_index = isset($_POST['xls_sheet_index']) && $_POST['xls_sheet_index'] !== ''
|
||||||
|
? intval($_POST['xls_sheet_index'])
|
||||||
|
: 0;
|
||||||
|
|
||||||
|
$api_config_id = isset($_POST['api_config_id']) && $_POST['api_config_id'] !== ''
|
||||||
|
? intval($_POST['api_config_id'])
|
||||||
|
: null;
|
||||||
|
|
||||||
$description = trim($_POST['description'] ?? '');
|
$description = trim($_POST['description'] ?? '');
|
||||||
$target_table = trim($_POST['target_table']);
|
$target_table = trim($_POST['target_table'] ?? 'datadb');
|
||||||
$idclient = intval($_POST['client_id'] ?? 0);
|
$idclient = intval($_POST['client_id'] ?? 0);
|
||||||
$clientname = trim($_POST['client_name'] ?? '');
|
$clientname = trim($_POST['client_name'] ?? '');
|
||||||
$idschema = intval($_POST['idschema'] ?? 0);
|
$idschema = intval($_POST['idschema'] ?? 0);
|
||||||
$schemaname = trim($_POST['schemaname'] ?? '');
|
$schemaname = trim($_POST['schemaname'] ?? '');
|
||||||
$idroutine = isset($_POST['idroutine']) && $_POST['idroutine'] !== '' ? intval($_POST['idroutine']) : null;
|
$idroutine = isset($_POST['idroutine']) && $_POST['idroutine'] !== ''
|
||||||
|
? intval($_POST['idroutine'])
|
||||||
|
: null;
|
||||||
|
|
||||||
$button_size = trim($_POST['button_size'] ?? 'medium');
|
$button_size = trim($_POST['button_size'] ?? 'medium');
|
||||||
$button_bg_color = trim($_POST['button_bg_color'] ?? '#007bff');
|
$button_bg_color = trim($_POST['button_bg_color'] ?? '#007bff');
|
||||||
$button_text_color = trim($_POST['button_text_color'] ?? '#ffffff');
|
$button_text_color = trim($_POST['button_text_color'] ?? '#ffffff');
|
||||||
$button_label = trim($_POST['button_label'] ?? 'Click Me');
|
$button_label = trim($_POST['button_label'] ?? 'Click Me');
|
||||||
|
|
||||||
// Controllo sui campi obbligatori
|
// Normalize source type
|
||||||
if (empty($name) || empty($header_row) || empty($start_column) || empty($target_table) || $idclient <= 0 || $idschema <= 0) {
|
// API / JSON is saved as API
|
||||||
|
if (!in_array($source_type, ['XLS', 'API', 'PDF'], true)) {
|
||||||
|
$source_type = 'XLS';
|
||||||
|
}
|
||||||
|
|
||||||
|
// Required fields validation
|
||||||
|
if ($name === '' || $target_table === '' || $idclient <= 0 || $idschema <= 0) {
|
||||||
throw new Exception("All fields marked with * are required, including client and schema.");
|
throw new Exception("All fields marked with * are required, including client and schema.");
|
||||||
}
|
}
|
||||||
|
|
||||||
// Connessione al database
|
// XLS-only validation
|
||||||
|
if ($source_type === 'XLS') {
|
||||||
|
if ($header_row === null || $header_row <= 0 || $start_column === '') {
|
||||||
|
throw new Exception("Header Row and Start Column are required for XLS templates.");
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($xls_sheet_index < 0) {
|
||||||
|
throw new Exception("XLS Sheet Number cannot be negative.");
|
||||||
|
}
|
||||||
|
|
||||||
|
$api_config_id = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
// API / JSON validation
|
||||||
|
if ($source_type === 'API') {
|
||||||
|
if (empty($api_config_id)) {
|
||||||
|
throw new Exception("API / JSON configuration is required for API / JSON templates.");
|
||||||
|
}
|
||||||
|
|
||||||
|
$header_row = null;
|
||||||
|
$start_column = null;
|
||||||
|
$xls_sheet_index = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
// PDF currently does not require XLS coordinates or API configuration
|
||||||
|
if ($source_type === 'PDF') {
|
||||||
|
$header_row = null;
|
||||||
|
$start_column = null;
|
||||||
|
$xls_sheet_index = null;
|
||||||
|
$api_config_id = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Database connection
|
||||||
$db = DBHandlerSelect::getInstance();
|
$db = DBHandlerSelect::getInstance();
|
||||||
$pdo = $db->getConnection();
|
$pdo = $db->getConnection();
|
||||||
|
|
||||||
// Inserisci il nuovo template
|
// Optional check: verify API configuration exists and is active
|
||||||
|
if ($api_config_id !== null) {
|
||||||
|
$stmt = $pdo->prepare("
|
||||||
|
SELECT COUNT(*)
|
||||||
|
FROM api_configurations
|
||||||
|
WHERE id = ?
|
||||||
|
AND is_active = 1
|
||||||
|
");
|
||||||
|
$stmt->execute([$api_config_id]);
|
||||||
|
|
||||||
|
if ((int)$stmt->fetchColumn() === 0) {
|
||||||
|
throw new Exception("Selected API / JSON configuration does not exist or is not active.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Insert the new template
|
||||||
$stmt = $pdo->prepare("
|
$stmt = $pdo->prepare("
|
||||||
INSERT INTO excel_templates
|
INSERT INTO excel_templates
|
||||||
(name, header_row, start_column, description, target_table, idclient, clientname, idschema, schemaname, idroutine,
|
(
|
||||||
button_size, button_bg_color, button_text_color, button_label, created_at, updated_at)
|
name,
|
||||||
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, NOW(), NOW())
|
source_type,
|
||||||
|
header_row,
|
||||||
|
start_column,
|
||||||
|
xls_sheet_index,
|
||||||
|
api_config_id,
|
||||||
|
description,
|
||||||
|
target_table,
|
||||||
|
idclient,
|
||||||
|
clientname,
|
||||||
|
idschema,
|
||||||
|
schemaname,
|
||||||
|
idroutine,
|
||||||
|
button_size,
|
||||||
|
button_bg_color,
|
||||||
|
button_text_color,
|
||||||
|
button_label,
|
||||||
|
created_at,
|
||||||
|
updated_at
|
||||||
|
)
|
||||||
|
VALUES
|
||||||
|
(
|
||||||
|
?, ?, ?, ?, ?, ?,
|
||||||
|
?, ?, ?, ?, ?, ?,
|
||||||
|
?, ?, ?, ?, ?,
|
||||||
|
NOW(), NOW()
|
||||||
|
)
|
||||||
");
|
");
|
||||||
|
|
||||||
$stmt->execute([
|
$stmt->execute([
|
||||||
$name,
|
$name,
|
||||||
|
$source_type,
|
||||||
$header_row,
|
$header_row,
|
||||||
$start_column,
|
$start_column,
|
||||||
|
$xls_sheet_index,
|
||||||
|
$api_config_id,
|
||||||
$description,
|
$description,
|
||||||
$target_table,
|
$target_table,
|
||||||
$idclient,
|
$idclient,
|
||||||
|
|||||||
+536
-223
File diff suppressed because it is too large
Load Diff
@@ -5,102 +5,211 @@ ini_set('error_log', __DIR__ . '/routine_debug.log');
|
|||||||
function applyRoutine(&$excelData, $routineData)
|
function applyRoutine(&$excelData, $routineData)
|
||||||
{
|
{
|
||||||
try {
|
try {
|
||||||
// Log iniziale
|
// Initial log
|
||||||
error_log("Inizio esecuzione routine Moncler: " . date('Y-m-d H:i:s'));
|
error_log("Inizio esecuzione routine Moncler: " . date('Y-m-d H:i:s'));
|
||||||
error_log("Dati routine: " . print_r($routineData, true));
|
error_log("Dati routine: " . print_r($routineData, true));
|
||||||
error_log("Dati excel_data: " . print_r($excelData, true));
|
error_log("Dati excel_data: " . print_r($excelData, true));
|
||||||
|
|
||||||
// Verifica se excelData è vuoto
|
// Check if excelData is empty
|
||||||
if (empty($excelData)) {
|
if (empty($excelData)) {
|
||||||
throw new Exception("excelData è vuoto o non valido.");
|
throw new Exception("excelData è vuoto o non valido.");
|
||||||
}
|
}
|
||||||
|
|
||||||
// Estrai informazioni dalla routine con valori predefiniti
|
// Extract routine settings with default values
|
||||||
$action1 = trim($routineData['action1'] ?? 'K');
|
$action1 = trim($routineData['action1'] ?? 'K');
|
||||||
$action2 = trim($routineData['action2'] ?? 'STYLE CODE + STYLE DESCRIPTION');
|
$action2 = trim($routineData['action2'] ?? 'STYLE CODE + STYLE DESCRIPTION');
|
||||||
$action3 = trim($routineData['action3'] ?? 'STYLE CODE');
|
$action3 = trim($routineData['action3'] ?? 'STYLE CODE');
|
||||||
$action4 = trim($routineData['action4'] ?? 'STYLE DESCRIPTION');
|
$action4 = trim($routineData['action4'] ?? 'STYLE DESCRIPTION');
|
||||||
$headers = $routineData['xls_headers'] ?? [];
|
$headers = $routineData['xls_headers'] ?? [];
|
||||||
|
|
||||||
|
// Package-related headers
|
||||||
|
$package_header = 'PACKAGE';
|
||||||
|
$other_test_header = 'Other test from Control Matrix';
|
||||||
|
$only_colorfastness_header = 'Only Colorfastness package';
|
||||||
|
$only_chemical_header = 'Only Chemical package';
|
||||||
|
|
||||||
if (empty($headers)) {
|
if (empty($headers)) {
|
||||||
throw new Exception("Nessun header trovato per la routine Moncler.");
|
throw new Exception("Nessun header trovato per la routine Moncler.");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// If headers arrive as JSON string, decode them
|
||||||
|
if (!is_array($headers) && is_string($headers)) {
|
||||||
|
$decoded_headers = json_decode($headers, true);
|
||||||
|
if (json_last_error() === JSON_ERROR_NONE && is_array($decoded_headers)) {
|
||||||
|
$headers = $decoded_headers;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!is_array($headers) || empty($headers)) {
|
||||||
|
throw new Exception("Gli header della routine non sono validi.");
|
||||||
|
}
|
||||||
|
|
||||||
error_log("Header ricevuti: " . print_r($headers, true));
|
error_log("Header ricevuti: " . print_r($headers, true));
|
||||||
|
|
||||||
// Normalizza gli header (solo trim)
|
// Normalize headers
|
||||||
$normalized_headers = array_map('trim', $headers);
|
$normalized_headers = array_map(function ($header) {
|
||||||
|
return trim((string)$header);
|
||||||
|
}, $headers);
|
||||||
|
|
||||||
error_log("Header normalizzati: " . print_r($normalized_headers, true));
|
error_log("Header normalizzati: " . print_r($normalized_headers, true));
|
||||||
error_log("Action values - action1: '$action1', action2: '$action2', action3: '$action3', action4: '$action4'");
|
error_log("Action values - action1: '$action1', action2: '$action2', action3: '$action3', action4: '$action4'");
|
||||||
|
|
||||||
// Trova gli indici delle colonne
|
// Find package-related column indexes
|
||||||
$action1_index = array_search($action1, $normalized_headers);
|
$package_index = array_search($package_header, $normalized_headers, true);
|
||||||
$action2_index = array_search($action2, $normalized_headers);
|
$other_test_index = array_search($other_test_header, $normalized_headers, true);
|
||||||
$action3_index = array_search($action3, $normalized_headers);
|
$only_colorfastness_index = array_search($only_colorfastness_header, $normalized_headers, true);
|
||||||
$action4_index = array_search($action4, $normalized_headers);
|
$only_chemical_index = array_search($only_chemical_header, $normalized_headers, true);
|
||||||
|
|
||||||
|
if ($package_index === false) {
|
||||||
|
throw new Exception("Colonna PACKAGE non trovata.");
|
||||||
|
}
|
||||||
|
|
||||||
|
error_log(
|
||||||
|
"Indici package - PACKAGE: " . var_export($package_index, true) .
|
||||||
|
", Other test from Control Matrix: " . var_export($other_test_index, true) .
|
||||||
|
", Only Colorfastness package: " . var_export($only_colorfastness_index, true) .
|
||||||
|
", Only Chemical package: " . var_export($only_chemical_index, true)
|
||||||
|
);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* PART 1
|
||||||
|
* Aggregate rows by action1 and split action2 into action3 + action4
|
||||||
|
* If it fails, continue with PART 2 only
|
||||||
|
*/
|
||||||
|
$part1_applied = false;
|
||||||
|
|
||||||
|
$action1_index = array_search($action1, $normalized_headers, true);
|
||||||
|
$action2_index = array_search($action2, $normalized_headers, true);
|
||||||
|
$action3_index = array_search($action3, $normalized_headers, true);
|
||||||
|
$action4_index = array_search($action4, $normalized_headers, true);
|
||||||
|
|
||||||
if ($action1_index === false || $action2_index === false || $action3_index === false || $action4_index === false) {
|
if ($action1_index === false || $action2_index === false || $action3_index === false || $action4_index === false) {
|
||||||
throw new Exception("Colonne non trovate - action1: '$action1' (index: " . var_export($action1_index, true) . "), action2: '$action2' (index: " . var_export($action2_index, true) . "), action3: '$action3' (index: " . var_export($action3_index, true) . "), action4: '$action4' (index: " . var_export($action4_index, true) . ")");
|
error_log(
|
||||||
}
|
"Unable to apply routine part 1. Package merge logic has been applied only. " .
|
||||||
|
"Missing columns - action1: '$action1' (index: " . var_export($action1_index, true) . ")" .
|
||||||
|
", action2: '$action2' (index: " . var_export($action2_index, true) . ")" .
|
||||||
|
", action3: '$action3' (index: " . var_export($action3_index, true) . ")" .
|
||||||
|
", action4: '$action4' (index: " . var_export($action4_index, true) . ")"
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
error_log("Indici colonne part 1 - action1: $action1_index, action2: $action2_index, action3: $action3_index, action4: $action4_index");
|
||||||
|
|
||||||
error_log("Indici colonne - action1: $action1_index, action2: $action2_index, action3: $action3_index, action4: $action4_index");
|
|
||||||
|
|
||||||
// Raggruppa le righe per il valore in action1 (colonna K)
|
|
||||||
$grouped_data = [];
|
$grouped_data = [];
|
||||||
|
|
||||||
foreach ($excelData as $row) {
|
foreach ($excelData as $row) {
|
||||||
if (!isset($row['data']) || !is_array($row['data'])) {
|
if (!isset($row['data']) || !is_array($row['data'])) {
|
||||||
error_log("Riga non valida, manca 'data' per excelrow {$row['excelrow']}");
|
error_log("Riga non valida, manca 'data' per excelrow " . ($row['excelrow'] ?? 'N/A'));
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
$key = $row['data'][$action1_index] ?? '';
|
$key = $row['data'][$action1_index] ?? '';
|
||||||
$key = empty($key) ? '_empty_' : $key;
|
$key = trim((string)$key);
|
||||||
|
$key = $key === '' ? '_empty_' : $key;
|
||||||
|
|
||||||
if (!isset($grouped_data[$key])) {
|
if (!isset($grouped_data[$key])) {
|
||||||
$grouped_data[$key] = [
|
$grouped_data[$key] = [
|
||||||
'data' => $row['data'],
|
'data' => $row['data'],
|
||||||
'excelrow' => [$row['excelrow']],
|
'excelrow' => [($row['excelrow'] ?? '')],
|
||||||
'style_codes' => [],
|
'style_codes' => [],
|
||||||
'style_descriptions' => []
|
'style_descriptions' => []
|
||||||
];
|
];
|
||||||
} else {
|
} else {
|
||||||
$grouped_data[$key]['excelrow'][] = $row['excelrow'];
|
$grouped_data[$key]['excelrow'][] = ($row['excelrow'] ?? '');
|
||||||
}
|
}
|
||||||
|
|
||||||
// Separa il valore in action2 (STYLE CODE + STYLE DESCRIPTION)
|
// Split STYLE CODE + STYLE DESCRIPTION
|
||||||
$action2_value = $row['data'][$action2_index] ?? '';
|
$action2_value = trim((string)($row['data'][$action2_index] ?? ''));
|
||||||
if (!empty($action2_value)) {
|
|
||||||
$parts = explode(' - ', trim($action2_value));
|
if ($action2_value !== '') {
|
||||||
$style_code = $parts[0] ?? '';
|
$parts = explode(' - ', $action2_value, 2);
|
||||||
$style_description = $parts[1] ?? '';
|
$style_code = trim((string)($parts[0] ?? ''));
|
||||||
if (!empty($style_code)) {
|
$style_description = trim((string)($parts[1] ?? ''));
|
||||||
|
|
||||||
|
if ($style_code !== '') {
|
||||||
$grouped_data[$key]['style_codes'][] = $style_code;
|
$grouped_data[$key]['style_codes'][] = $style_code;
|
||||||
}
|
}
|
||||||
if (!empty($style_description)) {
|
if ($style_description !== '') {
|
||||||
$grouped_data[$key]['style_descriptions'][] = $style_description;
|
$grouped_data[$key]['style_descriptions'][] = $style_description;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
error_log("Valore vuoto in action2 per excelrow {$row['excelrow']}");
|
error_log("Valore vuoto in action2 per excelrow " . ($row['excelrow'] ?? 'N/A'));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Crea il nuovo array excel_data aggregato
|
|
||||||
$new_excel_data = [];
|
$new_excel_data = [];
|
||||||
|
|
||||||
foreach ($grouped_data as $key => $group) {
|
foreach ($grouped_data as $key => $group) {
|
||||||
$row_data = $group['data'];
|
$row_data = $group['data'];
|
||||||
// Aggiorna action3 (STYLE CODE) e action4 (STYLE DESCRIPTION) con valori aggregati
|
|
||||||
|
// Update STYLE CODE and STYLE DESCRIPTION with aggregated unique values
|
||||||
$row_data[$action3_index] = implode(' - ', array_unique($group['style_codes']));
|
$row_data[$action3_index] = implode(' - ', array_unique($group['style_codes']));
|
||||||
$row_data[$action4_index] = implode(' - ', array_unique($group['style_descriptions']));
|
$row_data[$action4_index] = implode(' - ', array_unique($group['style_descriptions']));
|
||||||
// Concatena gli excelrow con '+' per le righe aggregate
|
|
||||||
$excelrow_value = count($group['excelrow']) > 1 ? implode('+', $group['excelrow']) : $group['excelrow'][0];
|
// Concatenate excelrow values with "+"
|
||||||
|
$excelrow_clean = array_filter($group['excelrow'], function ($value) {
|
||||||
|
return $value !== null && $value !== '';
|
||||||
|
});
|
||||||
|
|
||||||
|
$excelrow_value = count($excelrow_clean) > 1
|
||||||
|
? implode('+', $excelrow_clean)
|
||||||
|
: (reset($excelrow_clean) ?: '');
|
||||||
|
|
||||||
$new_excel_data[] = [
|
$new_excel_data[] = [
|
||||||
'data' => $row_data,
|
'data' => $row_data,
|
||||||
'excelrow' => $excelrow_value
|
'excelrow' => $excelrow_value
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
// Modifica excelData in-place
|
|
||||||
$excelData = $new_excel_data;
|
$excelData = $new_excel_data;
|
||||||
|
$part1_applied = true;
|
||||||
|
|
||||||
error_log("Routine Moncler completata - Righe aggregate: " . count($new_excel_data));
|
error_log("Routine part 1 completata - Righe aggregate: " . count($new_excel_data));
|
||||||
error_log("Excelrow aggregati: " . print_r(array_column($new_excel_data, 'excelrow'), true));
|
error_log("Excelrow aggregati part 1: " . print_r(array_column($new_excel_data, 'excelrow'), true));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* PART 2
|
||||||
|
* Merge package-related columns into PACKAGE
|
||||||
|
* Always applied if PACKAGE exists
|
||||||
|
*/
|
||||||
|
foreach ($excelData as $index => $row) {
|
||||||
|
if (!isset($row['data']) || !is_array($row['data'])) {
|
||||||
|
error_log("Riga non valida nella part 2, manca 'data' all'indice $index");
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
$package_values = [];
|
||||||
|
|
||||||
|
$value_package = trim((string)($row['data'][$package_index] ?? ''));
|
||||||
|
$value_other_test = $other_test_index !== false ? trim((string)($row['data'][$other_test_index] ?? '')) : '';
|
||||||
|
$value_only_colorfastness = $only_colorfastness_index !== false ? trim((string)($row['data'][$only_colorfastness_index] ?? '')) : '';
|
||||||
|
$value_only_chemical = $only_chemical_index !== false ? trim((string)($row['data'][$only_chemical_index] ?? '')) : '';
|
||||||
|
|
||||||
|
if ($value_package !== '') {
|
||||||
|
$package_values[] = $value_package;
|
||||||
|
}
|
||||||
|
if ($value_other_test !== '') {
|
||||||
|
$package_values[] = $value_other_test;
|
||||||
|
}
|
||||||
|
if ($value_only_colorfastness !== '') {
|
||||||
|
$package_values[] = $value_only_colorfastness;
|
||||||
|
}
|
||||||
|
if ($value_only_chemical !== '') {
|
||||||
|
$package_values[] = $value_only_chemical;
|
||||||
|
}
|
||||||
|
|
||||||
|
$package_values = array_unique($package_values);
|
||||||
|
|
||||||
|
$excelData[$index]['data'][$package_index] = implode(' - ', $package_values);
|
||||||
|
}
|
||||||
|
|
||||||
|
error_log("Routine part 2 completata - Merge package applicato su " . count($excelData) . " righe");
|
||||||
|
|
||||||
|
if (!$part1_applied) {
|
||||||
|
error_log("Warning finale: routine part 1 non applicata, routine part 2 applicata correttamente");
|
||||||
|
}
|
||||||
|
|
||||||
|
error_log("Routine Moncler completata con successo");
|
||||||
} catch (Exception $e) {
|
} catch (Exception $e) {
|
||||||
error_log("Eccezione nella routine Moncler: " . $e->getMessage());
|
error_log("Eccezione nella routine Moncler: " . $e->getMessage());
|
||||||
throw $e;
|
throw $e;
|
||||||
|
|||||||
@@ -0,0 +1,327 @@
|
|||||||
|
<?php
|
||||||
|
ini_set('log_errors', 1);
|
||||||
|
ini_set('error_log', __DIR__ . '/routine_debug.log');
|
||||||
|
|
||||||
|
function applyRoutine(&$excelData, $routineData)
|
||||||
|
{
|
||||||
|
try {
|
||||||
|
// Initial log
|
||||||
|
error_log("Inizio esecuzione routine Moncler: " . date('Y-m-d H:i:s'));
|
||||||
|
error_log("Dati routine: " . print_r($routineData, true));
|
||||||
|
error_log("Dati excel_data: " . print_r($excelData, true));
|
||||||
|
|
||||||
|
// Check if excelData is empty
|
||||||
|
if (empty($excelData)) {
|
||||||
|
throw new Exception("excelData è vuoto o non valido.");
|
||||||
|
}
|
||||||
|
|
||||||
|
// Extract routine settings with default values
|
||||||
|
$action1 = trim($routineData['action1'] ?? 'K');
|
||||||
|
$action2 = trim($routineData['action2'] ?? 'STYLE CODE + STYLE DESCRIPTION');
|
||||||
|
$action3 = trim($routineData['action3'] ?? 'STYLE CODE');
|
||||||
|
$action4 = trim($routineData['action4'] ?? 'STYLE DESCRIPTION');
|
||||||
|
$headers = $routineData['xls_headers'] ?? [];
|
||||||
|
|
||||||
|
// Package-related headers
|
||||||
|
$package_header = 'PACKAGE';
|
||||||
|
$other_test_header = 'Other test from Control Matrix';
|
||||||
|
$only_colorfastness_header = 'Only Colorfastness package';
|
||||||
|
$only_chemical_header = 'Only Chemical package';
|
||||||
|
|
||||||
|
if (empty($headers)) {
|
||||||
|
throw new Exception("Nessun header trovato per la routine Moncler.");
|
||||||
|
}
|
||||||
|
|
||||||
|
// If headers arrive as JSON string, decode them
|
||||||
|
if (!is_array($headers) && is_string($headers)) {
|
||||||
|
$decoded_headers = json_decode($headers, true);
|
||||||
|
if (json_last_error() === JSON_ERROR_NONE && is_array($decoded_headers)) {
|
||||||
|
$headers = $decoded_headers;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!is_array($headers) || empty($headers)) {
|
||||||
|
throw new Exception("Gli header della routine non sono validi.");
|
||||||
|
}
|
||||||
|
|
||||||
|
error_log("Header ricevuti: " . print_r($headers, true));
|
||||||
|
|
||||||
|
// Normalize headers
|
||||||
|
$normalized_headers = array_map(function ($header) {
|
||||||
|
return trim((string)$header);
|
||||||
|
}, $headers);
|
||||||
|
|
||||||
|
error_log("Header normalizzati: " . print_r($normalized_headers, true));
|
||||||
|
error_log("Action values - action1: '$action1', action2: '$action2', action3: '$action3', action4: '$action4'");
|
||||||
|
|
||||||
|
// Find main indexes
|
||||||
|
$action1_index = array_search($action1, $normalized_headers, true);
|
||||||
|
$action2_index = array_search($action2, $normalized_headers, true);
|
||||||
|
$action3_index = array_search($action3, $normalized_headers, true);
|
||||||
|
$action4_index = array_search($action4, $normalized_headers, true);
|
||||||
|
|
||||||
|
// Find package-related column indexes
|
||||||
|
$package_index = array_search($package_header, $normalized_headers, true);
|
||||||
|
$other_test_index = array_search($other_test_header, $normalized_headers, true);
|
||||||
|
$only_colorfastness_index = array_search($only_colorfastness_header, $normalized_headers, true);
|
||||||
|
$only_chemical_index = array_search($only_chemical_header, $normalized_headers, true);
|
||||||
|
|
||||||
|
if ($package_index === false) {
|
||||||
|
throw new Exception("Colonna PACKAGE non trovata.");
|
||||||
|
}
|
||||||
|
|
||||||
|
error_log(
|
||||||
|
"Indici colonne - action1: " . var_export($action1_index, true) .
|
||||||
|
", action2: " . var_export($action2_index, true) .
|
||||||
|
", action3: " . var_export($action3_index, true) .
|
||||||
|
", action4: " . var_export($action4_index, true) .
|
||||||
|
", PACKAGE: " . var_export($package_index, true) .
|
||||||
|
", Other test from Control Matrix: " . var_export($other_test_index, true) .
|
||||||
|
", Only Colorfastness package: " . var_export($only_colorfastness_index, true) .
|
||||||
|
", Only Chemical package: " . var_export($only_chemical_index, true)
|
||||||
|
);
|
||||||
|
|
||||||
|
// Helper: split a value already joined by " - "
|
||||||
|
$splitJoinedValues = function ($value) {
|
||||||
|
$value = trim((string)$value);
|
||||||
|
if ($value === '') {
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
|
||||||
|
$parts = preg_split('/\s*-\s*/', $value);
|
||||||
|
$parts = array_map('trim', $parts);
|
||||||
|
$parts = array_filter($parts, function ($item) {
|
||||||
|
return $item !== '';
|
||||||
|
});
|
||||||
|
|
||||||
|
return array_values(array_unique($parts));
|
||||||
|
};
|
||||||
|
|
||||||
|
// Helper: join unique values with " - "
|
||||||
|
$joinUniqueValues = function (array $values) {
|
||||||
|
$clean = [];
|
||||||
|
foreach ($values as $value) {
|
||||||
|
$value = trim((string)$value);
|
||||||
|
if ($value !== '' && !in_array($value, $clean, true)) {
|
||||||
|
$clean[] = $value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return implode(' - ', $clean);
|
||||||
|
};
|
||||||
|
|
||||||
|
// Helper: split STYLE CODE + STYLE DESCRIPTION into code + description
|
||||||
|
$splitStyleCombined = function ($value) {
|
||||||
|
$value = trim((string)$value);
|
||||||
|
|
||||||
|
if ($value === '') {
|
||||||
|
return ['', ''];
|
||||||
|
}
|
||||||
|
|
||||||
|
// Accept both "CODE - DESC" and "CODE-DESC"
|
||||||
|
$parts = preg_split('/\s*-\s*/', $value, 2);
|
||||||
|
|
||||||
|
$styleCode = trim((string)($parts[0] ?? ''));
|
||||||
|
$styleDescription = trim((string)($parts[1] ?? ''));
|
||||||
|
|
||||||
|
return [$styleCode, $styleDescription];
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* STEP 0
|
||||||
|
* Normalize STYLE CODE and STYLE DESCRIPTION before grouping by K
|
||||||
|
*
|
||||||
|
* Rules:
|
||||||
|
* - Read STYLE CODE + STYLE DESCRIPTION
|
||||||
|
* - If it contains code/description, fill missing values
|
||||||
|
* - If target field already contains a different value, add it instead of replacing it
|
||||||
|
* - Avoid duplicates
|
||||||
|
*/
|
||||||
|
$step0_applied = false;
|
||||||
|
|
||||||
|
if ($action2_index === false || $action3_index === false || $action4_index === false) {
|
||||||
|
error_log(
|
||||||
|
"Unable to apply routine step 0. Missing columns - action2: '$action2' (index: " . var_export($action2_index, true) . ")" .
|
||||||
|
", action3: '$action3' (index: " . var_export($action3_index, true) . ")" .
|
||||||
|
", action4: '$action4' (index: " . var_export($action4_index, true) . ")"
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
foreach ($excelData as $index => $row) {
|
||||||
|
if (!isset($row['data']) || !is_array($row['data'])) {
|
||||||
|
error_log("Riga non valida nello step 0, manca 'data' all'indice $index");
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
$combinedValue = trim((string)($row['data'][$action2_index] ?? ''));
|
||||||
|
$currentStyleCode = trim((string)($row['data'][$action3_index] ?? ''));
|
||||||
|
$currentStyleDescription = trim((string)($row['data'][$action4_index] ?? ''));
|
||||||
|
|
||||||
|
if ($combinedValue === '') {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
[$parsedStyleCode, $parsedStyleDescription] = $splitStyleCombined($combinedValue);
|
||||||
|
|
||||||
|
$styleCodeValues = $splitJoinedValues($currentStyleCode);
|
||||||
|
$styleDescriptionValues = $splitJoinedValues($currentStyleDescription);
|
||||||
|
|
||||||
|
if ($parsedStyleCode !== '' && !in_array($parsedStyleCode, $styleCodeValues, true)) {
|
||||||
|
$styleCodeValues[] = $parsedStyleCode;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($parsedStyleDescription !== '' && !in_array($parsedStyleDescription, $styleDescriptionValues, true)) {
|
||||||
|
$styleDescriptionValues[] = $parsedStyleDescription;
|
||||||
|
}
|
||||||
|
|
||||||
|
$excelData[$index]['data'][$action3_index] = $joinUniqueValues($styleCodeValues);
|
||||||
|
$excelData[$index]['data'][$action4_index] = $joinUniqueValues($styleDescriptionValues);
|
||||||
|
|
||||||
|
error_log(
|
||||||
|
"Step 0 - excelrow " . ($row['excelrow'] ?? 'N/A') .
|
||||||
|
" | combined: '" . $combinedValue . "'" .
|
||||||
|
" | style_code_result: '" . $excelData[$index]['data'][$action3_index] . "'" .
|
||||||
|
" | style_description_result: '" . $excelData[$index]['data'][$action4_index] . "'"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
$step0_applied = true;
|
||||||
|
error_log("Routine step 0 completato - Normalizzazione style applicata su " . count($excelData) . " righe");
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* STEP 1
|
||||||
|
* Aggregate rows by action1 using normalized STYLE CODE and STYLE DESCRIPTION
|
||||||
|
* If it fails, continue with STEP 2 only
|
||||||
|
*/
|
||||||
|
$step1_applied = false;
|
||||||
|
|
||||||
|
if ($action1_index === false || $action3_index === false || $action4_index === false) {
|
||||||
|
error_log(
|
||||||
|
"Unable to apply routine step 1. Package merge logic has been applied only. " .
|
||||||
|
"Missing columns - action1: '$action1' (index: " . var_export($action1_index, true) . ")" .
|
||||||
|
", action3: '$action3' (index: " . var_export($action3_index, true) . ")" .
|
||||||
|
", action4: '$action4' (index: " . var_export($action4_index, true) . ")"
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
$grouped_data = [];
|
||||||
|
|
||||||
|
foreach ($excelData as $row) {
|
||||||
|
if (!isset($row['data']) || !is_array($row['data'])) {
|
||||||
|
error_log("Riga non valida, manca 'data' per excelrow " . ($row['excelrow'] ?? 'N/A'));
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
$key = $row['data'][$action1_index] ?? '';
|
||||||
|
$key = trim((string)$key);
|
||||||
|
$key = $key === '' ? '_empty_' : $key;
|
||||||
|
|
||||||
|
if (!isset($grouped_data[$key])) {
|
||||||
|
$grouped_data[$key] = [
|
||||||
|
'data' => $row['data'],
|
||||||
|
'excelrow' => [($row['excelrow'] ?? '')],
|
||||||
|
'style_codes' => [],
|
||||||
|
'style_descriptions' => []
|
||||||
|
];
|
||||||
|
} else {
|
||||||
|
$grouped_data[$key]['excelrow'][] = ($row['excelrow'] ?? '');
|
||||||
|
}
|
||||||
|
|
||||||
|
// Collect normalized STYLE CODE values
|
||||||
|
$styleCodeValues = $splitJoinedValues($row['data'][$action3_index] ?? '');
|
||||||
|
foreach ($styleCodeValues as $styleCodeValue) {
|
||||||
|
if (!in_array($styleCodeValue, $grouped_data[$key]['style_codes'], true)) {
|
||||||
|
$grouped_data[$key]['style_codes'][] = $styleCodeValue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Collect normalized STYLE DESCRIPTION values
|
||||||
|
$styleDescriptionValues = $splitJoinedValues($row['data'][$action4_index] ?? '');
|
||||||
|
foreach ($styleDescriptionValues as $styleDescriptionValue) {
|
||||||
|
if (!in_array($styleDescriptionValue, $grouped_data[$key]['style_descriptions'], true)) {
|
||||||
|
$grouped_data[$key]['style_descriptions'][] = $styleDescriptionValue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
$new_excel_data = [];
|
||||||
|
|
||||||
|
foreach ($grouped_data as $key => $group) {
|
||||||
|
$row_data = $group['data'];
|
||||||
|
|
||||||
|
// Update STYLE CODE and STYLE DESCRIPTION with aggregated unique values
|
||||||
|
$row_data[$action3_index] = $joinUniqueValues($group['style_codes']);
|
||||||
|
$row_data[$action4_index] = $joinUniqueValues($group['style_descriptions']);
|
||||||
|
|
||||||
|
// Concatenate excelrow values with "+"
|
||||||
|
$excelrow_clean = array_filter($group['excelrow'], function ($value) {
|
||||||
|
return $value !== null && $value !== '';
|
||||||
|
});
|
||||||
|
|
||||||
|
$excelrow_value = count($excelrow_clean) > 1
|
||||||
|
? implode('+', $excelrow_clean)
|
||||||
|
: (reset($excelrow_clean) ?: '');
|
||||||
|
|
||||||
|
$new_excel_data[] = [
|
||||||
|
'data' => $row_data,
|
||||||
|
'excelrow' => $excelrow_value
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
$excelData = $new_excel_data;
|
||||||
|
$step1_applied = true;
|
||||||
|
|
||||||
|
error_log("Routine step 1 completato - Righe aggregate: " . count($new_excel_data));
|
||||||
|
error_log("Excelrow aggregati step 1: " . print_r(array_column($new_excel_data, 'excelrow'), true));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* STEP 2
|
||||||
|
* Merge package-related columns into PACKAGE
|
||||||
|
* Always applied if PACKAGE exists
|
||||||
|
*/
|
||||||
|
foreach ($excelData as $index => $row) {
|
||||||
|
if (!isset($row['data']) || !is_array($row['data'])) {
|
||||||
|
error_log("Riga non valida nello step 2, manca 'data' all'indice $index");
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
$package_values = [];
|
||||||
|
|
||||||
|
$value_package = trim((string)($row['data'][$package_index] ?? ''));
|
||||||
|
$value_other_test = $other_test_index !== false ? trim((string)($row['data'][$other_test_index] ?? '')) : '';
|
||||||
|
$value_only_colorfastness = $only_colorfastness_index !== false ? trim((string)($row['data'][$only_colorfastness_index] ?? '')) : '';
|
||||||
|
$value_only_chemical = $only_chemical_index !== false ? trim((string)($row['data'][$only_chemical_index] ?? '')) : '';
|
||||||
|
|
||||||
|
if ($value_package !== '') {
|
||||||
|
$package_values[] = $value_package;
|
||||||
|
}
|
||||||
|
if ($value_other_test !== '') {
|
||||||
|
$package_values[] = $value_other_test;
|
||||||
|
}
|
||||||
|
if ($value_only_colorfastness !== '') {
|
||||||
|
$package_values[] = $value_only_colorfastness;
|
||||||
|
}
|
||||||
|
if ($value_only_chemical !== '') {
|
||||||
|
$package_values[] = $value_only_chemical;
|
||||||
|
}
|
||||||
|
|
||||||
|
$package_values = array_unique($package_values);
|
||||||
|
|
||||||
|
$excelData[$index]['data'][$package_index] = implode(' - ', $package_values);
|
||||||
|
}
|
||||||
|
|
||||||
|
error_log("Routine step 2 completato - Merge package applicato su " . count($excelData) . " righe");
|
||||||
|
|
||||||
|
if (!$step0_applied) {
|
||||||
|
error_log("Warning finale: routine step 0 non applicata");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!$step1_applied) {
|
||||||
|
error_log("Warning finale: routine step 1 non applicata, routine step 2 applicata correttamente");
|
||||||
|
}
|
||||||
|
|
||||||
|
error_log("Routine Moncler completata con successo");
|
||||||
|
} catch (Exception $e) {
|
||||||
|
error_log("Eccezione nella routine Moncler: " . $e->getMessage());
|
||||||
|
throw $e;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,212 @@
|
|||||||
|
<?php
|
||||||
|
ini_set('log_errors', 1);
|
||||||
|
ini_set('error_log', __DIR__ . '/routine_debug.log');
|
||||||
|
|
||||||
|
function applyRoutine(&$excelData, $routineData)
|
||||||
|
{
|
||||||
|
try {
|
||||||
|
// Initial log
|
||||||
|
error_log("Inizio esecuzione nuova routine: " . date('Y-m-d H:i:s'));
|
||||||
|
error_log("Dati routine: " . print_r($routineData, true));
|
||||||
|
error_log("Dati excel_data: " . print_r($excelData, true));
|
||||||
|
|
||||||
|
// Check if excelData is empty
|
||||||
|
if (empty($excelData)) {
|
||||||
|
throw new Exception("excelData è vuoto o non valido.");
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get headers from routine data
|
||||||
|
$headers = $routineData['xls_headers'] ?? [];
|
||||||
|
|
||||||
|
if (empty($headers)) {
|
||||||
|
throw new Exception("Nessun header trovato per la routine.");
|
||||||
|
}
|
||||||
|
|
||||||
|
// If headers arrive as JSON string, decode them
|
||||||
|
if (!is_array($headers) && is_string($headers)) {
|
||||||
|
$decoded_headers = json_decode($headers, true);
|
||||||
|
if (json_last_error() === JSON_ERROR_NONE && is_array($decoded_headers)) {
|
||||||
|
$headers = $decoded_headers;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!is_array($headers) || empty($headers)) {
|
||||||
|
throw new Exception("Gli header della routine non sono validi.");
|
||||||
|
}
|
||||||
|
|
||||||
|
error_log("Header ricevuti: " . print_r($headers, true));
|
||||||
|
|
||||||
|
// Normalize headers
|
||||||
|
$normalized_headers = array_map(function ($header) {
|
||||||
|
return trim((string)$header);
|
||||||
|
}, $headers);
|
||||||
|
|
||||||
|
error_log("Header normalizzati: " . print_r($normalized_headers, true));
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Define source and target headers
|
||||||
|
*/
|
||||||
|
$parte_moncler_header = 'PARTE MONCLER';
|
||||||
|
$parte_fornitore_header = 'PARTE FORNITORE';
|
||||||
|
|
||||||
|
$descr_parte_moncler_header = 'DESCRIZIONE PARTE MONCLER';
|
||||||
|
$descr_parte_fornitore_header = 'DESCRIZIONE PARTE FORNITORE';
|
||||||
|
|
||||||
|
$colore_moncler_header = 'COLORE MONCLER';
|
||||||
|
$colore_fornitore_header = 'COLORE FORNITORE';
|
||||||
|
|
||||||
|
$composizione_tessile_header = 'COMPOSIZIONE TESSILE';
|
||||||
|
$composizione_totale_header = 'COMPOSIZIONE TOTALE';
|
||||||
|
|
||||||
|
$pacchetti_test_header = 'PACCHETTI TEST';
|
||||||
|
$test_addizionali_header = 'TEST ADDIZIONALI (A5 solo se richiesti dal cliente)';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Find indexes
|
||||||
|
*/
|
||||||
|
$parte_moncler_index = array_search($parte_moncler_header, $normalized_headers, true);
|
||||||
|
$parte_fornitore_index = array_search($parte_fornitore_header, $normalized_headers, true);
|
||||||
|
|
||||||
|
$descr_parte_moncler_index = array_search($descr_parte_moncler_header, $normalized_headers, true);
|
||||||
|
$descr_parte_fornitore_index = array_search($descr_parte_fornitore_header, $normalized_headers, true);
|
||||||
|
|
||||||
|
$colore_moncler_index = array_search($colore_moncler_header, $normalized_headers, true);
|
||||||
|
$colore_fornitore_index = array_search($colore_fornitore_header, $normalized_headers, true);
|
||||||
|
|
||||||
|
$composizione_tessile_index = array_search($composizione_tessile_header, $normalized_headers, true);
|
||||||
|
$composizione_totale_index = array_search($composizione_totale_header, $normalized_headers, true);
|
||||||
|
|
||||||
|
$pacchetti_test_index = array_search($pacchetti_test_header, $normalized_headers, true);
|
||||||
|
$test_addizionali_index = array_search($test_addizionali_header, $normalized_headers, true);
|
||||||
|
|
||||||
|
error_log(
|
||||||
|
"Indici trovati - " .
|
||||||
|
"PARTE MONCLER: " . var_export($parte_moncler_index, true) .
|
||||||
|
", PARTE FORNITORE: " . var_export($parte_fornitore_index, true) .
|
||||||
|
", DESCRIZIONE PARTE MONCLER: " . var_export($descr_parte_moncler_index, true) .
|
||||||
|
", DESCRIZIONE PARTE FORNITORE: " . var_export($descr_parte_fornitore_index, true) .
|
||||||
|
", COLORE MONCLER: " . var_export($colore_moncler_index, true) .
|
||||||
|
", COLORE FORNITORE: " . var_export($colore_fornitore_index, true) .
|
||||||
|
", COMPOSIZIONE TESSILE: " . var_export($composizione_tessile_index, true) .
|
||||||
|
", COMPOSIZIONE TOTALE: " . var_export($composizione_totale_index, true) .
|
||||||
|
", PACCHETTI TEST: " . var_export($pacchetti_test_index, true) .
|
||||||
|
", TEST ADDIZIONALI: " . var_export($test_addizionali_index, true)
|
||||||
|
);
|
||||||
|
|
||||||
|
// Required target columns must exist
|
||||||
|
if ($parte_moncler_index === false) {
|
||||||
|
throw new Exception("Colonna PARTE MONCLER non trovata.");
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($descr_parte_moncler_index === false) {
|
||||||
|
throw new Exception("Colonna DESCRIZIONE PARTE MONCLER non trovata.");
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($colore_moncler_index === false) {
|
||||||
|
throw new Exception("Colonna COLORE MONCLER non trovata.");
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($composizione_tessile_index === false) {
|
||||||
|
throw new Exception("Colonna COMPOSIZIONE TESSILE non trovata.");
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($pacchetti_test_index === false) {
|
||||||
|
throw new Exception("Colonna PACCHETTI TEST non trovata.");
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Apply merge row by row
|
||||||
|
*/
|
||||||
|
foreach ($excelData as $index => $row) {
|
||||||
|
if (!isset($row['data']) || !is_array($row['data'])) {
|
||||||
|
error_log("Riga non valida, manca 'data' all'indice $index");
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 1. PARTE MONCLER + PARTE FORNITORE -> PARTE MONCLER
|
||||||
|
$parte_values = [];
|
||||||
|
|
||||||
|
$value_parte_moncler = trim((string)($row['data'][$parte_moncler_index] ?? ''));
|
||||||
|
$value_parte_fornitore = $parte_fornitore_index !== false ? trim((string)($row['data'][$parte_fornitore_index] ?? '')) : '';
|
||||||
|
|
||||||
|
if ($value_parte_moncler !== '') {
|
||||||
|
$parte_values[] = $value_parte_moncler;
|
||||||
|
}
|
||||||
|
if ($value_parte_fornitore !== '') {
|
||||||
|
$parte_values[] = $value_parte_fornitore;
|
||||||
|
}
|
||||||
|
|
||||||
|
$parte_values = array_unique($parte_values);
|
||||||
|
$excelData[$index]['data'][$parte_moncler_index] = implode(' - ', $parte_values);
|
||||||
|
|
||||||
|
// 2. DESCRIZIONE PARTE MONCLER + DESCRIZIONE PARTE FORNITORE -> DESCRIZIONE PARTE MONCLER
|
||||||
|
$descr_values = [];
|
||||||
|
|
||||||
|
$value_descr_moncler = trim((string)($row['data'][$descr_parte_moncler_index] ?? ''));
|
||||||
|
$value_descr_fornitore = $descr_parte_fornitore_index !== false ? trim((string)($row['data'][$descr_parte_fornitore_index] ?? '')) : '';
|
||||||
|
|
||||||
|
if ($value_descr_moncler !== '') {
|
||||||
|
$descr_values[] = $value_descr_moncler;
|
||||||
|
}
|
||||||
|
if ($value_descr_fornitore !== '') {
|
||||||
|
$descr_values[] = $value_descr_fornitore;
|
||||||
|
}
|
||||||
|
|
||||||
|
$descr_values = array_unique($descr_values);
|
||||||
|
$excelData[$index]['data'][$descr_parte_moncler_index] = implode(' - ', $descr_values);
|
||||||
|
|
||||||
|
// 3. COLORE MONCLER + COLORE FORNITORE -> COLORE MONCLER
|
||||||
|
$colore_values = [];
|
||||||
|
|
||||||
|
$value_colore_moncler = trim((string)($row['data'][$colore_moncler_index] ?? ''));
|
||||||
|
$value_colore_fornitore = $colore_fornitore_index !== false ? trim((string)($row['data'][$colore_fornitore_index] ?? '')) : '';
|
||||||
|
|
||||||
|
if ($value_colore_moncler !== '') {
|
||||||
|
$colore_values[] = $value_colore_moncler;
|
||||||
|
}
|
||||||
|
if ($value_colore_fornitore !== '') {
|
||||||
|
$colore_values[] = $value_colore_fornitore;
|
||||||
|
}
|
||||||
|
|
||||||
|
$colore_values = array_unique($colore_values);
|
||||||
|
$excelData[$index]['data'][$colore_moncler_index] = implode(' - ', $colore_values);
|
||||||
|
|
||||||
|
// 4. COMPOSIZIONE TESSILE + COMPOSIZIONE TOTALE -> COMPOSIZIONE TESSILE
|
||||||
|
$composizione_values = [];
|
||||||
|
|
||||||
|
$value_composizione_tessile = trim((string)($row['data'][$composizione_tessile_index] ?? ''));
|
||||||
|
$value_composizione_totale = $composizione_totale_index !== false ? trim((string)($row['data'][$composizione_totale_index] ?? '')) : '';
|
||||||
|
|
||||||
|
if ($value_composizione_tessile !== '') {
|
||||||
|
$composizione_values[] = $value_composizione_tessile;
|
||||||
|
}
|
||||||
|
if ($value_composizione_totale !== '') {
|
||||||
|
$composizione_values[] = $value_composizione_totale;
|
||||||
|
}
|
||||||
|
|
||||||
|
$composizione_values = array_unique($composizione_values);
|
||||||
|
$excelData[$index]['data'][$composizione_tessile_index] = implode(' - ', $composizione_values);
|
||||||
|
|
||||||
|
// 5. PACCHETTI TEST + TEST ADDIZIONALI -> PACCHETTI TEST
|
||||||
|
$pacchetti_values = [];
|
||||||
|
|
||||||
|
$value_pacchetti_test = trim((string)($row['data'][$pacchetti_test_index] ?? ''));
|
||||||
|
$value_test_addizionali = $test_addizionali_index !== false ? trim((string)($row['data'][$test_addizionali_index] ?? '')) : '';
|
||||||
|
|
||||||
|
if ($value_pacchetti_test !== '') {
|
||||||
|
$pacchetti_values[] = $value_pacchetti_test;
|
||||||
|
}
|
||||||
|
if ($value_test_addizionali !== '') {
|
||||||
|
$pacchetti_values[] = $value_test_addizionali;
|
||||||
|
}
|
||||||
|
|
||||||
|
$pacchetti_values = array_unique($pacchetti_values);
|
||||||
|
$excelData[$index]['data'][$pacchetti_test_index] = implode(' - ', $pacchetti_values);
|
||||||
|
}
|
||||||
|
|
||||||
|
error_log("Nuova routine completata con successo. Righe elaborate: " . count($excelData));
|
||||||
|
} catch (Exception $e) {
|
||||||
|
error_log("Eccezione nella nuova routine: " . $e->getMessage());
|
||||||
|
throw $e;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,130 @@
|
|||||||
|
/**
|
||||||
|
* saveAll.js — Save All functionality using gridData
|
||||||
|
*/
|
||||||
|
(function() {
|
||||||
|
'use strict';
|
||||||
|
|
||||||
|
let saveAllRunning = false;
|
||||||
|
|
||||||
|
function isBusy() {
|
||||||
|
return saveAllRunning || window.batchRunning;
|
||||||
|
}
|
||||||
|
|
||||||
|
// ── Save single row ──────────────────────────────────────────────────
|
||||||
|
$(document).on('click', '.save-btn', async function() {
|
||||||
|
const btn = this;
|
||||||
|
const rowIndex = parseInt(btn.dataset.row);
|
||||||
|
const row = window.gridData?.[rowIndex];
|
||||||
|
if (!row) return;
|
||||||
|
|
||||||
|
const origHtml = btn.innerHTML;
|
||||||
|
btn.innerHTML = '<i class="fas fa-spinner fa-spin"></i>';
|
||||||
|
btn.disabled = true;
|
||||||
|
|
||||||
|
try {
|
||||||
|
const formData = window.buildSavePayload(rowIndex);
|
||||||
|
const resp = await fetch('save_edited_row.php', { method: 'POST', body: formData });
|
||||||
|
const result = await resp.json();
|
||||||
|
|
||||||
|
if (result.success) {
|
||||||
|
row._dirty = false;
|
||||||
|
if (window.gridRenderer?.clearDirty) window.gridRenderer.clearDirty(rowIndex);
|
||||||
|
// Flash success on row without re-rendering (preserves Select2 state)
|
||||||
|
const gridRow = document.querySelector(`.grid-row[data-id="${row.iddatadb}"]`);
|
||||||
|
if (gridRow) {
|
||||||
|
gridRow.classList.remove('row-dirty');
|
||||||
|
gridRow.querySelectorAll('.grid-cell').forEach(cell => {
|
||||||
|
cell.classList.remove('cell-changed');
|
||||||
|
cell.classList.add('flash-success');
|
||||||
|
});
|
||||||
|
setTimeout(() => gridRow.querySelectorAll('.flash-success').forEach(c => c.classList.remove('flash-success')), 500);
|
||||||
|
}
|
||||||
|
const toastEl = document.getElementById('saveSuccessToast');
|
||||||
|
if (toastEl) bootstrap.Toast.getOrCreateInstance(toastEl).show();
|
||||||
|
} else {
|
||||||
|
alert('Errore: ' + result.message);
|
||||||
|
}
|
||||||
|
} catch (e) {
|
||||||
|
alert('Errore: ' + e.message);
|
||||||
|
} finally {
|
||||||
|
btn.innerHTML = origHtml;
|
||||||
|
btn.disabled = false;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// ── Save All ─────────────────────────────────────────────────────────
|
||||||
|
$(document).on('click', '.save-all-btn', function(e) {
|
||||||
|
e.preventDefault();
|
||||||
|
if (isBusy()) return;
|
||||||
|
const modalEl = document.getElementById('saveAllConfirmModal');
|
||||||
|
if (!modalEl) return;
|
||||||
|
new bootstrap.Modal(modalEl, { keyboard: false }).show();
|
||||||
|
});
|
||||||
|
|
||||||
|
$(document).on('click', '#saveAllConfirmBtn', async function() {
|
||||||
|
const confirmModal = bootstrap.Modal.getInstance(document.getElementById('saveAllConfirmModal'));
|
||||||
|
if (confirmModal) confirmModal.hide();
|
||||||
|
saveAllRunning = true;
|
||||||
|
|
||||||
|
const bar = document.getElementById('batchExportBar');
|
||||||
|
const statusEl = document.getElementById('batchExportStatus');
|
||||||
|
const cancelBtn = document.getElementById('exportBatchCancelBtn');
|
||||||
|
if (bar) bar.style.display = '';
|
||||||
|
if (cancelBtn) cancelBtn.style.display = 'none';
|
||||||
|
if (statusEl) statusEl.textContent = 'Saving...';
|
||||||
|
|
||||||
|
const data = window.gridData || [];
|
||||||
|
const dirtyRows = data.map((r, i) => r._dirty ? i : -1).filter(i => i >= 0);
|
||||||
|
|
||||||
|
if (dirtyRows.length === 0) {
|
||||||
|
saveAllRunning = false;
|
||||||
|
if (bar) bar.style.display = 'none';
|
||||||
|
const msgEl = document.getElementById('saveAllResultMessage');
|
||||||
|
if (msgEl) msgEl.textContent = 'No changes to save.';
|
||||||
|
new bootstrap.Modal(document.getElementById('saveAllResultModal')).show();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
let success = 0, fail = 0;
|
||||||
|
|
||||||
|
for (const idx of dirtyRows) {
|
||||||
|
if (statusEl) statusEl.textContent = `Saving ${success + fail + 1} / ${dirtyRows.length}...`;
|
||||||
|
try {
|
||||||
|
const formData = window.buildSavePayload(idx);
|
||||||
|
const resp = await fetch('save_edited_row.php', { method: 'POST', body: formData });
|
||||||
|
const result = await resp.json();
|
||||||
|
if (result.success) {
|
||||||
|
data[idx]._dirty = false;
|
||||||
|
if (window.gridRenderer?.clearDirty) window.gridRenderer.clearDirty(idx);
|
||||||
|
success++;
|
||||||
|
} else {
|
||||||
|
fail++;
|
||||||
|
}
|
||||||
|
} catch (e) {
|
||||||
|
fail++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
saveAllRunning = false;
|
||||||
|
if (bar) bar.style.display = 'none';
|
||||||
|
|
||||||
|
const gr = window.gridRenderer;
|
||||||
|
if (gr) gr.renderVisibleRows();
|
||||||
|
|
||||||
|
const msg = `Saved: ${success}` + (fail > 0 ? `, Errors: ${fail}` : '');
|
||||||
|
const msgEl = document.getElementById('saveAllResultMessage');
|
||||||
|
if (msgEl) msgEl.textContent = msg;
|
||||||
|
new bootstrap.Modal(document.getElementById('saveAllResultModal')).show();
|
||||||
|
});
|
||||||
|
|
||||||
|
// ── beforeunload ─────────────────────────────────────────────────────
|
||||||
|
window.addEventListener('beforeunload', function(e) {
|
||||||
|
if (window.gridData && window.gridData.some(r => r._dirty)) {
|
||||||
|
e.preventDefault();
|
||||||
|
e.returnValue = '';
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// Public
|
||||||
|
window.saveAllRunning = () => saveAllRunning;
|
||||||
|
})();
|
||||||
@@ -23,7 +23,7 @@ try {
|
|||||||
foreach ($_POST as $key => $value) {
|
foreach ($_POST as $key => $value) {
|
||||||
if ($key !== 'iddatadb' && !in_array($key, $excludeFields)) {
|
if ($key !== 'iddatadb' && !in_array($key, $excludeFields)) {
|
||||||
$updates[] = "$key = ?";
|
$updates[] = "$key = ?";
|
||||||
$values[] = htmlspecialchars($value);
|
$values[] = $value;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
$values[] = $iddatadb;
|
$values[] = $iddatadb;
|
||||||
|
|||||||
@@ -12,7 +12,8 @@ try {
|
|||||||
|
|
||||||
$iddatadb = intval($_POST['iddatadb']);
|
$iddatadb = intval($_POST['iddatadb']);
|
||||||
$idclient = isset($_POST['idclient']) ? (is_numeric($_POST['idclient']) ? intval($_POST['idclient']) : null) : null;
|
$idclient = isset($_POST['idclient']) ? (is_numeric($_POST['idclient']) ? intval($_POST['idclient']) : null) : null;
|
||||||
|
$clienteFornitoreId = isset($_POST['cliente_fornitore_id']) ? (is_numeric($_POST['cliente_fornitore_id']) ? intval($_POST['cliente_fornitore_id']) : null) : null;
|
||||||
|
$testedComponent = isset($_POST['tested_component']) ? trim((string)$_POST['tested_component']) : null;
|
||||||
$db = DBHandlerSelect::getInstance();
|
$db = DBHandlerSelect::getInstance();
|
||||||
$pdo = $db->getConnection();
|
$pdo = $db->getConnection();
|
||||||
|
|
||||||
@@ -22,10 +23,11 @@ try {
|
|||||||
// (NON tocchiamo MySQL, gestiamo solo qui la traduzione)
|
// (NON tocchiamo MySQL, gestiamo solo qui la traduzione)
|
||||||
$fixedAliasMap = [
|
$fixedAliasMap = [
|
||||||
'ClienteResponsabile' => 'cliente_responsabile_id',
|
'ClienteResponsabile' => 'cliente_responsabile_id',
|
||||||
|
'ClienteFornitore' => 'cliente_fornitore_id',
|
||||||
|
'ClienteAnalisi' => 'clienteAnalisi',
|
||||||
'MoltiplicatorePrezzo' => 'moltiplicatore_prezzo_id',
|
'MoltiplicatorePrezzo' => 'moltiplicatore_prezzo_id',
|
||||||
'AnagraficaCertestObject' => 'anagrafica_certest_object_id',
|
'AnagraficaCertestObject' => 'anagrafica_certest_object_id',
|
||||||
'AnagraficaCertestService' => 'anagrafica_certest_service_id',
|
'AnagraficaCertestService' => 'anagrafica_certest_service_id',
|
||||||
'ClienteFornitore' => 'cliente_fornitore_id',
|
|
||||||
'ConsegnaRichiesta' => 'consegna_richiesta',
|
'ConsegnaRichiesta' => 'consegna_richiesta',
|
||||||
];
|
];
|
||||||
|
|
||||||
@@ -137,6 +139,16 @@ try {
|
|||||||
$params[] = $idclient;
|
$params[] = $idclient;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 5a2) cliente_fornitore_id (se presente)
|
||||||
|
if (isset($clienteFornitoreId)) {
|
||||||
|
$setParts[] = "cliente_fornitore_id = ?";
|
||||||
|
$params[] = $clienteFornitoreId;
|
||||||
|
}
|
||||||
|
// 5a3) tested_component (se presente)
|
||||||
|
if (isset($testedComponent)) {
|
||||||
|
$setParts[] = "tested_component = ?";
|
||||||
|
$params[] = ($testedComponent === '') ? null : $testedComponent;
|
||||||
|
}
|
||||||
// 5b) fixed fields dal POST
|
// 5b) fixed fields dal POST
|
||||||
// QUI è il punto chiave: accettiamo SOLO colonne reali (realWhitelist),
|
// QUI è il punto chiave: accettiamo SOLO colonne reali (realWhitelist),
|
||||||
// ma se dal JS arrivassero ancora key logiche, funzionerebbe uguale
|
// ma se dal JS arrivassero ancora key logiche, funzionerebbe uguale
|
||||||
|
|||||||
@@ -16,9 +16,11 @@ if (!$data || !isset($data['id'])) {
|
|||||||
exit;
|
exit;
|
||||||
}
|
}
|
||||||
|
|
||||||
$mappingId = $data['id'];
|
$mappingId = (int)$data['id'];
|
||||||
$mappingType = $data['mapping_type'] ?? '';
|
$mappingType = $data['mapping_type'] ?? '';
|
||||||
|
|
||||||
$excelColumn = $data['excel_column'] ?? null;
|
$excelColumn = $data['excel_column'] ?? null;
|
||||||
|
$jsonNode = $data['json_node'] ?? null;
|
||||||
$manualDefault = $data['manual_default'] ?? null;
|
$manualDefault = $data['manual_default'] ?? null;
|
||||||
|
|
||||||
$autoValue = $data['auto_value'] ?? 'none';
|
$autoValue = $data['auto_value'] ?? 'none';
|
||||||
@@ -26,7 +28,7 @@ $tablename = $data['tablename'] ?? '';
|
|||||||
|
|
||||||
try {
|
try {
|
||||||
// Normalize mapping type
|
// Normalize mapping type
|
||||||
$allowedTypes = ['', 'xls', 'manual', 'auto'];
|
$allowedTypes = ['', 'xls', 'json', 'manual', 'auto'];
|
||||||
if (!in_array($mappingType, $allowedTypes, true)) {
|
if (!in_array($mappingType, $allowedTypes, true)) {
|
||||||
echo json_encode(["success" => false, "message" => "Invalid mapping_type"]);
|
echo json_encode(["success" => false, "message" => "Invalid mapping_type"]);
|
||||||
exit;
|
exit;
|
||||||
@@ -41,28 +43,44 @@ try {
|
|||||||
// Decide what to persist based on mapping_type
|
// Decide what to persist based on mapping_type
|
||||||
$isManual = 0;
|
$isManual = 0;
|
||||||
$excelToSave = null;
|
$excelToSave = null;
|
||||||
|
$jsonNodeToSave = null;
|
||||||
$manualToSave = null;
|
$manualToSave = null;
|
||||||
$autoToSave = 'none';
|
$autoToSave = 'none';
|
||||||
|
|
||||||
if ($mappingType === 'xls') {
|
if ($mappingType === 'xls') {
|
||||||
|
|
||||||
$isManual = 0;
|
$isManual = 0;
|
||||||
$excelToSave = $excelColumn ?: null;
|
$excelToSave = $excelColumn ?: null;
|
||||||
|
$jsonNodeToSave = null;
|
||||||
|
$manualToSave = null;
|
||||||
|
$autoToSave = 'none';
|
||||||
|
} elseif ($mappingType === 'json') {
|
||||||
|
|
||||||
|
$isManual = 0;
|
||||||
|
$excelToSave = null;
|
||||||
|
$jsonNodeToSave = $jsonNode ?: null;
|
||||||
$manualToSave = null;
|
$manualToSave = null;
|
||||||
$autoToSave = 'none';
|
$autoToSave = 'none';
|
||||||
} elseif ($mappingType === 'manual') {
|
} elseif ($mappingType === 'manual') {
|
||||||
|
|
||||||
$isManual = 1;
|
$isManual = 1;
|
||||||
$excelToSave = null;
|
$excelToSave = null;
|
||||||
|
$jsonNodeToSave = null;
|
||||||
$manualToSave = $manualDefault;
|
$manualToSave = $manualDefault;
|
||||||
$autoToSave = 'none';
|
$autoToSave = 'none';
|
||||||
} elseif ($mappingType === 'auto') {
|
} elseif ($mappingType === 'auto') {
|
||||||
|
|
||||||
$isManual = 0;
|
$isManual = 0;
|
||||||
$excelToSave = null;
|
$excelToSave = null;
|
||||||
|
$jsonNodeToSave = null;
|
||||||
$manualToSave = null;
|
$manualToSave = null;
|
||||||
$autoToSave = $autoValue ?: 'none';
|
$autoToSave = $autoValue ?: 'none';
|
||||||
} else {
|
} else {
|
||||||
// reset
|
|
||||||
|
// Reset mapping
|
||||||
$isManual = 0;
|
$isManual = 0;
|
||||||
$excelToSave = null;
|
$excelToSave = null;
|
||||||
|
$jsonNodeToSave = null;
|
||||||
$manualToSave = null;
|
$manualToSave = null;
|
||||||
$autoToSave = 'none';
|
$autoToSave = 'none';
|
||||||
}
|
}
|
||||||
@@ -72,12 +90,20 @@ try {
|
|||||||
SET
|
SET
|
||||||
is_manual = ?,
|
is_manual = ?,
|
||||||
excel_column = ?,
|
excel_column = ?,
|
||||||
|
json_node = ?,
|
||||||
manual_default = ?,
|
manual_default = ?,
|
||||||
auto_value = ?
|
auto_value = ?
|
||||||
WHERE id = ?
|
WHERE id = ?
|
||||||
");
|
");
|
||||||
|
|
||||||
$result = $stmt->execute([$isManual, $excelToSave, $manualToSave, $autoToSave, $mappingId]);
|
$result = $stmt->execute([
|
||||||
|
$isManual,
|
||||||
|
$excelToSave,
|
||||||
|
$jsonNodeToSave,
|
||||||
|
$manualToSave,
|
||||||
|
$autoToSave,
|
||||||
|
$mappingId
|
||||||
|
]);
|
||||||
|
|
||||||
if (!$result) {
|
if (!$result) {
|
||||||
echo json_encode(["success" => false, "message" => "Database update failed"]);
|
echo json_encode(["success" => false, "message" => "Database update failed"]);
|
||||||
@@ -88,15 +114,17 @@ try {
|
|||||||
"success" => true,
|
"success" => true,
|
||||||
"message" => "Mapping updated successfully",
|
"message" => "Mapping updated successfully",
|
||||||
"saved" => [
|
"saved" => [
|
||||||
"id" => (int)$mappingId,
|
"id" => $mappingId,
|
||||||
"mapping_type" => $mappingType,
|
"mapping_type" => $mappingType,
|
||||||
"is_manual" => $isManual,
|
"is_manual" => $isManual,
|
||||||
"excel_column" => $excelToSave,
|
"excel_column" => $excelToSave,
|
||||||
|
"json_node" => $jsonNodeToSave,
|
||||||
"manual_default" => $manualToSave,
|
"manual_default" => $manualToSave,
|
||||||
"auto_value" => $autoToSave
|
"auto_value" => $autoToSave
|
||||||
]
|
]
|
||||||
]);
|
]);
|
||||||
} catch (Exception $e) {
|
} catch (Throwable $e) {
|
||||||
echo json_encode(["success" => false, "message" => "Error: " . $e->getMessage()]);
|
echo json_encode(["success" => false, "message" => "Error: " . $e->getMessage()]);
|
||||||
}
|
}
|
||||||
|
|
||||||
exit;
|
exit;
|
||||||
|
|||||||
@@ -0,0 +1,90 @@
|
|||||||
|
<?php
|
||||||
|
require_once dirname(__DIR__, 2) . '/vendor/autoload.php';
|
||||||
|
require_once __DIR__ . '/class/db-functions.php';
|
||||||
|
|
||||||
|
header('Content-Type: application/json');
|
||||||
|
ini_set('display_errors', '0');
|
||||||
|
error_reporting(E_ALL);
|
||||||
|
|
||||||
|
try {
|
||||||
|
if ($_SERVER['REQUEST_METHOD'] !== 'POST') {
|
||||||
|
http_response_code(405);
|
||||||
|
echo json_encode(['success' => false, 'message' => 'Method not allowed']);
|
||||||
|
exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
$partId = isset($_POST['part_id']) ? (int)$_POST['part_id'] : 0;
|
||||||
|
$iddatadb = isset($_POST['iddatadb']) ? (int)$_POST['iddatadb'] : 0;
|
||||||
|
$idmatrice = isset($_POST['idmatrice']) ? (int)$_POST['idmatrice'] : 0;
|
||||||
|
$analysisRecordkey = trim($_POST['analysis_recordkey'] ?? '');
|
||||||
|
$analysisName = trim($_POST['analysis_name'] ?? '');
|
||||||
|
$analysisMethod = trim($_POST['analysis_method'] ?? '');
|
||||||
|
$analysisLevel = isset($_POST['analysis_level']) && $_POST['analysis_level'] !== '' ? (int)$_POST['analysis_level'] : null;
|
||||||
|
$isWebSelectable = isset($_POST['is_web_selectable']) ? (int)$_POST['is_web_selectable'] : 0;
|
||||||
|
$isAccredited = isset($_POST['is_accredited']) ? (int)$_POST['is_accredited'] : 0;
|
||||||
|
|
||||||
|
if ($partId <= 0 || $analysisRecordkey === '') {
|
||||||
|
http_response_code(400);
|
||||||
|
echo json_encode(['success' => false, 'message' => 'Missing required data']);
|
||||||
|
exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
$db = DBHandlerSelect::getInstance();
|
||||||
|
$pdo = $db->getConnection();
|
||||||
|
|
||||||
|
$stmt = $pdo->prepare("
|
||||||
|
INSERT INTO identification_parts_analyses (
|
||||||
|
part_id,
|
||||||
|
iddatadb,
|
||||||
|
idmatrice,
|
||||||
|
analysis_recordkey,
|
||||||
|
analysis_name,
|
||||||
|
analysis_method,
|
||||||
|
analysis_level,
|
||||||
|
is_web_selectable,
|
||||||
|
is_accredited
|
||||||
|
) VALUES (
|
||||||
|
:part_id,
|
||||||
|
:iddatadb,
|
||||||
|
:idmatrice,
|
||||||
|
:analysis_recordkey,
|
||||||
|
:analysis_name,
|
||||||
|
:analysis_method,
|
||||||
|
:analysis_level,
|
||||||
|
:is_web_selectable,
|
||||||
|
:is_accredited
|
||||||
|
)
|
||||||
|
ON DUPLICATE KEY UPDATE
|
||||||
|
analysis_name = VALUES(analysis_name),
|
||||||
|
analysis_method = VALUES(analysis_method),
|
||||||
|
analysis_level = VALUES(analysis_level),
|
||||||
|
is_web_selectable = VALUES(is_web_selectable),
|
||||||
|
is_accredited = VALUES(is_accredited),
|
||||||
|
iddatadb = VALUES(iddatadb),
|
||||||
|
idmatrice = VALUES(idmatrice),
|
||||||
|
updated_at = CURRENT_TIMESTAMP
|
||||||
|
");
|
||||||
|
|
||||||
|
$stmt->execute([
|
||||||
|
':part_id' => $partId,
|
||||||
|
':iddatadb' => $iddatadb ?: null,
|
||||||
|
':idmatrice' => $idmatrice ?: null,
|
||||||
|
':analysis_recordkey' => $analysisRecordkey,
|
||||||
|
':analysis_name' => $analysisName ?: null,
|
||||||
|
':analysis_method' => $analysisMethod ?: null,
|
||||||
|
':analysis_level' => $analysisLevel,
|
||||||
|
':is_web_selectable' => $isWebSelectable,
|
||||||
|
':is_accredited' => $isAccredited,
|
||||||
|
]);
|
||||||
|
|
||||||
|
echo json_encode([
|
||||||
|
'success' => true,
|
||||||
|
'message' => 'Association saved'
|
||||||
|
]);
|
||||||
|
} catch (Throwable $e) {
|
||||||
|
http_response_code(500);
|
||||||
|
echo json_encode([
|
||||||
|
'success' => false,
|
||||||
|
'message' => $e->getMessage()
|
||||||
|
]);
|
||||||
|
}
|
||||||
File diff suppressed because one or more lines are too long
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user