Compare commits
122 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| d623ee797c | |||
| 4dd7b89c22 | |||
| dec42b4442 | |||
| d088364a0d | |||
| 6e43a178af | |||
| 25bd916221 | |||
| dab8d9aebf | |||
| 375a10a678 | |||
| 15990be884 | |||
| c3a6dd73b6 | |||
| 44ed1186e0 | |||
| 9050cb1006 | |||
| e6820fdb62 | |||
| 5da37a7836 | |||
| c5f27cb69a | |||
| 1d81d6c996 | |||
| 0c72dbf5ae | |||
| 8455be04e1 | |||
| e42d1b9c51 | |||
| 3e69e3c322 | |||
| 0eb4f7a2ad | |||
| 4f2cfc1930 | |||
| df075dd76a | |||
| 6460454201 | |||
| 574ddbbd32 | |||
| 41f414db5c | |||
| 4a863e8c16 | |||
| b431f1d4e9 | |||
| f97b52f158 | |||
| 836fc055ec | |||
| e8dd585df4 | |||
| 198b8c08ad | |||
| 28c467d55e | |||
| 56eee99a67 | |||
| f514b3d2c7 | |||
| a3eb0f0a57 | |||
| aa355905d7 | |||
| b38f3e1240 | |||
| cbd0c5b68a | |||
| f3e5cb4ffd | |||
| 0f0c6a04b7 | |||
| 813bd66f96 | |||
| ce00247d1c | |||
| 3a7dd266c8 | |||
| ba8dc4c721 | |||
| cfbbc36116 | |||
| 50d578eea1 | |||
| 6b0d2aa9b9 | |||
| 67bbd9bbbb | |||
| 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 | |||
| cdd6551e9c | |||
| 1b97bf4362 | |||
| c516589483 | |||
| 817bbadf22 | |||
| 1f27bc48d4 | |||
| c9fba48d88 | |||
| 70d4c0759e | |||
| 5d7880160a | |||
| bbe74d1529 | |||
| 93930227a2 | |||
| 2598a4c91b | |||
| 5e677a8b9c | |||
| 540c44d89a | |||
| 2ee9f2ecb1 | |||
| c9122774b1 | |||
| 1fed113c5c |
+10
-2
@@ -33,15 +33,21 @@ yarn-error.log
|
||||
/public/userarea/*.log
|
||||
/public/userarea/*.txt
|
||||
/public/userarea/*_response.json
|
||||
/public/userarea/customfield_values_response.json
|
||||
/public/userarea/error_log.txt
|
||||
/public/userarea/import_debug.log
|
||||
/public/userarea/last_url.txt
|
||||
/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/class/*.log
|
||||
/public/userarea/class/curl_auth_debug.log
|
||||
/public/userarea/class/curl_request_debug.log
|
||||
/public/userarea/schema_dettagli_response.json
|
||||
public/userarea/schemi_base_response.json
|
||||
|
||||
# File XLSX temporanei importati
|
||||
/public/userarea/imported_trf/*.xlsx
|
||||
@@ -52,4 +58,6 @@ yarn-error.log
|
||||
/public/photostrf/qrcodes/
|
||||
|
||||
# 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,854 @@
|
||||
(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 = true;
|
||||
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");
|
||||
}
|
||||
|
||||
const cacheKey = String(matrixId) + "_WEB_ONLY";
|
||||
|
||||
if (analysisLoadedCache[cacheKey]) {
|
||||
renderAnalysesList(analysisLoadedCache[cacheKey]);
|
||||
return;
|
||||
}
|
||||
|
||||
setAnalysisLoadingState(true);
|
||||
|
||||
$.ajax({
|
||||
url: "get_analisi_matrice_filter.php",
|
||||
method: "GET",
|
||||
dataType: "json",
|
||||
data: {
|
||||
id_matrice: matrixId,
|
||||
web_only: 1,
|
||||
},
|
||||
})
|
||||
.done(function (response) {
|
||||
const analyses = Array.isArray(response.value)
|
||||
? response.value.filter(function (item) {
|
||||
return (
|
||||
item.SelezionabileSuWeb === true ||
|
||||
item.SelezionabileSuWeb === 1 ||
|
||||
item.SelezionabileSuWeb === "1"
|
||||
);
|
||||
})
|
||||
: [];
|
||||
|
||||
analysisLoadedCache[cacheKey] = 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();
|
||||
});
|
||||
}
|
||||
|
||||
// WEB only is now fixed by default
|
||||
|
||||
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 = [];
|
||||
// DIMENSIONE GLOBALE MARKER
|
||||
let globalMarkerSize = 16;
|
||||
// COLORE TESTO LISTA DESCRIZIONI
|
||||
let globalDescriptionColor = "#000000";
|
||||
|
||||
// ===================
|
||||
// MODAL INITIALIZATION
|
||||
@@ -138,6 +140,7 @@ $(document).ready(function () {
|
||||
descriptionTextbox = null;
|
||||
markerObjects = {};
|
||||
globalMarkerSize = 16;
|
||||
globalDescriptionColor = "#000000";
|
||||
$("#photoSelectorContainerAnnotations").empty().hide();
|
||||
$("#samplePhotoAnnotations").attr("src", "");
|
||||
$("#partsListAnnotations").empty();
|
||||
@@ -177,7 +180,25 @@ $(document).ready(function () {
|
||||
updateMarkers();
|
||||
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
|
||||
// ===================
|
||||
@@ -614,17 +635,11 @@ $(document).ready(function () {
|
||||
partsListData.forEach((part) => {
|
||||
const partNumber = part.part_number;
|
||||
const partDescription = part.part_description;
|
||||
const isMixPart = String(part.mix || "N").toUpperCase() === "Y";
|
||||
|
||||
const partColor =
|
||||
partColors[partNumber] ||
|
||||
(partDescription.toLowerCase().startsWith("mix")
|
||||
? "#0000ff"
|
||||
: "#ff0000");
|
||||
if (
|
||||
partNumber &&
|
||||
partDescription &&
|
||||
(showMixParts ||
|
||||
!partDescription.toLowerCase().startsWith("mix"))
|
||||
) {
|
||||
partColors[partNumber] || (isMixPart ? "#0000ff" : "#ff0000");
|
||||
if (partNumber && partDescription && (showMixParts || !isMixPart)) {
|
||||
const colorOptions = predefinedColors
|
||||
.map(
|
||||
(color) =>
|
||||
@@ -922,11 +937,10 @@ $(document).ready(function () {
|
||||
) {
|
||||
partsListData = response.parts;
|
||||
response.parts.forEach((part) => {
|
||||
const defaultColor = part.part_description
|
||||
.toLowerCase()
|
||||
.startsWith("mix")
|
||||
? "#0000ff"
|
||||
: "#ff0000";
|
||||
const isMixPart =
|
||||
String(part.mix || "N").toUpperCase() === "Y";
|
||||
const defaultColor = isMixPart ? "#0000ff" : "#ff0000";
|
||||
|
||||
partColors[part.part_number] = defaultColor;
|
||||
});
|
||||
updatePartsList();
|
||||
@@ -1003,11 +1017,10 @@ $(document).ready(function () {
|
||||
(p) => p.part_number == marker.partNumber,
|
||||
);
|
||||
const partDescription = part ? part.part_description : "";
|
||||
if (
|
||||
!showMixParts &&
|
||||
partDescription &&
|
||||
partDescription.toLowerCase().startsWith("mix")
|
||||
) {
|
||||
const isMixPart =
|
||||
part && String(part.mix || "N").toUpperCase() === "Y";
|
||||
|
||||
if (!showMixParts && isMixPart) {
|
||||
console.log("Ignoro marker per parte Mix:", marker.partNumber);
|
||||
return;
|
||||
}
|
||||
@@ -1115,11 +1128,10 @@ $(document).ready(function () {
|
||||
}
|
||||
|
||||
const partsList = partsListData
|
||||
.filter(
|
||||
(part) =>
|
||||
showMixParts ||
|
||||
!part.part_description.toLowerCase().startsWith("mix"),
|
||||
)
|
||||
.filter((part) => {
|
||||
const isMixPart = String(part.mix || "N").toUpperCase() === "Y";
|
||||
return showMixParts || !isMixPart;
|
||||
})
|
||||
.map((part) => `${part.part_number} ${part.part_description}`);
|
||||
|
||||
const text = partsList.join("\n");
|
||||
@@ -1139,7 +1151,7 @@ $(document).ready(function () {
|
||||
backgroundColor: "transparent",
|
||||
fontFamily: "Arial",
|
||||
fontSize: Math.max(16, Math.round(globalMarkerSize * 0.8)),
|
||||
fill: "#000000",
|
||||
fill: globalDescriptionColor,
|
||||
padding: 10,
|
||||
editable: false,
|
||||
selectable: true,
|
||||
|
||||
@@ -3,6 +3,9 @@ ob_start();
|
||||
session_start();
|
||||
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' => []];
|
||||
|
||||
try {
|
||||
|
||||
@@ -24,7 +24,17 @@ class VisualLimsApiClient
|
||||
public static function getInstance()
|
||||
{
|
||||
if (self::$instance === null) {
|
||||
self::$instance = new VisualLimsApiClient();
|
||||
$dotenv = Dotenv::createImmutable(dirname(__DIR__, 3));
|
||||
$dotenv->load();
|
||||
|
||||
$simulate = ($_ENV['SIMULATE_EXPORT_LIMS'] ?? '') === 'true';
|
||||
|
||||
if ($simulate) {
|
||||
require_once __DIR__ . '/VisualLimsApiClientMock.class.php';
|
||||
self::$instance = new VisualLimsApiClientMock();
|
||||
} else {
|
||||
self::$instance = new VisualLimsApiClient();
|
||||
}
|
||||
}
|
||||
return self::$instance;
|
||||
}
|
||||
@@ -193,8 +203,112 @@ class VisualLimsApiClient
|
||||
return json_decode($response, true);
|
||||
}
|
||||
|
||||
/**
|
||||
* POST a file as multipart/form-data (used for photo/attachment uploads).
|
||||
*
|
||||
* @param string $endpoint OData endpoint, e.g. "Campione(613388)/UploadCampioneFile"
|
||||
* @param string $filePath Absolute path to the file on disk
|
||||
* @param string $fileName Original file name to send
|
||||
* @param array $extraFields Additional form fields to include
|
||||
* @return array|null Decoded JSON response
|
||||
*/
|
||||
public function postMultipart($endpoint, $filePath, $fileName, array $extraFields = [])
|
||||
{
|
||||
$token = $this->getToken();
|
||||
$url = "{$this->baseUrl}/api/odata/{$endpoint}";
|
||||
|
||||
$cfile = new CURLFile($filePath, mime_content_type($filePath) ?: 'application/octet-stream', $fileName);
|
||||
|
||||
$payload = array_merge($extraFields, [
|
||||
'file' => $cfile,
|
||||
]);
|
||||
|
||||
$ch = curl_init($url);
|
||||
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
|
||||
curl_setopt($ch, CURLOPT_POST, true);
|
||||
curl_setopt($ch, CURLOPT_POSTFIELDS, $payload);
|
||||
curl_setopt($ch, CURLOPT_HTTPHEADER, [
|
||||
"Authorization: Bearer {$token}",
|
||||
"Accept: application/json",
|
||||
// Content-Type is set automatically to multipart/form-data by cURL
|
||||
]);
|
||||
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 nella richiesta POST multipart: {$curl_error}");
|
||||
}
|
||||
|
||||
if ($http_code < 200 || $http_code >= 300) {
|
||||
throw new Exception("POST multipart fallito: HTTP {$http_code}, Risposta: " . substr($response, 0, 1000));
|
||||
}
|
||||
|
||||
return json_decode($response, true);
|
||||
}
|
||||
|
||||
public function getBaseUrl()
|
||||
{
|
||||
return $this->baseUrl;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get raw/binary content from VisualLims API.
|
||||
* Used for PDF downloads from MediaFile/DownloadStream.
|
||||
*/
|
||||
public function getRaw($endpoint)
|
||||
{
|
||||
$token = $this->getToken();
|
||||
|
||||
/*
|
||||
* Normal JSON OData calls use:
|
||||
* {$this->baseUrl}/api/odata/...
|
||||
*
|
||||
* Media file downloads use:
|
||||
* {$this->baseUrl}/api/MediaFile/DownloadStream...
|
||||
*/
|
||||
$url = rtrim($this->baseUrl, '/') . '/api/' . ltrim($endpoint, '/');
|
||||
|
||||
$ch = curl_init($url);
|
||||
|
||||
curl_setopt_array($ch, [
|
||||
CURLOPT_RETURNTRANSFER => true,
|
||||
CURLOPT_HTTPHEADER => [
|
||||
"Authorization: Bearer {$token}",
|
||||
"Accept: application/pdf,*/*"
|
||||
],
|
||||
CURLOPT_SSL_VERIFYPEER => false,
|
||||
CURLOPT_SSL_VERIFYHOST => false,
|
||||
CURLOPT_FOLLOWLOCATION => true,
|
||||
CURLOPT_TIMEOUT => 60
|
||||
]);
|
||||
|
||||
$response = curl_exec($ch);
|
||||
$httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
|
||||
$contentType = curl_getinfo($ch, CURLINFO_CONTENT_TYPE);
|
||||
$curlError = curl_error($ch);
|
||||
|
||||
curl_close($ch);
|
||||
|
||||
if ($response === false) {
|
||||
throw new Exception("Errore cURL download raw: " . $curlError);
|
||||
}
|
||||
|
||||
if ($httpCode < 200 || $httpCode >= 300) {
|
||||
throw new Exception(
|
||||
"Errore HTTP {$httpCode} durante download raw. Content-Type: {$contentType}. Response: " .
|
||||
substr($response, 0, 500)
|
||||
);
|
||||
}
|
||||
|
||||
if (empty($response)) {
|
||||
throw new Exception("Risposta vuota dal download raw.");
|
||||
}
|
||||
|
||||
return $response;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,135 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* Mock implementation of VisualLimsApiClient.
|
||||
* Activated when SIMULATE_EXPORT_LIMS=true in .env.
|
||||
* All HTTP calls are skipped; fake but structurally valid data is returned.
|
||||
* Every simulated call is logged via error_log() with a [SIMULATE] prefix.
|
||||
*/
|
||||
class VisualLimsApiClientMock
|
||||
{
|
||||
private int $fakeCommessaId;
|
||||
|
||||
public function __construct()
|
||||
{
|
||||
// Stable fake ID for the lifetime of a single request
|
||||
$this->fakeCommessaId = mt_rand(90001, 99999);
|
||||
error_log("[SIMULATE] VisualLimsApiClientMock initialised (fakeCommessaId={$this->fakeCommessaId})");
|
||||
}
|
||||
|
||||
public function get(string $endpoint): array
|
||||
{
|
||||
error_log("[SIMULATE] GET {$endpoint}");
|
||||
|
||||
// --- Fixed-field dropdown lists ---
|
||||
|
||||
if (str_starts_with($endpoint, 'MoltiplicatorePrezzi')) {
|
||||
return ['value' => [
|
||||
['IdMoltiplicatorePrezzo' => 1, 'Codice' => 'MP-01', 'Descrizione' => 'Standard (1x)'],
|
||||
['IdMoltiplicatorePrezzo' => 2, 'Codice' => 'MP-02', 'Descrizione' => 'Urgente (1.5x)'],
|
||||
['IdMoltiplicatorePrezzo' => 3, 'Codice' => 'MP-03', 'Descrizione' => 'Extra Urgente (2x)'],
|
||||
]];
|
||||
}
|
||||
|
||||
if (str_starts_with($endpoint, 'AnagraficaCertestObject')) {
|
||||
return ['value' => [
|
||||
['IdAnagrafica' => 1, 'Codice' => 'OBJ-01', 'NomeAnagrafica' => 'Articolo Tessile'],
|
||||
['IdAnagrafica' => 2, 'Codice' => 'OBJ-02', 'NomeAnagrafica' => 'Componente Meccanico'],
|
||||
['IdAnagrafica' => 3, 'Codice' => 'OBJ-03', 'NomeAnagrafica' => 'Materiale Plastico'],
|
||||
]];
|
||||
}
|
||||
|
||||
if (str_starts_with($endpoint, 'AnagraficaCertestService')) {
|
||||
return ['value' => [
|
||||
['IdAnagrafica' => 1, 'Codice' => 'SRV-01', 'NomeAnagrafica' => 'Analisi Chimica'],
|
||||
['IdAnagrafica' => 2, 'Codice' => 'SRV-02', 'NomeAnagrafica' => 'Test Meccanico'],
|
||||
['IdAnagrafica' => 3, 'Codice' => 'SRV-03', 'NomeAnagrafica' => 'Prova Ambientale'],
|
||||
]];
|
||||
}
|
||||
|
||||
// Cliente? list — get_clienti.php exits early in simulate mode, but guard here too
|
||||
if (str_starts_with($endpoint, 'Cliente?')) {
|
||||
return ['value' => []];
|
||||
}
|
||||
|
||||
// Cliente(N)?$expand=Responsabili
|
||||
if (str_starts_with($endpoint, 'Cliente(')) {
|
||||
preg_match('/Cliente\((\d+)\)/', $endpoint, $m);
|
||||
$clienteId = isset($m[1]) ? (int) $m[1] : 0;
|
||||
return [
|
||||
'IdCliente' => $clienteId,
|
||||
'Responsabili' => [
|
||||
['IdClienteResponsabile' => 1, 'Nominativo' => 'Marco Bianchi'],
|
||||
['IdClienteResponsabile' => 2, 'Nominativo' => 'Giulia Ferrari'],
|
||||
['IdClienteResponsabile' => 3, 'Nominativo' => 'Andrea Russo'],
|
||||
],
|
||||
];
|
||||
}
|
||||
|
||||
// --- CustomField dropdown values (get_customfield_values.php) ---
|
||||
|
||||
if (str_starts_with($endpoint, 'CustomField(')) {
|
||||
preg_match('/CustomField\((\d+)\)/', $endpoint, $m);
|
||||
$fieldId = isset($m[1]) ? (int) $m[1] : 0;
|
||||
return [
|
||||
'CustomFieldsValues' => [
|
||||
['IdCustomFieldsValue' => $fieldId * 10 + 1, 'Valore' => 'Opzione A'],
|
||||
['IdCustomFieldsValue' => $fieldId * 10 + 2, 'Valore' => 'Opzione B'],
|
||||
['IdCustomFieldsValue' => $fieldId * 10 + 3, 'Valore' => 'Opzione C'],
|
||||
],
|
||||
];
|
||||
}
|
||||
|
||||
// --- CommessaWeb OData calls (STEP 7 GET + STEP 10 verification) ---
|
||||
|
||||
preg_match('/\((\d+)\)/', $endpoint, $m);
|
||||
$id = isset($m[1]) ? (int) $m[1] : $this->fakeCommessaId;
|
||||
|
||||
return [
|
||||
'IdCommessa' => $id,
|
||||
'CodiceCommessa' => "SIM-{$id}",
|
||||
'CommesseCustomFields' => [], // Empty → PATCH step is skipped correctly
|
||||
];
|
||||
}
|
||||
|
||||
public function post(string $endpoint, array $payload): array
|
||||
{
|
||||
error_log("[SIMULATE] POST {$endpoint} payload=" . json_encode($payload));
|
||||
|
||||
// CommessaWeb creation
|
||||
if ($endpoint === 'CommessaWeb') {
|
||||
return [
|
||||
'IdCommessa' => $this->fakeCommessaId,
|
||||
'CodiceCommessa' => "SIM-{$this->fakeCommessaId}",
|
||||
'Richiedente' => $payload['Richiedente'] ?? '',
|
||||
'Descrizione' => $payload['Descrizione'] ?? '',
|
||||
];
|
||||
}
|
||||
|
||||
// Campione creation
|
||||
if ($endpoint === 'Campione') {
|
||||
return [
|
||||
'IdCampione' => mt_rand(10001, 19999),
|
||||
'Commessa' => $payload['Commessa'] ?? null,
|
||||
'Matrice' => $payload['Matrice'] ?? null,
|
||||
];
|
||||
}
|
||||
|
||||
// InviaCommessa / ImportaCommessa (currently commented out upstream)
|
||||
return ['simulated' => true, 'endpoint' => $endpoint];
|
||||
}
|
||||
|
||||
public function patch(string $endpoint, array $payload): array
|
||||
{
|
||||
error_log("[SIMULATE] PATCH {$endpoint} payload=" . json_encode($payload));
|
||||
|
||||
return [];
|
||||
}
|
||||
|
||||
public function postMultipart(string $endpoint, string $filePath, string $fileName, array $extraFields = []): array
|
||||
{
|
||||
error_log("[SIMULATE] POST multipart {$endpoint} file={$fileName}");
|
||||
|
||||
return ['simulated' => true, 'file' => $fileName];
|
||||
}
|
||||
}
|
||||
@@ -3,6 +3,9 @@ require_once dirname(__DIR__, 3) . '/vendor/autoload.php';
|
||||
|
||||
use Dotenv\Dotenv;
|
||||
|
||||
Dotenv::createImmutable(dirname(__DIR__, 3))->safeLoad();
|
||||
date_default_timezone_set($_ENV['APP_TIMEZONE'] ?? 'Europe/Rome');
|
||||
|
||||
class DBHandlerSelect
|
||||
{
|
||||
private static $instance = null;
|
||||
|
||||
@@ -0,0 +1,133 @@
|
||||
<?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 = ?
|
||||
AND part_description IS NOT NULL
|
||||
AND TRIM(part_description) <> ''
|
||||
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 = [
|
||||
// fixed_field_key, data_type
|
||||
['ClienteResponsabile', 'INT'],
|
||||
['ClienteFornitore', 'INT'],
|
||||
['ClienteAnalisi', 'INT'],
|
||||
['MoltiplicatorePrezzo', 'INT'],
|
||||
['AnagraficaCertestObject', 'INT'],
|
||||
['AnagraficaCertestService', 'INT'],
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -17,6 +17,8 @@ try {
|
||||
exit(1);
|
||||
}
|
||||
|
||||
date_default_timezone_set($_ENV['APP_TIMEZONE'] ?? 'Europe/Rome');
|
||||
|
||||
// Recupera le variabili d'ambiente
|
||||
$dbHost = $_ENV['DB_HOST'];
|
||||
$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);
|
||||
}
|
||||
@@ -0,0 +1,46 @@
|
||||
<?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);
|
||||
|
||||
try {
|
||||
$idRapportoFile = isset($_GET['id_rapporto_file']) ? (int)$_GET['id_rapporto_file'] : 0;
|
||||
|
||||
if ($idRapportoFile <= 0) {
|
||||
throw new Exception("Parametro id_rapporto_file mancante o non valido.");
|
||||
}
|
||||
|
||||
/*
|
||||
* This endpoint returns the PDF binary stream.
|
||||
* Do not call this with the normal get() method because get() expects JSON.
|
||||
*/
|
||||
$endpoint = "MediaFile/DownloadStream?objectType=RapportoFile&propertyName=FileContent&objectKey={$idRapportoFile}";
|
||||
|
||||
$api = VisualLimsApiClient::getInstance();
|
||||
$pdfContent = $api->getRaw($endpoint);
|
||||
|
||||
if (empty($pdfContent)) {
|
||||
throw new Exception("PDF vuoto o non ricevuto dal server.");
|
||||
}
|
||||
|
||||
$fileName = "rapporto_{$idRapportoFile}.pdf";
|
||||
|
||||
header('Content-Type: application/pdf');
|
||||
header('Content-Disposition: inline; filename="' . $fileName . '"');
|
||||
header('Content-Length: ' . strlen($pdfContent));
|
||||
header('Cache-Control: private, max-age=0, must-revalidate');
|
||||
header('Pragma: public');
|
||||
|
||||
echo $pdfContent;
|
||||
exit;
|
||||
} 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');
|
||||
|
||||
// Controlla se è stato passato un ID valido
|
||||
// Check if a valid ID was provided
|
||||
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;
|
||||
}
|
||||
|
||||
$id = intval($_GET['id']); // Sanifica l'ID
|
||||
$id = intval($_GET['id']);
|
||||
|
||||
// Recupera il template dal database
|
||||
// Retrieve template from database
|
||||
$db = DBHandlerSelect::getInstance();
|
||||
$pdo = $db->getConnection();
|
||||
|
||||
$stmt = $pdo->prepare("SELECT * FROM excel_templates WHERE id = ?");
|
||||
$stmt->execute([$id]);
|
||||
$template = $stmt->fetch(PDO::FETCH_ASSOC);
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
// Recupera tutte le routine dal database
|
||||
// Retrieve all routines
|
||||
$stmt = $pdo->prepare("SELECT * FROM routine");
|
||||
$stmt->execute();
|
||||
$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>
|
||||
<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 href="https://cdn.jsdelivr.net/npm/select2@4.1.0-rc.0/dist/css/select2.min.css" rel="stylesheet" />
|
||||
<?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://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>
|
||||
@@ -44,19 +147,22 @@ $routines = $stmt->fetchAll(PDO::FETCH_ASSOC);
|
||||
<div class="wrapper">
|
||||
<?php include('include/navbar.php'); ?>
|
||||
<?php include('include/topbar.php'); ?>
|
||||
|
||||
<div class="page-wrapper">
|
||||
<div class="page-content">
|
||||
|
||||
<div class="card mb-4">
|
||||
<div class="card-header">
|
||||
<h5 class="mb-0">Update XLS Template</h5>
|
||||
<h5 class="mb-0">Update Template</h5>
|
||||
</div>
|
||||
<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>
|
||||
<ul class="mb-0">
|
||||
<li>Template Name</li>
|
||||
<li>Row Header and Column Header: where the title of the excel starts</li>
|
||||
<li>Schema</li>
|
||||
<li>Source Type</li>
|
||||
<li>Schema and Client</li>
|
||||
<li>Row Header and Column Header only for XLS templates</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
@@ -69,34 +175,100 @@ $routines = $stmt->fetchAll(PDO::FETCH_ASSOC);
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="card-body">
|
||||
<div class="col-12">
|
||||
<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">
|
||||
<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 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>
|
||||
<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 class="mb-3">
|
||||
<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>
|
||||
<div class="mb-3" id="startColumnWrapper">
|
||||
<label class="form-label"><?= htmlspecialchars($columnheader, ENT_QUOTES, 'UTF-8'); ?> *</label>
|
||||
<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 class="mb-3">
|
||||
<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 class="mb-3">
|
||||
<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>
|
||||
<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'] ?? 'datadb'); ?>" readonly required>
|
||||
</div>
|
||||
|
||||
<div class="mb-3">
|
||||
@@ -110,12 +282,46 @@ $routines = $stmt->fetchAll(PDO::FETCH_ASSOC);
|
||||
|
||||
<div class="mb-3">
|
||||
<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 class="mb-3">
|
||||
<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 class="mb-3">
|
||||
@@ -128,7 +334,7 @@ $routines = $stmt->fetchAll(PDO::FETCH_ASSOC);
|
||||
<select name="client_id" id="clientSelect" class="form-control" required>
|
||||
<option value="">Select a client...</option>
|
||||
</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 class="mb-3">
|
||||
@@ -136,7 +342,7 @@ $routines = $stmt->fetchAll(PDO::FETCH_ASSOC);
|
||||
<select name="schema_id" id="schemaSelect" class="form-control" required>
|
||||
<option value="">Select a schema...</option>
|
||||
</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 class="mb-3">
|
||||
@@ -144,11 +350,12 @@ $routines = $stmt->fetchAll(PDO::FETCH_ASSOC);
|
||||
<select name="idroutine" id="routineSelect" class="form-control">
|
||||
<option value="">Select a routine...</option>
|
||||
<?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']); ?>
|
||||
</option>
|
||||
<?php endforeach; ?>
|
||||
</select>
|
||||
|
||||
<div id="routineDetails" class="mt-2" style="display: none;">
|
||||
<h6>Routine Details</h6>
|
||||
<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 class="overlay toggle-icon"></div>
|
||||
<a href="javaScript:;" class="back-to-top"><i class='bx bxs-up-arrow-alt'></i></a>
|
||||
<?php include('include/footer.php'); ?>
|
||||
@@ -175,9 +384,8 @@ $routines = $stmt->fetchAll(PDO::FETCH_ASSOC);
|
||||
|
||||
<script>
|
||||
document.addEventListener("DOMContentLoaded", function() {
|
||||
// Verifica che jQuery sia caricato
|
||||
if (typeof jQuery === 'undefined') {
|
||||
alert("Errore: jQuery non è caricato. Contatta l'amministratore.");
|
||||
alert("Error: jQuery is not loaded.");
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -192,12 +400,21 @@ $routines = $stmt->fetchAll(PDO::FETCH_ASSOC);
|
||||
const routineAction2 = document.getElementById("routineAction2");
|
||||
const routineAction3 = document.getElementById("routineAction3");
|
||||
|
||||
if (!form || !clientLoadingStatus || !schemaLoadingStatus || !routineSelect || !routineDetails) {
|
||||
alert("Errore: Uno o più elementi della pagina non sono stati trovati. Contatta l'amministratore.");
|
||||
return;
|
||||
}
|
||||
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");
|
||||
|
||||
const selectedClientId = <?php echo json_encode((int)($template['idclient'] ?? 0)); ?>;
|
||||
const selectedSchemaId = <?php echo json_encode((int)($template['idschema'] ?? 0)); ?>;
|
||||
|
||||
// Inizializza Select2
|
||||
$('#clientSelect').select2({
|
||||
placeholder: "Search for a client...",
|
||||
allowClear: true
|
||||
@@ -213,108 +430,201 @@ $routines = $stmt->fetchAll(PDO::FETCH_ASSOC);
|
||||
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() {
|
||||
try {
|
||||
clientLoadingStatus.style.display = 'inline';
|
||||
clientLoadingStatus.textContent = 'Recupero clienti in corso...';
|
||||
clientLoadingStatus.textContent = 'Loading clients...';
|
||||
|
||||
const response = await fetch("get_clienti.php", {
|
||||
method: "GET",
|
||||
headers: {
|
||||
"Content-Type": "application/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");
|
||||
select.innerHTML = '<option value="">Select a client...</option>';
|
||||
|
||||
data.value.forEach(client => {
|
||||
const nome = client.Nominativo || "Nome non disponibile";
|
||||
const id = client.IdCliente || "ID non disponibile";
|
||||
const option = new Option(`${nome.trim()} (ID: ${id})`, id);
|
||||
if (parseInt(id) === parseInt(<?php echo json_encode($template['idclient'] ?? 0); ?>)) {
|
||||
const id = client.IdCliente || "";
|
||||
const option = new Option(formatClientLabel(client), id);
|
||||
|
||||
if (parseInt(id) === parseInt(selectedClientId)) {
|
||||
option.selected = true;
|
||||
}
|
||||
|
||||
select.add(option);
|
||||
});
|
||||
|
||||
$(select).trigger('change');
|
||||
clientLoadingStatus.textContent = "Clienti caricati.";
|
||||
clientLoadingStatus.textContent = "Clients loaded.";
|
||||
} catch (error) {
|
||||
clientLoadingStatus.textContent = "Errore nel caricamento.";
|
||||
clientLoadingStatus.textContent = "Loading error.";
|
||||
|
||||
Swal.fire({
|
||||
title: "Errore!",
|
||||
text: "Impossibile caricare i clienti: " + error.message,
|
||||
title: "Error!",
|
||||
text: "Unable to load clients: " + error.message,
|
||||
icon: "error",
|
||||
confirmButtonText: "OK"
|
||||
});
|
||||
} finally {
|
||||
setTimeout(() => clientLoadingStatus.style.display = 'none', 2000);
|
||||
setTimeout(() => clientLoadingStatus.style.display = 'none', 1500);
|
||||
}
|
||||
}
|
||||
|
||||
// Carica gli schemi
|
||||
async function loadSchemas() {
|
||||
try {
|
||||
schemaLoadingStatus.style.display = 'inline';
|
||||
schemaLoadingStatus.textContent = 'Caricamento schemi in corso...';
|
||||
schemaLoadingStatus.textContent = 'Loading schemas...';
|
||||
|
||||
const response = await fetch("get_schemi.php", {
|
||||
method: "GET",
|
||||
headers: {
|
||||
"Content-Type": "application/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");
|
||||
select.innerHTML = '<option value="">Select a schema...</option>';
|
||||
data.value.forEach(schema => {
|
||||
const nome = schema.Nome || "Nome non disponibile";
|
||||
const id = schema.IdSchemaCustomFields || "ID non disponibile";
|
||||
|
||||
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 || "Name not available";
|
||||
const id = schema.IdSchemaCustomFields || "";
|
||||
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;
|
||||
}
|
||||
|
||||
select.add(option);
|
||||
});
|
||||
|
||||
$(select).trigger('change');
|
||||
schemaLoadingStatus.textContent = "Schemi caricati.";
|
||||
schemaLoadingStatus.textContent = "Schemas loaded.";
|
||||
} catch (error) {
|
||||
schemaLoadingStatus.textContent = "Errore nel caricamento.";
|
||||
schemaLoadingStatus.textContent = "Loading error.";
|
||||
|
||||
Swal.fire({
|
||||
title: "Errore!",
|
||||
text: "Impossibile caricare gli schemi: " + error.message,
|
||||
title: "Error!",
|
||||
text: "Unable to load schemas: " + error.message,
|
||||
icon: "error",
|
||||
confirmButtonText: "OK"
|
||||
});
|
||||
} finally {
|
||||
setTimeout(() => schemaLoadingStatus.style.display = 'none', 2000);
|
||||
setTimeout(() => schemaLoadingStatus.style.display = 'none', 1500);
|
||||
}
|
||||
}
|
||||
|
||||
// Carica i dati
|
||||
async function loadData() {
|
||||
try {
|
||||
await loadClients();
|
||||
await loadSchemas();
|
||||
} catch (error) {
|
||||
Swal.fire({
|
||||
title: "Errore!",
|
||||
text: "Errore nel caricamento dei dati: " + error.message,
|
||||
title: "Error!",
|
||||
text: "Error while loading data: " + error.message,
|
||||
icon: "error",
|
||||
confirmButtonText: "OK"
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
loadData();
|
||||
|
||||
// Routine dettagli
|
||||
const routines = <?php echo json_encode($routines); ?>;
|
||||
|
||||
function updateRoutineDetails() {
|
||||
const selectedId = routineSelect.value;
|
||||
routineDetails.style.display = selectedId ? 'block' : 'none';
|
||||
|
||||
if (selectedId) {
|
||||
const routine = routines.find(r => r.idroutine == selectedId);
|
||||
|
||||
if (routine) {
|
||||
routineName.textContent = routine.name || 'N/A';
|
||||
routineDescription.textContent = routine.description || 'N/A';
|
||||
@@ -336,10 +646,10 @@ $routines = $stmt->fetchAll(PDO::FETCH_ASSOC);
|
||||
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) {
|
||||
e.preventDefault();
|
||||
|
||||
@@ -351,8 +661,8 @@ $routines = $stmt->fetchAll(PDO::FETCH_ASSOC);
|
||||
|
||||
if (!clientId) {
|
||||
Swal.fire({
|
||||
title: "Errore!",
|
||||
text: "Per favore seleziona un cliente.",
|
||||
title: "Error!",
|
||||
text: "Please select a client.",
|
||||
icon: "error",
|
||||
confirmButtonText: "OK"
|
||||
});
|
||||
@@ -373,8 +683,8 @@ $routines = $stmt->fetchAll(PDO::FETCH_ASSOC);
|
||||
|
||||
if (!schemaId) {
|
||||
Swal.fire({
|
||||
title: "Errore!",
|
||||
text: "Per favore seleziona uno schema.",
|
||||
title: "Error!",
|
||||
text: "Please select a schema.",
|
||||
icon: "error",
|
||||
confirmButtonText: "OK"
|
||||
});
|
||||
@@ -387,13 +697,35 @@ $routines = $stmt->fetchAll(PDO::FETCH_ASSOC);
|
||||
const nameMatch = optionText.match(/^(.+?)(?:\s*\(ID:\s*\d+\))?$/);
|
||||
schemaName = nameMatch ? nameMatch[1].trim() : optionText;
|
||||
}
|
||||
|
||||
formData.append("idschema", schemaId);
|
||||
formData.append("schemaname", schemaName);
|
||||
|
||||
// Aggiungi idroutine
|
||||
const routineId = routineSelect.value;
|
||||
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", {
|
||||
method: "POST",
|
||||
body: formData
|
||||
@@ -402,8 +734,8 @@ $routines = $stmt->fetchAll(PDO::FETCH_ASSOC);
|
||||
.then(data => {
|
||||
if (data.success) {
|
||||
Swal.fire({
|
||||
title: "Successo!",
|
||||
text: "Template aggiornato con successo!",
|
||||
title: "Success!",
|
||||
text: "Template updated successfully!",
|
||||
icon: "success",
|
||||
confirmButtonText: "OK"
|
||||
}).then(() => {
|
||||
@@ -411,17 +743,17 @@ $routines = $stmt->fetchAll(PDO::FETCH_ASSOC);
|
||||
});
|
||||
} else {
|
||||
Swal.fire({
|
||||
title: "Errore!",
|
||||
title: "Error!",
|
||||
text: data.message,
|
||||
icon: "error",
|
||||
confirmButtonText: "OK"
|
||||
});
|
||||
}
|
||||
})
|
||||
.catch(error => {
|
||||
.catch(() => {
|
||||
Swal.fire({
|
||||
title: "Errore!",
|
||||
text: "Si è verificato un errore imprevisto.",
|
||||
title: "Error!",
|
||||
text: "An unexpected error occurred.",
|
||||
icon: "error",
|
||||
confirmButtonText: "OK"
|
||||
});
|
||||
|
||||
@@ -312,3 +312,22 @@
|
||||
|
||||
2026-02-25 15:23:12 [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-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 });
|
||||
})();
|
||||
}
|
||||
})();
|
||||
+774
-213
File diff suppressed because it is too large
Load Diff
@@ -13,9 +13,31 @@ if (!is_dir($logDir)) {
|
||||
mkdir($logDir, 0755, true);
|
||||
}
|
||||
|
||||
$uploadDir = realpath(__DIR__ . '/../photostrf') . '/';
|
||||
|
||||
// 🔹 Base URL API
|
||||
$apiBaseUrl = 'https://93.43.5.102/limsapi/api/odata/';
|
||||
|
||||
// 🔹 Batch UUID — if present, all logs go to a single file
|
||||
$batchUuid = $_POST['batch_uuid'] ?? null;
|
||||
|
||||
$writeLog = (function () use ($batchUuid, $logDir) {
|
||||
$batchLogFile = $batchUuid ? $logDir . "batch_export_{$batchUuid}.log" : null;
|
||||
|
||||
return function ($individualPath, $content, $stepLabel = null) use ($batchLogFile) {
|
||||
if ($batchLogFile) {
|
||||
$header = "\n" . str_repeat("=", 60) . "\n";
|
||||
if ($stepLabel) {
|
||||
$header .= "[{$stepLabel}] " . date('Y-m-d H:i:s') . "\n";
|
||||
}
|
||||
$header .= str_repeat("=", 60) . "\n";
|
||||
file_put_contents($batchLogFile, $header . $content . "\n", FILE_APPEND);
|
||||
} else {
|
||||
file_put_contents($individualPath, $content);
|
||||
}
|
||||
};
|
||||
})();
|
||||
|
||||
// 🔹 Funzione per validare e convertire date
|
||||
function validateDate($value)
|
||||
{
|
||||
@@ -27,15 +49,81 @@ function validateDate($value)
|
||||
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
|
||||
}
|
||||
|
||||
// ImportaCommessa con retry: la chiamata è asincrona lato LIMS e a volte
|
||||
// risponde 200 senza importare (StatoCommessaWeb resta "Inviata"/"Nuova").
|
||||
// Riprova con backoff esponenziale finché non passa a "Elaborata".
|
||||
function importaCommessaWithRetry($api, $commessaId, array $payload, $maxRetries = 3, $initialBackoff = 1)
|
||||
{
|
||||
$result = null;
|
||||
$stato = null;
|
||||
$succeeded = false;
|
||||
$log = "";
|
||||
$backoff = $initialBackoff;
|
||||
|
||||
set_time_limit(120); // i backoff non devono far scadere il timeout della richiesta
|
||||
|
||||
for ($attempt = 1; $attempt <= $maxRetries + 1; $attempt++) {
|
||||
try {
|
||||
$result = $api->post("CommessaWeb({$commessaId})/ImportaCommessa", $payload);
|
||||
$stato = $result['StatoCommessaWeb'] ?? null;
|
||||
$log .= "Attempt {$attempt}: HTTP 200, StatoCommessaWeb=" . ($stato ?? 'null') . "\n";
|
||||
} catch (Exception $e) {
|
||||
$stato = null;
|
||||
$log .= "Attempt {$attempt}: EXCEPTION " . $e->getMessage() . "\n";
|
||||
}
|
||||
|
||||
if ($stato === 'Elaborata') {
|
||||
$succeeded = true;
|
||||
break;
|
||||
}
|
||||
|
||||
if ($attempt <= $maxRetries) {
|
||||
$log .= " -> not Elaborata, waiting {$backoff}s before retry\n";
|
||||
sleep($backoff);
|
||||
$backoff *= 2;
|
||||
}
|
||||
}
|
||||
|
||||
return [
|
||||
'succeeded' => $succeeded,
|
||||
'stato' => $stato,
|
||||
'result' => $result,
|
||||
'log' => $log,
|
||||
];
|
||||
}
|
||||
|
||||
try {
|
||||
$iddatadb = $_POST['iddatadb'] ?? null;
|
||||
if (!$iddatadb) {
|
||||
throw new Exception("Missing iddatadb");
|
||||
}
|
||||
|
||||
// TEMP: simulate error on every other row for testing
|
||||
if (env('SIMULATE_EXPORT_LIMS') && $iddatadb % 2 === 0) {
|
||||
throw new Exception("Simulated error for iddatadb $iddatadb");
|
||||
}
|
||||
|
||||
// 🔹 STEP 1+2: Fetch Cliente ID from datadb and Schema ID from excel_templates
|
||||
// Also fetch fixed fields stored in datadb
|
||||
$stmt = $pdo->prepare("
|
||||
SELECT d.idclient AS clienteId, et.idschema AS schemaId
|
||||
SELECT d.idclient AS clienteId, et.idschema AS schemaId,
|
||||
d.cliente_responsabile_id,
|
||||
d.moltiplicatore_prezzo_id,
|
||||
d.anagrafica_certest_object_id,
|
||||
d.anagrafica_certest_service_id,
|
||||
d.cliente_fornitore_id,
|
||||
d.clienteAnalisi,
|
||||
d.consegna_richiesta
|
||||
FROM datadb as d
|
||||
INNER JOIN excel_templates as et ON d.templateid = et.id
|
||||
WHERE d.iddatadb = :iddatadb
|
||||
@@ -51,15 +139,60 @@ try {
|
||||
$clienteId = (int) $result['clienteId'];
|
||||
$schemaId = (int) $result['schemaId'];
|
||||
|
||||
// 🔹 STEP 3: Fetch Parts (including idmatrice)
|
||||
// Extract fixed fields (nullable INTs)
|
||||
$clienteResponsabile = !empty($result['cliente_responsabile_id']) ? (int) $result['cliente_responsabile_id'] : null;
|
||||
$moltiplicatorePrezzo = !empty($result['moltiplicatore_prezzo_id']) ? (int) $result['moltiplicatore_prezzo_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;
|
||||
$clienteFornitore = !empty($result['cliente_fornitore_id']) ? (int) $result['cliente_fornitore_id'] : null;
|
||||
$clienteAnalisi = !empty($result['clienteAnalisi']) ? (int) $result['clienteAnalisi'] : null;
|
||||
$consegnaRichiesta = !empty($result['consegna_richiesta']) ? $result['consegna_richiesta'] : null;
|
||||
|
||||
// 🔹 STEP 3: Fetch Parts (including idmatrice and part id for custom fields)
|
||||
$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
|
||||
WHERE iddatadb = :iddatadb
|
||||
");
|
||||
AND part_description IS NOT NULL
|
||||
AND TRIM(part_description) <> ''
|
||||
ORDER BY CAST(part_number AS UNSIGNED) ASC, part_number ASC
|
||||
");
|
||||
$stmt->execute(['iddatadb' => $iddatadb]);
|
||||
$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
|
||||
$stmt = $pdo->prepare("
|
||||
UPDATE import_data_details idd
|
||||
JOIN template_mapping m ON idd.mapping_id = m.id
|
||||
SET idd.field_value = CASE m.auto_value
|
||||
WHEN 'export_date' THEN :export_date
|
||||
WHEN 'export_time' THEN :export_time
|
||||
END
|
||||
WHERE idd.id = :iddatadb
|
||||
AND m.auto_value IN ('export_date', 'export_time')
|
||||
");
|
||||
$stmt->execute([
|
||||
'iddatadb' => $iddatadb,
|
||||
'export_date' => date('Y-m-d'),
|
||||
'export_time' => date('H:i'),
|
||||
]);
|
||||
|
||||
// 🔹 STEP 4: Fetch Field Values with Labels
|
||||
$stmt = $pdo->prepare("
|
||||
SELECT
|
||||
@@ -93,12 +226,21 @@ try {
|
||||
// 🔹 Initialize API client
|
||||
$api = VisualLimsApiClient::getInstance();
|
||||
|
||||
// 🔹 STEP 5: Create CommessaWeb (NOT WebOrder)
|
||||
// 🔹 STEP 5: Create CommessaWeb
|
||||
// Fixed fields are sent as direct properties — they are navigation properties on Commessa
|
||||
// accepted by the XAF API even though not explicitly listed in OData $metadata
|
||||
$commessaWebPayload = [
|
||||
"Cliente" => $clienteId,
|
||||
"SchemaCustomField" => $schemaId,
|
||||
"Richiedente" => "Test Web Import",
|
||||
"Descrizione" => "TEST CommessaWeb",
|
||||
"Cliente" => $clienteId,
|
||||
"SchemaCustomField" => $schemaId,
|
||||
"Richiedente" => "From TRFSmart Application", // TODO: replace with real value
|
||||
"Descrizione" => "From TRFSmart Application", // TODO: replace with real value
|
||||
"ClienteResponsabile" => $clienteResponsabile,
|
||||
"MoltiplicatorePrezzo" => $moltiplicatorePrezzo,
|
||||
"AnagraficaCertestObject" => $anagraficaObject,
|
||||
"AnagraficaCertestService" => $anagraficaService,
|
||||
"ClienteFornitore" => $clienteFornitore, // PLACEHOLDER — to be implemented
|
||||
"ClienteAnalisi" => $clienteAnalisi, // PLACEHOLDER — to be implemented
|
||||
// DeliveryRequest goes to Campione, not CommessaWeb
|
||||
];
|
||||
|
||||
// Costruisci log curl-like per STEP 5
|
||||
@@ -114,7 +256,7 @@ try {
|
||||
|
||||
// Salva log
|
||||
$logFileStep5 = $logDir . "commessa_create_step5_" . $iddatadb . "_" . time() . ".txt";
|
||||
file_put_contents($logFileStep5, $logContentStep5);
|
||||
$writeLog($logFileStep5, $logContentStep5, "STEP 5 - Create CommessaWeb (iddatadb={$iddatadb})");
|
||||
|
||||
$commessaId = $commessaWeb["IdCommessa"];
|
||||
$commessaWebCode = substr($commessaWeb["CodiceCommessa"] ?? "TEST CommessaWeb", 0, 30); // Limite a 30 caratteri
|
||||
@@ -131,11 +273,13 @@ try {
|
||||
}
|
||||
|
||||
$campionePayload = [
|
||||
"Commessa" => $commessaId,
|
||||
"Matrice" => $matriceId,
|
||||
"SottoMatrice" => null,
|
||||
"Commessa" => $commessaId,
|
||||
"Matrice" => $matriceId,
|
||||
"SottoMatrice" => null,
|
||||
"SchemaCustomField" => $schemaId,
|
||||
"NoteWeb" => $part["part_description"] ?? ""
|
||||
// "Riferimento" => $part["part_description"] ?? "",
|
||||
"NoteWeb" => $part["part_description"] ?? "",
|
||||
"ConsegnaRichiesta" => !empty($part["dateexpiry"]) ? $part["dateexpiry"] : $consegnaRichiesta,
|
||||
];
|
||||
|
||||
// Costruisci curl-like per questo campione
|
||||
@@ -160,20 +304,256 @@ try {
|
||||
|
||||
// Salva log per STEP 6
|
||||
$logFileStep6 = $logDir . "commessa_{$commessaId}_campioni_step6_" . time() . ".txt";
|
||||
file_put_contents($logFileStep6, $logContentStep6);
|
||||
$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
|
||||
$stmtPhotos = $pdo->prepare("
|
||||
SELECT id, file_path, file_name, StampaNelRapporto, PrimaPagina
|
||||
FROM datadb_photos
|
||||
WHERE iddatadb = :iddatadb
|
||||
ORDER BY id ASC
|
||||
");
|
||||
$stmtPhotos->execute(['iddatadb' => $iddatadb]);
|
||||
$photos = $stmtPhotos->fetchAll(PDO::FETCH_ASSOC);
|
||||
|
||||
|
||||
// 🔹 STEP 6.2: Upload photos to Campione .01 (fetched from API)
|
||||
$photosUploaded = 0;
|
||||
$logContentPhotos = "Photos for CommessaWeb {$commessaId} (iddatadb={$iddatadb}):\n";
|
||||
$logContentPhotos .= "Total photos found: " . count($photos) . ", campioni: " . count($campioni) . "\n\n";
|
||||
|
||||
if (!empty($campioni) && !empty($photos)) {
|
||||
// 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);
|
||||
|
||||
$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) {
|
||||
$logContentPhotos .= "=== Campione {$campioneId} (main) ===\n";
|
||||
|
||||
foreach ($photos as $photo) {
|
||||
$photoPath = $uploadDir . '/' . ltrim($photo['file_path'], './');
|
||||
$fullPath = realpath($photoPath);
|
||||
|
||||
if (!$fullPath || !file_exists($fullPath)) {
|
||||
$logContentPhotos .= "SKIP (file not found): {$photoPath}\n";
|
||||
continue;
|
||||
}
|
||||
|
||||
$photoEndpoint = "Campione({$campioneId})/UploadCampioneFile";
|
||||
$stampaNelRapporto = !empty($photo['StampaNelRapporto']);
|
||||
$primaPagina = !empty($photo['PrimaPagina']);
|
||||
|
||||
$logContentPhotos .= "curl --location --request POST '{$apiBaseUrl}{$photoEndpoint}' \\\n" .
|
||||
"--header 'Authorization: Bearer ••••••' \\\n" .
|
||||
"--form 'file=@{$fullPath}'\n\n";
|
||||
|
||||
// Step 1: Upload file (flags are ignored by API during upload)
|
||||
$photoResult = $api->postMultipart($photoEndpoint, $fullPath, $photo['file_name']);
|
||||
$logContentPhotos .= "UPLOAD RESPONSE:\n" . json_encode($photoResult, JSON_PRETTY_PRINT) . "\n\n";
|
||||
|
||||
// Step 2: PATCH CampioneFile to set flags (StampaNelRapporto, PrimaPagina)
|
||||
$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++;
|
||||
}
|
||||
} else {
|
||||
$logContentPhotos .= "SKIP: main campione has invalid IdCampione\n";
|
||||
}
|
||||
} elseif (empty($campioni)) {
|
||||
$logContentPhotos .= "SKIP: no campioni created, cannot upload photos\n";
|
||||
}
|
||||
|
||||
$logFilePhotos = $logDir . "commessa_{$commessaId}_photos_step5_2_" . time() . ".txt";
|
||||
$writeLog($logFilePhotos, $logContentPhotos, "STEP 6.2 - Photos (commessa={$commessaId})");
|
||||
|
||||
// 🔹 STEP 6.3: Add Analyses (AnalisiCampione) via Campione({id})/AddAnalisi bound action
|
||||
$stmt = $pdo->prepare("
|
||||
SELECT part_id, analysis_recordkey, analysis_name, analysis_method
|
||||
FROM identification_parts_analyses
|
||||
WHERE iddatadb = :iddatadb
|
||||
ORDER BY part_id, id
|
||||
");
|
||||
$stmt->execute(['iddatadb' => $iddatadb]);
|
||||
$analysesRows = $stmt->fetchAll(PDO::FETCH_ASSOC);
|
||||
|
||||
$partIdToIndex = [];
|
||||
foreach ($parts as $idx => $part) {
|
||||
$partIdToIndex[(int)$part['part_id']] = $idx;
|
||||
}
|
||||
|
||||
$totalAnalyses = count($analysesRows);
|
||||
$addedAnalyses = 0;
|
||||
$failedAnalyses = [];
|
||||
$logContentStep63Analisi = "Analyses for iddatadb={$iddatadb}: total={$totalAnalyses}\n\n";
|
||||
|
||||
foreach ($analysesRows as $a) {
|
||||
$partId = (int)$a['part_id'];
|
||||
$recordKey = trim((string)($a['analysis_recordkey'] ?? ''));
|
||||
$idx = $partIdToIndex[$partId] ?? null;
|
||||
|
||||
if ($idx === null || !isset($campioni[$idx]) || $recordKey === '') {
|
||||
$logContentStep63Analisi .= "SKIP (no campione for part_id={$partId} / empty recordkey): '{$recordKey}'\n";
|
||||
continue;
|
||||
}
|
||||
|
||||
$campioneId = (int)($campioni[$idx]['IdCampione'] ?? 0);
|
||||
if ($campioneId <= 0) {
|
||||
$logContentStep63Analisi .= "SKIP (invalid IdCampione for part_id={$partId}): '{$recordKey}'\n";
|
||||
continue;
|
||||
}
|
||||
|
||||
$payload = ['RecordKey' => $recordKey];
|
||||
$jsonPayload = json_encode($payload, JSON_UNESCAPED_SLASHES);
|
||||
|
||||
$logContentStep63Analisi .= "curl --location --request POST '{$apiBaseUrl}Campione({$campioneId})/AddAnalisi' \\\n" .
|
||||
"--header 'Content-Type: application/json' \\\n" .
|
||||
"--header 'Authorization: Bearer ••••••' \\\n" .
|
||||
"--data '{$jsonPayload}'\n";
|
||||
|
||||
try {
|
||||
$result = $api->post("Campione({$campioneId})/AddAnalisi", $payload);
|
||||
$logContentStep63Analisi .= "OK (part_id={$partId}, campione={$campioneId}): " .
|
||||
($a['analysis_name'] ?? '') . "\n---\n";
|
||||
$addedAnalyses++;
|
||||
} catch (Exception $e) {
|
||||
$errMsg = $e->getMessage();
|
||||
$logContentStep63Analisi .= "FAIL: {$errMsg}\n---\n";
|
||||
$failedAnalyses[] = [
|
||||
'part_id' => $partId,
|
||||
'campione_id' => $campioneId,
|
||||
'analysis_recordkey' => $recordKey,
|
||||
'analysis_name' => $a['analysis_name'] ?? '',
|
||||
'error' => $errMsg,
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
$logFileStep63Analisi = $logDir . "commessa_{$commessaId}_analyses_step63_" . time() . ".txt";
|
||||
$writeLog($logFileStep63Analisi, $logContentStep63Analisi, "STEP 6.3 - AddAnalisi (commessa={$commessaId})");
|
||||
|
||||
// 🔹 STEP 7: Update Custom Fields for CommessaWeb
|
||||
if (!empty($fieldValues)) {
|
||||
// GET con espansione per CustomField
|
||||
$expand = "CommesseCustomFields(\$expand=CustomField)";
|
||||
$commessaWithFields = $api->get("CommessaWeb(" . $commessaId . ")?\$expand=" . $expand);
|
||||
$commessaWithFields = $api->get("CommessaWeb({$commessaId})?\$expand={$expand}");
|
||||
|
||||
// Logga il GET
|
||||
$logContentGet = "curl --location --request GET '{$apiBaseUrl}CommessaWeb({$commessaId})?\$expand=CommesseCustomFields(\$expand=CustomField)' \\\n" .
|
||||
"--header 'Authorization: Bearer ••••••'\n\n" .
|
||||
"RESPONSE:\n" . json_encode($commessaWithFields, JSON_PRETTY_PRINT);
|
||||
$logFileGet = $logDir . "commessa_{$commessaId}_get_step7_" . time() . ".txt";
|
||||
file_put_contents($logFileGet, $logContentGet);
|
||||
$writeLog($logFileGet, $logContentGet, "STEP 7 - GET CustomFields (commessa={$commessaId})");
|
||||
|
||||
// Prepara payload PATCH
|
||||
$commessaCustomFields = [];
|
||||
@@ -186,7 +566,7 @@ try {
|
||||
|
||||
// Valida se il campo è di tipo Data
|
||||
if ($fieldType === 'Data' && $newValue !== $currentValue) {
|
||||
$newValue = validateDate($newValue);
|
||||
$newValue = formatDateToExport($newValue);
|
||||
}
|
||||
|
||||
$commessaCustomFields[] = [
|
||||
@@ -195,6 +575,7 @@ try {
|
||||
];
|
||||
}
|
||||
|
||||
$logFileStep7 = null;
|
||||
if (!empty($commessaCustomFields)) {
|
||||
$updatePayload = ["CommesseCustomFields" => $commessaCustomFields];
|
||||
|
||||
@@ -209,7 +590,7 @@ try {
|
||||
|
||||
$logContentStep7 .= "\n\nRESPONSE:\n" . json_encode($patchResponse, JSON_PRETTY_PRINT);
|
||||
$logFileStep7 = $logDir . "commessa_{$commessaId}_update_step7_" . time() . ".txt";
|
||||
file_put_contents($logFileStep7, $logContentStep7);
|
||||
$writeLog($logFileStep7, $logContentStep7, "STEP 7 - PATCH CustomFields (commessa={$commessaId})");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -226,18 +607,45 @@ try {
|
||||
]);
|
||||
|
||||
// 🔹 STEP 9: Send CommessaWeb to laboratory (commentato come richiesto)
|
||||
/*
|
||||
|
||||
$sendResult = $api->post("CommessaWeb({$commessaId})/InviaCommessa", []);
|
||||
|
||||
// Logga il POST
|
||||
$logContentStep9 = "curl --location --request POST '{$apiBaseUrl}CommessaWeb({$commessaId})/InviaCommessa' \\\n" .
|
||||
"--header 'Content-Type: application/json' \\\n" .
|
||||
"--header 'Authorization: Bearer ••••••' \\\n" .
|
||||
"--data '{}'\n\n" .
|
||||
"RESPONSE:\n" . json_encode($sendResult, JSON_PRETTY_PRINT);
|
||||
"--header 'Content-Type: application/json' \\\n" .
|
||||
"--header 'Authorization: Bearer ••••••' \\\n" .
|
||||
"--data '{}'\n\n" .
|
||||
"RESPONSE:\n" . json_encode($sendResult, JSON_PRETTY_PRINT);
|
||||
$logFileStep9 = $logDir . "commessa_{$commessaId}_send_step9_" . time() . ".txt";
|
||||
file_put_contents($logFileStep9, $logContentStep9);
|
||||
*/
|
||||
$writeLog($logFileStep9, $logContentStep9, "STEP 9 - InviaCommessa (commessa={$commessaId})");
|
||||
|
||||
|
||||
// 🔹 STEP 9.5: Importazione da CommessaWeb a Commessa (con retry)
|
||||
// Supplier call: POST api/odata/CommessaWeb(XXX)/ImportaCommessa
|
||||
$importUserId = (!empty($lims_global_user_id) && is_numeric($lims_global_user_id))
|
||||
? (int) $lims_global_user_id
|
||||
: 285;
|
||||
|
||||
$importPayload = [
|
||||
"IdUtente" => $importUserId
|
||||
];
|
||||
$importPayloadLog = json_encode($importPayload, JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES);
|
||||
|
||||
$importOutcome = importaCommessaWithRetry($api, $commessaId, $importPayload);
|
||||
$importResult = $importOutcome['result'];
|
||||
$importStato = $importOutcome['stato'];
|
||||
$importSucceeded = $importOutcome['succeeded'];
|
||||
|
||||
// Logga il POST (tutti i tentativi)
|
||||
$logContentStep91 = "curl --location --request POST '{$apiBaseUrl}CommessaWeb({$commessaId})/ImportaCommessa' \\\n" .
|
||||
"--header 'Content-Type: application/json' \\\n" .
|
||||
"--header 'Authorization: Bearer ••••••' \\\n" .
|
||||
"--data '{$importPayloadLog}'\n\n" .
|
||||
"ATTEMPTS:\n" . $importOutcome['log'] . "\n" .
|
||||
"SUCCEEDED: " . ($importSucceeded ? 'yes' : 'NO') . "\n\n" .
|
||||
"LAST RESPONSE:\n" . json_encode($importResult, JSON_PRETTY_PRINT);
|
||||
$logFileStep91 = $logDir . "commessa_{$commessaId}_importa_step91_" . time() . ".txt";
|
||||
$writeLog($logFileStep91, $logContentStep91, "STEP 9.5 - ImportaCommessa (commessa={$commessaId}, succeeded=" . ($importSucceeded ? '1' : '0') . ")");
|
||||
|
||||
// 🔹 STEP 10: GET di controllo post-PATCH
|
||||
$expand = "CommesseCustomFields(\$expand=CustomField)";
|
||||
@@ -248,8 +656,24 @@ try {
|
||||
"--header 'Authorization: Bearer ••••••'\n\n" .
|
||||
"RESPONSE:\n" . json_encode($commessaAfterPatch, JSON_PRETTY_PRINT);
|
||||
$logFileStep10 = $logDir . "commessa_{$commessaId}_get_step10_" . time() . ".txt";
|
||||
file_put_contents($logFileStep10, $logContentStep10);
|
||||
$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
|
||||
$finalCommessa = [
|
||||
"Cliente" => $clienteId,
|
||||
@@ -262,17 +686,26 @@ try {
|
||||
];
|
||||
|
||||
echo json_encode([
|
||||
"success" => true,
|
||||
"commessaWeb" => $finalCommessa,
|
||||
"success" => true,
|
||||
"idcommessaweb" => $commessaId,
|
||||
"commessaweb" => $finalCodiceCommessa ?: $commessaWebCode,
|
||||
"commessaWeb" => $finalCommessa,
|
||||
"commessaWebApiResponse" => $commessaWeb, // Incluso per debug
|
||||
"totalCampioni" => count($campioni),
|
||||
"totalCustomFields" => count($commessaAfterPatch["CommesseCustomFields"] ?? []),
|
||||
"message" => "Export successful",
|
||||
"logFiles" => [
|
||||
"step5_create" => $logFileStep5,
|
||||
"step6_campioni" => $logFileStep6,
|
||||
"step7_patch" => $logFileStep7,
|
||||
"step10_get" => $logFileStep10
|
||||
"totalCampioni" => count($campioni),
|
||||
"totalCustomFields" => count($commessaAfterPatch["CommesseCustomFields"] ?? []),
|
||||
"totalPhotos" => count($photos),
|
||||
"totalAnalyses" => $totalAnalyses,
|
||||
"addedAnalyses" => $addedAnalyses,
|
||||
"failedAnalyses" => $failedAnalyses,
|
||||
"message" => "Export successful",
|
||||
"logFiles" => [
|
||||
"step5_create" => $logFileStep5,
|
||||
"step5_2_photos" => $logFilePhotos,
|
||||
"step6_campioni" => $logFileStep6,
|
||||
"step63_analyses" => $logFileStep63Analisi,
|
||||
"step7_patch" => $logFileStep7 ?? null,
|
||||
"step9_1_importa" => $logFileStep91,
|
||||
"step10_get" => $logFileStep10
|
||||
]
|
||||
]);
|
||||
} catch (Exception $e) {
|
||||
@@ -282,10 +715,13 @@ try {
|
||||
"success" => false,
|
||||
"message" => "Export failed: " . $e->getMessage(),
|
||||
"logFiles" => [
|
||||
"step5_create" => $logFileStep5 ?? null,
|
||||
"step6_campioni" => $logFileStep6 ?? null,
|
||||
"step7_patch" => $logFileStep7 ?? null,
|
||||
"step10_get" => $logFileStep10 ?? null
|
||||
"step5_create" => $logFileStep5 ?? null,
|
||||
"step5_2_photos" => $logFilePhotos ?? null,
|
||||
"step6_campioni" => $logFileStep6 ?? null,
|
||||
"step63_analyses" => $logFileStep63Analisi ?? null,
|
||||
"step7_patch" => $logFileStep7 ?? null,
|
||||
"step9_1_importa" => $logFileStep91 ?? null,
|
||||
"step10_get" => $logFileStep10 ?? null
|
||||
]
|
||||
]);
|
||||
}
|
||||
|
||||
@@ -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,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 {
|
||||
$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();
|
||||
|
||||
$webOnly = isset($_GET['web_only']) ? (int)$_GET['web_only'] : 1;
|
||||
|
||||
$filterString = "Matrice/IdMatrice eq $idMatrice";
|
||||
|
||||
if ($webOnly === 1) {
|
||||
$filterString .= " and SelezionabileSuWeb eq true";
|
||||
}
|
||||
|
||||
$filter = rawurlencode($filterString);
|
||||
$endpoint = "Analisi?\$filter={$filter}";
|
||||
|
||||
$base_url = 'https://93.43.5.102/limsapi/api/odata/';
|
||||
$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);
|
||||
}
|
||||
@@ -9,7 +9,24 @@ ini_set('display_errors', '0');
|
||||
error_reporting(E_ALL);
|
||||
|
||||
try {
|
||||
$api = VisualLimsApiClient::getInstance();
|
||||
$api = VisualLimsApiClient::getInstance(); // also loads dotenv
|
||||
|
||||
// In simulate mode: return fake clients built from idclient values already in datadb.
|
||||
// This ensures client dropdowns auto-select the correct row idclient, which in turn
|
||||
// triggers the ClienteResponsabile select to populate via the mock.
|
||||
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]);
|
||||
exit;
|
||||
}
|
||||
|
||||
// Parametri OData
|
||||
$params = [
|
||||
@@ -57,10 +74,21 @@ try {
|
||||
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
|
||||
$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) {
|
||||
http_response_code(500);
|
||||
$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,17 +21,26 @@ try {
|
||||
}
|
||||
|
||||
$results = [];
|
||||
$cacheDir = __DIR__ . '/cache';
|
||||
if (!is_dir($cacheDir)) mkdir($cacheDir, 0777, true);
|
||||
|
||||
foreach ($fieldIds as $customFieldId) {
|
||||
$cacheFile = $cacheDir . '/customfield_' . $customFieldId . '.json';
|
||||
|
||||
// Use cache if fresh (1 hour)
|
||||
if (file_exists($cacheFile) && (time() - filemtime($cacheFile) < 3600)) {
|
||||
$results[$customFieldId] = json_decode(file_get_contents($cacheFile), true);
|
||||
continue;
|
||||
}
|
||||
|
||||
$endpoint = "CustomField($customFieldId)?\$expand=CustomFieldsValues";
|
||||
$data = $api->get($endpoint);
|
||||
$values = $data['CustomFieldsValues'] ?? [];
|
||||
$results[$customFieldId] = $values;
|
||||
|
||||
$results[$customFieldId] = $data['CustomFieldsValues'] ?? [];
|
||||
file_put_contents($cacheFile, json_encode($values));
|
||||
}
|
||||
|
||||
// Debug ფაილი
|
||||
file_put_contents(__DIR__ . '/customfield_values_response.json', json_encode($results));
|
||||
|
||||
echo json_encode($results);
|
||||
} catch (Exception $e) {
|
||||
http_response_code(500);
|
||||
|
||||
@@ -16,7 +16,8 @@ if (!$field) {
|
||||
exit;
|
||||
}
|
||||
|
||||
$api = VisualLimsApiClient::getInstance();
|
||||
$api = VisualLimsApiClient::getInstance(); // also loads dotenv as a side-effect
|
||||
$simulate = ($_ENV['SIMULATE_EXPORT_LIMS'] ?? '') === 'true';
|
||||
$base_url = 'https://93.43.5.102/limsapi/api/odata/';
|
||||
|
||||
$data = null;
|
||||
@@ -44,9 +45,12 @@ try {
|
||||
case 'ClienteResponsabile':
|
||||
$id_cliente = (int)($_GET['id_cliente'] ?? 0);
|
||||
if ($id_cliente <= 0) {
|
||||
http_response_code(400);
|
||||
echo json_encode(['error' => 'Manca o invalido id_cliente']);
|
||||
exit;
|
||||
if (!$simulate) {
|
||||
http_response_code(400);
|
||||
echo json_encode(['error' => 'Manca o invalido id_cliente']);
|
||||
exit;
|
||||
}
|
||||
$id_cliente = 1; // dummy — mock ignores the actual ID
|
||||
}
|
||||
$endpoint = "Cliente($id_cliente)?\$expand=Responsabili";
|
||||
$cache_file = __DIR__ . '/cache/cliente_responsabili_' . $id_cliente . '.json';
|
||||
@@ -62,13 +66,13 @@ try {
|
||||
exit;
|
||||
}
|
||||
|
||||
// Opzionale: caching semplice (molto utile se i dati cambiano poco)
|
||||
if ($cache_file && file_exists($cache_file) && (time() - filemtime($cache_file) < 3600)) { // 1 ora
|
||||
// Caching skipped in simulate mode to avoid polluting real-data cache files
|
||||
if (!$simulate && $cache_file && file_exists($cache_file) && (time() - filemtime($cache_file) < 3600)) { // 1 ora
|
||||
$data = json_decode(file_get_contents($cache_file), true);
|
||||
} else {
|
||||
$data = $api->get($endpoint, $options);
|
||||
|
||||
if ($cache_file) {
|
||||
if (!$simulate && $cache_file) {
|
||||
file_put_contents($cache_file, json_encode($data, JSON_PRETTY_PRINT));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,63 @@
|
||||
<?php
|
||||
require_once 'include/headscript.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(['error' => 'Invalid request method']);
|
||||
exit;
|
||||
}
|
||||
|
||||
$input = json_decode(file_get_contents('php://input'), true);
|
||||
$template_id = isset($input['template_id']) ? (int)$input['template_id'] : 0;
|
||||
$code = isset($input['code']) ? trim($input['code']) : '';
|
||||
|
||||
if ($template_id <= 0) {
|
||||
http_response_code(400);
|
||||
echo json_encode(['error' => 'Missing or invalid template_id']);
|
||||
exit;
|
||||
}
|
||||
|
||||
if ($code === '') {
|
||||
http_response_code(400);
|
||||
echo json_encode(['error' => 'Missing code']);
|
||||
exit;
|
||||
}
|
||||
|
||||
/*
|
||||
* TODO: Replace this block with your real API call.
|
||||
* Expected response for import_json.php:
|
||||
* {
|
||||
* "success": true,
|
||||
* "reference": "CODE123",
|
||||
* "json": { ... real JSON payload ... }
|
||||
* }
|
||||
*/
|
||||
|
||||
$sampleJson = [
|
||||
'data' => [[
|
||||
'type' => 'trf_data_request',
|
||||
'id' => $code,
|
||||
'attributes' => [
|
||||
'trf_type' => 'apparel',
|
||||
'service_required' => 'regular',
|
||||
'submitter_information' => [
|
||||
'submitter_type' => 'supplier'
|
||||
]
|
||||
]
|
||||
]]
|
||||
];
|
||||
|
||||
echo json_encode([
|
||||
'success' => true,
|
||||
'reference' => $code,
|
||||
'json' => $sampleJson
|
||||
]);
|
||||
} catch (Throwable $e) {
|
||||
http_response_code(500);
|
||||
echo json_encode(['error' => $e->getMessage()]);
|
||||
}
|
||||
@@ -1,59 +1,46 @@
|
||||
<?php
|
||||
require_once dirname(__DIR__, 2) . '/vendor/autoload.php'; // Risale a root/vendor/
|
||||
use Dotenv\Dotenv;
|
||||
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; }
|
||||
|
||||
// Set JSON header
|
||||
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 {
|
||||
$dotenv = Dotenv::createImmutable($envPath);
|
||||
$dotenv->load();
|
||||
} catch (Exception $e) {
|
||||
file_put_contents(__DIR__ . '/error_log.txt', date('Y-m-d H:i:s') . ' - Errore caricamento .env: ' . $e->getMessage() . PHP_EOL, FILE_APPEND);
|
||||
echo json_encode(['success' => false, 'message' => 'Errore caricamento configurazione: ' . $e->getMessage()]);
|
||||
exit(1);
|
||||
}
|
||||
// Read from matrici cache (populated by get_matrici.php / warm_cache.php)
|
||||
$cacheFile = __DIR__ . '/cache/matrici.json';
|
||||
if (file_exists($cacheFile)) {
|
||||
$data = json_decode(file_get_contents($cacheFile), true);
|
||||
$matrici = $data['value'] ?? [];
|
||||
} 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
|
||||
$dbHost = $_ENV['DB_HOST'];
|
||||
$dbName = $_ENV['DB_DATABASE'];
|
||||
$dbUser = $_ENV['DB_USERNAME'];
|
||||
$dbPass = $_ENV['DB_PASSWORD'];
|
||||
$dbPrefix = $_ENV['DB_PREFIX'];
|
||||
// Extract unique MacroMatrice values
|
||||
$macroValues = [];
|
||||
foreach ($matrici as $m) {
|
||||
$macro = $m['MacroMatrice'] ?? null;
|
||||
if ($macro !== null && $macro !== '' && substr($macro, 0, 1) !== '*') {
|
||||
$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]);
|
||||
} catch (PDOException $e) {
|
||||
// Log errore e restituisci risposta di errore
|
||||
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' => 'Errore nel recupero delle MacroMatrice: ' . $e->getMessage()]);
|
||||
exit(1);
|
||||
} catch (Exception $e) {
|
||||
http_response_code(500);
|
||||
echo json_encode(['success' => false, 'message' => $e->getMessage()]);
|
||||
}
|
||||
|
||||
@@ -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,38 @@
|
||||
<?php
|
||||
require_once __DIR__ . '/include/headscript.php'; // o il tuo bootstrap standard
|
||||
|
||||
header('Content-Type: application/json');
|
||||
|
||||
try {
|
||||
if (!isset($_GET['iddatadb']) || !is_numeric($_GET['iddatadb'])) {
|
||||
echo json_encode(['success' => true, 'field' => null]);
|
||||
exit;
|
||||
}
|
||||
|
||||
$iddatadb = (int)$_GET['iddatadb'];
|
||||
|
||||
$db = DBHandlerSelect::getInstance();
|
||||
$pdo = $db->getConnection();
|
||||
|
||||
// 1) prendo templateid da datadb
|
||||
// 2) cerco max 1 record in template_mapping con is_visible_parts=1
|
||||
$sql = "
|
||||
SELECT tm.field_id, tm.field_label, tm.data_type, tm.has_list
|
||||
FROM datadb d
|
||||
JOIN template_mapping tm ON tm.template_id = d.templateid
|
||||
WHERE d.iddatadb = ?
|
||||
AND tm.is_visible_parts = 1
|
||||
ORDER BY tm.id ASC
|
||||
LIMIT 1
|
||||
";
|
||||
$stmt = $pdo->prepare($sql);
|
||||
$stmt->execute([$iddatadb]);
|
||||
$row = $stmt->fetch(PDO::FETCH_ASSOC);
|
||||
|
||||
echo json_encode([
|
||||
'success' => true,
|
||||
'field' => $row ? $row : null
|
||||
]);
|
||||
} catch (Throwable $e) {
|
||||
echo json_encode(['success' => false, 'message' => $e->getMessage()]);
|
||||
}
|
||||
@@ -0,0 +1,143 @@
|
||||
<?php
|
||||
require_once dirname(__DIR__, 2) . '/vendor/autoload.php';
|
||||
require_once __DIR__ . '/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();
|
||||
|
||||
$idCliente = isset($_GET['id_cliente']) ? (int)$_GET['id_cliente'] : 0;
|
||||
$limit = isset($_GET['limit']) ? (int)$_GET['limit'] : 3;
|
||||
$signedStatus = trim($_GET['signed_status'] ?? 'all');
|
||||
|
||||
if ($idCliente <= 0) {
|
||||
throw new Exception("Parametro id_cliente mancante o non valido.");
|
||||
}
|
||||
|
||||
/*
|
||||
* Allowed limits only.
|
||||
* This prevents risky wide queries on the live LIMS.
|
||||
*/
|
||||
$allowedLimits = [1, 3, 5, 10];
|
||||
|
||||
if (!in_array($limit, $allowedLimits, true)) {
|
||||
$limit = 3;
|
||||
}
|
||||
|
||||
/*
|
||||
* Allowed signature filters.
|
||||
*/
|
||||
$allowedSignedStatuses = ['all', 'signed', 'not_signed'];
|
||||
|
||||
if (!in_array($signedStatus, $allowedSignedStatuses, true)) {
|
||||
$signedStatus = 'all';
|
||||
}
|
||||
|
||||
/*
|
||||
* Base filter by customer.
|
||||
* We already verified that Rapporto can expand Cliente and returns Cliente.IdCliente.
|
||||
*/
|
||||
$filters = [
|
||||
"Cliente/IdCliente eq {$idCliente}"
|
||||
];
|
||||
|
||||
if ($signedStatus === 'signed') {
|
||||
$filters[] = "Firmato eq true";
|
||||
}
|
||||
|
||||
if ($signedStatus === 'not_signed') {
|
||||
$filters[] = "Firmato eq false";
|
||||
}
|
||||
|
||||
$filter = implode(' and ', $filters);
|
||||
|
||||
/*
|
||||
* Important:
|
||||
* - $top limits the number of reports.
|
||||
* - $orderby=Data desc gets the latest reports first.
|
||||
* - $expand=RapportiFiles retrieves only the PDF file metadata, not the binary PDF.
|
||||
*/
|
||||
$params = [
|
||||
'$filter' => $filter,
|
||||
'$select' => 'IdRapporto,CodiceRapporto,Data,Versione,Firmato,DataStampa',
|
||||
'$expand' => 'RapportiFiles',
|
||||
'$orderby' => 'Data desc',
|
||||
'$top' => $limit
|
||||
];
|
||||
|
||||
$endpoint = "Rapporto?" . http_build_query($params);
|
||||
|
||||
file_put_contents(
|
||||
__DIR__ . '/last_rapporti_cliente_endpoint.txt',
|
||||
'[' . date('Y-m-d H:i:s') . '] ' . $endpoint . PHP_EOL,
|
||||
FILE_APPEND
|
||||
);
|
||||
|
||||
$data = $api->get($endpoint);
|
||||
|
||||
$items = $data['value'] ?? [];
|
||||
|
||||
if (!is_array($items)) {
|
||||
$items = [];
|
||||
}
|
||||
|
||||
$reports = [];
|
||||
|
||||
foreach ($items as $item) {
|
||||
$rapportiFiles = $item['RapportiFiles'] ?? [];
|
||||
$pdfFiles = [];
|
||||
|
||||
if (is_array($rapportiFiles)) {
|
||||
foreach ($rapportiFiles as $file) {
|
||||
$idRapportoFile = intval($file['IdRapportoFile'] ?? 0);
|
||||
|
||||
if ($idRapportoFile > 0) {
|
||||
$pdfFiles[] = [
|
||||
'id_rapporto_file' => $idRapportoFile,
|
||||
'file_name' => $file['FileName'] ?? null,
|
||||
'categoria' => $file['Categoria'] ?? null,
|
||||
'tipo_rapporto' => $file['TipoRapporto'] ?? null,
|
||||
'download_url' => "download_rapporto_pdf.php?id_rapporto_file={$idRapportoFile}"
|
||||
];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$reports[] = [
|
||||
'id_rapporto' => $item['IdRapporto'] ?? null,
|
||||
'codice_rapporto' => $item['CodiceRapporto'] ?? null,
|
||||
'data' => $item['Data'] ?? null,
|
||||
'data_stampa' => $item['DataStampa'] ?? null,
|
||||
'versione' => $item['Versione'] ?? null,
|
||||
'firmato' => $item['Firmato'] ?? null,
|
||||
'pdf_files' => $pdfFiles
|
||||
];
|
||||
}
|
||||
|
||||
echo json_encode([
|
||||
'success' => true,
|
||||
'id_cliente' => $idCliente,
|
||||
'limit' => $limit,
|
||||
'signed_status' => $signedStatus,
|
||||
'endpoint' => $endpoint,
|
||||
'count' => count($reports),
|
||||
'reports' => $reports
|
||||
], JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE);
|
||||
} catch (Exception $e) {
|
||||
file_put_contents(
|
||||
__DIR__ . '/error_log.txt',
|
||||
date('Y-m-d H:i:s') . ' - get_rapporti_cliente.php - ' . $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,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,179 @@
|
||||
require_once dirname(__DIR__, 2) . '/vendor/autoload.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');
|
||||
error_reporting(E_ALL);
|
||||
|
||||
try {
|
||||
$api = VisualLimsApiClient::getInstance();
|
||||
$rapporto_id = 533329;
|
||||
|
||||
// Costruzione manuale dell'endpoint con espansione annidata
|
||||
$endpoint = "Rapporto($rapporto_id)?\$expand=CampioniDatiRapporto(\$expand=AnalisiDatiRapporto,CustomFieldsDatiRapporto)";
|
||||
// Esempi:
|
||||
// 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
|
||||
$data = $api->get($endpoint);
|
||||
$codiceRapporto = trim($_GET['codice'] ?? '');
|
||||
// Safe step mode: default is base, but allows controlled read-only steps
|
||||
$step = trim($_GET['step'] ?? 'base');
|
||||
|
||||
file_put_contents(__DIR__ . '/rapporto_expanded.json', json_encode($data, JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES));
|
||||
echo json_encode($data);
|
||||
if ($codiceRapporto === '') {
|
||||
throw new Exception("Parametro codice mancante. Usa ?codice=2541111");
|
||||
}
|
||||
|
||||
$allowedSteps = [
|
||||
'base' => '',
|
||||
'campioni' => 'CampioniDatiRapporto',
|
||||
'files' => 'RapportiFiles,Cliente',
|
||||
'cliente' => 'Cliente'
|
||||
];
|
||||
|
||||
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);
|
||||
// Safe version of codice rapporto for filenames
|
||||
$codiceRapportoFileSafe = preg_replace('/[^a-zA-Z0-9_-]/', '_', $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);
|
||||
|
||||
$pdfFiles = [];
|
||||
|
||||
if ($step === 'files') {
|
||||
$rapportiFiles = $detailData['RapportiFiles'] ?? [];
|
||||
|
||||
if (is_array($rapportiFiles)) {
|
||||
foreach ($rapportiFiles as $file) {
|
||||
$idRapportoFile = intval($file['IdRapportoFile'] ?? 0);
|
||||
|
||||
if ($idRapportoFile > 0) {
|
||||
$pdfFiles[] = [
|
||||
'id_rapporto_file' => $idRapportoFile,
|
||||
'file_name' => $file['FileName'] ?? null,
|
||||
'categoria' => $file['Categoria'] ?? null,
|
||||
'tipo_rapporto' => $file['TipoRapporto'] ?? null,
|
||||
'download_endpoint' => "MediaFile/DownloadStream?objectType=RapportoFile&propertyName=FileContent&objectKey={$idRapportoFile}"
|
||||
];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$clienteData = null;
|
||||
|
||||
if ($step === 'cliente' || $step === 'files') {
|
||||
$clienteData = $detailData['Cliente'] ?? null;
|
||||
}
|
||||
|
||||
file_put_contents(
|
||||
__DIR__ . "/rapporto_codice_{$codiceRapportoFileSafe}_{$step}.json",
|
||||
json_encode([
|
||||
'search' => $searchData,
|
||||
'detail' => $detailData
|
||||
], JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE)
|
||||
);
|
||||
|
||||
$response = [
|
||||
'success' => true,
|
||||
'codice_rapporto' => $codiceRapporto,
|
||||
'id_rapporto' => $rapportoId,
|
||||
'step' => $step,
|
||||
'search_endpoint' => $searchEndpoint,
|
||||
'detail_endpoint' => $detailEndpoint,
|
||||
'rapporto_base' => $rapportoBase,
|
||||
'data' => $detailData
|
||||
];
|
||||
|
||||
if ($step === 'files') {
|
||||
$response['pdf_files'] = $pdfFiles;
|
||||
}
|
||||
|
||||
if ($step === 'cliente' || $step === 'files') {
|
||||
$response['cliente'] = $clienteData;
|
||||
}
|
||||
|
||||
echo json_encode($response, JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE);
|
||||
} 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);
|
||||
echo json_encode(['error' => $e->getMessage()]);
|
||||
|
||||
echo json_encode([
|
||||
'success' => false,
|
||||
'error' => $e->getMessage()
|
||||
], JSON_PRETTY_PRINT | JSON_UNESCAPED_UNICODE);
|
||||
}
|
||||
|
||||
@@ -0,0 +1,94 @@
|
||||
<?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 users
|
||||
if (($_ENV['SIMULATE_EXPORT_LIMS'] ?? '') === 'true') {
|
||||
$fakeUsers = [
|
||||
[
|
||||
'IdUtente' => 1001,
|
||||
'Nominativo' => 'Utente Simulato 1001'
|
||||
],
|
||||
[
|
||||
'IdUtente' => 1002,
|
||||
'Nominativo' => 'Utente Simulato 1002'
|
||||
]
|
||||
];
|
||||
|
||||
echo json_encode(['value' => $fakeUsers]);
|
||||
exit;
|
||||
}
|
||||
|
||||
// OData parameters
|
||||
$params = [];
|
||||
|
||||
// Build query string
|
||||
$queryString = http_build_query($params);
|
||||
|
||||
// Final endpoint
|
||||
$endpoint = "Utente?$queryString";
|
||||
|
||||
// Function to execute request with 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__ . '/utenti_response.json', json_encode($data, JSON_PRETTY_PRINT));
|
||||
|
||||
return $data;
|
||||
} catch (Exception $e) {
|
||||
$errorMessage = $e->getMessage();
|
||||
|
||||
// Retry only on token/auth-related issue
|
||||
if (
|
||||
strpos($errorMessage, 'HTTP 400') !== false &&
|
||||
strpos($errorMessage, 'Cannot persist the object') !== false
|
||||
) {
|
||||
try {
|
||||
$api->refreshToken(); // must exist in VisualLimsApiClient
|
||||
error_log("Tentativo $retry: Refresh token eseguito per endpoint $endpoint");
|
||||
} catch (Exception $refreshEx) {
|
||||
error_log("Errore durante il refresh del token: " . $refreshEx->getMessage());
|
||||
throw new Exception("Impossibile eseguire il refresh del token: " . $refreshEx->getMessage());
|
||||
}
|
||||
|
||||
usleep(500000); // 500 ms
|
||||
continue;
|
||||
}
|
||||
|
||||
throw $e;
|
||||
}
|
||||
}
|
||||
|
||||
throw new Exception("Massimo numero di tentativi raggiunto per $endpoint");
|
||||
}
|
||||
|
||||
// Execute request
|
||||
$data = makeApiRequest($api, $endpoint);
|
||||
|
||||
echo json_encode($data);
|
||||
} 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_utenti.php: " . json_encode($errorResponse));
|
||||
echo json_encode($errorResponse);
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@@ -10,65 +10,125 @@
|
||||
<title>Template Buttons - <?= htmlspecialchars($titlewebsite, ENT_QUOTES, 'UTF-8'); ?></title>
|
||||
|
||||
<style>
|
||||
/* Layout flessibile per gestire dimensioni diverse */
|
||||
#templateButtons {
|
||||
/* Main buttons container */
|
||||
.template-buttons {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
gap: 10px;
|
||||
gap: 12px;
|
||||
justify-content: flex-start;
|
||||
/* Allinea a sinistra */
|
||||
padding: 20px;
|
||||
padding: 10px 0;
|
||||
}
|
||||
|
||||
/* Definizione delle dimensioni */
|
||||
/* Definizione delle dimensioni */
|
||||
/* Button sizes */
|
||||
.btn-small {
|
||||
font-size: 12px;
|
||||
padding: 6px 12px;
|
||||
min-width: 100px;
|
||||
min-height: 30px;
|
||||
display: flex;
|
||||
/* Aggiunto */
|
||||
min-height: 36px;
|
||||
display: inline-flex;
|
||||
justify-content: center;
|
||||
/* Aggiunto */
|
||||
align-items: center;
|
||||
/* Aggiunto */
|
||||
gap: 8px;
|
||||
border-radius: 8px;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.btn-medium {
|
||||
font-size: 16px;
|
||||
padding: 10px 20px;
|
||||
min-width: 130px;
|
||||
min-height: 45px;
|
||||
display: flex;
|
||||
/* Aggiunto */
|
||||
min-width: 140px;
|
||||
min-height: 48px;
|
||||
display: inline-flex;
|
||||
justify-content: center;
|
||||
/* Aggiunto */
|
||||
align-items: center;
|
||||
/* Aggiunto */
|
||||
gap: 8px;
|
||||
border-radius: 10px;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.btn-large {
|
||||
font-size: 20px;
|
||||
padding: 14px 28px;
|
||||
min-width: 180px;
|
||||
min-height: 60px;
|
||||
display: flex;
|
||||
/* Aggiunto */
|
||||
min-width: 190px;
|
||||
min-height: 64px;
|
||||
display: inline-flex;
|
||||
justify-content: center;
|
||||
/* Aggiunto */
|
||||
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 {
|
||||
width: 100%;
|
||||
padding: 10px;
|
||||
padding: 10px 14px;
|
||||
font-size: 16px;
|
||||
margin-bottom: 15px;
|
||||
border: 1px solid #ccc;
|
||||
border-radius: 5px;
|
||||
margin-bottom: 18px;
|
||||
border: 1px solid #d9d9d9;
|
||||
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>
|
||||
</head>
|
||||
@@ -87,14 +147,54 @@
|
||||
<h6 class="mb-0">Active Templates</h6>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<!-- Barra di ricerca -->
|
||||
<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 class="overlay toggle-icon"></div>
|
||||
<a href="javaScript:;" class="back-to-top"><i class='bx bxs-up-arrow-alt'></i></a>
|
||||
<?php include('include/footer.php'); ?>
|
||||
@@ -104,46 +204,148 @@
|
||||
|
||||
<script>
|
||||
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");
|
||||
|
||||
// Redirect based on template source type
|
||||
if (sourceType === 'XLS') {
|
||||
btn.href = `import_xls2.php?id=${template.id}`;
|
||||
} else if (sourceType === 'API' || sourceType === 'JSON') {
|
||||
btn.href = `import_json.php?id=${template.id}`;
|
||||
} else if (sourceType === 'PDF') {
|
||||
btn.href = `import_pdf.php?id=${template.id}`;
|
||||
} else {
|
||||
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")
|
||||
.then(response => response.json())
|
||||
.then(data => {
|
||||
if (!data.success) {
|
||||
console.error("Error loading templates:", data.message);
|
||||
clearContainers();
|
||||
Object.values(containers).forEach(container => {
|
||||
container.innerHTML = `<div class="empty-message">Error loading templates.</div>`;
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
let templateButtons = document.getElementById("templateButtons");
|
||||
templateButtons.innerHTML = '';
|
||||
|
||||
data.data.forEach(template => {
|
||||
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);
|
||||
});
|
||||
})
|
||||
.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";
|
||||
if (!Array.isArray(data.data)) {
|
||||
clearContainers();
|
||||
Object.values(containers).forEach(container => {
|
||||
container.innerHTML = `<div class="empty-message">Invalid response format.</div>`;
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
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>
|
||||
|
||||
+597
-230
File diff suppressed because it is too large
Load Diff
@@ -24,7 +24,8 @@ $columns = json_decode(urldecode($_POST['columns'] ?? '[]'), true);
|
||||
$rows = json_decode(urldecode($_POST['rows'] ?? '[]'), true);
|
||||
$excelrows = json_decode(urldecode($_POST['excelrows'] ?? '[]'), true);
|
||||
|
||||
$newFilename = htmlspecialchars($_POST['filename']);
|
||||
$newFilename = $_POST['filename'];
|
||||
$source_type = strtolower(trim($_POST['source_type'] ?? 'xls'));
|
||||
|
||||
$_SESSION['template_id'] = $template_id;
|
||||
$_SESSION['selected_rows'] = $selected_rows;
|
||||
@@ -37,6 +38,7 @@ error_log("Received Data - Template ID: $template_id, Selected Rows: " . json_en
|
||||
error_log("Columns: " . json_encode($columns));
|
||||
error_log("Rows: " . json_encode($rows));
|
||||
error_log("Excelrows: " . json_encode($excelrows));
|
||||
error_log("Source type: " . $source_type);
|
||||
|
||||
$user_id = $iduserlogin ?? 1;
|
||||
|
||||
@@ -47,7 +49,22 @@ $pdo = $db->getConnection();
|
||||
$importReferenceCode = date('YmdHis') . '-' . uniqid();
|
||||
|
||||
// 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,
|
||||
json_node,
|
||||
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]);
|
||||
$allMappings = $stmt->fetchAll(PDO::FETCH_ASSOC);
|
||||
|
||||
@@ -67,12 +84,20 @@ foreach ($allMappings as $mapping) {
|
||||
|
||||
// Inserisci le righe selezionate in datadb
|
||||
$insertedIds = [];
|
||||
foreach ($selected_rows as $rowIndex) {
|
||||
$row = $rows[$rowIndex] ?? null;
|
||||
$excelrow = $excelrows[$rowIndex] ?? null;
|
||||
foreach ($selected_rows as $loopIndex => $rowIndex) {
|
||||
|
||||
if ($source_type === 'json') {
|
||||
// JSON import sends only selected rows in rows/excelrows
|
||||
$row = $rows[$loopIndex] ?? null;
|
||||
$excelrow = $excelrows[$loopIndex] ?? ('JSON-' . ($loopIndex + 1));
|
||||
} else {
|
||||
// XLS import keeps original row indexes
|
||||
$row = $rows[$rowIndex] ?? null;
|
||||
$excelrow = $excelrows[$rowIndex] ?? null;
|
||||
}
|
||||
|
||||
if ($row === null || $excelrow === null) {
|
||||
error_log("Errore: riga o excelrow mancante per rowIndex $rowIndex");
|
||||
error_log("Errore: riga o excelrow mancante. Source type: $source_type, loopIndex: $loopIndex, rowIndex: $rowIndex");
|
||||
continue;
|
||||
}
|
||||
|
||||
@@ -104,14 +129,72 @@ foreach ($selected_rows as $rowIndex) {
|
||||
foreach ($allMappings as $mapping) {
|
||||
$fieldValue = null;
|
||||
if (!$mapping['is_manual']) {
|
||||
$excelColumn = trim($mapping['excel_column']);
|
||||
$excelColumnIndex = array_search($excelColumn, array_map('trim', $columns));
|
||||
if ($excelColumnIndex !== false && isset($row[$excelColumnIndex]) && $row[$excelColumnIndex] !== '') {
|
||||
$fieldValue = $row[$excelColumnIndex];
|
||||
error_log("Found Excel column '$excelColumn' at index $excelColumnIndex, value: " . var_export($fieldValue, true));
|
||||
$sourceColumn = '';
|
||||
|
||||
if ($source_type === 'json') {
|
||||
$sourceColumn = trim($mapping['json_node'] ?? '');
|
||||
} else {
|
||||
$sourceColumn = trim($mapping['excel_column'] ?? '');
|
||||
}
|
||||
|
||||
// Fallback: if JSON node is empty, try excel_column
|
||||
if ($sourceColumn === '') {
|
||||
$sourceColumn = trim($mapping['excel_column'] ?? '');
|
||||
}
|
||||
|
||||
$columnsTrimmed = array_map('trim', $columns);
|
||||
|
||||
$candidateColumns = [];
|
||||
|
||||
if ($sourceColumn !== '') {
|
||||
$candidateColumns[] = $sourceColumn;
|
||||
|
||||
if ($source_type === 'json') {
|
||||
// Common JSON path variants
|
||||
$candidateColumns[] = preg_replace('/^data\[\]\./', '', $sourceColumn);
|
||||
$candidateColumns[] = preg_replace('/^data\.0\./', '', $sourceColumn);
|
||||
$candidateColumns[] = str_replace('data[].', 'data.0.', $sourceColumn);
|
||||
$candidateColumns[] = str_replace('data.0.', 'data[].', $sourceColumn);
|
||||
}
|
||||
}
|
||||
|
||||
// Remove empty and duplicate candidates
|
||||
$candidateColumns = array_values(array_unique(array_filter($candidateColumns, function ($value) {
|
||||
return trim((string)$value) !== '';
|
||||
})));
|
||||
|
||||
$sourceColumnIndex = false;
|
||||
$matchedColumn = '';
|
||||
|
||||
foreach ($candidateColumns as $candidateColumn) {
|
||||
$candidateColumn = trim($candidateColumn);
|
||||
$index = array_search($candidateColumn, $columnsTrimmed);
|
||||
|
||||
if ($index !== false) {
|
||||
$sourceColumnIndex = $index;
|
||||
$matchedColumn = $candidateColumn;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if ($sourceColumnIndex !== false && isset($row[$sourceColumnIndex]) && $row[$sourceColumnIndex] !== '') {
|
||||
$fieldValue = $row[$sourceColumnIndex];
|
||||
|
||||
error_log(
|
||||
"Found source column. Original: '$sourceColumn', Matched: '$matchedColumn', Index: $sourceColumnIndex, Value: " .
|
||||
var_export($fieldValue, true)
|
||||
);
|
||||
} else {
|
||||
$fieldValue = $mapping['manual_default'] ?? '';
|
||||
error_log("Excel column '$excelColumn' not found or empty, using default: " . var_export($fieldValue, true));
|
||||
|
||||
error_log(
|
||||
"Source column not found or empty. Original: '$sourceColumn'. Candidates: " .
|
||||
json_encode($candidateColumns) .
|
||||
". Available columns: " .
|
||||
json_encode($columnsTrimmed) .
|
||||
". Using default: " .
|
||||
var_export($fieldValue, true)
|
||||
);
|
||||
}
|
||||
switch ($mapping['data_type']) {
|
||||
case 'INT':
|
||||
@@ -126,7 +209,7 @@ foreach ($selected_rows as $rowIndex) {
|
||||
case 'Testo':
|
||||
case 'VARCHAR':
|
||||
default:
|
||||
$fieldValue = !empty($fieldValue) ? htmlspecialchars((string)$fieldValue) : ($mapping['manual_default'] ?? '');
|
||||
$fieldValue = !empty($fieldValue) ? (string)$fieldValue : ($mapping['manual_default'] ?? '');
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
@@ -135,10 +218,19 @@ foreach ($selected_rows as $rowIndex) {
|
||||
$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 === '')) {
|
||||
error_log("Required field missing for mapping ID: " . $mapping['id'] . ", field: " . $mapping['field_label']);
|
||||
}
|
||||
error_log("Inserting into import_data_details - Mapping ID: " . $mapping['id'] . ", Field Value: " . var_export($fieldValue, true) . ", Is Manual: " . $mapping['is_manual'] . ", Excel Column: " . ($mapping['excel_column'] ?? 'N/A') . ", Manual Default: " . ($mapping['manual_default'] ?? 'N/A'));
|
||||
error_log("Inserting into import_data_details - Mapping ID: " . $mapping['id'] . ", Field Value: " . var_export($fieldValue, true) . ", Is Manual: " . $mapping['is_manual'] . ", Source Column: " . ($sourceColumn ?? 'N/A') . ", Source Type: " . $source_type . ", Manual Default: " . ($mapping['manual_default'] ?? 'N/A'));
|
||||
$stmt = $pdo->prepare("INSERT INTO import_data_details (id, mapping_id, field_value) VALUES (?, ?, ?)");
|
||||
$stmt->execute([$iddatadb, $mapping['id'], $fieldValue]);
|
||||
error_log("Inserted into import_data_details for ID $iddatadb, Mapping ID: " . $mapping['id'] . ", Field Value: " . var_export($fieldValue, true));
|
||||
@@ -147,28 +239,5 @@ foreach ($selected_rows as $rowIndex) {
|
||||
|
||||
$_SESSION['inserted_ids'] = $insertedIds;
|
||||
|
||||
$params = [
|
||||
'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
|
||||
header("Location: imported.php?id=" . urlencode($template_id) . "&importref=" . urlencode($importReferenceCode));
|
||||
exit;
|
||||
?>
|
||||
@@ -0,0 +1,819 @@
|
||||
<?php
|
||||
include('include/headscript.php');
|
||||
|
||||
// Check if a valid template ID has been provided
|
||||
if (!isset($_GET['id']) || !is_numeric($_GET['id'])) {
|
||||
header("Location: xlstemplates_grid.php?status=error&message=" . urlencode("Invalid ID"));
|
||||
exit;
|
||||
}
|
||||
|
||||
$id = intval($_GET['id']);
|
||||
|
||||
// Load template
|
||||
$db = DBHandlerSelect::getInstance();
|
||||
$pdo = $db->getConnection();
|
||||
$stmt = $pdo->prepare("SELECT * FROM excel_templates WHERE id = ?");
|
||||
$stmt->execute([$id]);
|
||||
$template = $stmt->fetch(PDO::FETCH_ASSOC);
|
||||
|
||||
if (!$template) {
|
||||
header("Location: template_dashboard.php?status=error&message=" . urlencode("Template not found"));
|
||||
exit;
|
||||
}
|
||||
|
||||
// Check mappings
|
||||
$stmt = $pdo->prepare("SELECT id FROM template_mapping WHERE template_id = ?");
|
||||
$stmt->execute([$id]);
|
||||
$hasMappings = $stmt->fetch(PDO::FETCH_ASSOC);
|
||||
|
||||
error_log("Loaded JSON import template: " . print_r($template, true));
|
||||
?>
|
||||
|
||||
<!doctype html>
|
||||
<html lang="en">
|
||||
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<link rel="icon" href="assets/images/favicon-32x32.png" type="image/png" />
|
||||
<?php include('cssinclude.php'); ?>
|
||||
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/css/bootstrap.min.css" rel="stylesheet">
|
||||
<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 {
|
||||
overflow-x: auto;
|
||||
max-width: 100%;
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
|
||||
.table {
|
||||
width: 100%;
|
||||
border-collapse: collapse;
|
||||
}
|
||||
|
||||
.table th,
|
||||
.table td {
|
||||
padding: 10px;
|
||||
text-align: left;
|
||||
border: 1px solid #dee2e6;
|
||||
min-width: 120px;
|
||||
max-width: 260px;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
vertical-align: middle;
|
||||
}
|
||||
|
||||
.table th:first-child,
|
||||
.table td:first-child {
|
||||
min-width: 50px;
|
||||
max-width: 50px;
|
||||
}
|
||||
|
||||
.table th {
|
||||
background-color: #f8f9fa;
|
||||
position: relative;
|
||||
cursor: col-resize;
|
||||
user-select: none;
|
||||
}
|
||||
|
||||
.table th .resize-handle {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
right: 0;
|
||||
width: 5px;
|
||||
height: 100%;
|
||||
cursor: col-resize;
|
||||
background: transparent;
|
||||
}
|
||||
|
||||
.table th .resize-handle:hover {
|
||||
background: #007bff;
|
||||
}
|
||||
|
||||
.loader {
|
||||
display: none;
|
||||
border: 4px solid #f3f3f3;
|
||||
border-top: 4px solid #3498db;
|
||||
border-radius: 50%;
|
||||
width: 30px;
|
||||
height: 30px;
|
||||
animation: spin 1s linear infinite;
|
||||
margin: 10px auto;
|
||||
}
|
||||
|
||||
@keyframes spin {
|
||||
0% {
|
||||
transform: rotate(0deg);
|
||||
}
|
||||
|
||||
100% {
|
||||
transform: rotate(360deg);
|
||||
}
|
||||
}
|
||||
|
||||
.column-filters th {
|
||||
background: #ffffff;
|
||||
cursor: default;
|
||||
}
|
||||
|
||||
.column-filters input {
|
||||
width: 100%;
|
||||
min-width: 80px;
|
||||
}
|
||||
|
||||
.json-code-input {
|
||||
font-size: 1.15rem;
|
||||
min-height: 48px;
|
||||
}
|
||||
|
||||
.json-paste-area {
|
||||
min-height: 260px;
|
||||
font-family: Consolas, Monaco, monospace;
|
||||
font-size: 13px;
|
||||
}
|
||||
|
||||
.json-help-box {
|
||||
background: #f8f9fa;
|
||||
border: 1px solid #dee2e6;
|
||||
border-radius: 8px;
|
||||
padding: 12px 14px;
|
||||
font-size: 13px;
|
||||
}
|
||||
|
||||
.source-badge {
|
||||
font-size: 12px;
|
||||
}
|
||||
</style>
|
||||
<title><?= htmlspecialchars($template['name']) ?> - JSON Import - <?= htmlspecialchars($titlewebsite, ENT_QUOTES, 'UTF-8'); ?></title>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<div class="wrapper">
|
||||
<?php include('include/navbar.php'); ?>
|
||||
<?php include('include/topbar.php'); ?>
|
||||
<div class="page-wrapper">
|
||||
<div class="page-content">
|
||||
<?php include('top_stat_widget.php'); ?>
|
||||
|
||||
<div class="mb-3 text">
|
||||
<a href="imported.php?id=<?= $id ?>" class="btn btn-warning me-2">Imported (i)</a>
|
||||
<a href="tolims.php?id=<?= $id ?>" class="btn btn-success">To LIMS (l)</a>
|
||||
</div>
|
||||
|
||||
<div class="card radius-10">
|
||||
<div class="card-header">
|
||||
<div class="d-flex align-items-center justify-content-between flex-wrap gap-2">
|
||||
<div>
|
||||
<h6 class="mb-0"><?= htmlspecialchars($template['name']) ?> - JSON Import</h6>
|
||||
<small>
|
||||
Template ID: <?= $id ?>
|
||||
</small>
|
||||
</div>
|
||||
<span class="badge bg-info text-dark">JSON mode</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="card-body">
|
||||
<?php if (!$hasMappings): ?>
|
||||
<div class="alert alert-warning" role="alert">
|
||||
Nessun mapping trovato per questo template. Configura i mapping prima di procedere.
|
||||
</div>
|
||||
<?php endif; ?>
|
||||
|
||||
<div class="json-help-box mb-3">
|
||||
<strong>Flusso:</strong> inserisci/scansiona un codice per recuperare il JSON da API, oppure incolla manualmente un JSON nel secondo tab.
|
||||
Ogni JSON aggiunto diventa una riga della tabella di preview. Quando hai finito, seleziona le righe e clicca <strong>Prosegui</strong>.
|
||||
</div>
|
||||
|
||||
<ul class="nav nav-tabs" id="jsonImportTabs" role="tablist">
|
||||
<li class="nav-item" role="presentation">
|
||||
<button class="nav-link active" id="api-code-tab" data-bs-toggle="tab" data-bs-target="#api-code-pane" type="button" role="tab" aria-controls="api-code-pane" aria-selected="true">
|
||||
Code / Barcode
|
||||
</button>
|
||||
</li>
|
||||
<li class="nav-item" role="presentation">
|
||||
<button class="nav-link" id="paste-json-tab" data-bs-toggle="tab" data-bs-target="#paste-json-pane" type="button" role="tab" aria-controls="paste-json-pane" aria-selected="false">
|
||||
Paste JSON
|
||||
</button>
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
<div class="tab-content border border-top-0 p-3 mb-3" id="jsonImportTabsContent">
|
||||
<div class="tab-pane fade show active" id="api-code-pane" role="tabpanel" aria-labelledby="api-code-tab" tabindex="0">
|
||||
<form id="jsonCodeForm" class="row g-3 align-items-end">
|
||||
<div class="col-lg-8">
|
||||
<label for="json_code" class="form-label">Code / Barcode</label>
|
||||
<input type="text" class="form-control json-code-input" id="json_code" name="json_code" placeholder="Write or scan code" autocomplete="off" <?= !$hasMappings ? 'disabled' : '' ?>>
|
||||
<small class="text-muted">Lo scanner barcode normalmente scrive qui il codice e invia Enter.</small>
|
||||
</div>
|
||||
<div class="col-lg-4 d-flex gap-2">
|
||||
<button type="submit" class="btn btn-primary flex-fill" <?= !$hasMappings ? 'disabled' : '' ?>>Load JSON</button>
|
||||
<button type="button" class="btn btn-outline-secondary" id="clearCodeBtn">Clear</button>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
|
||||
<div class="tab-pane fade" id="paste-json-pane" role="tabpanel" aria-labelledby="paste-json-tab" tabindex="0">
|
||||
<form id="pasteJsonForm">
|
||||
<div class="mb-3">
|
||||
<label for="manual_json_reference" class="form-label">Reference / filename</label>
|
||||
<input type="text" class="form-control" id="manual_json_reference" placeholder="Optional reference, e.g. manual-json-001">
|
||||
</div>
|
||||
<div class="mb-3">
|
||||
<label for="manual_json" class="form-label">Paste JSON</label>
|
||||
<textarea class="form-control json-paste-area" id="manual_json" placeholder='{"data":[{"id":"MM000620","attributes":{"trf_type":"apparel"}}]}' <?= !$hasMappings ? 'disabled' : '' ?>></textarea>
|
||||
</div>
|
||||
<button type="submit" class="btn btn-primary" <?= !$hasMappings ? 'disabled' : '' ?>>Add pasted JSON</button>
|
||||
<button type="button" class="btn btn-outline-secondary" id="clearManualJsonBtn">Clear</button>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="loader" id="loader"></div>
|
||||
<div id="errorContainer" class="alert alert-danger mt-3" style="display:none;"></div>
|
||||
<div id="successContainer" class="alert alert-success mt-3" style="display:none;"></div>
|
||||
|
||||
<div id="tableContainer"></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="overlay toggle-icon"></div>
|
||||
<a href="javaScript:;" class="back-to-top"><i class='bx bxs-up-arrow-alt'></i></a>
|
||||
<?php include('include/footer.php'); ?>
|
||||
</div>
|
||||
|
||||
<script src="https://cdn.jsdelivr.net/npm/@popperjs/core@2.11.6/dist/umd/popper.min.js"></script>
|
||||
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/js/bootstrap.min.js"></script>
|
||||
<?php include('jsinclude.php'); ?>
|
||||
|
||||
<script>
|
||||
document.addEventListener("DOMContentLoaded", function() {
|
||||
const TEMPLATE_ID = <?= (int)$id ?>;
|
||||
const API_ENDPOINT = 'get_json_by_code.php';
|
||||
const INCLUDE_SOURCE_CODE_COLUMN = true;
|
||||
const UNWRAP_SINGLE_DATA_ITEM = true;
|
||||
|
||||
const jsonCodeForm = document.getElementById('jsonCodeForm');
|
||||
const pasteJsonForm = document.getElementById('pasteJsonForm');
|
||||
const jsonCodeInput = document.getElementById('json_code');
|
||||
const manualJsonInput = document.getElementById('manual_json');
|
||||
const manualJsonReferenceInput = document.getElementById('manual_json_reference');
|
||||
const clearCodeBtn = document.getElementById('clearCodeBtn');
|
||||
const clearManualJsonBtn = document.getElementById('clearManualJsonBtn');
|
||||
const loader = document.getElementById('loader');
|
||||
const errorContainer = document.getElementById('errorContainer');
|
||||
const successContainer = document.getElementById('successContainer');
|
||||
const tableContainer = document.getElementById('tableContainer');
|
||||
|
||||
let jsonRows = [];
|
||||
let columns = [];
|
||||
|
||||
if (jsonCodeInput && !jsonCodeInput.disabled) {
|
||||
jsonCodeInput.focus();
|
||||
}
|
||||
|
||||
jsonCodeForm.addEventListener('submit', function(e) {
|
||||
e.preventDefault();
|
||||
const code = jsonCodeInput.value.trim();
|
||||
|
||||
if (!code) {
|
||||
showError('Inserisci o scansiona un codice.');
|
||||
return;
|
||||
}
|
||||
|
||||
loadJsonFromApi(code);
|
||||
});
|
||||
|
||||
pasteJsonForm.addEventListener('submit', function(e) {
|
||||
e.preventDefault();
|
||||
|
||||
const rawJson = manualJsonInput.value.trim();
|
||||
const reference = manualJsonReferenceInput.value.trim() || 'manual-json-' + (jsonRows.length + 1);
|
||||
|
||||
if (!rawJson) {
|
||||
showError('Incolla un JSON prima di aggiungerlo.');
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
const parsedJson = JSON.parse(rawJson);
|
||||
addJsonRow(parsedJson, reference, 'paste');
|
||||
manualJsonInput.value = '';
|
||||
manualJsonReferenceInput.value = '';
|
||||
showSuccess('JSON incollato aggiunto correttamente.');
|
||||
} catch (err) {
|
||||
showError('JSON non valido: ' + err.message);
|
||||
}
|
||||
});
|
||||
|
||||
clearCodeBtn.addEventListener('click', function() {
|
||||
jsonCodeInput.value = '';
|
||||
jsonCodeInput.focus();
|
||||
});
|
||||
|
||||
clearManualJsonBtn.addEventListener('click', function() {
|
||||
manualJsonInput.value = '';
|
||||
manualJsonReferenceInput.value = '';
|
||||
});
|
||||
|
||||
function loadJsonFromApi(code) {
|
||||
hideMessages();
|
||||
loader.style.display = 'block';
|
||||
|
||||
fetch(API_ENDPOINT, {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'application/json'
|
||||
},
|
||||
body: JSON.stringify({
|
||||
template_id: TEMPLATE_ID,
|
||||
code: code
|
||||
})
|
||||
})
|
||||
.then(response => {
|
||||
if (!response.ok) {
|
||||
throw new Error('HTTP status ' + response.status);
|
||||
}
|
||||
return response.json();
|
||||
})
|
||||
.then(responseData => {
|
||||
loader.style.display = 'none';
|
||||
|
||||
if (responseData.error) {
|
||||
showError(responseData.error);
|
||||
return;
|
||||
}
|
||||
|
||||
let jsonPayload = responseData;
|
||||
let reference = code;
|
||||
|
||||
// Supported endpoint response format:
|
||||
// { success: true, json: {...}, reference: "..." }
|
||||
if (responseData.json !== undefined) {
|
||||
jsonPayload = responseData.json;
|
||||
reference = responseData.reference || responseData.filename || responseData.code || code;
|
||||
}
|
||||
|
||||
addJsonRow(jsonPayload, reference, 'api');
|
||||
jsonCodeInput.value = '';
|
||||
jsonCodeInput.focus();
|
||||
showSuccess('JSON recuperato e aggiunto correttamente.');
|
||||
})
|
||||
.catch(error => {
|
||||
loader.style.display = 'none';
|
||||
showError('Errore durante il recupero del JSON: ' + error.message);
|
||||
});
|
||||
}
|
||||
|
||||
function addJsonRow(jsonPayload, reference, sourceType) {
|
||||
const normalizedPayload = normalizeJsonPayload(jsonPayload);
|
||||
const flattened = flattenJson(normalizedPayload);
|
||||
|
||||
if (INCLUDE_SOURCE_CODE_COLUMN) {
|
||||
flattened.source_code = reference;
|
||||
flattened.source_type = sourceType;
|
||||
}
|
||||
|
||||
const newColumns = Object.keys(flattened).filter(col => !columns.includes(col));
|
||||
columns = columns.concat(newColumns);
|
||||
|
||||
jsonRows.push({
|
||||
excelrow: 'JSON-' + (jsonRows.length + 1),
|
||||
reference: reference,
|
||||
sourceType: sourceType,
|
||||
flat: flattened
|
||||
});
|
||||
|
||||
renderTable();
|
||||
}
|
||||
|
||||
function normalizeJsonPayload(payload) {
|
||||
if (
|
||||
UNWRAP_SINGLE_DATA_ITEM &&
|
||||
payload &&
|
||||
typeof payload === 'object' &&
|
||||
!Array.isArray(payload) &&
|
||||
Array.isArray(payload.data) &&
|
||||
payload.data.length === 1 &&
|
||||
payload.data[0] &&
|
||||
typeof payload.data[0] === 'object'
|
||||
) {
|
||||
return payload.data[0];
|
||||
}
|
||||
|
||||
return payload;
|
||||
}
|
||||
|
||||
function flattenJson(value, prefix = '', result = {}) {
|
||||
if (value === null || value === undefined) {
|
||||
result[prefix || 'value'] = '';
|
||||
return result;
|
||||
}
|
||||
|
||||
if (Array.isArray(value)) {
|
||||
if (value.length === 0) {
|
||||
result[prefix || 'value'] = '';
|
||||
return result;
|
||||
}
|
||||
|
||||
value.forEach((item, index) => {
|
||||
const newPrefix = prefix ? prefix + '.' + index : String(index);
|
||||
flattenJson(item, newPrefix, result);
|
||||
});
|
||||
return result;
|
||||
}
|
||||
|
||||
if (typeof value === 'object') {
|
||||
const keys = Object.keys(value);
|
||||
if (keys.length === 0) {
|
||||
result[prefix || 'value'] = '';
|
||||
return result;
|
||||
}
|
||||
|
||||
keys.forEach(key => {
|
||||
const newPrefix = prefix ? prefix + '.' + key : key;
|
||||
flattenJson(value[key], newPrefix, result);
|
||||
});
|
||||
return result;
|
||||
}
|
||||
|
||||
result[prefix || 'value'] = String(value);
|
||||
return result;
|
||||
}
|
||||
|
||||
function buildImportData() {
|
||||
const rows = jsonRows.map(row => columns.map(col => row.flat[col] ?? ''));
|
||||
const excelData = rows.map((rowData, index) => ({
|
||||
excelrow: jsonRows[index].excelrow,
|
||||
data: rowData
|
||||
}));
|
||||
|
||||
return {
|
||||
template_id: TEMPLATE_ID,
|
||||
columns: columns,
|
||||
rows: rows,
|
||||
excel_data: excelData,
|
||||
filename: 'json_import_' + new Date().toISOString().replace(/[:.]/g, '-').slice(0, 19)
|
||||
};
|
||||
}
|
||||
|
||||
function renderTable() {
|
||||
if (jsonRows.length === 0) {
|
||||
tableContainer.innerHTML = '';
|
||||
return;
|
||||
}
|
||||
|
||||
const data = buildImportData();
|
||||
|
||||
let html = `
|
||||
<form id="selectRowsForm" action="import_insert.php" method="POST">
|
||||
<input type="hidden" name="template_id" value="${escapeHtml(data.template_id)}">
|
||||
<input type="hidden" name="columns" value="${encodeURIComponent(JSON.stringify(data.columns))}">
|
||||
<input type="hidden" name="rows" id="selectedRowsData" value="">
|
||||
<input type="hidden" name="excelrows" id="selectedExcelRowsData" value="">
|
||||
<input type="hidden" name="filename" value="${escapeHtml(data.filename)}">
|
||||
<input type="hidden" name="source_type" value="json">
|
||||
|
||||
<div class="d-flex justify-content-between align-items-center flex-wrap gap-2 mb-3">
|
||||
<div>
|
||||
<strong>JSON rows loaded:</strong> ${jsonRows.length}
|
||||
<span class="badge bg-secondary source-badge ms-2">Columns: ${data.columns.length}</span>
|
||||
</div>
|
||||
<div class="d-flex gap-2">
|
||||
<button type="button" class="btn btn-outline-danger btn-sm" id="clearAllRowsBtn">Clear all rows</button>
|
||||
<button type="submit" class="btn btn-primary" id="proceedButtonTop" disabled>Prosegui</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="top-scrollbar" id="topTableScrollbar">
|
||||
<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>
|
||||
<tr>
|
||||
<th><input type="checkbox" id="selectAll"> Select</th>
|
||||
${data.columns.map(col => `<th title="${escapeHtml(col)}">${escapeHtml(readableColumnLabel(col))}<div class="resize-handle"></div></th>`).join('')}
|
||||
<th>Action</th>
|
||||
</tr>
|
||||
<tr class="column-filters">
|
||||
<th></th>
|
||||
${data.columns.map((col, i) => `
|
||||
<th>
|
||||
<input type="text"
|
||||
class="form-control form-control-sm column-filter"
|
||||
data-col-index="${i}"
|
||||
placeholder="Filter...">
|
||||
</th>
|
||||
`).join('')}
|
||||
<th></th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
${data.excel_data.map((row, index) => `
|
||||
<tr data-row-index="${index}">
|
||||
<td><input type="checkbox" class="row-checkbox" name="selected_rows[]" value="${index}" data-excelrow="${escapeHtml(row.excelrow)}"></td>
|
||||
${row.data.map(cell => `<td title="${escapeHtml(cell)}">${escapeHtml(cell)}</td>`).join('')}
|
||||
<td><button type="button" class="btn btn-sm btn-outline-danger remove-row-btn" data-row-index="${index}">Remove</button></td>
|
||||
</tr>
|
||||
`).join('')}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
|
||||
<button type="submit" class="btn btn-primary mt-3" id="proceedButtonBottom" disabled>Prosegui</button>
|
||||
</form>
|
||||
`;
|
||||
|
||||
tableContainer.innerHTML = html;
|
||||
bindTableEvents(data);
|
||||
}
|
||||
|
||||
function bindTableEvents(data) {
|
||||
const selectRowsForm = document.getElementById('selectRowsForm');
|
||||
const clearAllRowsBtn = document.getElementById('clearAllRowsBtn');
|
||||
const removeRowButtons = document.querySelectorAll('.remove-row-btn');
|
||||
|
||||
selectRowsForm.addEventListener('submit', function(e) {
|
||||
const checkedBoxes = Array.from(document.querySelectorAll('.row-checkbox:checked'));
|
||||
|
||||
if (checkedBoxes.length === 0) {
|
||||
e.preventDefault();
|
||||
alert('Seleziona almeno una riga.');
|
||||
return;
|
||||
}
|
||||
|
||||
const selectedRows = [];
|
||||
const selectedExcelRows = [];
|
||||
|
||||
checkedBoxes.forEach((cb, newIndex) => {
|
||||
const originalIndex = parseInt(cb.value, 10);
|
||||
|
||||
if (data.rows && data.rows[originalIndex]) {
|
||||
selectedRows.push(data.rows[originalIndex]);
|
||||
}
|
||||
|
||||
if (data.excel_data && data.excel_data[originalIndex]) {
|
||||
selectedExcelRows.push(data.excel_data[originalIndex].excelrow);
|
||||
}
|
||||
|
||||
// Reindex selected_rows so import_insert.php receives only the reduced rows array
|
||||
cb.value = newIndex;
|
||||
});
|
||||
|
||||
document.getElementById('selectedRowsData').value =
|
||||
encodeURIComponent(JSON.stringify(selectedRows));
|
||||
|
||||
document.getElementById('selectedExcelRowsData').value =
|
||||
encodeURIComponent(JSON.stringify(selectedExcelRows));
|
||||
});
|
||||
|
||||
clearAllRowsBtn.addEventListener('click', function() {
|
||||
if (!confirm('Vuoi rimuovere tutte le righe JSON caricate?')) return;
|
||||
jsonRows = [];
|
||||
columns = [];
|
||||
renderTable();
|
||||
showSuccess('Righe JSON rimosse.');
|
||||
jsonCodeInput.focus();
|
||||
});
|
||||
|
||||
removeRowButtons.forEach(button => {
|
||||
button.addEventListener('click', function() {
|
||||
const rowIndex = parseInt(this.dataset.rowIndex, 10);
|
||||
jsonRows.splice(rowIndex, 1);
|
||||
rebuildColumnsFromRows();
|
||||
renderTable();
|
||||
});
|
||||
});
|
||||
|
||||
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 proceedButtonBottom = document.getElementById('proceedButtonBottom');
|
||||
const selectAllCheckbox = document.getElementById('selectAll');
|
||||
const checkboxes = document.querySelectorAll('.row-checkbox');
|
||||
|
||||
function updateProceedButton() {
|
||||
const enabled = Array.from(document.querySelectorAll('.row-checkbox')).some(cb => cb.checked);
|
||||
|
||||
if (proceedButtonTop) proceedButtonTop.disabled = !enabled;
|
||||
if (proceedButtonBottom) proceedButtonBottom.disabled = !enabled;
|
||||
}
|
||||
|
||||
selectAllCheckbox.addEventListener('change', function() {
|
||||
const visibleRows = Array.from(document.querySelectorAll('#importPreviewTable tbody tr'))
|
||||
.filter(row => row.style.display !== 'none');
|
||||
|
||||
visibleRows.forEach(row => {
|
||||
const checkbox = row.querySelector('.row-checkbox');
|
||||
if (checkbox) {
|
||||
checkbox.checked = this.checked;
|
||||
}
|
||||
});
|
||||
|
||||
updateProceedButton();
|
||||
});
|
||||
|
||||
checkboxes.forEach(checkbox => {
|
||||
checkbox.addEventListener('change', function() {
|
||||
const visibleCheckboxes = Array.from(document.querySelectorAll('#importPreviewTable 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();
|
||||
});
|
||||
});
|
||||
|
||||
const thElements = document.querySelectorAll('#importPreviewTable th');
|
||||
thElements.forEach((th, index) => {
|
||||
if (index === 0) return;
|
||||
const resizeHandle = th.querySelector('.resize-handle');
|
||||
if (resizeHandle) {
|
||||
resizeHandle.addEventListener('mousedown', (e) => {
|
||||
e.preventDefault();
|
||||
const startX = e.clientX;
|
||||
const startWidth = th.offsetWidth;
|
||||
|
||||
const onMouseMove = (e) => {
|
||||
const newWidth = Math.max(60, startWidth + (e.clientX - startX));
|
||||
th.style.width = `${newWidth}px`;
|
||||
th.style.minWidth = `${newWidth}px`;
|
||||
th.style.maxWidth = `${newWidth}px`;
|
||||
|
||||
const cells = document.querySelectorAll(`#importPreviewTable td:nth-child(${index + 1})`);
|
||||
cells.forEach(cell => {
|
||||
cell.style.width = `${newWidth}px`;
|
||||
cell.style.minWidth = `${newWidth}px`;
|
||||
cell.style.maxWidth = `${newWidth}px`;
|
||||
});
|
||||
};
|
||||
|
||||
const onMouseUp = () => {
|
||||
document.removeEventListener('mousemove', onMouseMove);
|
||||
document.removeEventListener('mouseup', onMouseUp);
|
||||
};
|
||||
|
||||
document.addEventListener('mousemove', onMouseMove);
|
||||
document.addEventListener('mouseup', onMouseUp);
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
const rows = document.querySelectorAll('#importPreviewTable tbody tr');
|
||||
const filterInputs = document.querySelectorAll('.column-filter');
|
||||
const activeFilters = {};
|
||||
|
||||
function applyColumnFilters() {
|
||||
rows.forEach(row => {
|
||||
let visible = true;
|
||||
|
||||
for (const [colIndexStr, filterValue] of Object.entries(activeFilters)) {
|
||||
const colIndex = parseInt(colIndexStr, 10);
|
||||
const cell = row.cells[colIndex + 1];
|
||||
const cellText = (cell?.textContent || '').toLowerCase();
|
||||
const searchText = (filterValue || '').toLowerCase().trim();
|
||||
|
||||
if (searchText && !cellText.includes(searchText)) {
|
||||
visible = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
row.style.display = visible ? '' : 'none';
|
||||
});
|
||||
|
||||
const visibleCheckboxes = Array.from(document.querySelectorAll('#importPreviewTable 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 => {
|
||||
input.addEventListener('input', function() {
|
||||
const colIndex = this.dataset.colIndex;
|
||||
activeFilters[colIndex] = this.value;
|
||||
applyColumnFilters();
|
||||
});
|
||||
});
|
||||
|
||||
updateProceedButton();
|
||||
}
|
||||
|
||||
function rebuildColumnsFromRows() {
|
||||
columns = [];
|
||||
jsonRows.forEach(row => {
|
||||
Object.keys(row.flat).forEach(col => {
|
||||
if (!columns.includes(col)) {
|
||||
columns.push(col);
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
function readableColumnLabel(columnName) {
|
||||
if (!columnName) return 'Column without name';
|
||||
return columnName;
|
||||
}
|
||||
|
||||
function showError(message) {
|
||||
successContainer.style.display = 'none';
|
||||
errorContainer.textContent = message;
|
||||
errorContainer.style.display = 'block';
|
||||
}
|
||||
|
||||
function showSuccess(message) {
|
||||
errorContainer.style.display = 'none';
|
||||
successContainer.textContent = message;
|
||||
successContainer.style.display = 'block';
|
||||
setTimeout(() => {
|
||||
successContainer.style.display = 'none';
|
||||
}, 3500);
|
||||
}
|
||||
|
||||
function hideMessages() {
|
||||
errorContainer.style.display = 'none';
|
||||
successContainer.style.display = 'none';
|
||||
}
|
||||
|
||||
function escapeHtml(value) {
|
||||
return String(value ?? '')
|
||||
.replace(/&/g, '&')
|
||||
.replace(/</g, '<')
|
||||
.replace(/>/g, '>')
|
||||
.replace(/"/g, '"')
|
||||
.replace(/'/g, ''');
|
||||
}
|
||||
});
|
||||
</script>
|
||||
</body>
|
||||
|
||||
</html>
|
||||
+193
-52
@@ -40,6 +40,22 @@ error_log("Loaded template: " . print_r($template, true));
|
||||
<?php include('cssinclude.php'); ?>
|
||||
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/css/bootstrap.min.css" rel="stylesheet">
|
||||
<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 {
|
||||
overflow-x: auto;
|
||||
max-width: 100%;
|
||||
@@ -143,16 +159,20 @@ error_log("Loaded template: " . print_r($template, true));
|
||||
<div class="page-content">
|
||||
<?php include('top_stat_widget.php'); ?>
|
||||
<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="historical_trf.php?id=<?= $id ?>&status=P" class="btn btn-primary me-2">In Progress (P)</a>
|
||||
<a href="historical_trf.php?id=<?= $id ?>&status=l" class="btn btn-success">To LIMS (l)</a>
|
||||
<a href="imported.php?id=<?= $id ?>" class="btn btn-warning me-2">Imported (i)</a>
|
||||
<a href="tolims.php?id=<?= $id ?>" class="btn btn-success">To LIMS (l)</a>
|
||||
</div>
|
||||
<div class="card radius-10">
|
||||
<div class="card-header">
|
||||
<div class="d-flex align-items-center">
|
||||
<div>
|
||||
<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>
|
||||
@@ -229,8 +249,9 @@ error_log("Loaded template: " . print_r($template, true));
|
||||
const templateId = <?= $id ?>;
|
||||
console.log('Template ID passed to formData:', templateId);
|
||||
formData.append('template_id', templateId);
|
||||
formData.append('header_row', <?= $template['header_row'] ?>);
|
||||
formData.append('start_column', <?= $template['start_column'] ?>);
|
||||
formData.append('header_row', <?= (int)$template['header_row'] ?>);
|
||||
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', {
|
||||
method: 'POST',
|
||||
@@ -313,67 +334,177 @@ error_log("Loaded template: " . print_r($template, true));
|
||||
function showTable(data) {
|
||||
console.log('Mostro tabella con dati:', data);
|
||||
let html = `
|
||||
<form id="selectRowsForm" action="import_insert.php" method="POST">
|
||||
<input type="hidden" name="template_id" value="${data.template_id}">
|
||||
<input type="hidden" name="columns" value="${encodeURIComponent(JSON.stringify(data.columns))}">
|
||||
<input type="hidden" name="rows" value="${encodeURIComponent(JSON.stringify(data.rows))}">
|
||||
<input type="hidden" name="excelrows" value="${encodeURIComponent(JSON.stringify(data.excel_data.map(r => r.excelrow)))}">
|
||||
<form id="selectRowsForm" action="import_insert.php" method="POST">
|
||||
<input type="hidden" name="template_id" value="${data.template_id}">
|
||||
<input type="hidden" name="columns" value="${encodeURIComponent(JSON.stringify(data.columns))}">
|
||||
<input type="hidden" name="rows" id="selectedRowsData" value="">
|
||||
<input type="hidden" name="excelrows" id="selectedExcelRowsData" value="">
|
||||
<input type="hidden" name="filename" value="${data.filename}">
|
||||
|
||||
<input type="hidden" name="filename" value="${data.filename}">
|
||||
<!-- TOP BUTTON -->
|
||||
<div class="d-flex justify-content-end mb-3">
|
||||
<button type="submit" class="btn btn-primary" id="proceedButtonTop" disabled>Prosegui</button>
|
||||
</div>
|
||||
|
||||
<div class="table-container">
|
||||
<table class="table table-striped table-bordered">
|
||||
<thead>
|
||||
<tr>
|
||||
<th><input type="checkbox" id="selectAll"> Seleziona</th>
|
||||
${data.columns.map(col => `<th>${col || 'Colonna senza nome'}<div class="resize-handle"></div></th>`).join('')}
|
||||
</tr>
|
||||
<tr class="column-filters">
|
||||
<th></th>
|
||||
${data.columns.map((col, i) => `
|
||||
<th>
|
||||
<input type="text"
|
||||
class="form-control form-control-sm column-filter"
|
||||
data-col-index="${i}"
|
||||
placeholder="Filter...">
|
||||
</th>
|
||||
`).join('')}
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
${data.excel_data.map((row, index) => `
|
||||
<tr>
|
||||
<td><input type="checkbox" class="row-checkbox" name="selected_rows[]" value="${index}" data-excelrow="${row.excelrow}"></td>
|
||||
${row.data.map(cell => `<td>${cell}</td>`).join('')}
|
||||
</tr>
|
||||
`).join('')}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
<button type="submit" class="btn btn-primary mt-3" id="proceedButton" disabled>Prosegui</button>
|
||||
</form>
|
||||
`;
|
||||
<div class="top-scrollbar" id="topTableScrollbar">
|
||||
<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>
|
||||
<tr>
|
||||
<th><input type="checkbox" id="selectAll"> Seleziona</th>
|
||||
${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 class="column-filters">
|
||||
<th></th>
|
||||
${data.columns.map((col, i) => `
|
||||
<th>
|
||||
<input type="text"
|
||||
class="form-control form-control-sm column-filter"
|
||||
data-col-index="${i}"
|
||||
placeholder="Filter...">
|
||||
</th>
|
||||
`).join('')}
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
${data.excel_data.map((row, index) => `
|
||||
<tr>
|
||||
<td><input type="checkbox" class="row-checkbox" name="selected_rows[]" value="${index}" data-excelrow="${row.excelrow}"></td>
|
||||
${row.data.map(cell => `<td>${cell}</td>`).join('')}
|
||||
</tr>
|
||||
`).join('')}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
|
||||
<!-- BOTTOM BUTTON -->
|
||||
<button type="submit" class="btn btn-primary mt-3" id="proceedButtonBottom" disabled>Prosegui</button>
|
||||
</form>
|
||||
`;
|
||||
tableContainer.innerHTML = html;
|
||||
|
||||
const proceedButton = document.getElementById('proceedButton');
|
||||
const selectRowsForm = document.getElementById('selectRowsForm');
|
||||
|
||||
selectRowsForm.addEventListener('submit', function(e) {
|
||||
const checkedBoxes = Array.from(document.querySelectorAll('.row-checkbox:checked'));
|
||||
|
||||
if (checkedBoxes.length === 0) {
|
||||
e.preventDefault();
|
||||
alert('Seleziona almeno una riga.');
|
||||
return;
|
||||
}
|
||||
|
||||
const selectedRows = [];
|
||||
const selectedExcelRows = [];
|
||||
|
||||
checkedBoxes.forEach((cb, newIndex) => {
|
||||
const originalIndex = parseInt(cb.value, 10);
|
||||
|
||||
if (data.rows && data.rows[originalIndex]) {
|
||||
selectedRows.push(data.rows[originalIndex]);
|
||||
}
|
||||
|
||||
if (data.excel_data && data.excel_data[originalIndex]) {
|
||||
selectedExcelRows.push(data.excel_data[originalIndex].excelrow);
|
||||
}
|
||||
|
||||
// Reindex selected_rows so import_insert.php receives only the reduced rows array
|
||||
cb.value = newIndex;
|
||||
});
|
||||
|
||||
document.getElementById('selectedRowsData').value =
|
||||
encodeURIComponent(JSON.stringify(selectedRows));
|
||||
|
||||
document.getElementById('selectedExcelRowsData').value =
|
||||
encodeURIComponent(JSON.stringify(selectedExcelRows));
|
||||
});
|
||||
|
||||
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 proceedButtonBottom = document.getElementById('proceedButtonBottom');
|
||||
const selectAllCheckbox = document.getElementById('selectAll');
|
||||
const checkboxes = document.querySelectorAll('.row-checkbox');
|
||||
|
||||
function updateProceedButton() {
|
||||
proceedButton.disabled = !Array.from(checkboxes).some(cb => cb.checked);
|
||||
const enabled = Array.from(checkboxes).some(cb => cb.checked);
|
||||
|
||||
if (proceedButtonTop) proceedButtonTop.disabled = !enabled;
|
||||
if (proceedButtonBottom) proceedButtonBottom.disabled = !enabled;
|
||||
}
|
||||
|
||||
selectAllCheckbox.addEventListener('change', function() {
|
||||
checkboxes.forEach(checkbox => {
|
||||
checkbox.checked = this.checked;
|
||||
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;
|
||||
}
|
||||
});
|
||||
|
||||
updateProceedButton();
|
||||
});
|
||||
|
||||
checkboxes.forEach(checkbox => {
|
||||
checkbox.addEventListener('change', function() {
|
||||
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();
|
||||
});
|
||||
});
|
||||
@@ -421,12 +552,11 @@ error_log("Loaded template: " . print_r($template, true));
|
||||
|
||||
function applyColumnFilters() {
|
||||
rows.forEach(row => {
|
||||
// Le celle di data partono da index 1 (perché index 0 è checkbox)
|
||||
let visible = true;
|
||||
|
||||
for (const [colIndexStr, filterValue] of Object.entries(activeFilters)) {
|
||||
const colIndex = parseInt(colIndexStr, 10); // 0..N-1 sulle colonne dati
|
||||
const cell = row.cells[colIndex + 1]; // +1 per saltare la colonna checkbox
|
||||
const colIndex = parseInt(colIndexStr, 10);
|
||||
const cell = row.cells[colIndex + 1];
|
||||
|
||||
const cellText = (cell?.textContent || '').toLowerCase();
|
||||
const searchText = (filterValue || '').toLowerCase().trim();
|
||||
@@ -439,6 +569,17 @@ error_log("Loaded template: " . print_r($template, true));
|
||||
|
||||
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 => {
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -10,6 +10,9 @@ error_reporting(E_ALL | E_STRICT);
|
||||
include('../../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
|
||||
// logged in, and in that case we redirect
|
||||
// the user to vanguard login page.
|
||||
@@ -26,6 +29,8 @@ $nameuser = $user->present()->first_name;
|
||||
$surnameuser = $user->present()->last_name;
|
||||
$emailuser = $user->present()->email;
|
||||
$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;
|
||||
|
||||
|
||||
@@ -22,7 +22,7 @@
|
||||
<ul>
|
||||
<!-- <li> <a href="index.php"><i class='bx bx-radio-circle'></i>Default</a>
|
||||
</li> -->
|
||||
<li> <a href="import_dashboard.php"><i class='bx bx-radio-circle'></i>XLS Import</a>
|
||||
<li> <a href="import_dashboard.php"><i class='bx bx-radio-circle'></i>Import AREA</a>
|
||||
</li>
|
||||
|
||||
|
||||
@@ -57,8 +57,14 @@
|
||||
</li>
|
||||
|
||||
|
||||
|
||||
|
||||
<li class="menu-label">Reports</li>
|
||||
<li>
|
||||
<a href="rapporti_cliente_lookup.php" target="">
|
||||
<div class="parent-icon"><i class="bx bx-file-find"></i>
|
||||
</div>
|
||||
<div class="menu-title">Ricerca Reports</div>
|
||||
</a>
|
||||
</li>
|
||||
|
||||
<li class="menu-label">Others</li>
|
||||
|
||||
|
||||
@@ -1,11 +1,22 @@
|
||||
<?php include('include/headscript.php');
|
||||
|
||||
// Recupera tutte le routine dal database
|
||||
// Retrieve all routines from database
|
||||
$db = DBHandlerSelect::getInstance();
|
||||
$pdo = $db->getConnection();
|
||||
|
||||
$stmt = $pdo->prepare("SELECT * FROM routine");
|
||||
$stmt->execute();
|
||||
$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>
|
||||
<html lang="en">
|
||||
@@ -18,26 +29,30 @@ $routines = $stmt->fetchAll(PDO::FETCH_ASSOC);
|
||||
<?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://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>
|
||||
|
||||
<body>
|
||||
<div class="wrapper">
|
||||
<?php include('include/navbar.php'); ?>
|
||||
<?php include('include/topbar.php'); ?>
|
||||
|
||||
<div class="page-wrapper">
|
||||
<div class="page-content">
|
||||
|
||||
<div class="card mb-4">
|
||||
<div class="card-header">
|
||||
<h5 class="mb-0">Insert new XLS Template</h5>
|
||||
<h5 class="mb-0">Insert New Template</h5>
|
||||
</div>
|
||||
<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>
|
||||
<ul class="mb-0">
|
||||
<li>Template Name</li>
|
||||
<li>Row Header and Column Header: where the title of the excel starts</li>
|
||||
<li>Schema and client</li>
|
||||
<li>Source Type</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>
|
||||
</div>
|
||||
</div>
|
||||
@@ -50,22 +65,86 @@ $routines = $stmt->fetchAll(PDO::FETCH_ASSOC);
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="card-body">
|
||||
<div class="col-12">
|
||||
<form id="insertTemplateForm" method="POST">
|
||||
|
||||
<div class="mb-3">
|
||||
<label class="form-label"><?= htmlspecialchars($templatename, ENT_QUOTES, 'UTF-8'); ?> *</label>
|
||||
<input type="text" name="name" class="form-control" required>
|
||||
</div>
|
||||
|
||||
<div class="mb-3">
|
||||
<label class="form-label"><?= htmlspecialchars($rowheader, ENT_QUOTES, 'UTF-8'); ?> *</label>
|
||||
<input type="number" name="header_row" class="form-control" required>
|
||||
<label class="form-label">Source Type *</label>
|
||||
<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 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>
|
||||
<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 class="mb-3">
|
||||
@@ -86,12 +165,12 @@ $routines = $stmt->fetchAll(PDO::FETCH_ASSOC);
|
||||
|
||||
<div class="mb-3">
|
||||
<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 class="mb-3">
|
||||
<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 class="mb-3">
|
||||
@@ -125,6 +204,7 @@ $routines = $stmt->fetchAll(PDO::FETCH_ASSOC);
|
||||
</option>
|
||||
<?php endforeach; ?>
|
||||
</select>
|
||||
|
||||
<div id="routineDetails" class="mt-2" style="display: none;">
|
||||
<h6>Routine Details</h6>
|
||||
<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 class="overlay toggle-icon"></div>
|
||||
<a href="javaScript:;" class="back-to-top"><i class='bx bxs-up-arrow-alt'></i></a>
|
||||
<?php include('include/footer.php'); ?>
|
||||
@@ -167,6 +249,18 @@ $routines = $stmt->fetchAll(PDO::FETCH_ASSOC);
|
||||
const routineAction2 = document.getElementById("routineAction2");
|
||||
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) {
|
||||
alert("Errore: Uno o più elementi della pagina non sono stati trovati. Contatta l'amministratore.");
|
||||
return;
|
||||
@@ -187,30 +281,101 @@ $routines = $stmt->fetchAll(PDO::FETCH_ASSOC);
|
||||
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() {
|
||||
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) 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");
|
||||
select.innerHTML = '<option value="">Select a client...</option>';
|
||||
|
||||
data.value.forEach(client => {
|
||||
const nome = client.Nominativo || "Nome 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).trigger('change');
|
||||
clientLoadingStatus.textContent = "Clienti caricati.";
|
||||
} catch (error) {
|
||||
clientLoadingStatus.textContent = "Errore nel caricamento.";
|
||||
|
||||
Swal.fire({
|
||||
title: "Errore!",
|
||||
text: "Impossibile caricare i clienti: " + error.message,
|
||||
@@ -226,26 +391,43 @@ $routines = $stmt->fetchAll(PDO::FETCH_ASSOC);
|
||||
try {
|
||||
schemaLoadingStatus.style.display = 'inline';
|
||||
schemaLoadingStatus.textContent = 'Caricamento schemi in corso...';
|
||||
|
||||
const response = await fetch("get_schemi.php", {
|
||||
method: "GET",
|
||||
headers: {
|
||||
"Content-Type": "application/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");
|
||||
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 id = schema.IdSchemaCustomFields || "ID non disponibile";
|
||||
const option = new Option(`${nome.trim()} (ID: ${id})`, id);
|
||||
select.add(option);
|
||||
});
|
||||
|
||||
$(select).trigger('change');
|
||||
schemaLoadingStatus.textContent = "Schemi caricati.";
|
||||
} catch (error) {
|
||||
schemaLoadingStatus.textContent = "Errore nel caricamento.";
|
||||
|
||||
Swal.fire({
|
||||
title: "Errore!",
|
||||
text: "Impossibile caricare gli schemi: " + error.message,
|
||||
@@ -270,6 +452,7 @@ $routines = $stmt->fetchAll(PDO::FETCH_ASSOC);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
loadData();
|
||||
|
||||
const routines = <?php echo json_encode($routines); ?>;
|
||||
@@ -277,8 +460,10 @@ $routines = $stmt->fetchAll(PDO::FETCH_ASSOC);
|
||||
function updateRoutineDetails() {
|
||||
const selectedId = routineSelect.value;
|
||||
routineDetails.style.display = selectedId ? 'block' : 'none';
|
||||
|
||||
if (selectedId) {
|
||||
const routine = routines.find(r => r.idroutine == selectedId);
|
||||
|
||||
if (routine) {
|
||||
routineName.textContent = routine.name || 'N/A';
|
||||
routineDescription.textContent = routine.description || 'N/A';
|
||||
@@ -300,6 +485,7 @@ $routines = $stmt->fetchAll(PDO::FETCH_ASSOC);
|
||||
routineAction3.textContent = '';
|
||||
}
|
||||
}
|
||||
|
||||
routineSelect.addEventListener('change', updateRoutineDetails);
|
||||
updateRoutineDetails();
|
||||
|
||||
@@ -308,6 +494,28 @@ $routines = $stmt->fetchAll(PDO::FETCH_ASSOC);
|
||||
|
||||
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 clientId = clientSelect.value;
|
||||
const selectedClientOption = clientSelect.options[clientSelect.selectedIndex];
|
||||
@@ -350,6 +558,7 @@ $routines = $stmt->fetchAll(PDO::FETCH_ASSOC);
|
||||
const nameMatch = optionText.match(/^(.+?)(?:\s*\(ID:\s*\d+\))?$/);
|
||||
schemaName = nameMatch ? nameMatch[1].trim() : optionText;
|
||||
}
|
||||
|
||||
formData.append("idschema", schemaId);
|
||||
formData.append("schemaname", schemaName);
|
||||
|
||||
|
||||
@@ -13,7 +13,10 @@ try {
|
||||
}
|
||||
|
||||
// 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);
|
||||
|
||||
$response["success"] = true;
|
||||
|
||||
@@ -14,11 +14,57 @@ if (!$iddatadb) {
|
||||
}
|
||||
|
||||
try {
|
||||
$stmt = $pdo->prepare("SELECT id, iddatadb, part_number, part_description, idmatrice, note, dateexpiry FROM identification_parts WHERE iddatadb = :iddatadb ORDER BY part_number ASC");
|
||||
$stmt->execute([':iddatadb' => $iddatadb]);
|
||||
// 1) prendo templateid da datadb
|
||||
$stmtTpl = $pdo->prepare("SELECT templateid FROM datadb WHERE iddatadb = :iddatadb LIMIT 1");
|
||||
$stmtTpl->execute([':iddatadb' => $iddatadb]);
|
||||
$templateid = $stmtTpl->fetchColumn();
|
||||
|
||||
// 2) prendo (max 1) field_id visibile in parts
|
||||
$extraFieldId = null;
|
||||
if ($templateid) {
|
||||
$stmtEF = $pdo->prepare("SELECT field_id FROM template_mapping WHERE template_id = :templateid AND is_visible_parts = 1 ORDER BY id ASC LIMIT 1");
|
||||
$stmtEF->execute([':templateid' => $templateid]);
|
||||
$extraFieldId = $stmtEF->fetchColumn();
|
||||
if ($extraFieldId !== false) $extraFieldId = (int)$extraFieldId;
|
||||
else $extraFieldId = null;
|
||||
}
|
||||
|
||||
// 3) carico parts + join su tabella figlia
|
||||
if ($extraFieldId) {
|
||||
$stmt = $pdo->prepare("
|
||||
SELECT
|
||||
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_text AS extra_value_text
|
||||
FROM identification_parts p
|
||||
LEFT JOIN identification_parts_customfields cf
|
||||
ON cf.part_id = p.id AND cf.field_id = :extraFieldId
|
||||
WHERE p.iddatadb = :iddatadb
|
||||
ORDER BY p.part_number ASC
|
||||
");
|
||||
$stmt->execute([
|
||||
':iddatadb' => $iddatadb,
|
||||
':extraFieldId' => $extraFieldId
|
||||
]);
|
||||
} else {
|
||||
$stmt = $pdo->prepare("
|
||||
SELECT id, iddatadb, part_number, part_description, mix, idmatrice, note, dateexpiry,
|
||||
NULL AS extra_value_id, NULL AS extra_value_text
|
||||
FROM identification_parts
|
||||
WHERE iddatadb = :iddatadb
|
||||
ORDER BY part_number ASC
|
||||
");
|
||||
$stmt->execute([':iddatadb' => $iddatadb]);
|
||||
}
|
||||
|
||||
$parts = $stmt->fetchAll(PDO::FETCH_ASSOC);
|
||||
|
||||
echo json_encode(['success' => true, 'parts' => $parts]);
|
||||
echo json_encode([
|
||||
'success' => true,
|
||||
'parts' => $parts,
|
||||
'extra_field_id' => $extraFieldId,
|
||||
'debug_sql' => ($extraFieldId ? 'WITH_CF_JOIN' : 'NO_CF')
|
||||
]);
|
||||
} catch (PDOException $e) {
|
||||
echo json_encode(['success' => false, 'message' => 'Errore nel caricamento: ' . $e->getMessage()]);
|
||||
}
|
||||
|
||||
@@ -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": 563528,
|
||||
"Matrice": 3879,
|
||||
"SottoMatrice": null,
|
||||
"SchemaCustomField": 82,
|
||||
"NoteWeb": "Parte AAA",
|
||||
"ConsegnaRichiesta": "2026-01-30"
|
||||
}'
|
||||
|
||||
RESPONSE:
|
||||
{
|
||||
"@odata.context": "https:\/\/bvcpsitaly-elims.com\/limsapi\/api\/odata\/$metadata#Campione\/$entity",
|
||||
"IdCampione": 734582,
|
||||
"CodiceCampione": "10978",
|
||||
"CodiceCampioneWeb": "10978",
|
||||
"StatoCampione": "Nuovo",
|
||||
"DataCreazioneWeb": "2026-03-11T14:10:02.1595894+01:00",
|
||||
"RiferimentoWeb": "",
|
||||
"ConsegnaRichiesta": "2026-01-30T00:00:00+01: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": 563528,
|
||||
"Matrice": 3879,
|
||||
"SottoMatrice": null,
|
||||
"SchemaCustomField": 82,
|
||||
"NoteWeb": "PARE BBB",
|
||||
"ConsegnaRichiesta": "2026-01-30"
|
||||
}'
|
||||
|
||||
RESPONSE:
|
||||
{
|
||||
"@odata.context": "https:\/\/bvcpsitaly-elims.com\/limsapi\/api\/odata\/$metadata#Campione\/$entity",
|
||||
"IdCampione": 734583,
|
||||
"CodiceCampione": "10979",
|
||||
"CodiceCampioneWeb": "10979",
|
||||
"StatoCampione": "Nuovo",
|
||||
"DataCreazioneWeb": "2026-03-11T14:10:03.9972635+01:00",
|
||||
"RiferimentoWeb": "",
|
||||
"ConsegnaRichiesta": "2026-01-30T00:00:00+01:00",
|
||||
"DataAccettazioneLims": null,
|
||||
"Riferimento": null,
|
||||
"NoteWeb": "PARE BBB",
|
||||
"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(563528)/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": 563528,
|
||||
"CodiceCommessa": "26C0029",
|
||||
"RiferimentoCertificato": null,
|
||||
"StatoCommessaWeb": "Elaborata",
|
||||
"DataCreazioneWeb": "2026-03-11T14:10:00.897+01:00",
|
||||
"CodiceCommessaWeb": "26C0029",
|
||||
"DataInviatoWeb": "2026-03-11T14:10:11.76+01:00",
|
||||
"Richiedente": "Test Web Import",
|
||||
"Descrizione": "TEST CommessaWeb"
|
||||
}
|
||||
@@ -0,0 +1,44 @@
|
||||
Photos for CommessaWeb 563528 (iddatadb=1259):
|
||||
Total photos found: 2, campioni: 2
|
||||
|
||||
=== Campione 734582 (main) ===
|
||||
curl --location --request POST 'https://93.43.5.102/limsapi/api/odata/Campione(734582)/UploadCampioneFile' \
|
||||
--header 'Authorization: Bearer ••••••' \
|
||||
--form 'file=@C:\xampp\htdocs\trf_certest\public\photostrf\1259-20260311130930-104a2ca5-8a4c-4df8-b2ca-a28f518b7b25.jpg'
|
||||
|
||||
RESPONSE:
|
||||
{
|
||||
"@odata.context": "https:\/\/bvcpsitaly-elims.com\/limsapi\/api\/odata\/$metadata#CampioneFiles\/$entity",
|
||||
"IdCampioneFile": 1779561,
|
||||
"FileName": "1259-20260311130930-104a2ca5-8a4c-4df8-b2ca-a28f518b7b25.jpg",
|
||||
"Web": false,
|
||||
"AllegaAlRapporto": false,
|
||||
"StampaNelRapporto": false,
|
||||
"ReportGermania": false,
|
||||
"PrimaPagina": false,
|
||||
"Duplica": false,
|
||||
"LastUpdate": "2026-03-11T14:10:06.7051549+01:00",
|
||||
"Titolo": null
|
||||
}
|
||||
|
||||
---
|
||||
curl --location --request POST 'https://93.43.5.102/limsapi/api/odata/Campione(734582)/UploadCampioneFile' \
|
||||
--header 'Authorization: Bearer ••••••' \
|
||||
--form 'file=@C:\xampp\htdocs\trf_certest\public\photostrf\1259-20260311130930-1e672dd9-5420-4432-b422-02d8d271c178.jpg'
|
||||
|
||||
RESPONSE:
|
||||
{
|
||||
"@odata.context": "https:\/\/bvcpsitaly-elims.com\/limsapi\/api\/odata\/$metadata#CampioneFiles\/$entity",
|
||||
"IdCampioneFile": 1779562,
|
||||
"FileName": "1259-20260311130930-1e672dd9-5420-4432-b422-02d8d271c178.jpg",
|
||||
"Web": false,
|
||||
"AllegaAlRapporto": false,
|
||||
"StampaNelRapporto": false,
|
||||
"ReportGermania": false,
|
||||
"PrimaPagina": false,
|
||||
"Duplica": false,
|
||||
"LastUpdate": "2026-03-11T14:10:08.5442313+01:00",
|
||||
"Titolo": null
|
||||
}
|
||||
|
||||
---
|
||||
@@ -0,0 +1,18 @@
|
||||
curl --location --request POST 'https://93.43.5.102/limsapi/api/odata/CommessaWeb(563528)/InviaCommessa' \
|
||||
--header 'Content-Type: application/json' \
|
||||
--header 'Authorization: Bearer ••••••' \
|
||||
--data '{}'
|
||||
|
||||
RESPONSE:
|
||||
{
|
||||
"@odata.context": "https:\/\/bvcpsitaly-elims.com\/limsapi\/api\/odata\/$metadata#CommessaWeb\/$entity",
|
||||
"IdCommessa": 563528,
|
||||
"CodiceCommessa": "26C0029",
|
||||
"RiferimentoCertificato": null,
|
||||
"StatoCommessaWeb": "Nuova",
|
||||
"DataCreazioneWeb": "2026-03-11T14:10:00.897+01:00",
|
||||
"CodiceCommessaWeb": "26C0029",
|
||||
"DataInviatoWeb": "2026-03-11T14:10:11.7602299+01:00",
|
||||
"Richiedente": "Test Web Import",
|
||||
"Descrizione": "TEST CommessaWeb"
|
||||
}
|
||||
@@ -0,0 +1,226 @@
|
||||
curl --location --request PATCH 'https://93.43.5.102/limsapi/api/odata/CommessaWeb(563528)' \
|
||||
--header 'Content-Type: application/json' \
|
||||
--header 'Authorization: Bearer ••••••' \
|
||||
--data '{
|
||||
"CommesseCustomFields": [
|
||||
{
|
||||
"IdCommesseCustomFields": 23254249,
|
||||
"Valore": ""
|
||||
},
|
||||
{
|
||||
"IdCommesseCustomFields": 23254250,
|
||||
"Valore": ""
|
||||
},
|
||||
{
|
||||
"IdCommesseCustomFields": 23254251,
|
||||
"Valore": ""
|
||||
},
|
||||
{
|
||||
"IdCommesseCustomFields": 23254252,
|
||||
"Valore": ""
|
||||
},
|
||||
{
|
||||
"IdCommesseCustomFields": 23254253,
|
||||
"Valore": ""
|
||||
},
|
||||
{
|
||||
"IdCommesseCustomFields": 23254254,
|
||||
"Valore": ""
|
||||
},
|
||||
{
|
||||
"IdCommesseCustomFields": 23254260,
|
||||
"Valore": ""
|
||||
},
|
||||
{
|
||||
"IdCommesseCustomFields": 23254261,
|
||||
"Valore": ""
|
||||
},
|
||||
{
|
||||
"IdCommesseCustomFields": 23254262,
|
||||
"Valore": ""
|
||||
},
|
||||
{
|
||||
"IdCommesseCustomFields": 23254264,
|
||||
"Valore": ""
|
||||
},
|
||||
{
|
||||
"IdCommesseCustomFields": 23254265,
|
||||
"Valore": ""
|
||||
},
|
||||
{
|
||||
"IdCommesseCustomFields": 23254266,
|
||||
"Valore": ""
|
||||
},
|
||||
{
|
||||
"IdCommesseCustomFields": 23254267,
|
||||
"Valore": ""
|
||||
},
|
||||
{
|
||||
"IdCommesseCustomFields": 23254268,
|
||||
"Valore": ""
|
||||
},
|
||||
{
|
||||
"IdCommesseCustomFields": 23254228,
|
||||
"Valore": "13526"
|
||||
},
|
||||
{
|
||||
"IdCommesseCustomFields": 23254229,
|
||||
"Valore": "20262"
|
||||
},
|
||||
{
|
||||
"IdCommesseCustomFields": 23254230,
|
||||
"Valore": "ART. PEACH"
|
||||
},
|
||||
{
|
||||
"IdCommesseCustomFields": 23254269,
|
||||
"Valore": ""
|
||||
},
|
||||
{
|
||||
"IdCommesseCustomFields": 23254270,
|
||||
"Valore": ""
|
||||
},
|
||||
{
|
||||
"IdCommesseCustomFields": 23254271,
|
||||
"Valore": ""
|
||||
},
|
||||
{
|
||||
"IdCommesseCustomFields": 23254272,
|
||||
"Valore": ""
|
||||
},
|
||||
{
|
||||
"IdCommesseCustomFields": 23254273,
|
||||
"Valore": ""
|
||||
},
|
||||
{
|
||||
"IdCommesseCustomFields": 23254274,
|
||||
"Valore": ""
|
||||
},
|
||||
{
|
||||
"IdCommesseCustomFields": 23254275,
|
||||
"Valore": ""
|
||||
},
|
||||
{
|
||||
"IdCommesseCustomFields": 23254276,
|
||||
"Valore": ""
|
||||
},
|
||||
{
|
||||
"IdCommesseCustomFields": 23254277,
|
||||
"Valore": ""
|
||||
},
|
||||
{
|
||||
"IdCommesseCustomFields": 23254278,
|
||||
"Valore": ""
|
||||
},
|
||||
{
|
||||
"IdCommesseCustomFields": 23254279,
|
||||
"Valore": ""
|
||||
},
|
||||
{
|
||||
"IdCommesseCustomFields": 23254280,
|
||||
"Valore": ""
|
||||
},
|
||||
{
|
||||
"IdCommesseCustomFields": 23254281,
|
||||
"Valore": ""
|
||||
},
|
||||
{
|
||||
"IdCommesseCustomFields": 23254231,
|
||||
"Valore": "L209A4M00130M7280"
|
||||
},
|
||||
{
|
||||
"IdCommesseCustomFields": 23254232,
|
||||
"Valore": "BLACK"
|
||||
},
|
||||
{
|
||||
"IdCommesseCustomFields": 23254233,
|
||||
"Valore": "PE007J"
|
||||
},
|
||||
{
|
||||
"IdCommesseCustomFields": 23254234,
|
||||
"Valore": "262"
|
||||
},
|
||||
{
|
||||
"IdCommesseCustomFields": 23254235,
|
||||
"Valore": ""
|
||||
},
|
||||
{
|
||||
"IdCommesseCustomFields": 23254236,
|
||||
"Valore": ""
|
||||
},
|
||||
{
|
||||
"IdCommesseCustomFields": 23254237,
|
||||
"Valore": ""
|
||||
},
|
||||
{
|
||||
"IdCommesseCustomFields": 23254238,
|
||||
"Valore": ""
|
||||
},
|
||||
{
|
||||
"IdCommesseCustomFields": 23254239,
|
||||
"Valore": ""
|
||||
},
|
||||
{
|
||||
"IdCommesseCustomFields": 23254240,
|
||||
"Valore": "Oggi"
|
||||
},
|
||||
{
|
||||
"IdCommesseCustomFields": 23254241,
|
||||
"Valore": ""
|
||||
},
|
||||
{
|
||||
"IdCommesseCustomFields": 23254242,
|
||||
"Valore": "MONCLER"
|
||||
},
|
||||
{
|
||||
"IdCommesseCustomFields": 23254243,
|
||||
"Valore": "236"
|
||||
},
|
||||
{
|
||||
"IdCommesseCustomFields": 23254244,
|
||||
"Valore": ""
|
||||
},
|
||||
{
|
||||
"IdCommesseCustomFields": 23254245,
|
||||
"Valore": "BBB"
|
||||
},
|
||||
{
|
||||
"IdCommesseCustomFields": 23254246,
|
||||
"Valore": "solocla"
|
||||
},
|
||||
{
|
||||
"IdCommesseCustomFields": 23254247,
|
||||
"Valore": ""
|
||||
},
|
||||
{
|
||||
"IdCommesseCustomFields": 23254248,
|
||||
"Valore": ""
|
||||
},
|
||||
{
|
||||
"IdCommesseCustomFields": 23254255,
|
||||
"Valore": ""
|
||||
},
|
||||
{
|
||||
"IdCommesseCustomFields": 23254256,
|
||||
"Valore": "MONCLER"
|
||||
},
|
||||
{
|
||||
"IdCommesseCustomFields": 23254257,
|
||||
"Valore": ""
|
||||
},
|
||||
{
|
||||
"IdCommesseCustomFields": 23254258,
|
||||
"Valore": ""
|
||||
},
|
||||
{
|
||||
"IdCommesseCustomFields": 23254259,
|
||||
"Valore": ""
|
||||
},
|
||||
{
|
||||
"IdCommesseCustomFields": 23254263,
|
||||
"Valore": ""
|
||||
}
|
||||
]
|
||||
}'
|
||||
|
||||
RESPONSE:
|
||||
null
|
||||
@@ -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,40 @@
|
||||
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": 95833,
|
||||
"Matrice": 8413,
|
||||
"SottoMatrice": null,
|
||||
"SchemaCustomField": 82,
|
||||
"NoteWeb": "aaa"
|
||||
}'
|
||||
|
||||
RESPONSE:
|
||||
{
|
||||
"IdCampione": 14445,
|
||||
"Commessa": 95833,
|
||||
"Matrice": 8413
|
||||
}
|
||||
|
||||
---
|
||||
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": 95833,
|
||||
"Matrice": 3879,
|
||||
"SottoMatrice": null,
|
||||
"SchemaCustomField": 82,
|
||||
"NoteWeb": "bbb"
|
||||
}'
|
||||
|
||||
RESPONSE:
|
||||
{
|
||||
"IdCampione": 15750,
|
||||
"Commessa": 95833,
|
||||
"Matrice": 3879
|
||||
}
|
||||
|
||||
---
|
||||
@@ -0,0 +1,9 @@
|
||||
curl --location --request GET 'https://93.43.5.102/limsapi/api/odata/CommessaWeb(95833)?$expand=CommesseCustomFields($expand=CustomField)' \
|
||||
--header 'Authorization: Bearer ••••••'
|
||||
|
||||
RESPONSE:
|
||||
{
|
||||
"IdCommessa": 95833,
|
||||
"CodiceCommessa": "SIM-95833",
|
||||
"CommesseCustomFields": []
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
curl --location --request GET 'https://93.43.5.102/limsapi/api/odata/CommessaWeb(95833)?$expand=CommesseCustomFields($expand=CustomField)' \
|
||||
--header 'Authorization: Bearer ••••••'
|
||||
|
||||
RESPONSE:
|
||||
{
|
||||
"IdCommessa": 95833,
|
||||
"CodiceCommessa": "SIM-95833",
|
||||
"CommesseCustomFields": []
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
curl --location --request POST 'https://93.43.5.102/limsapi/api/odata/CommessaWeb(95833)/ImportaCommessa' \
|
||||
--header 'Content-Type: application/json' \
|
||||
--header 'Authorization: Bearer ••••••' \
|
||||
--data '{}'
|
||||
|
||||
RESPONSE:
|
||||
{
|
||||
"simulated": true,
|
||||
"endpoint": "CommessaWeb(95833)\/ImportaCommessa"
|
||||
}
|
||||
@@ -0,0 +1,15 @@
|
||||
Photos for CommessaWeb 95833 (iddatadb=1237):
|
||||
Total photos found: 1
|
||||
|
||||
curl --location --request POST 'https://93.43.5.102/limsapi/api/odata/AllegatoCommessaWeb' \
|
||||
--header 'Authorization: Bearer ••••••' \
|
||||
--form 'IdCommessa=95833' \
|
||||
--form 'file=@C:\xampp\htdocs\trf_certest\public\photostrf\1237-20260228192737-Blue and White Simple Daily Vlogger YouTube Banner (15) (1) (1).png'
|
||||
|
||||
RESPONSE:
|
||||
{
|
||||
"simulated": true,
|
||||
"file": "1237-20260228192737-Blue and White Simple Daily Vlogger YouTube Banner (15) (1) (1).png"
|
||||
}
|
||||
|
||||
---
|
||||
@@ -0,0 +1,22 @@
|
||||
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": "Test Web Import",
|
||||
"Descrizione": "TEST CommessaWeb",
|
||||
"ClienteResponsabile": 3,
|
||||
"MoltiplicatorePrezzo": 1,
|
||||
"AnagraficaCertestObject": 2,
|
||||
"AnagraficaCertestService": 1,
|
||||
"ClienteFornitore": null
|
||||
}'
|
||||
|
||||
RESPONSE:
|
||||
{
|
||||
"IdCommessa": 95833,
|
||||
"CodiceCommessa": "SIM-95833",
|
||||
"Richiedente": "Test Web Import",
|
||||
"Descrizione": "TEST CommessaWeb"
|
||||
}
|
||||
@@ -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": "Test Web Import",
|
||||
"Descrizione": "TEST CommessaWeb",
|
||||
"ClienteResponsabile": 7586,
|
||||
"MoltiplicatorePrezzo": 3,
|
||||
"AnagraficaCertestObject": 8963,
|
||||
"AnagraficaCertestService": 9007,
|
||||
"ClienteFornitore": null
|
||||
}'
|
||||
|
||||
RESPONSE:
|
||||
{
|
||||
"@odata.context": "https:\/\/bvcpsitaly-elims.com\/limsapi\/api\/odata\/$metadata#CommessaWeb\/$entity",
|
||||
"IdCommessa": 562479,
|
||||
"CodiceCommessa": "26C0013",
|
||||
"RiferimentoCertificato": null,
|
||||
"StatoCommessaWeb": "Nuova",
|
||||
"DataCreazioneWeb": "2026-03-06T09:09:16.9338074+01:00",
|
||||
"CodiceCommessaWeb": "26C0013",
|
||||
"DataInviatoWeb": null,
|
||||
"Richiedente": "Test Web Import",
|
||||
"Descrizione": "TEST CommessaWeb"
|
||||
}
|
||||
@@ -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": "Test Web Import",
|
||||
"Descrizione": "TEST CommessaWeb",
|
||||
"ClienteResponsabile": 10653,
|
||||
"MoltiplicatorePrezzo": 1,
|
||||
"AnagraficaCertestObject": 8960,
|
||||
"AnagraficaCertestService": 9010,
|
||||
"ClienteFornitore": null
|
||||
}'
|
||||
|
||||
RESPONSE:
|
||||
{
|
||||
"@odata.context": "https:\/\/bvcpsitaly-elims.com\/limsapi\/api\/odata\/$metadata#CommessaWeb\/$entity",
|
||||
"IdCommessa": 563528,
|
||||
"CodiceCommessa": "26C0029",
|
||||
"RiferimentoCertificato": null,
|
||||
"StatoCommessaWeb": "Nuova",
|
||||
"DataCreazioneWeb": "2026-03-11T14:10:00.8960358+01:00",
|
||||
"CodiceCommessaWeb": "26C0029",
|
||||
"DataInviatoWeb": null,
|
||||
"Richiedente": "Test Web Import",
|
||||
"Descrizione": "TEST CommessaWeb"
|
||||
}
|
||||
@@ -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
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user