Compare commits
64 Commits
feature/im
...
main
| Author | SHA1 | Date | |
|---|---|---|---|
| 813bd66f96 | |||
| ce00247d1c | |||
| 3a7dd266c8 | |||
| ba8dc4c721 | |||
| cfbbc36116 | |||
| 50d578eea1 | |||
| 6b0d2aa9b9 | |||
| fa7997c980 | |||
| 66be442eb6 | |||
| 19a2d6e3f7 | |||
| a15ab08576 | |||
| f71e8a56b5 | |||
| cb38bfb75a | |||
| 28a708dad3 | |||
| 6b9cf20ab9 | |||
| d40fc7d177 | |||
| b812563023 | |||
| 39a821357e | |||
| 53c223ea5f | |||
| 9775a12d4a | |||
| 7dfb935e33 | |||
| abb4200215 | |||
| 0be7109df4 | |||
| 578671e013 | |||
| d24836e2b1 | |||
| d983659000 | |||
| 7c5aa7734f | |||
| 4f0dbc7e91 | |||
| fb09f033ae | |||
| 8bae2d7008 | |||
| 7463fc6726 | |||
| 0bc2ff7e9d | |||
| aa1c32b7ed | |||
| c573a46318 | |||
| bf18a904bd | |||
| b3ce489348 | |||
| 08b89e01cc | |||
| 3e66d67dc5 | |||
| 490786731a | |||
| e96f538a47 | |||
| cc96ecb67f | |||
| 4cf03ae742 | |||
| 7a486e9dcf | |||
| 2a7b1fae17 | |||
| a05d9fed2b | |||
| 65170a0a7c | |||
| 223688c372 | |||
| b562eb4033 | |||
| 0645a0c675 | |||
| a02a6b2c4c | |||
| 45dd8d6907 | |||
| 755f6812d4 | |||
| 5e59ae2162 | |||
| 71a19144c8 | |||
| 381a05341b | |||
| 5a58decd40 | |||
| eb21910ef3 | |||
| 5dedc779df | |||
| f4e0074a73 | |||
| 9c850e4ea6 | |||
| f300811341 | |||
| 48387a9945 | |||
| 0e90db8219 | |||
| eaf70d5a46 |
11
.gitignore
vendored
11
.gitignore
vendored
@ -33,15 +33,20 @@ yarn-error.log
|
|||||||
/public/userarea/*.log
|
/public/userarea/*.log
|
||||||
/public/userarea/*.txt
|
/public/userarea/*.txt
|
||||||
/public/userarea/*_response.json
|
/public/userarea/*_response.json
|
||||||
|
/public/userarea/customfield_values_response.json
|
||||||
/public/userarea/error_log.txt
|
/public/userarea/error_log.txt
|
||||||
/public/userarea/import_debug.log
|
/public/userarea/import_debug.log
|
||||||
/public/userarea/last_url.txt
|
/public/userarea/last_url.txt
|
||||||
/public/userarea/logaspi/
|
/public/userarea/logaspi/
|
||||||
/public/userarea/logsapi/
|
/public/userarea/logs/api/
|
||||||
|
/public/userarea/logs/api/**
|
||||||
|
/public/userarea/logs/
|
||||||
|
/public/userarea/logs/**
|
||||||
/public/userarea/photostrf/
|
/public/userarea/photostrf/
|
||||||
/public/userarea/class/*.log
|
/public/userarea/class/*.log
|
||||||
/public/userarea/class/curl_auth_debug.log
|
/public/userarea/class/curl_auth_debug.log
|
||||||
/public/userarea/class/curl_request_debug.log
|
/public/userarea/class/curl_request_debug.log
|
||||||
|
/public/userarea/schema_dettagli_response.json
|
||||||
|
|
||||||
# File XLSX temporanei importati
|
# File XLSX temporanei importati
|
||||||
/public/userarea/imported_trf/*.xlsx
|
/public/userarea/imported_trf/*.xlsx
|
||||||
@ -52,4 +57,6 @@ yarn-error.log
|
|||||||
/public/photostrf/qrcodes/
|
/public/photostrf/qrcodes/
|
||||||
|
|
||||||
# Ignora tutti i log ovunque
|
# Ignora tutti i log ovunque
|
||||||
*.log
|
*.log
|
||||||
|
|
||||||
|
public/userarea/cache/
|
||||||
1994
liquibase/changelog/db.changelog-master.yaml
Normal file
1994
liquibase/changelog/db.changelog-master.yaml
Normal file
File diff suppressed because it is too large
Load Diff
6
liquibase/liquibase.properties
Normal file
6
liquibase/liquibase.properties
Normal file
@ -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
|
||||||
73
public/userarea/add_record.php
Normal file
73
public/userarea/add_record.php
Normal file
@ -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()]);
|
||||||
|
}
|
||||||
849
public/userarea/analysisModal.js
Normal file
849
public/userarea/analysisModal.js
Normal file
@ -0,0 +1,849 @@
|
|||||||
|
(function () {
|
||||||
|
"use strict";
|
||||||
|
let analysisMatriciMap = {};
|
||||||
|
let analysisLoadedCache = {};
|
||||||
|
let analysisAssignedState = {};
|
||||||
|
let analysisSelectedState = {};
|
||||||
|
|
||||||
|
function loadAnalysisMatrixNames() {
|
||||||
|
return $.ajax({
|
||||||
|
url: "get_matrici_db.php",
|
||||||
|
method: "GET",
|
||||||
|
dataType: "json",
|
||||||
|
})
|
||||||
|
.done(function (data) {
|
||||||
|
analysisMatriciMap = {};
|
||||||
|
|
||||||
|
(data.value || []).forEach(function (matrice) {
|
||||||
|
analysisMatriciMap[String(matrice.IdMatrice)] =
|
||||||
|
matrice.NomeMatrice || "#" + matrice.IdMatrice;
|
||||||
|
});
|
||||||
|
|
||||||
|
applyAnalysisMatrixNames();
|
||||||
|
})
|
||||||
|
.fail(function () {
|
||||||
|
analysisMatriciMap = {};
|
||||||
|
applyAnalysisMatrixNames();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function applyAnalysisMatrixNames() {
|
||||||
|
const modal = document.getElementById("analysisModal");
|
||||||
|
if (!modal) return;
|
||||||
|
|
||||||
|
modal.querySelectorAll(".analysis-matrix-item").forEach((item) => {
|
||||||
|
const matrixId = item.getAttribute("data-matrix-id");
|
||||||
|
const nameEl = item.querySelector(".analysis-matrix-name");
|
||||||
|
|
||||||
|
let matrixName = "No Matrix";
|
||||||
|
if (matrixId && matrixId !== "NO_MATRIX") {
|
||||||
|
matrixName =
|
||||||
|
analysisMatriciMap[String(matrixId)] || "#" + matrixId;
|
||||||
|
}
|
||||||
|
|
||||||
|
item.setAttribute("data-matrix-name", matrixName);
|
||||||
|
|
||||||
|
if (nameEl) {
|
||||||
|
nameEl.textContent = matrixName;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
modal.querySelectorAll(".analysis-part-matrix-name").forEach((el) => {
|
||||||
|
const matrixId = el.getAttribute("data-matrix-id");
|
||||||
|
|
||||||
|
if (!matrixId || matrixId === "NO_MATRIX") {
|
||||||
|
el.textContent = "No Matrix";
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
el.textContent =
|
||||||
|
analysisMatriciMap[String(matrixId)] || "#" + matrixId;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
function readInitialAssignedAnalyses() {
|
||||||
|
const jsonEl = document.getElementById("analysisAssignedInitialData");
|
||||||
|
if (!jsonEl) {
|
||||||
|
analysisAssignedState = {};
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
analysisAssignedState =
|
||||||
|
JSON.parse(jsonEl.textContent || "{}") || {};
|
||||||
|
} catch (e) {
|
||||||
|
analysisAssignedState = {};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function getSelectedPartIds() {
|
||||||
|
const modal = document.getElementById("analysisModal");
|
||||||
|
if (!modal) return [];
|
||||||
|
|
||||||
|
return Array.from(
|
||||||
|
modal.querySelectorAll(".analysis-part-checkbox:checked"),
|
||||||
|
).map((el) => String(el.value));
|
||||||
|
}
|
||||||
|
|
||||||
|
function getCurrentSelectedAnalysisRecordKeys() {
|
||||||
|
return Object.keys(analysisSelectedState).filter(function (key) {
|
||||||
|
return analysisSelectedState[key] === true;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function renderAssignedAnalysesForPart(partId) {
|
||||||
|
const container = document.getElementById(
|
||||||
|
"analysisAssignedListPart" + partId,
|
||||||
|
);
|
||||||
|
if (!container) return;
|
||||||
|
|
||||||
|
const items = Array.isArray(analysisAssignedState[String(partId)])
|
||||||
|
? analysisAssignedState[String(partId)]
|
||||||
|
: [];
|
||||||
|
|
||||||
|
if (!items.length) {
|
||||||
|
container.innerHTML = "";
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
container.innerHTML = items
|
||||||
|
.map(function (item) {
|
||||||
|
const recordKey = item.analysis_recordkey || "";
|
||||||
|
const title = item.analysis_name || "Unnamed analysis";
|
||||||
|
const method = item.analysis_method || "";
|
||||||
|
|
||||||
|
return `
|
||||||
|
<div class="analysis-assigned-chip" data-recordkey="${escapeHtml(recordKey)}">
|
||||||
|
<div class="analysis-assigned-chip-text">
|
||||||
|
<div class="analysis-assigned-chip-title">${escapeHtml(title)}</div>
|
||||||
|
${method ? `<div class="analysis-assigned-chip-method">${escapeHtml(method)}</div>` : ""}
|
||||||
|
</div>
|
||||||
|
<button type="button"
|
||||||
|
class="analysis-remove-btn"
|
||||||
|
data-part-id="${escapeHtml(partId)}"
|
||||||
|
data-recordkey="${escapeHtml(recordKey)}"
|
||||||
|
title="Remove analysis">×</button>
|
||||||
|
</div>
|
||||||
|
`;
|
||||||
|
})
|
||||||
|
.join("");
|
||||||
|
}
|
||||||
|
|
||||||
|
function renderAssignedAnalysesForSelectedParts() {
|
||||||
|
const modal = document.getElementById("analysisModal");
|
||||||
|
if (!modal) return;
|
||||||
|
|
||||||
|
modal.querySelectorAll(".analysis-part-item").forEach(function (item) {
|
||||||
|
const partId = item.getAttribute("data-part-id");
|
||||||
|
renderAssignedAnalysesForPart(partId);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function syncSelectedAnalysisRows() {
|
||||||
|
const modal = document.getElementById("analysisModal");
|
||||||
|
if (!modal) return;
|
||||||
|
|
||||||
|
modal
|
||||||
|
.querySelectorAll(".analysis-analysis-item")
|
||||||
|
.forEach(function (item) {
|
||||||
|
const recordKey = item.getAttribute("data-recordkey") || "";
|
||||||
|
if (recordKey && analysisSelectedState[recordKey]) {
|
||||||
|
item.classList.add("analysis-selected");
|
||||||
|
} else {
|
||||||
|
item.classList.remove("analysis-selected");
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function buildAnalysisPayloadFromRow(rowEl) {
|
||||||
|
return {
|
||||||
|
analysis_recordkey: rowEl.getAttribute("data-recordkey") || "",
|
||||||
|
analysis_name: rowEl.getAttribute("data-analysis-name") || "",
|
||||||
|
analysis_method: rowEl.getAttribute("data-analysis-method") || "",
|
||||||
|
analysis_level: rowEl.getAttribute("data-analysis-level") || "",
|
||||||
|
is_web_selectable: rowEl.getAttribute("data-web") === "1" ? 1 : 0,
|
||||||
|
is_accredited:
|
||||||
|
rowEl.getAttribute("data-accredited") === "1" ? 1 : 0,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
function addAnalysisToLocalState(partId, payload, iddatadb, idmatrice) {
|
||||||
|
const key = String(partId);
|
||||||
|
if (!Array.isArray(analysisAssignedState[key])) {
|
||||||
|
analysisAssignedState[key] = [];
|
||||||
|
}
|
||||||
|
|
||||||
|
const exists = analysisAssignedState[key].some(function (item) {
|
||||||
|
return item.analysis_recordkey === payload.analysis_recordkey;
|
||||||
|
});
|
||||||
|
|
||||||
|
if (!exists) {
|
||||||
|
analysisAssignedState[key].push({
|
||||||
|
id: null,
|
||||||
|
part_id: parseInt(partId, 10),
|
||||||
|
iddatadb: iddatadb || null,
|
||||||
|
idmatrice: idmatrice || null,
|
||||||
|
analysis_recordkey: payload.analysis_recordkey,
|
||||||
|
analysis_name: payload.analysis_name,
|
||||||
|
analysis_method: payload.analysis_method,
|
||||||
|
analysis_level:
|
||||||
|
payload.analysis_level !== ""
|
||||||
|
? parseInt(payload.analysis_level, 10)
|
||||||
|
: null,
|
||||||
|
is_web_selectable: payload.is_web_selectable,
|
||||||
|
is_accredited: payload.is_accredited,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function removeAnalysisFromLocalState(partId, recordKey) {
|
||||||
|
const key = String(partId);
|
||||||
|
if (!Array.isArray(analysisAssignedState[key])) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
analysisAssignedState[key] = analysisAssignedState[key].filter(
|
||||||
|
function (item) {
|
||||||
|
return item.analysis_recordkey !== recordKey;
|
||||||
|
},
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
function saveAnalysisAssociation(partId, payload, callback) {
|
||||||
|
const modal = document.getElementById("analysisModal");
|
||||||
|
if (!modal) return;
|
||||||
|
|
||||||
|
const iddatadb =
|
||||||
|
modal.querySelector("#analysisModalIddatadb")?.value || "";
|
||||||
|
const partItem = modal.querySelector(
|
||||||
|
'.analysis-part-item[data-part-id="' + partId + '"]',
|
||||||
|
);
|
||||||
|
const idmatrice = partItem
|
||||||
|
? partItem.getAttribute("data-matrix-id") || ""
|
||||||
|
: "";
|
||||||
|
|
||||||
|
$.ajax({
|
||||||
|
url: "save_part_analysis.php",
|
||||||
|
method: "POST",
|
||||||
|
dataType: "json",
|
||||||
|
data: {
|
||||||
|
part_id: partId,
|
||||||
|
iddatadb: iddatadb,
|
||||||
|
idmatrice: idmatrice !== "NO_MATRIX" ? idmatrice : "",
|
||||||
|
analysis_recordkey: payload.analysis_recordkey,
|
||||||
|
analysis_name: payload.analysis_name,
|
||||||
|
analysis_method: payload.analysis_method,
|
||||||
|
analysis_level: payload.analysis_level,
|
||||||
|
is_web_selectable: payload.is_web_selectable,
|
||||||
|
is_accredited: payload.is_accredited,
|
||||||
|
},
|
||||||
|
})
|
||||||
|
.done(function () {
|
||||||
|
addAnalysisToLocalState(
|
||||||
|
partId,
|
||||||
|
payload,
|
||||||
|
iddatadb,
|
||||||
|
idmatrice !== "NO_MATRIX" ? idmatrice : null,
|
||||||
|
);
|
||||||
|
renderAssignedAnalysesForPart(partId);
|
||||||
|
if (typeof callback === "function") callback(true);
|
||||||
|
})
|
||||||
|
.fail(function (xhr) {
|
||||||
|
let message = "Error saving analysis association";
|
||||||
|
if (xhr && xhr.responseJSON && xhr.responseJSON.message) {
|
||||||
|
message = xhr.responseJSON.message;
|
||||||
|
}
|
||||||
|
alert(message);
|
||||||
|
if (typeof callback === "function") callback(false);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function deleteAnalysisAssociation(partId, recordKey, callback) {
|
||||||
|
$.ajax({
|
||||||
|
url: "delete_part_analysis.php",
|
||||||
|
method: "POST",
|
||||||
|
dataType: "json",
|
||||||
|
data: {
|
||||||
|
part_id: partId,
|
||||||
|
analysis_recordkey: recordKey,
|
||||||
|
},
|
||||||
|
})
|
||||||
|
.done(function () {
|
||||||
|
removeAnalysisFromLocalState(partId, recordKey);
|
||||||
|
renderAssignedAnalysesForPart(partId);
|
||||||
|
if (typeof callback === "function") callback(true);
|
||||||
|
})
|
||||||
|
.fail(function (xhr) {
|
||||||
|
let message = "Error deleting analysis association";
|
||||||
|
if (xhr && xhr.responseJSON && xhr.responseJSON.message) {
|
||||||
|
message = xhr.responseJSON.message;
|
||||||
|
}
|
||||||
|
alert(message);
|
||||||
|
if (typeof callback === "function") callback(false);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
function setAnalysisLoadingState(isLoading) {
|
||||||
|
const modal = document.getElementById("analysisModal");
|
||||||
|
if (!modal) return;
|
||||||
|
|
||||||
|
const loadingEl = modal.querySelector("#analysisLoadingBox");
|
||||||
|
if (!loadingEl) return;
|
||||||
|
|
||||||
|
if (isLoading) {
|
||||||
|
loadingEl.classList.remove("d-none");
|
||||||
|
} else {
|
||||||
|
loadingEl.classList.add("d-none");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function showAnalysisError(message) {
|
||||||
|
const modal = document.getElementById("analysisModal");
|
||||||
|
if (!modal) return;
|
||||||
|
|
||||||
|
const errorEl = modal.querySelector("#analysisErrorBox");
|
||||||
|
const emptyEl = modal.querySelector("#analysisEmptyBox");
|
||||||
|
const listEl = modal.querySelector("#analysisList");
|
||||||
|
|
||||||
|
if (listEl) listEl.innerHTML = "";
|
||||||
|
if (emptyEl) emptyEl.classList.add("d-none");
|
||||||
|
|
||||||
|
if (errorEl) {
|
||||||
|
errorEl.textContent = message || "Error loading analyses";
|
||||||
|
errorEl.classList.remove("d-none");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function showAnalysisEmpty(message) {
|
||||||
|
const modal = document.getElementById("analysisModal");
|
||||||
|
if (!modal) return;
|
||||||
|
|
||||||
|
const errorEl = modal.querySelector("#analysisErrorBox");
|
||||||
|
const emptyEl = modal.querySelector("#analysisEmptyBox");
|
||||||
|
const listEl = modal.querySelector("#analysisList");
|
||||||
|
|
||||||
|
if (listEl) listEl.innerHTML = "";
|
||||||
|
if (errorEl) errorEl.classList.add("d-none");
|
||||||
|
|
||||||
|
if (emptyEl) {
|
||||||
|
emptyEl.textContent =
|
||||||
|
message || "No analyses found for this matrix";
|
||||||
|
emptyEl.classList.remove("d-none");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function escapeHtml(value) {
|
||||||
|
return String(value ?? "")
|
||||||
|
.replace(/&/g, "&")
|
||||||
|
.replace(/</g, "<")
|
||||||
|
.replace(/>/g, ">")
|
||||||
|
.replace(/"/g, """)
|
||||||
|
.replace(/'/g, "'");
|
||||||
|
}
|
||||||
|
|
||||||
|
function renderAnalysesList(analyses) {
|
||||||
|
const modal = document.getElementById("analysisModal");
|
||||||
|
if (!modal) return;
|
||||||
|
|
||||||
|
const errorEl = modal.querySelector("#analysisErrorBox");
|
||||||
|
const emptyEl = modal.querySelector("#analysisEmptyBox");
|
||||||
|
const listEl = modal.querySelector("#analysisList");
|
||||||
|
|
||||||
|
if (!listEl) return;
|
||||||
|
|
||||||
|
if (errorEl) errorEl.classList.add("d-none");
|
||||||
|
|
||||||
|
if (!Array.isArray(analyses) || analyses.length === 0) {
|
||||||
|
showAnalysisEmpty("No analyses found for this matrix");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (emptyEl) emptyEl.classList.add("d-none");
|
||||||
|
|
||||||
|
listEl.innerHTML = analyses
|
||||||
|
.map(function (item) {
|
||||||
|
const recordKey = item.RecordKey || "";
|
||||||
|
const analysisName =
|
||||||
|
item.NomeAnalisiTraduzione ||
|
||||||
|
item.NomeAnalisi ||
|
||||||
|
"Unnamed analysis";
|
||||||
|
const methodName = item.MetodoNome || "";
|
||||||
|
const selectable = item.SelezionabileSuWeb === true;
|
||||||
|
const accredited = item.Accreditato === true;
|
||||||
|
const level = item.Livello ?? "";
|
||||||
|
const searchText = (
|
||||||
|
analysisName +
|
||||||
|
" " +
|
||||||
|
methodName
|
||||||
|
).toLowerCase();
|
||||||
|
|
||||||
|
let badges = "";
|
||||||
|
if (selectable) {
|
||||||
|
badges += '<span class="badge bg-success">Web</span>';
|
||||||
|
} else {
|
||||||
|
badges += '<span class="badge bg-secondary">Not web</span>';
|
||||||
|
}
|
||||||
|
|
||||||
|
if (accredited) {
|
||||||
|
badges +=
|
||||||
|
'<span class="badge bg-info text-dark">Accredited</span>';
|
||||||
|
}
|
||||||
|
|
||||||
|
if (level !== "") {
|
||||||
|
badges +=
|
||||||
|
'<span class="badge bg-light text-dark border">Level ' +
|
||||||
|
escapeHtml(level) +
|
||||||
|
"</span>";
|
||||||
|
}
|
||||||
|
|
||||||
|
return `
|
||||||
|
<div
|
||||||
|
class="list-group-item analysis-analysis-item"
|
||||||
|
data-recordkey="${escapeHtml(recordKey)}"
|
||||||
|
data-analysis-name="${escapeHtml(analysisName)}"
|
||||||
|
data-analysis-method="${escapeHtml(methodName)}"
|
||||||
|
data-analysis-level="${escapeHtml(level)}"
|
||||||
|
data-web="${selectable ? "1" : "0"}"
|
||||||
|
data-accredited="${accredited ? "1" : "0"}"
|
||||||
|
data-search="${escapeHtml(searchText)}"
|
||||||
|
>
|
||||||
|
<div class="fw-semibold">${escapeHtml(analysisName)}</div>
|
||||||
|
|
||||||
|
${methodName ? `<div class="analysis-analysis-meta mt-1">${escapeHtml(methodName)}</div>` : ""}
|
||||||
|
|
||||||
|
<div class="analysis-analysis-badges mt-2">
|
||||||
|
${badges}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
`;
|
||||||
|
})
|
||||||
|
.join("");
|
||||||
|
|
||||||
|
filterAnalysisList();
|
||||||
|
syncSelectedAnalysisRows();
|
||||||
|
}
|
||||||
|
|
||||||
|
function filterAnalysisList() {
|
||||||
|
const modal = document.getElementById("analysisModal");
|
||||||
|
if (!modal) return;
|
||||||
|
|
||||||
|
const webOnlyEl = modal.querySelector("#analysisWebOnly");
|
||||||
|
const searchEl = modal.querySelector("#analysisSearchInput");
|
||||||
|
const items = modal.querySelectorAll(".analysis-analysis-item");
|
||||||
|
const emptyEl = modal.querySelector("#analysisEmptyBox");
|
||||||
|
const errorEl = modal.querySelector("#analysisErrorBox");
|
||||||
|
|
||||||
|
const webOnly = webOnlyEl ? webOnlyEl.checked : false;
|
||||||
|
const searchValue = searchEl ? searchEl.value.trim().toLowerCase() : "";
|
||||||
|
|
||||||
|
let visibleCount = 0;
|
||||||
|
|
||||||
|
items.forEach((item) => {
|
||||||
|
const isWeb = item.getAttribute("data-web") === "1";
|
||||||
|
const searchText = (
|
||||||
|
item.getAttribute("data-search") || ""
|
||||||
|
).toLowerCase();
|
||||||
|
|
||||||
|
let visible = true;
|
||||||
|
|
||||||
|
if (webOnly && !isWeb) {
|
||||||
|
visible = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (visible && searchValue && !searchText.includes(searchValue)) {
|
||||||
|
visible = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
item.style.display = visible ? "" : "none";
|
||||||
|
|
||||||
|
if (visible) {
|
||||||
|
visibleCount++;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
if (errorEl) {
|
||||||
|
errorEl.classList.add("d-none");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (emptyEl) {
|
||||||
|
if (items.length === 0) {
|
||||||
|
emptyEl.textContent = "No analyses found for this matrix";
|
||||||
|
emptyEl.classList.remove("d-none");
|
||||||
|
} else if (visibleCount === 0) {
|
||||||
|
emptyEl.textContent = "No analyses match the current filters";
|
||||||
|
emptyEl.classList.remove("d-none");
|
||||||
|
} else {
|
||||||
|
emptyEl.classList.add("d-none");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function loadAnalysesByMatrix(matrixId) {
|
||||||
|
const modal = document.getElementById("analysisModal");
|
||||||
|
if (!modal) return;
|
||||||
|
|
||||||
|
if (!matrixId || matrixId === "NO_MATRIX") {
|
||||||
|
showAnalysisEmpty("No matrix selected");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const listEl = modal.querySelector("#analysisList");
|
||||||
|
const errorEl = modal.querySelector("#analysisErrorBox");
|
||||||
|
const emptyEl = modal.querySelector("#analysisEmptyBox");
|
||||||
|
|
||||||
|
if (listEl) listEl.innerHTML = "";
|
||||||
|
if (errorEl) errorEl.classList.add("d-none");
|
||||||
|
if (emptyEl) {
|
||||||
|
emptyEl.textContent = "";
|
||||||
|
emptyEl.classList.add("d-none");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (analysisLoadedCache[String(matrixId)]) {
|
||||||
|
renderAnalysesList(analysisLoadedCache[String(matrixId)]);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
setAnalysisLoadingState(true);
|
||||||
|
|
||||||
|
$.ajax({
|
||||||
|
url: "get_analisi_matrice_filter.php",
|
||||||
|
method: "GET",
|
||||||
|
dataType: "json",
|
||||||
|
data: {
|
||||||
|
id_matrice: matrixId,
|
||||||
|
},
|
||||||
|
})
|
||||||
|
.done(function (response) {
|
||||||
|
const analyses = Array.isArray(response.value)
|
||||||
|
? response.value
|
||||||
|
: [];
|
||||||
|
analysisLoadedCache[String(matrixId)] = analyses;
|
||||||
|
renderAnalysesList(analyses);
|
||||||
|
})
|
||||||
|
.fail(function (xhr) {
|
||||||
|
let message = "Error loading analyses";
|
||||||
|
if (xhr && xhr.responseJSON && xhr.responseJSON.error) {
|
||||||
|
message = xhr.responseJSON.error;
|
||||||
|
}
|
||||||
|
showAnalysisError(message);
|
||||||
|
})
|
||||||
|
.always(function () {
|
||||||
|
setAnalysisLoadingState(false);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function updateSelectedPartsInfo() {
|
||||||
|
const modal = document.getElementById("analysisModal");
|
||||||
|
if (!modal) return;
|
||||||
|
|
||||||
|
const checked = modal.querySelectorAll(
|
||||||
|
".analysis-part-checkbox:checked",
|
||||||
|
);
|
||||||
|
const ids = Array.from(checked).map((el) => el.value);
|
||||||
|
|
||||||
|
const countEl = modal.querySelector("#analysisSelectedPartsCount");
|
||||||
|
const idsEl = modal.querySelector("#analysisSelectedPartsIds");
|
||||||
|
|
||||||
|
if (countEl) {
|
||||||
|
countEl.textContent = `${ids.length} selected`;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (idsEl) {
|
||||||
|
idsEl.textContent = ids.length ? ids.join(", ") : "-";
|
||||||
|
}
|
||||||
|
|
||||||
|
modal.querySelectorAll(".analysis-part-item").forEach((item) => {
|
||||||
|
const checkbox = item.querySelector(".analysis-part-checkbox");
|
||||||
|
if (checkbox && checkbox.checked) {
|
||||||
|
item.classList.add("part-checked");
|
||||||
|
} else {
|
||||||
|
item.classList.remove("part-checked");
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function selectPartsByMatrix(matrixId, matrixLabel) {
|
||||||
|
const modal = document.getElementById("analysisModal");
|
||||||
|
if (!modal) return;
|
||||||
|
|
||||||
|
const partItems = modal.querySelectorAll(".analysis-part-item");
|
||||||
|
const matrixLabelEl = modal.querySelector("#analysisCurrentMatrix");
|
||||||
|
|
||||||
|
partItems.forEach((item) => {
|
||||||
|
const itemMatrixId = item.getAttribute("data-matrix-id");
|
||||||
|
const checkbox = item.querySelector(".analysis-part-checkbox");
|
||||||
|
|
||||||
|
item.classList.remove("matrix-active");
|
||||||
|
|
||||||
|
if (itemMatrixId === String(matrixId)) {
|
||||||
|
item.classList.add("matrix-active");
|
||||||
|
if (checkbox) checkbox.checked = true;
|
||||||
|
} else {
|
||||||
|
if (checkbox) checkbox.checked = false;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
if (matrixLabelEl) {
|
||||||
|
matrixLabelEl.textContent = matrixLabel || "-";
|
||||||
|
}
|
||||||
|
analysisSelectedState = {};
|
||||||
|
|
||||||
|
const selectedPartIds = getSelectedPartIds();
|
||||||
|
selectedPartIds.forEach(function (partId) {
|
||||||
|
const assigned = Array.isArray(
|
||||||
|
analysisAssignedState[String(partId)],
|
||||||
|
)
|
||||||
|
? analysisAssignedState[String(partId)]
|
||||||
|
: [];
|
||||||
|
|
||||||
|
assigned.forEach(function (item) {
|
||||||
|
if (item.analysis_recordkey) {
|
||||||
|
analysisSelectedState[item.analysis_recordkey] = true;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
updateSelectedPartsInfo();
|
||||||
|
loadAnalysesByMatrix(matrixId);
|
||||||
|
}
|
||||||
|
|
||||||
|
function clearAnalysisSelection() {
|
||||||
|
const modal = document.getElementById("analysisModal");
|
||||||
|
if (!modal) return;
|
||||||
|
|
||||||
|
modal.querySelectorAll(".analysis-matrix-item").forEach((item) => {
|
||||||
|
item.classList.remove("active");
|
||||||
|
});
|
||||||
|
|
||||||
|
modal.querySelectorAll(".analysis-part-item").forEach((item) => {
|
||||||
|
item.classList.remove("matrix-active", "part-checked");
|
||||||
|
});
|
||||||
|
|
||||||
|
modal.querySelectorAll(".analysis-part-checkbox").forEach((cb) => {
|
||||||
|
cb.checked = false;
|
||||||
|
});
|
||||||
|
|
||||||
|
const matrixLabelEl = modal.querySelector("#analysisCurrentMatrix");
|
||||||
|
if (matrixLabelEl) matrixLabelEl.textContent = "-";
|
||||||
|
|
||||||
|
const webOnlyEl = modal.querySelector("#analysisWebOnly");
|
||||||
|
if (webOnlyEl) webOnlyEl.checked = false;
|
||||||
|
|
||||||
|
const searchEl = modal.querySelector("#analysisSearchInput");
|
||||||
|
if (searchEl) searchEl.value = "";
|
||||||
|
|
||||||
|
const listEl = modal.querySelector("#analysisList");
|
||||||
|
if (listEl) listEl.innerHTML = "";
|
||||||
|
|
||||||
|
showAnalysisEmpty("Select a matrix to load analyses");
|
||||||
|
updateSelectedPartsInfo();
|
||||||
|
}
|
||||||
|
|
||||||
|
function initAnalysisModal() {
|
||||||
|
const modal = document.getElementById("analysisModal");
|
||||||
|
if (!modal) return;
|
||||||
|
|
||||||
|
analysisLoadedCache = {};
|
||||||
|
analysisSelectedState = {};
|
||||||
|
readInitialAssignedAnalyses();
|
||||||
|
renderAssignedAnalysesForSelectedParts();
|
||||||
|
|
||||||
|
modal.querySelectorAll(".analysis-matrix-item").forEach((btn) => {
|
||||||
|
btn.addEventListener("click", function () {
|
||||||
|
modal
|
||||||
|
.querySelectorAll(".analysis-matrix-item")
|
||||||
|
.forEach((x) => x.classList.remove("active"));
|
||||||
|
this.classList.add("active");
|
||||||
|
|
||||||
|
const matrixId = this.getAttribute("data-matrix-id");
|
||||||
|
const matrixLabel =
|
||||||
|
this.getAttribute("data-matrix-name") ||
|
||||||
|
this.textContent.trim();
|
||||||
|
|
||||||
|
selectPartsByMatrix(matrixId, matrixLabel);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
modal.querySelectorAll(".analysis-part-checkbox").forEach((cb) => {
|
||||||
|
cb.addEventListener("change", function () {
|
||||||
|
updateSelectedPartsInfo();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
const clearBtn = modal.querySelector("#analysisClearSelectionBtn");
|
||||||
|
if (clearBtn) {
|
||||||
|
clearBtn.addEventListener("click", function () {
|
||||||
|
clearAnalysisSelection();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
const webOnlyEl = modal.querySelector("#analysisWebOnly");
|
||||||
|
if (webOnlyEl) {
|
||||||
|
webOnlyEl.addEventListener("change", function () {
|
||||||
|
filterAnalysisList();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
const searchEl = modal.querySelector("#analysisSearchInput");
|
||||||
|
if (searchEl) {
|
||||||
|
searchEl.addEventListener("input", function () {
|
||||||
|
filterAnalysisList();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
modal.addEventListener("click", function (e) {
|
||||||
|
const removeBtn = e.target.closest(".analysis-remove-btn");
|
||||||
|
if (removeBtn) {
|
||||||
|
e.preventDefault();
|
||||||
|
e.stopPropagation();
|
||||||
|
|
||||||
|
const partId = removeBtn.getAttribute("data-part-id");
|
||||||
|
const recordKey = removeBtn.getAttribute("data-recordkey");
|
||||||
|
|
||||||
|
deleteAnalysisAssociation(partId, recordKey, function () {
|
||||||
|
const stillUsed = Object.keys(analysisAssignedState).some(
|
||||||
|
function (pid) {
|
||||||
|
return (
|
||||||
|
Array.isArray(analysisAssignedState[pid]) &&
|
||||||
|
analysisAssignedState[pid].some(
|
||||||
|
function (item) {
|
||||||
|
return (
|
||||||
|
item.analysis_recordkey ===
|
||||||
|
recordKey
|
||||||
|
);
|
||||||
|
},
|
||||||
|
)
|
||||||
|
);
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
|
if (!stillUsed) {
|
||||||
|
delete analysisSelectedState[recordKey];
|
||||||
|
}
|
||||||
|
|
||||||
|
syncSelectedAnalysisRows();
|
||||||
|
});
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const row = e.target.closest(".analysis-analysis-item");
|
||||||
|
if (!row) return;
|
||||||
|
|
||||||
|
const selectedPartIds = getSelectedPartIds();
|
||||||
|
if (!selectedPartIds.length) {
|
||||||
|
alert("Select at least one part first");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const payload = buildAnalysisPayloadFromRow(row);
|
||||||
|
if (!payload.analysis_recordkey) {
|
||||||
|
alert("Invalid analysis record key");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const recordKey = payload.analysis_recordkey;
|
||||||
|
const alreadySelected = !!analysisSelectedState[recordKey];
|
||||||
|
|
||||||
|
if (alreadySelected) {
|
||||||
|
selectedPartIds.forEach(function (partId) {
|
||||||
|
deleteAnalysisAssociation(partId, recordKey);
|
||||||
|
});
|
||||||
|
|
||||||
|
delete analysisSelectedState[recordKey];
|
||||||
|
syncSelectedAnalysisRows();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
let pending = selectedPartIds.length;
|
||||||
|
let atLeastOneSaved = false;
|
||||||
|
|
||||||
|
selectedPartIds.forEach(function (partId) {
|
||||||
|
saveAnalysisAssociation(partId, payload, function (ok) {
|
||||||
|
if (ok) atLeastOneSaved = true;
|
||||||
|
|
||||||
|
pending--;
|
||||||
|
if (pending <= 0 && atLeastOneSaved) {
|
||||||
|
analysisSelectedState[recordKey] = true;
|
||||||
|
syncSelectedAnalysisRows();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
const firstActiveMatrix = modal.querySelector(
|
||||||
|
".analysis-matrix-item.active",
|
||||||
|
);
|
||||||
|
if (firstActiveMatrix) {
|
||||||
|
const matrixId = firstActiveMatrix.getAttribute("data-matrix-id");
|
||||||
|
const matrixLabel =
|
||||||
|
firstActiveMatrix.getAttribute("data-matrix-name") ||
|
||||||
|
firstActiveMatrix.textContent.trim();
|
||||||
|
|
||||||
|
selectPartsByMatrix(matrixId, matrixLabel);
|
||||||
|
} else {
|
||||||
|
updateSelectedPartsInfo();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// OPEN ANALYSIS MODAL FROM PARTS MODAL BUTTON
|
||||||
|
$(document).on("click", ".open-analysis-modal-btn", function () {
|
||||||
|
const partsModal = document.getElementById("partsModal");
|
||||||
|
const iddatadb = $("#partsModal").data("iddatadb");
|
||||||
|
|
||||||
|
if (!iddatadb) {
|
||||||
|
console.error("iddatadb not found on #partsModal");
|
||||||
|
alert("iddatadb not found");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
$.ajax({
|
||||||
|
url: "modal_analysis.php",
|
||||||
|
method: "GET",
|
||||||
|
data: { iddatadb: iddatadb },
|
||||||
|
success: function (response) {
|
||||||
|
$("#analysisModalContainer").html(response);
|
||||||
|
|
||||||
|
const modalElement = document.getElementById("analysisModal");
|
||||||
|
if (!modalElement) {
|
||||||
|
console.error("Analysis modal not found: #analysisModal");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
let modal = bootstrap.Modal.getInstance(modalElement);
|
||||||
|
if (!modal) {
|
||||||
|
modal = new bootstrap.Modal(modalElement, {
|
||||||
|
backdrop: true,
|
||||||
|
keyboard: true,
|
||||||
|
focus: true,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
loadAnalysisMatrixNames().always(function () {
|
||||||
|
initAnalysisModal();
|
||||||
|
modal.show();
|
||||||
|
});
|
||||||
|
},
|
||||||
|
error: function (xhr, status, error) {
|
||||||
|
console.error("Error loading analysis modal:", error);
|
||||||
|
alert("Error loading analysis modal: " + error);
|
||||||
|
},
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
// CLEANUP ON CLOSE
|
||||||
|
$(document).on("hidden.bs.modal", "#analysisModal", function () {
|
||||||
|
const modalElement = document.getElementById("analysisModal");
|
||||||
|
if (modalElement) {
|
||||||
|
const modal = bootstrap.Modal.getInstance(modalElement);
|
||||||
|
if (modal) modal.dispose();
|
||||||
|
}
|
||||||
|
|
||||||
|
$("#analysisModalContainer").empty();
|
||||||
|
|
||||||
|
// keep parts modal alive, but remove extra backdrop leftovers
|
||||||
|
$(".modal-backdrop").last().remove();
|
||||||
|
|
||||||
|
if ($("#partsModal").hasClass("show")) {
|
||||||
|
$("body").addClass("modal-open");
|
||||||
|
} else {
|
||||||
|
$("body").removeClass("modal-open").css("padding-right", "");
|
||||||
|
}
|
||||||
|
});
|
||||||
|
})();
|
||||||
@ -3,6 +3,9 @@ ob_start();
|
|||||||
session_start();
|
session_start();
|
||||||
require_once '../../vendor/autoload.php';
|
require_once '../../vendor/autoload.php';
|
||||||
|
|
||||||
|
Dotenv\Dotenv::createImmutable(dirname(__DIR__, 2))->safeLoad();
|
||||||
|
date_default_timezone_set($_ENV['APP_TIMEZONE'] ?? 'Europe/Rome');
|
||||||
|
|
||||||
$response = ['error' => '', 'rows' => [], 'columns' => [], 'template_id' => 0, 'filename' => '', 'excel_data' => []];
|
$response = ['error' => '', 'rows' => [], 'columns' => [], 'template_id' => 0, 'filename' => '', 'excel_data' => []];
|
||||||
|
|
||||||
try {
|
try {
|
||||||
|
|||||||
@ -255,4 +255,39 @@ class VisualLimsApiClient
|
|||||||
{
|
{
|
||||||
return $this->baseUrl;
|
return $this->baseUrl;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Recupera contenuto binario - Adattato per https://bvcpsitaly-elims.com/limsapi
|
||||||
|
*/
|
||||||
|
public function getRaw($endpoint)
|
||||||
|
{
|
||||||
|
$token = $this->getToken();
|
||||||
|
|
||||||
|
// IMPORTANTE: usa /odata/ e NON /api/odata/
|
||||||
|
$url = "{$this->baseUrl}/odata/{$endpoint}";
|
||||||
|
|
||||||
|
$ch = curl_init($url);
|
||||||
|
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
|
||||||
|
curl_setopt($ch, CURLOPT_HTTPHEADER, [
|
||||||
|
"Authorization: Bearer {$token}",
|
||||||
|
"Accept: */*"
|
||||||
|
]);
|
||||||
|
curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);
|
||||||
|
curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, false);
|
||||||
|
|
||||||
|
$response = curl_exec($ch);
|
||||||
|
$http_code = curl_getinfo($ch, CURLINFO_HTTP_CODE);
|
||||||
|
$curl_error = curl_error($ch);
|
||||||
|
curl_close($ch);
|
||||||
|
|
||||||
|
if ($response === false) {
|
||||||
|
throw new Exception("Errore cURL: " . $curl_error);
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($http_code !== 200) {
|
||||||
|
throw new Exception("HTTP {$http_code} su endpoint: " . $url);
|
||||||
|
}
|
||||||
|
|
||||||
|
return $response;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -3,6 +3,9 @@ require_once dirname(__DIR__, 3) . '/vendor/autoload.php';
|
|||||||
|
|
||||||
use Dotenv\Dotenv;
|
use Dotenv\Dotenv;
|
||||||
|
|
||||||
|
Dotenv::createImmutable(dirname(__DIR__, 3))->safeLoad();
|
||||||
|
date_default_timezone_set($_ENV['APP_TIMEZONE'] ?? 'Europe/Rome');
|
||||||
|
|
||||||
class DBHandlerSelect
|
class DBHandlerSelect
|
||||||
{
|
{
|
||||||
private static $instance = null;
|
private static $instance = null;
|
||||||
|
|||||||
131
public/userarea/clone_parts_to_visible.php
Normal file
131
public/userarea/clone_parts_to_visible.php
Normal file
@ -0,0 +1,131 @@
|
|||||||
|
<?php
|
||||||
|
header('Content-Type: application/json');
|
||||||
|
include('include/headscript.php');
|
||||||
|
|
||||||
|
$dbHandler = DBHandlerSelect::getInstance();
|
||||||
|
$pdo = $dbHandler->getConnection();
|
||||||
|
$pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
|
||||||
|
|
||||||
|
$data = json_decode(file_get_contents('php://input'), true);
|
||||||
|
|
||||||
|
$sourceIddatadb = isset($data['source_iddatadb']) ? (int)$data['source_iddatadb'] : 0;
|
||||||
|
$targetList = $data['target_iddatadb_list'] ?? [];
|
||||||
|
|
||||||
|
$targetIds = array_values(array_unique(array_filter(array_map('intval', (array)$targetList), function ($v) use ($sourceIddatadb) {
|
||||||
|
return $v > 0 && $v !== $sourceIddatadb;
|
||||||
|
})));
|
||||||
|
|
||||||
|
if ($sourceIddatadb <= 0 || empty($targetIds)) {
|
||||||
|
echo json_encode([
|
||||||
|
'success' => false,
|
||||||
|
'message' => 'Missing source or target records'
|
||||||
|
]);
|
||||||
|
exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
$pdo->beginTransaction();
|
||||||
|
|
||||||
|
// 1. Load source parts
|
||||||
|
$stmtParts = $pdo->prepare("
|
||||||
|
SELECT id, part_number, part_description, mix, idmatrice, note, dateexpiry
|
||||||
|
FROM identification_parts
|
||||||
|
WHERE iddatadb = ?
|
||||||
|
ORDER BY part_number ASC, id ASC
|
||||||
|
");
|
||||||
|
$stmtParts->execute([$sourceIddatadb]);
|
||||||
|
$sourceParts = $stmtParts->fetchAll(PDO::FETCH_ASSOC);
|
||||||
|
|
||||||
|
if (empty($sourceParts)) {
|
||||||
|
$pdo->rollBack();
|
||||||
|
echo json_encode([
|
||||||
|
'success' => false,
|
||||||
|
'message' => 'No parts found for source record'
|
||||||
|
]);
|
||||||
|
exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 2. Prepare statements
|
||||||
|
$stmtInsertPart = $pdo->prepare("
|
||||||
|
INSERT INTO identification_parts
|
||||||
|
(iddatadb, part_number, part_description, mix, idmatrice, note, dateexpiry, created_at, updated_at)
|
||||||
|
VALUES
|
||||||
|
(:iddatadb, :part_number, :part_description, :mix, :idmatrice, :note, :dateexpiry, NOW(), NOW())
|
||||||
|
");
|
||||||
|
|
||||||
|
$stmtLoadCF = $pdo->prepare("
|
||||||
|
SELECT field_id, value_id, value_text
|
||||||
|
FROM identification_parts_customfields
|
||||||
|
WHERE part_id = ?
|
||||||
|
ORDER BY id ASC
|
||||||
|
");
|
||||||
|
|
||||||
|
$stmtInsertCF = $pdo->prepare("
|
||||||
|
INSERT INTO identification_parts_customfields
|
||||||
|
(part_id, field_id, value_id, value_text, created_at, updated_at)
|
||||||
|
VALUES
|
||||||
|
(:part_id, :field_id, :value_id, :value_text, NOW(), NOW())
|
||||||
|
");
|
||||||
|
|
||||||
|
$details = [];
|
||||||
|
$totalClonedParts = 0;
|
||||||
|
|
||||||
|
// 3. Clone source parts to each target record
|
||||||
|
foreach ($targetIds as $targetIddatadb) {
|
||||||
|
$clonedCountForTarget = 0;
|
||||||
|
|
||||||
|
foreach ($sourceParts as $part) {
|
||||||
|
$stmtInsertPart->execute([
|
||||||
|
':iddatadb' => $targetIddatadb,
|
||||||
|
':part_number' => $part['part_number'],
|
||||||
|
':part_description' => $part['part_description'],
|
||||||
|
':mix' => $part['mix'] ?? 'N',
|
||||||
|
':idmatrice' => $part['idmatrice'] !== '' ? $part['idmatrice'] : null,
|
||||||
|
':note' => $part['note'] ?? null,
|
||||||
|
':dateexpiry' => $part['dateexpiry'] ?? null,
|
||||||
|
]);
|
||||||
|
|
||||||
|
$newPartId = (int)$pdo->lastInsertId();
|
||||||
|
|
||||||
|
// Load source custom fields for this part
|
||||||
|
$stmtLoadCF->execute([(int)$part['id']]);
|
||||||
|
$customFields = $stmtLoadCF->fetchAll(PDO::FETCH_ASSOC);
|
||||||
|
|
||||||
|
foreach ($customFields as $cf) {
|
||||||
|
$stmtInsertCF->execute([
|
||||||
|
':part_id' => $newPartId,
|
||||||
|
':field_id' => (int)$cf['field_id'],
|
||||||
|
':value_id' => ($cf['value_id'] !== null && $cf['value_id'] !== '') ? (int)$cf['value_id'] : null,
|
||||||
|
':value_text' => $cf['value_text'] !== '' ? $cf['value_text'] : null,
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
|
||||||
|
$clonedCountForTarget++;
|
||||||
|
$totalClonedParts++;
|
||||||
|
}
|
||||||
|
|
||||||
|
$details[] = [
|
||||||
|
'target_iddatadb' => $targetIddatadb,
|
||||||
|
'cloned_parts' => $clonedCountForTarget
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
$pdo->commit();
|
||||||
|
|
||||||
|
echo json_encode([
|
||||||
|
'success' => true,
|
||||||
|
'source_iddatadb' => $sourceIddatadb,
|
||||||
|
'cloned_targets' => count($targetIds),
|
||||||
|
'total_cloned_parts' => $totalClonedParts,
|
||||||
|
'details' => $details
|
||||||
|
]);
|
||||||
|
} catch (Throwable $e) {
|
||||||
|
if ($pdo->inTransaction()) {
|
||||||
|
$pdo->rollBack();
|
||||||
|
}
|
||||||
|
|
||||||
|
echo json_encode([
|
||||||
|
'success' => false,
|
||||||
|
'message' => 'Clone failed: ' . $e->getMessage()
|
||||||
|
]);
|
||||||
|
}
|
||||||
@ -30,6 +30,8 @@ if ((int)$stmt->fetchColumn() > 0) {
|
|||||||
$fixedFields = [
|
$fixedFields = [
|
||||||
// fixed_field_key, data_type
|
// fixed_field_key, data_type
|
||||||
['ClienteResponsabile', 'INT'],
|
['ClienteResponsabile', 'INT'],
|
||||||
|
['ClienteFornitore', 'INT'],
|
||||||
|
['ClienteAnalisi', 'INT'],
|
||||||
['MoltiplicatorePrezzo', 'INT'],
|
['MoltiplicatorePrezzo', 'INT'],
|
||||||
['AnagraficaCertestObject', 'INT'],
|
['AnagraficaCertestObject', 'INT'],
|
||||||
['AnagraficaCertestService', 'INT'],
|
['AnagraficaCertestService', 'INT'],
|
||||||
|
|||||||
3202
public/userarea/cronfiles/debug_log.txt
Normal file
3202
public/userarea/cronfiles/debug_log.txt
Normal file
File diff suppressed because it is too large
Load Diff
@ -17,6 +17,8 @@ try {
|
|||||||
exit(1);
|
exit(1);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
date_default_timezone_set($_ENV['APP_TIMEZONE'] ?? 'Europe/Rome');
|
||||||
|
|
||||||
// Recupera le variabili d'ambiente
|
// Recupera le variabili d'ambiente
|
||||||
$dbHost = $_ENV['DB_HOST'];
|
$dbHost = $_ENV['DB_HOST'];
|
||||||
$dbName = $_ENV['DB_DATABASE'];
|
$dbName = $_ENV['DB_DATABASE'];
|
||||||
|
|||||||
1
public/userarea/cronfiles/last_url.txt
Normal file
1
public/userarea/cronfiles/last_url.txt
Normal file
@ -0,0 +1 @@
|
|||||||
|
https://93.43.5.102/limsapi/api/odata/Matrice
|
||||||
25589
public/userarea/cronfiles/matrici_response.json
Normal file
25589
public/userarea/cronfiles/matrici_response.json
Normal file
File diff suppressed because it is too large
Load Diff
1
public/userarea/cronfiles/success_log.txt
Normal file
1
public/userarea/cronfiles/success_log.txt
Normal file
@ -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
290
public/userarea/debug_commessa_api.php
Normal file
290
public/userarea/debug_commessa_api.php
Normal file
@ -0,0 +1,290 @@
|
|||||||
|
<?php
|
||||||
|
require_once "class/VisualLimsApiClient.class.php";
|
||||||
|
include('include/headscript.php');
|
||||||
|
|
||||||
|
// Force HTML response
|
||||||
|
header('Content-Type: text/html; charset=UTF-8');
|
||||||
|
|
||||||
|
// Initialize variables
|
||||||
|
$error = null;
|
||||||
|
$result = null;
|
||||||
|
$entityType = $_GET['entity_type'] ?? 'CommessaWeb';
|
||||||
|
$entityId = isset($_GET['entity_id']) ? (int)$_GET['entity_id'] : 0;
|
||||||
|
$customExpand = trim($_GET['expand'] ?? '');
|
||||||
|
$rawEndpoint = null;
|
||||||
|
|
||||||
|
function h($value)
|
||||||
|
{
|
||||||
|
return htmlspecialchars((string)$value, ENT_QUOTES, 'UTF-8');
|
||||||
|
}
|
||||||
|
|
||||||
|
function renderArrayAsTable(array $items)
|
||||||
|
{
|
||||||
|
if (empty($items)) {
|
||||||
|
echo '<div class="alert alert-secondary">No records found</div>';
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
$firstRow = reset($items);
|
||||||
|
if (!is_array($firstRow)) {
|
||||||
|
echo '<pre class="bg-light p-3 border rounded">' . h(json_encode($items, JSON_PRETTY_PRINT | JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES)) . '</pre>';
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
$headers = [];
|
||||||
|
foreach ($items as $row) {
|
||||||
|
if (is_array($row)) {
|
||||||
|
$headers = array_unique(array_merge($headers, array_keys($row)));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
echo '<div class="table-responsive">';
|
||||||
|
echo '<table class="table table-striped table-bordered table-sm align-middle">';
|
||||||
|
echo '<thead class="table-dark"><tr>';
|
||||||
|
foreach ($headers as $header) {
|
||||||
|
echo '<th>' . h($header) . '</th>';
|
||||||
|
}
|
||||||
|
echo '</tr></thead><tbody>';
|
||||||
|
|
||||||
|
foreach ($items as $row) {
|
||||||
|
echo '<tr>';
|
||||||
|
foreach ($headers as $header) {
|
||||||
|
$value = $row[$header] ?? '';
|
||||||
|
if (is_array($value) || is_object($value)) {
|
||||||
|
$value = json_encode($value, JSON_PRETTY_PRINT | JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES);
|
||||||
|
echo '<td><pre style="white-space:pre-wrap; margin:0;">' . h($value) . '</pre></td>';
|
||||||
|
} else {
|
||||||
|
echo '<td>' . h($value) . '</td>';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
echo '</tr>';
|
||||||
|
}
|
||||||
|
|
||||||
|
echo '</tbody></table>';
|
||||||
|
echo '</div>';
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
if ($entityId > 0) {
|
||||||
|
$api = VisualLimsApiClient::getInstance();
|
||||||
|
|
||||||
|
// Default expands for better debugging
|
||||||
|
if ($customExpand !== '') {
|
||||||
|
$expand = $customExpand;
|
||||||
|
} else {
|
||||||
|
if ($entityType === 'CommessaWeb') {
|
||||||
|
$expand = implode(',', [
|
||||||
|
'CommesseCustomFields($expand=CustomField)',
|
||||||
|
'Campioni'
|
||||||
|
]);
|
||||||
|
} else {
|
||||||
|
$expand = implode(',', [
|
||||||
|
'CommesseCustomFields($expand=CustomField)',
|
||||||
|
'Campioni'
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
$rawEndpoint = $entityType . '(' . $entityId . ')?$expand=' . $expand;
|
||||||
|
$result = $api->get($rawEndpoint);
|
||||||
|
}
|
||||||
|
} catch (Exception $e) {
|
||||||
|
$error = $e->getMessage();
|
||||||
|
}
|
||||||
|
?>
|
||||||
|
|
||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="en">
|
||||||
|
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8">
|
||||||
|
<title>Debug Commessa API</title>
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||||
|
<style>
|
||||||
|
body {
|
||||||
|
background: #f5f7fb;
|
||||||
|
font-family: Arial, sans-serif;
|
||||||
|
}
|
||||||
|
|
||||||
|
.debug-card {
|
||||||
|
background: #fff;
|
||||||
|
border-radius: 12px;
|
||||||
|
box-shadow: 0 4px 18px rgba(0, 0, 0, 0.08);
|
||||||
|
padding: 20px;
|
||||||
|
margin-bottom: 20px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.section-title {
|
||||||
|
font-size: 18px;
|
||||||
|
font-weight: 700;
|
||||||
|
margin-bottom: 15px;
|
||||||
|
}
|
||||||
|
|
||||||
|
pre {
|
||||||
|
background: #f8f9fa;
|
||||||
|
border: 1px solid #ddd;
|
||||||
|
border-radius: 8px;
|
||||||
|
padding: 12px;
|
||||||
|
white-space: pre-wrap;
|
||||||
|
word-break: break-word;
|
||||||
|
}
|
||||||
|
|
||||||
|
.label-box {
|
||||||
|
display: inline-block;
|
||||||
|
padding: 6px 10px;
|
||||||
|
border-radius: 8px;
|
||||||
|
background: #eef3ff;
|
||||||
|
color: #2446a8;
|
||||||
|
font-weight: 600;
|
||||||
|
margin-right: 8px;
|
||||||
|
margin-bottom: 8px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.mini-grid {
|
||||||
|
display: grid;
|
||||||
|
grid-template-columns: repeat(auto-fit, minmax(220px, 1fr));
|
||||||
|
gap: 12px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.mini-box {
|
||||||
|
background: #f8fafc;
|
||||||
|
border: 1px solid #e4e7eb;
|
||||||
|
border-radius: 10px;
|
||||||
|
padding: 12px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.mini-box strong {
|
||||||
|
display: block;
|
||||||
|
margin-bottom: 6px;
|
||||||
|
color: #1f2937;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
</head>
|
||||||
|
|
||||||
|
<body>
|
||||||
|
<div class="container py-4">
|
||||||
|
<div class="debug-card">
|
||||||
|
<h2 class="mb-3">Commessa / CommessaWeb API Inspector</h2>
|
||||||
|
|
||||||
|
<form method="GET" class="row g-3">
|
||||||
|
<div class="col-md-3">
|
||||||
|
<label class="form-label">Entity Type</label>
|
||||||
|
<select name="entity_type" class="form-select">
|
||||||
|
<option value="CommessaWeb" <?= $entityType === 'CommessaWeb' ? 'selected' : '' ?>>CommessaWeb</option>
|
||||||
|
<option value="Commessa" <?= $entityType === 'Commessa' ? 'selected' : '' ?>>Commessa</option>
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="col-md-2">
|
||||||
|
<label class="form-label">Entity ID</label>
|
||||||
|
<input type="number" name="entity_id" class="form-control" value="<?= h($entityId) ?>" required>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="col-md-5">
|
||||||
|
<label class="form-label">Expand</label>
|
||||||
|
<input type="text" name="expand" class="form-control" value="<?= h($customExpand) ?>" placeholder="CommesseCustomFields($expand=CustomField),Campioni">
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="col-md-2 d-flex align-items-end">
|
||||||
|
<button type="submit" class="btn btn-primary w-100">Load Data</button>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<?php if ($rawEndpoint): ?>
|
||||||
|
<div class="debug-card">
|
||||||
|
<div class="section-title">Requested Endpoint</div>
|
||||||
|
<pre><?= h($rawEndpoint) ?></pre>
|
||||||
|
</div>
|
||||||
|
<?php endif; ?>
|
||||||
|
|
||||||
|
<?php if ($error): ?>
|
||||||
|
<div class="debug-card">
|
||||||
|
<div class="alert alert-danger mb-0">
|
||||||
|
<strong>API Error:</strong><br>
|
||||||
|
<?= h($error) ?>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<?php endif; ?>
|
||||||
|
|
||||||
|
<?php if ($result && is_array($result)): ?>
|
||||||
|
<div class="debug-card">
|
||||||
|
<div class="section-title">Main Information</div>
|
||||||
|
<div class="mini-grid">
|
||||||
|
<div class="mini-box">
|
||||||
|
<strong>ID</strong>
|
||||||
|
<?= h($result['IdCommessa'] ?? $result['IdCommessaWeb'] ?? '') ?>
|
||||||
|
</div>
|
||||||
|
<div class="mini-box">
|
||||||
|
<strong>Code</strong>
|
||||||
|
<?= h($result['CodiceCommessa'] ?? '') ?>
|
||||||
|
</div>
|
||||||
|
<div class="mini-box">
|
||||||
|
<strong>Cliente</strong>
|
||||||
|
<?= h($result['Cliente'] ?? '') ?>
|
||||||
|
</div>
|
||||||
|
<div class="mini-box">
|
||||||
|
<strong>SchemaCustomField</strong>
|
||||||
|
<?= h($result['SchemaCustomField'] ?? '') ?>
|
||||||
|
</div>
|
||||||
|
<div class="mini-box">
|
||||||
|
<strong>Richiedente</strong>
|
||||||
|
<?= h($result['Richiedente'] ?? '') ?>
|
||||||
|
</div>
|
||||||
|
<div class="mini-box">
|
||||||
|
<strong>Descrizione</strong>
|
||||||
|
<?= h($result['Descrizione'] ?? '') ?>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="debug-card">
|
||||||
|
<div class="section-title">Direct Properties</div>
|
||||||
|
<pre><?= h(json_encode($result, JSON_PRETTY_PRINT | JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES)) ?></pre>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<?php if (!empty($result['CommesseCustomFields']) && is_array($result['CommesseCustomFields'])): ?>
|
||||||
|
<div class="debug-card">
|
||||||
|
<div class="section-title">CommesseCustomFields</div>
|
||||||
|
<?php
|
||||||
|
$fieldsRows = [];
|
||||||
|
foreach ($result['CommesseCustomFields'] as $field) {
|
||||||
|
$fieldsRows[] = [
|
||||||
|
'IdCommesseCustomFields' => $field['IdCommesseCustomFields'] ?? '',
|
||||||
|
'Valore' => $field['Valore'] ?? '',
|
||||||
|
'CustomFieldId' => $field['CustomField']['IdCustomField'] ?? '',
|
||||||
|
'Label' => $field['CustomField']['Descrizione'] ?? ($field['CustomField']['Name'] ?? ''),
|
||||||
|
'Tipo' => $field['CustomField']['Tipo'] ?? '',
|
||||||
|
];
|
||||||
|
}
|
||||||
|
renderArrayAsTable($fieldsRows);
|
||||||
|
?>
|
||||||
|
</div>
|
||||||
|
<?php endif; ?>
|
||||||
|
|
||||||
|
<?php if (!empty($result['Campioni']) && is_array($result['Campioni'])): ?>
|
||||||
|
<div class="debug-card">
|
||||||
|
<div class="section-title">Campioni</div>
|
||||||
|
<?php renderArrayAsTable($result['Campioni']); ?>
|
||||||
|
</div>
|
||||||
|
<?php endif; ?>
|
||||||
|
|
||||||
|
<div class="debug-card">
|
||||||
|
<div class="section-title">Raw JSON</div>
|
||||||
|
<pre><?= h(json_encode($result, JSON_PRETTY_PRINT | JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES)) ?></pre>
|
||||||
|
</div>
|
||||||
|
<?php elseif ($entityId > 0 && !$error): ?>
|
||||||
|
<div class="debug-card">
|
||||||
|
<div class="alert alert-warning mb-0">No data returned from API</div>
|
||||||
|
</div>
|
||||||
|
<?php endif; ?>
|
||||||
|
</div>
|
||||||
|
</body>
|
||||||
|
|
||||||
|
</html>
|
||||||
|
|
||||||
|
<!-- Example of use
|
||||||
|
debug_commessa_api.php?entity_type=CommessaWeb&entity_id=564779
|
||||||
|
debug_commessa_api.php?entity_type=Commessa&entity_id=12345
|
||||||
|
debug_commessa_api.php?entity_type=CommessaWeb&entity_id=564779&expand=CommesseCustomFields($expand=CustomField),Campioni
|
||||||
|
-->
|
||||||
48
public/userarea/delete_part_analysis.php
Normal file
48
public/userarea/delete_part_analysis.php
Normal file
@ -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()
|
||||||
|
]);
|
||||||
|
}
|
||||||
112
public/userarea/download_rapporto_file.php
Normal file
112
public/userarea/download_rapporto_file.php
Normal file
@ -0,0 +1,112 @@
|
|||||||
|
<?php
|
||||||
|
require_once dirname(__DIR__, 2) . '/vendor/autoload.php';
|
||||||
|
require_once dirname(__FILE__) . '/class/VisualLimsApiClient.class.php';
|
||||||
|
|
||||||
|
ini_set('display_errors', '0');
|
||||||
|
error_reporting(E_ALL);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Uso:
|
||||||
|
* download_rapporto_file.php?id=123
|
||||||
|
*/
|
||||||
|
|
||||||
|
try {
|
||||||
|
$api = VisualLimsApiClient::getInstance();
|
||||||
|
|
||||||
|
$id = isset($_GET['id']) ? intval($_GET['id']) : 0;
|
||||||
|
|
||||||
|
if (!$id) {
|
||||||
|
throw new Exception("ID RapportoFile mancante");
|
||||||
|
}
|
||||||
|
|
||||||
|
$debugDir = __DIR__ . '/logs/lims_get_commessa/';
|
||||||
|
|
||||||
|
if (!is_dir($debugDir)) {
|
||||||
|
mkdir($debugDir, 0755, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Recupero il record RapportoFile.
|
||||||
|
*/
|
||||||
|
$data = $api->get("RapportoFile({$id})", []);
|
||||||
|
|
||||||
|
file_put_contents(
|
||||||
|
$debugDir . "rapporto_file_{$id}.json",
|
||||||
|
json_encode($data, JSON_PRETTY_PRINT | JSON_UNESCAPED_UNICODE)
|
||||||
|
);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Provo vari nomi campo possibili per il contenuto PDF.
|
||||||
|
* Dopo il primo test potremo adattarlo al nome esatto.
|
||||||
|
*/
|
||||||
|
$possibleContentFields = [
|
||||||
|
'File',
|
||||||
|
'file',
|
||||||
|
'Content',
|
||||||
|
'content',
|
||||||
|
'FileContent',
|
||||||
|
'fileContent',
|
||||||
|
'Contenuto',
|
||||||
|
'contenuto',
|
||||||
|
'Bytes',
|
||||||
|
'bytes'
|
||||||
|
];
|
||||||
|
|
||||||
|
$base64 = null;
|
||||||
|
|
||||||
|
foreach ($possibleContentFields as $field) {
|
||||||
|
if (!empty($data[$field])) {
|
||||||
|
$base64 = $data[$field];
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!$base64) {
|
||||||
|
header('Content-Type: application/json; charset=utf-8');
|
||||||
|
echo json_encode([
|
||||||
|
'success' => false,
|
||||||
|
'message' => 'Record RapportoFile recuperato, ma non ho trovato un campo base64 del PDF.',
|
||||||
|
'hint' => 'Apri logs/lims_get_commessa/rapporto_file_' . $id . '.json e controlla il nome reale del campo file.',
|
||||||
|
'data' => $data
|
||||||
|
], JSON_PRETTY_PRINT | JSON_UNESCAPED_UNICODE);
|
||||||
|
exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Se il contenuto ha prefisso data URI, lo pulisco.
|
||||||
|
*/
|
||||||
|
if (strpos($base64, 'base64,') !== false) {
|
||||||
|
$parts = explode('base64,', $base64);
|
||||||
|
$base64 = end($parts);
|
||||||
|
}
|
||||||
|
|
||||||
|
$pdfBinary = base64_decode($base64, true);
|
||||||
|
|
||||||
|
if ($pdfBinary === false) {
|
||||||
|
throw new Exception("Il contenuto trovato non è un base64 valido");
|
||||||
|
}
|
||||||
|
|
||||||
|
$filename = $data['NomeFile']
|
||||||
|
?? $data['nomeFile']
|
||||||
|
?? $data['FileName']
|
||||||
|
?? $data['fileName']
|
||||||
|
?? "rapporto_{$id}.pdf";
|
||||||
|
|
||||||
|
if (!str_ends_with(strtolower($filename), '.pdf')) {
|
||||||
|
$filename .= '.pdf';
|
||||||
|
}
|
||||||
|
|
||||||
|
header('Content-Type: application/pdf');
|
||||||
|
header('Content-Disposition: inline; filename="' . basename($filename) . '"');
|
||||||
|
header('Content-Length: ' . strlen($pdfBinary));
|
||||||
|
|
||||||
|
echo $pdfBinary;
|
||||||
|
} catch (Exception $e) {
|
||||||
|
http_response_code(500);
|
||||||
|
header('Content-Type: application/json; charset=utf-8');
|
||||||
|
|
||||||
|
echo json_encode([
|
||||||
|
'success' => false,
|
||||||
|
'error' => $e->getMessage()
|
||||||
|
], JSON_PRETTY_PRINT | JSON_UNESCAPED_UNICODE);
|
||||||
|
}
|
||||||
@ -1,29 +1,65 @@
|
|||||||
<?php include('include/headscript.php');
|
<?php include('include/headscript.php');
|
||||||
|
|
||||||
// Controlla se è stato passato un ID valido
|
// Check if a valid ID was provided
|
||||||
if (!isset($_GET['id']) || !is_numeric($_GET['id'])) {
|
if (!isset($_GET['id']) || !is_numeric($_GET['id'])) {
|
||||||
header("Location: xlstemplates_grid.php?status=error&message=" . urlencode("Invalid ID"));
|
header("Location: templates_dashboard.php?status=error&message=" . urlencode("Invalid ID"));
|
||||||
exit;
|
exit;
|
||||||
}
|
}
|
||||||
|
|
||||||
$id = intval($_GET['id']); // Sanifica l'ID
|
$id = intval($_GET['id']);
|
||||||
|
|
||||||
// Recupera il template dal database
|
// Retrieve template from database
|
||||||
$db = DBHandlerSelect::getInstance();
|
$db = DBHandlerSelect::getInstance();
|
||||||
$pdo = $db->getConnection();
|
$pdo = $db->getConnection();
|
||||||
|
|
||||||
$stmt = $pdo->prepare("SELECT * FROM excel_templates WHERE id = ?");
|
$stmt = $pdo->prepare("SELECT * FROM excel_templates WHERE id = ?");
|
||||||
$stmt->execute([$id]);
|
$stmt->execute([$id]);
|
||||||
$template = $stmt->fetch(PDO::FETCH_ASSOC);
|
$template = $stmt->fetch(PDO::FETCH_ASSOC);
|
||||||
|
|
||||||
if (!$template) {
|
if (!$template) {
|
||||||
header("Location: template_dashboard.php?status=error&message=" . urlencode("Template not found"));
|
header("Location: templates_dashboard.php?status=error&message=" . urlencode("Template not found"));
|
||||||
exit;
|
exit;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Recupera tutte le routine dal database
|
// Retrieve all routines
|
||||||
$stmt = $pdo->prepare("SELECT * FROM routine");
|
$stmt = $pdo->prepare("SELECT * FROM routine");
|
||||||
$stmt->execute();
|
$stmt->execute();
|
||||||
$routines = $stmt->fetchAll(PDO::FETCH_ASSOC);
|
$routines = $stmt->fetchAll(PDO::FETCH_ASSOC);
|
||||||
|
|
||||||
|
$buttonBgPalette = [
|
||||||
|
'#0d6efd' => 'Blue',
|
||||||
|
'#6610f2' => 'Indigo',
|
||||||
|
'#6f42c1' => 'Purple',
|
||||||
|
'#d63384' => 'Pink',
|
||||||
|
'#dc3545' => 'Red',
|
||||||
|
'#fd7e14' => 'Orange',
|
||||||
|
'#ffc107' => 'Yellow',
|
||||||
|
'#198754' => 'Green',
|
||||||
|
'#20c997' => 'Teal',
|
||||||
|
'#0dcaf0' => 'Cyan',
|
||||||
|
'#212529' => 'Dark',
|
||||||
|
'#6c757d' => 'Gray',
|
||||||
|
'#495057' => 'Slate',
|
||||||
|
'#795548' => 'Brown',
|
||||||
|
'#2f5d50' => 'Sage Green',
|
||||||
|
];
|
||||||
|
|
||||||
|
$buttonTextPalette = [
|
||||||
|
'#ffffff' => 'White',
|
||||||
|
'#6c757d' => 'Gray',
|
||||||
|
'#000000' => 'Black',
|
||||||
|
];
|
||||||
|
|
||||||
|
$currentButtonBgColor = strtolower($template['button_bg_color'] ?? '#007bff');
|
||||||
|
$currentButtonTextColor = strtolower($template['button_text_color'] ?? '#ffffff');
|
||||||
|
|
||||||
|
if (!array_key_exists($currentButtonBgColor, array_change_key_case($buttonBgPalette, CASE_LOWER))) {
|
||||||
|
$currentButtonBgColor = '#0d6efd';
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!array_key_exists($currentButtonTextColor, array_change_key_case($buttonTextPalette, CASE_LOWER))) {
|
||||||
|
$currentButtonTextColor = '#ffffff';
|
||||||
|
}
|
||||||
?>
|
?>
|
||||||
<!doctype html>
|
<!doctype html>
|
||||||
<html lang="en">
|
<html lang="en">
|
||||||
@ -34,7 +70,64 @@ $routines = $stmt->fetchAll(PDO::FETCH_ASSOC);
|
|||||||
<link rel="icon" href="assets/images/favicon-32x32.png" type="image/png" />
|
<link rel="icon" href="assets/images/favicon-32x32.png" type="image/png" />
|
||||||
<link href="https://cdn.jsdelivr.net/npm/select2@4.1.0-rc.0/dist/css/select2.min.css" rel="stylesheet" />
|
<link href="https://cdn.jsdelivr.net/npm/select2@4.1.0-rc.0/dist/css/select2.min.css" rel="stylesheet" />
|
||||||
<?php include('cssinclude.php'); ?>
|
<?php include('cssinclude.php'); ?>
|
||||||
<!-- Include jQuery prima di Select2 -->
|
<style>
|
||||||
|
.color-palette {
|
||||||
|
display: flex;
|
||||||
|
flex-wrap: wrap;
|
||||||
|
gap: 10px;
|
||||||
|
margin-top: 8px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.color-option {
|
||||||
|
position: relative;
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
|
||||||
|
.color-option input {
|
||||||
|
position: absolute;
|
||||||
|
opacity: 0;
|
||||||
|
pointer-events: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.color-swatch {
|
||||||
|
width: 42px;
|
||||||
|
height: 42px;
|
||||||
|
border-radius: 10px;
|
||||||
|
border: 2px solid #d6dbe0;
|
||||||
|
display: inline-flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
box-shadow: 0 2px 6px rgba(0, 0, 0, 0.12);
|
||||||
|
transition: all 0.18s ease;
|
||||||
|
}
|
||||||
|
|
||||||
|
.color-option input:checked+.color-swatch {
|
||||||
|
border: 3px solid #111827;
|
||||||
|
transform: scale(1.08);
|
||||||
|
box-shadow: 0 0 0 4px rgba(13, 110, 253, 0.18);
|
||||||
|
}
|
||||||
|
|
||||||
|
.color-swatch .check-mark {
|
||||||
|
display: none;
|
||||||
|
font-size: 18px;
|
||||||
|
font-weight: 700;
|
||||||
|
color: #ffffff;
|
||||||
|
text-shadow: 0 1px 3px rgba(0, 0, 0, 0.7);
|
||||||
|
}
|
||||||
|
|
||||||
|
.color-option input:checked+.color-swatch .check-mark {
|
||||||
|
display: inline-block;
|
||||||
|
}
|
||||||
|
|
||||||
|
.color-name {
|
||||||
|
display: block;
|
||||||
|
font-size: 11px;
|
||||||
|
text-align: center;
|
||||||
|
margin-top: 4px;
|
||||||
|
color: #555;
|
||||||
|
max-width: 55px;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
<script src="https://code.jquery.com/jquery-3.6.0.min.js" integrity="sha256-/xUj+3OJU5yExlq6GSYGSHk7tPXikynS7ogEvDej/m4=" crossorigin="anonymous"></script>
|
<script src="https://code.jquery.com/jquery-3.6.0.min.js" integrity="sha256-/xUj+3OJU5yExlq6GSYGSHk7tPXikynS7ogEvDej/m4=" crossorigin="anonymous"></script>
|
||||||
<script src="https://cdn.jsdelivr.net/npm/select2@4.1.0-rc.0/dist/js/select2.min.js"></script>
|
<script src="https://cdn.jsdelivr.net/npm/select2@4.1.0-rc.0/dist/js/select2.min.js"></script>
|
||||||
<title>Edit Template <?= htmlspecialchars($titlewebsite, ENT_QUOTES, 'UTF-8'); ?></title>
|
<title>Edit Template <?= htmlspecialchars($titlewebsite, ENT_QUOTES, 'UTF-8'); ?></title>
|
||||||
@ -44,19 +137,22 @@ $routines = $stmt->fetchAll(PDO::FETCH_ASSOC);
|
|||||||
<div class="wrapper">
|
<div class="wrapper">
|
||||||
<?php include('include/navbar.php'); ?>
|
<?php include('include/navbar.php'); ?>
|
||||||
<?php include('include/topbar.php'); ?>
|
<?php include('include/topbar.php'); ?>
|
||||||
|
|
||||||
<div class="page-wrapper">
|
<div class="page-wrapper">
|
||||||
<div class="page-content">
|
<div class="page-content">
|
||||||
|
|
||||||
<div class="card mb-4">
|
<div class="card mb-4">
|
||||||
<div class="card-header">
|
<div class="card-header">
|
||||||
<h5 class="mb-0">Update XLS Template</h5>
|
<h5 class="mb-0">Update Template</h5>
|
||||||
</div>
|
</div>
|
||||||
<div class="card-body">
|
<div class="card-body">
|
||||||
<p class="mb-2">Edit the following form in order to update the selected import XLS template</p>
|
<p class="mb-2">Edit the following form in order to update the selected import template</p>
|
||||||
<p class="mb-2">Mandatory Fields</p>
|
<p class="mb-2">Mandatory Fields</p>
|
||||||
<ul class="mb-0">
|
<ul class="mb-0">
|
||||||
<li>Template Name</li>
|
<li>Template Name</li>
|
||||||
<li>Row Header and Column Header: where the title of the excel starts</li>
|
<li>Source Type</li>
|
||||||
<li>Schema</li>
|
<li>Schema and Client</li>
|
||||||
|
<li>Row Header and Column Header only for XLS templates</li>
|
||||||
</ul>
|
</ul>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@ -69,34 +165,44 @@ $routines = $stmt->fetchAll(PDO::FETCH_ASSOC);
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="card-body">
|
<div class="card-body">
|
||||||
<div class="col-12">
|
<div class="col-12">
|
||||||
<form id="editTemplateForm" method="POST">
|
<form id="editTemplateForm" method="POST">
|
||||||
<input type="hidden" name="id" value="<?php echo $template['id']; ?>">
|
<input type="hidden" name="id" value="<?php echo (int)$template['id']; ?>">
|
||||||
|
|
||||||
<div class="mb-3">
|
<div class="mb-3">
|
||||||
<label class="form-label"><?= htmlspecialchars($templatename, ENT_QUOTES, 'UTF-8'); ?> *</label>
|
<label class="form-label"><?= htmlspecialchars($templatename, ENT_QUOTES, 'UTF-8'); ?> *</label>
|
||||||
<input type="text" name="name" class="form-control" value="<?php echo htmlspecialchars($template['name']); ?>" required>
|
<input type="text" name="name" class="form-control" value="<?php echo htmlspecialchars($template['name'] ?? ''); ?>" required>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="mb-3">
|
<div class="mb-3">
|
||||||
|
<label class="form-label">Source Type *</label>
|
||||||
|
<select name="source_type" id="sourceType" class="form-control" required>
|
||||||
|
<option value="XLS" <?php echo (($template['source_type'] ?? 'XLS') === 'XLS') ? 'selected' : ''; ?>>XLS</option>
|
||||||
|
<option value="API" <?php echo (($template['source_type'] ?? 'XLS') === 'API') ? 'selected' : ''; ?>>API</option>
|
||||||
|
</select>
|
||||||
|
<small class="text-muted">Choose the source used by this template</small>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="mb-3" id="headerRowWrapper">
|
||||||
<label class="form-label"><?= htmlspecialchars($rowheader, ENT_QUOTES, 'UTF-8'); ?> *</label>
|
<label class="form-label"><?= htmlspecialchars($rowheader, ENT_QUOTES, 'UTF-8'); ?> *</label>
|
||||||
<input type="number" name="header_row" class="form-control" value="<?php echo $template['header_row']; ?>" required>
|
<input type="number" name="header_row" id="headerRow" class="form-control" value="<?php echo htmlspecialchars($template['header_row'] ?? ''); ?>">
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="mb-3">
|
<div class="mb-3" id="startColumnWrapper">
|
||||||
<label class="form-label"><?= htmlspecialchars($columnheader, ENT_QUOTES, 'UTF-8'); ?>*</label>
|
<label class="form-label"><?= htmlspecialchars($columnheader, ENT_QUOTES, 'UTF-8'); ?> *</label>
|
||||||
<input type="text" name="start_column" class="form-control" value="<?php echo htmlspecialchars($template['start_column']); ?>" required>
|
<input type="text" name="start_column" id="startColumn" class="form-control" value="<?php echo htmlspecialchars($template['start_column'] ?? ''); ?>">
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="mb-3">
|
<div class="mb-3">
|
||||||
<label class="form-label"><?= htmlspecialchars($desctemplate, ENT_QUOTES, 'UTF-8'); ?></label>
|
<label class="form-label"><?= htmlspecialchars($desctemplate, ENT_QUOTES, 'UTF-8'); ?></label>
|
||||||
<textarea name="description" class="form-control"><?php echo htmlspecialchars($template['description']); ?></textarea>
|
<textarea name="description" class="form-control"><?php echo htmlspecialchars($template['description'] ?? ''); ?></textarea>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="mb-3">
|
<div class="mb-3">
|
||||||
<label class="form-label"><?= htmlspecialchars($desttable, ENT_QUOTES, 'UTF-8'); ?>*</label>
|
<label class="form-label"><?= htmlspecialchars($desttable, ENT_QUOTES, 'UTF-8'); ?> *</label>
|
||||||
<input type="text" name="target_table" class="form-control" value="<?php echo htmlspecialchars($template['target_table']); ?>" readonly required>
|
<input type="text" name="target_table" class="form-control" value="<?php echo htmlspecialchars($template['target_table'] ?? 'datadb'); ?>" readonly required>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="mb-3">
|
<div class="mb-3">
|
||||||
@ -110,12 +216,46 @@ $routines = $stmt->fetchAll(PDO::FETCH_ASSOC);
|
|||||||
|
|
||||||
<div class="mb-3">
|
<div class="mb-3">
|
||||||
<label class="form-label">Button Background Color</label>
|
<label class="form-label">Button Background Color</label>
|
||||||
<input type="color" name="button_bg_color" class="form-control" value="<?php echo htmlspecialchars($template['button_bg_color'] ?? '#007bff'); ?>">
|
|
||||||
|
<div class="color-palette">
|
||||||
|
<?php foreach ($buttonBgPalette as $colorValue => $colorLabel): ?>
|
||||||
|
<?php $isChecked = strtolower($colorValue) === $currentButtonBgColor; ?>
|
||||||
|
|
||||||
|
<label class="color-option" title="<?= htmlspecialchars($colorLabel, ENT_QUOTES, 'UTF-8'); ?>">
|
||||||
|
<input
|
||||||
|
type="radio"
|
||||||
|
name="button_bg_color"
|
||||||
|
value="<?= htmlspecialchars($colorValue, ENT_QUOTES, 'UTF-8'); ?>"
|
||||||
|
<?= $isChecked ? 'checked' : ''; ?>>
|
||||||
|
<span class="color-swatch" style="background-color: <?= htmlspecialchars($colorValue, ENT_QUOTES, 'UTF-8'); ?>;">
|
||||||
|
<span class="check-mark">✓</span>
|
||||||
|
</span>
|
||||||
|
<span class="color-name"><?= htmlspecialchars($colorLabel, ENT_QUOTES, 'UTF-8'); ?></span>
|
||||||
|
</label>
|
||||||
|
<?php endforeach; ?>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="mb-3">
|
<div class="mb-3">
|
||||||
<label class="form-label">Button Text Color</label>
|
<label class="form-label">Button Text Color</label>
|
||||||
<input type="color" name="button_text_color" class="form-control" value="<?php echo htmlspecialchars($template['button_text_color'] ?? '#ffffff'); ?>">
|
|
||||||
|
<div class="color-palette">
|
||||||
|
<?php foreach ($buttonTextPalette as $colorValue => $colorLabel): ?>
|
||||||
|
<?php $isChecked = strtolower($colorValue) === $currentButtonTextColor; ?>
|
||||||
|
|
||||||
|
<label class="color-option" title="<?= htmlspecialchars($colorLabel, ENT_QUOTES, 'UTF-8'); ?>">
|
||||||
|
<input
|
||||||
|
type="radio"
|
||||||
|
name="button_text_color"
|
||||||
|
value="<?= htmlspecialchars($colorValue, ENT_QUOTES, 'UTF-8'); ?>"
|
||||||
|
<?= $isChecked ? 'checked' : ''; ?>>
|
||||||
|
<span class="color-swatch" style="background-color: <?= htmlspecialchars($colorValue, ENT_QUOTES, 'UTF-8'); ?>;">
|
||||||
|
<span class="check-mark">✓</span>
|
||||||
|
</span>
|
||||||
|
<span class="color-name"><?= htmlspecialchars($colorLabel, ENT_QUOTES, 'UTF-8'); ?></span>
|
||||||
|
</label>
|
||||||
|
<?php endforeach; ?>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="mb-3">
|
<div class="mb-3">
|
||||||
@ -128,7 +268,7 @@ $routines = $stmt->fetchAll(PDO::FETCH_ASSOC);
|
|||||||
<select name="client_id" id="clientSelect" class="form-control" required>
|
<select name="client_id" id="clientSelect" class="form-control" required>
|
||||||
<option value="">Select a client...</option>
|
<option value="">Select a client...</option>
|
||||||
</select>
|
</select>
|
||||||
<span id="clientLoadingStatus" class="text-muted" style="margin-left: 10px; display: none;">Recupero clienti in corso...</span>
|
<span id="clientLoadingStatus" class="text-muted" style="margin-left: 10px; display: none;">Loading clients...</span>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="mb-3">
|
<div class="mb-3">
|
||||||
@ -136,7 +276,7 @@ $routines = $stmt->fetchAll(PDO::FETCH_ASSOC);
|
|||||||
<select name="schema_id" id="schemaSelect" class="form-control" required>
|
<select name="schema_id" id="schemaSelect" class="form-control" required>
|
||||||
<option value="">Select a schema...</option>
|
<option value="">Select a schema...</option>
|
||||||
</select>
|
</select>
|
||||||
<span id="schemaLoadingStatus" class="text-muted" style="margin-left: 10px; display: none;">Caricamento schemi in corso...</span>
|
<span id="schemaLoadingStatus" class="text-muted" style="margin-left: 10px; display: none;">Loading schemas...</span>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="mb-3">
|
<div class="mb-3">
|
||||||
@ -144,11 +284,12 @@ $routines = $stmt->fetchAll(PDO::FETCH_ASSOC);
|
|||||||
<select name="idroutine" id="routineSelect" class="form-control">
|
<select name="idroutine" id="routineSelect" class="form-control">
|
||||||
<option value="">Select a routine...</option>
|
<option value="">Select a routine...</option>
|
||||||
<?php foreach ($routines as $routine): ?>
|
<?php foreach ($routines as $routine): ?>
|
||||||
<option value="<?php echo $routine['idroutine']; ?>" <?php echo ($template['idroutine'] ?? '') == $routine['idroutine'] ? 'selected' : ''; ?>>
|
<option value="<?php echo $routine['idroutine']; ?>" <?php echo (($template['idroutine'] ?? '') == $routine['idroutine']) ? 'selected' : ''; ?>>
|
||||||
<?php echo htmlspecialchars($routine['name']); ?>
|
<?php echo htmlspecialchars($routine['name']); ?>
|
||||||
</option>
|
</option>
|
||||||
<?php endforeach; ?>
|
<?php endforeach; ?>
|
||||||
</select>
|
</select>
|
||||||
|
|
||||||
<div id="routineDetails" class="mt-2" style="display: none;">
|
<div id="routineDetails" class="mt-2" style="display: none;">
|
||||||
<h6>Routine Details</h6>
|
<h6>Routine Details</h6>
|
||||||
<p><strong>Name:</strong> <span id="routineName"></span></p>
|
<p><strong>Name:</strong> <span id="routineName"></span></p>
|
||||||
@ -166,8 +307,10 @@ $routines = $stmt->fetchAll(PDO::FETCH_ASSOC);
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="overlay toggle-icon"></div>
|
<div class="overlay toggle-icon"></div>
|
||||||
<a href="javaScript:;" class="back-to-top"><i class='bx bxs-up-arrow-alt'></i></a>
|
<a href="javaScript:;" class="back-to-top"><i class='bx bxs-up-arrow-alt'></i></a>
|
||||||
<?php include('include/footer.php'); ?>
|
<?php include('include/footer.php'); ?>
|
||||||
@ -175,9 +318,8 @@ $routines = $stmt->fetchAll(PDO::FETCH_ASSOC);
|
|||||||
|
|
||||||
<script>
|
<script>
|
||||||
document.addEventListener("DOMContentLoaded", function() {
|
document.addEventListener("DOMContentLoaded", function() {
|
||||||
// Verifica che jQuery sia caricato
|
|
||||||
if (typeof jQuery === 'undefined') {
|
if (typeof jQuery === 'undefined') {
|
||||||
alert("Errore: jQuery non è caricato. Contatta l'amministratore.");
|
alert("Error: jQuery is not loaded.");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -192,12 +334,15 @@ $routines = $stmt->fetchAll(PDO::FETCH_ASSOC);
|
|||||||
const routineAction2 = document.getElementById("routineAction2");
|
const routineAction2 = document.getElementById("routineAction2");
|
||||||
const routineAction3 = document.getElementById("routineAction3");
|
const routineAction3 = document.getElementById("routineAction3");
|
||||||
|
|
||||||
if (!form || !clientLoadingStatus || !schemaLoadingStatus || !routineSelect || !routineDetails) {
|
const sourceType = document.getElementById("sourceType");
|
||||||
alert("Errore: Uno o più elementi della pagina non sono stati trovati. Contatta l'amministratore.");
|
const headerRowWrapper = document.getElementById("headerRowWrapper");
|
||||||
return;
|
const startColumnWrapper = document.getElementById("startColumnWrapper");
|
||||||
}
|
const headerRow = document.getElementById("headerRow");
|
||||||
|
const startColumn = document.getElementById("startColumn");
|
||||||
|
|
||||||
|
const selectedClientId = <?php echo json_encode((int)($template['idclient'] ?? 0)); ?>;
|
||||||
|
const selectedSchemaId = <?php echo json_encode((int)($template['idschema'] ?? 0)); ?>;
|
||||||
|
|
||||||
// Inizializza Select2
|
|
||||||
$('#clientSelect').select2({
|
$('#clientSelect').select2({
|
||||||
placeholder: "Search for a client...",
|
placeholder: "Search for a client...",
|
||||||
allowClear: true
|
allowClear: true
|
||||||
@ -213,108 +358,173 @@ $routines = $stmt->fetchAll(PDO::FETCH_ASSOC);
|
|||||||
allowClear: true
|
allowClear: true
|
||||||
});
|
});
|
||||||
|
|
||||||
// Carica i clienti
|
function updateSourceFields() {
|
||||||
|
const selectedSource = sourceType.value;
|
||||||
|
|
||||||
|
if (selectedSource === 'API') {
|
||||||
|
headerRowWrapper.style.opacity = '0.6';
|
||||||
|
startColumnWrapper.style.opacity = '0.6';
|
||||||
|
|
||||||
|
headerRow.required = false;
|
||||||
|
startColumn.required = false;
|
||||||
|
|
||||||
|
headerRow.disabled = true;
|
||||||
|
startColumn.disabled = true;
|
||||||
|
} else {
|
||||||
|
headerRowWrapper.style.opacity = '1';
|
||||||
|
startColumnWrapper.style.opacity = '1';
|
||||||
|
|
||||||
|
headerRow.required = true;
|
||||||
|
startColumn.required = true;
|
||||||
|
|
||||||
|
headerRow.disabled = false;
|
||||||
|
startColumn.disabled = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
sourceType.addEventListener('change', updateSourceFields);
|
||||||
|
updateSourceFields();
|
||||||
|
|
||||||
|
function formatClientLabel(client) {
|
||||||
|
const nome = client.Nominativo || "Name not available";
|
||||||
|
const id = client.IdCliente || "";
|
||||||
|
const codiceCliente = (client.CodiceCliente ?? client.codiceCliente ?? "").toString().trim();
|
||||||
|
const suffix = (codiceCliente.split("_")[1] || "").trim();
|
||||||
|
const shortCode = suffix || (codiceCliente ? codiceCliente.charAt(0) : "--");
|
||||||
|
|
||||||
|
return `${nome.trim()} - ${shortCode} (ID: ${id})`;
|
||||||
|
}
|
||||||
|
|
||||||
async function loadClients() {
|
async function loadClients() {
|
||||||
try {
|
try {
|
||||||
clientLoadingStatus.style.display = 'inline';
|
clientLoadingStatus.style.display = 'inline';
|
||||||
clientLoadingStatus.textContent = 'Recupero clienti in corso...';
|
clientLoadingStatus.textContent = 'Loading clients...';
|
||||||
|
|
||||||
const response = await fetch("get_clienti.php", {
|
const response = await fetch("get_clienti.php", {
|
||||||
method: "GET",
|
method: "GET",
|
||||||
headers: {
|
headers: {
|
||||||
"Content-Type": "application/json"
|
"Content-Type": "application/json"
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
const data = await response.json();
|
const data = await response.json();
|
||||||
if (!response.ok) throw new Error(data.error || `Errore HTTP: ${response.status}`);
|
|
||||||
|
if (!response.ok) {
|
||||||
|
throw new Error(data.error || `HTTP error: ${response.status}`);
|
||||||
|
}
|
||||||
|
|
||||||
const select = document.getElementById("clientSelect");
|
const select = document.getElementById("clientSelect");
|
||||||
select.innerHTML = '<option value="">Select a client...</option>';
|
select.innerHTML = '<option value="">Select a client...</option>';
|
||||||
|
|
||||||
data.value.forEach(client => {
|
data.value.forEach(client => {
|
||||||
const nome = client.Nominativo || "Nome non disponibile";
|
const id = client.IdCliente || "";
|
||||||
const id = client.IdCliente || "ID non disponibile";
|
const option = new Option(formatClientLabel(client), id);
|
||||||
const option = new Option(`${nome.trim()} (ID: ${id})`, id);
|
|
||||||
if (parseInt(id) === parseInt(<?php echo json_encode($template['idclient'] ?? 0); ?>)) {
|
if (parseInt(id) === parseInt(selectedClientId)) {
|
||||||
option.selected = true;
|
option.selected = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
select.add(option);
|
select.add(option);
|
||||||
});
|
});
|
||||||
|
|
||||||
$(select).trigger('change');
|
$(select).trigger('change');
|
||||||
clientLoadingStatus.textContent = "Clienti caricati.";
|
clientLoadingStatus.textContent = "Clients loaded.";
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
clientLoadingStatus.textContent = "Errore nel caricamento.";
|
clientLoadingStatus.textContent = "Loading error.";
|
||||||
|
|
||||||
Swal.fire({
|
Swal.fire({
|
||||||
title: "Errore!",
|
title: "Error!",
|
||||||
text: "Impossibile caricare i clienti: " + error.message,
|
text: "Unable to load clients: " + error.message,
|
||||||
icon: "error",
|
icon: "error",
|
||||||
confirmButtonText: "OK"
|
confirmButtonText: "OK"
|
||||||
});
|
});
|
||||||
} finally {
|
} finally {
|
||||||
setTimeout(() => clientLoadingStatus.style.display = 'none', 2000);
|
setTimeout(() => clientLoadingStatus.style.display = 'none', 1500);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Carica gli schemi
|
|
||||||
async function loadSchemas() {
|
async function loadSchemas() {
|
||||||
try {
|
try {
|
||||||
schemaLoadingStatus.style.display = 'inline';
|
schemaLoadingStatus.style.display = 'inline';
|
||||||
schemaLoadingStatus.textContent = 'Caricamento schemi in corso...';
|
schemaLoadingStatus.textContent = 'Loading schemas...';
|
||||||
|
|
||||||
const response = await fetch("get_schemi.php", {
|
const response = await fetch("get_schemi.php", {
|
||||||
method: "GET",
|
method: "GET",
|
||||||
headers: {
|
headers: {
|
||||||
"Content-Type": "application/json"
|
"Content-Type": "application/json"
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
const data = await response.json();
|
const data = await response.json();
|
||||||
if (!response.ok) throw new Error(data.error || `Errore HTTP: ${response.status}`);
|
|
||||||
|
if (!response.ok) {
|
||||||
|
throw new Error(data.error || `HTTP error: ${response.status}`);
|
||||||
|
}
|
||||||
|
|
||||||
const select = document.getElementById("schemaSelect");
|
const select = document.getElementById("schemaSelect");
|
||||||
select.innerHTML = '<option value="">Select a schema...</option>';
|
select.innerHTML = '<option value="">Select a schema...</option>';
|
||||||
data.value.forEach(schema => {
|
|
||||||
const nome = schema.Nome || "Nome non disponibile";
|
const sortedSchemas = [...data.value].sort((a, b) => {
|
||||||
const id = schema.IdSchemaCustomFields || "ID non disponibile";
|
const nomeA = (a.Nome || "").trim().toLowerCase();
|
||||||
|
const nomeB = (b.Nome || "").trim().toLowerCase();
|
||||||
|
return nomeA.localeCompare(nomeB, 'it', {
|
||||||
|
sensitivity: 'base'
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
sortedSchemas.forEach(schema => {
|
||||||
|
const nome = schema.Nome || "Name not available";
|
||||||
|
const id = schema.IdSchemaCustomFields || "";
|
||||||
const option = new Option(`${nome.trim()} (ID: ${id})`, id);
|
const option = new Option(`${nome.trim()} (ID: ${id})`, id);
|
||||||
if (parseInt(id) === parseInt(<?php echo json_encode($template['idschema'] ?? 0); ?>)) {
|
|
||||||
|
if (parseInt(id) === parseInt(selectedSchemaId)) {
|
||||||
option.selected = true;
|
option.selected = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
select.add(option);
|
select.add(option);
|
||||||
});
|
});
|
||||||
|
|
||||||
$(select).trigger('change');
|
$(select).trigger('change');
|
||||||
schemaLoadingStatus.textContent = "Schemi caricati.";
|
schemaLoadingStatus.textContent = "Schemas loaded.";
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
schemaLoadingStatus.textContent = "Errore nel caricamento.";
|
schemaLoadingStatus.textContent = "Loading error.";
|
||||||
|
|
||||||
Swal.fire({
|
Swal.fire({
|
||||||
title: "Errore!",
|
title: "Error!",
|
||||||
text: "Impossibile caricare gli schemi: " + error.message,
|
text: "Unable to load schemas: " + error.message,
|
||||||
icon: "error",
|
icon: "error",
|
||||||
confirmButtonText: "OK"
|
confirmButtonText: "OK"
|
||||||
});
|
});
|
||||||
} finally {
|
} finally {
|
||||||
setTimeout(() => schemaLoadingStatus.style.display = 'none', 2000);
|
setTimeout(() => schemaLoadingStatus.style.display = 'none', 1500);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Carica i dati
|
|
||||||
async function loadData() {
|
async function loadData() {
|
||||||
try {
|
try {
|
||||||
await loadClients();
|
await loadClients();
|
||||||
await loadSchemas();
|
await loadSchemas();
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
Swal.fire({
|
Swal.fire({
|
||||||
title: "Errore!",
|
title: "Error!",
|
||||||
text: "Errore nel caricamento dei dati: " + error.message,
|
text: "Error while loading data: " + error.message,
|
||||||
icon: "error",
|
icon: "error",
|
||||||
confirmButtonText: "OK"
|
confirmButtonText: "OK"
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
loadData();
|
loadData();
|
||||||
|
|
||||||
// Routine dettagli
|
|
||||||
const routines = <?php echo json_encode($routines); ?>;
|
const routines = <?php echo json_encode($routines); ?>;
|
||||||
|
|
||||||
function updateRoutineDetails() {
|
function updateRoutineDetails() {
|
||||||
const selectedId = routineSelect.value;
|
const selectedId = routineSelect.value;
|
||||||
routineDetails.style.display = selectedId ? 'block' : 'none';
|
routineDetails.style.display = selectedId ? 'block' : 'none';
|
||||||
|
|
||||||
if (selectedId) {
|
if (selectedId) {
|
||||||
const routine = routines.find(r => r.idroutine == selectedId);
|
const routine = routines.find(r => r.idroutine == selectedId);
|
||||||
|
|
||||||
if (routine) {
|
if (routine) {
|
||||||
routineName.textContent = routine.name || 'N/A';
|
routineName.textContent = routine.name || 'N/A';
|
||||||
routineDescription.textContent = routine.description || 'N/A';
|
routineDescription.textContent = routine.description || 'N/A';
|
||||||
@ -336,10 +546,10 @@ $routines = $stmt->fetchAll(PDO::FETCH_ASSOC);
|
|||||||
routineAction3.textContent = '';
|
routineAction3.textContent = '';
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
routineSelect.addEventListener('change', updateRoutineDetails);
|
|
||||||
updateRoutineDetails(); // Inizializza dettagli se una routine è preselezionata
|
|
||||||
|
|
||||||
// Submit del form
|
routineSelect.addEventListener('change', updateRoutineDetails);
|
||||||
|
updateRoutineDetails();
|
||||||
|
|
||||||
form.addEventListener("submit", function(e) {
|
form.addEventListener("submit", function(e) {
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
|
|
||||||
@ -351,8 +561,8 @@ $routines = $stmt->fetchAll(PDO::FETCH_ASSOC);
|
|||||||
|
|
||||||
if (!clientId) {
|
if (!clientId) {
|
||||||
Swal.fire({
|
Swal.fire({
|
||||||
title: "Errore!",
|
title: "Error!",
|
||||||
text: "Per favore seleziona un cliente.",
|
text: "Please select a client.",
|
||||||
icon: "error",
|
icon: "error",
|
||||||
confirmButtonText: "OK"
|
confirmButtonText: "OK"
|
||||||
});
|
});
|
||||||
@ -373,8 +583,8 @@ $routines = $stmt->fetchAll(PDO::FETCH_ASSOC);
|
|||||||
|
|
||||||
if (!schemaId) {
|
if (!schemaId) {
|
||||||
Swal.fire({
|
Swal.fire({
|
||||||
title: "Errore!",
|
title: "Error!",
|
||||||
text: "Per favore seleziona uno schema.",
|
text: "Please select a schema.",
|
||||||
icon: "error",
|
icon: "error",
|
||||||
confirmButtonText: "OK"
|
confirmButtonText: "OK"
|
||||||
});
|
});
|
||||||
@ -387,10 +597,10 @@ $routines = $stmt->fetchAll(PDO::FETCH_ASSOC);
|
|||||||
const nameMatch = optionText.match(/^(.+?)(?:\s*\(ID:\s*\d+\))?$/);
|
const nameMatch = optionText.match(/^(.+?)(?:\s*\(ID:\s*\d+\))?$/);
|
||||||
schemaName = nameMatch ? nameMatch[1].trim() : optionText;
|
schemaName = nameMatch ? nameMatch[1].trim() : optionText;
|
||||||
}
|
}
|
||||||
|
|
||||||
formData.append("idschema", schemaId);
|
formData.append("idschema", schemaId);
|
||||||
formData.append("schemaname", schemaName);
|
formData.append("schemaname", schemaName);
|
||||||
|
|
||||||
// Aggiungi idroutine
|
|
||||||
const routineId = routineSelect.value;
|
const routineId = routineSelect.value;
|
||||||
formData.append("idroutine", routineId);
|
formData.append("idroutine", routineId);
|
||||||
|
|
||||||
@ -402,8 +612,8 @@ $routines = $stmt->fetchAll(PDO::FETCH_ASSOC);
|
|||||||
.then(data => {
|
.then(data => {
|
||||||
if (data.success) {
|
if (data.success) {
|
||||||
Swal.fire({
|
Swal.fire({
|
||||||
title: "Successo!",
|
title: "Success!",
|
||||||
text: "Template aggiornato con successo!",
|
text: "Template updated successfully!",
|
||||||
icon: "success",
|
icon: "success",
|
||||||
confirmButtonText: "OK"
|
confirmButtonText: "OK"
|
||||||
}).then(() => {
|
}).then(() => {
|
||||||
@ -411,17 +621,17 @@ $routines = $stmt->fetchAll(PDO::FETCH_ASSOC);
|
|||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
Swal.fire({
|
Swal.fire({
|
||||||
title: "Errore!",
|
title: "Error!",
|
||||||
text: data.message,
|
text: data.message,
|
||||||
icon: "error",
|
icon: "error",
|
||||||
confirmButtonText: "OK"
|
confirmButtonText: "OK"
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
.catch(error => {
|
.catch(() => {
|
||||||
Swal.fire({
|
Swal.fire({
|
||||||
title: "Errore!",
|
title: "Error!",
|
||||||
text: "Si è verificato un errore imprevisto.",
|
text: "An unexpected error occurred.",
|
||||||
icon: "error",
|
icon: "error",
|
||||||
confirmButtonText: "OK"
|
confirmButtonText: "OK"
|
||||||
});
|
});
|
||||||
|
|||||||
@ -314,3 +314,20 @@
|
|||||||
2026-02-26 15:01:32 [AnagraficaCertestObject] Autenticazione fallita: HTTP 400, Errore cURL: , Risposta: {"title":"Bad Request","status":400,"detail":"Cannot persist the object. It was modified or deleted (purged) by another application.","instance":"POST /api/authentication/authenticate","errorCode":"96bfc1252b"}
|
2026-02-26 15:01:32 [AnagraficaCertestObject] Autenticazione fallita: HTTP 400, Errore cURL: , Risposta: {"title":"Bad Request","status":400,"detail":"Cannot persist the object. It was modified or deleted (purged) by another application.","instance":"POST /api/authentication/authenticate","errorCode":"96bfc1252b"}
|
||||||
2026-02-28 21:01:27 [AnagraficaCertestService] Autenticazione fallita: HTTP 400, Errore cURL: , Risposta: {"title":"Bad Request","status":400,"detail":"Cannot persist the object. It was modified or deleted (purged) by another application.","instance":"POST /api/authentication/authenticate","errorCode":"96bfc1252b"}
|
2026-02-28 21:01:27 [AnagraficaCertestService] Autenticazione fallita: HTTP 400, Errore cURL: , Risposta: {"title":"Bad Request","status":400,"detail":"Cannot persist the object. It was modified or deleted (purged) by another application.","instance":"POST /api/authentication/authenticate","errorCode":"96bfc1252b"}
|
||||||
2026-03-01 19:11:58 [AnagraficaCertestObject] Autenticazione fallita: HTTP 400, Errore cURL: , Risposta: {"title":"Bad Request","status":400,"detail":"Cannot persist the object. It was modified or deleted (purged) by another application.","instance":"POST /api/authentication/authenticate","errorCode":"96bfc1252b"}
|
2026-03-01 19:11:58 [AnagraficaCertestObject] Autenticazione fallita: HTTP 400, Errore cURL: , Risposta: {"title":"Bad Request","status":400,"detail":"Cannot persist the object. It was modified or deleted (purged) by another application.","instance":"POST /api/authentication/authenticate","errorCode":"96bfc1252b"}
|
||||||
|
2026-03-18 15:51:45 [AnagraficaCertestService] Autenticazione fallita: HTTP 400, Errore cURL: , Risposta: {"title":"Bad Request","status":400,"detail":"Cannot persist the object. It was modified or deleted (purged) by another application.","instance":"POST /api/authentication/authenticate","errorCode":"96bfc1252b"}
|
||||||
|
2026-03-19 09:50:34 [AnagraficaCertestService] Autenticazione fallita: HTTP 400, Errore cURL: , Risposta: {"title":"Bad Request","status":400,"detail":"Cannot persist the object. It was modified or deleted (purged) by another application.","instance":"POST /api/authentication/authenticate","errorCode":"96bfc1252b"}
|
||||||
|
2026-03-25 14:13:34 - Autenticazione fallita: HTTP 503, Errore cURL: , Risposta: The service is unavailable.
|
||||||
|
2026-03-25 14:13:34 - Autenticazione fallita: HTTP 503, Errore cURL: , Risposta: The service is unavailable.
|
||||||
|
2026-03-26 10:57:18 [AnagraficaCertestObject] Autenticazione fallita: HTTP 500, Errore cURL: , Risposta: {"title":"Internal Server Error","status":500,"detail":"Timeout expired. The timeout period elapsed prior to obtaining a connection from the pool. This may have occurred because all pooled connections were in use and max pool size was reached.","instance":"POST /api/authentication/authenticate","errorCode":"63ab532c6"}
|
||||||
|
2026-03-26 10:57:21 [AnagraficaCertestObject] Autenticazione fallita: HTTP 500, Errore cURL: , Risposta: {"title":"Internal Server Error","status":500,"detail":"Timeout expired. The timeout period elapsed prior to obtaining a connection from the pool. This may have occurred because all pooled connections were in use and max pool size was reached.","instance":"POST /api/authentication/authenticate","errorCode":"63ab532c6"}
|
||||||
|
2026-03-26 10:57:21 [AnagraficaCertestService] Autenticazione fallita: HTTP 500, Errore cURL: , Risposta: {"title":"Internal Server Error","status":500,"detail":"Timeout expired. The timeout period elapsed prior to obtaining a connection from the pool. This may have occurred because all pooled connections were in use and max pool size was reached.","instance":"POST /api/authentication/authenticate","errorCode":"63ab532c6"}
|
||||||
|
2026-03-26 10:57:21 [MoltiplicatorePrezzo] Autenticazione fallita: HTTP 500, Errore cURL: , Risposta: {"title":"Internal Server Error","status":500,"detail":"Timeout expired. The timeout period elapsed prior to obtaining a connection from the pool. This may have occurred because all pooled connections were in use and max pool size was reached.","instance":"POST /api/authentication/authenticate","errorCode":"63ab532c6"}
|
||||||
|
2026-03-26 10:57:36 [AnagraficaCertestObject] Autenticazione fallita: HTTP 500, Errore cURL: , Risposta: {"title":"Internal Server Error","status":500,"detail":"Timeout expired. The timeout period elapsed prior to obtaining a connection from the pool. This may have occurred because all pooled connections were in use and max pool size was reached.","instance":"POST /api/authentication/authenticate","errorCode":"63ab532c6"}
|
||||||
|
2026-03-26 10:57:36 [AnagraficaCertestService] Autenticazione fallita: HTTP 500, Errore cURL: , Risposta: {"title":"Internal Server Error","status":500,"detail":"Timeout expired. The timeout period elapsed prior to obtaining a connection from the pool. This may have occurred because all pooled connections were in use and max pool size was reached.","instance":"POST /api/authentication/authenticate","errorCode":"63ab532c6"}
|
||||||
|
2026-03-26 10:57:36 [MoltiplicatorePrezzo] Autenticazione fallita: HTTP 500, Errore cURL: , Risposta: {"title":"Internal Server Error","status":500,"detail":"Timeout expired. The timeout period elapsed prior to obtaining a connection from the pool. This may have occurred because all pooled connections were in use and max pool size was reached.","instance":"POST /api/authentication/authenticate","errorCode":"63ab532c6"}
|
||||||
|
2026-03-26 10:57:41 [MoltiplicatorePrezzo] Autenticazione fallita: HTTP 500, Errore cURL: , Risposta: {"title":"Internal Server Error","status":500,"detail":"Timeout expired. The timeout period elapsed prior to obtaining a connection from the pool. This may have occurred because all pooled connections were in use and max pool size was reached.","instance":"POST /api/authentication/authenticate","errorCode":"63ab532c6"}
|
||||||
|
2026-03-26 10:57:56 [AnagraficaCertestObject] Autenticazione fallita: HTTP 500, Errore cURL: , Risposta: {"title":"Internal Server Error","status":500,"detail":"Timeout expired. The timeout period elapsed prior to obtaining a connection from the pool. This may have occurred because all pooled connections were in use and max pool size was reached.","instance":"POST /api/authentication/authenticate","errorCode":"63ab532c6"}
|
||||||
|
2026-03-26 10:58:11 [AnagraficaCertestService] Autenticazione fallita: HTTP 500, Errore cURL: , Risposta: {"title":"Internal Server Error","status":500,"detail":"Timeout expired. The timeout period elapsed prior to obtaining a connection from the pool. This may have occurred because all pooled connections were in use and max pool size was reached.","instance":"POST /api/authentication/authenticate","errorCode":"63ab532c6"}
|
||||||
|
2026-04-30 15:01:25 - Errore nel recupero dati: HTTP 404, Risposta: {"title":"Not Found","status":404,"detail":"Not Found","instance":"GET /api/odata/Rapporto(2621521)","errorCode":"d25cbd678"}
|
||||||
|
2026-04-30 15:02:04 - Errore nel recupero dati: HTTP 404, Risposta:
|
||||||
|
2026-04-30 15:03:19 - Errore nella richiesta: URL rejected: Malformed input to a URL function
|
||||||
|
|||||||
555
public/userarea/exportLims_gridData.js
Normal file
555
public/userarea/exportLims_gridData.js
Normal file
@ -0,0 +1,555 @@
|
|||||||
|
/**
|
||||||
|
* exportLims_gridData.js — Export to LIMS using gridData (for imported.php)
|
||||||
|
*
|
||||||
|
* Replaces export_to_lims.js for pages that use gridData instead of DOM-rendered rows.
|
||||||
|
* Single export + batch export (Export All) with validation.
|
||||||
|
*/
|
||||||
|
(function () {
|
||||||
|
'use strict';
|
||||||
|
|
||||||
|
let pendingConfirmHandler = null;
|
||||||
|
let batchRunning = false;
|
||||||
|
Object.defineProperty(window, "batchRunning", { get: () => batchRunning });
|
||||||
|
|
||||||
|
let batchCancelled = false;
|
||||||
|
let pendingBatchConfirmHandler = null;
|
||||||
|
|
||||||
|
// ── Helpers ──────────────────────────────────────────────────────────
|
||||||
|
|
||||||
|
function cleanupBackdrop() {
|
||||||
|
document.querySelectorAll(".modal-backdrop").forEach(b => b.remove());
|
||||||
|
document.body.classList.remove("modal-open");
|
||||||
|
document.body.style.paddingRight = "";
|
||||||
|
const overlay = document.querySelector(".overlay.toggle-icon");
|
||||||
|
if (overlay) overlay.style.display = "none";
|
||||||
|
}
|
||||||
|
|
||||||
|
function getGridRow(iddatadb) {
|
||||||
|
return document.querySelector(`.grid-row[data-id="${iddatadb}"]`);
|
||||||
|
}
|
||||||
|
|
||||||
|
function getRowIndexByIddatadb(iddatadb) {
|
||||||
|
return (window.gridData || []).findIndex(r => String(r.iddatadb) === String(iddatadb));
|
||||||
|
}
|
||||||
|
|
||||||
|
// ── Validation ──────────────────────────────────────────────────────
|
||||||
|
|
||||||
|
async function validateRows(rowsToValidate) {
|
||||||
|
const response = await fetch("validate_export.php", {
|
||||||
|
method: "POST",
|
||||||
|
headers: { "Content-Type": "application/json" },
|
||||||
|
body: JSON.stringify({ rows: rowsToValidate }),
|
||||||
|
});
|
||||||
|
if (!response.ok) throw new Error(`Validation HTTP error: ${response.status}`);
|
||||||
|
return response.json();
|
||||||
|
}
|
||||||
|
|
||||||
|
function clearValidationErrors() {
|
||||||
|
// Clear from gridData
|
||||||
|
(window.gridData || []).forEach(row => { delete row._validationErrors; delete row._exportError; });
|
||||||
|
|
||||||
|
document.querySelectorAll(".grid-cell.validation-error").forEach(cell => {
|
||||||
|
cell.classList.remove("validation-error");
|
||||||
|
cell.querySelectorAll(".input-validation-error").forEach(el => el.classList.remove("input-validation-error"));
|
||||||
|
const tooltip = cell.querySelector(".validation-tooltip");
|
||||||
|
if (tooltip) tooltip.remove();
|
||||||
|
});
|
||||||
|
document.querySelectorAll(".grid-row.validation-row-error").forEach(row => row.classList.remove("validation-row-error"));
|
||||||
|
clearAllRowErrors();
|
||||||
|
}
|
||||||
|
|
||||||
|
function showValidationErrors(gridRow, iddatadb, errors) {
|
||||||
|
// Store in gridData for re-render persistence
|
||||||
|
const idx = getRowIndexByIddatadb(iddatadb);
|
||||||
|
if (idx >= 0) window.gridData[idx]._validationErrors = errors;
|
||||||
|
|
||||||
|
if (!gridRow) return;
|
||||||
|
gridRow.classList.add("validation-row-error");
|
||||||
|
const messages = [];
|
||||||
|
|
||||||
|
errors.forEach(err => {
|
||||||
|
messages.push(err.message);
|
||||||
|
if (!err.field) return;
|
||||||
|
|
||||||
|
let cell = null;
|
||||||
|
if (err.field.startsWith("field_label:")) {
|
||||||
|
const label = err.field.substring("field_label:".length);
|
||||||
|
const headers = document.querySelectorAll(".grid-header");
|
||||||
|
let targetIndex = null;
|
||||||
|
headers.forEach(h => {
|
||||||
|
if (h.textContent.trim() === label) targetIndex = h.getAttribute("data-index");
|
||||||
|
});
|
||||||
|
if (targetIndex) {
|
||||||
|
cell = gridRow.querySelector(`.grid-cell[data-index="${targetIndex}"]`);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
cell = gridRow.querySelector(`.grid-cell[data-col="${err.field}"]`);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (cell) {
|
||||||
|
cell.classList.add("validation-error");
|
||||||
|
cell.querySelectorAll("input, select").forEach(el => el.classList.add("input-validation-error"));
|
||||||
|
let tooltip = cell.querySelector(".validation-tooltip");
|
||||||
|
if (!tooltip) {
|
||||||
|
tooltip = document.createElement("div");
|
||||||
|
tooltip.className = "validation-tooltip";
|
||||||
|
cell.appendChild(tooltip);
|
||||||
|
}
|
||||||
|
tooltip.textContent = err.message;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
showRowError(gridRow, iddatadb, messages.join("\n"));
|
||||||
|
}
|
||||||
|
|
||||||
|
// ── Send export ─────────────────────────────────────────────────────
|
||||||
|
|
||||||
|
async function sendExport(iddatadb, batchUuid) {
|
||||||
|
const formData = new FormData();
|
||||||
|
formData.append("iddatadb", iddatadb);
|
||||||
|
if (batchUuid) formData.append("batch_uuid", batchUuid);
|
||||||
|
|
||||||
|
const response = await fetch("export_to_lims.php", { method: "POST", body: formData });
|
||||||
|
if (!response.ok) throw new Error(`HTTP error! status: ${response.status}`);
|
||||||
|
const data = await response.json();
|
||||||
|
|
||||||
|
if (data.success) {
|
||||||
|
// Update gridData
|
||||||
|
const idx = getRowIndexByIddatadb(iddatadb);
|
||||||
|
if (idx >= 0) {
|
||||||
|
window.gridData[idx].status = 'l';
|
||||||
|
window.gridData[idx].commessaweb = data.commessaweb;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Update visible DOM row
|
||||||
|
const gridRow = getGridRow(iddatadb);
|
||||||
|
if (gridRow) {
|
||||||
|
const statusBadge = gridRow.querySelector('.grid-cell[data-col="status"] .status-badge');
|
||||||
|
if (statusBadge) {
|
||||||
|
statusBadge.classList.remove("status-i", "status-P");
|
||||||
|
statusBadge.classList.add("status-l");
|
||||||
|
statusBadge.textContent = "To LIMS";
|
||||||
|
}
|
||||||
|
const statusCell = gridRow.querySelector('.grid-cell[data-col="status"]');
|
||||||
|
if (statusCell && data.commessaweb) {
|
||||||
|
let cwSpan = statusCell.querySelector(".commessaweb-code");
|
||||||
|
if (!cwSpan) {
|
||||||
|
cwSpan = document.createElement("span");
|
||||||
|
cwSpan.className = "commessaweb-code";
|
||||||
|
cwSpan.style.cssText = "display:block; font-size:0.75em; color:#555; margin-top:2px;";
|
||||||
|
statusCell.appendChild(cwSpan);
|
||||||
|
}
|
||||||
|
cwSpan.textContent = data.commessaweb;
|
||||||
|
}
|
||||||
|
const exportBtn = gridRow.querySelector(".export-lims-btn");
|
||||||
|
if (exportBtn) {
|
||||||
|
exportBtn.disabled = true;
|
||||||
|
exportBtn.style.background = "#ccc";
|
||||||
|
exportBtn.style.cursor = "not-allowed";
|
||||||
|
exportBtn.style.opacity = "0.5";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return data;
|
||||||
|
}
|
||||||
|
|
||||||
|
// ── Show result modal ───────────────────────────────────────────────
|
||||||
|
|
||||||
|
function showExportResult(data) {
|
||||||
|
const el = document.getElementById("exportResponseModal");
|
||||||
|
if (!el) return;
|
||||||
|
const modal = new bootstrap.Modal(el, { keyboard: false });
|
||||||
|
const msg = document.getElementById("exportResponseMessage");
|
||||||
|
const label = document.getElementById("exportResponseModalLabel");
|
||||||
|
|
||||||
|
if (data.success) {
|
||||||
|
msg.innerHTML = `${data.message.replace(/\n/g, "<br>")}` +
|
||||||
|
`<br>ID CommessaWeb: ${data.idcommessaweb}` +
|
||||||
|
`<br>Codice CommessaWeb: ${data.commessaweb}` +
|
||||||
|
(data.totalPhotos > 0 ? `<br>Foto: ${data.totalPhotos}` : "");
|
||||||
|
label.textContent = "Esportazione Completata";
|
||||||
|
} else {
|
||||||
|
msg.textContent = `Errore: ${data.message}`;
|
||||||
|
label.textContent = "Errore Esportazione";
|
||||||
|
}
|
||||||
|
modal.show();
|
||||||
|
el.addEventListener("hidden.bs.modal", cleanupBackdrop, { once: true });
|
||||||
|
}
|
||||||
|
|
||||||
|
// ── Row UI helpers ──────────────────────────────────────────────────
|
||||||
|
|
||||||
|
function setRowExporting(row, active) {
|
||||||
|
if (!row) return;
|
||||||
|
const btnCell = row.querySelector(".button-cell");
|
||||||
|
if (active) {
|
||||||
|
row.classList.remove("batch-disabled");
|
||||||
|
row.classList.add("batch-exporting");
|
||||||
|
if (btnCell) {
|
||||||
|
btnCell.querySelectorAll(".action-btn").forEach(b => { b.dataset.prevDisplay = b.style.display; b.style.display = "none"; });
|
||||||
|
const spinner = document.createElement("span");
|
||||||
|
spinner.className = "batch-row-spinner";
|
||||||
|
spinner.innerHTML = '<i class="fas fa-spinner fa-spin"></i> Exporting...';
|
||||||
|
btnCell.appendChild(spinner);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
row.classList.remove("batch-exporting");
|
||||||
|
if (btnCell) {
|
||||||
|
const spinner = btnCell.querySelector(".batch-row-spinner");
|
||||||
|
if (spinner) spinner.remove();
|
||||||
|
btnCell.querySelectorAll(".action-btn").forEach(b => { b.style.display = b.dataset.prevDisplay || ""; delete b.dataset.prevDisplay; });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function showRowError(row, iddatadb, message) {
|
||||||
|
if (!row) return;
|
||||||
|
row.classList.add("batch-row-error");
|
||||||
|
const btnCell = row.querySelector(".button-cell");
|
||||||
|
if (btnCell) {
|
||||||
|
const old = btnCell.querySelector(".batch-error-msg");
|
||||||
|
if (old) old.remove();
|
||||||
|
const errorEl = document.createElement("div");
|
||||||
|
errorEl.className = "batch-error-msg";
|
||||||
|
errorEl.textContent = "Warning — click for details";
|
||||||
|
errorEl.addEventListener("click", () => {
|
||||||
|
document.getElementById("exportResponseMessage").innerHTML = message.replace(/\n/g, "<br>");
|
||||||
|
document.getElementById("exportResponseModalLabel").textContent = "Error (id: " + iddatadb + ")";
|
||||||
|
new bootstrap.Modal(document.getElementById("exportResponseModal"), { keyboard: false }).show();
|
||||||
|
});
|
||||||
|
btnCell.appendChild(errorEl);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function clearAllRowErrors() {
|
||||||
|
document.querySelectorAll(".grid-row.batch-row-error").forEach(row => {
|
||||||
|
row.classList.remove("batch-row-error");
|
||||||
|
const msg = row.querySelector(".batch-error-msg");
|
||||||
|
if (msg) msg.remove();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function disableAllRowButtons() {
|
||||||
|
document.querySelectorAll(".grid-row[data-id]").forEach(row => row.classList.add("batch-disabled"));
|
||||||
|
const toggle = document.querySelector(".actions-dropdown .dropdown-toggle");
|
||||||
|
if (toggle) { toggle.disabled = true; toggle.style.opacity = "0.5"; toggle.style.pointerEvents = "none"; }
|
||||||
|
}
|
||||||
|
|
||||||
|
function enableAllRowButtons() {
|
||||||
|
document.querySelectorAll(".grid-row[data-id]").forEach(row => row.classList.remove("batch-disabled"));
|
||||||
|
const toggle = document.querySelector(".actions-dropdown .dropdown-toggle");
|
||||||
|
if (toggle) { toggle.disabled = false; toggle.style.opacity = ""; toggle.style.pointerEvents = ""; }
|
||||||
|
}
|
||||||
|
|
||||||
|
// ── Single row export: validate → confirm → send ────────────────────
|
||||||
|
|
||||||
|
function startExportConfirmFlow(iddatadb, rowIndex) {
|
||||||
|
const gridRow = getGridRow(iddatadb);
|
||||||
|
clearValidationErrors();
|
||||||
|
|
||||||
|
if (gridRow) {
|
||||||
|
setRowExporting(gridRow, true);
|
||||||
|
const spinner = gridRow.querySelector(".batch-row-spinner");
|
||||||
|
if (spinner) spinner.innerHTML = '<i class="fas fa-spinner fa-spin"></i> Validating...';
|
||||||
|
}
|
||||||
|
|
||||||
|
validateRows([{ iddatadb: parseInt(iddatadb), index: rowIndex }])
|
||||||
|
.then(validationData => {
|
||||||
|
if (gridRow) { setRowExporting(gridRow, false); gridRow.classList.remove("batch-disabled"); }
|
||||||
|
|
||||||
|
if (!validationData.success) {
|
||||||
|
showExportResult({ success: false, message: validationData.message || "Validation error" });
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const result = validationData.results[rowIndex];
|
||||||
|
if (result && !result.valid) {
|
||||||
|
if (gridRow) showValidationErrors(gridRow, iddatadb, result.errors);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
showConfirmAndExport(iddatadb, rowIndex);
|
||||||
|
})
|
||||||
|
.catch(error => {
|
||||||
|
if (gridRow) { setRowExporting(gridRow, false); gridRow.classList.remove("batch-disabled"); }
|
||||||
|
showExportResult({ success: false, message: "Validation error: " + error.message });
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function showConfirmAndExport(iddatadb, rowIndex) {
|
||||||
|
const confirmModalElement = document.getElementById("exportConfirmModal");
|
||||||
|
if (!confirmModalElement) return;
|
||||||
|
|
||||||
|
const confirmModal = new bootstrap.Modal(confirmModalElement, { keyboard: false });
|
||||||
|
document.getElementById("exportIddatadb").textContent = iddatadb;
|
||||||
|
confirmModal.show();
|
||||||
|
|
||||||
|
const confirmBtn = document.getElementById("exportConfirmBtn");
|
||||||
|
if (!confirmBtn) { confirmModal.hide(); return; }
|
||||||
|
|
||||||
|
const confirmHandler = async () => {
|
||||||
|
pendingConfirmHandler = null;
|
||||||
|
confirmModal.hide();
|
||||||
|
|
||||||
|
const gridRow = getGridRow(iddatadb);
|
||||||
|
if (gridRow) setRowExporting(gridRow, true);
|
||||||
|
|
||||||
|
try {
|
||||||
|
const data = await sendExport(iddatadb);
|
||||||
|
if (gridRow) { setRowExporting(gridRow, false); gridRow.classList.remove("batch-disabled"); }
|
||||||
|
if (!data.success) showRowError(gridRow, iddatadb, data.message || "Unknown error");
|
||||||
|
showExportResult(data);
|
||||||
|
} catch (error) {
|
||||||
|
if (gridRow) { setRowExporting(gridRow, false); gridRow.classList.remove("batch-disabled"); }
|
||||||
|
showRowError(gridRow, iddatadb, error.message);
|
||||||
|
showExportResult({ success: false, message: error.message });
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
if (pendingConfirmHandler) confirmBtn.removeEventListener("click", pendingConfirmHandler);
|
||||||
|
pendingConfirmHandler = confirmHandler;
|
||||||
|
confirmBtn.addEventListener("click", confirmHandler, { once: true });
|
||||||
|
}
|
||||||
|
|
||||||
|
// ── Single row click (event delegation) ─────────────────────────────
|
||||||
|
|
||||||
|
$(document).on('click', '.export-lims-btn', function (e) {
|
||||||
|
e.preventDefault();
|
||||||
|
if (batchRunning) return;
|
||||||
|
|
||||||
|
const iddatadb = this.dataset.iddatadb;
|
||||||
|
const rowIndex = parseInt(this.dataset.row);
|
||||||
|
const gridRow = getGridRow(iddatadb);
|
||||||
|
|
||||||
|
// Check unsaved changes for this row
|
||||||
|
const dataRow = window.gridData?.[rowIndex];
|
||||||
|
if (dataRow && dataRow._dirty) {
|
||||||
|
const unsavedModal = new bootstrap.Modal(document.getElementById("exportUnsavedModal"), { keyboard: false });
|
||||||
|
unsavedModal.show();
|
||||||
|
|
||||||
|
document.getElementById("saveAndExportBtn")?.addEventListener("click", () => {
|
||||||
|
unsavedModal.hide();
|
||||||
|
// Save first, then export
|
||||||
|
const formData = window.buildSavePayload(rowIndex);
|
||||||
|
fetch('save_edited_row.php', { method: 'POST', body: formData })
|
||||||
|
.then(r => r.json())
|
||||||
|
.then(result => {
|
||||||
|
if (result.success) {
|
||||||
|
dataRow._dirty = false;
|
||||||
|
startExportConfirmFlow(iddatadb, rowIndex);
|
||||||
|
} else {
|
||||||
|
alert('Save failed: ' + result.message);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}, { once: true });
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
startExportConfirmFlow(iddatadb, rowIndex);
|
||||||
|
});
|
||||||
|
|
||||||
|
// ── Batch export (Export All) ───────────────────────────────────────
|
||||||
|
|
||||||
|
function collectEligibleRows() {
|
||||||
|
// Read from gridData, not DOM
|
||||||
|
const eligible = [];
|
||||||
|
(window.gridData || []).forEach((row, index) => {
|
||||||
|
if (row.status !== 'l') {
|
||||||
|
eligible.push({ iddatadb: row.iddatadb, index, row: getGridRow(row.iddatadb) });
|
||||||
|
}
|
||||||
|
});
|
||||||
|
return eligible;
|
||||||
|
}
|
||||||
|
|
||||||
|
function hasUnsavedChanges() {
|
||||||
|
return (window.gridData || []).some(r => r._dirty);
|
||||||
|
}
|
||||||
|
|
||||||
|
async function validateAndFilter(eligibleRows) {
|
||||||
|
const rowsToValidate = eligibleRows.map(({ iddatadb, index }) => ({
|
||||||
|
iddatadb: parseInt(iddatadb),
|
||||||
|
index,
|
||||||
|
}));
|
||||||
|
const validationData = await validateRows(rowsToValidate);
|
||||||
|
if (!validationData.success) throw new Error(validationData.message || "Validation error");
|
||||||
|
|
||||||
|
const validRows = [];
|
||||||
|
let invalidCount = 0;
|
||||||
|
|
||||||
|
for (const item of eligibleRows) {
|
||||||
|
const result = validationData.results[item.index];
|
||||||
|
if (result && !result.valid) {
|
||||||
|
if (item.row) showValidationErrors(item.row, item.iddatadb, result.errors);
|
||||||
|
invalidCount++;
|
||||||
|
} else {
|
||||||
|
validRows.push(item);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return { validRows, invalidCount };
|
||||||
|
}
|
||||||
|
|
||||||
|
function showValidationSpinner(show) {
|
||||||
|
const bar = document.getElementById("batchExportBar");
|
||||||
|
const statusEl = document.getElementById("batchExportStatus");
|
||||||
|
const cancelBtn = document.getElementById("exportBatchCancelBtn");
|
||||||
|
if (show) {
|
||||||
|
if (bar) bar.style.display = "";
|
||||||
|
if (statusEl) statusEl.textContent = "Validating...";
|
||||||
|
if (cancelBtn) cancelBtn.style.display = "none";
|
||||||
|
} else {
|
||||||
|
if (bar) bar.style.display = "none";
|
||||||
|
if (cancelBtn) cancelBtn.style.display = "";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function showBatchConfirm(eligibleRows) {
|
||||||
|
clearValidationErrors();
|
||||||
|
showValidationSpinner(true);
|
||||||
|
|
||||||
|
validateAndFilter(eligibleRows)
|
||||||
|
.then(({ validRows, invalidCount }) => {
|
||||||
|
showValidationSpinner(false);
|
||||||
|
if (validRows.length === 0) {
|
||||||
|
document.getElementById("exportResponseMessage").innerHTML =
|
||||||
|
`No valid rows for export.<br><strong>${invalidCount}</strong> rows with validation errors.`;
|
||||||
|
document.getElementById("exportResponseModalLabel").textContent = "Validation Failed";
|
||||||
|
new bootstrap.Modal(document.getElementById("exportResponseModal"), { keyboard: false }).show();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const confirmModal = new bootstrap.Modal(document.getElementById("exportBatchConfirmModal"), { keyboard: false });
|
||||||
|
let countText = String(validRows.length);
|
||||||
|
if (invalidCount > 0) countText += ` (${invalidCount} excluded due to errors)`;
|
||||||
|
document.getElementById("exportBatchCount").textContent = countText;
|
||||||
|
confirmModal.show();
|
||||||
|
|
||||||
|
const confirmBtn = document.getElementById("exportBatchConfirmBtn");
|
||||||
|
if (pendingBatchConfirmHandler) confirmBtn.removeEventListener("click", pendingBatchConfirmHandler);
|
||||||
|
pendingBatchConfirmHandler = () => {
|
||||||
|
pendingBatchConfirmHandler = null;
|
||||||
|
confirmModal.hide();
|
||||||
|
startBatchExport(validRows);
|
||||||
|
};
|
||||||
|
confirmBtn.addEventListener("click", pendingBatchConfirmHandler, { once: true });
|
||||||
|
})
|
||||||
|
.catch(error => {
|
||||||
|
showValidationSpinner(false);
|
||||||
|
document.getElementById("exportResponseMessage").textContent = "Validation error: " + error.message;
|
||||||
|
document.getElementById("exportResponseModalLabel").textContent = "Validation Error";
|
||||||
|
new bootstrap.Modal(document.getElementById("exportResponseModal"), { keyboard: false }).show();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
$(document).on('click', '.export-all-lims-btn', function (e) {
|
||||||
|
e.preventDefault();
|
||||||
|
if (batchRunning) return;
|
||||||
|
|
||||||
|
if (hasUnsavedChanges()) {
|
||||||
|
const unsavedModal = new bootstrap.Modal(document.getElementById("exportBatchUnsavedModal"), { keyboard: false });
|
||||||
|
unsavedModal.show();
|
||||||
|
document.getElementById("batchSaveAndExportBtn")?.addEventListener("click", () => {
|
||||||
|
unsavedModal.hide();
|
||||||
|
// Trigger save all first — listen for completion
|
||||||
|
alert("Please Save All first, then Export All.");
|
||||||
|
}, { once: true });
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const eligibleRows = collectEligibleRows();
|
||||||
|
if (eligibleRows.length === 0) {
|
||||||
|
document.getElementById("exportResponseMessage").textContent = "All rows already exported to LIMS.";
|
||||||
|
document.getElementById("exportResponseModalLabel").textContent = "Export All";
|
||||||
|
new bootstrap.Modal(document.getElementById("exportResponseModal"), { keyboard: false }).show();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
showBatchConfirm(eligibleRows);
|
||||||
|
});
|
||||||
|
|
||||||
|
function startBatchExport(eligibleRows) {
|
||||||
|
batchCancelled = false;
|
||||||
|
batchRunning = true;
|
||||||
|
const batchUuid = crypto.randomUUID();
|
||||||
|
const total = eligibleRows.length;
|
||||||
|
let processed = 0, succeeded = 0, failed = 0;
|
||||||
|
|
||||||
|
disableAllRowButtons();
|
||||||
|
|
||||||
|
const bar = document.getElementById("batchExportBar");
|
||||||
|
const statusEl = document.getElementById("batchExportStatus");
|
||||||
|
const cancelBtn = document.getElementById("exportBatchCancelBtn");
|
||||||
|
if (bar) bar.style.display = "";
|
||||||
|
if (cancelBtn) cancelBtn.disabled = false;
|
||||||
|
if (statusEl) statusEl.textContent = `Exporting 0 / ${total}...`;
|
||||||
|
|
||||||
|
cancelBtn?.addEventListener("click", () => {
|
||||||
|
batchCancelled = true;
|
||||||
|
if (statusEl) statusEl.textContent = "Cancelling... (waiting for current row)";
|
||||||
|
if (cancelBtn) cancelBtn.disabled = true;
|
||||||
|
}, { once: true });
|
||||||
|
|
||||||
|
(async () => {
|
||||||
|
for (let i = 0; i < eligibleRows.length; i++) {
|
||||||
|
if (batchCancelled) break;
|
||||||
|
|
||||||
|
const { iddatadb, row } = eligibleRows[i];
|
||||||
|
if (statusEl) statusEl.textContent = `Exporting ${processed + 1} / ${total} (id: ${iddatadb})...`;
|
||||||
|
|
||||||
|
const gridRow = row || getGridRow(iddatadb);
|
||||||
|
if (gridRow) setRowExporting(gridRow, true);
|
||||||
|
|
||||||
|
try {
|
||||||
|
const data = await sendExport(iddatadb, batchUuid);
|
||||||
|
processed++;
|
||||||
|
if (data.success) {
|
||||||
|
succeeded++;
|
||||||
|
} else {
|
||||||
|
failed++;
|
||||||
|
const errIdx = getRowIndexByIddatadb(iddatadb);
|
||||||
|
if (errIdx >= 0) window.gridData[errIdx]._exportError = data.message || "Unknown error";
|
||||||
|
if (gridRow) showRowError(gridRow, iddatadb, data.message || "Unknown error");
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
processed++;
|
||||||
|
failed++;
|
||||||
|
const errIdx = getRowIndexByIddatadb(iddatadb);
|
||||||
|
if (errIdx >= 0) window.gridData[errIdx]._exportError = error.message;
|
||||||
|
if (gridRow) showRowError(gridRow, iddatadb, error.message);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (gridRow) { setRowExporting(gridRow, false); gridRow.classList.remove("batch-disabled"); }
|
||||||
|
}
|
||||||
|
|
||||||
|
batchRunning = false;
|
||||||
|
enableAllRowButtons();
|
||||||
|
if (bar) bar.style.display = "none";
|
||||||
|
|
||||||
|
// Re-render to reflect status changes, then restore error indicators
|
||||||
|
const gr = window.gridRenderer;
|
||||||
|
if (gr) gr.renderVisibleRows();
|
||||||
|
|
||||||
|
// Restore error state from gridData._exportError
|
||||||
|
(window.gridData || []).forEach((row, idx) => {
|
||||||
|
if (row._exportError) {
|
||||||
|
const gridRow = getGridRow(row.iddatadb);
|
||||||
|
if (gridRow) showRowError(gridRow, row.iddatadb, row._exportError);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
const msgEl = document.getElementById("exportResponseMessage");
|
||||||
|
const labelEl = document.getElementById("exportResponseModalLabel");
|
||||||
|
|
||||||
|
if (batchCancelled) {
|
||||||
|
labelEl.textContent = "Export All — Cancelled";
|
||||||
|
msgEl.innerHTML = `Exported: <strong>${succeeded}</strong><br>Errors: <strong>${failed}</strong><br>Not processed: <strong>${total - processed}</strong>`;
|
||||||
|
} else if (failed === 0) {
|
||||||
|
labelEl.textContent = "Export All — Complete";
|
||||||
|
msgEl.innerHTML = `All <strong>${succeeded}</strong> rows exported successfully.`;
|
||||||
|
} else {
|
||||||
|
labelEl.textContent = "Export All — Completed with errors";
|
||||||
|
msgEl.innerHTML = `Exported: <strong>${succeeded}</strong><br>Errors: <strong>${failed}</strong>`;
|
||||||
|
}
|
||||||
|
|
||||||
|
const modalEl = document.getElementById("exportResponseModal");
|
||||||
|
new bootstrap.Modal(modalEl, { keyboard: false }).show();
|
||||||
|
modalEl.addEventListener("hidden.bs.modal", cleanupBackdrop, { once: true });
|
||||||
|
})();
|
||||||
|
}
|
||||||
|
})();
|
||||||
@ -330,8 +330,16 @@ document.addEventListener("DOMContentLoaded", () => {
|
|||||||
// Validate first
|
// Validate first
|
||||||
clearValidationErrors();
|
clearValidationErrors();
|
||||||
|
|
||||||
|
// Show validating state on the row
|
||||||
|
setRowExporting(gridRow, true);
|
||||||
|
const spinner = gridRow.querySelector(".batch-row-spinner");
|
||||||
|
if (spinner) spinner.innerHTML = '<i class="fas fa-spinner fa-spin"></i> Validating...';
|
||||||
|
|
||||||
validateRows([{ iddatadb: parseInt(iddatadb), index: parseInt(rowIndex) }])
|
validateRows([{ iddatadb: parseInt(iddatadb), index: parseInt(rowIndex) }])
|
||||||
.then((validationData) => {
|
.then((validationData) => {
|
||||||
|
setRowExporting(gridRow, false);
|
||||||
|
gridRow.classList.remove("batch-disabled");
|
||||||
|
|
||||||
if (!validationData.success) {
|
if (!validationData.success) {
|
||||||
showExportResult({ success: false, message: validationData.message || "Errore di validazione" });
|
showExportResult({ success: false, message: validationData.message || "Errore di validazione" });
|
||||||
return;
|
return;
|
||||||
@ -348,6 +356,9 @@ document.addEventListener("DOMContentLoaded", () => {
|
|||||||
showConfirmAndExport(iddatadb, btn);
|
showConfirmAndExport(iddatadb, btn);
|
||||||
})
|
})
|
||||||
.catch((error) => {
|
.catch((error) => {
|
||||||
|
setRowExporting(gridRow, false);
|
||||||
|
gridRow.classList.remove("batch-disabled");
|
||||||
|
|
||||||
console.error("Validation error:", error);
|
console.error("Validation error:", error);
|
||||||
showExportResult({ success: false, message: "Errore di validazione: " + error.message });
|
showExportResult({ success: false, message: "Errore di validazione: " + error.message });
|
||||||
});
|
});
|
||||||
@ -379,13 +390,29 @@ document.addEventListener("DOMContentLoaded", () => {
|
|||||||
console.log(`Confirmed export for iddatadb: ${iddatadb}`);
|
console.log(`Confirmed export for iddatadb: ${iddatadb}`);
|
||||||
confirmModal.hide();
|
confirmModal.hide();
|
||||||
|
|
||||||
|
const gridRow = btn.closest(".grid-row");
|
||||||
|
setRowExporting(gridRow, true);
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const gridRow = btn.closest(".grid-row");
|
|
||||||
const data = await sendExport(iddatadb, gridRow);
|
const data = await sendExport(iddatadb, gridRow);
|
||||||
console.log("Export response:", data);
|
console.log("Export response:", data);
|
||||||
|
|
||||||
|
// Stop spinner, fully restore row
|
||||||
|
setRowExporting(gridRow, false);
|
||||||
|
gridRow.classList.remove("batch-disabled");
|
||||||
|
|
||||||
|
if (!data.success) {
|
||||||
|
showRowError(gridRow, iddatadb, data.message || "Errore sconosciuto");
|
||||||
|
}
|
||||||
showExportResult(data);
|
showExportResult(data);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error("Export error:", error);
|
console.error("Export error:", error);
|
||||||
|
|
||||||
|
// Stop spinner, fully restore row so user can retry
|
||||||
|
setRowExporting(gridRow, false);
|
||||||
|
gridRow.classList.remove("batch-disabled");
|
||||||
|
|
||||||
|
showRowError(gridRow, iddatadb, error.message);
|
||||||
showExportResult({
|
showExportResult({
|
||||||
success: false,
|
success: false,
|
||||||
message: error.message,
|
message: error.message,
|
||||||
|
|||||||
@ -49,6 +49,16 @@ function validateDate($value)
|
|||||||
return null; // Imposta null se non è una data valida
|
return null; // Imposta null se non è una data valida
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 🔹 Funzione per validare e convertire date
|
||||||
|
function formatDateToExport($value)
|
||||||
|
{
|
||||||
|
$date = DateTime::createFromFormat('Y-m-d', $value) ?: DateTime::createFromFormat('Y-m-d H:i:s', $value);
|
||||||
|
if ($date) {
|
||||||
|
return $date->format('d/m/Y');
|
||||||
|
}
|
||||||
|
return null; // Imposta null se non è una data valida
|
||||||
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
$iddatadb = $_POST['iddatadb'] ?? null;
|
$iddatadb = $_POST['iddatadb'] ?? null;
|
||||||
if (!$iddatadb) {
|
if (!$iddatadb) {
|
||||||
@ -69,6 +79,7 @@ try {
|
|||||||
d.anagrafica_certest_object_id,
|
d.anagrafica_certest_object_id,
|
||||||
d.anagrafica_certest_service_id,
|
d.anagrafica_certest_service_id,
|
||||||
d.cliente_fornitore_id,
|
d.cliente_fornitore_id,
|
||||||
|
d.clienteAnalisi,
|
||||||
d.consegna_richiesta
|
d.consegna_richiesta
|
||||||
FROM datadb as d
|
FROM datadb as d
|
||||||
INNER JOIN excel_templates as et ON d.templateid = et.id
|
INNER JOIN excel_templates as et ON d.templateid = et.id
|
||||||
@ -91,17 +102,35 @@ try {
|
|||||||
$anagraficaObject = !empty($result['anagrafica_certest_object_id']) ? (int) $result['anagrafica_certest_object_id'] : null;
|
$anagraficaObject = !empty($result['anagrafica_certest_object_id']) ? (int) $result['anagrafica_certest_object_id'] : null;
|
||||||
$anagraficaService = !empty($result['anagrafica_certest_service_id']) ? (int) $result['anagrafica_certest_service_id'] : null;
|
$anagraficaService = !empty($result['anagrafica_certest_service_id']) ? (int) $result['anagrafica_certest_service_id'] : null;
|
||||||
$clienteFornitore = !empty($result['cliente_fornitore_id']) ? (int) $result['cliente_fornitore_id'] : null;
|
$clienteFornitore = !empty($result['cliente_fornitore_id']) ? (int) $result['cliente_fornitore_id'] : null;
|
||||||
|
$clienteAnalisi = !empty($result['clienteAnalisi']) ? (int) $result['clienteAnalisi'] : null;
|
||||||
$consegnaRichiesta = !empty($result['consegna_richiesta']) ? $result['consegna_richiesta'] : null;
|
$consegnaRichiesta = !empty($result['consegna_richiesta']) ? $result['consegna_richiesta'] : null;
|
||||||
|
|
||||||
// 🔹 STEP 3: Fetch Parts (including idmatrice)
|
// 🔹 STEP 3: Fetch Parts (including idmatrice and part id for custom fields)
|
||||||
$stmt = $pdo->prepare("
|
$stmt = $pdo->prepare("
|
||||||
SELECT part_number, part_description, material, color, mix, idmatrice
|
SELECT id AS part_id, part_number, part_description, material, color, mix, idmatrice, dateexpiry
|
||||||
FROM identification_parts
|
FROM identification_parts
|
||||||
WHERE iddatadb = :iddatadb
|
WHERE iddatadb = :iddatadb
|
||||||
|
ORDER BY CAST(part_number AS UNSIGNED) ASC, part_number ASC
|
||||||
");
|
");
|
||||||
$stmt->execute(['iddatadb' => $iddatadb]);
|
$stmt->execute(['iddatadb' => $iddatadb]);
|
||||||
$parts = $stmt->fetchAll(PDO::FETCH_ASSOC);
|
$parts = $stmt->fetchAll(PDO::FETCH_ASSOC);
|
||||||
|
|
||||||
|
// 🔹 STEP 3.1: Fetch custom field values per part from identification_parts_customfields
|
||||||
|
$partIds = array_column($parts, 'part_id');
|
||||||
|
$partsCustomFields = []; // part_id => [ { field_id, value_id, value_text }, ... ]
|
||||||
|
if (!empty($partIds)) {
|
||||||
|
$placeholders = implode(',', array_fill(0, count($partIds), '?'));
|
||||||
|
$cfStmt = $pdo->prepare("
|
||||||
|
SELECT part_id, field_id, value_id, value_text
|
||||||
|
FROM identification_parts_customfields
|
||||||
|
WHERE part_id IN ({$placeholders})
|
||||||
|
");
|
||||||
|
$cfStmt->execute($partIds);
|
||||||
|
foreach ($cfStmt->fetchAll(PDO::FETCH_ASSOC) as $cfRow) {
|
||||||
|
$partsCustomFields[(int)$cfRow['part_id']][] = $cfRow;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// 🔹 STEP 4a: Auto-populate export_date / export_time fields
|
// 🔹 STEP 4a: Auto-populate export_date / export_time fields
|
||||||
$stmt = $pdo->prepare("
|
$stmt = $pdo->prepare("
|
||||||
UPDATE import_data_details idd
|
UPDATE import_data_details idd
|
||||||
@ -165,6 +194,7 @@ try {
|
|||||||
"AnagraficaCertestObject" => $anagraficaObject,
|
"AnagraficaCertestObject" => $anagraficaObject,
|
||||||
"AnagraficaCertestService" => $anagraficaService,
|
"AnagraficaCertestService" => $anagraficaService,
|
||||||
"ClienteFornitore" => $clienteFornitore, // PLACEHOLDER — to be implemented
|
"ClienteFornitore" => $clienteFornitore, // PLACEHOLDER — to be implemented
|
||||||
|
"ClienteAnalisi" => $clienteAnalisi, // PLACEHOLDER — to be implemented
|
||||||
// DeliveryRequest goes to Campione, not CommessaWeb
|
// DeliveryRequest goes to Campione, not CommessaWeb
|
||||||
];
|
];
|
||||||
|
|
||||||
@ -202,9 +232,9 @@ try {
|
|||||||
"Matrice" => $matriceId,
|
"Matrice" => $matriceId,
|
||||||
"SottoMatrice" => null,
|
"SottoMatrice" => null,
|
||||||
"SchemaCustomField" => $schemaId,
|
"SchemaCustomField" => $schemaId,
|
||||||
"Riferimento" => $part["part_description"] ?? "",
|
// "Riferimento" => $part["part_description"] ?? "",
|
||||||
"NoteWeb" => $part["part_description"] ?? "",
|
"NoteWeb" => $part["part_description"] ?? "",
|
||||||
"ConsegnaRichiesta" => $consegnaRichiesta,
|
"ConsegnaRichiesta" => !empty($part["dateexpiry"]) ? $part["dateexpiry"] : $consegnaRichiesta,
|
||||||
];
|
];
|
||||||
|
|
||||||
// Costruisci curl-like per questo campione
|
// Costruisci curl-like per questo campione
|
||||||
@ -231,6 +261,83 @@ try {
|
|||||||
$logFileStep6 = $logDir . "commessa_{$commessaId}_campioni_step6_" . time() . ".txt";
|
$logFileStep6 = $logDir . "commessa_{$commessaId}_campioni_step6_" . time() . ".txt";
|
||||||
$writeLog($logFileStep6, $logContentStep6, "STEP 6 - Campioni (commessa={$commessaId})");
|
$writeLog($logFileStep6, $logContentStep6, "STEP 6 - Campioni (commessa={$commessaId})");
|
||||||
|
|
||||||
|
// 🔹 STEP 6.0: PATCH each Campione custom fields:
|
||||||
|
// - field 189 (Tested Component) = part_description
|
||||||
|
// - additional fields from identification_parts_customfields (field_id → value_id/value_text)
|
||||||
|
$logContentStep63 = "";
|
||||||
|
foreach ($campioni as $index => $campione) {
|
||||||
|
$campioneId = (int)($campione['IdCampione'] ?? 0);
|
||||||
|
$partDescription = $parts[$index]['part_description'] ?? '';
|
||||||
|
$partId = (int)($parts[$index]['part_id'] ?? 0);
|
||||||
|
|
||||||
|
if ($campioneId <= 0) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Build a map of overrides: IdCustomField => value
|
||||||
|
$overrides = [];
|
||||||
|
|
||||||
|
// Override 1: Tested Component (field 189) = part_description
|
||||||
|
if ($partDescription !== '') {
|
||||||
|
$overrides[189] = $partDescription;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Override 2: values from identification_parts_customfields
|
||||||
|
$partCFs = $partsCustomFields[$partId] ?? [];
|
||||||
|
foreach ($partCFs as $pcf) {
|
||||||
|
$cfFieldId = (int)$pcf['field_id'];
|
||||||
|
$cfValue = $pcf['value_text'] ?? $pcf['value_id'] ?? null;
|
||||||
|
if ($cfFieldId > 0 && $cfValue !== null && $cfValue !== '') {
|
||||||
|
$overrides[$cfFieldId] = (string)$cfValue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Skip if nothing to override
|
||||||
|
if (empty($overrides)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// GET campione custom fields
|
||||||
|
$campioneWithFields = $api->get("Campione({$campioneId})?\$expand=CampioniCustomFields(\$expand=CustomField)");
|
||||||
|
|
||||||
|
$logContentStep63 .= "GET Campione({$campioneId}) CustomFields:\n" .
|
||||||
|
json_encode($campioneWithFields['CampioniCustomFields'] ?? [], JSON_PRETTY_PRINT) . "\n\n";
|
||||||
|
$logContentStep63 .= "Overrides for part {$partId}: " . json_encode($overrides) . "\n\n";
|
||||||
|
|
||||||
|
// Build PATCH payload — apply overrides where IdCustomField matches
|
||||||
|
$campioniCustomFields = [];
|
||||||
|
foreach ($campioneWithFields["CampioniCustomFields"] ?? [] as $cf) {
|
||||||
|
$definitionId = (int)($cf["CustomField"]["IdCustomField"] ?? 0);
|
||||||
|
$fieldInstanceId = (int)$cf["IdCampioniCustomFields"];
|
||||||
|
$currentValue = $cf["Valore"] ?? '';
|
||||||
|
|
||||||
|
$newValue = $overrides[$definitionId] ?? $currentValue;
|
||||||
|
|
||||||
|
$campioniCustomFields[] = [
|
||||||
|
"IdCampioniCustomFields" => $fieldInstanceId,
|
||||||
|
"Valore" => $newValue
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!empty($campioniCustomFields)) {
|
||||||
|
$patchPayload = ["CampioniCustomFields" => $campioniCustomFields];
|
||||||
|
$patchJson = json_encode($patchPayload, JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES);
|
||||||
|
|
||||||
|
$logContentStep63 .= "PATCH Campione({$campioneId}):\n" .
|
||||||
|
"curl --location --request PATCH '{$apiBaseUrl}Campione({$campioneId})' \\\n" .
|
||||||
|
"--header 'Content-Type: application/json' \\\n" .
|
||||||
|
"--header 'Authorization: Bearer ••••••' \\\n" .
|
||||||
|
"--data '{$patchJson}'\n\n";
|
||||||
|
|
||||||
|
$patchResult = $api->patch("Campione({$campioneId})", $patchPayload);
|
||||||
|
|
||||||
|
$logContentStep63 .= "RESPONSE:\n" . json_encode($patchResult, JSON_PRETTY_PRINT) . "\n\n---\n";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
$logFileStep63 = $logDir . "commessa_{$commessaId}_campioni_customfields_step60_" . time() . ".txt";
|
||||||
|
$writeLog($logFileStep63, $logContentStep63, "STEP 6.0 - Campioni CustomFields (commessa={$commessaId})");
|
||||||
|
|
||||||
// 🔹 STEP 6.1: Fetch photos linked to this iddatadb
|
// 🔹 STEP 6.1: Fetch photos linked to this iddatadb
|
||||||
$stmtPhotos = $pdo->prepare("
|
$stmtPhotos = $pdo->prepare("
|
||||||
SELECT id, file_path, file_name, StampaNelRapporto, PrimaPagina
|
SELECT id, file_path, file_name, StampaNelRapporto, PrimaPagina
|
||||||
@ -242,15 +349,28 @@ try {
|
|||||||
$photos = $stmtPhotos->fetchAll(PDO::FETCH_ASSOC);
|
$photos = $stmtPhotos->fetchAll(PDO::FETCH_ASSOC);
|
||||||
|
|
||||||
|
|
||||||
// 🔹 STEP 6.2: Upload photos to the first (main) Campione only
|
// 🔹 STEP 6.2: Upload photos to Campione .01 (fetched from API)
|
||||||
$photosUploaded = 0;
|
$photosUploaded = 0;
|
||||||
$logContentPhotos = "Photos for CommessaWeb {$commessaId} (iddatadb={$iddatadb}):\n";
|
$logContentPhotos = "Photos for CommessaWeb {$commessaId} (iddatadb={$iddatadb}):\n";
|
||||||
$logContentPhotos .= "Total photos found: " . count($photos) . ", campioni: " . count($campioni) . "\n\n";
|
$logContentPhotos .= "Total photos found: " . count($photos) . ", campioni: " . count($campioni) . "\n\n";
|
||||||
|
|
||||||
if (!empty($campioni) && !empty($photos)) {
|
if (!empty($campioni) && !empty($photos)) {
|
||||||
$mainCampione = $campioni[0];
|
// Fetch campioni list from API to find the .01 campione
|
||||||
|
$commessaCampioni = $api->get("CommessaWeb({$commessaId})?\$expand=Campioni");
|
||||||
|
$apiCampioni = $commessaCampioni['Campioni'] ?? [];
|
||||||
|
|
||||||
|
// Sort by CodiceCampione to find .01
|
||||||
|
usort($apiCampioni, fn($a, $b) => strcmp($a['CodiceCampione'] ?? '', $b['CodiceCampione'] ?? ''));
|
||||||
|
|
||||||
|
$mainCampione = $apiCampioni[0] ?? null;
|
||||||
$campioneId = (int)($mainCampione['IdCampione'] ?? 0);
|
$campioneId = (int)($mainCampione['IdCampione'] ?? 0);
|
||||||
|
|
||||||
|
$logContentPhotos .= "API Campioni order:\n";
|
||||||
|
foreach ($apiCampioni as $ac) {
|
||||||
|
$logContentPhotos .= " - {$ac['CodiceCampione']} (IdCampione: {$ac['IdCampione']})\n";
|
||||||
|
}
|
||||||
|
$logContentPhotos .= "Selected .01 campione: IdCampione={$campioneId}\n\n";
|
||||||
|
|
||||||
if ($campioneId > 0) {
|
if ($campioneId > 0) {
|
||||||
$logContentPhotos .= "=== Campione {$campioneId} (main) ===\n";
|
$logContentPhotos .= "=== Campione {$campioneId} (main) ===\n";
|
||||||
|
|
||||||
@ -264,21 +384,42 @@ try {
|
|||||||
}
|
}
|
||||||
|
|
||||||
$photoEndpoint = "Campione({$campioneId})/UploadCampioneFile";
|
$photoEndpoint = "Campione({$campioneId})/UploadCampioneFile";
|
||||||
$stampaNelRapporto = !empty($photo['StampaNelRapporto']) ? 'true' : 'false';
|
$stampaNelRapporto = !empty($photo['StampaNelRapporto']);
|
||||||
$primaPagina = !empty($photo['PrimaPagina']) ? 'true' : 'false';
|
$primaPagina = !empty($photo['PrimaPagina']);
|
||||||
|
|
||||||
$logContentPhotos .= "curl --location --request POST '{$apiBaseUrl}{$photoEndpoint}' \\\n" .
|
$logContentPhotos .= "curl --location --request POST '{$apiBaseUrl}{$photoEndpoint}' \\\n" .
|
||||||
"--header 'Authorization: Bearer ••••••' \\\n" .
|
"--header 'Authorization: Bearer ••••••' \\\n" .
|
||||||
"--form 'file=@{$fullPath}' \\\n" .
|
"--form 'file=@{$fullPath}'\n\n";
|
||||||
"--form 'StampaNelRapporto={$stampaNelRapporto}' \\\n" .
|
|
||||||
"--form 'PrimaPagina={$primaPagina}'\n\n";
|
|
||||||
|
|
||||||
$extraFields = [
|
// Step 1: Upload file (flags are ignored by API during upload)
|
||||||
'StampaNelRapporto' => $stampaNelRapporto,
|
$photoResult = $api->postMultipart($photoEndpoint, $fullPath, $photo['file_name']);
|
||||||
'PrimaPagina' => $primaPagina,
|
$logContentPhotos .= "UPLOAD RESPONSE:\n" . json_encode($photoResult, JSON_PRETTY_PRINT) . "\n\n";
|
||||||
];
|
|
||||||
$photoResult = $api->postMultipart($photoEndpoint, $fullPath, $photo['file_name'], $extraFields);
|
// Step 2: PATCH CampioneFile to set flags (StampaNelRapporto, PrimaPagina)
|
||||||
$logContentPhotos .= "RESPONSE:\n" . json_encode($photoResult, JSON_PRETTY_PRINT) . "\n\n---\n";
|
$campioneFileId = (int)($photoResult['IdCampioneFile'] ?? 0);
|
||||||
|
if ($campioneFileId > 0 && ($stampaNelRapporto || $primaPagina)) {
|
||||||
|
$patchPayload = [];
|
||||||
|
|
||||||
|
if ($stampaNelRapporto) {
|
||||||
|
$patchPayload['StampaNelRapporto'] = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($primaPagina) {
|
||||||
|
$patchPayload['PrimaPagina'] = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
$patchEndpoint = "CampioneFile({$campioneFileId})";
|
||||||
|
$patchJsonLog = json_encode($patchPayload, JSON_PRETTY_PRINT);
|
||||||
|
$logContentPhotos .= "curl --location --request PATCH '{$apiBaseUrl}{$patchEndpoint}' \\\n" .
|
||||||
|
"--header 'Content-Type: application/json' \\\n" .
|
||||||
|
"--header 'Authorization: Bearer ••••••' \\\n" .
|
||||||
|
"--data '{$patchJsonLog}'\n\n";
|
||||||
|
|
||||||
|
$patchResult = $api->patch($patchEndpoint, $patchPayload);
|
||||||
|
$logContentPhotos .= "PATCH RESPONSE:\n" . json_encode($patchResult, JSON_PRETTY_PRINT) . "\n\n";
|
||||||
|
}
|
||||||
|
|
||||||
|
$logContentPhotos .= "---\n";
|
||||||
$photosUploaded++;
|
$photosUploaded++;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
@ -315,7 +456,7 @@ try {
|
|||||||
|
|
||||||
// Valida se il campo è di tipo Data
|
// Valida se il campo è di tipo Data
|
||||||
if ($fieldType === 'Data' && $newValue !== $currentValue) {
|
if ($fieldType === 'Data' && $newValue !== $currentValue) {
|
||||||
$newValue = validateDate($newValue);
|
$newValue = formatDateToExport($newValue);
|
||||||
}
|
}
|
||||||
|
|
||||||
$commessaCustomFields[] = [
|
$commessaCustomFields[] = [
|
||||||
@ -372,7 +513,13 @@ try {
|
|||||||
// 🔹 STEP 9.5: Importazione da CommessaWeb a Commessa (commentato come richiesto)
|
// 🔹 STEP 9.5: Importazione da CommessaWeb a Commessa (commentato come richiesto)
|
||||||
// Supplier call: POST api/odata/CommessaWeb(XXX)/ImportaCommessa
|
// Supplier call: POST api/odata/CommessaWeb(XXX)/ImportaCommessa
|
||||||
|
|
||||||
$importPayload = ["IdUtente" => 285]; // user-id
|
$importUserId = (!empty($lims_global_user_id) && is_numeric($lims_global_user_id))
|
||||||
|
? (int) $lims_global_user_id
|
||||||
|
: 285;
|
||||||
|
|
||||||
|
$importPayload = [
|
||||||
|
"IdUtente" => $importUserId
|
||||||
|
];
|
||||||
$importResult = $api->post("CommessaWeb({$commessaId})/ImportaCommessa", $importPayload);
|
$importResult = $api->post("CommessaWeb({$commessaId})/ImportaCommessa", $importPayload);
|
||||||
|
|
||||||
$importPayloadLog = json_encode($importPayload, JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES);
|
$importPayloadLog = json_encode($importPayload, JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES);
|
||||||
@ -395,7 +542,23 @@ try {
|
|||||||
"RESPONSE:\n" . json_encode($commessaAfterPatch, JSON_PRETTY_PRINT);
|
"RESPONSE:\n" . json_encode($commessaAfterPatch, JSON_PRETTY_PRINT);
|
||||||
$logFileStep10 = $logDir . "commessa_{$commessaId}_get_step10_" . time() . ".txt";
|
$logFileStep10 = $logDir . "commessa_{$commessaId}_get_step10_" . time() . ".txt";
|
||||||
$writeLog($logFileStep10, $logContentStep10, "STEP 10 - GET verify (commessa={$commessaId})");
|
$writeLog($logFileStep10, $logContentStep10, "STEP 10 - GET verify (commessa={$commessaId})");
|
||||||
|
// 🔹 STEP 10.1: Save final CodiceCommessa into datadb.commessaweb
|
||||||
|
// After ImportaCommessa, the API returns the final LIMS job code in CodiceCommessa.
|
||||||
|
// Example: CodiceCommessa = 2614795, CodiceCommessaWeb = 26C0103.
|
||||||
|
$finalCodiceCommessa = trim((string)($commessaAfterPatch['CodiceCommessa'] ?? ''));
|
||||||
|
|
||||||
|
if ($finalCodiceCommessa !== '') {
|
||||||
|
$stmt = $pdo->prepare("
|
||||||
|
UPDATE datadb
|
||||||
|
SET commessaweb = :commessaweb,
|
||||||
|
status = 'l'
|
||||||
|
WHERE iddatadb = :iddatadb
|
||||||
|
");
|
||||||
|
$stmt->execute([
|
||||||
|
'commessaweb' => substr($finalCodiceCommessa, 0, 30),
|
||||||
|
'iddatadb' => $iddatadb
|
||||||
|
]);
|
||||||
|
}
|
||||||
// 🔹 STEP 11: Prepare final response
|
// 🔹 STEP 11: Prepare final response
|
||||||
$finalCommessa = [
|
$finalCommessa = [
|
||||||
"Cliente" => $clienteId,
|
"Cliente" => $clienteId,
|
||||||
@ -410,7 +573,7 @@ try {
|
|||||||
echo json_encode([
|
echo json_encode([
|
||||||
"success" => true,
|
"success" => true,
|
||||||
"idcommessaweb" => $commessaId,
|
"idcommessaweb" => $commessaId,
|
||||||
"commessaweb" => $commessaWebCode,
|
"commessaweb" => $finalCodiceCommessa ?: $commessaWebCode,
|
||||||
"commessaWeb" => $finalCommessa,
|
"commessaWeb" => $finalCommessa,
|
||||||
"commessaWebApiResponse" => $commessaWeb, // Incluso per debug
|
"commessaWebApiResponse" => $commessaWeb, // Incluso per debug
|
||||||
"totalCampioni" => count($campioni),
|
"totalCampioni" => count($campioni),
|
||||||
|
|||||||
50
public/userarea/get_analisi.php
Normal file
50
public/userarea/get_analisi.php
Normal file
@ -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);
|
||||||
|
}
|
||||||
68
public/userarea/get_analisi_by_cliente_matrice.php
Normal file
68
public/userarea/get_analisi_by_cliente_matrice.php
Normal file
@ -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()]);
|
||||||
|
}
|
||||||
56
public/userarea/get_analisi_by_matrice.php
Normal file
56
public/userarea/get_analisi_by_matrice.php
Normal file
@ -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()]);
|
||||||
|
}
|
||||||
60
public/userarea/get_analisi_matrice_filter.php
Normal file
60
public/userarea/get_analisi_matrice_filter.php
Normal file
@ -0,0 +1,60 @@
|
|||||||
|
<?php
|
||||||
|
require_once dirname(__DIR__, 2) . '/vendor/autoload.php';
|
||||||
|
require_once dirname(__FILE__) . '/class/VisualLimsApiClient.class.php';
|
||||||
|
|
||||||
|
header('Content-Type: application/json');
|
||||||
|
ini_set('display_errors', '0');
|
||||||
|
error_reporting(E_ALL);
|
||||||
|
|
||||||
|
try {
|
||||||
|
$idMatrice = isset($_GET['id_matrice']) ? (int)$_GET['id_matrice'] : 0;
|
||||||
|
$debug = isset($_GET['debug']) ? (int)$_GET['debug'] : 0;
|
||||||
|
|
||||||
|
if ($idMatrice <= 0) {
|
||||||
|
http_response_code(400);
|
||||||
|
echo json_encode(['error' => 'Missing or invalid id_matrice']);
|
||||||
|
exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
$api = VisualLimsApiClient::getInstance();
|
||||||
|
|
||||||
|
$filter = rawurlencode("Matrice/IdMatrice eq $idMatrice");
|
||||||
|
$endpoint = "Analisi?\$filter={$filter}";
|
||||||
|
|
||||||
|
$base_url = 'https://93.43.5.102/limsapi/api/odata/';
|
||||||
|
$full_url = $base_url . $endpoint;
|
||||||
|
|
||||||
|
file_put_contents(__DIR__ . '/last_url_analisi.txt', $full_url . PHP_EOL, FILE_APPEND);
|
||||||
|
|
||||||
|
$data = $api->get($endpoint, []);
|
||||||
|
|
||||||
|
file_put_contents(
|
||||||
|
__DIR__ . '/analisi_by_matrice_response.json',
|
||||||
|
json_encode($data, JSON_PRETTY_PRINT | JSON_UNESCAPED_UNICODE)
|
||||||
|
);
|
||||||
|
|
||||||
|
if ($debug === 1) {
|
||||||
|
echo json_encode([
|
||||||
|
'endpoint' => $endpoint,
|
||||||
|
'full_url' => $full_url,
|
||||||
|
'raw_response' => $data
|
||||||
|
], JSON_PRETTY_PRINT | JSON_UNESCAPED_UNICODE);
|
||||||
|
exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
$analisi = $data['value'] ?? [];
|
||||||
|
|
||||||
|
echo json_encode([
|
||||||
|
'value' => $analisi,
|
||||||
|
'count' => is_array($analisi) ? count($analisi) : 0
|
||||||
|
], JSON_UNESCAPED_UNICODE);
|
||||||
|
} catch (Exception $e) {
|
||||||
|
file_put_contents(
|
||||||
|
__DIR__ . '/error_log_analisi.txt',
|
||||||
|
date('Y-m-d H:i:s') . ' - ' . $e->getMessage() . PHP_EOL,
|
||||||
|
FILE_APPEND
|
||||||
|
);
|
||||||
|
|
||||||
|
http_response_code(500);
|
||||||
|
echo json_encode(['error' => $e->getMessage()]);
|
||||||
|
}
|
||||||
95
public/userarea/get_analisiabilitate.php
Normal file
95
public/userarea/get_analisiabilitate.php
Normal file
@ -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);
|
||||||
|
}
|
||||||
88
public/userarea/get_campionematrice.php
Normal file
88
public/userarea/get_campionematrice.php
Normal file
@ -0,0 +1,88 @@
|
|||||||
|
<?php
|
||||||
|
require_once dirname(__DIR__, 2) . '/vendor/autoload.php';
|
||||||
|
require_once dirname(__FILE__) . '/class/VisualLimsApiClient.class.php';
|
||||||
|
|
||||||
|
header('Content-Type: application/json');
|
||||||
|
|
||||||
|
ini_set('display_errors', '0');
|
||||||
|
error_reporting(E_ALL);
|
||||||
|
|
||||||
|
try {
|
||||||
|
$api = VisualLimsApiClient::getInstance();
|
||||||
|
|
||||||
|
// Campione hardcoded del tuo esempio
|
||||||
|
$idCampione = 749027;
|
||||||
|
|
||||||
|
// Matrice attesa dal log STEP 6
|
||||||
|
$expectedMatriceId = 6430;
|
||||||
|
|
||||||
|
// Endpoint con expand della Matrice
|
||||||
|
$endpoint = "Campione($idCampione)";
|
||||||
|
$options = [
|
||||||
|
'$expand' => 'Matrice'
|
||||||
|
];
|
||||||
|
|
||||||
|
// Debug URL
|
||||||
|
$base_url = 'https://93.43.5.102/limsapi/api/odata/';
|
||||||
|
$query = http_build_query($options);
|
||||||
|
$queryReadable = urldecode($query);
|
||||||
|
|
||||||
|
$full_url = $base_url . $endpoint . ($queryReadable ? '?' . $queryReadable : '');
|
||||||
|
|
||||||
|
file_put_contents(
|
||||||
|
__DIR__ . '/last_url_check_matrice.txt',
|
||||||
|
$full_url . PHP_EOL,
|
||||||
|
FILE_APPEND
|
||||||
|
);
|
||||||
|
|
||||||
|
// Chiamata API
|
||||||
|
$data = $api->get($endpoint, $options);
|
||||||
|
|
||||||
|
// Recupero Matrice dalla response
|
||||||
|
$matrice = $data['Matrice'] ?? null;
|
||||||
|
|
||||||
|
$actualMatriceId = null;
|
||||||
|
|
||||||
|
if (is_array($matrice)) {
|
||||||
|
// Provo i nomi più probabili
|
||||||
|
$actualMatriceId = $matrice['IdMatrice']
|
||||||
|
?? $matrice['idMatrice']
|
||||||
|
?? $matrice['Id']
|
||||||
|
?? $matrice['ID']
|
||||||
|
?? null;
|
||||||
|
}
|
||||||
|
|
||||||
|
$matrice_ok = ((int)$actualMatriceId === (int)$expectedMatriceId);
|
||||||
|
|
||||||
|
$output = [
|
||||||
|
'success' => true,
|
||||||
|
'idCampione' => $idCampione,
|
||||||
|
'expectedMatriceId' => $expectedMatriceId,
|
||||||
|
'actualMatriceId' => $actualMatriceId,
|
||||||
|
'matrice_ok' => $matrice_ok,
|
||||||
|
'request_url' => $full_url,
|
||||||
|
'matrice' => $matrice,
|
||||||
|
'raw_response' => $data
|
||||||
|
];
|
||||||
|
|
||||||
|
// Salva JSON completo
|
||||||
|
file_put_contents(
|
||||||
|
__DIR__ . '/check_matrice_campione_749027_response.json',
|
||||||
|
json_encode($output, JSON_PRETTY_PRINT | JSON_UNESCAPED_UNICODE)
|
||||||
|
);
|
||||||
|
|
||||||
|
echo json_encode($output, JSON_PRETTY_PRINT | JSON_UNESCAPED_UNICODE);
|
||||||
|
} catch (Exception $e) {
|
||||||
|
file_put_contents(
|
||||||
|
__DIR__ . '/error_log_check_matrice.txt',
|
||||||
|
date('Y-m-d H:i:s') . ' - ' . $e->getMessage() . PHP_EOL,
|
||||||
|
FILE_APPEND
|
||||||
|
);
|
||||||
|
|
||||||
|
http_response_code(500);
|
||||||
|
|
||||||
|
echo json_encode([
|
||||||
|
'success' => false,
|
||||||
|
'error' => $e->getMessage()
|
||||||
|
], JSON_PRETTY_PRINT | JSON_UNESCAPED_UNICODE);
|
||||||
|
}
|
||||||
@ -74,10 +74,21 @@ try {
|
|||||||
throw new Exception("Massimo numero di tentativi raggiunto per $endpoint");
|
throw new Exception("Massimo numero di tentativi raggiunto per $endpoint");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Cache file (1 hour TTL)
|
||||||
|
$cacheFile = __DIR__ . '/cache/clienti.json';
|
||||||
|
if (file_exists($cacheFile) && (time() - filemtime($cacheFile) < 3600)) {
|
||||||
|
readfile($cacheFile);
|
||||||
|
exit;
|
||||||
|
}
|
||||||
|
|
||||||
// Esegui la chiamata con retry
|
// Esegui la chiamata con retry
|
||||||
$data = makeApiRequest($api, $endpoint);
|
$data = makeApiRequest($api, $endpoint);
|
||||||
|
|
||||||
echo json_encode($data);
|
$json = json_encode($data);
|
||||||
|
if (!is_dir(__DIR__ . '/cache')) mkdir(__DIR__ . '/cache', 0777, true);
|
||||||
|
file_put_contents($cacheFile, $json);
|
||||||
|
|
||||||
|
echo $json;
|
||||||
} catch (Exception $e) {
|
} catch (Exception $e) {
|
||||||
http_response_code(500);
|
http_response_code(500);
|
||||||
$errorResponse = [
|
$errorResponse = [
|
||||||
|
|||||||
103
public/userarea/get_clienti_raw.php
Normal file
103
public/userarea/get_clienti_raw.php
Normal file
@ -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 = [];
|
$results = [];
|
||||||
|
$cacheDir = __DIR__ . '/cache';
|
||||||
|
if (!is_dir($cacheDir)) mkdir($cacheDir, 0777, true);
|
||||||
|
|
||||||
foreach ($fieldIds as $customFieldId) {
|
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";
|
$endpoint = "CustomField($customFieldId)?\$expand=CustomFieldsValues";
|
||||||
$data = $api->get($endpoint);
|
$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);
|
echo json_encode($results);
|
||||||
} catch (Exception $e) {
|
} catch (Exception $e) {
|
||||||
http_response_code(500);
|
http_response_code(500);
|
||||||
|
|||||||
@ -1,59 +1,46 @@
|
|||||||
<?php
|
<?php
|
||||||
require_once dirname(__DIR__, 2) . '/vendor/autoload.php'; // Risale a root/vendor/
|
require_once dirname(__DIR__, 2) . '/vendor/autoload.php';
|
||||||
use Dotenv\Dotenv;
|
require_once __DIR__ . '/class/db-functions.php';
|
||||||
|
include dirname(__DIR__) . '/../extra/auth.php';
|
||||||
|
if (!Auth::check()) { http_response_code(401); echo json_encode(['error' => 'Unauthorized']); exit; }
|
||||||
|
|
||||||
// Set JSON header
|
|
||||||
header('Content-Type: application/json');
|
header('Content-Type: application/json');
|
||||||
|
|
||||||
// Debug: Log the path where we expect the .env file
|
|
||||||
$envPath = dirname(__DIR__, 2);
|
|
||||||
file_put_contents(__DIR__ . '/debug_log.txt', date('Y-m-d H:i:s') . ' - Expected .env path: ' . $envPath . PHP_EOL, FILE_APPEND);
|
|
||||||
|
|
||||||
// Carica il file .env dalla root del progetto
|
|
||||||
try {
|
try {
|
||||||
$dotenv = Dotenv::createImmutable($envPath);
|
// Read from matrici cache (populated by get_matrici.php / warm_cache.php)
|
||||||
$dotenv->load();
|
$cacheFile = __DIR__ . '/cache/matrici.json';
|
||||||
} catch (Exception $e) {
|
if (file_exists($cacheFile)) {
|
||||||
file_put_contents(__DIR__ . '/error_log.txt', date('Y-m-d H:i:s') . ' - Errore caricamento .env: ' . $e->getMessage() . PHP_EOL, FILE_APPEND);
|
$data = json_decode(file_get_contents($cacheFile), true);
|
||||||
echo json_encode(['success' => false, 'message' => 'Errore caricamento configurazione: ' . $e->getMessage()]);
|
$matrici = $data['value'] ?? [];
|
||||||
exit(1);
|
} else {
|
||||||
}
|
// Fallback: trigger cache creation
|
||||||
|
require_once __DIR__ . '/class/VisualLimsApiClient.class.php';
|
||||||
|
$api = VisualLimsApiClient::getInstance();
|
||||||
|
$apiData = $api->get('Matrice');
|
||||||
|
$matrici = [];
|
||||||
|
foreach (($apiData['value'] ?? []) as $item) {
|
||||||
|
$nome = $item['NomeMatrice'] ?? '';
|
||||||
|
if ($nome !== '' && substr($nome, 0, 1) !== '*') {
|
||||||
|
$matrici[] = ['IdMatrice' => $item['IdMatrice'], 'NomeMatrice' => $nome, 'MacroMatrice' => $item['MacroMatrice'] ?? null];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!is_dir(__DIR__ . '/cache')) mkdir(__DIR__ . '/cache', 0777, true);
|
||||||
|
file_put_contents($cacheFile, json_encode(['success' => true, 'value' => $matrici]));
|
||||||
|
}
|
||||||
|
|
||||||
// Recupera le variabili d'ambiente
|
// Extract unique MacroMatrice values
|
||||||
$dbHost = $_ENV['DB_HOST'];
|
$macroValues = [];
|
||||||
$dbName = $_ENV['DB_DATABASE'];
|
foreach ($matrici as $m) {
|
||||||
$dbUser = $_ENV['DB_USERNAME'];
|
$macro = $m['MacroMatrice'] ?? null;
|
||||||
$dbPass = $_ENV['DB_PASSWORD'];
|
if ($macro !== null && $macro !== '' && substr($macro, 0, 1) !== '*') {
|
||||||
$dbPrefix = $_ENV['DB_PREFIX'];
|
$macroValues[$macro] = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
$macroMatrici = array_keys($macroValues);
|
||||||
|
sort($macroMatrici);
|
||||||
|
|
||||||
// Debug: Log database connection details (excluding password)
|
|
||||||
file_put_contents(__DIR__ . '/debug_log.txt', date('Y-m-d H:i:s') . " - DB Connection: host=$dbHost, dbname=$dbName, user=$dbUser, prefix=$dbPrefix" . PHP_EOL, FILE_APPEND);
|
|
||||||
|
|
||||||
// Connessione al database MySQL
|
|
||||||
try {
|
|
||||||
$pdo = new PDO("mysql:host=$dbHost;dbname=$dbName;charset=utf8mb4", $dbUser, $dbPass);
|
|
||||||
$pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
|
|
||||||
} catch (PDOException $e) {
|
|
||||||
file_put_contents(__DIR__ . '/error_log.txt', date('Y-m-d H:i:s') . ' - Errore connessione DB: ' . $e->getMessage() . PHP_EOL, FILE_APPEND);
|
|
||||||
echo json_encode(['success' => false, 'message' => 'Errore connessione al database: ' . $e->getMessage()]);
|
|
||||||
exit(1);
|
|
||||||
}
|
|
||||||
|
|
||||||
try {
|
|
||||||
// Query per recuperare i valori distinti di MacroMatrice, escludendo quelli che iniziano con '*' e ordinandoli
|
|
||||||
$query = "SELECT DISTINCT MacroMatrice FROM {$dbPrefix}matrici WHERE MacroMatrice IS NOT NULL AND MacroMatrice NOT LIKE '*%' ORDER BY MacroMatrice ASC";
|
|
||||||
$stmt = $pdo->prepare($query);
|
|
||||||
$stmt->execute();
|
|
||||||
$macroMatrici = $stmt->fetchAll(PDO::FETCH_COLUMN);
|
|
||||||
|
|
||||||
// Debug: Log del numero di MacroMatrice recuperate
|
|
||||||
file_put_contents(__DIR__ . '/debug_log.txt', date('Y-m-d H:i:s') . ' - Retrieved ' . count($macroMatrici) . ' MacroMatrice from database' . PHP_EOL, FILE_APPEND);
|
|
||||||
|
|
||||||
// Restituisci risposta JSON
|
|
||||||
echo json_encode(['success' => true, 'value' => $macroMatrici]);
|
echo json_encode(['success' => true, 'value' => $macroMatrici]);
|
||||||
} catch (PDOException $e) {
|
} catch (Exception $e) {
|
||||||
// Log errore e restituisci risposta di errore
|
http_response_code(500);
|
||||||
file_put_contents(__DIR__ . '/error_log.txt', date('Y-m-d H:i:s') . ' - Errore nel recupero delle MacroMatrice: ' . $e->getMessage() . PHP_EOL, FILE_APPEND);
|
echo json_encode(['success' => false, 'message' => $e->getMessage()]);
|
||||||
echo json_encode(['success' => false, 'message' => 'Errore nel recupero delle MacroMatrice: ' . $e->getMessage()]);
|
|
||||||
exit(1);
|
|
||||||
}
|
}
|
||||||
|
|||||||
47
public/userarea/get_matrici.php
Normal file
47
public/userarea/get_matrici.php
Normal file
@ -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()]);
|
||||||
|
}
|
||||||
256
public/userarea/get_rapporto_full_by_codice.php
Normal file
256
public/userarea/get_rapporto_full_by_codice.php
Normal file
@ -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);
|
||||||
|
}
|
||||||
118
public/userarea/get_rapporto_lims.php
Normal file
118
public/userarea/get_rapporto_lims.php
Normal file
@ -0,0 +1,118 @@
|
|||||||
|
<?php
|
||||||
|
require_once dirname(__DIR__, 2) . '/vendor/autoload.php';
|
||||||
|
require_once dirname(__FILE__) . '/class/VisualLimsApiClient.class.php';
|
||||||
|
|
||||||
|
header('Content-Type: application/json; charset=utf-8');
|
||||||
|
|
||||||
|
ini_set('display_errors', '0');
|
||||||
|
error_reporting(E_ALL);
|
||||||
|
|
||||||
|
try {
|
||||||
|
$api = VisualLimsApiClient::getInstance();
|
||||||
|
|
||||||
|
$commessa = trim($_GET['commessa'] ?? '');
|
||||||
|
|
||||||
|
if ($commessa === '') {
|
||||||
|
throw new Exception("Parametro commessa mancante");
|
||||||
|
}
|
||||||
|
|
||||||
|
$debugDir = __DIR__ . '/logs/lims_rapporto/';
|
||||||
|
if (!is_dir($debugDir)) {
|
||||||
|
mkdir($debugDir, 0755, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* STEP 1
|
||||||
|
* Provo a cercare il rapporto con filtri leggeri.
|
||||||
|
* NIENTE expand pesanti.
|
||||||
|
*/
|
||||||
|
$attempts = [];
|
||||||
|
|
||||||
|
$filters = [
|
||||||
|
'CodiceCommessa' => "CodiceCommessa eq '{$commessa}'",
|
||||||
|
'Commessa_CodiceCommessa' => "Commessa/CodiceCommessa eq '{$commessa}'",
|
||||||
|
'Commessa_IdCommessa' => is_numeric($commessa) ? "Commessa/IdCommessa eq {$commessa}" : null,
|
||||||
|
'Codice' => "Codice eq '{$commessa}'"
|
||||||
|
];
|
||||||
|
|
||||||
|
foreach ($filters as $label => $filter) {
|
||||||
|
if (!$filter) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
$options = [
|
||||||
|
'$filter' => $filter,
|
||||||
|
'$top' => 10
|
||||||
|
];
|
||||||
|
|
||||||
|
$data = $api->get('Rapporto', $options);
|
||||||
|
|
||||||
|
$attempts[$label] = [
|
||||||
|
'success' => true,
|
||||||
|
'filter' => $filter,
|
||||||
|
'records' => isset($data['value']) && is_array($data['value']) ? count($data['value']) : null,
|
||||||
|
'data' => $data
|
||||||
|
];
|
||||||
|
|
||||||
|
file_put_contents(
|
||||||
|
$debugDir . "rapporto_{$commessa}_{$label}.json",
|
||||||
|
json_encode($data, JSON_PRETTY_PRINT | JSON_UNESCAPED_UNICODE)
|
||||||
|
);
|
||||||
|
} catch (Exception $e) {
|
||||||
|
$attempts[$label] = [
|
||||||
|
'success' => false,
|
||||||
|
'filter' => $filter,
|
||||||
|
'error' => $e->getMessage()
|
||||||
|
];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* STEP 2
|
||||||
|
* Prendo solo eventuali rapporti trovati.
|
||||||
|
*/
|
||||||
|
$rapportiFound = [];
|
||||||
|
|
||||||
|
foreach ($attempts as $label => $attempt) {
|
||||||
|
if (!$attempt['success']) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
$items = $attempt['data']['value'] ?? [];
|
||||||
|
|
||||||
|
if (!is_array($items)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach ($items as $item) {
|
||||||
|
$rapportiFound[] = [
|
||||||
|
'matched_by' => $label,
|
||||||
|
'rapporto' => $item
|
||||||
|
];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
echo json_encode([
|
||||||
|
'success' => true,
|
||||||
|
'input_commessa' => $commessa,
|
||||||
|
'message' => 'Ricerca leggera su Rapporto completata. Se trovi un rapporto, poi recuperiamo RapportiFiles solo per quello.',
|
||||||
|
'rapporti_found_count' => count($rapportiFound),
|
||||||
|
'rapporti_found' => $rapportiFound,
|
||||||
|
'attempts_summary' => array_map(function ($a) {
|
||||||
|
return [
|
||||||
|
'success' => $a['success'],
|
||||||
|
'filter' => $a['filter'],
|
||||||
|
'records' => $a['records'] ?? null,
|
||||||
|
'error' => $a['error'] ?? null
|
||||||
|
];
|
||||||
|
}, $attempts)
|
||||||
|
], JSON_PRETTY_PRINT | JSON_UNESCAPED_UNICODE);
|
||||||
|
} catch (Exception $e) {
|
||||||
|
http_response_code(500);
|
||||||
|
|
||||||
|
echo json_encode([
|
||||||
|
'success' => false,
|
||||||
|
'error' => $e->getMessage()
|
||||||
|
], JSON_PRETTY_PRINT | JSON_UNESCAPED_UNICODE);
|
||||||
|
}
|
||||||
@ -2,25 +2,140 @@
|
|||||||
require_once dirname(__DIR__, 2) . '/vendor/autoload.php';
|
require_once dirname(__DIR__, 2) . '/vendor/autoload.php';
|
||||||
require_once dirname(__FILE__) . '/class/VisualLimsApiClient.class.php';
|
require_once dirname(__FILE__) . '/class/VisualLimsApiClient.class.php';
|
||||||
|
|
||||||
header('Content-Type: application/json');
|
header('Content-Type: application/json; charset=utf-8');
|
||||||
|
|
||||||
ini_set('display_errors', '0');
|
ini_set('display_errors', '0');
|
||||||
error_reporting(E_ALL);
|
error_reporting(E_ALL);
|
||||||
|
|
||||||
try {
|
try {
|
||||||
$api = VisualLimsApiClient::getInstance();
|
$api = VisualLimsApiClient::getInstance();
|
||||||
$rapporto_id = 533329;
|
|
||||||
|
|
||||||
// Costruzione manuale dell'endpoint con espansione annidata
|
// Esempi:
|
||||||
$endpoint = "Rapporto($rapporto_id)?\$expand=CampioniDatiRapporto(\$expand=AnalisiDatiRapporto,CustomFieldsDatiRapporto)";
|
// rapporto_by_codice_expand_step.php?codice=2541111&step=base
|
||||||
|
// rapporto_by_codice_expand_step.php?codice=2541111&step=files
|
||||||
|
// rapporto_by_codice_expand_step.php?codice=2541111&step=campioni
|
||||||
|
// rapporto_by_codice_expand_step.php?codice=2541111&step=files_campioni
|
||||||
|
|
||||||
// Non passiamo options, già incluso nell'endpoint
|
$codiceRapporto = trim($_GET['codice'] ?? '');
|
||||||
$data = $api->get($endpoint);
|
$step = trim($_GET['step'] ?? 'base');
|
||||||
|
|
||||||
file_put_contents(__DIR__ . '/rapporto_expanded.json', json_encode($data, JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES));
|
if ($codiceRapporto === '') {
|
||||||
echo json_encode($data);
|
throw new Exception("Parametro codice mancante. Usa ?codice=2541111");
|
||||||
|
}
|
||||||
|
|
||||||
|
$allowedSteps = [
|
||||||
|
'base' => '',
|
||||||
|
'files' => 'RapportiFiles',
|
||||||
|
'allegati' => 'RapportiAllegati',
|
||||||
|
'campioni' => 'CampioniDatiRapporto',
|
||||||
|
'files_campioni' => 'RapportiFiles,CampioniDatiRapporto'
|
||||||
|
];
|
||||||
|
|
||||||
|
if (!array_key_exists($step, $allowedSteps)) {
|
||||||
|
throw new Exception("Step non valido. Usa: " . implode(', ', array_keys($allowedSteps)));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Escape OData per eventuali apostrofi
|
||||||
|
$codiceRapportoSafe = str_replace("'", "''", $codiceRapporto);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* STEP 1 - Trova IdRapporto partendo da CodiceRapporto.
|
||||||
|
* Query leggera, con $select e $top=1.
|
||||||
|
*/
|
||||||
|
$searchParams = [
|
||||||
|
'$filter' => "CodiceRapporto eq '{$codiceRapportoSafe}'",
|
||||||
|
'$select' => 'IdRapporto,CodiceRapporto,Data,Versione,Firmato,DataStampa',
|
||||||
|
'$top' => 1
|
||||||
|
];
|
||||||
|
|
||||||
|
$searchEndpoint = "Rapporto?" . http_build_query($searchParams);
|
||||||
|
|
||||||
|
$searchData = $api->get($searchEndpoint);
|
||||||
|
|
||||||
|
$items = $searchData['value'] ?? [];
|
||||||
|
|
||||||
|
if (!is_array($items) || count($items) === 0) {
|
||||||
|
echo json_encode([
|
||||||
|
'success' => false,
|
||||||
|
'message' => 'Nessun rapporto trovato per questo CodiceRapporto.',
|
||||||
|
'codice_rapporto' => $codiceRapporto,
|
||||||
|
'search_endpoint' => $searchEndpoint,
|
||||||
|
'search_data' => $searchData
|
||||||
|
], JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE);
|
||||||
|
exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
$rapportoBase = $items[0];
|
||||||
|
$rapportoId = intval($rapportoBase['IdRapporto'] ?? 0);
|
||||||
|
|
||||||
|
if (!$rapportoId) {
|
||||||
|
throw new Exception("IdRapporto non trovato nella risposta.");
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* STEP 2 - Se step=base, restituisco solo la ricerca base.
|
||||||
|
*/
|
||||||
|
if ($step === 'base') {
|
||||||
|
echo json_encode([
|
||||||
|
'success' => true,
|
||||||
|
'codice_rapporto' => $codiceRapporto,
|
||||||
|
'id_rapporto' => $rapportoId,
|
||||||
|
'step' => $step,
|
||||||
|
'search_endpoint' => $searchEndpoint,
|
||||||
|
'rapporto_base' => $rapportoBase
|
||||||
|
], JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE);
|
||||||
|
exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* STEP 3 - Espande SOLO il rapporto trovato.
|
||||||
|
*/
|
||||||
|
$expandValue = $allowedSteps[$step];
|
||||||
|
|
||||||
|
$detailParams = [
|
||||||
|
'$expand' => $expandValue
|
||||||
|
];
|
||||||
|
|
||||||
|
$detailEndpoint = "Rapporto({$rapportoId})?" . http_build_query($detailParams);
|
||||||
|
|
||||||
|
file_put_contents(
|
||||||
|
__DIR__ . '/last_rapporto_by_codice_expand_endpoint.txt',
|
||||||
|
'[' . date('Y-m-d H:i:s') . '] SEARCH: ' . $searchEndpoint . PHP_EOL .
|
||||||
|
'[' . date('Y-m-d H:i:s') . '] DETAIL: ' . $detailEndpoint . PHP_EOL,
|
||||||
|
FILE_APPEND
|
||||||
|
);
|
||||||
|
|
||||||
|
$detailData = $api->get($detailEndpoint);
|
||||||
|
|
||||||
|
file_put_contents(
|
||||||
|
__DIR__ . "/rapporto_codice_{$codiceRapportoSafe}_{$step}.json",
|
||||||
|
json_encode([
|
||||||
|
'search' => $searchData,
|
||||||
|
'detail' => $detailData
|
||||||
|
], JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE)
|
||||||
|
);
|
||||||
|
|
||||||
|
echo json_encode([
|
||||||
|
'success' => true,
|
||||||
|
'codice_rapporto' => $codiceRapporto,
|
||||||
|
'id_rapporto' => $rapportoId,
|
||||||
|
'step' => $step,
|
||||||
|
'search_endpoint' => $searchEndpoint,
|
||||||
|
'detail_endpoint' => $detailEndpoint,
|
||||||
|
'rapporto_base' => $rapportoBase,
|
||||||
|
'data' => $detailData
|
||||||
|
], JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE);
|
||||||
} catch (Exception $e) {
|
} catch (Exception $e) {
|
||||||
file_put_contents(__DIR__ . '/error_log.txt', date('Y-m-d H:i:s') . ' - ' . $e->getMessage() . PHP_EOL, FILE_APPEND);
|
file_put_contents(
|
||||||
|
__DIR__ . '/error_log.txt',
|
||||||
|
date('Y-m-d H:i:s') . ' - ' . $e->getMessage() . PHP_EOL,
|
||||||
|
FILE_APPEND
|
||||||
|
);
|
||||||
|
|
||||||
http_response_code(500);
|
http_response_code(500);
|
||||||
echo json_encode(['error' => $e->getMessage()]);
|
|
||||||
|
echo json_encode([
|
||||||
|
'success' => false,
|
||||||
|
'error' => $e->getMessage()
|
||||||
|
], JSON_PRETTY_PRINT | JSON_UNESCAPED_UNICODE);
|
||||||
}
|
}
|
||||||
|
|||||||
1342
public/userarea/gridRenderer.js
Normal file
1342
public/userarea/gridRenderer.js
Normal file
File diff suppressed because it is too large
Load Diff
@ -10,65 +10,125 @@
|
|||||||
<title>Template Buttons - <?= htmlspecialchars($titlewebsite, ENT_QUOTES, 'UTF-8'); ?></title>
|
<title>Template Buttons - <?= htmlspecialchars($titlewebsite, ENT_QUOTES, 'UTF-8'); ?></title>
|
||||||
|
|
||||||
<style>
|
<style>
|
||||||
/* Layout flessibile per gestire dimensioni diverse */
|
/* Main buttons container */
|
||||||
#templateButtons {
|
.template-buttons {
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-wrap: wrap;
|
flex-wrap: wrap;
|
||||||
gap: 10px;
|
gap: 12px;
|
||||||
justify-content: flex-start;
|
justify-content: flex-start;
|
||||||
/* Allinea a sinistra */
|
padding: 10px 0;
|
||||||
padding: 20px;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Definizione delle dimensioni */
|
/* Button sizes */
|
||||||
/* Definizione delle dimensioni */
|
|
||||||
.btn-small {
|
.btn-small {
|
||||||
font-size: 12px;
|
font-size: 12px;
|
||||||
padding: 6px 12px;
|
padding: 6px 12px;
|
||||||
min-width: 100px;
|
min-width: 100px;
|
||||||
min-height: 30px;
|
min-height: 36px;
|
||||||
display: flex;
|
display: inline-flex;
|
||||||
/* Aggiunto */
|
|
||||||
justify-content: center;
|
justify-content: center;
|
||||||
/* Aggiunto */
|
|
||||||
align-items: center;
|
align-items: center;
|
||||||
/* Aggiunto */
|
gap: 8px;
|
||||||
|
border-radius: 8px;
|
||||||
|
text-align: center;
|
||||||
}
|
}
|
||||||
|
|
||||||
.btn-medium {
|
.btn-medium {
|
||||||
font-size: 16px;
|
font-size: 16px;
|
||||||
padding: 10px 20px;
|
padding: 10px 20px;
|
||||||
min-width: 130px;
|
min-width: 140px;
|
||||||
min-height: 45px;
|
min-height: 48px;
|
||||||
display: flex;
|
display: inline-flex;
|
||||||
/* Aggiunto */
|
|
||||||
justify-content: center;
|
justify-content: center;
|
||||||
/* Aggiunto */
|
|
||||||
align-items: center;
|
align-items: center;
|
||||||
/* Aggiunto */
|
gap: 8px;
|
||||||
|
border-radius: 10px;
|
||||||
|
text-align: center;
|
||||||
}
|
}
|
||||||
|
|
||||||
.btn-large {
|
.btn-large {
|
||||||
font-size: 20px;
|
font-size: 20px;
|
||||||
padding: 14px 28px;
|
padding: 14px 28px;
|
||||||
min-width: 180px;
|
min-width: 190px;
|
||||||
min-height: 60px;
|
min-height: 64px;
|
||||||
display: flex;
|
display: inline-flex;
|
||||||
/* Aggiunto */
|
|
||||||
justify-content: center;
|
justify-content: center;
|
||||||
/* Aggiunto */
|
|
||||||
align-items: center;
|
align-items: center;
|
||||||
/* Aggiunto */
|
gap: 10px;
|
||||||
|
border-radius: 12px;
|
||||||
|
text-align: center;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Stile della barra di ricerca */
|
.template-icon {
|
||||||
|
font-size: 18px;
|
||||||
|
line-height: 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
.btn-large .template-icon {
|
||||||
|
font-size: 22px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.btn-small .template-icon {
|
||||||
|
font-size: 15px;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Search box */
|
||||||
#searchInput {
|
#searchInput {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
padding: 10px;
|
padding: 10px 14px;
|
||||||
font-size: 16px;
|
font-size: 16px;
|
||||||
margin-bottom: 15px;
|
margin-bottom: 18px;
|
||||||
border: 1px solid #ccc;
|
border: 1px solid #d9d9d9;
|
||||||
border-radius: 5px;
|
border-radius: 8px;
|
||||||
|
outline: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
#searchInput:focus {
|
||||||
|
border-color: #0d6efd;
|
||||||
|
box-shadow: 0 0 0 0.15rem rgba(13, 110, 253, 0.15);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Tabs */
|
||||||
|
.custom-tabs {
|
||||||
|
border-bottom: 1px solid #e5e5e5;
|
||||||
|
margin-bottom: 20px;
|
||||||
|
gap: 6px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.custom-tabs .nav-link {
|
||||||
|
border: none;
|
||||||
|
border-radius: 10px 10px 0 0;
|
||||||
|
color: #555;
|
||||||
|
font-weight: 500;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 8px;
|
||||||
|
padding: 10px 18px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.custom-tabs .nav-link:hover {
|
||||||
|
background: #f8f9fa;
|
||||||
|
color: #0d6efd;
|
||||||
|
}
|
||||||
|
|
||||||
|
.custom-tabs .nav-link.active {
|
||||||
|
background: #0d6efd;
|
||||||
|
color: #fff;
|
||||||
|
}
|
||||||
|
|
||||||
|
.tab-pane {
|
||||||
|
min-height: 140px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.empty-message {
|
||||||
|
color: #6c757d;
|
||||||
|
font-style: italic;
|
||||||
|
padding: 10px 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.loading-message {
|
||||||
|
color: #6c757d;
|
||||||
|
padding: 10px 0;
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
</head>
|
</head>
|
||||||
@ -87,14 +147,54 @@
|
|||||||
<h6 class="mb-0">Active Templates</h6>
|
<h6 class="mb-0">Active Templates</h6>
|
||||||
</div>
|
</div>
|
||||||
<div class="card-body">
|
<div class="card-body">
|
||||||
<!-- Barra di ricerca -->
|
|
||||||
<input type="text" id="searchInput" placeholder="Search template...">
|
<input type="text" id="searchInput" placeholder="Search template...">
|
||||||
<div id="templateButtons"></div>
|
|
||||||
|
<ul class="nav nav-tabs custom-tabs" id="templateTabs" role="tablist">
|
||||||
|
<li class="nav-item" role="presentation">
|
||||||
|
<button class="nav-link active" id="xls-tab" data-bs-toggle="tab" data-bs-target="#xls-pane" type="button" role="tab" aria-controls="xls-pane" aria-selected="true">
|
||||||
|
<i class="bx bx-spreadsheet template-icon"></i>
|
||||||
|
XLS
|
||||||
|
</button>
|
||||||
|
</li>
|
||||||
|
<li class="nav-item" role="presentation">
|
||||||
|
<button class="nav-link" id="api-tab" data-bs-toggle="tab" data-bs-target="#api-pane" type="button" role="tab" aria-controls="api-pane" aria-selected="false">
|
||||||
|
<i class="bx bx-transfer-alt template-icon"></i>
|
||||||
|
JSON/API
|
||||||
|
</button>
|
||||||
|
</li>
|
||||||
|
<li class="nav-item" role="presentation">
|
||||||
|
<button class="nav-link" id="pdf-tab" data-bs-toggle="tab" data-bs-target="#pdf-pane" type="button" role="tab" aria-controls="pdf-pane" aria-selected="false">
|
||||||
|
<i class="bx bx-file-pdf template-icon"></i>
|
||||||
|
PDF
|
||||||
|
</button>
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
|
||||||
|
<div class="tab-content" id="templateTabsContent">
|
||||||
|
<div class="tab-pane fade show active" id="xls-pane" role="tabpanel" aria-labelledby="xls-tab">
|
||||||
|
<div id="templateButtonsXLS" class="template-buttons">
|
||||||
|
<div class="loading-message">Loading XLS templates...</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="tab-pane fade" id="api-pane" role="tabpanel" aria-labelledby="api-tab">
|
||||||
|
<div id="templateButtonsAPI" class="template-buttons">
|
||||||
|
<div class="loading-message">Loading API templates...</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="tab-pane fade" id="pdf-pane" role="tabpanel" aria-labelledby="pdf-tab">
|
||||||
|
<div id="templateButtonsPDF" class="template-buttons">
|
||||||
|
<div class="loading-message">Loading PDF templates...</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="overlay toggle-icon"></div>
|
<div class="overlay toggle-icon"></div>
|
||||||
<a href="javaScript:;" class="back-to-top"><i class='bx bxs-up-arrow-alt'></i></a>
|
<a href="javaScript:;" class="back-to-top"><i class='bx bxs-up-arrow-alt'></i></a>
|
||||||
<?php include('include/footer.php'); ?>
|
<?php include('include/footer.php'); ?>
|
||||||
@ -104,46 +204,137 @@
|
|||||||
|
|
||||||
<script>
|
<script>
|
||||||
document.addEventListener("DOMContentLoaded", function() {
|
document.addEventListener("DOMContentLoaded", function() {
|
||||||
|
const allTemplates = [];
|
||||||
|
|
||||||
|
const containers = {
|
||||||
|
XLS: document.getElementById("templateButtonsXLS"),
|
||||||
|
API: document.getElementById("templateButtonsAPI"),
|
||||||
|
PDF: document.getElementById("templateButtonsPDF")
|
||||||
|
};
|
||||||
|
|
||||||
|
function getSizeClass(buttonSize) {
|
||||||
|
if (buttonSize === "small") return "btn-small";
|
||||||
|
if (buttonSize === "large") return "btn-large";
|
||||||
|
return "btn-medium";
|
||||||
|
}
|
||||||
|
|
||||||
|
function getTemplateIcon(sourceType) {
|
||||||
|
switch ((sourceType || '').toUpperCase()) {
|
||||||
|
case 'XLS':
|
||||||
|
return 'bx bx-spreadsheet';
|
||||||
|
case 'API':
|
||||||
|
return 'bx bx-transfer-alt';
|
||||||
|
case 'PDF':
|
||||||
|
return 'bx bx-file-pdf';
|
||||||
|
default:
|
||||||
|
return 'bx bx-file';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function createButton(template) {
|
||||||
|
const sizeClass = getSizeClass(template.button_size);
|
||||||
|
const sourceType = (template.source_type || '').toUpperCase();
|
||||||
|
const iconClass = getTemplateIcon(sourceType);
|
||||||
|
|
||||||
|
const btn = document.createElement("a");
|
||||||
|
btn.href = `import_xls2.php?id=${template.id}`;
|
||||||
|
btn.className = `btn ${sizeClass}`;
|
||||||
|
btn.style.backgroundColor = template.button_bg_color || '#0d6efd';
|
||||||
|
btn.style.color = template.button_text_color || '#ffffff';
|
||||||
|
btn.setAttribute("data-label", (template.button_label || '').toLowerCase());
|
||||||
|
btn.setAttribute("data-source-type", sourceType);
|
||||||
|
|
||||||
|
btn.innerHTML = `
|
||||||
|
<i class="${iconClass} template-icon"></i>
|
||||||
|
<span>${escapeHtml(template.button_label || 'Unnamed')}</span>
|
||||||
|
`;
|
||||||
|
|
||||||
|
return btn;
|
||||||
|
}
|
||||||
|
|
||||||
|
function escapeHtml(text) {
|
||||||
|
const div = document.createElement('div');
|
||||||
|
div.textContent = text;
|
||||||
|
return div.innerHTML;
|
||||||
|
}
|
||||||
|
|
||||||
|
function clearContainers() {
|
||||||
|
Object.values(containers).forEach(container => {
|
||||||
|
container.innerHTML = '';
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function renderTemplates(searchValue = '') {
|
||||||
|
clearContainers();
|
||||||
|
|
||||||
|
const grouped = {
|
||||||
|
XLS: [],
|
||||||
|
API: [],
|
||||||
|
PDF: []
|
||||||
|
};
|
||||||
|
|
||||||
|
allTemplates.forEach(template => {
|
||||||
|
const sourceType = (template.source_type || '').toUpperCase();
|
||||||
|
const label = (template.button_label || '').toLowerCase();
|
||||||
|
|
||||||
|
if (searchValue && !label.includes(searchValue)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (grouped[sourceType]) {
|
||||||
|
grouped[sourceType].push(template);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
Object.keys(grouped).forEach(type => {
|
||||||
|
const container = containers[type];
|
||||||
|
if (!container) return;
|
||||||
|
|
||||||
|
if (grouped[type].length === 0) {
|
||||||
|
container.innerHTML = `<div class="empty-message">No templates found in this section.</div>`;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
grouped[type].forEach(template => {
|
||||||
|
container.appendChild(createButton(template));
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
fetch("load_active_templates.php")
|
fetch("load_active_templates.php")
|
||||||
.then(response => response.json())
|
.then(response => response.json())
|
||||||
.then(data => {
|
.then(data => {
|
||||||
if (!data.success) {
|
if (!data.success) {
|
||||||
console.error("Error loading templates:", data.message);
|
console.error("Error loading templates:", data.message);
|
||||||
|
clearContainers();
|
||||||
|
Object.values(containers).forEach(container => {
|
||||||
|
container.innerHTML = `<div class="empty-message">Error loading templates.</div>`;
|
||||||
|
});
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
let templateButtons = document.getElementById("templateButtons");
|
if (!Array.isArray(data.data)) {
|
||||||
templateButtons.innerHTML = '';
|
clearContainers();
|
||||||
|
Object.values(containers).forEach(container => {
|
||||||
data.data.forEach(template => {
|
container.innerHTML = `<div class="empty-message">Invalid response format.</div>`;
|
||||||
let sizeClass = "btn-medium"; // Default
|
});
|
||||||
if (template.button_size === "small") sizeClass = "btn-small";
|
return;
|
||||||
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";
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
allTemplates.push(...data.data);
|
||||||
|
renderTemplates();
|
||||||
|
})
|
||||||
|
.catch(error => {
|
||||||
|
console.error("Fetch error:", error);
|
||||||
|
clearContainers();
|
||||||
|
Object.values(containers).forEach(container => {
|
||||||
|
container.innerHTML = `<div class="empty-message">Fetch error while loading templates.</div>`;
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
document.getElementById("searchInput").addEventListener("input", function() {
|
||||||
|
const searchValue = this.value.toLowerCase().trim();
|
||||||
|
renderTemplates(searchValue);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
</script>
|
</script>
|
||||||
|
|||||||
@ -1,10 +1,6 @@
|
|||||||
<?php
|
<?php
|
||||||
include('include/headscript.php');
|
include('include/headscript.php');
|
||||||
|
|
||||||
// ✅ FIX timezone (Rome)
|
|
||||||
ini_set('date.timezone', 'Europe/Rome');
|
|
||||||
date_default_timezone_set('Europe/Rome');
|
|
||||||
|
|
||||||
if ($_SERVER['REQUEST_METHOD'] !== 'POST' || !isset($_POST['template_id']) || !isset($_POST['selected_rows']) || !isset($_POST['filename'])) {
|
if ($_SERVER['REQUEST_METHOD'] !== 'POST' || !isset($_POST['template_id']) || !isset($_POST['selected_rows']) || !isset($_POST['filename'])) {
|
||||||
header("Location: xlstemplates_grid.php?status=error&message=" . urlencode("Richiesta non valida"));
|
header("Location: xlstemplates_grid.php?status=error&message=" . urlencode("Richiesta non valida"));
|
||||||
exit;
|
exit;
|
||||||
@ -167,6 +163,8 @@ $fixedFieldsRaw = $fixedStmt->fetchAll(PDO::FETCH_ASSOC);
|
|||||||
// Ordine desiderato: Cliente Responsabile prima, poi gli altri, ConsegnaRichiesta prima delle 3 speciali
|
// Ordine desiderato: Cliente Responsabile prima, poi gli altri, ConsegnaRichiesta prima delle 3 speciali
|
||||||
$desiredOrder = [
|
$desiredOrder = [
|
||||||
'ClienteResponsabile',
|
'ClienteResponsabile',
|
||||||
|
'ClienteFornitore',
|
||||||
|
'ClienteAnalisi',
|
||||||
'AnagraficaCertestObject',
|
'AnagraficaCertestObject',
|
||||||
'AnagraficaCertestService',
|
'AnagraficaCertestService',
|
||||||
'MoltiplicatorePrezzo',
|
'MoltiplicatorePrezzo',
|
||||||
@ -174,9 +172,13 @@ $desiredOrder = [
|
|||||||
// se ci sono altri campi fixed li mettiamo dopo
|
// se ci sono altri campi fixed li mettiamo dopo
|
||||||
];
|
];
|
||||||
|
|
||||||
|
// No exclusions: fixed fields will be rendered together at the end
|
||||||
|
$excludeFromFixed = [];
|
||||||
|
|
||||||
$fixedFields = [];
|
$fixedFields = [];
|
||||||
$tempMap = [];
|
$tempMap = [];
|
||||||
foreach ($fixedFieldsRaw as $f) {
|
foreach ($fixedFieldsRaw as $f) {
|
||||||
|
if (in_array($f['fixed_field_key'], $excludeFromFixed, true)) continue;
|
||||||
$tempMap[$f['fixed_field_key']] = $f;
|
$tempMap[$f['fixed_field_key']] = $f;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -195,10 +197,11 @@ foreach ($tempMap as $f) {
|
|||||||
// Maps logical fixed_field_key → real datadb column name (mirrors JS fixedAliasMap)
|
// Maps logical fixed_field_key → real datadb column name (mirrors JS fixedAliasMap)
|
||||||
$fixedAliasMap = [
|
$fixedAliasMap = [
|
||||||
'ClienteResponsabile' => 'cliente_responsabile_id',
|
'ClienteResponsabile' => 'cliente_responsabile_id',
|
||||||
|
'ClienteFornitore' => 'cliente_fornitore_id',
|
||||||
|
'ClienteAnalisi' => 'clienteAnalisi',
|
||||||
'MoltiplicatorePrezzo' => 'moltiplicatore_prezzo_id',
|
'MoltiplicatorePrezzo' => 'moltiplicatore_prezzo_id',
|
||||||
'AnagraficaCertestObject' => 'anagrafica_certest_object_id',
|
'AnagraficaCertestObject' => 'anagrafica_certest_object_id',
|
||||||
'AnagraficaCertestService' => 'anagrafica_certest_service_id',
|
'AnagraficaCertestService' => 'anagrafica_certest_service_id',
|
||||||
'ClienteFornitore' => 'cliente_fornitore_id',
|
|
||||||
'ConsegnaRichiesta' => 'consegna_richiesta',
|
'ConsegnaRichiesta' => 'consegna_richiesta',
|
||||||
];
|
];
|
||||||
|
|
||||||
@ -333,6 +336,7 @@ function fixedDefaultValue(array $f): string
|
|||||||
align-items: center;
|
align-items: center;
|
||||||
padding: 0;
|
padding: 0;
|
||||||
border-bottom: 1px solid #dee2e6;
|
border-bottom: 1px solid #dee2e6;
|
||||||
|
min-width: fit-content;
|
||||||
}
|
}
|
||||||
|
|
||||||
.grid-row:last-child {
|
.grid-row:last-child {
|
||||||
@ -360,8 +364,15 @@ function fixedDefaultValue(array $f): string
|
|||||||
}
|
}
|
||||||
|
|
||||||
@keyframes batch-pulse {
|
@keyframes batch-pulse {
|
||||||
0%, 100% { background-position: 0% 50%; }
|
|
||||||
50% { background-position: 100% 50%; }
|
0%,
|
||||||
|
100% {
|
||||||
|
background-position: 0% 50%;
|
||||||
|
}
|
||||||
|
|
||||||
|
50% {
|
||||||
|
background-position: 100% 50%;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.batch-row-spinner {
|
.batch-row-spinner {
|
||||||
@ -876,9 +887,8 @@ function fixedDefaultValue(array $f): string
|
|||||||
<div class="page-wrapper">
|
<div class="page-wrapper">
|
||||||
<div class="page-content">
|
<div class="page-content">
|
||||||
<div class="mb-3 text">
|
<div class="mb-3 text">
|
||||||
<a href="historical_trf.php?id=<?= $template_id ?>&status=i" class="btn btn-warning me-2">Imported (i)</a>
|
<a href="imported.php?id=<?= $template_id ?>" class="btn btn-warning me-2">Imported (i)</a>
|
||||||
<a href="historical_trf.php?id=<?= $template_id ?>&status=P" class="btn btn-primary me-2">In Progress (P)</a>
|
<a href="tolims.php?id=<?= $template_id ?>" class="btn btn-success">To LIMS (l)</a>
|
||||||
<a href="historical_trf.php?id=<?= $template_id ?>&status=l" class="btn btn-success">To LIMS (l)</a>
|
|
||||||
</div>
|
</div>
|
||||||
<div class="card radius-10">
|
<div class="card radius-10">
|
||||||
<div class="card-header">
|
<div class="card-header">
|
||||||
@ -946,14 +956,15 @@ function fixedDefaultValue(array $f): string
|
|||||||
|
|
||||||
$topColIndex = $mainFieldMapping ? 2 : 1; ?>
|
$topColIndex = $mainFieldMapping ? 2 : 1; ?>
|
||||||
|
|
||||||
<div class="grid-cell grid-top-cell" style="flex: 0 0 300px;" data-index="<?= $mainFieldMapping ? 2 : 1 ?>">
|
<div class="grid-cell grid-top-cell" style="flex: 0 0 150px;" data-index="<?= $mainFieldMapping ? 2 : 1 ?>"></div>
|
||||||
|
|
||||||
|
<div class="grid-cell grid-top-cell" style="flex: 0 0 300px;" data-index="<?= $mainFieldMapping ? 3 : 2 ?>">
|
||||||
<select class="custom-field dropdown-select client-select searchable-client" data-column="idclient" id="clientSelect" name="idclient">
|
<select class="custom-field dropdown-select client-select searchable-client" data-column="idclient" id="clientSelect" name="idclient">
|
||||||
<option value="">Select a client...</option>
|
<option value="">Select a client...</option>
|
||||||
</select>
|
</select>
|
||||||
<button type="button" class="propagate-btn" data-column="idclient"><i class="fas fa-arrow-down"></i></button>
|
<button type="button" class="propagate-btn" data-column="idclient"><i class="fas fa-arrow-down"></i></button>
|
||||||
<span id="clientLoadingStatus" class="text-muted" style="margin-left: 10px; display: none;">Recupero clienti in corso...</span>
|
<span id="clientLoadingStatus" class="text-muted" style="margin-left: 10px; display: none;">Recupero clienti in corso...</span>
|
||||||
</div>
|
</div>
|
||||||
<div class="grid-cell grid-top-cell" style="flex: 0 0 150px;" data-index="<?= $mainFieldMapping ? 3 : 2 ?>"></div>
|
|
||||||
<?php
|
<?php
|
||||||
|
|
||||||
$topColIndex = $mainFieldMapping ? 4 : 3;
|
$topColIndex = $mainFieldMapping ? 4 : 3;
|
||||||
@ -1065,6 +1076,8 @@ function fixedDefaultValue(array $f): string
|
|||||||
$isApiField = in_array($key, [
|
$isApiField = in_array($key, [
|
||||||
'MoltiplicatorePrezzo',
|
'MoltiplicatorePrezzo',
|
||||||
'ClienteResponsabile',
|
'ClienteResponsabile',
|
||||||
|
'ClienteFornitore',
|
||||||
|
'ClienteAnalisi',
|
||||||
'AnagraficaCertestObject',
|
'AnagraficaCertestObject',
|
||||||
'AnagraficaCertestService'
|
'AnagraficaCertestService'
|
||||||
], true);
|
], true);
|
||||||
@ -1118,9 +1131,9 @@ function fixedDefaultValue(array $f): string
|
|||||||
<div class="resizer"></div>
|
<div class="resizer"></div>
|
||||||
</div>
|
</div>
|
||||||
<?php endif; ?>
|
<?php endif; ?>
|
||||||
<div class="grid-header" data-index="<?= $mainFieldMapping ? 2 : 1 ?>" style="flex: 0 0 300px; position: relative;">Client<div class="resizer"></div>
|
<div class="grid-header" data-index="<?= $mainFieldMapping ? 2 : 1 ?>" style="flex: 0 0 150px; position: relative;">Status<div class="resizer"></div>
|
||||||
</div>
|
</div>
|
||||||
<div class="grid-header" data-index="<?= $mainFieldMapping ? 3 : 2 ?>" style="flex: 0 0 150px; position: relative;">Status<div class="resizer"></div>
|
<div class="grid-header" data-index="<?= $mainFieldMapping ? 3 : 2 ?>" style="flex: 0 0 300px; position: relative;">Client<div class="resizer"></div>
|
||||||
</div>
|
</div>
|
||||||
<?php
|
<?php
|
||||||
$headerIndex = $mainFieldMapping ? 4 : 3;
|
$headerIndex = $mainFieldMapping ? 4 : 3;
|
||||||
@ -1151,6 +1164,16 @@ function fixedDefaultValue(array $f): string
|
|||||||
$key = $f['fixed_field_key'];
|
$key = $f['fixed_field_key'];
|
||||||
$label = $slugMapping[$key] ?? $key;
|
$label = $slugMapping[$key] ?? $key;
|
||||||
|
|
||||||
|
if ($key === 'ClienteFornitore') {
|
||||||
|
$label = 'Fornitore';
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($key === 'ClienteAnalisi') {
|
||||||
|
$label = 'Buyer';
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
echo "<div class='grid-header' data-index='$headerIndex' style='flex: 0 0 180px; position: relative;'>"
|
echo "<div class='grid-header' data-index='$headerIndex' style='flex: 0 0 180px; position: relative;'>"
|
||||||
. htmlspecialchars($label) .
|
. htmlspecialchars($label) .
|
||||||
"<div class='resizer'></div></div>";
|
"<div class='resizer'></div></div>";
|
||||||
@ -1178,7 +1201,7 @@ function fixedDefaultValue(array $f): string
|
|||||||
<!-- commented only for admin roles -->
|
<!-- commented only for admin roles -->
|
||||||
<?php if ((Auth::user()->hasRole('Admin'))) : ?>
|
<?php if ((Auth::user()->hasRole('Admin'))) : ?>
|
||||||
<?php $isExported = (($row['status'] ?? '') === 'l'); ?>
|
<?php $isExported = (($row['status'] ?? '') === 'l'); ?>
|
||||||
<button type="button" class="export-lims-btn action-btn" data-row="<?= $index ?>" data-iddatadb="<?= $row['iddatadb'] ?>" title="<?= $isExported ? 'Già esportato' : 'Export to LIMS' ?>" style="background: <?= $isExported ? '#ccc' : '#eb0b0b' ?>; color: white; border: none; border-radius: 5px; cursor: <?= $isExported ? 'not-allowed' : 'pointer' ?>; <?= $isExported ? 'opacity: 0.5;' : '' ?>"<?= $isExported ? ' disabled' : '' ?>><i class="fas fa-upload"></i></button>
|
<button type="button" class="export-lims-btn action-btn" data-row="<?= $index ?>" data-iddatadb="<?= $row['iddatadb'] ?>" title="<?= $isExported ? 'Già esportato' : 'Export to LIMS' ?>" style="background: <?= $isExported ? '#ccc' : '#eb0b0b' ?>; color: white; border: none; border-radius: 5px; cursor: <?= $isExported ? 'not-allowed' : 'pointer' ?>; <?= $isExported ? 'opacity: 0.5;' : '' ?>" <?= $isExported ? ' disabled' : '' ?>><i class="fas fa-upload"></i></button>
|
||||||
<?php endif; ?>
|
<?php endif; ?>
|
||||||
|
|
||||||
<button type="button" class="save-btn action-btn" data-row="<?= $index ?>" title="Save" style="background: #28a745; color: white; border: none; border-radius: 5px; cursor: pointer;"><i class="fas fa-save"></i></button>
|
<button type="button" class="save-btn action-btn" data-row="<?= $index ?>" title="Save" style="background: #28a745; color: white; border: none; border-radius: 5px; cursor: pointer;"><i class="fas fa-save"></i></button>
|
||||||
@ -1211,20 +1234,21 @@ function fixedDefaultValue(array $f): string
|
|||||||
<?php endif; ?>
|
<?php endif; ?>
|
||||||
</div>
|
</div>
|
||||||
<?php endif; ?>
|
<?php endif; ?>
|
||||||
<div class="grid-cell editable-cell" data-col="idclient" data-row="<?= $index ?>" data-index="<?= $mainFieldMapping ? 2 : 1 ?>" style="flex: 0 0 300px;">
|
<div class="grid-cell editable-cell" data-col="status" data-row="<?= $index ?>" data-index="<?= $mainFieldMapping ? 2 : 1 ?>" style="flex: 0 0 150px;">
|
||||||
<select name="rows[<?= $index ?>][idclient]" class="cell-input dropdown-select client-select searchable-client" data-current-value="<?= htmlspecialchars($row['idclient'] ?? $default_idclient) ?>">
|
|
||||||
<option value="">Select a client...</option>
|
|
||||||
</select>
|
|
||||||
</div>
|
|
||||||
<div class="grid-cell editable-cell" data-col="status" data-row="<?= $index ?>" data-index="<?= $mainFieldMapping ? 3 : 2 ?>" style="flex: 0 0 150px;">
|
|
||||||
<span class="status-badge status-<?= htmlspecialchars($row['status'] ?? 'i') ?>">
|
<span class="status-badge status-<?= htmlspecialchars($row['status'] ?? 'i') ?>">
|
||||||
<?= htmlspecialchars($row['status'] === 'i' ? 'Imported' : ($row['status'] === 'P' ? 'In Progress' : 'To LIMS')) ?>
|
<?= htmlspecialchars($row['status'] === 'i' ? 'Imported' : ($row['status'] === 'P' ? 'In Progress' : 'To LIMS')) ?>
|
||||||
</span>
|
</span>
|
||||||
<?php if (!empty($row['commessaweb'])): ?>
|
<?php if (!empty($row['commessaweb'])): ?>
|
||||||
<span class="commessaweb-code" style="display:block; font-size:0.75em; color:#555; margin-top:2px;" title="CommessaWeb"><?= htmlspecialchars($row['commessaweb']) ?></span>
|
<span class="commessaweb-code" style="display:block; font-size:0.75em; color:#555; margin-top:2px;" title="CommessaWeb"><?= htmlspecialchars($row['commessaweb']) ?></span>
|
||||||
<?php endif; ?>
|
<?php endif; ?>
|
||||||
<input type="hidden" name="rows[<?= $index ?>][status]" value="<?= htmlspecialchars($row['status'] ?? 'i') ?>">
|
<input type="hidden" name="rows[<?= $index ?>][status]" value="<?= htmlspecialchars($row['status'] ?? 'i') ?>">
|
||||||
</div>
|
</div>
|
||||||
|
<div class="grid-cell editable-cell" data-col="idclient" data-row="<?= $index ?>" data-index="<?= $mainFieldMapping ? 3 : 2 ?>" style="flex: 0 0 300px;">
|
||||||
|
<select name="rows[<?= $index ?>][idclient]" class="cell-input dropdown-select client-select searchable-client" data-current-value="<?= htmlspecialchars($row['idclient'] ?? $default_idclient) ?>">
|
||||||
|
<option value="">Select a client...</option>
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
|
||||||
<?php
|
<?php
|
||||||
$cellIndex = $mainFieldMapping ? 4 : 3;
|
$cellIndex = $mainFieldMapping ? 4 : 3;
|
||||||
$rowDetails = array_filter($manualDetails, fn($d) => $d['datadb_id'] == $row['iddatadb']);
|
$rowDetails = array_filter($manualDetails, fn($d) => $d['datadb_id'] == $row['iddatadb']);
|
||||||
@ -1351,7 +1375,7 @@ function fixedDefaultValue(array $f): string
|
|||||||
. (((int)$f['is_required'] === 1) ? "required" : "")
|
. (((int)$f['is_required'] === 1) ? "required" : "")
|
||||||
. ">";
|
. ">";
|
||||||
} else {
|
} else {
|
||||||
$isApiField = in_array($key, ['MoltiplicatorePrezzo', 'ClienteResponsabile', 'AnagraficaCertestObject', 'AnagraficaCertestService'], true);
|
$isApiField = in_array($key, ['MoltiplicatorePrezzo', 'ClienteResponsabile', 'ClienteFornitore', 'ClienteAnalisi', 'AnagraficaCertestObject', 'AnagraficaCertestService'], true);
|
||||||
$selectClass = $isApiField ? 'api-fixed-select' : '';
|
$selectClass = $isApiField ? 'api-fixed-select' : '';
|
||||||
echo "<select
|
echo "<select
|
||||||
name='rows[$index][$key]'
|
name='rows[$index][$key]'
|
||||||
@ -1398,6 +1422,7 @@ function fixedDefaultValue(array $f): string
|
|||||||
</div>
|
</div>
|
||||||
</form>
|
</form>
|
||||||
<div id="partsModalContainer"></div>
|
<div id="partsModalContainer"></div>
|
||||||
|
<div id="analysisModalContainer"></div>
|
||||||
<div id="annotationsModalContainer"></div>
|
<div id="annotationsModalContainer"></div>
|
||||||
<?php include 'photos_functions.php'; ?>
|
<?php include 'photos_functions.php'; ?>
|
||||||
</div>
|
</div>
|
||||||
@ -1416,8 +1441,63 @@ function fixedDefaultValue(array $f): string
|
|||||||
<script src="photos.js"></script>
|
<script src="photos.js"></script>
|
||||||
<script src="annotationsModal.js"></script>
|
<script src="annotationsModal.js"></script>
|
||||||
<script src="partsTable.js"></script>
|
<script src="partsTable.js"></script>
|
||||||
|
<script src="analysisModal.js"></script>
|
||||||
<script src="tracking.js"></script>
|
<script src="tracking.js"></script>
|
||||||
<script src="export_to_lims.js"></script>
|
<script src="export_to_lims.js"></script>
|
||||||
|
<script>
|
||||||
|
let globalClientData = [];
|
||||||
|
let globalClientPromise = null;
|
||||||
|
|
||||||
|
function formatClientLabel(client) {
|
||||||
|
const nome = client.Nominativo || "Nome non disponibile";
|
||||||
|
const id = client.IdCliente || "";
|
||||||
|
const codiceCliente = (client.CodiceCliente ?? client.codiceCliente ?? '').toString().trim();
|
||||||
|
const suffix = (codiceCliente.split('_')[1] || '').trim();
|
||||||
|
const shortCode = suffix || (codiceCliente ? codiceCliente.charAt(0) : '--');
|
||||||
|
return `${nome.trim()} - ${shortCode} (ID: ${id})`;
|
||||||
|
}
|
||||||
|
|
||||||
|
async function ensureClientsLoaded(retryCount = 0, maxRetries = 3) {
|
||||||
|
if (globalClientData.length > 0) {
|
||||||
|
return globalClientData;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (globalClientPromise) {
|
||||||
|
return globalClientPromise;
|
||||||
|
}
|
||||||
|
|
||||||
|
globalClientPromise = (async () => {
|
||||||
|
const response = await fetch("get_clienti.php", {
|
||||||
|
method: "GET",
|
||||||
|
headers: {
|
||||||
|
"Content-Type": "application/json"
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
const data = await response.json();
|
||||||
|
|
||||||
|
if (!response.ok) {
|
||||||
|
if (
|
||||||
|
response.status === 500 &&
|
||||||
|
data.error &&
|
||||||
|
data.error.includes('Cannot persist the object') &&
|
||||||
|
retryCount < maxRetries
|
||||||
|
) {
|
||||||
|
await new Promise(resolve => setTimeout(resolve, 1000));
|
||||||
|
globalClientPromise = null;
|
||||||
|
return ensureClientsLoaded(retryCount + 1, maxRetries);
|
||||||
|
}
|
||||||
|
|
||||||
|
throw new Error(data.error || `Errore HTTP: ${response.status}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
globalClientData = data.value || [];
|
||||||
|
return globalClientData;
|
||||||
|
})();
|
||||||
|
|
||||||
|
return globalClientPromise;
|
||||||
|
}
|
||||||
|
</script>
|
||||||
<script>
|
<script>
|
||||||
function expandColumn(cell, expand) {
|
function expandColumn(cell, expand) {
|
||||||
const columnIndex = cell.getAttribute('data-index');
|
const columnIndex = cell.getAttribute('data-index');
|
||||||
@ -1525,6 +1605,14 @@ function fixedDefaultValue(array $f): string
|
|||||||
const rowIndex = btn.dataset.row;
|
const rowIndex = btn.dataset.row;
|
||||||
const row = btn.closest('.grid-row');
|
const row = btn.closest('.grid-row');
|
||||||
const iddatadb = row.getAttribute('data-id');
|
const iddatadb = row.getAttribute('data-id');
|
||||||
|
|
||||||
|
// Show spinner on save button
|
||||||
|
const origHtml = btn.innerHTML;
|
||||||
|
btn.innerHTML = '<i class="fas fa-spinner fa-spin"></i>';
|
||||||
|
btn.disabled = true;
|
||||||
|
row.style.opacity = '0.6';
|
||||||
|
row.style.pointerEvents = 'none';
|
||||||
|
|
||||||
const formData = new FormData();
|
const formData = new FormData();
|
||||||
|
|
||||||
const inputs = row.querySelectorAll(`input[name^="rows[${rowIndex}][details]"], select[name^="rows[${rowIndex}][details]"]`);
|
const inputs = row.querySelectorAll(`input[name^="rows[${rowIndex}][details]"], select[name^="rows[${rowIndex}][details]"]`);
|
||||||
@ -1555,6 +1643,8 @@ function fixedDefaultValue(array $f): string
|
|||||||
// Map: fixed key (logical) -> datadb real column
|
// Map: fixed key (logical) -> datadb real column
|
||||||
const fixedAliasMap = {
|
const fixedAliasMap = {
|
||||||
ClienteResponsabile: 'cliente_responsabile_id',
|
ClienteResponsabile: 'cliente_responsabile_id',
|
||||||
|
ClienteFornitore: 'cliente_fornitore_id',
|
||||||
|
ClienteAnalisi: 'clienteAnalisi',
|
||||||
MoltiplicatorePrezzo: 'moltiplicatore_prezzo_id',
|
MoltiplicatorePrezzo: 'moltiplicatore_prezzo_id',
|
||||||
AnagraficaCertestObject: 'anagrafica_certest_object_id',
|
AnagraficaCertestObject: 'anagrafica_certest_object_id',
|
||||||
AnagraficaCertestService: 'anagrafica_certest_service_id',
|
AnagraficaCertestService: 'anagrafica_certest_service_id',
|
||||||
@ -1608,6 +1698,12 @@ function fixedDefaultValue(array $f): string
|
|||||||
})
|
})
|
||||||
.catch(error => {
|
.catch(error => {
|
||||||
alert('Errore durante il salvataggio: ' + error.message);
|
alert('Errore durante il salvataggio: ' + error.message);
|
||||||
|
})
|
||||||
|
.finally(() => {
|
||||||
|
btn.innerHTML = origHtml;
|
||||||
|
btn.disabled = false;
|
||||||
|
row.style.opacity = '';
|
||||||
|
row.style.pointerEvents = '';
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
@ -1616,7 +1712,9 @@ function fixedDefaultValue(array $f): string
|
|||||||
document.querySelector('.save-all-btn').addEventListener('click', (e) => {
|
document.querySelector('.save-all-btn').addEventListener('click', (e) => {
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
if (saveAllRunning || window.batchRunning) return;
|
if (saveAllRunning || window.batchRunning) return;
|
||||||
const confirmModal = new bootstrap.Modal(document.getElementById('saveAllConfirmModal'), { keyboard: false });
|
const confirmModal = new bootstrap.Modal(document.getElementById('saveAllConfirmModal'), {
|
||||||
|
keyboard: false
|
||||||
|
});
|
||||||
confirmModal.show();
|
confirmModal.show();
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -1634,7 +1732,11 @@ function fixedDefaultValue(array $f): string
|
|||||||
// Disable all row buttons
|
// Disable all row buttons
|
||||||
document.querySelectorAll('.grid-row[data-id]').forEach(r => r.classList.add('batch-disabled'));
|
document.querySelectorAll('.grid-row[data-id]').forEach(r => r.classList.add('batch-disabled'));
|
||||||
const toggle = document.querySelector('.actions-dropdown .dropdown-toggle');
|
const toggle = document.querySelector('.actions-dropdown .dropdown-toggle');
|
||||||
if (toggle) { toggle.disabled = true; toggle.style.opacity = '0.5'; toggle.style.pointerEvents = 'none'; }
|
if (toggle) {
|
||||||
|
toggle.disabled = true;
|
||||||
|
toggle.style.opacity = '0.5';
|
||||||
|
toggle.style.pointerEvents = 'none';
|
||||||
|
}
|
||||||
|
|
||||||
// Show status bar
|
// Show status bar
|
||||||
const bar = document.getElementById('batchExportBar');
|
const bar = document.getElementById('batchExportBar');
|
||||||
@ -1651,10 +1753,14 @@ function fixedDefaultValue(array $f): string
|
|||||||
|
|
||||||
for (const row of rows) {
|
for (const row of rows) {
|
||||||
const saveBtn = row.querySelector('.save-btn');
|
const saveBtn = row.querySelector('.save-btn');
|
||||||
if (!saveBtn) { continue; }
|
if (!saveBtn) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
const rowIndex = saveBtn.dataset.row;
|
const rowIndex = saveBtn.dataset.row;
|
||||||
const iddatadb = row.getAttribute('data-id');
|
const iddatadb = row.getAttribute('data-id');
|
||||||
if (!rowIndex || !iddatadb) { continue; }
|
if (!rowIndex || !iddatadb) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
processed++;
|
processed++;
|
||||||
statusEl.textContent = `Salvataggio ${processed} / ${total} (id: ${iddatadb})...`;
|
statusEl.textContent = `Salvataggio ${processed} / ${total} (id: ${iddatadb})...`;
|
||||||
@ -1664,7 +1770,10 @@ function fixedDefaultValue(array $f): string
|
|||||||
row.classList.add('batch-exporting');
|
row.classList.add('batch-exporting');
|
||||||
const btnCell = row.querySelector('.button-cell');
|
const btnCell = row.querySelector('.button-cell');
|
||||||
if (btnCell) {
|
if (btnCell) {
|
||||||
btnCell.querySelectorAll('.action-btn').forEach(b => { b.dataset.prevDisplay = b.style.display; b.style.display = 'none'; });
|
btnCell.querySelectorAll('.action-btn').forEach(b => {
|
||||||
|
b.dataset.prevDisplay = b.style.display;
|
||||||
|
b.style.display = 'none';
|
||||||
|
});
|
||||||
const spinner = document.createElement('span');
|
const spinner = document.createElement('span');
|
||||||
spinner.className = 'batch-row-spinner';
|
spinner.className = 'batch-row-spinner';
|
||||||
spinner.innerHTML = '<i class="fas fa-spinner fa-spin"></i> Saving...';
|
spinner.innerHTML = '<i class="fas fa-spinner fa-spin"></i> Saving...';
|
||||||
@ -1696,6 +1805,8 @@ function fixedDefaultValue(array $f): string
|
|||||||
const m = inp.name.match(/rows\[\d+\]\[([^\]]+)\]/);
|
const m = inp.name.match(/rows\[\d+\]\[([^\]]+)\]/);
|
||||||
const fixedAliasMap = {
|
const fixedAliasMap = {
|
||||||
ClienteResponsabile: 'cliente_responsabile_id',
|
ClienteResponsabile: 'cliente_responsabile_id',
|
||||||
|
ClienteFornitore: 'cliente_fornitore_id',
|
||||||
|
ClienteAnalisi: 'clienteAnalisi',
|
||||||
MoltiplicatorePrezzo: 'moltiplicatore_prezzo_id',
|
MoltiplicatorePrezzo: 'moltiplicatore_prezzo_id',
|
||||||
AnagraficaCertestObject: 'anagrafica_certest_object_id',
|
AnagraficaCertestObject: 'anagrafica_certest_object_id',
|
||||||
AnagraficaCertestService: 'anagrafica_certest_service_id',
|
AnagraficaCertestService: 'anagrafica_certest_service_id',
|
||||||
@ -1758,7 +1869,10 @@ function fixedDefaultValue(array $f): string
|
|||||||
if (btnCell) {
|
if (btnCell) {
|
||||||
const sp = btnCell.querySelector('.batch-row-spinner');
|
const sp = btnCell.querySelector('.batch-row-spinner');
|
||||||
if (sp) sp.remove();
|
if (sp) sp.remove();
|
||||||
btnCell.querySelectorAll('.action-btn').forEach(b => { b.style.display = b.dataset.prevDisplay || ''; delete b.dataset.prevDisplay; });
|
btnCell.querySelectorAll('.action-btn').forEach(b => {
|
||||||
|
b.style.display = b.dataset.prevDisplay || '';
|
||||||
|
delete b.dataset.prevDisplay;
|
||||||
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1771,7 +1885,11 @@ function fixedDefaultValue(array $f): string
|
|||||||
|
|
||||||
// Re-enable all row buttons
|
// Re-enable all row buttons
|
||||||
document.querySelectorAll('.grid-row[data-id]').forEach(r => r.classList.remove('batch-disabled'));
|
document.querySelectorAll('.grid-row[data-id]').forEach(r => r.classList.remove('batch-disabled'));
|
||||||
if (toggle) { toggle.disabled = false; toggle.style.opacity = ''; toggle.style.pointerEvents = ''; }
|
if (toggle) {
|
||||||
|
toggle.disabled = false;
|
||||||
|
toggle.style.opacity = '';
|
||||||
|
toggle.style.pointerEvents = '';
|
||||||
|
}
|
||||||
|
|
||||||
const resultEl = document.getElementById('saveAllResultMessage');
|
const resultEl = document.getElementById('saveAllResultMessage');
|
||||||
const resultLabel = document.getElementById('saveAllResultModalLabel');
|
const resultLabel = document.getElementById('saveAllResultModalLabel');
|
||||||
@ -1782,7 +1900,9 @@ function fixedDefaultValue(array $f): string
|
|||||||
resultLabel.textContent = 'Salvataggio con Errori';
|
resultLabel.textContent = 'Salvataggio con Errori';
|
||||||
resultEl.innerHTML = `<i class="fas fa-exclamation-triangle" style="color:#ffc107;"></i> Salvate <strong>${successCount}</strong> righe con successo.<br><br><strong>Errori:</strong><br>` + errorMessages.join('<br>');
|
resultEl.innerHTML = `<i class="fas fa-exclamation-triangle" style="color:#ffc107;"></i> Salvate <strong>${successCount}</strong> righe con successo.<br><br><strong>Errori:</strong><br>` + errorMessages.join('<br>');
|
||||||
}
|
}
|
||||||
new bootstrap.Modal(document.getElementById('saveAllResultModal'), { keyboard: false }).show();
|
new bootstrap.Modal(document.getElementById('saveAllResultModal'), {
|
||||||
|
keyboard: false
|
||||||
|
}).show();
|
||||||
});
|
});
|
||||||
|
|
||||||
window.addEventListener("beforeunload", function(e) {
|
window.addEventListener("beforeunload", function(e) {
|
||||||
@ -1814,50 +1934,31 @@ function fixedDefaultValue(array $f): string
|
|||||||
<script>
|
<script>
|
||||||
document.addEventListener("DOMContentLoaded", function() {
|
document.addEventListener("DOMContentLoaded", function() {
|
||||||
const inputs = document.querySelectorAll('.cell-input');
|
const inputs = document.querySelectorAll('.cell-input');
|
||||||
let clientData = []; // Dichiarazione di clientData qui
|
let clientData = globalClientData;
|
||||||
|
|
||||||
// Funzione per caricare i client
|
// Funzione per caricare i client
|
||||||
async function loadClients(retryCount = 0, maxRetries = 3) {
|
async function loadClients() {
|
||||||
const clientLoadingStatus = document.getElementById("clientLoadingStatus");
|
const clientLoadingStatus = document.getElementById("clientLoadingStatus");
|
||||||
try {
|
try {
|
||||||
clientLoadingStatus.style.display = 'inline';
|
clientLoadingStatus.style.display = 'inline';
|
||||||
clientLoadingStatus.textContent = 'Recupero clienti in corso...';
|
clientLoadingStatus.textContent = 'Recupero clienti in corso...';
|
||||||
const response = await fetch("get_clienti.php", {
|
|
||||||
method: "GET",
|
|
||||||
headers: {
|
|
||||||
"Content-Type": "application/json"
|
|
||||||
}
|
|
||||||
});
|
|
||||||
const data = await response.json();
|
|
||||||
if (!response.ok) {
|
|
||||||
if (response.status === 500 && data.error.includes('Cannot persist the object') && retryCount < maxRetries) {
|
|
||||||
console.log(`Tentativo ${retryCount + 1}/${maxRetries}: Riprovo a caricare i clienti...`);
|
|
||||||
await new Promise(resolve => setTimeout(resolve, 1000));
|
|
||||||
return loadClients(retryCount + 1, maxRetries);
|
|
||||||
}
|
|
||||||
throw new Error(data.error || `Errore HTTP: ${response.status}`);
|
|
||||||
}
|
|
||||||
|
|
||||||
clientData = data.value || [];
|
clientData = await ensureClientsLoaded();
|
||||||
|
|
||||||
const select = document.getElementById("clientSelect");
|
const select = document.getElementById("clientSelect");
|
||||||
select.innerHTML = '<option value="">Select a client...</option>';
|
select.innerHTML = '<option value="">Select a client...</option>';
|
||||||
clientData.forEach(client => {
|
|
||||||
const nome = client.Nominativo || "Nome non disponibile";
|
|
||||||
const id = client.IdCliente || "ID non disponibile";
|
|
||||||
const codiceCliente = (client.CodiceCliente || '').toString().trim();
|
|
||||||
const suffix = (codiceCliente.split('_')[1] || '').trim();
|
|
||||||
const shortCode = suffix || (codiceCliente ? codiceCliente.charAt(0) : '--');
|
|
||||||
|
|
||||||
const option = new Option(`${nome.trim()} - ${shortCode} (ID: ${id})`, id);
|
clientData.forEach(client => {
|
||||||
if (parseInt(id) === parseInt(<?php echo json_encode($default_idclient ?? 0); ?>)) {
|
const option = new Option(formatClientLabel(client), client.IdCliente);
|
||||||
|
if (parseInt(client.IdCliente) === parseInt(<?php echo json_encode($default_idclient ?? 0); ?>)) {
|
||||||
option.selected = true;
|
option.selected = true;
|
||||||
}
|
}
|
||||||
select.add(option);
|
select.add(option);
|
||||||
});
|
});
|
||||||
|
|
||||||
populateClientDropdowns();
|
populateClientDropdowns();
|
||||||
|
|
||||||
clientLoadingStatus.textContent = "Clienti caricati.";
|
clientLoadingStatus.textContent = "Clienti caricati.";
|
||||||
// ✅ force refresh of header dependent fixed fields
|
|
||||||
$('#clientSelect').trigger('change');
|
$('#clientSelect').trigger('change');
|
||||||
$('.grid-top .api-fixed-select[data-fixed-key="ClienteResponsabile"]').trigger('fixed:reload');
|
$('.grid-top .api-fixed-select[data-fixed-key="ClienteResponsabile"]').trigger('fixed:reload');
|
||||||
|
|
||||||
@ -2219,7 +2320,7 @@ function fixedDefaultValue(array $f): string
|
|||||||
} else {
|
} else {
|
||||||
// Select nativa
|
// Select nativa
|
||||||
try {
|
try {
|
||||||
$(dropdown).select2('destroy');
|
if ($(dropdown).hasClass('select2-hidden-accessible')) $(dropdown).select2('destroy');
|
||||||
} catch (e) {}
|
} catch (e) {}
|
||||||
dropdown.innerHTML = '<option value="">Seleziona...</option>';
|
dropdown.innerHTML = '<option value="">Seleziona...</option>';
|
||||||
items.forEach(value => {
|
items.forEach(value => {
|
||||||
@ -2245,121 +2346,6 @@ function fixedDefaultValue(array $f): string
|
|||||||
|
|
||||||
populateDropdowns();
|
populateDropdowns();
|
||||||
});
|
});
|
||||||
|
|
||||||
document.addEventListener("DOMContentLoaded", function() {
|
|
||||||
let clientData = [];
|
|
||||||
|
|
||||||
async function loadClients(retryCount = 0, maxRetries = 3) {
|
|
||||||
const clientLoadingStatus = document.getElementById("clientLoadingStatus");
|
|
||||||
try {
|
|
||||||
clientLoadingStatus.style.display = 'inline';
|
|
||||||
clientLoadingStatus.textContent = 'Recupero clienti in corso...';
|
|
||||||
const response = await fetch("get_clienti.php", {
|
|
||||||
method: "GET",
|
|
||||||
headers: {
|
|
||||||
"Content-Type": "application/json"
|
|
||||||
}
|
|
||||||
});
|
|
||||||
const data = await response.json();
|
|
||||||
if (!response.ok) {
|
|
||||||
if (response.status === 500 && data.error.includes('Cannot persist the object') && retryCount < maxRetries) {
|
|
||||||
console.log(`Tentativo ${retryCount + 1}/${maxRetries}: Riprovo a caricare i clienti...`);
|
|
||||||
await new Promise(resolve => setTimeout(resolve, 1000));
|
|
||||||
return loadClients(retryCount + 1, maxRetries);
|
|
||||||
}
|
|
||||||
throw new Error(data.error || `Errore HTTP: ${response.status}`);
|
|
||||||
}
|
|
||||||
|
|
||||||
clientData = data.value || [];
|
|
||||||
const select = document.getElementById("clientSelect");
|
|
||||||
select.innerHTML = '<option value="">Select a client...</option>';
|
|
||||||
clientData.forEach(client => {
|
|
||||||
const nome = client.Nominativo || "Nome non disponibile";
|
|
||||||
const id = client.IdCliente || "ID non disponibile";
|
|
||||||
|
|
||||||
// CodiceCliente es: "Bl01858_E" -> vogliamo "E"
|
|
||||||
const codiceCliente = (client.CodiceCliente ?? client.codiceCliente ?? '').toString().trim();
|
|
||||||
const suffix = (codiceCliente.split('_')[1] || '').trim(); // parte dopo "_"
|
|
||||||
const shortCode = suffix || '--';
|
|
||||||
|
|
||||||
const option = new Option(`${nome.trim()} - ${shortCode} (ID: ${id})`, id);
|
|
||||||
|
|
||||||
if (parseInt(id) === parseInt(<?php echo json_encode($default_idclient ?? 0); ?>)) {
|
|
||||||
option.selected = true;
|
|
||||||
}
|
|
||||||
select.add(option);
|
|
||||||
});
|
|
||||||
|
|
||||||
populateClientDropdowns();
|
|
||||||
clientLoadingStatus.textContent = "Clienti caricati.";
|
|
||||||
} catch (error) {
|
|
||||||
clientLoadingStatus.textContent = "Errore nel caricamento.";
|
|
||||||
Swal.fire({
|
|
||||||
title: "Errore!",
|
|
||||||
text: "Impossibile caricare i clienti: " + error.message,
|
|
||||||
icon: "error",
|
|
||||||
confirmButtonText: "OK"
|
|
||||||
});
|
|
||||||
} finally {
|
|
||||||
setTimeout(() => clientLoadingStatus.style.display = 'none', 2000);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function populateClientDropdowns() {
|
|
||||||
const clientDropdowns = document.querySelectorAll('select[name^="rows"][name$="[idclient]"]');
|
|
||||||
clientDropdowns.forEach(dropdown => {
|
|
||||||
const currentValue = dropdown.getAttribute('data-current-value') || '';
|
|
||||||
dropdown.innerHTML = '<option value="">Select a client...</option>';
|
|
||||||
clientData.forEach(client => {
|
|
||||||
const nome = client.Nominativo || "Nome non disponibile";
|
|
||||||
const id = client.IdCliente || "ID non disponibile";
|
|
||||||
|
|
||||||
// CodiceCliente es: "Bl01858_E" -> vogliamo "E"
|
|
||||||
const codiceCliente = (client.CodiceCliente || '').toString().trim();
|
|
||||||
const suffix = (codiceCliente.split('_')[1] || '').trim(); // parte dopo "_"
|
|
||||||
const shortCode = suffix || '--';
|
|
||||||
|
|
||||||
const option = new Option(`${nome.trim()} - ${shortCode} (ID: ${id})`, id);
|
|
||||||
|
|
||||||
if (String(id) === String(currentValue)) {
|
|
||||||
option.selected = true;
|
|
||||||
}
|
|
||||||
dropdown.add(option);
|
|
||||||
});
|
|
||||||
|
|
||||||
// Ripristina il valore corrente
|
|
||||||
if (currentValue) {
|
|
||||||
dropdown.value = currentValue;
|
|
||||||
dropdown.setAttribute('data-restoring', '');
|
|
||||||
const event = new Event('change', {
|
|
||||||
bubbles: true
|
|
||||||
});
|
|
||||||
dropdown.dispatchEvent(event);
|
|
||||||
dropdown.removeAttribute('data-restoring');
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
loadClients();
|
|
||||||
|
|
||||||
document.getElementById('clientSelect').addEventListener('change', function() {
|
|
||||||
const gridCell = this.closest('.grid-cell');
|
|
||||||
const event = new Event('change', {
|
|
||||||
bubbles: true
|
|
||||||
});
|
|
||||||
gridCell.dispatchEvent(event);
|
|
||||||
});
|
|
||||||
|
|
||||||
document.addEventListener('change', function(e) {
|
|
||||||
if (e.target.matches('select[name^="rows"][name$="[idclient]"]')) {
|
|
||||||
const gridCell = e.target.closest('.grid-cell');
|
|
||||||
const event = new Event('change', {
|
|
||||||
bubbles: true
|
|
||||||
});
|
|
||||||
gridCell.dispatchEvent(event);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
});
|
|
||||||
</script>
|
</script>
|
||||||
<script>
|
<script>
|
||||||
document.addEventListener("DOMContentLoaded", function() {
|
document.addEventListener("DOMContentLoaded", function() {
|
||||||
@ -2381,6 +2367,13 @@ function fixedDefaultValue(array $f): string
|
|||||||
});
|
});
|
||||||
gridCell.dispatchEvent(event);
|
gridCell.dispatchEvent(event);
|
||||||
}
|
}
|
||||||
|
if (e.target.matches('select[name^="rows"][name$="[cliente_fornitore_id]"]')) {
|
||||||
|
const gridCell = e.target.closest('.grid-cell');
|
||||||
|
const event = new Event('change', {
|
||||||
|
bubbles: true
|
||||||
|
});
|
||||||
|
gridCell.dispatchEvent(event);
|
||||||
|
}
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
</script>
|
</script>
|
||||||
@ -2397,11 +2390,12 @@ function fixedDefaultValue(array $f): string
|
|||||||
|
|
||||||
// Ensure Select2 dropdowns trigger change events for unsaved changes tracking
|
// Ensure Select2 dropdowns trigger change events for unsaved changes tracking
|
||||||
$('.searchable-client').on('select2:select select2:clear', function(e) {
|
$('.searchable-client').on('select2:select select2:clear', function(e) {
|
||||||
const gridCell = this.closest('.grid-cell');
|
// Trigger native change on the real <select> element so
|
||||||
const event = new Event('change', {
|
// addEventListener('change') handlers can detect modifications.
|
||||||
|
const nativeEvent = new Event('change', {
|
||||||
bubbles: true
|
bubbles: true
|
||||||
});
|
});
|
||||||
gridCell.dispatchEvent(event);
|
this.dispatchEvent(nativeEvent);
|
||||||
});
|
});
|
||||||
|
|
||||||
// Update propagate functionality for client dropdown
|
// Update propagate functionality for client dropdown
|
||||||
@ -2415,7 +2409,7 @@ function fixedDefaultValue(array $f): string
|
|||||||
const event = new Event('change', {
|
const event = new Event('change', {
|
||||||
bubbles: true
|
bubbles: true
|
||||||
});
|
});
|
||||||
clientSelect.closest('.grid-cell').dispatchEvent(event);
|
clientSelect.dispatchEvent(event);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
@ -2438,10 +2432,7 @@ function fixedDefaultValue(array $f): string
|
|||||||
.map(s => s.trim())
|
.map(s => s.trim())
|
||||||
.filter(s => s.length > 0);
|
.filter(s => s.length > 0);
|
||||||
|
|
||||||
// (opzionale) dedup per evitare doppioni tipo "Cap | Cap"
|
if (!parts.length) {
|
||||||
const uniqueParts = [...new Set(parts)];
|
|
||||||
|
|
||||||
if (!uniqueParts.length) {
|
|
||||||
alert('⚠️ Inserisci prima una descrizione nel campo Tested Component.');
|
alert('⚠️ Inserisci prima una descrizione nel campo Tested Component.');
|
||||||
$input.focus();
|
$input.focus();
|
||||||
return;
|
return;
|
||||||
@ -2452,7 +2443,7 @@ function fixedDefaultValue(array $f): string
|
|||||||
const errors = [];
|
const errors = [];
|
||||||
|
|
||||||
// ✅ esegue una chiamata per ogni parte (compatibile con l’attuale add_part_quick.php)
|
// ✅ esegue una chiamata per ogni parte (compatibile con l’attuale add_part_quick.php)
|
||||||
for (const p of uniqueParts) {
|
for (const p of parts) {
|
||||||
const formData = new FormData();
|
const formData = new FormData();
|
||||||
formData.append('iddatadb', iddatadb);
|
formData.append('iddatadb', iddatadb);
|
||||||
formData.append('part_description', p);
|
formData.append('part_description', p);
|
||||||
@ -2626,6 +2617,12 @@ function fixedDefaultValue(array $f): string
|
|||||||
getParams: (clientId) => ({
|
getParams: (clientId) => ({
|
||||||
id_cliente: clientId
|
id_cliente: clientId
|
||||||
})
|
})
|
||||||
|
},
|
||||||
|
'ClienteFornitore': {
|
||||||
|
source: 'clients'
|
||||||
|
},
|
||||||
|
'ClienteAnalisi': {
|
||||||
|
source: 'clients'
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -2649,6 +2646,21 @@ function fixedDefaultValue(array $f): string
|
|||||||
const config = apiFields[fieldKey];
|
const config = apiFields[fieldKey];
|
||||||
if (!config) return [];
|
if (!config) return [];
|
||||||
|
|
||||||
|
if (config.source === 'clients') {
|
||||||
|
const clients = await ensureClientsLoaded();
|
||||||
|
const results = clients.map(client => ({
|
||||||
|
id: client.IdCliente,
|
||||||
|
text: formatClientLabel(client)
|
||||||
|
}));
|
||||||
|
|
||||||
|
results.sort((a, b) => String(a.text || '').localeCompare(String(b.text || ''), 'it', {
|
||||||
|
sensitivity: 'base'
|
||||||
|
}));
|
||||||
|
|
||||||
|
fixedFieldDataCache[fieldKey] = results;
|
||||||
|
return results;
|
||||||
|
}
|
||||||
|
|
||||||
const cacheKey = fieldKey + (clientId ? '_' + clientId : '');
|
const cacheKey = fieldKey + (clientId ? '_' + clientId : '');
|
||||||
if (fixedFieldDataCache[cacheKey]) {
|
if (fixedFieldDataCache[cacheKey]) {
|
||||||
return fixedFieldDataCache[cacheKey];
|
return fixedFieldDataCache[cacheKey];
|
||||||
@ -2751,7 +2763,8 @@ function fixedDefaultValue(array $f): string
|
|||||||
|
|
||||||
if (results.length > 12) {
|
if (results.length > 12) {
|
||||||
// Select2 con ricerca
|
// Select2 con ricerca
|
||||||
$select.select2('destroy').empty().select2({
|
if ($select.hasClass('select2-hidden-accessible')) $select.select2('destroy');
|
||||||
|
$select.empty().select2({
|
||||||
data: [{
|
data: [{
|
||||||
id: '',
|
id: '',
|
||||||
text: 'Seleziona...'
|
text: 'Seleziona...'
|
||||||
@ -2762,9 +2775,7 @@ function fixedDefaultValue(array $f): string
|
|||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
// Select nativa senza Select2
|
// Select nativa senza Select2
|
||||||
try {
|
if ($select.hasClass('select2-hidden-accessible')) $select.select2('destroy');
|
||||||
$select.select2('destroy');
|
|
||||||
} catch (e) {}
|
|
||||||
$select.empty();
|
$select.empty();
|
||||||
$select.append(new Option('Seleziona...', '', true, false));
|
$select.append(new Option('Seleziona...', '', true, false));
|
||||||
results.forEach(r => {
|
results.forEach(r => {
|
||||||
@ -3073,4 +3084,4 @@ function fixedDefaultValue(array $f): string
|
|||||||
|
|
||||||
</body>
|
</body>
|
||||||
|
|
||||||
</html>
|
</html>
|
||||||
@ -24,7 +24,7 @@ $columns = json_decode(urldecode($_POST['columns'] ?? '[]'), true);
|
|||||||
$rows = json_decode(urldecode($_POST['rows'] ?? '[]'), true);
|
$rows = json_decode(urldecode($_POST['rows'] ?? '[]'), true);
|
||||||
$excelrows = json_decode(urldecode($_POST['excelrows'] ?? '[]'), true);
|
$excelrows = json_decode(urldecode($_POST['excelrows'] ?? '[]'), true);
|
||||||
|
|
||||||
$newFilename = htmlspecialchars($_POST['filename']);
|
$newFilename = $_POST['filename'];
|
||||||
|
|
||||||
$_SESSION['template_id'] = $template_id;
|
$_SESSION['template_id'] = $template_id;
|
||||||
$_SESSION['selected_rows'] = $selected_rows;
|
$_SESSION['selected_rows'] = $selected_rows;
|
||||||
@ -47,7 +47,7 @@ $pdo = $db->getConnection();
|
|||||||
$importReferenceCode = date('YmdHis') . '-' . uniqid();
|
$importReferenceCode = date('YmdHis') . '-' . uniqid();
|
||||||
|
|
||||||
// Recupera tutti i mapping dal template
|
// Recupera tutti i mapping dal template
|
||||||
$stmt = $pdo->prepare("SELECT id, excel_column, data_type, is_required, manual_default, is_manual, field_label, field_id, main_field FROM template_mapping WHERE template_id = ?");
|
$stmt = $pdo->prepare("SELECT id, excel_column, data_type, is_required, manual_default, is_manual, field_label, field_id, main_field, auto_value FROM template_mapping WHERE template_id = ?");
|
||||||
$stmt->execute([$template_id]);
|
$stmt->execute([$template_id]);
|
||||||
$allMappings = $stmt->fetchAll(PDO::FETCH_ASSOC);
|
$allMappings = $stmt->fetchAll(PDO::FETCH_ASSOC);
|
||||||
|
|
||||||
@ -126,7 +126,7 @@ foreach ($selected_rows as $rowIndex) {
|
|||||||
case 'Testo':
|
case 'Testo':
|
||||||
case 'VARCHAR':
|
case 'VARCHAR':
|
||||||
default:
|
default:
|
||||||
$fieldValue = !empty($fieldValue) ? htmlspecialchars((string)$fieldValue) : ($mapping['manual_default'] ?? '');
|
$fieldValue = !empty($fieldValue) ? (string)$fieldValue : ($mapping['manual_default'] ?? '');
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
@ -135,6 +135,15 @@ foreach ($selected_rows as $rowIndex) {
|
|||||||
$fieldValue = date('Y-m-d');
|
$fieldValue = date('Y-m-d');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
// Apply auto_value if field is still empty
|
||||||
|
if (($fieldValue === null || $fieldValue === '') && !empty($mapping['auto_value']) && $mapping['auto_value'] !== 'none') {
|
||||||
|
if ($mapping['auto_value'] === 'import_date') {
|
||||||
|
$fieldValue = date('Y-m-d');
|
||||||
|
} elseif ($mapping['auto_value'] === 'import_time') {
|
||||||
|
$fieldValue = date('H:i');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if ($mapping['is_required'] && (is_null($fieldValue) || $fieldValue === '')) {
|
if ($mapping['is_required'] && (is_null($fieldValue) || $fieldValue === '')) {
|
||||||
error_log("Required field missing for mapping ID: " . $mapping['id'] . ", field: " . $mapping['field_label']);
|
error_log("Required field missing for mapping ID: " . $mapping['id'] . ", field: " . $mapping['field_label']);
|
||||||
}
|
}
|
||||||
@ -147,28 +156,6 @@ foreach ($selected_rows as $rowIndex) {
|
|||||||
|
|
||||||
$_SESSION['inserted_ids'] = $insertedIds;
|
$_SESSION['inserted_ids'] = $insertedIds;
|
||||||
|
|
||||||
$params = [
|
header("Location: imported.php?id=" . urlencode($template_id) . "&importref=" . urlencode($importReferenceCode));
|
||||||
'template_id' => $template_id,
|
|
||||||
'filename' => $newFilename,
|
|
||||||
];
|
|
||||||
|
|
||||||
?>
|
|
||||||
<form id="redirectForm" action="import_edit2.php" method="post">
|
|
||||||
<input type="hidden" name="template_id" value="<?= htmlspecialchars($template_id) ?>">
|
|
||||||
<input type="hidden" name="filename" value="<?= htmlspecialchars($newFilename) ?>">
|
|
||||||
<?php foreach ($selected_rows as $row): ?>
|
|
||||||
<input type="hidden" name="selected_rows[]" value="<?= htmlspecialchars($row) ?>">
|
|
||||||
<?php endforeach; ?>
|
|
||||||
<?php foreach ($insertedIds as $id): ?>
|
|
||||||
<input type="hidden" name="inserted_ids[]" value="<?= htmlspecialchars($id) ?>">
|
|
||||||
<?php endforeach; ?>
|
|
||||||
<input type="hidden" name="columns" value='<?= json_encode($columns) ?>'>
|
|
||||||
<input type="hidden" name="rows" value='<?= json_encode($rows) ?>'>
|
|
||||||
<input type="hidden" name="excelrows" value='<?= json_encode($excelrows) ?>'>
|
|
||||||
</form>
|
|
||||||
<script>
|
|
||||||
document.getElementById('redirectForm').submit();
|
|
||||||
</script>
|
|
||||||
<?php
|
|
||||||
exit;
|
exit;
|
||||||
?>
|
?>
|
||||||
|
|||||||
@ -40,6 +40,22 @@ error_log("Loaded template: " . print_r($template, true));
|
|||||||
<?php include('cssinclude.php'); ?>
|
<?php include('cssinclude.php'); ?>
|
||||||
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/css/bootstrap.min.css" rel="stylesheet">
|
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/css/bootstrap.min.css" rel="stylesheet">
|
||||||
<style>
|
<style>
|
||||||
|
.top-scrollbar {
|
||||||
|
overflow-x: auto;
|
||||||
|
overflow-y: hidden;
|
||||||
|
width: 100%;
|
||||||
|
height: 18px;
|
||||||
|
margin-bottom: 8px;
|
||||||
|
border: 1px solid #dee2e6;
|
||||||
|
border-radius: 0.25rem;
|
||||||
|
background: #f8f9fa;
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.top-scrollbar-inner {
|
||||||
|
height: 1px;
|
||||||
|
}
|
||||||
|
|
||||||
.table-container {
|
.table-container {
|
||||||
overflow-x: auto;
|
overflow-x: auto;
|
||||||
max-width: 100%;
|
max-width: 100%;
|
||||||
@ -143,9 +159,8 @@ error_log("Loaded template: " . print_r($template, true));
|
|||||||
<div class="page-content">
|
<div class="page-content">
|
||||||
<?php include('top_stat_widget.php'); ?>
|
<?php include('top_stat_widget.php'); ?>
|
||||||
<div class="mb-3 text">
|
<div class="mb-3 text">
|
||||||
<a href="historical_trf.php?id=<?= $id ?>&status=i" class="btn btn-warning me-2">Imported (i)</a>
|
<a href="imported.php?id=<?= $id ?>" class="btn btn-warning me-2">Imported (i)</a>
|
||||||
<a href="historical_trf.php?id=<?= $id ?>&status=P" class="btn btn-primary me-2">In Progress (P)</a>
|
<a href="tolims.php?id=<?= $id ?>" class="btn btn-success">To LIMS (l)</a>
|
||||||
<a href="historical_trf.php?id=<?= $id ?>&status=l" class="btn btn-success">To LIMS (l)</a>
|
|
||||||
</div>
|
</div>
|
||||||
<div class="card radius-10">
|
<div class="card radius-10">
|
||||||
<div class="card-header">
|
<div class="card-header">
|
||||||
@ -325,12 +340,19 @@ error_log("Loaded template: " . print_r($template, true));
|
|||||||
<button type="submit" class="btn btn-primary" id="proceedButtonTop" disabled>Prosegui</button>
|
<button type="submit" class="btn btn-primary" id="proceedButtonTop" disabled>Prosegui</button>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="table-container">
|
<div class="top-scrollbar" id="topTableScrollbar">
|
||||||
<table class="table table-striped table-bordered">
|
<div class="top-scrollbar-inner" id="topTableScrollbarInner"></div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="table-container" id="mainTableContainer">
|
||||||
|
<table class="table table-striped table-bordered" id="importPreviewTable">
|
||||||
<thead>
|
<thead>
|
||||||
<tr>
|
<tr>
|
||||||
<th><input type="checkbox" id="selectAll"> Seleziona</th>
|
<th><input type="checkbox" id="selectAll"> Seleziona</th>
|
||||||
${data.columns.map(col => `<th>${col || 'Colonna senza nome'}<div class="resize-handle"></div></th>`).join('')}
|
${data.columns.map(col => {
|
||||||
|
const label = !col ? 'Colonna senza nome' : (col.match(/^__empty_\d+__$/) ? 'Colonna senza nome' : col);
|
||||||
|
return `<th>${label}<div class="resize-handle"></div></th>`;
|
||||||
|
}).join('')}
|
||||||
</tr>
|
</tr>
|
||||||
<tr class="column-filters">
|
<tr class="column-filters">
|
||||||
<th></th>
|
<th></th>
|
||||||
@ -361,6 +383,47 @@ error_log("Loaded template: " . print_r($template, true));
|
|||||||
`;
|
`;
|
||||||
tableContainer.innerHTML = html;
|
tableContainer.innerHTML = html;
|
||||||
|
|
||||||
|
const topTableScrollbar = document.getElementById('topTableScrollbar');
|
||||||
|
const topTableScrollbarInner = document.getElementById('topTableScrollbarInner');
|
||||||
|
const mainTableContainer = document.getElementById('mainTableContainer');
|
||||||
|
const importPreviewTable = document.getElementById('importPreviewTable');
|
||||||
|
|
||||||
|
function updateTopTableScrollbar() {
|
||||||
|
if (!topTableScrollbar || !topTableScrollbarInner || !mainTableContainer || !importPreviewTable) return;
|
||||||
|
|
||||||
|
topTableScrollbarInner.style.width = importPreviewTable.scrollWidth + 'px';
|
||||||
|
|
||||||
|
if (mainTableContainer.scrollWidth > mainTableContainer.clientWidth) {
|
||||||
|
topTableScrollbar.style.display = 'block';
|
||||||
|
} else {
|
||||||
|
topTableScrollbar.style.display = 'none';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let syncingTop = false;
|
||||||
|
let syncingBottom = false;
|
||||||
|
|
||||||
|
if (topTableScrollbar && mainTableContainer) {
|
||||||
|
topTableScrollbar.addEventListener('scroll', function() {
|
||||||
|
if (syncingBottom) return;
|
||||||
|
syncingTop = true;
|
||||||
|
mainTableContainer.scrollLeft = topTableScrollbar.scrollLeft;
|
||||||
|
syncingTop = false;
|
||||||
|
});
|
||||||
|
|
||||||
|
mainTableContainer.addEventListener('scroll', function() {
|
||||||
|
if (syncingTop) return;
|
||||||
|
syncingBottom = true;
|
||||||
|
topTableScrollbar.scrollLeft = mainTableContainer.scrollLeft;
|
||||||
|
syncingBottom = false;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
updateTopTableScrollbar();
|
||||||
|
setTimeout(updateTopTableScrollbar, 100);
|
||||||
|
setTimeout(updateTopTableScrollbar, 300);
|
||||||
|
window.addEventListener('resize', updateTopTableScrollbar);
|
||||||
|
|
||||||
const proceedButtonTop = document.getElementById('proceedButtonTop');
|
const proceedButtonTop = document.getElementById('proceedButtonTop');
|
||||||
const proceedButtonBottom = document.getElementById('proceedButtonBottom');
|
const proceedButtonBottom = document.getElementById('proceedButtonBottom');
|
||||||
const selectAllCheckbox = document.getElementById('selectAll');
|
const selectAllCheckbox = document.getElementById('selectAll');
|
||||||
@ -374,16 +437,32 @@ error_log("Loaded template: " . print_r($template, true));
|
|||||||
}
|
}
|
||||||
|
|
||||||
selectAllCheckbox.addEventListener('change', function() {
|
selectAllCheckbox.addEventListener('change', function() {
|
||||||
checkboxes.forEach(checkbox => {
|
const visibleRows = Array.from(document.querySelectorAll('.table tbody tr'))
|
||||||
checkbox.checked = this.checked;
|
.filter(row => row.style.display !== 'none');
|
||||||
|
|
||||||
|
visibleRows.forEach(row => {
|
||||||
|
const checkbox = row.querySelector('.row-checkbox');
|
||||||
|
if (checkbox) {
|
||||||
|
checkbox.checked = this.checked;
|
||||||
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
updateProceedButton();
|
updateProceedButton();
|
||||||
});
|
});
|
||||||
|
|
||||||
checkboxes.forEach(checkbox => {
|
checkboxes.forEach(checkbox => {
|
||||||
checkbox.addEventListener('change', function() {
|
checkbox.addEventListener('change', function() {
|
||||||
console.log('Checkbox changed, checked:', this.checked, 'excelrow:', this.dataset.excelrow);
|
console.log('Checkbox changed, checked:', this.checked, 'excelrow:', this.dataset.excelrow);
|
||||||
selectAllCheckbox.checked = Array.from(checkboxes).every(cb => cb.checked);
|
|
||||||
|
const visibleCheckboxes = Array.from(document.querySelectorAll('.table tbody tr'))
|
||||||
|
.filter(row => row.style.display !== 'none')
|
||||||
|
.map(row => row.querySelector('.row-checkbox'))
|
||||||
|
.filter(cb => cb !== null);
|
||||||
|
|
||||||
|
selectAllCheckbox.checked =
|
||||||
|
visibleCheckboxes.length > 0 &&
|
||||||
|
visibleCheckboxes.every(cb => cb.checked);
|
||||||
|
|
||||||
updateProceedButton();
|
updateProceedButton();
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
@ -431,12 +510,11 @@ error_log("Loaded template: " . print_r($template, true));
|
|||||||
|
|
||||||
function applyColumnFilters() {
|
function applyColumnFilters() {
|
||||||
rows.forEach(row => {
|
rows.forEach(row => {
|
||||||
// Le celle di data partono da index 1 (perché index 0 è checkbox)
|
|
||||||
let visible = true;
|
let visible = true;
|
||||||
|
|
||||||
for (const [colIndexStr, filterValue] of Object.entries(activeFilters)) {
|
for (const [colIndexStr, filterValue] of Object.entries(activeFilters)) {
|
||||||
const colIndex = parseInt(colIndexStr, 10); // 0..N-1 sulle colonne dati
|
const colIndex = parseInt(colIndexStr, 10);
|
||||||
const cell = row.cells[colIndex + 1]; // +1 per saltare la colonna checkbox
|
const cell = row.cells[colIndex + 1];
|
||||||
|
|
||||||
const cellText = (cell?.textContent || '').toLowerCase();
|
const cellText = (cell?.textContent || '').toLowerCase();
|
||||||
const searchText = (filterValue || '').toLowerCase().trim();
|
const searchText = (filterValue || '').toLowerCase().trim();
|
||||||
@ -449,6 +527,17 @@ error_log("Loaded template: " . print_r($template, true));
|
|||||||
|
|
||||||
row.style.display = visible ? '' : 'none';
|
row.style.display = visible ? '' : 'none';
|
||||||
});
|
});
|
||||||
|
|
||||||
|
const visibleCheckboxes = Array.from(document.querySelectorAll('.table tbody tr'))
|
||||||
|
.filter(row => row.style.display !== 'none')
|
||||||
|
.map(row => row.querySelector('.row-checkbox'))
|
||||||
|
.filter(cb => cb !== null);
|
||||||
|
|
||||||
|
selectAllCheckbox.checked =
|
||||||
|
visibleCheckboxes.length > 0 &&
|
||||||
|
visibleCheckboxes.every(cb => cb.checked);
|
||||||
|
|
||||||
|
updateProceedButton();
|
||||||
}
|
}
|
||||||
|
|
||||||
filterInputs.forEach(input => {
|
filterInputs.forEach(input => {
|
||||||
|
|||||||
1614
public/userarea/imported.php
Normal file
1614
public/userarea/imported.php
Normal file
File diff suppressed because it is too large
Load Diff
@ -10,6 +10,9 @@ error_reporting(E_ALL | E_STRICT);
|
|||||||
include('../../extra/auth.php');
|
include('../../extra/auth.php');
|
||||||
//require_once __DIR__ . '/extra/auth.php';
|
//require_once __DIR__ . '/extra/auth.php';
|
||||||
|
|
||||||
|
// Laravel bootstrap (loaded by auth.php) forces UTC via config/app.php — re-apply our TZ
|
||||||
|
date_default_timezone_set($_ENV['APP_TIMEZONE'] ?? 'Europe/Rome');
|
||||||
|
|
||||||
// Here we just check if user is not
|
// Here we just check if user is not
|
||||||
// logged in, and in that case we redirect
|
// logged in, and in that case we redirect
|
||||||
// the user to vanguard login page.
|
// the user to vanguard login page.
|
||||||
@ -26,6 +29,8 @@ $nameuser = $user->present()->first_name;
|
|||||||
$surnameuser = $user->present()->last_name;
|
$surnameuser = $user->present()->last_name;
|
||||||
$emailuser = $user->present()->email;
|
$emailuser = $user->present()->email;
|
||||||
$avatar = $user->present()->avatar;
|
$avatar = $user->present()->avatar;
|
||||||
|
$lims_user_id = $user->lims_user_id ?? '';
|
||||||
|
$lims_global_user_id = $user->lims_global_user_id ?? '';
|
||||||
|
|
||||||
$kindofrole = $user->present()->role_id;
|
$kindofrole = $user->present()->role_id;
|
||||||
|
|
||||||
|
|||||||
@ -1,6 +1,6 @@
|
|||||||
<?php include('include/headscript.php');
|
<?php include('include/headscript.php');
|
||||||
|
|
||||||
// Recupera tutte le routine dal database
|
// Retrieve all routines from database
|
||||||
$db = DBHandlerSelect::getInstance();
|
$db = DBHandlerSelect::getInstance();
|
||||||
$pdo = $db->getConnection();
|
$pdo = $db->getConnection();
|
||||||
$stmt = $pdo->prepare("SELECT * FROM routine");
|
$stmt = $pdo->prepare("SELECT * FROM routine");
|
||||||
@ -18,26 +18,29 @@ $routines = $stmt->fetchAll(PDO::FETCH_ASSOC);
|
|||||||
<?php include('cssinclude.php'); ?>
|
<?php include('cssinclude.php'); ?>
|
||||||
<script src="https://code.jquery.com/jquery-3.6.0.min.js" integrity="sha256-/xUj+3OJU5yExlq6GSYGSHk7tPXikynS7ogEvDej/m4=" crossorigin="anonymous"></script>
|
<script src="https://code.jquery.com/jquery-3.6.0.min.js" integrity="sha256-/xUj+3OJU5yExlq6GSYGSHk7tPXikynS7ogEvDej/m4=" crossorigin="anonymous"></script>
|
||||||
<script src="https://cdn.jsdelivr.net/npm/select2@4.1.0-rc.0/dist/js/select2.min.js"></script>
|
<script src="https://cdn.jsdelivr.net/npm/select2@4.1.0-rc.0/dist/js/select2.min.js"></script>
|
||||||
<title>Insert XLS Template <?= htmlspecialchars($titlewebsite, ENT_QUOTES, 'UTF-8'); ?></title>
|
<title>Insert Template <?= htmlspecialchars($titlewebsite, ENT_QUOTES, 'UTF-8'); ?></title>
|
||||||
</head>
|
</head>
|
||||||
|
|
||||||
<body>
|
<body>
|
||||||
<div class="wrapper">
|
<div class="wrapper">
|
||||||
<?php include('include/navbar.php'); ?>
|
<?php include('include/navbar.php'); ?>
|
||||||
<?php include('include/topbar.php'); ?>
|
<?php include('include/topbar.php'); ?>
|
||||||
|
|
||||||
<div class="page-wrapper">
|
<div class="page-wrapper">
|
||||||
<div class="page-content">
|
<div class="page-content">
|
||||||
|
|
||||||
<div class="card mb-4">
|
<div class="card mb-4">
|
||||||
<div class="card-header">
|
<div class="card-header">
|
||||||
<h5 class="mb-0">Insert new XLS Template</h5>
|
<h5 class="mb-0">Insert New Template</h5>
|
||||||
</div>
|
</div>
|
||||||
<div class="card-body">
|
<div class="card-body">
|
||||||
<p class="mb-2">Fill the following form in order to create a new import XLS template</p>
|
<p class="mb-2">Fill the following form in order to create a new import template</p>
|
||||||
<p class="mb-2">Mandatory Fields</p>
|
<p class="mb-2">Mandatory Fields</p>
|
||||||
<ul class="mb-0">
|
<ul class="mb-0">
|
||||||
<li>Template Name</li>
|
<li>Template Name</li>
|
||||||
<li>Row Header and Column Header: where the title of the excel starts</li>
|
<li>Source Type</li>
|
||||||
<li>Schema and client</li>
|
<li>Schema and Client</li>
|
||||||
|
<li>Row Header and Column Header only for XLS templates</li>
|
||||||
</ul>
|
</ul>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@ -50,22 +53,33 @@ $routines = $stmt->fetchAll(PDO::FETCH_ASSOC);
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="card-body">
|
<div class="card-body">
|
||||||
<div class="col-12">
|
<div class="col-12">
|
||||||
<form id="insertTemplateForm" method="POST">
|
<form id="insertTemplateForm" method="POST">
|
||||||
|
|
||||||
<div class="mb-3">
|
<div class="mb-3">
|
||||||
<label class="form-label"><?= htmlspecialchars($templatename, ENT_QUOTES, 'UTF-8'); ?> *</label>
|
<label class="form-label"><?= htmlspecialchars($templatename, ENT_QUOTES, 'UTF-8'); ?> *</label>
|
||||||
<input type="text" name="name" class="form-control" required>
|
<input type="text" name="name" class="form-control" required>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="mb-3">
|
<div class="mb-3">
|
||||||
<label class="form-label"><?= htmlspecialchars($rowheader, ENT_QUOTES, 'UTF-8'); ?> *</label>
|
<label class="form-label">Source Type *</label>
|
||||||
<input type="number" name="header_row" class="form-control" required>
|
<select name="source_type" id="sourceType" class="form-control" required>
|
||||||
|
<option value="XLS" selected>XLS</option>
|
||||||
|
<option value="API">API</option>
|
||||||
|
</select>
|
||||||
|
<small class="text-muted">Choose the source used by this template</small>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="mb-3">
|
<div class="mb-3" id="headerRowWrapper">
|
||||||
|
<label class="form-label"><?= htmlspecialchars($rowheader, ENT_QUOTES, 'UTF-8'); ?> *</label>
|
||||||
|
<input type="number" name="header_row" id="headerRow" class="form-control" value="1" required>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="mb-3" id="startColumnWrapper">
|
||||||
<label class="form-label"><?= htmlspecialchars($columnheader, ENT_QUOTES, 'UTF-8'); ?> *</label>
|
<label class="form-label"><?= htmlspecialchars($columnheader, ENT_QUOTES, 'UTF-8'); ?> *</label>
|
||||||
<input type="text" name="start_column" class="form-control" required>
|
<input type="text" name="start_column" id="startColumn" class="form-control" value="A" required>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="mb-3">
|
<div class="mb-3">
|
||||||
@ -86,12 +100,12 @@ $routines = $stmt->fetchAll(PDO::FETCH_ASSOC);
|
|||||||
|
|
||||||
<div class="mb-3">
|
<div class="mb-3">
|
||||||
<label class="form-label">Button Background Color</label>
|
<label class="form-label">Button Background Color</label>
|
||||||
<input type="color" name="button_bg_color" class="form-control" value="#007bff">
|
<input type="color" name="button_bg_color" class="form-control form-control-color" value="#007bff">
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="mb-3">
|
<div class="mb-3">
|
||||||
<label class="form-label">Button Text Color</label>
|
<label class="form-label">Button Text Color</label>
|
||||||
<input type="color" name="button_text_color" class="form-control" value="#ffffff">
|
<input type="color" name="button_text_color" class="form-control form-control-color" value="#ffffff">
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="mb-3">
|
<div class="mb-3">
|
||||||
@ -125,6 +139,7 @@ $routines = $stmt->fetchAll(PDO::FETCH_ASSOC);
|
|||||||
</option>
|
</option>
|
||||||
<?php endforeach; ?>
|
<?php endforeach; ?>
|
||||||
</select>
|
</select>
|
||||||
|
|
||||||
<div id="routineDetails" class="mt-2" style="display: none;">
|
<div id="routineDetails" class="mt-2" style="display: none;">
|
||||||
<h6>Routine Details</h6>
|
<h6>Routine Details</h6>
|
||||||
<p><strong>Name:</strong> <span id="routineName"></span></p>
|
<p><strong>Name:</strong> <span id="routineName"></span></p>
|
||||||
@ -142,8 +157,10 @@ $routines = $stmt->fetchAll(PDO::FETCH_ASSOC);
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="overlay toggle-icon"></div>
|
<div class="overlay toggle-icon"></div>
|
||||||
<a href="javaScript:;" class="back-to-top"><i class='bx bxs-up-arrow-alt'></i></a>
|
<a href="javaScript:;" class="back-to-top"><i class='bx bxs-up-arrow-alt'></i></a>
|
||||||
<?php include('include/footer.php'); ?>
|
<?php include('include/footer.php'); ?>
|
||||||
@ -167,6 +184,12 @@ $routines = $stmt->fetchAll(PDO::FETCH_ASSOC);
|
|||||||
const routineAction2 = document.getElementById("routineAction2");
|
const routineAction2 = document.getElementById("routineAction2");
|
||||||
const routineAction3 = document.getElementById("routineAction3");
|
const routineAction3 = document.getElementById("routineAction3");
|
||||||
|
|
||||||
|
const sourceType = document.getElementById("sourceType");
|
||||||
|
const headerRowWrapper = document.getElementById("headerRowWrapper");
|
||||||
|
const startColumnWrapper = document.getElementById("startColumnWrapper");
|
||||||
|
const headerRow = document.getElementById("headerRow");
|
||||||
|
const startColumn = document.getElementById("startColumn");
|
||||||
|
|
||||||
if (!form || !clientLoadingStatus || !schemaLoadingStatus || !routineSelect || !routineDetails) {
|
if (!form || !clientLoadingStatus || !schemaLoadingStatus || !routineSelect || !routineDetails) {
|
||||||
alert("Errore: Uno o più elementi della pagina non sono stati trovati. Contatta l'amministratore.");
|
alert("Errore: Uno o più elementi della pagina non sono stati trovati. Contatta l'amministratore.");
|
||||||
return;
|
return;
|
||||||
@ -187,30 +210,66 @@ $routines = $stmt->fetchAll(PDO::FETCH_ASSOC);
|
|||||||
allowClear: true
|
allowClear: true
|
||||||
});
|
});
|
||||||
|
|
||||||
|
function updateSourceFields() {
|
||||||
|
const selectedSource = sourceType.value;
|
||||||
|
|
||||||
|
if (selectedSource === 'API') {
|
||||||
|
headerRowWrapper.style.opacity = '0.6';
|
||||||
|
startColumnWrapper.style.opacity = '0.6';
|
||||||
|
|
||||||
|
headerRow.required = false;
|
||||||
|
startColumn.required = false;
|
||||||
|
|
||||||
|
headerRow.disabled = true;
|
||||||
|
startColumn.disabled = true;
|
||||||
|
} else {
|
||||||
|
headerRowWrapper.style.opacity = '1';
|
||||||
|
startColumnWrapper.style.opacity = '1';
|
||||||
|
|
||||||
|
headerRow.required = true;
|
||||||
|
startColumn.required = true;
|
||||||
|
|
||||||
|
headerRow.disabled = false;
|
||||||
|
startColumn.disabled = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
sourceType.addEventListener('change', updateSourceFields);
|
||||||
|
updateSourceFields();
|
||||||
|
|
||||||
async function loadClients() {
|
async function loadClients() {
|
||||||
try {
|
try {
|
||||||
clientLoadingStatus.style.display = 'inline';
|
clientLoadingStatus.style.display = 'inline';
|
||||||
clientLoadingStatus.textContent = 'Recupero clienti in corso...';
|
clientLoadingStatus.textContent = 'Recupero clienti in corso...';
|
||||||
|
|
||||||
const response = await fetch("get_clienti.php", {
|
const response = await fetch("get_clienti.php", {
|
||||||
method: "GET",
|
method: "GET",
|
||||||
headers: {
|
headers: {
|
||||||
"Content-Type": "application/json"
|
"Content-Type": "application/json"
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
const data = await response.json();
|
const data = await response.json();
|
||||||
if (!response.ok) throw new Error(data.error || `Errore HTTP: ${response.status}`);
|
|
||||||
|
if (!response.ok) {
|
||||||
|
throw new Error(data.error || `Errore HTTP: ${response.status}`);
|
||||||
|
}
|
||||||
|
|
||||||
const select = document.getElementById("clientSelect");
|
const select = document.getElementById("clientSelect");
|
||||||
select.innerHTML = '<option value="">Select a client...</option>';
|
select.innerHTML = '<option value="">Select a client...</option>';
|
||||||
|
|
||||||
data.value.forEach(client => {
|
data.value.forEach(client => {
|
||||||
const nome = client.Nominativo || "Nome non disponibile";
|
const nome = client.Nominativo || "Nome non disponibile";
|
||||||
const id = client.IdCliente || "ID non disponibile";
|
const id = client.IdCliente || "ID non disponibile";
|
||||||
const option = new Option(`${nome.trim()} (ID: ${id})`, id);
|
const option = new Option(`${nome.trim()} (ID: ${id})`, id);
|
||||||
select.add(option);
|
select.add(option);
|
||||||
});
|
});
|
||||||
|
|
||||||
$(select).trigger('change');
|
$(select).trigger('change');
|
||||||
clientLoadingStatus.textContent = "Clienti caricati.";
|
clientLoadingStatus.textContent = "Clienti caricati.";
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
clientLoadingStatus.textContent = "Errore nel caricamento.";
|
clientLoadingStatus.textContent = "Errore nel caricamento.";
|
||||||
|
|
||||||
Swal.fire({
|
Swal.fire({
|
||||||
title: "Errore!",
|
title: "Errore!",
|
||||||
text: "Impossibile caricare i clienti: " + error.message,
|
text: "Impossibile caricare i clienti: " + error.message,
|
||||||
@ -226,26 +285,43 @@ $routines = $stmt->fetchAll(PDO::FETCH_ASSOC);
|
|||||||
try {
|
try {
|
||||||
schemaLoadingStatus.style.display = 'inline';
|
schemaLoadingStatus.style.display = 'inline';
|
||||||
schemaLoadingStatus.textContent = 'Caricamento schemi in corso...';
|
schemaLoadingStatus.textContent = 'Caricamento schemi in corso...';
|
||||||
|
|
||||||
const response = await fetch("get_schemi.php", {
|
const response = await fetch("get_schemi.php", {
|
||||||
method: "GET",
|
method: "GET",
|
||||||
headers: {
|
headers: {
|
||||||
"Content-Type": "application/json"
|
"Content-Type": "application/json"
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
const data = await response.json();
|
const data = await response.json();
|
||||||
if (!response.ok) throw new Error(data.error || `Errore HTTP: ${response.status}`);
|
|
||||||
|
if (!response.ok) {
|
||||||
|
throw new Error(data.error || `Errore HTTP: ${response.status}`);
|
||||||
|
}
|
||||||
|
|
||||||
const select = document.getElementById("schemaSelect");
|
const select = document.getElementById("schemaSelect");
|
||||||
select.innerHTML = '<option value="">Select a schema...</option>';
|
select.innerHTML = '<option value="">Select a schema...</option>';
|
||||||
data.value.forEach(schema => {
|
|
||||||
|
const sortedSchemas = [...data.value].sort((a, b) => {
|
||||||
|
const nomeA = (a.Nome || "").trim().toLowerCase();
|
||||||
|
const nomeB = (b.Nome || "").trim().toLowerCase();
|
||||||
|
return nomeA.localeCompare(nomeB, 'it', {
|
||||||
|
sensitivity: 'base'
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
sortedSchemas.forEach(schema => {
|
||||||
const nome = schema.Nome || "Nome non disponibile";
|
const nome = schema.Nome || "Nome non disponibile";
|
||||||
const id = schema.IdSchemaCustomFields || "ID non disponibile";
|
const id = schema.IdSchemaCustomFields || "ID non disponibile";
|
||||||
const option = new Option(`${nome.trim()} (ID: ${id})`, id);
|
const option = new Option(`${nome.trim()} (ID: ${id})`, id);
|
||||||
select.add(option);
|
select.add(option);
|
||||||
});
|
});
|
||||||
|
|
||||||
$(select).trigger('change');
|
$(select).trigger('change');
|
||||||
schemaLoadingStatus.textContent = "Schemi caricati.";
|
schemaLoadingStatus.textContent = "Schemi caricati.";
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
schemaLoadingStatus.textContent = "Errore nel caricamento.";
|
schemaLoadingStatus.textContent = "Errore nel caricamento.";
|
||||||
|
|
||||||
Swal.fire({
|
Swal.fire({
|
||||||
title: "Errore!",
|
title: "Errore!",
|
||||||
text: "Impossibile caricare gli schemi: " + error.message,
|
text: "Impossibile caricare gli schemi: " + error.message,
|
||||||
@ -270,6 +346,7 @@ $routines = $stmt->fetchAll(PDO::FETCH_ASSOC);
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
loadData();
|
loadData();
|
||||||
|
|
||||||
const routines = <?php echo json_encode($routines); ?>;
|
const routines = <?php echo json_encode($routines); ?>;
|
||||||
@ -277,8 +354,10 @@ $routines = $stmt->fetchAll(PDO::FETCH_ASSOC);
|
|||||||
function updateRoutineDetails() {
|
function updateRoutineDetails() {
|
||||||
const selectedId = routineSelect.value;
|
const selectedId = routineSelect.value;
|
||||||
routineDetails.style.display = selectedId ? 'block' : 'none';
|
routineDetails.style.display = selectedId ? 'block' : 'none';
|
||||||
|
|
||||||
if (selectedId) {
|
if (selectedId) {
|
||||||
const routine = routines.find(r => r.idroutine == selectedId);
|
const routine = routines.find(r => r.idroutine == selectedId);
|
||||||
|
|
||||||
if (routine) {
|
if (routine) {
|
||||||
routineName.textContent = routine.name || 'N/A';
|
routineName.textContent = routine.name || 'N/A';
|
||||||
routineDescription.textContent = routine.description || 'N/A';
|
routineDescription.textContent = routine.description || 'N/A';
|
||||||
@ -300,6 +379,7 @@ $routines = $stmt->fetchAll(PDO::FETCH_ASSOC);
|
|||||||
routineAction3.textContent = '';
|
routineAction3.textContent = '';
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
routineSelect.addEventListener('change', updateRoutineDetails);
|
routineSelect.addEventListener('change', updateRoutineDetails);
|
||||||
updateRoutineDetails();
|
updateRoutineDetails();
|
||||||
|
|
||||||
@ -350,6 +430,7 @@ $routines = $stmt->fetchAll(PDO::FETCH_ASSOC);
|
|||||||
const nameMatch = optionText.match(/^(.+?)(?:\s*\(ID:\s*\d+\))?$/);
|
const nameMatch = optionText.match(/^(.+?)(?:\s*\(ID:\s*\d+\))?$/);
|
||||||
schemaName = nameMatch ? nameMatch[1].trim() : optionText;
|
schemaName = nameMatch ? nameMatch[1].trim() : optionText;
|
||||||
}
|
}
|
||||||
|
|
||||||
formData.append("idschema", schemaId);
|
formData.append("idschema", schemaId);
|
||||||
formData.append("schemaname", schemaName);
|
formData.append("schemaname", schemaName);
|
||||||
|
|
||||||
|
|||||||
@ -13,7 +13,10 @@ try {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Recupera solo i template attivi
|
// Recupera solo i template attivi
|
||||||
$stmt = $pdo->query("SELECT id, button_label, button_bg_color, button_text_color, button_size FROM excel_templates WHERE status = 'active'");
|
$stmt = $pdo->query("SELECT id, button_label, button_size, button_bg_color, button_text_color, source_type
|
||||||
|
FROM excel_templates
|
||||||
|
WHERE status = 'active'
|
||||||
|
ORDER BY button_label ASC");
|
||||||
$templates = $stmt->fetchAll(PDO::FETCH_ASSOC);
|
$templates = $stmt->fetchAll(PDO::FETCH_ASSOC);
|
||||||
|
|
||||||
$response["success"] = true;
|
$response["success"] = true;
|
||||||
|
|||||||
@ -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
|
||||||
|
}
|
||||||
|
|
||||||
|
---
|
||||||
1314
public/userarea/logs/api/commessa_564663_get_step10_1773755561.txt
Normal file
1314
public/userarea/logs/api/commessa_564663_get_step10_1773755561.txt
Normal file
File diff suppressed because it is too large
Load Diff
1314
public/userarea/logs/api/commessa_564663_get_step7_1773755556.txt
Normal file
1314
public/userarea/logs/api/commessa_564663_get_step7_1773755556.txt
Normal file
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
|
||||||
|
}
|
||||||
|
|
||||||
|
---
|
||||||
1314
public/userarea/logs/api/commessa_564817_get_step10_1773827657.txt
Normal file
1314
public/userarea/logs/api/commessa_564817_get_step10_1773827657.txt
Normal file
File diff suppressed because it is too large
Load Diff
1314
public/userarea/logs/api/commessa_564817_get_step7_1773827651.txt
Normal file
1314
public/userarea/logs/api/commessa_564817_get_step7_1773827651.txt
Normal file
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
|
||||||
|
}
|
||||||
|
|
||||||
|
---
|
||||||
1314
public/userarea/logs/api/commessa_565085_get_step10_1773911014.txt
Normal file
1314
public/userarea/logs/api/commessa_565085_get_step10_1773911014.txt
Normal file
File diff suppressed because it is too large
Load Diff
1314
public/userarea/logs/api/commessa_565085_get_step7_1773911008.txt
Normal file
1314
public/userarea/logs/api/commessa_565085_get_step7_1773911008.txt
Normal file
File diff suppressed because it is too large
Load Diff
@ -0,0 +1,20 @@
|
|||||||
|
curl --location --request POST 'https://93.43.5.102/limsapi/api/odata/CommessaWeb(565085)/ImportaCommessa' \
|
||||||
|
--header 'Content-Type: application/json' \
|
||||||
|
--header 'Authorization: Bearer ••••••' \
|
||||||
|
--data '{
|
||||||
|
"IdUtente": 285
|
||||||
|
}'
|
||||||
|
|
||||||
|
RESPONSE:
|
||||||
|
{
|
||||||
|
"@odata.context": "https:\/\/bvcpsitaly-elims.com\/limsapi\/api\/odata\/$metadata#CommessaWeb\/$entity",
|
||||||
|
"IdCommessa": 565085,
|
||||||
|
"CodiceCommessa": "26C0089",
|
||||||
|
"RiferimentoCertificato": null,
|
||||||
|
"StatoCommessaWeb": "Elaborata",
|
||||||
|
"DataCreazioneWeb": "2026-03-19T10:03:18.653+01:00",
|
||||||
|
"CodiceCommessaWeb": "26C0089",
|
||||||
|
"DataInviatoWeb": "2026-03-19T10:03:30.603+01:00",
|
||||||
|
"Richiedente": "From TRFSmart Application",
|
||||||
|
"Descrizione": "From TRFSmart Application"
|
||||||
|
}
|
||||||
@ -0,0 +1,48 @@
|
|||||||
|
Photos for CommessaWeb 565085 (iddatadb=1279):
|
||||||
|
Total photos found: 2, campioni: 3
|
||||||
|
|
||||||
|
=== Campione 736899 (main) ===
|
||||||
|
curl --location --request POST 'https://93.43.5.102/limsapi/api/odata/Campione(736899)/UploadCampioneFile' \
|
||||||
|
--header 'Authorization: Bearer ••••••' \
|
||||||
|
--form 'file=@C:\xampp\htdocs\trf_certest\public\photostrf\1279-20260319090037-1e672dd9-5420-4432-b422-02d8d271c178.jpg' \
|
||||||
|
--form 'StampaNelRapporto=true' \
|
||||||
|
--form 'PrimaPagina=true'
|
||||||
|
|
||||||
|
RESPONSE:
|
||||||
|
{
|
||||||
|
"@odata.context": "https:\/\/bvcpsitaly-elims.com\/limsapi\/api\/odata\/$metadata#CampioneFiles\/$entity",
|
||||||
|
"IdCampioneFile": 1786602,
|
||||||
|
"FileName": "1279-20260319090037-1e672dd9-5420-4432-b422-02d8d271c178.jpg",
|
||||||
|
"Web": false,
|
||||||
|
"AllegaAlRapporto": false,
|
||||||
|
"StampaNelRapporto": false,
|
||||||
|
"ReportGermania": false,
|
||||||
|
"PrimaPagina": false,
|
||||||
|
"Duplica": false,
|
||||||
|
"LastUpdate": "2026-03-19T10:03:25.2757049+01:00",
|
||||||
|
"Titolo": null
|
||||||
|
}
|
||||||
|
|
||||||
|
---
|
||||||
|
curl --location --request POST 'https://93.43.5.102/limsapi/api/odata/Campione(736899)/UploadCampioneFile' \
|
||||||
|
--header 'Authorization: Bearer ••••••' \
|
||||||
|
--form 'file=@C:\xampp\htdocs\trf_certest\public\photostrf\1279-20260319090037-104a2ca5-8a4c-4df8-b2ca-a28f518b7b25.jpg' \
|
||||||
|
--form 'StampaNelRapporto=false' \
|
||||||
|
--form 'PrimaPagina=false'
|
||||||
|
|
||||||
|
RESPONSE:
|
||||||
|
{
|
||||||
|
"@odata.context": "https:\/\/bvcpsitaly-elims.com\/limsapi\/api\/odata\/$metadata#CampioneFiles\/$entity",
|
||||||
|
"IdCampioneFile": 1786603,
|
||||||
|
"FileName": "1279-20260319090037-104a2ca5-8a4c-4df8-b2ca-a28f518b7b25.jpg",
|
||||||
|
"Web": false,
|
||||||
|
"AllegaAlRapporto": false,
|
||||||
|
"StampaNelRapporto": false,
|
||||||
|
"ReportGermania": false,
|
||||||
|
"PrimaPagina": false,
|
||||||
|
"Duplica": false,
|
||||||
|
"LastUpdate": "2026-03-19T10:03:26.7566613+01:00",
|
||||||
|
"Titolo": null
|
||||||
|
}
|
||||||
|
|
||||||
|
---
|
||||||
@ -0,0 +1,18 @@
|
|||||||
|
curl --location --request POST 'https://93.43.5.102/limsapi/api/odata/CommessaWeb(565085)/InviaCommessa' \
|
||||||
|
--header 'Content-Type: application/json' \
|
||||||
|
--header 'Authorization: Bearer ••••••' \
|
||||||
|
--data '{}'
|
||||||
|
|
||||||
|
RESPONSE:
|
||||||
|
{
|
||||||
|
"@odata.context": "https:\/\/bvcpsitaly-elims.com\/limsapi\/api\/odata\/$metadata#CommessaWeb\/$entity",
|
||||||
|
"IdCommessa": 565085,
|
||||||
|
"CodiceCommessa": "26C0089",
|
||||||
|
"RiferimentoCertificato": null,
|
||||||
|
"StatoCommessaWeb": "Nuova",
|
||||||
|
"DataCreazioneWeb": "2026-03-19T10:03:18.653+01:00",
|
||||||
|
"CodiceCommessaWeb": "26C0089",
|
||||||
|
"DataInviatoWeb": "2026-03-19T10:03:30.6022436+01:00",
|
||||||
|
"Richiedente": "From TRFSmart Application",
|
||||||
|
"Descrizione": "From TRFSmart Application"
|
||||||
|
}
|
||||||
@ -0,0 +1,226 @@
|
|||||||
|
curl --location --request PATCH 'https://93.43.5.102/limsapi/api/odata/CommessaWeb(565085)' \
|
||||||
|
--header 'Content-Type: application/json' \
|
||||||
|
--header 'Authorization: Bearer ••••••' \
|
||||||
|
--data '{
|
||||||
|
"CommesseCustomFields": [
|
||||||
|
{
|
||||||
|
"IdCommesseCustomFields": 23328248,
|
||||||
|
"Valore": "18/03/2026"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"IdCommesseCustomFields": 23328249,
|
||||||
|
"Valore": "09:00"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"IdCommesseCustomFields": 23328250,
|
||||||
|
"Valore": "9299"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"IdCommesseCustomFields": 23328251,
|
||||||
|
"Valore": "17/03/2026"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"IdCommesseCustomFields": 23328252,
|
||||||
|
"Valore": "/"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"IdCommesseCustomFields": 23328253,
|
||||||
|
"Valore": "5"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"IdCommesseCustomFields": 23328259,
|
||||||
|
"Valore": "673"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"IdCommesseCustomFields": 23328260,
|
||||||
|
"Valore": "674"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"IdCommesseCustomFields": 23328261,
|
||||||
|
"Valore": "669"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"IdCommesseCustomFields": 23328263,
|
||||||
|
"Valore": "1469"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"IdCommesseCustomFields": 23328264,
|
||||||
|
"Valore": "1604"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"IdCommesseCustomFields": 23328265,
|
||||||
|
"Valore": "12875"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"IdCommesseCustomFields": 23328266,
|
||||||
|
"Valore": "8755"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"IdCommesseCustomFields": 23328267,
|
||||||
|
"Valore": ""
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"IdCommesseCustomFields": 23328227,
|
||||||
|
"Valore": "13497"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"IdCommesseCustomFields": 23328228,
|
||||||
|
"Valore": "20262"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"IdCommesseCustomFields": 23328229,
|
||||||
|
"Valore": "ART. PEACH"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"IdCommesseCustomFields": 23328268,
|
||||||
|
"Valore": "1892"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"IdCommesseCustomFields": 23328269,
|
||||||
|
"Valore": ""
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"IdCommesseCustomFields": 23328270,
|
||||||
|
"Valore": ""
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"IdCommesseCustomFields": 23328271,
|
||||||
|
"Valore": "19/03/2026"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"IdCommesseCustomFields": 23328272,
|
||||||
|
"Valore": "18/03/2026"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"IdCommesseCustomFields": 23328273,
|
||||||
|
"Valore": ""
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"IdCommesseCustomFields": 23328274,
|
||||||
|
"Valore": "09:00"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"IdCommesseCustomFields": 23328275,
|
||||||
|
"Valore": "09:03"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"IdCommesseCustomFields": 23328276,
|
||||||
|
"Valore": "09:58"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"IdCommesseCustomFields": 23328277,
|
||||||
|
"Valore": "19/03/2026"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"IdCommesseCustomFields": 23328278,
|
||||||
|
"Valore": ""
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"IdCommesseCustomFields": 23328279,
|
||||||
|
"Valore": ""
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"IdCommesseCustomFields": 23328280,
|
||||||
|
"Valore": ""
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"IdCommesseCustomFields": 23328230,
|
||||||
|
"Valore": "L209B4M00110M7280"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"IdCommesseCustomFields": 23328231,
|
||||||
|
"Valore": "BLACK"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"IdCommesseCustomFields": 23328232,
|
||||||
|
"Valore": "PE007J"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"IdCommesseCustomFields": 23328233,
|
||||||
|
"Valore": "264"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"IdCommesseCustomFields": 23328234,
|
||||||
|
"Valore": "420"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"IdCommesseCustomFields": 23328235,
|
||||||
|
"Valore": "AAA"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"IdCommesseCustomFields": 23328236,
|
||||||
|
"Valore": "3055"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"IdCommesseCustomFields": 23328237,
|
||||||
|
"Valore": "AAA"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"IdCommesseCustomFields": 23328238,
|
||||||
|
"Valore": "AAA"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"IdCommesseCustomFields": 23328239,
|
||||||
|
"Valore": "P"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"IdCommesseCustomFields": 23328240,
|
||||||
|
"Valore": "RM-ACC"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"IdCommesseCustomFields": 23328241,
|
||||||
|
"Valore": "\u00a0CONCERIA M2 SRL"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"IdCommesseCustomFields": 23328242,
|
||||||
|
"Valore": "344"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"IdCommesseCustomFields": 23328243,
|
||||||
|
"Valore": "1208"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"IdCommesseCustomFields": 23328244,
|
||||||
|
"Valore": "AAA"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"IdCommesseCustomFields": 23328245,
|
||||||
|
"Valore": "AAA"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"IdCommesseCustomFields": 23328246,
|
||||||
|
"Valore": "Not provided"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"IdCommesseCustomFields": 23328247,
|
||||||
|
"Valore": "AAA"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"IdCommesseCustomFields": 23328254,
|
||||||
|
"Valore": "aaa"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"IdCommesseCustomFields": 23328255,
|
||||||
|
"Valore": "4678"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"IdCommesseCustomFields": 23328256,
|
||||||
|
"Valore": "Leather&amp;Fur"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"IdCommesseCustomFields": 23328257,
|
||||||
|
"Valore": "278"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"IdCommesseCustomFields": 23328258,
|
||||||
|
"Valore": "2089"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"IdCommesseCustomFields": 23328262,
|
||||||
|
"Valore": "745"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}'
|
||||||
|
|
||||||
|
RESPONSE:
|
||||||
|
null
|
||||||
@ -0,0 +1,28 @@
|
|||||||
|
curl --location --request POST 'https://93.43.5.102/limsapi/api/odata/CommessaWeb' \
|
||||||
|
--header 'Content-Type: application/json' \
|
||||||
|
--header 'Authorization: Bearer ••••••' \
|
||||||
|
--data '{
|
||||||
|
"Cliente": 3378,
|
||||||
|
"SchemaCustomField": 82,
|
||||||
|
"Richiedente": "From TRFSmart Application",
|
||||||
|
"Descrizione": "From TRFSmart Application",
|
||||||
|
"ClienteResponsabile": 7598,
|
||||||
|
"MoltiplicatorePrezzo": 2,
|
||||||
|
"AnagraficaCertestObject": 8964,
|
||||||
|
"AnagraficaCertestService": 9012,
|
||||||
|
"ClienteFornitore": 11208
|
||||||
|
}'
|
||||||
|
|
||||||
|
RESPONSE:
|
||||||
|
{
|
||||||
|
"@odata.context": "https:\/\/bvcpsitaly-elims.com\/limsapi\/api\/odata\/$metadata#CommessaWeb\/$entity",
|
||||||
|
"IdCommessa": 564662,
|
||||||
|
"CodiceCommessa": "26C0058",
|
||||||
|
"RiferimentoCertificato": null,
|
||||||
|
"StatoCommessaWeb": "Nuova",
|
||||||
|
"DataCreazioneWeb": "2026-03-17T14:49:39.4933462+01:00",
|
||||||
|
"CodiceCommessaWeb": "26C0058",
|
||||||
|
"DataInviatoWeb": null,
|
||||||
|
"Richiedente": "From TRFSmart Application",
|
||||||
|
"Descrizione": "From TRFSmart Application"
|
||||||
|
}
|
||||||
@ -0,0 +1,28 @@
|
|||||||
|
curl --location --request POST 'https://93.43.5.102/limsapi/api/odata/CommessaWeb' \
|
||||||
|
--header 'Content-Type: application/json' \
|
||||||
|
--header 'Authorization: Bearer ••••••' \
|
||||||
|
--data '{
|
||||||
|
"Cliente": 3378,
|
||||||
|
"SchemaCustomField": 82,
|
||||||
|
"Richiedente": "From TRFSmart Application",
|
||||||
|
"Descrizione": "From TRFSmart Application",
|
||||||
|
"ClienteResponsabile": 7598,
|
||||||
|
"MoltiplicatorePrezzo": 2,
|
||||||
|
"AnagraficaCertestObject": 8964,
|
||||||
|
"AnagraficaCertestService": 9012,
|
||||||
|
"ClienteFornitore": 11208
|
||||||
|
}'
|
||||||
|
|
||||||
|
RESPONSE:
|
||||||
|
{
|
||||||
|
"@odata.context": "https:\/\/bvcpsitaly-elims.com\/limsapi\/api\/odata\/$metadata#CommessaWeb\/$entity",
|
||||||
|
"IdCommessa": 564663,
|
||||||
|
"CodiceCommessa": "26C0059",
|
||||||
|
"RiferimentoCertificato": null,
|
||||||
|
"StatoCommessaWeb": "Nuova",
|
||||||
|
"DataCreazioneWeb": "2026-03-17T14:52:29.3463415+01:00",
|
||||||
|
"CodiceCommessaWeb": "26C0059",
|
||||||
|
"DataInviatoWeb": null,
|
||||||
|
"Richiedente": "From TRFSmart Application",
|
||||||
|
"Descrizione": "From TRFSmart Application"
|
||||||
|
}
|
||||||
@ -0,0 +1,28 @@
|
|||||||
|
curl --location --request POST 'https://93.43.5.102/limsapi/api/odata/CommessaWeb' \
|
||||||
|
--header 'Content-Type: application/json' \
|
||||||
|
--header 'Authorization: Bearer ••••••' \
|
||||||
|
--data '{
|
||||||
|
"Cliente": 3378,
|
||||||
|
"SchemaCustomField": 82,
|
||||||
|
"Richiedente": "From TRFSmart Application",
|
||||||
|
"Descrizione": "From TRFSmart Application",
|
||||||
|
"ClienteResponsabile": 7586,
|
||||||
|
"MoltiplicatorePrezzo": null,
|
||||||
|
"AnagraficaCertestObject": 8973,
|
||||||
|
"AnagraficaCertestService": 9007,
|
||||||
|
"ClienteFornitore": 11208
|
||||||
|
}'
|
||||||
|
|
||||||
|
RESPONSE:
|
||||||
|
{
|
||||||
|
"@odata.context": "https:\/\/bvcpsitaly-elims.com\/limsapi\/api\/odata\/$metadata#CommessaWeb\/$entity",
|
||||||
|
"IdCommessa": 564817,
|
||||||
|
"CodiceCommessa": "26C0071",
|
||||||
|
"RiferimentoCertificato": null,
|
||||||
|
"StatoCommessaWeb": "Nuova",
|
||||||
|
"DataCreazioneWeb": "2026-03-18T10:54:02.1867227+01:00",
|
||||||
|
"CodiceCommessaWeb": "26C0071",
|
||||||
|
"DataInviatoWeb": null,
|
||||||
|
"Richiedente": "From TRFSmart Application",
|
||||||
|
"Descrizione": "From TRFSmart Application"
|
||||||
|
}
|
||||||
@ -0,0 +1,29 @@
|
|||||||
|
curl --location --request POST 'https://93.43.5.102/limsapi/api/odata/CommessaWeb' \
|
||||||
|
--header 'Content-Type: application/json' \
|
||||||
|
--header 'Authorization: Bearer ••••••' \
|
||||||
|
--data '{
|
||||||
|
"Cliente": 3378,
|
||||||
|
"SchemaCustomField": 82,
|
||||||
|
"Richiedente": "From TRFSmart Application",
|
||||||
|
"Descrizione": "From TRFSmart Application",
|
||||||
|
"ClienteResponsabile": 7586,
|
||||||
|
"MoltiplicatorePrezzo": 2,
|
||||||
|
"AnagraficaCertestObject": 8973,
|
||||||
|
"AnagraficaCertestService": 9007,
|
||||||
|
"ClienteFornitore": 4505,
|
||||||
|
"ClienteAnalisi": null
|
||||||
|
}'
|
||||||
|
|
||||||
|
RESPONSE:
|
||||||
|
{
|
||||||
|
"@odata.context": "https:\/\/bvcpsitaly-elims.com\/limsapi\/api\/odata\/$metadata#CommessaWeb\/$entity",
|
||||||
|
"IdCommessa": 565085,
|
||||||
|
"CodiceCommessa": "26C0089",
|
||||||
|
"RiferimentoCertificato": null,
|
||||||
|
"StatoCommessaWeb": "Nuova",
|
||||||
|
"DataCreazioneWeb": "2026-03-19T10:03:18.6527414+01:00",
|
||||||
|
"CodiceCommessaWeb": "26C0089",
|
||||||
|
"DataInviatoWeb": null,
|
||||||
|
"Richiedente": "From TRFSmart Application",
|
||||||
|
"Descrizione": "From TRFSmart Application"
|
||||||
|
}
|
||||||
File diff suppressed because it is too large
Load Diff
425
public/userarea/modal_analysis.php
Normal file
425
public/userarea/modal_analysis.php
Normal file
@ -0,0 +1,425 @@
|
|||||||
|
<?php
|
||||||
|
include('include/headscript.php');
|
||||||
|
|
||||||
|
$iddatadb = isset($_GET['iddatadb']) ? (int)$_GET['iddatadb'] : 0;
|
||||||
|
|
||||||
|
if ($iddatadb <= 0) {
|
||||||
|
?>
|
||||||
|
<div class="modal fade" id="analysisModal" tabindex="-1" aria-hidden="true">
|
||||||
|
<div class="modal-dialog modal-fullscreen">
|
||||||
|
<div class="modal-content">
|
||||||
|
<div class="modal-header">
|
||||||
|
<h5 class="modal-title">Analysis</h5>
|
||||||
|
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
|
||||||
|
</div>
|
||||||
|
<div class="modal-body">
|
||||||
|
<div class="alert alert-danger mb-0">Invalid iddatadb</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<?php
|
||||||
|
exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
$db = DBHandlerSelect::getInstance();
|
||||||
|
$pdo = $db->getConnection();
|
||||||
|
|
||||||
|
$stmt = $pdo->prepare("
|
||||||
|
SELECT
|
||||||
|
p.id,
|
||||||
|
p.iddatadb,
|
||||||
|
p.part_number,
|
||||||
|
p.part_description,
|
||||||
|
p.material,
|
||||||
|
p.color,
|
||||||
|
p.mix,
|
||||||
|
p.idmatrice,
|
||||||
|
p.note,
|
||||||
|
p.dateexpiry,
|
||||||
|
a.NomeMatriceTraduzione
|
||||||
|
FROM identification_parts p
|
||||||
|
LEFT JOIN auth_matrici a ON a.IdMatrice = p.idmatrice
|
||||||
|
WHERE p.iddatadb = ?
|
||||||
|
ORDER BY
|
||||||
|
CASE WHEN p.part_number IS NULL THEN 999999 ELSE p.part_number END ASC,
|
||||||
|
p.id ASC
|
||||||
|
");
|
||||||
|
$stmt->execute([$iddatadb]);
|
||||||
|
$parts = $stmt->fetchAll(PDO::FETCH_ASSOC);
|
||||||
|
|
||||||
|
$partIds = array_column($parts, 'id');
|
||||||
|
$assignedAnalysesByPart = [];
|
||||||
|
|
||||||
|
if (!empty($partIds)) {
|
||||||
|
$placeholders = implode(',', array_fill(0, count($partIds), '?'));
|
||||||
|
|
||||||
|
$stmtAssigned = $pdo->prepare("
|
||||||
|
SELECT
|
||||||
|
ipa.id,
|
||||||
|
ipa.part_id,
|
||||||
|
ipa.analysis_recordkey,
|
||||||
|
ipa.analysis_name,
|
||||||
|
ipa.analysis_method,
|
||||||
|
ipa.analysis_level,
|
||||||
|
ipa.is_web_selectable,
|
||||||
|
ipa.is_accredited
|
||||||
|
FROM identification_parts_analyses ipa
|
||||||
|
WHERE ipa.part_id IN ($placeholders)
|
||||||
|
ORDER BY ipa.analysis_name ASC, ipa.id ASC
|
||||||
|
");
|
||||||
|
$stmtAssigned->execute($partIds);
|
||||||
|
$assignedRows = $stmtAssigned->fetchAll(PDO::FETCH_ASSOC);
|
||||||
|
|
||||||
|
foreach ($assignedRows as $row) {
|
||||||
|
$partId = (int)$row['part_id'];
|
||||||
|
if (!isset($assignedAnalysesByPart[$partId])) {
|
||||||
|
$assignedAnalysesByPart[$partId] = [];
|
||||||
|
}
|
||||||
|
$assignedAnalysesByPart[$partId][] = $row;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Build matrix groups from parts.
|
||||||
|
* No join for now: we use idmatrice only.
|
||||||
|
*/
|
||||||
|
$matrixGroups = [];
|
||||||
|
foreach ($parts as $part) {
|
||||||
|
$matrixKey = (!empty($part['idmatrice'])) ? (string)$part['idmatrice'] : 'NO_MATRIX';
|
||||||
|
|
||||||
|
if (!isset($matrixGroups[$matrixKey])) {
|
||||||
|
$matrixGroups[$matrixKey] = [
|
||||||
|
'idmatrice' => $part['idmatrice'],
|
||||||
|
'NomeMatriceTraduzione' => $part['NomeMatriceTraduzione'] ?? '',
|
||||||
|
'parts_count' => 0,
|
||||||
|
'parts' => [],
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
$matrixGroups[$matrixKey]['parts_count']++;
|
||||||
|
$matrixGroups[$matrixKey]['parts'][] = $part['id'];
|
||||||
|
}
|
||||||
|
|
||||||
|
$matrixGroups = array_values($matrixGroups);
|
||||||
|
?>
|
||||||
|
|
||||||
|
<div class="modal fade" id="analysisModal" tabindex="-1" aria-labelledby="analysisModalLabel" aria-hidden="true">
|
||||||
|
<div class="modal-dialog" style="max-width: 96vw; width: 96vw; margin: 1.5vh auto;">
|
||||||
|
<div class="modal-content analysis-modal-content">
|
||||||
|
<div class="modal-header">
|
||||||
|
<div>
|
||||||
|
<h5 class="modal-title mb-0" id="analysisModalLabel">Analysis - TRF <?= htmlspecialchars((string)$iddatadb, ENT_QUOTES, 'UTF-8') ?></h5>
|
||||||
|
<small class="text-muted">
|
||||||
|
Parts: <?= count($parts) ?> |
|
||||||
|
Matrices: <?= count($matrixGroups) ?>
|
||||||
|
</small>
|
||||||
|
</div>
|
||||||
|
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="modal-body analysis-modal-body">
|
||||||
|
<div class="row g-3 h-100">
|
||||||
|
<!-- MATRICES -->
|
||||||
|
<div class="col-lg-3 col-md-4">
|
||||||
|
<div class="card h-100 shadow-sm">
|
||||||
|
<div class="card-header py-2 d-flex justify-content-between align-items-center">
|
||||||
|
<strong>Matrices</strong>
|
||||||
|
<span class="badge bg-secondary"><?= count($matrixGroups) ?></span>
|
||||||
|
</div>
|
||||||
|
<div class="card-body p-2 analysis-scroll-area">
|
||||||
|
<div class="list-group analysis-matrix-list" id="analysisMatrixList">
|
||||||
|
<?php if (empty($matrixGroups)): ?>
|
||||||
|
<div class="text-muted small p-2">No matrices found</div>
|
||||||
|
<?php else: ?>
|
||||||
|
<?php foreach ($matrixGroups as $index => $group): ?>
|
||||||
|
<button type="button"
|
||||||
|
class="list-group-item list-group-item-action analysis-matrix-item <?= $index === 0 ? 'active' : '' ?>"
|
||||||
|
data-matrix-id="<?= htmlspecialchars((string)($group['idmatrice'] ?? 'NO_MATRIX'), ENT_QUOTES, 'UTF-8') ?>">
|
||||||
|
<div class="fw-semibold">
|
||||||
|
<?= htmlspecialchars(
|
||||||
|
!empty($group['NomeMatriceTraduzione'])
|
||||||
|
? $group['NomeMatriceTraduzione']
|
||||||
|
: (!empty($group['idmatrice']) ? ('Matrix without translation') : 'No Matrix'),
|
||||||
|
ENT_QUOTES,
|
||||||
|
'UTF-8'
|
||||||
|
) ?>
|
||||||
|
</div>
|
||||||
|
<div class="small text-muted">
|
||||||
|
ID: <?= !empty($group['idmatrice']) ? (int)$group['idmatrice'] : '-' ?>
|
||||||
|
</div>
|
||||||
|
<div class="small text-muted">
|
||||||
|
Parts linked: <?= (int)$group['parts_count'] ?>
|
||||||
|
</div>
|
||||||
|
</button>
|
||||||
|
<?php endforeach; ?>
|
||||||
|
<?php endif; ?>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- PARTS -->
|
||||||
|
<div class="col-lg-4 col-md-8">
|
||||||
|
<div class="card h-100 shadow-sm">
|
||||||
|
<div class="card-header py-2 d-flex justify-content-between align-items-center">
|
||||||
|
<strong>Parts</strong>
|
||||||
|
<div class="d-flex align-items-center gap-2">
|
||||||
|
<button type="button" class="btn btn-outline-secondary btn-sm" id="analysisClearSelectionBtn">Clear</button>
|
||||||
|
<span class="badge bg-primary" id="analysisSelectedPartsCount">0 selected</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="card-body p-2 analysis-scroll-area">
|
||||||
|
<div class="list-group analysis-parts-list" id="analysisPartsList">
|
||||||
|
<?php if (empty($parts)): ?>
|
||||||
|
<div class="text-muted small p-2">No parts found</div>
|
||||||
|
<?php else: ?>
|
||||||
|
<?php foreach ($parts as $part): ?>
|
||||||
|
<?php
|
||||||
|
$matrixId = !empty($part['idmatrice']) ? (string)$part['idmatrice'] : 'NO_MATRIX';
|
||||||
|
$partLabel = trim(($part['part_number'] !== null ? ('Part ' . $part['part_number']) : 'Part') . ' - ' . ($part['part_description'] ?? ''));
|
||||||
|
?>
|
||||||
|
<div class="list-group-item analysis-part-item"
|
||||||
|
data-part-id="<?= (int)$part['id'] ?>"
|
||||||
|
data-matrix-id="<?= htmlspecialchars($matrixId, ENT_QUOTES, 'UTF-8') ?>">
|
||||||
|
<div class="d-flex align-items-start gap-2">
|
||||||
|
<input type="checkbox"
|
||||||
|
class="form-check-input mt-1 analysis-part-checkbox"
|
||||||
|
value="<?= (int)$part['id'] ?>">
|
||||||
|
<div class="flex-grow-1">
|
||||||
|
<div class="fw-semibold">
|
||||||
|
<?= htmlspecialchars($partLabel, ENT_QUOTES, 'UTF-8') ?>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="small text-muted mt-1">
|
||||||
|
Matrix:
|
||||||
|
<strong>
|
||||||
|
<?= htmlspecialchars(
|
||||||
|
!empty($part['NomeMatriceTraduzione'])
|
||||||
|
? $part['NomeMatriceTraduzione']
|
||||||
|
: (!empty($part['idmatrice']) ? 'Matrix without translation' : 'No Matrix'),
|
||||||
|
ENT_QUOTES,
|
||||||
|
'UTF-8'
|
||||||
|
) ?>
|
||||||
|
</strong>
|
||||||
|
<?php if (!empty($part['idmatrice'])): ?>
|
||||||
|
<span class="small text-muted">(ID: <?= (int)$part['idmatrice'] ?>)</span>
|
||||||
|
<?php endif; ?>
|
||||||
|
<?php if (!empty($part['mix']) && strtoupper($part['mix']) === 'Y'): ?>
|
||||||
|
| <span class="badge bg-warning text-dark">Mix</span>
|
||||||
|
<?php endif; ?>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<?php if (!empty($part['material']) || !empty($part['color'])): ?>
|
||||||
|
<div class="small text-muted">
|
||||||
|
<?php if (!empty($part['material'])): ?>
|
||||||
|
Material: <?= htmlspecialchars($part['material'], ENT_QUOTES, 'UTF-8') ?>
|
||||||
|
<?php endif; ?>
|
||||||
|
<?php if (!empty($part['material']) && !empty($part['color'])): ?>
|
||||||
|
|
|
||||||
|
<?php endif; ?>
|
||||||
|
<?php if (!empty($part['color'])): ?>
|
||||||
|
Color: <?= htmlspecialchars($part['color'], ENT_QUOTES, 'UTF-8') ?>
|
||||||
|
<?php endif; ?>
|
||||||
|
</div>
|
||||||
|
<?php endif; ?>
|
||||||
|
|
||||||
|
<?php if (!empty($part['note'])): ?>
|
||||||
|
<div class="small text-muted mt-1">
|
||||||
|
Note: <?= htmlspecialchars($part['note'], ENT_QUOTES, 'UTF-8') ?>
|
||||||
|
</div>
|
||||||
|
<?php endif; ?>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
<div class="analysis-part-assigned-list mt-2" id="analysisAssignedListPart<?= (int)$part['id'] ?>"></div>
|
||||||
|
</div>
|
||||||
|
<?php endforeach; ?>
|
||||||
|
<?php endif; ?>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- ANALYSES PLACEHOLDER -->
|
||||||
|
<div class="col-lg-5">
|
||||||
|
<div class="card h-100 shadow-sm">
|
||||||
|
<div class="card-header py-2">
|
||||||
|
<strong>Analyses</strong>
|
||||||
|
</div>
|
||||||
|
<div class="card-body analysis-scroll-area" id="analysisRightPanel">
|
||||||
|
<div class="mt-1 mb-3">
|
||||||
|
<div class="small text-muted">Selected matrix:</div>
|
||||||
|
<div id="analysisCurrentMatrix" class="fw-semibold">-</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="mb-3">
|
||||||
|
<div class="small text-muted">Selected parts IDs:</div>
|
||||||
|
<div id="analysisSelectedPartsIds" class="fw-semibold">-</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="d-flex flex-wrap align-items-center gap-2 mb-3">
|
||||||
|
<div class="form-check m-0">
|
||||||
|
<input class="form-check-input" type="checkbox" id="analysisWebOnly">
|
||||||
|
<label class="form-check-label small" for="analysisWebOnly">
|
||||||
|
Web only
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="flex-grow-1" style="min-width: 220px;">
|
||||||
|
<input type="text" class="form-control form-control-sm" id="analysisSearchInput" placeholder="Search analyses or method">
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div id="analysisLoadingBox" class="alert alert-info mb-3 d-none">
|
||||||
|
Loading analyses...
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div id="analysisErrorBox" class="alert alert-danger mb-3 d-none"></div>
|
||||||
|
|
||||||
|
<div id="analysisEmptyBox" class="alert alert-secondary mb-3">
|
||||||
|
Select a matrix to load analyses
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="list-group" id="analysisList"></div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<input type="hidden" id="analysisModalIddatadb" value="<?= (int)$iddatadb ?>">
|
||||||
|
|
||||||
|
<script type="application/json" id="analysisAssignedInitialData">
|
||||||
|
<?= json_encode($assignedAnalysesByPart, JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES) ?>
|
||||||
|
</script>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="modal-footer">
|
||||||
|
<button type="button" class="btn btn-secondary btn-sm" data-bs-dismiss="modal">Close</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<style>
|
||||||
|
#analysisModal {
|
||||||
|
z-index: 1080 !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
#analysisModal .modal-content {
|
||||||
|
width: 100% !important;
|
||||||
|
max-width: 100% !important;
|
||||||
|
min-height: 95vh;
|
||||||
|
border-radius: 14px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.analysis-modal-content {
|
||||||
|
overflow: hidden;
|
||||||
|
}
|
||||||
|
|
||||||
|
.analysis-modal-body {
|
||||||
|
height: calc(95vh - 120px);
|
||||||
|
}
|
||||||
|
|
||||||
|
.analysis-scroll-area {
|
||||||
|
overflow-y: auto;
|
||||||
|
max-height: calc(95vh - 180px);
|
||||||
|
}
|
||||||
|
|
||||||
|
.analysis-matrix-item {
|
||||||
|
text-align: left;
|
||||||
|
border-radius: 8px !important;
|
||||||
|
margin-bottom: 8px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.analysis-part-item {
|
||||||
|
border-radius: 8px !important;
|
||||||
|
margin-bottom: 8px;
|
||||||
|
transition: all 0.2s ease-in-out;
|
||||||
|
}
|
||||||
|
|
||||||
|
.analysis-part-item.matrix-active {
|
||||||
|
background-color: #e8f4ff !important;
|
||||||
|
border-color: #86b7fe !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.analysis-part-item.part-checked {
|
||||||
|
background-color: #eaf7ea !important;
|
||||||
|
border-color: #7ac77a !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
#analysisSelectedPartsIds {
|
||||||
|
word-break: break-word;
|
||||||
|
}
|
||||||
|
|
||||||
|
.analysis-analysis-item {
|
||||||
|
border-radius: 8px !important;
|
||||||
|
margin-bottom: 6px;
|
||||||
|
background-color: #ffffff;
|
||||||
|
}
|
||||||
|
|
||||||
|
#analysisList .analysis-analysis-item:nth-child(even) {
|
||||||
|
background-color: #eef4ff;
|
||||||
|
}
|
||||||
|
|
||||||
|
.analysis-analysis-meta {
|
||||||
|
font-size: 0.84rem;
|
||||||
|
color: #6c757d;
|
||||||
|
}
|
||||||
|
|
||||||
|
.analysis-analysis-badges .badge {
|
||||||
|
margin-right: 4px;
|
||||||
|
margin-bottom: 4px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.analysis-assigned-chip {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: space-between;
|
||||||
|
gap: 8px;
|
||||||
|
padding: 4px 8px;
|
||||||
|
border: 1px solid #dbe7f3;
|
||||||
|
background: #f7fbff;
|
||||||
|
border-radius: 8px;
|
||||||
|
margin-bottom: 6px;
|
||||||
|
font-size: 0.84rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.analysis-assigned-chip-text {
|
||||||
|
min-width: 0;
|
||||||
|
flex: 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
.analysis-assigned-chip-title {
|
||||||
|
font-weight: 600;
|
||||||
|
line-height: 1.2;
|
||||||
|
}
|
||||||
|
|
||||||
|
.analysis-assigned-chip-method {
|
||||||
|
color: #6c757d;
|
||||||
|
font-size: 0.78rem;
|
||||||
|
line-height: 1.2;
|
||||||
|
margin-top: 2px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.analysis-remove-btn {
|
||||||
|
border: 0;
|
||||||
|
background: transparent;
|
||||||
|
color: #dc3545;
|
||||||
|
font-weight: 700;
|
||||||
|
font-size: 1rem;
|
||||||
|
line-height: 1;
|
||||||
|
padding: 0 2px;
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
|
||||||
|
.analysis-analysis-item.analysis-selected {
|
||||||
|
border-color: #198754 !important;
|
||||||
|
background-color: #eaf7ea !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.analysis-part-assigned-list {
|
||||||
|
border-top: 1px dashed #dee2e6;
|
||||||
|
padding-top: 8px;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
@ -307,4 +307,23 @@
|
|||||||
width: 250px !important;
|
width: 250px !important;
|
||||||
min-width: 250px !important;
|
min-width: 250px !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#partsTableBody .extra-field-td .select2-container {
|
||||||
|
width: 100% !important;
|
||||||
|
min-width: 180px !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
#partsTableBody .extra-field-td .select2-selection--single {
|
||||||
|
height: 31px !important;
|
||||||
|
padding: 0.15rem 0.35rem !important;
|
||||||
|
border: 1px solid #ced4da !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
#partsTableBody .extra-field-td .select2-selection__rendered {
|
||||||
|
line-height: 28px !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
#partsTableBody .extra-field-td .select2-selection__arrow {
|
||||||
|
height: 29px !important;
|
||||||
|
}
|
||||||
</style>
|
</style>
|
||||||
@ -9,13 +9,30 @@
|
|||||||
<div class="row parts-row">
|
<div class="row parts-row">
|
||||||
<div class="col-md-9">
|
<div class="col-md-9">
|
||||||
<!-- Prima riga: Elenco Parti, Rinumera, Voce -->
|
<!-- Prima riga: Elenco Parti, Rinumera, Voce -->
|
||||||
<div style="display: flex; justify-content: space-between; align-items: center; margin-bottom: 10px;">
|
<div style="display: flex; justify-content: space-between; align-items: center; margin-bottom: 10px; min-width: 0;">
|
||||||
<h6 style="margin: 0;">Elenco Parti</h6>
|
<h6 style="margin: 0; white-space: nowrap;">Elenco Parti</h6>
|
||||||
<div style="display: flex; align-items: center;">
|
<div style="display: flex; align-items: center; min-width: 0; gap: 8px;">
|
||||||
<button type="button" class="btn btn-info btn-sm" id="renumberPartsBtn" style="padding: 0.1rem 0.5rem; font-size: 0.8rem;">Rinumera Parti</button>
|
<button type="button"
|
||||||
<button type="button" class="btn btn-secondary btn-sm ms-2" id="toggleVoiceBtn" style="padding: 0.1rem 0.5rem; font-size: 0.8rem;"><i class="fas fa-microphone"></i> Voce</button>
|
class="btn btn-dark btn-sm open-analysis-modal-btn"
|
||||||
<button type="button" class="btn btn-info btn-sm ms-2 d-none" id="quotationeBtn" style="padding: 0.1rem 0.5rem; font-size: 0.8rem;">Add Quotation</button>
|
id="openAnalysisModalBtn"
|
||||||
<button type="button" class="btn btn-primary btn-sm ms-2" id="showHideImageBtn" style="padding: 0.1rem 0.5rem; font-size: 0.8rem;">
|
style="padding: 0.1rem 0.5rem; font-size: 0.8rem;">
|
||||||
|
<i class="fas fa-flask"></i> Analysis
|
||||||
|
</button>
|
||||||
|
|
||||||
|
|
||||||
|
<input type="checkbox" id="showMixParts" name="showMixParts" style="margin-right: 5px; margin-left: 10px;">
|
||||||
|
<label for="showMixParts" style="font-size: 0.9rem; margin-right: 10px;">Mix</label>
|
||||||
|
|
||||||
|
<button type="button" class="btn btn-info btn-sm" id="renumberPartsBtn" style="padding: 0.1rem 0.5rem; font-size: 0.8rem;">
|
||||||
|
Rinumera Parti
|
||||||
|
</button>
|
||||||
|
<button type="button" class="btn btn-primary btn-sm" id="clonePartsBtn" style="padding: 0.1rem 0.5rem; font-size: 0.8rem;">
|
||||||
|
<i class="fas fa-clone"></i> Clona Parti
|
||||||
|
</button>
|
||||||
|
<button type="button" class="btn btn-secondary btn-sm ms-2" id="toggleVoiceBtn" style="padding: 0.1rem 0.5rem; font-size: 0.8rem;">
|
||||||
|
<i class="fas fa-microphone"></i> Voce
|
||||||
|
</button>
|
||||||
|
<button type="button" class="btn btn-outline-primary btn-sm ms-2" id="showHideImageBtn" style="padding: 0.1rem 0.5rem; font-size: 0.8rem;">
|
||||||
<i class="fas fa-eye-slash" style="font-size: 0.8rem;"></i>
|
<i class="fas fa-eye-slash" style="font-size: 0.8rem;"></i>
|
||||||
<i class="fas fa-image ms-1" style="font-size: 0.8rem;"></i>
|
<i class="fas fa-image ms-1" style="font-size: 0.8rem;"></i>
|
||||||
</button>
|
</button>
|
||||||
@ -23,53 +40,62 @@
|
|||||||
</div>
|
</div>
|
||||||
<!-- Seconda riga: +, M, MacroMatrice, Matrice globale, Propaga -->
|
<!-- Seconda riga: +, M, MacroMatrice, Matrice globale, Propaga -->
|
||||||
<div style="display: flex; align-items: center; margin-bottom: 10px;">
|
<div style="display: flex; align-items: center; margin-bottom: 10px;">
|
||||||
<button type="button" class="btn btn-success btn-sm add-row-global" style="padding: 0.1rem 0.3rem; font-size: 0.8rem; margin-right: 5px;"><i class="fas fa-plus fa-xs"></i></button>
|
<button type="button" class="btn btn-success btn-sm add-row-global" style="padding: 0.1rem 0.3rem; font-size: 0.8rem; margin-right: 5px;"><i class="fas fa-plus"></i></button>
|
||||||
<button type="button" class="btn btn-primary btn-sm add-mix-global" style="padding: 0.1rem 0.3rem; font-size: 0.8rem; margin-right: 10px;">M</button>
|
<button type="button" class="btn btn-primary btn-sm add-mix-global" style="padding: 0.1rem 0.3rem; font-size: 0.8rem; margin-right: 10px;">M</button>
|
||||||
<select id="macro-matrice-filter" class="form-control form-control-sm ms-2" style="width: 200px !important; min-width: 200px !important; margin-right: 10px;">
|
<select id="macro-matrice-filter" class="form-control form-control-sm ms-2" style="width: 200px !important; min-width: 200px !important; margin-right: 10px;">
|
||||||
<option value="">Tutte le MacroMatrici</option>
|
<option value="">Tutte le MacroMatrici</option>
|
||||||
</select>
|
</select>
|
||||||
<select id="global-matrice" class="form-control form-control-sm" style="width: 350px !important; margin-right: 10px;"></select>
|
<select id="global-matrice" class="form-control form-control-sm" style="width: 350px !important; margin-right: 10px;"></select>
|
||||||
<button type="button" class="btn btn-primary btn-sm propagate-all-btn" style="padding: 0.1rem 0.5rem; font-size: 0.8rem;"><i class="fas fa-arrow-right fa-xs"></i> Propaga a tutte</button>
|
<button type="button" class="btn btn-primary btn-sm propagate-all-btn" style="padding: 0.1rem 0.5rem; font-size: 0.8rem;"><i class="fas fa-arrow-right"></i> Propaga a tutte</button>
|
||||||
</div>
|
</div>
|
||||||
<table class="table table-striped table-sm" id="partsTable">
|
<div class="parts-table-scroll">
|
||||||
<thead>
|
<table class="table table-striped table-sm" id="partsTable">
|
||||||
<tr>
|
<colgroup id="partsTableColgroup">
|
||||||
<th style="width: 55px;">Num</th>
|
<col class="parts-col-num">
|
||||||
<th>Descrizione</th>
|
<col class="parts-col-description">
|
||||||
<th style="width: 200px;">Matrice</th>
|
<col class="parts-col-matrice">
|
||||||
<th style="width: 150px;">
|
<col class="parts-col-date">
|
||||||
<input type="date" class="form-control form-control-sm propagate-date-input" style="width: 130px; margin-left: 5px; display: inline-block;" title="Propaga data a tutte le parti">
|
<col class="parts-col-actions">
|
||||||
</th>
|
</colgroup>
|
||||||
<th style="width: 200px;">
|
<thead>
|
||||||
<button type="button" class="btn btn-light btn-sm propagate-note-btn" style="padding: 0.2rem 0.4rem; font-size: 0.9rem; margin-left: 5px;" title="Propaga nota a tutte le parti">
|
<tr>
|
||||||
<i class="fas fa-sticky-note"></i>
|
<th style="width: 55px;">Num</th>
|
||||||
</button>
|
<th>Descrizione</th>
|
||||||
Azioni
|
<th style="width: 200px;">Matrice</th>
|
||||||
|
<th style="width: 150px;">
|
||||||
|
<input type="date" class="form-control form-control-sm propagate-date-input" style="width: 130px; margin-left: 5px; display: inline-block;" title="Propaga data a tutte le parti">
|
||||||
|
</th>
|
||||||
|
<th style="width: 200px;">
|
||||||
|
<button type="button" class="btn btn-light btn-sm propagate-note-btn" style="padding: 0.2rem 0.4rem; font-size: 0.9rem; margin-left: 5px;" title="Propaga nota a tutte le parti">
|
||||||
|
<i class="fas fa-sticky-note"></i>
|
||||||
|
</button>
|
||||||
|
Azioni
|
||||||
|
|
||||||
</th>
|
</th>
|
||||||
</tr>
|
</tr>
|
||||||
</thead>
|
</thead>
|
||||||
<tbody id="partsTableBody">
|
<tbody id="partsTableBody">
|
||||||
<tr data-part-id="new">
|
<tr data-part-id="new">
|
||||||
<td><input type="number" class="form-control form-control-sm part-number" value="1" style="width: 55px;"></td>
|
<td><input type="number" class="form-control form-control-sm part-number" value="1" style="width: 55px;"></td>
|
||||||
<td><input type="text" class="form-control form-control-sm part-description" placeholder="Inserisci descrizione"></td>
|
<td><input type="text" class="form-control form-control-sm part-description" placeholder="Inserisci descrizione"></td>
|
||||||
<td>
|
<td>
|
||||||
<div style="display: flex; align-items: center;">
|
<div style="display: flex; align-items: center;">
|
||||||
<button type="button" class="btn btn-primary btn-sm propagate-matrice-btn" style="padding: 0.1rem 0.3rem; font-size: 0.8rem; margin-right: 3px;"><i class="fas fa-arrow-right fa-xs"></i></button>
|
<button type="button" class="btn btn-primary btn-sm propagate-matrice-btn" style="padding: 0.1rem 0.3rem; font-size: 0.8rem; margin-right: 3px;"><i class="fas fa-arrow-right"></i></button>
|
||||||
<select class="part-matrice form-control form-control-sm" style="width: 150px;"></select>
|
<select class="part-matrice form-control form-control-sm" style="width: 150px;"></select>
|
||||||
</div>
|
</div>
|
||||||
</td>
|
</td>
|
||||||
<td><input type="date" class="form-control form-control-sm part-dateexpiry" style="width: 130px;"></td>
|
<td><input type="date" class="form-control form-control-sm part-dateexpiry" style="width: 130px;"></td>
|
||||||
<td>
|
<td>
|
||||||
<button type="button" class="btn btn-light btn-sm note-btn" style="padding: 0.2rem 0.4rem; font-size: 0.9rem;" title="Aggiungi/Modifica nota"><i class="fas fa-sticky-note"></i></button>
|
<button type="button" class="btn btn-light btn-sm note-btn" style="padding: 0.2rem 0.4rem; font-size: 0.9rem;" title="Aggiungi/Modifica nota"><i class="fas fa-sticky-note"></i></button>
|
||||||
<button type="button" class="btn btn-warning btn-sm add-mix-row" style="padding: 0.1rem 0.3rem; font-size: 0.8rem;">M+</button>
|
<button type="button" class="btn btn-warning btn-sm add-mix-row" style="padding: 0.1rem 0.3rem; font-size: 0.8rem;">M+</button>
|
||||||
<button type="button" class="btn btn-danger btn-sm remove-row" style="padding: 0.1rem 0.3rem; font-size: 0.8rem; display: none;"><i class="fas fa-trash fa-xs"></i></button>
|
<button type="button" class="btn btn-danger btn-sm remove-row" style="padding: 0.1rem 0.3rem; font-size: 0.8rem; display: none;"><i class="fas fa-trash"></i></button>
|
||||||
<span class="save-status text-success" style="display: none; margin-left: 5px;"><i class="fas fa-check fa-xs"></i></span>
|
<span class="save-status text-success" style="display: none; margin-left: 5px;"><i class="fas fa-check"></i></span>
|
||||||
<span class="save-loading text-warning" style="display: none; margin-left: 5px;"><i class="fas fa-spinner fa-spin fa-xs"></i></span>
|
<span class="save-loading text-warning" style="display: none; margin-left: 5px;"><i class="fas fa-spinner fa-spin"></i></span>
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
</tbody>
|
</tbody>
|
||||||
</table>
|
</table>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="col-md-3">
|
<div class="col-md-3">
|
||||||
<h6>Foto del Campione</h6>
|
<h6>Foto del Campione</h6>
|
||||||
@ -188,6 +214,14 @@
|
|||||||
z-index: 1055 !important
|
z-index: 1055 !important
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Fix: app.css sets .btn i { margin-top:-1em; margin-bottom:-1em; font-size:1.3rem } which collapses small icon buttons */
|
||||||
|
#partsModal .btn i {
|
||||||
|
margin-top: 0 !important;
|
||||||
|
margin-bottom: 0 !important;
|
||||||
|
font-size: 0.8rem !important;
|
||||||
|
margin-right: 2px !important;
|
||||||
|
}
|
||||||
|
|
||||||
/* Tabelle */
|
/* Tabelle */
|
||||||
#partsTable tr {
|
#partsTable tr {
|
||||||
display: table-row !important
|
display: table-row !important
|
||||||
@ -269,28 +303,20 @@
|
|||||||
white-space: nowrap;
|
white-space: nowrap;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Select delle righe (colonna Matrice) = 150px */
|
/* Select delle righe Matrice: si adatta alla colonna */
|
||||||
.part-matrice {
|
.part-matrice {
|
||||||
width: 300px !important;
|
width: 100% !important;
|
||||||
min-width: 300px !important;
|
min-width: 0 !important;
|
||||||
max-width: 300px !important;
|
max-width: 100% !important;
|
||||||
flex: 0 0 300px !important;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.part-matrice.select2-hidden-accessible+.select2 {
|
.part-matrice.select2-hidden-accessible+.select2 {
|
||||||
width: 300px !important;
|
width: 100% !important;
|
||||||
min-width: 300px !important;
|
min-width: 0 !important;
|
||||||
max-width: 300px !important;
|
max-width: 100% !important;
|
||||||
flex: 0 0 300px !important;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Colonna Descrizione (2ª colonna) = 420px */
|
|
||||||
#partsTable th:nth-child(2),
|
|
||||||
#partsTable td:nth-child(2) {
|
|
||||||
width: 300px !important;
|
|
||||||
min-width: 300px !important;
|
|
||||||
max-width: 300px !important;
|
|
||||||
}
|
|
||||||
|
|
||||||
#partsTable .part-number {
|
#partsTable .part-number {
|
||||||
text-align: center;
|
text-align: center;
|
||||||
@ -495,6 +521,77 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Resizable parts table - stable */
|
||||||
|
.parts-table-scroll {
|
||||||
|
width: 100%;
|
||||||
|
overflow-x: auto;
|
||||||
|
overflow-y: visible;
|
||||||
|
contain: inline-size;
|
||||||
|
}
|
||||||
|
|
||||||
|
#partsTable {
|
||||||
|
table-layout: fixed !important;
|
||||||
|
width: max-content !important;
|
||||||
|
min-width: unset !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
#partsTable col.parts-col-num {
|
||||||
|
width: 55px;
|
||||||
|
}
|
||||||
|
|
||||||
|
#partsTable col.parts-col-description {
|
||||||
|
width: 320px;
|
||||||
|
}
|
||||||
|
|
||||||
|
#partsTable col.parts-col-matrice {
|
||||||
|
width: 360px;
|
||||||
|
}
|
||||||
|
|
||||||
|
#partsTable col.parts-col-date {
|
||||||
|
width: 150px;
|
||||||
|
}
|
||||||
|
|
||||||
|
#partsTable col.parts-col-actions {
|
||||||
|
width: 230px;
|
||||||
|
}
|
||||||
|
|
||||||
|
#partsTable th,
|
||||||
|
#partsTable td {
|
||||||
|
position: relative;
|
||||||
|
box-sizing: border-box;
|
||||||
|
}
|
||||||
|
|
||||||
|
#partsTable th {
|
||||||
|
user-select: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
#partsTable th .parts-resizer {
|
||||||
|
position: absolute;
|
||||||
|
top: 0;
|
||||||
|
right: 0;
|
||||||
|
width: 9px;
|
||||||
|
height: 100%;
|
||||||
|
cursor: col-resize;
|
||||||
|
z-index: 50;
|
||||||
|
background: rgba(108, 117, 125, 0.12);
|
||||||
|
border-right: 1px solid rgba(108, 117, 125, 0.45);
|
||||||
|
}
|
||||||
|
|
||||||
|
#partsTable th .parts-resizer:hover {
|
||||||
|
background: rgba(108, 117, 125, 0.25);
|
||||||
|
border-right: 2px solid rgba(73, 80, 87, 0.7);
|
||||||
|
}
|
||||||
|
|
||||||
|
#partsTable td {
|
||||||
|
overflow: hidden;
|
||||||
|
text-overflow: ellipsis;
|
||||||
|
}
|
||||||
|
|
||||||
|
#partsTable td input,
|
||||||
|
#partsTable td select,
|
||||||
|
#partsTable td .select2-container {
|
||||||
|
max-width: 100% !important;
|
||||||
|
}
|
||||||
|
|
||||||
/* rosso */
|
/* rosso */
|
||||||
</style>
|
</style>
|
||||||
264
public/userarea/modals_gridData.js
Normal file
264
public/userarea/modals_gridData.js
Normal file
@ -0,0 +1,264 @@
|
|||||||
|
/**
|
||||||
|
* modals_gridData.js — Photos, Parts, Tested Component handlers for gridData pages
|
||||||
|
*/
|
||||||
|
(function () {
|
||||||
|
"use strict";
|
||||||
|
|
||||||
|
// ── Photos — use photos.js loadPopupContent (exported to window) ──
|
||||||
|
$(document).on("click", ".photos-btn", function () {
|
||||||
|
const iddatadb = $(this).data("iddatadb") || null;
|
||||||
|
const idquotations = $(this).data("idquotations") || null;
|
||||||
|
const modal = document.getElementById("photosModal");
|
||||||
|
if (!modal) return;
|
||||||
|
|
||||||
|
modal.style.display = "block";
|
||||||
|
|
||||||
|
if (typeof window.loadPopupContent === "function") {
|
||||||
|
window.loadPopupContent(iddatadb, idquotations);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// Close photos modal
|
||||||
|
$(document).on("click", ".close-btn", function () {
|
||||||
|
const modal = document.getElementById("photosModal");
|
||||||
|
if (modal) modal.style.display = "none";
|
||||||
|
});
|
||||||
|
|
||||||
|
// Close on backdrop click
|
||||||
|
$(document).on("click", "#photosModal", function (e) {
|
||||||
|
if (e.target === this) {
|
||||||
|
this.style.display = "none";
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// ── Parts (matching import_edit2.php behavior) ─────────────────────
|
||||||
|
$(document).on("click", ".parts-btn", function () {
|
||||||
|
const iddatadb = $(this).data("iddatadb") || null;
|
||||||
|
const idquotations = $(this).data("idquotations") || null;
|
||||||
|
|
||||||
|
$.ajax({
|
||||||
|
url: "modal_partsTable.php",
|
||||||
|
method: "GET",
|
||||||
|
data: { iddatadb: iddatadb },
|
||||||
|
success: function (response) {
|
||||||
|
$("#partsModalContainer").html(response);
|
||||||
|
const modalElement = document.getElementById("partsModal");
|
||||||
|
if (!modalElement) return;
|
||||||
|
|
||||||
|
$("#trfHeader").text(iddatadb || idquotations || "");
|
||||||
|
|
||||||
|
const visibleIddatadbList = Array.isArray(window.gridData)
|
||||||
|
? window.gridData
|
||||||
|
.map((r) => parseInt(r.iddatadb, 10))
|
||||||
|
.filter((v) => !isNaN(v) && v > 0)
|
||||||
|
: [];
|
||||||
|
|
||||||
|
$("#partsModal")
|
||||||
|
.data("iddatadb", iddatadb)
|
||||||
|
.data("idquotations", idquotations)
|
||||||
|
.data("visible-iddatadb-list", visibleIddatadbList);
|
||||||
|
|
||||||
|
let modal = bootstrap.Modal.getInstance(modalElement);
|
||||||
|
if (!modal)
|
||||||
|
modal = new bootstrap.Modal(modalElement, {
|
||||||
|
backdrop: true,
|
||||||
|
keyboard: true,
|
||||||
|
focus: true,
|
||||||
|
});
|
||||||
|
modal.show();
|
||||||
|
|
||||||
|
if (typeof window.loadParts === "function") {
|
||||||
|
window.loadParts(iddatadb, idquotations);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
error: function (xhr, status, error) {
|
||||||
|
console.error("Error loading parts:", error);
|
||||||
|
},
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
$(document).on("hidden.bs.modal", "#partsModal", function () {
|
||||||
|
const modalElement = document.getElementById("partsModal");
|
||||||
|
if (modalElement) {
|
||||||
|
const modal = bootstrap.Modal.getInstance(modalElement);
|
||||||
|
if (modal) modal.dispose();
|
||||||
|
}
|
||||||
|
$("#partsModalContainer").empty();
|
||||||
|
$(".modal-backdrop").remove();
|
||||||
|
$("body").removeClass("modal-open").css("padding-right", "");
|
||||||
|
});
|
||||||
|
|
||||||
|
// ── Tested Component quick add ───────────────────────────────────────
|
||||||
|
$(document).on("click", ".add-part-btn", async function () {
|
||||||
|
const iddatadb = $(this).data("iddatadb") || null;
|
||||||
|
const rowIndex = parseInt($(this).data("row"));
|
||||||
|
const row = window.gridData?.[rowIndex];
|
||||||
|
const id = iddatadb || (row ? row.iddatadb : null);
|
||||||
|
if (!id) return;
|
||||||
|
|
||||||
|
const $cell = $(this).closest(".grid-cell, div");
|
||||||
|
const $input = $cell.find("input");
|
||||||
|
const raw = ($input.val() || "").trim();
|
||||||
|
const parts = raw
|
||||||
|
.split("|")
|
||||||
|
.map((s) => s.trim())
|
||||||
|
.filter((s) => s.length > 0);
|
||||||
|
const uniqueParts = [...new Set(parts)];
|
||||||
|
|
||||||
|
if (!uniqueParts.length) {
|
||||||
|
alert("Insert a description first.");
|
||||||
|
$input.focus();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
for (const p of uniqueParts) {
|
||||||
|
const formData = new FormData();
|
||||||
|
formData.append("iddatadb", id);
|
||||||
|
formData.append("part_description", p);
|
||||||
|
await fetch("add_part_quick.php", {
|
||||||
|
method: "POST",
|
||||||
|
body: formData,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
if (row) {
|
||||||
|
row.tested_component = raw;
|
||||||
|
row._dirty = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
alert(`Added ${uniqueParts.length} part(s).`);
|
||||||
|
$input.val(raw);
|
||||||
|
} catch (e) {
|
||||||
|
alert("Error: " + e.message);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// ── Delete row ───────────────────────────────────────────────────────
|
||||||
|
let deleteIddatadb = null;
|
||||||
|
let deleteRowIndex = null;
|
||||||
|
|
||||||
|
$(document).on("click", ".delete-btn", function () {
|
||||||
|
deleteIddatadb = $(this).data("iddatadb");
|
||||||
|
deleteRowIndex = parseInt($(this).data("row"));
|
||||||
|
const modalEl = document.getElementById("deleteConfirmModal");
|
||||||
|
if (modalEl) {
|
||||||
|
document.getElementById("deleteIddatadbText").textContent =
|
||||||
|
deleteIddatadb;
|
||||||
|
new bootstrap.Modal(modalEl).show();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
$(document).on("click", "#deleteConfirmBtn", async function () {
|
||||||
|
const modalEl = document.getElementById("deleteConfirmModal");
|
||||||
|
const modal = bootstrap.Modal.getInstance(modalEl);
|
||||||
|
if (modal) modal.hide();
|
||||||
|
|
||||||
|
if (!deleteIddatadb) return;
|
||||||
|
|
||||||
|
try {
|
||||||
|
const resp = await fetch("delete_record.php", {
|
||||||
|
method: "POST",
|
||||||
|
headers: { "Content-Type": "application/json" },
|
||||||
|
body: JSON.stringify({ id: deleteIddatadb }),
|
||||||
|
});
|
||||||
|
const result = await resp.json();
|
||||||
|
|
||||||
|
if (result.success) {
|
||||||
|
// Remove from gridData
|
||||||
|
const idx = window.gridData.findIndex(
|
||||||
|
(r) => r.iddatadb === deleteIddatadb,
|
||||||
|
);
|
||||||
|
if (idx >= 0) window.gridData.splice(idx, 1);
|
||||||
|
|
||||||
|
// Re-render
|
||||||
|
const gr = window.gridRenderer;
|
||||||
|
if (gr) gr.renderVisibleRows();
|
||||||
|
} else {
|
||||||
|
alert("Error: " + result.message);
|
||||||
|
}
|
||||||
|
} catch (e) {
|
||||||
|
alert("Error: " + e.message);
|
||||||
|
}
|
||||||
|
|
||||||
|
deleteIddatadb = null;
|
||||||
|
deleteRowIndex = null;
|
||||||
|
});
|
||||||
|
|
||||||
|
// ── Add new row ──────────────────────────────────────────────────────
|
||||||
|
$(document).on("click", "#addRowBtn", async function () {
|
||||||
|
const btn = this;
|
||||||
|
btn.disabled = true;
|
||||||
|
|
||||||
|
try {
|
||||||
|
const templateId = window.gridMeta?.templateId;
|
||||||
|
if (!templateId) {
|
||||||
|
alert("Template ID missing");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const urlParams = new URLSearchParams(window.location.search);
|
||||||
|
const importref = urlParams.get("importref") || "";
|
||||||
|
const resp = await fetch("add_record.php", {
|
||||||
|
method: "POST",
|
||||||
|
headers: { "Content-Type": "application/json" },
|
||||||
|
body: JSON.stringify({
|
||||||
|
template_id: templateId,
|
||||||
|
importreferencecode: importref,
|
||||||
|
}),
|
||||||
|
});
|
||||||
|
const result = await resp.json();
|
||||||
|
|
||||||
|
if (result.success && result.iddatadb) {
|
||||||
|
// Build new row object
|
||||||
|
const newRow = {
|
||||||
|
iddatadb: result.iddatadb,
|
||||||
|
status: "i",
|
||||||
|
idclient: window.gridMeta?.defaultIdclient || "",
|
||||||
|
cliente_fornitore_id: null,
|
||||||
|
commessaweb: null,
|
||||||
|
user_name: result.user_name || "",
|
||||||
|
importreferencecode: result.importreferencecode || "",
|
||||||
|
filename_import: "",
|
||||||
|
importdate: new Date()
|
||||||
|
.toISOString()
|
||||||
|
.slice(0, 19)
|
||||||
|
.replace("T", " "),
|
||||||
|
fixedFields: {},
|
||||||
|
details: {},
|
||||||
|
mainFieldValue: "",
|
||||||
|
_dirty: false,
|
||||||
|
};
|
||||||
|
|
||||||
|
// Add to beginning of gridData
|
||||||
|
window.gridData.unshift(newRow);
|
||||||
|
|
||||||
|
// Re-render
|
||||||
|
const gr = window.gridRenderer;
|
||||||
|
if (gr) gr.renderVisibleRows();
|
||||||
|
|
||||||
|
// Highlight new row briefly
|
||||||
|
const newGridRow = document.querySelector(
|
||||||
|
`.grid-row[data-id="${result.iddatadb}"]`,
|
||||||
|
);
|
||||||
|
if (newGridRow) {
|
||||||
|
newGridRow.classList.add("row-just-created");
|
||||||
|
newGridRow.scrollIntoView({
|
||||||
|
behavior: "smooth",
|
||||||
|
block: "center",
|
||||||
|
});
|
||||||
|
setTimeout(
|
||||||
|
() => newGridRow.classList.remove("row-just-created"),
|
||||||
|
4000,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
alert("Error: " + (result.message || "Unknown error"));
|
||||||
|
}
|
||||||
|
} catch (e) {
|
||||||
|
alert("Error: " + e.message);
|
||||||
|
} finally {
|
||||||
|
btn.disabled = false;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
})();
|
||||||
@ -143,7 +143,7 @@ $(document).ready(function () {
|
|||||||
if (iddatadb) {
|
if (iddatadb) {
|
||||||
if (matrici.length === 0) {
|
if (matrici.length === 0) {
|
||||||
$.ajax({
|
$.ajax({
|
||||||
url: "get_matrici_db.php",
|
url: "get_matrici.php",
|
||||||
method: "GET",
|
method: "GET",
|
||||||
dataType: "json",
|
dataType: "json",
|
||||||
success: function (data) {
|
success: function (data) {
|
||||||
|
|||||||
@ -81,10 +81,19 @@ $(document).ready(function () {
|
|||||||
data: { field_ids: fieldId },
|
data: { field_ids: fieldId },
|
||||||
success: function (res) {
|
success: function (res) {
|
||||||
const arr = res && res[fieldId] ? res[fieldId] : [];
|
const arr = res && res[fieldId] ? res[fieldId] : [];
|
||||||
extraFieldOptions = (arr || []).map((x) => ({
|
|
||||||
id: x.IdCustomFieldsValue,
|
extraFieldOptions = (arr || [])
|
||||||
label: x.Valore,
|
.map((x) => ({
|
||||||
}));
|
id: x.IdCustomFieldsValue,
|
||||||
|
label: x.Valore || "",
|
||||||
|
}))
|
||||||
|
.sort((a, b) =>
|
||||||
|
a.label.localeCompare(b.label, "it", {
|
||||||
|
sensitivity: "base",
|
||||||
|
numeric: true,
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
|
||||||
done();
|
done();
|
||||||
},
|
},
|
||||||
error: function () {
|
error: function () {
|
||||||
@ -127,6 +136,50 @@ $(document).ready(function () {
|
|||||||
</td>`;
|
</td>`;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function initializeExtraFieldSelect2($context) {
|
||||||
|
if (typeof $.fn.select2 === "undefined") return;
|
||||||
|
|
||||||
|
$context.find(".part-extra-select").each(function () {
|
||||||
|
const $select = $(this);
|
||||||
|
|
||||||
|
if ($select.hasClass("select2-hidden-accessible")) {
|
||||||
|
$select.select2("destroy");
|
||||||
|
}
|
||||||
|
|
||||||
|
$select.select2({
|
||||||
|
placeholder: "Seleziona...",
|
||||||
|
allowClear: true,
|
||||||
|
width: "100%",
|
||||||
|
dropdownParent: $("#partsModal"),
|
||||||
|
minimumResultsForSearch: 0,
|
||||||
|
sorter: function (data) {
|
||||||
|
return data.sort(function (a, b) {
|
||||||
|
const textA = (a.text || "").toLowerCase();
|
||||||
|
const textB = (b.text || "").toLowerCase();
|
||||||
|
return textA.localeCompare(textB, "it", {
|
||||||
|
sensitivity: "base",
|
||||||
|
numeric: true,
|
||||||
|
});
|
||||||
|
});
|
||||||
|
},
|
||||||
|
matcher: function (params, data) {
|
||||||
|
if ($.trim(params.term) === "") return data;
|
||||||
|
if (typeof data.text === "undefined") return null;
|
||||||
|
|
||||||
|
const term = params.term.toLowerCase();
|
||||||
|
const text = data.text.toLowerCase();
|
||||||
|
|
||||||
|
return text.indexOf(term) > -1 ? data : null;
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
const selected = ($select.attr("data-selected") || "").toString();
|
||||||
|
if (selected) {
|
||||||
|
$select.val(selected).trigger("change.select2");
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
$(document).on("change", ".part-extra-select", function () {
|
$(document).on("change", ".part-extra-select", function () {
|
||||||
const valId = $(this).val() || "";
|
const valId = $(this).val() || "";
|
||||||
const $row = $(this).closest("tr");
|
const $row = $(this).closest("tr");
|
||||||
@ -164,13 +217,14 @@ $(document).ready(function () {
|
|||||||
const $row = $(this);
|
const $row = $(this);
|
||||||
$row.find("td:last").before(buildExtraFieldCellHtml($row));
|
$row.find("td:last").before(buildExtraFieldCellHtml($row));
|
||||||
|
|
||||||
// ripristina selezione se già presente nello stato riga
|
|
||||||
const selected = ($row.data("extra-value-id") || "").toString();
|
const selected = ($row.data("extra-value-id") || "").toString();
|
||||||
if (selected) {
|
if (selected) {
|
||||||
$row.find(".part-extra-value-id").val(selected);
|
$row.find(".part-extra-value-id").val(selected);
|
||||||
$row.find(".part-extra-select").val(selected);
|
$row.find(".part-extra-select").attr("data-selected", selected);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
initializeExtraFieldSelect2($("#partsTableBody"));
|
||||||
}
|
}
|
||||||
|
|
||||||
function setPartId($row, id) {
|
function setPartId($row, id) {
|
||||||
@ -288,51 +342,12 @@ $(document).ready(function () {
|
|||||||
// ===================
|
// ===================
|
||||||
function loadParts(iddatadb, idquotations, callback = null) {
|
function loadParts(iddatadb, idquotations, callback = null) {
|
||||||
if (iddatadb) {
|
if (iddatadb) {
|
||||||
if (matrici.length === 0) {
|
loadMacroMatrici();
|
||||||
$.ajax({
|
initializeGlobalSelect2();
|
||||||
url: "get_matrici_db.php",
|
loadPartsExtraField(iddatadb, function () {
|
||||||
method: "GET",
|
loadPhoto(iddatadb, idquotations);
|
||||||
dataType: "json",
|
loadExistingParts(iddatadb, idquotations, callback);
|
||||||
success: function (data) {
|
});
|
||||||
matrici = data.value || [];
|
|
||||||
loadMacroMatrici();
|
|
||||||
initializeGlobalSelect2();
|
|
||||||
loadPartsExtraField(iddatadb, function () {
|
|
||||||
loadPhoto(iddatadb, idquotations);
|
|
||||||
loadExistingParts(iddatadb, idquotations, callback);
|
|
||||||
});
|
|
||||||
},
|
|
||||||
error: function (xhr, status, error) {
|
|
||||||
matrici = [];
|
|
||||||
loadMacroMatrici();
|
|
||||||
initializeGlobalSelect2();
|
|
||||||
loadPartsExtraField(iddatadb, function () {
|
|
||||||
loadPhoto(iddatadb, idquotations);
|
|
||||||
loadExistingParts(iddatadb, idquotations, callback);
|
|
||||||
});
|
|
||||||
const errorMsg = $(
|
|
||||||
'<div class="alert alert-danger temp-alert" role="alert">Errore nel caricamento delle matrici: ' +
|
|
||||||
error +
|
|
||||||
" (" +
|
|
||||||
xhr.status +
|
|
||||||
")</div>",
|
|
||||||
);
|
|
||||||
$("#partsModal .modal-body").prepend(errorMsg);
|
|
||||||
setTimeout(function () {
|
|
||||||
errorMsg.fadeOut(500, function () {
|
|
||||||
$(this).remove();
|
|
||||||
});
|
|
||||||
}, 5000);
|
|
||||||
},
|
|
||||||
});
|
|
||||||
} else {
|
|
||||||
loadMacroMatrici();
|
|
||||||
initializeGlobalSelect2();
|
|
||||||
loadPartsExtraField(iddatadb, function () {
|
|
||||||
loadPhoto(iddatadb, idquotations);
|
|
||||||
loadExistingParts(iddatadb, idquotations, callback);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
} else {
|
} else {
|
||||||
loadPartsExtraField(iddatadb, function () {
|
loadPartsExtraField(iddatadb, function () {
|
||||||
loadPhoto(iddatadb, idquotations);
|
loadPhoto(iddatadb, idquotations);
|
||||||
@ -887,7 +902,7 @@ $(document).ready(function () {
|
|||||||
<td><input type="text" class="form-control form-control-sm part-description" value="${description}" placeholder="Inserisci descrizione"></td>
|
<td><input type="text" class="form-control form-control-sm part-description" value="${description}" placeholder="Inserisci descrizione"></td>
|
||||||
<td>
|
<td>
|
||||||
<div style="display: flex; align-items: center;">
|
<div style="display: flex; align-items: center;">
|
||||||
<button type="button" class="btn btn-primary btn-sm propagate-matrice-btn" style="padding: 0.1rem 0.3rem; font-size: 0.8rem; margin-right: 3px;"><i class="fas fa-arrow-right fa-xs"></i></button>
|
<button type="button" class="btn btn-primary btn-sm propagate-matrice-btn" style="padding: 0.1rem 0.3rem; font-size: 0.8rem; margin-right: 3px;"><i class="fas fa-arrow-right"></i></button>
|
||||||
<select class="part-matrice form-control form-control-sm" style="width: 150px;"></select>
|
<select class="part-matrice form-control form-control-sm" style="width: 150px;"></select>
|
||||||
</div>
|
</div>
|
||||||
</td>
|
</td>
|
||||||
@ -896,15 +911,19 @@ $(document).ready(function () {
|
|||||||
<td>
|
<td>
|
||||||
<button type="button" class="btn btn-light btn-sm note-btn" style="padding: 0.1rem 0.3rem; font-size: 0.8rem;" title="Aggiungi/Modifica nota"><i class="fas fa-file-alt"></i></button>
|
<button type="button" class="btn btn-light btn-sm note-btn" style="padding: 0.1rem 0.3rem; font-size: 0.8rem;" title="Aggiungi/Modifica nota"><i class="fas fa-file-alt"></i></button>
|
||||||
<button type="button" class="btn btn-warning btn-sm add-mix-row" style="padding: 0.1rem 0.3rem; font-size: 0.8rem;">M+</button>
|
<button type="button" class="btn btn-warning btn-sm add-mix-row" style="padding: 0.1rem 0.3rem; font-size: 0.8rem;">M+</button>
|
||||||
<button type="button" class="btn btn-danger btn-sm remove-row" style="padding: 0.1rem 0.3rem; font-size: 0.8rem; display: none;"><i class="fas fa-trash fa-xs"></i></button>
|
<button type="button" class="btn btn-danger btn-sm remove-row" style="padding: 0.1rem 0.3rem; font-size: 0.8rem; display: none;"><i class="fas fa-trash"></i></button>
|
||||||
<span class="save-status text-success" style="display: none; margin-left: 5px;"><i class="fas fa-check fa-xs"></i></span>
|
<span class="save-status text-success" style="display: none; margin-left: 5px;"><i class="fas fa-check"></i></span>
|
||||||
<span class="save-loading text-warning" style="display: none; margin-left: 5px;"><i class="fas fa-spinner fa-spin fa-xs"></i></span>
|
<span class="save-loading text-warning" style="display: none; margin-left: 5px;"><i class="fas fa-spinner fa-spin"></i></span>
|
||||||
</td>
|
</td>
|
||||||
</tr>`;
|
</tr>`;
|
||||||
$("#partsTableBody").append(newRow);
|
$("#partsTableBody").append(newRow);
|
||||||
const $select = $("#partsTableBody tr:last .part-matrice");
|
const $newRow = $("#partsTableBody tr:last");
|
||||||
|
const $select = $newRow.find(".part-matrice");
|
||||||
const selectedMacro = $("#macro-matrice-filter").val() || "";
|
const selectedMacro = $("#macro-matrice-filter").val() || "";
|
||||||
|
|
||||||
initializeSelect2($select, nextPartNumber, "", null, selectedMacro);
|
initializeSelect2($select, nextPartNumber, "", null, selectedMacro);
|
||||||
|
initializeExtraFieldSelect2($newRow);
|
||||||
|
|
||||||
updateRowButtons();
|
updateRowButtons();
|
||||||
markUnsaved();
|
markUnsaved();
|
||||||
}
|
}
|
||||||
@ -1380,7 +1399,7 @@ $(document).ready(function () {
|
|||||||
<td><input type="text" class="form-control form-control-sm part-description" value="${escapedDescription}" placeholder="Inserisci descrizione"></td>
|
<td><input type="text" class="form-control form-control-sm part-description" value="${escapedDescription}" placeholder="Inserisci descrizione"></td>
|
||||||
<td>
|
<td>
|
||||||
<div style="display: flex; align-items: center;">
|
<div style="display: flex; align-items: center;">
|
||||||
<button type="button" class="btn btn-primary btn-sm propagate-matrice-btn" style="padding: 0.1rem 0.3rem; font-size: 0.8rem; margin-right: 3px;"><i class="fas fa-arrow-right fa-xs"></i></button>
|
<button type="button" class="btn btn-primary btn-sm propagate-matrice-btn" style="padding: 0.1rem 0.3rem; font-size: 0.8rem; margin-right: 3px;"><i class="fas fa-arrow-right"></i></button>
|
||||||
<select class="part-matrice form-control form-control-sm" style="width: 150px;" ${idquotations && !part.idmatrice ? "disabled" : ""}></select>
|
<select class="part-matrice form-control form-control-sm" style="width: 150px;" ${idquotations && !part.idmatrice ? "disabled" : ""}></select>
|
||||||
</div>
|
</div>
|
||||||
</td>
|
</td>
|
||||||
@ -1389,9 +1408,9 @@ $(document).ready(function () {
|
|||||||
<td>
|
<td>
|
||||||
<button type="button" class="btn btn-light btn-sm note-btn ${part.note ? "has-note" : ""}" style="padding: 0.1rem 0.3rem; font-size: 0.8rem;" title="Aggiungi/Modifica nota"><i class="fas fa-file-alt"></i></button>
|
<button type="button" class="btn btn-light btn-sm note-btn ${part.note ? "has-note" : ""}" style="padding: 0.1rem 0.3rem; font-size: 0.8rem;" title="Aggiungi/Modifica nota"><i class="fas fa-file-alt"></i></button>
|
||||||
<button type="button" class="btn btn-warning btn-sm add-mix-row" style="padding: 0.1rem 0.3rem; font-size: 0.8rem;">M+</button>
|
<button type="button" class="btn btn-warning btn-sm add-mix-row" style="padding: 0.1rem 0.3rem; font-size: 0.8rem;">M+</button>
|
||||||
<button type="button" class="btn btn-danger btn-sm remove-row" style="padding: 0.1rem 0.3rem; font-size: 0.8rem;"><i class="fas fa-trash fa-xs"></i></button>
|
<button type="button" class="btn btn-danger btn-sm remove-row" style="padding: 0.1rem 0.3rem; font-size: 0.8rem;"><i class="fas fa-trash"></i></button>
|
||||||
<span class="save-status text-success" style="display: none; margin-left: 5px;"><i class="fas fa-check fa-xs"></i></span>
|
<span class="save-status text-success" style="display: none; margin-left: 5px;"><i class="fas fa-check"></i></span>
|
||||||
<span class="save-loading text-warning" style="display: none; margin-left: 5px;"><i class="fas fa-spinner fa-spin fa-xs"></i></span>
|
<span class="save-loading text-warning" style="display: none; margin-left: 5px;"><i class="fas fa-spinner fa-spin"></i></span>
|
||||||
</td>
|
</td>
|
||||||
</tr>`;
|
</tr>`;
|
||||||
$("#partsTableBody").append(newRow);
|
$("#partsTableBody").append(newRow);
|
||||||
@ -1407,8 +1426,14 @@ $(document).ready(function () {
|
|||||||
const vid = String(part.extra_value_id);
|
const vid = String(part.extra_value_id);
|
||||||
$row.data("extra-value-id", vid);
|
$row.data("extra-value-id", vid);
|
||||||
$row.find(".part-extra-value-id").val(vid);
|
$row.find(".part-extra-value-id").val(vid);
|
||||||
$row.find(".part-extra-select").val(vid);
|
$row.find(".part-extra-select").attr(
|
||||||
|
"data-selected",
|
||||||
|
vid,
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
initializeExtraFieldSelect2($row);
|
||||||
|
|
||||||
const $select = $("#partsTableBody").find(
|
const $select = $("#partsTableBody").find(
|
||||||
`tr[data-part-id="${part.id}"] .part-matrice`,
|
`tr[data-part-id="${part.id}"] .part-matrice`,
|
||||||
);
|
);
|
||||||
@ -1454,7 +1479,7 @@ $(document).ready(function () {
|
|||||||
|
|
||||||
function loadMacroMatrici() {
|
function loadMacroMatrici() {
|
||||||
$.ajax({
|
$.ajax({
|
||||||
url: "get_macro_matrici.php",
|
url: "search_matrici.php?macro_list=1",
|
||||||
method: "GET",
|
method: "GET",
|
||||||
dataType: "json",
|
dataType: "json",
|
||||||
success: function (data) {
|
success: function (data) {
|
||||||
@ -1551,18 +1576,6 @@ $(document).ready(function () {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Filtra le matrici in base alla MacroMatrice selezionata
|
|
||||||
const filteredMatrici = selectedMacro
|
|
||||||
? matrici.filter(
|
|
||||||
(matrice) => matrice.MacroMatrice === selectedMacro,
|
|
||||||
)
|
|
||||||
: matrici;
|
|
||||||
|
|
||||||
// Crea opzioni per Select2
|
|
||||||
const options = filteredMatrici.map(function (matrice) {
|
|
||||||
return { id: matrice.IdMatrice, text: matrice.NomeMatrice };
|
|
||||||
});
|
|
||||||
|
|
||||||
// Memorizza il valore corrente
|
// Memorizza il valore corrente
|
||||||
const currentValue = $select.val();
|
const currentValue = $select.val();
|
||||||
|
|
||||||
@ -1571,39 +1584,43 @@ $(document).ready(function () {
|
|||||||
$select.select2("destroy");
|
$select.select2("destroy");
|
||||||
}
|
}
|
||||||
|
|
||||||
// Inizializza Select2
|
// Inizializza Select2 con AJAX
|
||||||
$select.empty().select2({
|
$select.empty().select2({
|
||||||
placeholder: filteredMatrici.length
|
placeholder: "Seleziona matrice globale",
|
||||||
? "Seleziona matrice globale"
|
|
||||||
: "Nessuna matrice disponibile",
|
|
||||||
allowClear: true,
|
allowClear: true,
|
||||||
data: options,
|
|
||||||
dropdownParent: $("#partsModal"),
|
dropdownParent: $("#partsModal"),
|
||||||
minimumResultsForSearch: 0, // Abilita ricerca senza limite minimo di caratteri
|
minimumInputLength: 0,
|
||||||
matcher: function (params, data) {
|
ajax: {
|
||||||
if (!params.term) return data; // Mostra tutte le opzioni se non c'è termine di ricerca
|
url: "search_matrici.php",
|
||||||
const term = params.term.toUpperCase();
|
dataType: "json",
|
||||||
if (data.text.toUpperCase().indexOf(term) >= 0) return data;
|
delay: 150,
|
||||||
return null;
|
data: function (params) {
|
||||||
|
return {
|
||||||
|
q: params.term || "",
|
||||||
|
limit: 20,
|
||||||
|
macro: selectedMacro || "",
|
||||||
|
};
|
||||||
|
},
|
||||||
|
processResults: function (data) {
|
||||||
|
return { results: data.results || [] };
|
||||||
|
},
|
||||||
|
cache: true,
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
// Ripristina il valore corrente se valido
|
// Ripristina il valore corrente
|
||||||
if (
|
if (currentValue) {
|
||||||
currentValue &&
|
$.ajax({
|
||||||
filteredMatrici.some((m) => m.IdMatrice == currentValue)
|
url: "search_matrici.php",
|
||||||
) {
|
data: { id: currentValue },
|
||||||
$select.val(currentValue).trigger("change");
|
dataType: "json",
|
||||||
} else if (filteredMatrici.length === 0) {
|
}).then(function (data) {
|
||||||
const errorMsg = $(
|
const item = (data.results || [])[0];
|
||||||
'<div class="alert alert-warning temp-alert" role="alert">Nessuna matrice disponibile per la MacroMatrice selezionata.</div>',
|
if (item) {
|
||||||
);
|
const option = new Option(item.text, item.id, true, true);
|
||||||
$("#partsModal .modal-body").prepend(errorMsg);
|
$select.append(option).trigger("change");
|
||||||
setTimeout(function () {
|
}
|
||||||
errorMsg.fadeOut(500, function () {
|
});
|
||||||
$(this).remove();
|
|
||||||
});
|
|
||||||
}, 5000);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1622,32 +1639,6 @@ $(document).ready(function () {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Filtra le matrici in base alla MacroMatrice selezionata
|
|
||||||
const filteredMatrici = selectedMacro
|
|
||||||
? matrici.filter((m) => m.MacroMatrice === selectedMacro)
|
|
||||||
: matrici;
|
|
||||||
|
|
||||||
// Crea opzioni per Select2, includendo il valore pre-selezionato se non è nel filtro
|
|
||||||
let options = filteredMatrici.map(function (matrice) {
|
|
||||||
return { id: matrice.IdMatrice, text: matrice.NomeMatrice };
|
|
||||||
});
|
|
||||||
|
|
||||||
// Se idmatrice esiste ed è fuori dal filtro, aggiungilo come opzione
|
|
||||||
if (
|
|
||||||
idmatrice &&
|
|
||||||
!filteredMatrici.some((m) => m.IdMatrice == idmatrice)
|
|
||||||
) {
|
|
||||||
const selectedMatrice = matrici.find(
|
|
||||||
(m) => m.IdMatrice == idmatrice,
|
|
||||||
);
|
|
||||||
if (selectedMatrice) {
|
|
||||||
options.push({
|
|
||||||
id: selectedMatrice.IdMatrice,
|
|
||||||
text: selectedMatrice.NomeMatrice,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Memorizza il valore corrente
|
// Memorizza il valore corrente
|
||||||
const currentValue = idmatrice || $select.val();
|
const currentValue = idmatrice || $select.val();
|
||||||
|
|
||||||
@ -1656,44 +1647,48 @@ $(document).ready(function () {
|
|||||||
$select.select2("destroy");
|
$select.select2("destroy");
|
||||||
}
|
}
|
||||||
|
|
||||||
// Inizializza Select2
|
// Inizializza Select2 con AJAX
|
||||||
$select.empty().select2({
|
$select.empty().select2({
|
||||||
placeholder: filteredMatrici.length
|
placeholder: "Seleziona matrice",
|
||||||
? "Seleziona matrice"
|
|
||||||
: "Nessuna matrice disponibile",
|
|
||||||
allowClear: true,
|
allowClear: true,
|
||||||
data: options,
|
|
||||||
dropdownParent: $("#partsModal"),
|
dropdownParent: $("#partsModal"),
|
||||||
minimumResultsForSearch: 0, // Abilita ricerca senza limite minimo di caratteri
|
minimumInputLength: 0,
|
||||||
matcher: function (params, data) {
|
ajax: {
|
||||||
if (!params.term) return data; // Mostra tutte le opzioni se non c'è termine di ricerca
|
url: "search_matrici.php",
|
||||||
const term = params.term.toUpperCase();
|
dataType: "json",
|
||||||
if (data.text.toUpperCase().indexOf(term) >= 0) return data;
|
delay: 150,
|
||||||
return null;
|
data: function (params) {
|
||||||
|
return {
|
||||||
|
q: params.term || "",
|
||||||
|
limit: 20,
|
||||||
|
macro: selectedMacro || "",
|
||||||
|
};
|
||||||
|
},
|
||||||
|
processResults: function (data) {
|
||||||
|
return { results: data.results || [] };
|
||||||
|
},
|
||||||
|
cache: true,
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
// Ripristina il valore se valido
|
// Ripristina il valore se valido
|
||||||
if (partId && partId !== "new" && currentValue) {
|
if (partId && partId !== "new" && currentValue) {
|
||||||
const matrice = matrici.find((m) => m.IdMatrice == currentValue);
|
$.ajax({
|
||||||
if (matrice) {
|
url: "search_matrici.php",
|
||||||
const option = new Option(
|
data: { id: currentValue },
|
||||||
matrice.NomeMatrice,
|
dataType: "json",
|
||||||
matrice.IdMatrice,
|
}).then(function (data) {
|
||||||
true,
|
const item = (data.results || [])[0];
|
||||||
true,
|
if (item) {
|
||||||
);
|
const option = new Option(item.text, item.id, true, true);
|
||||||
|
if (!fromFilter) $select.append(option).trigger("change");
|
||||||
if (!fromFilter) $select.append(option).trigger("change");
|
else $select.append(option);
|
||||||
else $select.append(option);
|
partMatrice[partNumber] = item.id;
|
||||||
|
} else {
|
||||||
partMatrice[partNumber] = matrice.IdMatrice;
|
if (!fromFilter) $select.val(null).trigger("change");
|
||||||
} else {
|
partMatrice[partNumber] = null;
|
||||||
// Aggiusta valore non valido
|
}
|
||||||
if (!fromFilter) $select.val(null).trigger("change");
|
});
|
||||||
|
|
||||||
partMatrice[partNumber] = null;
|
|
||||||
}
|
|
||||||
} else {
|
} else {
|
||||||
$select.val(null).trigger("change", [{ skipHandler: true }]);
|
$select.val(null).trigger("change", [{ skipHandler: true }]);
|
||||||
}
|
}
|
||||||
@ -1770,18 +1765,7 @@ $(document).ready(function () {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
// Mostra messaggio se non ci sono matrici
|
// Messaggio se macro selezionata ma nessun risultato sarà gestito dal placeholder Select2
|
||||||
if (filteredMatrici.length === 0 && selectedMacro) {
|
|
||||||
const errorMsg = $(
|
|
||||||
'<div class="alert alert-warning temp-alert" role="alert">Nessuna matrice disponibile per la MacroMatrice selezionata.</div>',
|
|
||||||
);
|
|
||||||
$("#partsModal .modal-body").prepend(errorMsg);
|
|
||||||
setTimeout(function () {
|
|
||||||
errorMsg.fadeOut(500, function () {
|
|
||||||
$(this).remove();
|
|
||||||
});
|
|
||||||
}, 5000);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Debug per verificare inizializzazione
|
// Debug per verificare inizializzazione
|
||||||
console.log(
|
console.log(
|
||||||
@ -1797,8 +1781,13 @@ $(document).ready(function () {
|
|||||||
$(document).on("click", ".propagate-matrice-btn", function () {
|
$(document).on("click", ".propagate-matrice-btn", function () {
|
||||||
const $row = $(this).closest("tr");
|
const $row = $(this).closest("tr");
|
||||||
const globalVal = $("#global-matrice").val();
|
const globalVal = $("#global-matrice").val();
|
||||||
|
const globalText = $("#global-matrice").find("option:selected").text();
|
||||||
if (globalVal) {
|
if (globalVal) {
|
||||||
$row.find(".part-matrice").val(globalVal).trigger("change");
|
const $target = $row.find(".part-matrice");
|
||||||
|
if (!$target.find(`option[value="${globalVal}"]`).length) {
|
||||||
|
$target.append(new Option(globalText, globalVal, true, true));
|
||||||
|
}
|
||||||
|
$target.val(globalVal).trigger("change");
|
||||||
} else {
|
} else {
|
||||||
const errorMsg = $(
|
const errorMsg = $(
|
||||||
'<div class="alert alert-danger temp-alert" role="alert">Seleziona una matrice globale prima di propagare.</div>',
|
'<div class="alert alert-danger temp-alert" role="alert">Seleziona una matrice globale prima di propagare.</div>',
|
||||||
@ -1814,8 +1803,17 @@ $(document).ready(function () {
|
|||||||
|
|
||||||
$(document).on("click", ".propagate-all-btn", function () {
|
$(document).on("click", ".propagate-all-btn", function () {
|
||||||
const globalVal = $("#global-matrice").val();
|
const globalVal = $("#global-matrice").val();
|
||||||
|
const globalText = $("#global-matrice").find("option:selected").text();
|
||||||
if (globalVal) {
|
if (globalVal) {
|
||||||
$("#partsTableBody .part-matrice").val(globalVal).trigger("change");
|
$("#partsTableBody .part-matrice").each(function () {
|
||||||
|
const $target = $(this);
|
||||||
|
if (!$target.find(`option[value="${globalVal}"]`).length) {
|
||||||
|
$target.append(
|
||||||
|
new Option(globalText, globalVal, true, true),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
$target.val(globalVal).trigger("change");
|
||||||
|
});
|
||||||
} else {
|
} else {
|
||||||
const errorMsg = $(
|
const errorMsg = $(
|
||||||
'<div class="alert alert-danger temp-alert" role="alert">Seleziona una matrice globale prima di propagare.</div>',
|
'<div class="alert alert-danger temp-alert" role="alert">Seleziona una matrice globale prima di propagare.</div>',
|
||||||
@ -1981,7 +1979,109 @@ $(document).ready(function () {
|
|||||||
".add-row-global, .add-mix-global, .add-mix-row, .remove-row, .propagate-matrice-btn, .propagate-all-btn, .note-btn",
|
".add-row-global, .add-mix-global, .add-mix-row, .remove-row, .propagate-matrice-btn, .propagate-all-btn, .note-btn",
|
||||||
markUnsaved,
|
markUnsaved,
|
||||||
);
|
);
|
||||||
|
$(document).on("click", "#clonePartsBtn", function (e) {
|
||||||
|
e.preventDefault();
|
||||||
|
|
||||||
|
const sourceIddatadb =
|
||||||
|
parseInt($("#partsModal").data("iddatadb"), 10) || null;
|
||||||
|
const visibleListRaw =
|
||||||
|
$("#partsModal").data("visible-iddatadb-list") || [];
|
||||||
|
|
||||||
|
if (!sourceIddatadb) {
|
||||||
|
const errorMsg = $(
|
||||||
|
'<div class="alert alert-danger temp-alert" role="alert">Source record not found.</div>',
|
||||||
|
);
|
||||||
|
$("#partsModal .modal-body").prepend(errorMsg);
|
||||||
|
setTimeout(() => {
|
||||||
|
errorMsg.fadeOut(500, function () {
|
||||||
|
$(this).remove();
|
||||||
|
});
|
||||||
|
}, 5000);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const targetIds = (Array.isArray(visibleListRaw) ? visibleListRaw : [])
|
||||||
|
.map((v) => parseInt(v, 10))
|
||||||
|
.filter((v) => !isNaN(v) && v > 0 && v !== sourceIddatadb);
|
||||||
|
|
||||||
|
if (!targetIds.length) {
|
||||||
|
const errorMsg = $(
|
||||||
|
'<div class="alert alert-warning temp-alert" role="alert">No other visible records available for clone.</div>',
|
||||||
|
);
|
||||||
|
$("#partsModal .modal-body").prepend(errorMsg);
|
||||||
|
setTimeout(() => {
|
||||||
|
errorMsg.fadeOut(500, function () {
|
||||||
|
$(this).remove();
|
||||||
|
});
|
||||||
|
}, 5000);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (
|
||||||
|
!confirm(
|
||||||
|
`Confermi il clone delle parti del record ${sourceIddatadb} negli altri ${targetIds.length} record visibili?`,
|
||||||
|
)
|
||||||
|
) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const $btn = $(this);
|
||||||
|
const originalHtml = $btn.html();
|
||||||
|
$btn.prop("disabled", true).html(
|
||||||
|
'<i class="fas fa-spinner fa-spin"></i> Clonazione...',
|
||||||
|
);
|
||||||
|
|
||||||
|
$.ajax({
|
||||||
|
url: "clone_parts_to_visible.php",
|
||||||
|
method: "POST",
|
||||||
|
contentType: "application/json",
|
||||||
|
dataType: "json",
|
||||||
|
data: JSON.stringify({
|
||||||
|
source_iddatadb: sourceIddatadb,
|
||||||
|
target_iddatadb_list: targetIds,
|
||||||
|
}),
|
||||||
|
success: function (response) {
|
||||||
|
$btn.prop("disabled", false).html(originalHtml);
|
||||||
|
|
||||||
|
if (response.success) {
|
||||||
|
const successMsg = $(
|
||||||
|
`<div class="alert alert-success temp-alert" role="alert">
|
||||||
|
Clone completed. Source parts copied to ${response.cloned_targets || 0} record(s), total cloned parts: ${response.total_cloned_parts || 0}.
|
||||||
|
</div>`,
|
||||||
|
);
|
||||||
|
$("#partsModal .modal-body").prepend(successMsg);
|
||||||
|
setTimeout(() => {
|
||||||
|
successMsg.fadeOut(500, function () {
|
||||||
|
$(this).remove();
|
||||||
|
});
|
||||||
|
}, 5000);
|
||||||
|
} else {
|
||||||
|
const errorMsg = $(
|
||||||
|
`<div class="alert alert-danger temp-alert" role="alert">Clone error: ${response.message || "Unknown error"}</div>`,
|
||||||
|
);
|
||||||
|
$("#partsModal .modal-body").prepend(errorMsg);
|
||||||
|
setTimeout(() => {
|
||||||
|
errorMsg.fadeOut(500, function () {
|
||||||
|
$(this).remove();
|
||||||
|
});
|
||||||
|
}, 5000);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
error: function (xhr, status, error) {
|
||||||
|
$btn.prop("disabled", false).html(originalHtml);
|
||||||
|
|
||||||
|
const errorMsg = $(
|
||||||
|
`<div class="alert alert-danger temp-alert" role="alert">Clone error: ${error} (${xhr.status})</div>`,
|
||||||
|
);
|
||||||
|
$("#partsModal .modal-body").prepend(errorMsg);
|
||||||
|
setTimeout(() => {
|
||||||
|
errorMsg.fadeOut(500, function () {
|
||||||
|
$(this).remove();
|
||||||
|
});
|
||||||
|
}, 5000);
|
||||||
|
},
|
||||||
|
});
|
||||||
|
});
|
||||||
// Esporta la funzione loadParts per essere usata da import_Edit2.php
|
// Esporta la funzione loadParts per essere usata da import_Edit2.php
|
||||||
window.loadParts = loadParts;
|
window.loadParts = loadParts;
|
||||||
|
|
||||||
@ -2363,4 +2463,142 @@ $(document).on("click", "#showHideImageBtn", function () {
|
|||||||
"<i class='fas fa-eye' style='font-size: 0.8rem;'></i><i class='fas fa-image ms-1' style='font-size: 0.8rem;'></i>",
|
"<i class='fas fa-eye' style='font-size: 0.8rem;'></i><i class='fas fa-image ms-1' style='font-size: 0.8rem;'></i>",
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (typeof window.applyPartsColumnWidths === "function") {
|
||||||
|
setTimeout(window.applyPartsColumnWidths, 150);
|
||||||
|
}
|
||||||
});
|
});
|
||||||
|
// ===================
|
||||||
|
// RESIZABLE PARTS TABLE COLUMNS - FIXED COLGROUP VERSION
|
||||||
|
// ===================
|
||||||
|
(function () {
|
||||||
|
// Larghezze default per indice colonna (0-based)
|
||||||
|
const defaultWidths = [55, 320, 360, 150, 230];
|
||||||
|
const savedWidths = [...defaultWidths];
|
||||||
|
|
||||||
|
function getColgroup() {
|
||||||
|
return $("#partsTableColgroup");
|
||||||
|
}
|
||||||
|
|
||||||
|
function syncColgroupToHeaders() {
|
||||||
|
const $table = $("#partsTable");
|
||||||
|
const $ths = $table.find("thead tr:first th");
|
||||||
|
const $colgroup = getColgroup();
|
||||||
|
const thCount = $ths.length;
|
||||||
|
|
||||||
|
// Assicura che ci siano esattamente tante <col> quante <th>
|
||||||
|
while ($colgroup.find("col").length < thCount) {
|
||||||
|
$colgroup.append("<col>");
|
||||||
|
}
|
||||||
|
while ($colgroup.find("col").length > thCount) {
|
||||||
|
$colgroup.find("col:last").remove();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Applica le larghezze salvate
|
||||||
|
$colgroup.find("col").each(function (i) {
|
||||||
|
const w = savedWidths[i] !== undefined ? savedWidths[i] : 150;
|
||||||
|
$(this).css("width", w + "px");
|
||||||
|
});
|
||||||
|
|
||||||
|
// Imposta larghezza totale della tabella = somma colonne (evita reflow)
|
||||||
|
const total = savedWidths.slice(0, thCount).reduce((a, b) => a + b, 0);
|
||||||
|
$table.css("width", total + "px");
|
||||||
|
}
|
||||||
|
|
||||||
|
function applyColumnWidth(colIndex, newWidth) {
|
||||||
|
const w = Math.max(40, Math.round(newWidth));
|
||||||
|
savedWidths[colIndex] = w;
|
||||||
|
|
||||||
|
const $col = getColgroup().find("col").eq(colIndex);
|
||||||
|
if ($col.length) {
|
||||||
|
$col.css("width", w + "px");
|
||||||
|
}
|
||||||
|
|
||||||
|
// Aggiorna larghezza totale tabella senza toccare le altre colonne
|
||||||
|
const thCount = $("#partsTable thead tr:first th").length;
|
||||||
|
const total = savedWidths.slice(0, thCount).reduce((a, b) => a + b, 0);
|
||||||
|
$("#partsTable").css("width", total + "px");
|
||||||
|
}
|
||||||
|
|
||||||
|
function addResizers() {
|
||||||
|
const $table = $("#partsTable");
|
||||||
|
if (!$table.length) return;
|
||||||
|
|
||||||
|
$table.find("thead tr:first th").each(function (colIndex) {
|
||||||
|
const $th = $(this);
|
||||||
|
|
||||||
|
// Salta colonna Num (indice 0) — non ridimensionabile
|
||||||
|
if (colIndex === 0) return;
|
||||||
|
|
||||||
|
// Non aggiungere due volte
|
||||||
|
if ($th.find(".parts-resizer").length) return;
|
||||||
|
|
||||||
|
const $resizer = $("<span class='parts-resizer'></span>");
|
||||||
|
$th.css("position", "relative"); // necessario per il posizionamento assoluto
|
||||||
|
$th.append($resizer);
|
||||||
|
|
||||||
|
$resizer.on("mousedown", function (e) {
|
||||||
|
e.preventDefault();
|
||||||
|
e.stopPropagation();
|
||||||
|
|
||||||
|
const startX = e.pageX;
|
||||||
|
// Leggi la larghezza ESATTA dalla <col>, non dal <th>
|
||||||
|
const startWidth =
|
||||||
|
savedWidths[colIndex] !== undefined
|
||||||
|
? savedWidths[colIndex]
|
||||||
|
: parseInt(
|
||||||
|
getColgroup()
|
||||||
|
.find("col")
|
||||||
|
.eq(colIndex)
|
||||||
|
.css("width"),
|
||||||
|
10,
|
||||||
|
) || 150;
|
||||||
|
|
||||||
|
$("body")
|
||||||
|
.css("user-select", "none")
|
||||||
|
.css("cursor", "col-resize");
|
||||||
|
|
||||||
|
$(document).on("mousemove.partsResize", function (ev) {
|
||||||
|
const delta = ev.pageX - startX;
|
||||||
|
applyColumnWidth(colIndex, startWidth + delta);
|
||||||
|
});
|
||||||
|
|
||||||
|
$(document).on("mouseup.partsResize", function () {
|
||||||
|
$("body").css("user-select", "").css("cursor", "");
|
||||||
|
$(document).off(".partsResize");
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function init() {
|
||||||
|
syncColgroupToHeaders();
|
||||||
|
addResizers();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Init al primo show del modale
|
||||||
|
$(document).on("shown.bs.modal", "#partsModal", function () {
|
||||||
|
setTimeout(init, 120);
|
||||||
|
});
|
||||||
|
|
||||||
|
// Re-init dopo ogni AJAX (nuove righe, caricamenti)
|
||||||
|
$(document).ajaxComplete(function () {
|
||||||
|
if ($("#partsModal").hasClass("show")) {
|
||||||
|
setTimeout(syncColgroupToHeaders, 200);
|
||||||
|
setTimeout(addResizers, 220);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// Re-init dopo aggiunta/rimozione righe
|
||||||
|
$(document).on(
|
||||||
|
"click",
|
||||||
|
".add-row-global, .add-mix-global, .add-mix-row, .remove-row, #renumberPartsBtn, #clonePartsBtn",
|
||||||
|
function () {
|
||||||
|
setTimeout(syncColgroupToHeaders, 120);
|
||||||
|
setTimeout(addResizers, 140);
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
|
window.initPartsResizableColumns = init;
|
||||||
|
window.applyPartsColumnWidths = syncColgroupToHeaders;
|
||||||
|
})();
|
||||||
|
|||||||
@ -1,5 +1,5 @@
|
|||||||
document.addEventListener("DOMContentLoaded", function () {
|
document.addEventListener("DOMContentLoaded", function () {
|
||||||
// Funzione per caricare il contenuto del popup
|
// Funzione per caricare il contenuto del popup (exported for external use)
|
||||||
async function loadPopupContent(iddatadb, idquotations) {
|
async function loadPopupContent(iddatadb, idquotations) {
|
||||||
const popupContent = document.getElementById("popupContent");
|
const popupContent = document.getElementById("popupContent");
|
||||||
if (!popupContent) {
|
if (!popupContent) {
|
||||||
@ -475,6 +475,11 @@ document.addEventListener("DOMContentLoaded", function () {
|
|||||||
);
|
);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (canvas) {
|
||||||
|
canvas.dispose();
|
||||||
|
}
|
||||||
|
|
||||||
canvas = new fabric.Canvas("collageCanvas", {
|
canvas = new fabric.Canvas("collageCanvas", {
|
||||||
backgroundColor: "#fff",
|
backgroundColor: "#fff",
|
||||||
selection: true,
|
selection: true,
|
||||||
@ -583,6 +588,7 @@ document.addEventListener("DOMContentLoaded", function () {
|
|||||||
if (isCropping || isRemovingBackground) {
|
if (isCropping || isRemovingBackground) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const state = JSON.stringify(
|
const state = JSON.stringify(
|
||||||
canvas.toJSON([
|
canvas.toJSON([
|
||||||
"cornerColor",
|
"cornerColor",
|
||||||
@ -590,12 +596,16 @@ document.addEventListener("DOMContentLoaded", function () {
|
|||||||
"cornerSize",
|
"cornerSize",
|
||||||
"borderColor",
|
"borderColor",
|
||||||
"transparentCorners",
|
"transparentCorners",
|
||||||
|
"backgroundImage",
|
||||||
]),
|
]),
|
||||||
);
|
);
|
||||||
|
|
||||||
history.push(state);
|
history.push(state);
|
||||||
|
|
||||||
if (history.length > maxHistory) {
|
if (history.length > maxHistory) {
|
||||||
history.shift();
|
history.shift();
|
||||||
}
|
}
|
||||||
|
|
||||||
updateButtons();
|
updateButtons();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -603,10 +613,11 @@ document.addEventListener("DOMContentLoaded", function () {
|
|||||||
if (history.length <= 1) {
|
if (history.length <= 1) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
history.pop();
|
history.pop();
|
||||||
const previousState = history[history.length - 1];
|
const previousState = history[history.length - 1];
|
||||||
|
|
||||||
if (previousState) {
|
if (previousState) {
|
||||||
canvas.clear();
|
|
||||||
canvas.loadFromJSON(previousState, () => {
|
canvas.loadFromJSON(previousState, () => {
|
||||||
canvas.renderAll();
|
canvas.renderAll();
|
||||||
updateLayersPanel();
|
updateLayersPanel();
|
||||||
@ -615,8 +626,11 @@ document.addEventListener("DOMContentLoaded", function () {
|
|||||||
} else {
|
} else {
|
||||||
console.warn("Nessuno stato precedente disponibile");
|
console.warn("Nessuno stato precedente disponibile");
|
||||||
canvas.clear();
|
canvas.clear();
|
||||||
canvas.setBackgroundColor("#fff");
|
canvas.backgroundImage = null;
|
||||||
canvas.renderAll();
|
canvas.setBackgroundColor(
|
||||||
|
"#fff",
|
||||||
|
canvas.renderAll.bind(canvas),
|
||||||
|
);
|
||||||
updateLayersPanel();
|
updateLayersPanel();
|
||||||
updateButtons();
|
updateButtons();
|
||||||
}
|
}
|
||||||
@ -909,6 +923,61 @@ document.addEventListener("DOMContentLoaded", function () {
|
|||||||
canvas.renderAll();
|
canvas.renderAll();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function setCanvasBackground(imgPath) {
|
||||||
|
console.log("setCanvasBackground chiamata con:", imgPath);
|
||||||
|
|
||||||
|
if (!canvas) {
|
||||||
|
console.error("Canvas non inizializzato");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
fabric.Image.fromURL(
|
||||||
|
imgPath,
|
||||||
|
(img) => {
|
||||||
|
const canvasWidth = canvas.getWidth();
|
||||||
|
const canvasHeight = canvas.getHeight();
|
||||||
|
|
||||||
|
const imgWidth = img.width;
|
||||||
|
const imgHeight = img.height;
|
||||||
|
|
||||||
|
if (!imgWidth || !imgHeight) {
|
||||||
|
alert(
|
||||||
|
"Impossibile leggere le dimensioni dell'immagine.",
|
||||||
|
);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Scale to cover: l'immagine copre tutto il canvas mantenendo il ratio
|
||||||
|
const scale = Math.max(
|
||||||
|
canvasWidth / imgWidth,
|
||||||
|
canvasHeight / imgHeight,
|
||||||
|
);
|
||||||
|
|
||||||
|
img.set({
|
||||||
|
originX: "left",
|
||||||
|
originY: "top",
|
||||||
|
scaleX: scale,
|
||||||
|
scaleY: scale,
|
||||||
|
left: (canvasWidth - imgWidth * scale) / 2,
|
||||||
|
top: (canvasHeight - imgHeight * scale) / 2,
|
||||||
|
selectable: false,
|
||||||
|
evented: false,
|
||||||
|
hasControls: false,
|
||||||
|
hasBorders: false,
|
||||||
|
excludeFromExport: false,
|
||||||
|
});
|
||||||
|
|
||||||
|
canvas.setBackgroundImage(img, () => {
|
||||||
|
canvas.requestRenderAll();
|
||||||
|
saveCanvasState();
|
||||||
|
updateLayersPanel();
|
||||||
|
updateButtons();
|
||||||
|
});
|
||||||
|
},
|
||||||
|
{ crossOrigin: "anonymous" },
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
const addToCanvasBtn = document.getElementById("addToCanvasBtn");
|
const addToCanvasBtn = document.getElementById("addToCanvasBtn");
|
||||||
if (addToCanvasBtn) {
|
if (addToCanvasBtn) {
|
||||||
addToCanvasBtn.addEventListener("click", () => {
|
addToCanvasBtn.addEventListener("click", () => {
|
||||||
@ -948,6 +1017,32 @@ document.addEventListener("DOMContentLoaded", function () {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const setBackgroundBtn = document.getElementById("setBackgroundBtn");
|
||||||
|
if (setBackgroundBtn) {
|
||||||
|
setBackgroundBtn.addEventListener("click", () => {
|
||||||
|
const checkboxes = document.querySelectorAll(
|
||||||
|
".photo-checkbox:checked",
|
||||||
|
);
|
||||||
|
|
||||||
|
if (checkboxes.length === 0) {
|
||||||
|
alert("Seleziona una foto da usare come sfondo!");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (checkboxes.length > 1) {
|
||||||
|
alert("Seleziona una sola foto per lo sfondo!");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const imgPath = checkboxes[0].getAttribute("data-path");
|
||||||
|
setCanvasBackground(imgPath);
|
||||||
|
|
||||||
|
checkboxes.forEach((cb) => {
|
||||||
|
cb.checked = false;
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
const saveCollageBtn = document.getElementById("saveCollageBtn");
|
const saveCollageBtn = document.getElementById("saveCollageBtn");
|
||||||
if (saveCollageBtn) {
|
if (saveCollageBtn) {
|
||||||
saveCollageBtn.addEventListener("click", async () => {
|
saveCollageBtn.addEventListener("click", async () => {
|
||||||
@ -1123,4 +1218,7 @@ document.addEventListener("DOMContentLoaded", function () {
|
|||||||
closeBtn,
|
closeBtn,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Export for external use (gridData pages)
|
||||||
|
window.loadPopupContent = loadPopupContent;
|
||||||
});
|
});
|
||||||
|
|||||||
@ -298,8 +298,11 @@ $result->saveToFile($qrCodeFile);
|
|||||||
<?php endforeach; ?>
|
<?php endforeach; ?>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- Bottone per aggiungere selezionate al canvas -->
|
<!-- Bottoni azioni collage -->
|
||||||
<button id="addToCanvasBtn">Aggiungi Selezionate al Canvas</button>
|
<div style="display: flex; gap: 10px; flex-wrap: wrap; margin-bottom: 10px;">
|
||||||
|
<button id="addToCanvasBtn">Aggiungi Selezionate al Canvas</button>
|
||||||
|
<button id="setBackgroundBtn" type="button">Imposta Selezionata come Sfondo</button>
|
||||||
|
</div>
|
||||||
|
|
||||||
<!-- Canvas per editing -->
|
<!-- Canvas per editing -->
|
||||||
<canvas id="collageCanvas" width="960" height="720" style="border: 1px solid #ccc; margin-top: 20px;"></canvas>
|
<canvas id="collageCanvas" width="960" height="720" style="border: 1px solid #ccc; margin-top: 20px;"></canvas>
|
||||||
|
|||||||
36
public/userarea/ping_lims_api.php
Normal file
36
public/userarea/ping_lims_api.php
Normal file
@ -0,0 +1,36 @@
|
|||||||
|
<?php
|
||||||
|
require_once dirname(__DIR__, 2) . '/vendor/autoload.php';
|
||||||
|
require_once dirname(__FILE__) . '/class/VisualLimsApiClient.class.php';
|
||||||
|
|
||||||
|
header('Content-Type: application/json; charset=utf-8');
|
||||||
|
|
||||||
|
ini_set('display_errors', '0');
|
||||||
|
error_reporting(E_ALL);
|
||||||
|
set_time_limit(20);
|
||||||
|
|
||||||
|
try {
|
||||||
|
$api = VisualLimsApiClient::getInstance();
|
||||||
|
|
||||||
|
$start = microtime(true);
|
||||||
|
|
||||||
|
// Chiamata minima: 1 solo record, 1 solo campo
|
||||||
|
$data = $api->get('Rapporto', [
|
||||||
|
'$top' => 1,
|
||||||
|
'$select' => 'IdRapporto'
|
||||||
|
]);
|
||||||
|
|
||||||
|
$elapsed = round(microtime(true) - $start, 3);
|
||||||
|
|
||||||
|
echo json_encode([
|
||||||
|
'success' => true,
|
||||||
|
'elapsed_seconds' => $elapsed,
|
||||||
|
'data' => $data
|
||||||
|
], JSON_PRETTY_PRINT | JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES);
|
||||||
|
} catch (Exception $e) {
|
||||||
|
http_response_code(500);
|
||||||
|
|
||||||
|
echo json_encode([
|
||||||
|
'success' => false,
|
||||||
|
'error' => $e->getMessage()
|
||||||
|
], JSON_PRETTY_PRINT | JSON_UNESCAPED_UNICODE);
|
||||||
|
}
|
||||||
@ -9,46 +9,76 @@ try {
|
|||||||
throw new Exception("Invalid request method.");
|
throw new Exception("Invalid request method.");
|
||||||
}
|
}
|
||||||
|
|
||||||
// Recupera e sanifica i dati
|
// Retrieve and sanitize form data
|
||||||
$id = intval($_POST['id']);
|
$id = intval($_POST['id'] ?? 0);
|
||||||
$name = trim($_POST['name']);
|
$name = trim($_POST['name'] ?? '');
|
||||||
$header_row = intval($_POST['header_row']);
|
$source_type = strtoupper(trim($_POST['source_type'] ?? 'XLS'));
|
||||||
$start_column = trim($_POST['start_column']);
|
$header_row = isset($_POST['header_row']) && $_POST['header_row'] !== '' ? intval($_POST['header_row']) : null;
|
||||||
|
$start_column = trim($_POST['start_column'] ?? '');
|
||||||
$description = trim($_POST['description'] ?? '');
|
$description = trim($_POST['description'] ?? '');
|
||||||
$target_table = trim($_POST['target_table']);
|
$target_table = trim($_POST['target_table'] ?? 'datadb');
|
||||||
$idclient = intval($_POST['client_id'] ?? 0); // Usa client_id dal form
|
$idclient = intval($_POST['client_id'] ?? 0);
|
||||||
$clientname = trim($_POST['client_name'] ?? ''); // Usa client_name dal form
|
$clientname = trim($_POST['client_name'] ?? '');
|
||||||
$idschema = intval($_POST['idschema'] ?? 0); // Nuovo campo
|
$idschema = intval($_POST['idschema'] ?? 0);
|
||||||
$schemaname = trim($_POST['schemaname'] ?? ''); // Corretto da schemamaname
|
$schemaname = trim($_POST['schemaname'] ?? '');
|
||||||
$idroutine = isset($_POST['idroutine']) && $_POST['idroutine'] !== '' ? intval($_POST['idroutine']) : null; // Aggiunto idroutine
|
$idroutine = isset($_POST['idroutine']) && $_POST['idroutine'] !== '' ? intval($_POST['idroutine']) : null;
|
||||||
$button_size = trim($_POST['button_size'] ?? 'medium'); // Nuovo campo
|
$button_size = trim($_POST['button_size'] ?? 'medium');
|
||||||
$button_bg_color = trim($_POST['button_bg_color'] ?? '#007bff'); // Nuovo campo
|
$button_bg_color = trim($_POST['button_bg_color'] ?? '#007bff');
|
||||||
$button_text_color = trim($_POST['button_text_color'] ?? '#ffffff'); // Nuovo campo
|
$button_text_color = trim($_POST['button_text_color'] ?? '#ffffff');
|
||||||
$button_label = trim($_POST['button_label'] ?? 'Click Me'); // Nuovo campo
|
$button_label = trim($_POST['button_label'] ?? 'Click Me');
|
||||||
|
|
||||||
// Controllo sui campi obbligatori
|
if (!in_array($source_type, ['XLS', 'API'], true)) {
|
||||||
if (empty($id) || empty($name) || empty($header_row) || empty($start_column) || empty($target_table) || $idschema <= 0) {
|
$source_type = 'XLS';
|
||||||
throw new Exception("All fields marked with * are required, including schema.");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Validazione del idclient
|
// Required fields validation
|
||||||
if ($idclient <= 0) {
|
if ($id <= 0 || $name === '' || $target_table === '' || $idclient <= 0 || $idschema <= 0) {
|
||||||
throw new Exception("Please select a valid client.");
|
throw new Exception("All fields marked with * are required, including client and schema.");
|
||||||
}
|
}
|
||||||
|
|
||||||
// Connessione al database
|
// XLS-only validation
|
||||||
|
if ($source_type === 'XLS') {
|
||||||
|
if ($header_row === null || $header_row <= 0 || $start_column === '') {
|
||||||
|
throw new Exception("Header Row and Start Column are required for XLS templates.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// API templates do not require XLS coordinates
|
||||||
|
if ($source_type === 'API') {
|
||||||
|
$header_row = null;
|
||||||
|
$start_column = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Database connection
|
||||||
$db = DBHandlerSelect::getInstance();
|
$db = DBHandlerSelect::getInstance();
|
||||||
$pdo = $db->getConnection();
|
$pdo = $db->getConnection();
|
||||||
|
|
||||||
// Aggiorna il database, includendo i nuovi campi
|
// Update template
|
||||||
$stmt = $pdo->prepare("UPDATE excel_templates
|
$stmt = $pdo->prepare("
|
||||||
SET name = ?, header_row = ?, start_column = ?, description = ?, target_table = ?,
|
UPDATE excel_templates
|
||||||
idclient = ?, clientname = ?, schemaname = ?, idschema = ?, idroutine = ?,
|
SET
|
||||||
button_size = ?, button_bg_color = ?, button_text_color = ?, button_label = ?,
|
name = ?,
|
||||||
updated_at = NOW()
|
source_type = ?,
|
||||||
WHERE id = ?");
|
header_row = ?,
|
||||||
|
start_column = ?,
|
||||||
|
description = ?,
|
||||||
|
target_table = ?,
|
||||||
|
idclient = ?,
|
||||||
|
clientname = ?,
|
||||||
|
schemaname = ?,
|
||||||
|
idschema = ?,
|
||||||
|
idroutine = ?,
|
||||||
|
button_size = ?,
|
||||||
|
button_bg_color = ?,
|
||||||
|
button_text_color = ?,
|
||||||
|
button_label = ?,
|
||||||
|
updated_at = NOW()
|
||||||
|
WHERE id = ?
|
||||||
|
");
|
||||||
|
|
||||||
$stmt->execute([
|
$stmt->execute([
|
||||||
$name,
|
$name,
|
||||||
|
$source_type,
|
||||||
$header_row,
|
$header_row,
|
||||||
$start_column,
|
$start_column,
|
||||||
$description,
|
$description,
|
||||||
@ -65,12 +95,10 @@ try {
|
|||||||
$id
|
$id
|
||||||
]);
|
]);
|
||||||
|
|
||||||
// rowCount potrebbe essere 0 se non ci sono modifiche, quindi consideriamo comunque un successo
|
|
||||||
$response["success"] = true;
|
$response["success"] = true;
|
||||||
$response["message"] = "Template updated successfully!";
|
$response["message"] = "Template updated successfully!";
|
||||||
} catch (Exception $e) {
|
} catch (Exception $e) {
|
||||||
$response["message"] = $e->getMessage();
|
$response["message"] = $e->getMessage();
|
||||||
}
|
}
|
||||||
|
|
||||||
// Restituisce un JSON per il fetch
|
|
||||||
echo json_encode($response);
|
echo json_encode($response);
|
||||||
|
|||||||
@ -10,6 +10,9 @@ session_start();
|
|||||||
// Includi PHPSpreadsheet
|
// Includi PHPSpreadsheet
|
||||||
require_once '../../vendor/autoload.php';
|
require_once '../../vendor/autoload.php';
|
||||||
|
|
||||||
|
Dotenv\Dotenv::createImmutable(dirname(__DIR__, 2))->safeLoad();
|
||||||
|
date_default_timezone_set($_ENV['APP_TIMEZONE'] ?? 'Europe/Rome');
|
||||||
|
|
||||||
$response = ['error' => '', 'rows' => [], 'columns' => [], 'template_id' => 0, 'filename' => ''];
|
$response = ['error' => '', 'rows' => [], 'columns' => [], 'template_id' => 0, 'filename' => ''];
|
||||||
|
|
||||||
try {
|
try {
|
||||||
@ -70,7 +73,7 @@ try {
|
|||||||
$columnLetter = \PhpOffice\PhpSpreadsheet\Cell\Coordinate::stringFromColumnIndex($col);
|
$columnLetter = \PhpOffice\PhpSpreadsheet\Cell\Coordinate::stringFromColumnIndex($col);
|
||||||
$cell = $worksheet->getCell($columnLetter . $header_row);
|
$cell = $worksheet->getCell($columnLetter . $header_row);
|
||||||
$cellValue = $cell ? $cell->getCalculatedValue() : ''; // Usa getCalculatedValue per le formule
|
$cellValue = $cell ? $cell->getCalculatedValue() : ''; // Usa getCalculatedValue per le formule
|
||||||
$headerRowData[] = htmlspecialchars($cellValue ?: '');
|
$headerRowData[] = $cellValue ?: '';
|
||||||
}
|
}
|
||||||
|
|
||||||
// Estrai i dati a partire dalla riga successiva
|
// Estrai i dati a partire dalla riga successiva
|
||||||
@ -80,7 +83,7 @@ try {
|
|||||||
$columnLetter = \PhpOffice\PhpSpreadsheet\Cell\Coordinate::stringFromColumnIndex($col);
|
$columnLetter = \PhpOffice\PhpSpreadsheet\Cell\Coordinate::stringFromColumnIndex($col);
|
||||||
$cell = $worksheet->getCell($columnLetter . $row);
|
$cell = $worksheet->getCell($columnLetter . $row);
|
||||||
$cellValue = $cell ? $cell->getCalculatedValue() : ''; // Usa getCalculatedValue per le formule
|
$cellValue = $cell ? $cell->getCalculatedValue() : ''; // Usa getCalculatedValue per le formule
|
||||||
$rowData[] = htmlspecialchars($cellValue ?: '');
|
$rowData[] = $cellValue ?: '';
|
||||||
}
|
}
|
||||||
if (!empty(array_filter($rowData))) {
|
if (!empty(array_filter($rowData))) {
|
||||||
$excelData[] = $rowData;
|
$excelData[] = $rowData;
|
||||||
|
|||||||
@ -74,6 +74,16 @@ try {
|
|||||||
$startRow = max(1, $header_row);
|
$startRow = max(1, $header_row);
|
||||||
$startColumn = max(1, $start_column);
|
$startColumn = max(1, $start_column);
|
||||||
|
|
||||||
|
// Advance startColumn to first non-empty cell in header row (match JS behavior)
|
||||||
|
for ($sc = $startColumn; $sc <= $highestColumnIndex; $sc++) {
|
||||||
|
$cl = \PhpOffice\PhpSpreadsheet\Cell\Coordinate::stringFromColumnIndex($sc);
|
||||||
|
$cv = trim((string)($worksheet->getCell($cl . $header_row)->getCalculatedValue() ?? ''));
|
||||||
|
if ($cv !== '') {
|
||||||
|
$startColumn = $sc;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Debug dei parametri
|
// Debug dei parametri
|
||||||
error_log("Processing - template_id: $template_id, startRow: $startRow, startColumn: $startColumn, highestRow: $highestRow, highestColumn: $highestColumn, highestColumnIndex: $highestColumnIndex");
|
error_log("Processing - template_id: $template_id, startRow: $startRow, startColumn: $startColumn, highestRow: $highestRow, highestColumn: $highestColumn, highestColumnIndex: $highestColumnIndex");
|
||||||
|
|
||||||
@ -84,25 +94,78 @@ try {
|
|||||||
$response['error'] = "La colonna di partenza ($startColumn) supera il numero totale di colonne ($highestColumnIndex).";
|
$response['error'] = "La colonna di partenza ($startColumn) supera il numero totale di colonne ($highestColumnIndex).";
|
||||||
} else {
|
} else {
|
||||||
$excelData = [];
|
$excelData = [];
|
||||||
// Estrai la riga degli header
|
|
||||||
$headerRowData = [];
|
// Build merge map for header row: physCol -> mergeStartCol
|
||||||
for ($col = $startColumn; $col <= $highestColumnIndex; $col++) {
|
$mergeStartMap = [];
|
||||||
$columnLetter = \PhpOffice\PhpSpreadsheet\Cell\Coordinate::stringFromColumnIndex($col);
|
foreach ($worksheet->getMergeCells() as $range) {
|
||||||
$cell = $worksheet->getCell($columnLetter . $header_row);
|
[$startCell, $endCell] = explode(':', $range);
|
||||||
$cellValue = $cell ? $cell->getCalculatedValue() : '';
|
$mStartCol = \PhpOffice\PhpSpreadsheet\Cell\Coordinate::columnIndexFromString(preg_replace('/\d+/', '', $startCell));
|
||||||
$headerRowData[] = htmlspecialchars($cellValue ?: '');
|
$mEndCol = \PhpOffice\PhpSpreadsheet\Cell\Coordinate::columnIndexFromString(preg_replace('/\d+/', '', $endCell));
|
||||||
|
$mStartRow = (int)preg_replace('/[A-Z]+/i', '', $startCell);
|
||||||
|
$mEndRow = (int)preg_replace('/[A-Z]+/i', '', $endCell);
|
||||||
|
if ($header_row >= $mStartRow && $header_row <= $mEndRow) {
|
||||||
|
for ($c = $mStartCol; $c <= $mEndCol; $c++) {
|
||||||
|
$mergeStartMap[$c] = $mStartCol;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Estrai i dati a partire dalla riga successiva, includendo excelrow
|
// Build logical columns: each merge = one column
|
||||||
|
$logicalCols = []; // array of physical column indices (one per logical column)
|
||||||
|
$seen = [];
|
||||||
|
for ($col = $startColumn; $col <= $highestColumnIndex; $col++) {
|
||||||
|
if (isset($mergeStartMap[$col])) {
|
||||||
|
$ms = $mergeStartMap[$col];
|
||||||
|
if (in_array($ms, $seen, true)) continue;
|
||||||
|
$seen[] = $ms;
|
||||||
|
$logicalCols[] = $ms;
|
||||||
|
} else {
|
||||||
|
$logicalCols[] = $col;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Build header row using logical columns
|
||||||
|
$headerRowData = [];
|
||||||
|
$logicalNum = 0;
|
||||||
|
foreach ($logicalCols as $physCol) {
|
||||||
|
$logicalNum++;
|
||||||
|
$columnLetter = \PhpOffice\PhpSpreadsheet\Cell\Coordinate::stringFromColumnIndex($physCol);
|
||||||
|
$cell = $worksheet->getCell($columnLetter . $header_row);
|
||||||
|
$cellValue = trim((string)($cell ? $cell->getCalculatedValue() : ''));
|
||||||
|
$cellValue = preg_replace('/[\r\n\t]+/', ' ', $cellValue);
|
||||||
|
// Empty headers get __empty_N__ to match mapping page
|
||||||
|
$headerRowData[] = ($cellValue !== '') ? $cellValue : '__empty_' . $logicalNum . '__';
|
||||||
|
}
|
||||||
|
|
||||||
|
error_log("Logical headers: " . json_encode($headerRowData));
|
||||||
|
error_log("Logical cols (physical indices): " . json_encode($logicalCols));
|
||||||
|
|
||||||
|
// Find which logical columns have real headers
|
||||||
|
$headerFilledIndices = [];
|
||||||
|
foreach ($headerRowData as $idx => $hVal) {
|
||||||
|
if (!str_starts_with($hVal, '__empty_')) $headerFilledIndices[] = $idx;
|
||||||
|
}
|
||||||
|
$minFilled = max(1, min(2, count($headerFilledIndices)));
|
||||||
|
|
||||||
|
// Extract data rows using logical columns
|
||||||
for ($row = $startRow + 1; $row <= $highestRow; $row++) {
|
for ($row = $startRow + 1; $row <= $highestRow; $row++) {
|
||||||
$rowData = [];
|
$rowData = [];
|
||||||
for ($col = $startColumn; $col <= $highestColumnIndex; $col++) {
|
foreach ($logicalCols as $physCol) {
|
||||||
$columnLetter = \PhpOffice\PhpSpreadsheet\Cell\Coordinate::stringFromColumnIndex($col);
|
$columnLetter = \PhpOffice\PhpSpreadsheet\Cell\Coordinate::stringFromColumnIndex($physCol);
|
||||||
$cell = $worksheet->getCell($columnLetter . $row);
|
$cell = $worksheet->getCell($columnLetter . $row);
|
||||||
$cellValue = $cell ? $cell->getCalculatedValue() : '';
|
$cellValue = $cell ? $cell->getCalculatedValue() : '';
|
||||||
$rowData[] = htmlspecialchars($cellValue ?: '');
|
$rowData[] = $cellValue ?: '';
|
||||||
}
|
}
|
||||||
if (!empty(array_filter($rowData))) {
|
|
||||||
|
// Count how many header columns have data in this row
|
||||||
|
$filledCount = 0;
|
||||||
|
foreach ($headerFilledIndices as $idx) {
|
||||||
|
if (isset($rowData[$idx]) && trim((string)$rowData[$idx]) !== '') {
|
||||||
|
$filledCount++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($filledCount >= $minFilled) {
|
||||||
$excelData[] = ['data' => $rowData, 'excelrow' => $row];
|
$excelData[] = ['data' => $rowData, 'excelrow' => $row];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -9,12 +9,13 @@ try {
|
|||||||
throw new Exception("Invalid request method.");
|
throw new Exception("Invalid request method.");
|
||||||
}
|
}
|
||||||
|
|
||||||
// Recupera e sanifica i dati
|
// Retrieve and sanitize form data
|
||||||
$name = trim($_POST['name']);
|
$name = trim($_POST['name'] ?? '');
|
||||||
$header_row = intval($_POST['header_row']);
|
$source_type = strtoupper(trim($_POST['source_type'] ?? 'XLS'));
|
||||||
$start_column = trim($_POST['start_column']);
|
$header_row = isset($_POST['header_row']) && $_POST['header_row'] !== '' ? intval($_POST['header_row']) : null;
|
||||||
|
$start_column = trim($_POST['start_column'] ?? '');
|
||||||
$description = trim($_POST['description'] ?? '');
|
$description = trim($_POST['description'] ?? '');
|
||||||
$target_table = trim($_POST['target_table']);
|
$target_table = trim($_POST['target_table'] ?? 'datadb');
|
||||||
$idclient = intval($_POST['client_id'] ?? 0);
|
$idclient = intval($_POST['client_id'] ?? 0);
|
||||||
$clientname = trim($_POST['client_name'] ?? '');
|
$clientname = trim($_POST['client_name'] ?? '');
|
||||||
$idschema = intval($_POST['idschema'] ?? 0);
|
$idschema = intval($_POST['idschema'] ?? 0);
|
||||||
@ -25,24 +26,61 @@ try {
|
|||||||
$button_text_color = trim($_POST['button_text_color'] ?? '#ffffff');
|
$button_text_color = trim($_POST['button_text_color'] ?? '#ffffff');
|
||||||
$button_label = trim($_POST['button_label'] ?? 'Click Me');
|
$button_label = trim($_POST['button_label'] ?? 'Click Me');
|
||||||
|
|
||||||
// Controllo sui campi obbligatori
|
// Normalize source type
|
||||||
if (empty($name) || empty($header_row) || empty($start_column) || empty($target_table) || $idclient <= 0 || $idschema <= 0) {
|
if (!in_array($source_type, ['XLS', 'API'], true)) {
|
||||||
|
$source_type = 'XLS';
|
||||||
|
}
|
||||||
|
|
||||||
|
// Required fields validation
|
||||||
|
if ($name === '' || $target_table === '' || $idclient <= 0 || $idschema <= 0) {
|
||||||
throw new Exception("All fields marked with * are required, including client and schema.");
|
throw new Exception("All fields marked with * are required, including client and schema.");
|
||||||
}
|
}
|
||||||
|
|
||||||
// Connessione al database
|
// XLS-only validation
|
||||||
|
if ($source_type === 'XLS') {
|
||||||
|
if ($header_row === null || $header_row <= 0 || $start_column === '') {
|
||||||
|
throw new Exception("Header Row and Start Column are required for XLS templates.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// API templates do not require XLS coordinates
|
||||||
|
if ($source_type === 'API') {
|
||||||
|
$header_row = null;
|
||||||
|
$start_column = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Database connection
|
||||||
$db = DBHandlerSelect::getInstance();
|
$db = DBHandlerSelect::getInstance();
|
||||||
$pdo = $db->getConnection();
|
$pdo = $db->getConnection();
|
||||||
|
|
||||||
// Inserisci il nuovo template
|
// Insert the new template
|
||||||
$stmt = $pdo->prepare("
|
$stmt = $pdo->prepare("
|
||||||
INSERT INTO excel_templates
|
INSERT INTO excel_templates
|
||||||
(name, header_row, start_column, description, target_table, idclient, clientname, idschema, schemaname, idroutine,
|
(
|
||||||
button_size, button_bg_color, button_text_color, button_label, created_at, updated_at)
|
name,
|
||||||
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, NOW(), NOW())
|
source_type,
|
||||||
|
header_row,
|
||||||
|
start_column,
|
||||||
|
description,
|
||||||
|
target_table,
|
||||||
|
idclient,
|
||||||
|
clientname,
|
||||||
|
idschema,
|
||||||
|
schemaname,
|
||||||
|
idroutine,
|
||||||
|
button_size,
|
||||||
|
button_bg_color,
|
||||||
|
button_text_color,
|
||||||
|
button_label,
|
||||||
|
created_at,
|
||||||
|
updated_at
|
||||||
|
)
|
||||||
|
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, NOW(), NOW())
|
||||||
");
|
");
|
||||||
|
|
||||||
$stmt->execute([
|
$stmt->execute([
|
||||||
$name,
|
$name,
|
||||||
|
$source_type,
|
||||||
$header_row,
|
$header_row,
|
||||||
$start_column,
|
$start_column,
|
||||||
$description,
|
$description,
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
@ -5,102 +5,211 @@ ini_set('error_log', __DIR__ . '/routine_debug.log');
|
|||||||
function applyRoutine(&$excelData, $routineData)
|
function applyRoutine(&$excelData, $routineData)
|
||||||
{
|
{
|
||||||
try {
|
try {
|
||||||
// Log iniziale
|
// Initial log
|
||||||
error_log("Inizio esecuzione routine Moncler: " . date('Y-m-d H:i:s'));
|
error_log("Inizio esecuzione routine Moncler: " . date('Y-m-d H:i:s'));
|
||||||
error_log("Dati routine: " . print_r($routineData, true));
|
error_log("Dati routine: " . print_r($routineData, true));
|
||||||
error_log("Dati excel_data: " . print_r($excelData, true));
|
error_log("Dati excel_data: " . print_r($excelData, true));
|
||||||
|
|
||||||
// Verifica se excelData è vuoto
|
// Check if excelData is empty
|
||||||
if (empty($excelData)) {
|
if (empty($excelData)) {
|
||||||
throw new Exception("excelData è vuoto o non valido.");
|
throw new Exception("excelData è vuoto o non valido.");
|
||||||
}
|
}
|
||||||
|
|
||||||
// Estrai informazioni dalla routine con valori predefiniti
|
// Extract routine settings with default values
|
||||||
$action1 = trim($routineData['action1'] ?? 'K');
|
$action1 = trim($routineData['action1'] ?? 'K');
|
||||||
$action2 = trim($routineData['action2'] ?? 'STYLE CODE + STYLE DESCRIPTION');
|
$action2 = trim($routineData['action2'] ?? 'STYLE CODE + STYLE DESCRIPTION');
|
||||||
$action3 = trim($routineData['action3'] ?? 'STYLE CODE');
|
$action3 = trim($routineData['action3'] ?? 'STYLE CODE');
|
||||||
$action4 = trim($routineData['action4'] ?? 'STYLE DESCRIPTION');
|
$action4 = trim($routineData['action4'] ?? 'STYLE DESCRIPTION');
|
||||||
$headers = $routineData['xls_headers'] ?? [];
|
$headers = $routineData['xls_headers'] ?? [];
|
||||||
|
|
||||||
|
// Package-related headers
|
||||||
|
$package_header = 'PACKAGE';
|
||||||
|
$other_test_header = 'Other test from Control Matrix';
|
||||||
|
$only_colorfastness_header = 'Only Colorfastness package';
|
||||||
|
$only_chemical_header = 'Only Chemical package';
|
||||||
|
|
||||||
if (empty($headers)) {
|
if (empty($headers)) {
|
||||||
throw new Exception("Nessun header trovato per la routine Moncler.");
|
throw new Exception("Nessun header trovato per la routine Moncler.");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// If headers arrive as JSON string, decode them
|
||||||
|
if (!is_array($headers) && is_string($headers)) {
|
||||||
|
$decoded_headers = json_decode($headers, true);
|
||||||
|
if (json_last_error() === JSON_ERROR_NONE && is_array($decoded_headers)) {
|
||||||
|
$headers = $decoded_headers;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!is_array($headers) || empty($headers)) {
|
||||||
|
throw new Exception("Gli header della routine non sono validi.");
|
||||||
|
}
|
||||||
|
|
||||||
error_log("Header ricevuti: " . print_r($headers, true));
|
error_log("Header ricevuti: " . print_r($headers, true));
|
||||||
|
|
||||||
// Normalizza gli header (solo trim)
|
// Normalize headers
|
||||||
$normalized_headers = array_map('trim', $headers);
|
$normalized_headers = array_map(function ($header) {
|
||||||
|
return trim((string)$header);
|
||||||
|
}, $headers);
|
||||||
|
|
||||||
error_log("Header normalizzati: " . print_r($normalized_headers, true));
|
error_log("Header normalizzati: " . print_r($normalized_headers, true));
|
||||||
error_log("Action values - action1: '$action1', action2: '$action2', action3: '$action3', action4: '$action4'");
|
error_log("Action values - action1: '$action1', action2: '$action2', action3: '$action3', action4: '$action4'");
|
||||||
|
|
||||||
// Trova gli indici delle colonne
|
// Find package-related column indexes
|
||||||
$action1_index = array_search($action1, $normalized_headers);
|
$package_index = array_search($package_header, $normalized_headers, true);
|
||||||
$action2_index = array_search($action2, $normalized_headers);
|
$other_test_index = array_search($other_test_header, $normalized_headers, true);
|
||||||
$action3_index = array_search($action3, $normalized_headers);
|
$only_colorfastness_index = array_search($only_colorfastness_header, $normalized_headers, true);
|
||||||
$action4_index = array_search($action4, $normalized_headers);
|
$only_chemical_index = array_search($only_chemical_header, $normalized_headers, true);
|
||||||
|
|
||||||
|
if ($package_index === false) {
|
||||||
|
throw new Exception("Colonna PACKAGE non trovata.");
|
||||||
|
}
|
||||||
|
|
||||||
|
error_log(
|
||||||
|
"Indici package - PACKAGE: " . var_export($package_index, true) .
|
||||||
|
", Other test from Control Matrix: " . var_export($other_test_index, true) .
|
||||||
|
", Only Colorfastness package: " . var_export($only_colorfastness_index, true) .
|
||||||
|
", Only Chemical package: " . var_export($only_chemical_index, true)
|
||||||
|
);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* PART 1
|
||||||
|
* Aggregate rows by action1 and split action2 into action3 + action4
|
||||||
|
* If it fails, continue with PART 2 only
|
||||||
|
*/
|
||||||
|
$part1_applied = false;
|
||||||
|
|
||||||
|
$action1_index = array_search($action1, $normalized_headers, true);
|
||||||
|
$action2_index = array_search($action2, $normalized_headers, true);
|
||||||
|
$action3_index = array_search($action3, $normalized_headers, true);
|
||||||
|
$action4_index = array_search($action4, $normalized_headers, true);
|
||||||
|
|
||||||
if ($action1_index === false || $action2_index === false || $action3_index === false || $action4_index === false) {
|
if ($action1_index === false || $action2_index === false || $action3_index === false || $action4_index === false) {
|
||||||
throw new Exception("Colonne non trovate - action1: '$action1' (index: " . var_export($action1_index, true) . "), action2: '$action2' (index: " . var_export($action2_index, true) . "), action3: '$action3' (index: " . var_export($action3_index, true) . "), action4: '$action4' (index: " . var_export($action4_index, true) . ")");
|
error_log(
|
||||||
|
"Unable to apply routine part 1. Package merge logic has been applied only. " .
|
||||||
|
"Missing columns - action1: '$action1' (index: " . var_export($action1_index, true) . ")" .
|
||||||
|
", action2: '$action2' (index: " . var_export($action2_index, true) . ")" .
|
||||||
|
", action3: '$action3' (index: " . var_export($action3_index, true) . ")" .
|
||||||
|
", action4: '$action4' (index: " . var_export($action4_index, true) . ")"
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
error_log("Indici colonne part 1 - action1: $action1_index, action2: $action2_index, action3: $action3_index, action4: $action4_index");
|
||||||
|
|
||||||
|
$grouped_data = [];
|
||||||
|
|
||||||
|
foreach ($excelData as $row) {
|
||||||
|
if (!isset($row['data']) || !is_array($row['data'])) {
|
||||||
|
error_log("Riga non valida, manca 'data' per excelrow " . ($row['excelrow'] ?? 'N/A'));
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
$key = $row['data'][$action1_index] ?? '';
|
||||||
|
$key = trim((string)$key);
|
||||||
|
$key = $key === '' ? '_empty_' : $key;
|
||||||
|
|
||||||
|
if (!isset($grouped_data[$key])) {
|
||||||
|
$grouped_data[$key] = [
|
||||||
|
'data' => $row['data'],
|
||||||
|
'excelrow' => [($row['excelrow'] ?? '')],
|
||||||
|
'style_codes' => [],
|
||||||
|
'style_descriptions' => []
|
||||||
|
];
|
||||||
|
} else {
|
||||||
|
$grouped_data[$key]['excelrow'][] = ($row['excelrow'] ?? '');
|
||||||
|
}
|
||||||
|
|
||||||
|
// Split STYLE CODE + STYLE DESCRIPTION
|
||||||
|
$action2_value = trim((string)($row['data'][$action2_index] ?? ''));
|
||||||
|
|
||||||
|
if ($action2_value !== '') {
|
||||||
|
$parts = explode(' - ', $action2_value, 2);
|
||||||
|
$style_code = trim((string)($parts[0] ?? ''));
|
||||||
|
$style_description = trim((string)($parts[1] ?? ''));
|
||||||
|
|
||||||
|
if ($style_code !== '') {
|
||||||
|
$grouped_data[$key]['style_codes'][] = $style_code;
|
||||||
|
}
|
||||||
|
if ($style_description !== '') {
|
||||||
|
$grouped_data[$key]['style_descriptions'][] = $style_description;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
error_log("Valore vuoto in action2 per excelrow " . ($row['excelrow'] ?? 'N/A'));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
$new_excel_data = [];
|
||||||
|
|
||||||
|
foreach ($grouped_data as $key => $group) {
|
||||||
|
$row_data = $group['data'];
|
||||||
|
|
||||||
|
// Update STYLE CODE and STYLE DESCRIPTION with aggregated unique values
|
||||||
|
$row_data[$action3_index] = implode(' - ', array_unique($group['style_codes']));
|
||||||
|
$row_data[$action4_index] = implode(' - ', array_unique($group['style_descriptions']));
|
||||||
|
|
||||||
|
// Concatenate excelrow values with "+"
|
||||||
|
$excelrow_clean = array_filter($group['excelrow'], function ($value) {
|
||||||
|
return $value !== null && $value !== '';
|
||||||
|
});
|
||||||
|
|
||||||
|
$excelrow_value = count($excelrow_clean) > 1
|
||||||
|
? implode('+', $excelrow_clean)
|
||||||
|
: (reset($excelrow_clean) ?: '');
|
||||||
|
|
||||||
|
$new_excel_data[] = [
|
||||||
|
'data' => $row_data,
|
||||||
|
'excelrow' => $excelrow_value
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
$excelData = $new_excel_data;
|
||||||
|
$part1_applied = true;
|
||||||
|
|
||||||
|
error_log("Routine part 1 completata - Righe aggregate: " . count($new_excel_data));
|
||||||
|
error_log("Excelrow aggregati part 1: " . print_r(array_column($new_excel_data, 'excelrow'), true));
|
||||||
}
|
}
|
||||||
|
|
||||||
error_log("Indici colonne - action1: $action1_index, action2: $action2_index, action3: $action3_index, action4: $action4_index");
|
/**
|
||||||
|
* PART 2
|
||||||
// Raggruppa le righe per il valore in action1 (colonna K)
|
* Merge package-related columns into PACKAGE
|
||||||
$grouped_data = [];
|
* Always applied if PACKAGE exists
|
||||||
foreach ($excelData as $row) {
|
*/
|
||||||
|
foreach ($excelData as $index => $row) {
|
||||||
if (!isset($row['data']) || !is_array($row['data'])) {
|
if (!isset($row['data']) || !is_array($row['data'])) {
|
||||||
error_log("Riga non valida, manca 'data' per excelrow {$row['excelrow']}");
|
error_log("Riga non valida nella part 2, manca 'data' all'indice $index");
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
$key = $row['data'][$action1_index] ?? '';
|
|
||||||
$key = empty($key) ? '_empty_' : $key;
|
$package_values = [];
|
||||||
if (!isset($grouped_data[$key])) {
|
|
||||||
$grouped_data[$key] = [
|
$value_package = trim((string)($row['data'][$package_index] ?? ''));
|
||||||
'data' => $row['data'],
|
$value_other_test = $other_test_index !== false ? trim((string)($row['data'][$other_test_index] ?? '')) : '';
|
||||||
'excelrow' => [$row['excelrow']],
|
$value_only_colorfastness = $only_colorfastness_index !== false ? trim((string)($row['data'][$only_colorfastness_index] ?? '')) : '';
|
||||||
'style_codes' => [],
|
$value_only_chemical = $only_chemical_index !== false ? trim((string)($row['data'][$only_chemical_index] ?? '')) : '';
|
||||||
'style_descriptions' => []
|
|
||||||
];
|
if ($value_package !== '') {
|
||||||
} else {
|
$package_values[] = $value_package;
|
||||||
$grouped_data[$key]['excelrow'][] = $row['excelrow'];
|
}
|
||||||
|
if ($value_other_test !== '') {
|
||||||
|
$package_values[] = $value_other_test;
|
||||||
|
}
|
||||||
|
if ($value_only_colorfastness !== '') {
|
||||||
|
$package_values[] = $value_only_colorfastness;
|
||||||
|
}
|
||||||
|
if ($value_only_chemical !== '') {
|
||||||
|
$package_values[] = $value_only_chemical;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Separa il valore in action2 (STYLE CODE + STYLE DESCRIPTION)
|
$package_values = array_unique($package_values);
|
||||||
$action2_value = $row['data'][$action2_index] ?? '';
|
|
||||||
if (!empty($action2_value)) {
|
$excelData[$index]['data'][$package_index] = implode(' - ', $package_values);
|
||||||
$parts = explode(' - ', trim($action2_value));
|
|
||||||
$style_code = $parts[0] ?? '';
|
|
||||||
$style_description = $parts[1] ?? '';
|
|
||||||
if (!empty($style_code)) {
|
|
||||||
$grouped_data[$key]['style_codes'][] = $style_code;
|
|
||||||
}
|
|
||||||
if (!empty($style_description)) {
|
|
||||||
$grouped_data[$key]['style_descriptions'][] = $style_description;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
error_log("Valore vuoto in action2 per excelrow {$row['excelrow']}");
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Crea il nuovo array excel_data aggregato
|
error_log("Routine part 2 completata - Merge package applicato su " . count($excelData) . " righe");
|
||||||
$new_excel_data = [];
|
|
||||||
foreach ($grouped_data as $key => $group) {
|
if (!$part1_applied) {
|
||||||
$row_data = $group['data'];
|
error_log("Warning finale: routine part 1 non applicata, routine part 2 applicata correttamente");
|
||||||
// Aggiorna action3 (STYLE CODE) e action4 (STYLE DESCRIPTION) con valori aggregati
|
|
||||||
$row_data[$action3_index] = implode(' - ', array_unique($group['style_codes']));
|
|
||||||
$row_data[$action4_index] = implode(' - ', array_unique($group['style_descriptions']));
|
|
||||||
// Concatena gli excelrow con '+' per le righe aggregate
|
|
||||||
$excelrow_value = count($group['excelrow']) > 1 ? implode('+', $group['excelrow']) : $group['excelrow'][0];
|
|
||||||
$new_excel_data[] = [
|
|
||||||
'data' => $row_data,
|
|
||||||
'excelrow' => $excelrow_value
|
|
||||||
];
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Modifica excelData in-place
|
error_log("Routine Moncler completata con successo");
|
||||||
$excelData = $new_excel_data;
|
|
||||||
|
|
||||||
error_log("Routine Moncler completata - Righe aggregate: " . count($new_excel_data));
|
|
||||||
error_log("Excelrow aggregati: " . print_r(array_column($new_excel_data, 'excelrow'), true));
|
|
||||||
} catch (Exception $e) {
|
} catch (Exception $e) {
|
||||||
error_log("Eccezione nella routine Moncler: " . $e->getMessage());
|
error_log("Eccezione nella routine Moncler: " . $e->getMessage());
|
||||||
throw $e;
|
throw $e;
|
||||||
|
|||||||
327
public/userarea/routines/moncler_new_routine.php
Normal file
327
public/userarea/routines/moncler_new_routine.php
Normal file
@ -0,0 +1,327 @@
|
|||||||
|
<?php
|
||||||
|
ini_set('log_errors', 1);
|
||||||
|
ini_set('error_log', __DIR__ . '/routine_debug.log');
|
||||||
|
|
||||||
|
function applyRoutine(&$excelData, $routineData)
|
||||||
|
{
|
||||||
|
try {
|
||||||
|
// Initial log
|
||||||
|
error_log("Inizio esecuzione routine Moncler: " . date('Y-m-d H:i:s'));
|
||||||
|
error_log("Dati routine: " . print_r($routineData, true));
|
||||||
|
error_log("Dati excel_data: " . print_r($excelData, true));
|
||||||
|
|
||||||
|
// Check if excelData is empty
|
||||||
|
if (empty($excelData)) {
|
||||||
|
throw new Exception("excelData è vuoto o non valido.");
|
||||||
|
}
|
||||||
|
|
||||||
|
// Extract routine settings with default values
|
||||||
|
$action1 = trim($routineData['action1'] ?? 'K');
|
||||||
|
$action2 = trim($routineData['action2'] ?? 'STYLE CODE + STYLE DESCRIPTION');
|
||||||
|
$action3 = trim($routineData['action3'] ?? 'STYLE CODE');
|
||||||
|
$action4 = trim($routineData['action4'] ?? 'STYLE DESCRIPTION');
|
||||||
|
$headers = $routineData['xls_headers'] ?? [];
|
||||||
|
|
||||||
|
// Package-related headers
|
||||||
|
$package_header = 'PACKAGE';
|
||||||
|
$other_test_header = 'Other test from Control Matrix';
|
||||||
|
$only_colorfastness_header = 'Only Colorfastness package';
|
||||||
|
$only_chemical_header = 'Only Chemical package';
|
||||||
|
|
||||||
|
if (empty($headers)) {
|
||||||
|
throw new Exception("Nessun header trovato per la routine Moncler.");
|
||||||
|
}
|
||||||
|
|
||||||
|
// If headers arrive as JSON string, decode them
|
||||||
|
if (!is_array($headers) && is_string($headers)) {
|
||||||
|
$decoded_headers = json_decode($headers, true);
|
||||||
|
if (json_last_error() === JSON_ERROR_NONE && is_array($decoded_headers)) {
|
||||||
|
$headers = $decoded_headers;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!is_array($headers) || empty($headers)) {
|
||||||
|
throw new Exception("Gli header della routine non sono validi.");
|
||||||
|
}
|
||||||
|
|
||||||
|
error_log("Header ricevuti: " . print_r($headers, true));
|
||||||
|
|
||||||
|
// Normalize headers
|
||||||
|
$normalized_headers = array_map(function ($header) {
|
||||||
|
return trim((string)$header);
|
||||||
|
}, $headers);
|
||||||
|
|
||||||
|
error_log("Header normalizzati: " . print_r($normalized_headers, true));
|
||||||
|
error_log("Action values - action1: '$action1', action2: '$action2', action3: '$action3', action4: '$action4'");
|
||||||
|
|
||||||
|
// Find main indexes
|
||||||
|
$action1_index = array_search($action1, $normalized_headers, true);
|
||||||
|
$action2_index = array_search($action2, $normalized_headers, true);
|
||||||
|
$action3_index = array_search($action3, $normalized_headers, true);
|
||||||
|
$action4_index = array_search($action4, $normalized_headers, true);
|
||||||
|
|
||||||
|
// Find package-related column indexes
|
||||||
|
$package_index = array_search($package_header, $normalized_headers, true);
|
||||||
|
$other_test_index = array_search($other_test_header, $normalized_headers, true);
|
||||||
|
$only_colorfastness_index = array_search($only_colorfastness_header, $normalized_headers, true);
|
||||||
|
$only_chemical_index = array_search($only_chemical_header, $normalized_headers, true);
|
||||||
|
|
||||||
|
if ($package_index === false) {
|
||||||
|
throw new Exception("Colonna PACKAGE non trovata.");
|
||||||
|
}
|
||||||
|
|
||||||
|
error_log(
|
||||||
|
"Indici colonne - action1: " . var_export($action1_index, true) .
|
||||||
|
", action2: " . var_export($action2_index, true) .
|
||||||
|
", action3: " . var_export($action3_index, true) .
|
||||||
|
", action4: " . var_export($action4_index, true) .
|
||||||
|
", PACKAGE: " . var_export($package_index, true) .
|
||||||
|
", Other test from Control Matrix: " . var_export($other_test_index, true) .
|
||||||
|
", Only Colorfastness package: " . var_export($only_colorfastness_index, true) .
|
||||||
|
", Only Chemical package: " . var_export($only_chemical_index, true)
|
||||||
|
);
|
||||||
|
|
||||||
|
// Helper: split a value already joined by " - "
|
||||||
|
$splitJoinedValues = function ($value) {
|
||||||
|
$value = trim((string)$value);
|
||||||
|
if ($value === '') {
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
|
||||||
|
$parts = preg_split('/\s*-\s*/', $value);
|
||||||
|
$parts = array_map('trim', $parts);
|
||||||
|
$parts = array_filter($parts, function ($item) {
|
||||||
|
return $item !== '';
|
||||||
|
});
|
||||||
|
|
||||||
|
return array_values(array_unique($parts));
|
||||||
|
};
|
||||||
|
|
||||||
|
// Helper: join unique values with " - "
|
||||||
|
$joinUniqueValues = function (array $values) {
|
||||||
|
$clean = [];
|
||||||
|
foreach ($values as $value) {
|
||||||
|
$value = trim((string)$value);
|
||||||
|
if ($value !== '' && !in_array($value, $clean, true)) {
|
||||||
|
$clean[] = $value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return implode(' - ', $clean);
|
||||||
|
};
|
||||||
|
|
||||||
|
// Helper: split STYLE CODE + STYLE DESCRIPTION into code + description
|
||||||
|
$splitStyleCombined = function ($value) {
|
||||||
|
$value = trim((string)$value);
|
||||||
|
|
||||||
|
if ($value === '') {
|
||||||
|
return ['', ''];
|
||||||
|
}
|
||||||
|
|
||||||
|
// Accept both "CODE - DESC" and "CODE-DESC"
|
||||||
|
$parts = preg_split('/\s*-\s*/', $value, 2);
|
||||||
|
|
||||||
|
$styleCode = trim((string)($parts[0] ?? ''));
|
||||||
|
$styleDescription = trim((string)($parts[1] ?? ''));
|
||||||
|
|
||||||
|
return [$styleCode, $styleDescription];
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* STEP 0
|
||||||
|
* Normalize STYLE CODE and STYLE DESCRIPTION before grouping by K
|
||||||
|
*
|
||||||
|
* Rules:
|
||||||
|
* - Read STYLE CODE + STYLE DESCRIPTION
|
||||||
|
* - If it contains code/description, fill missing values
|
||||||
|
* - If target field already contains a different value, add it instead of replacing it
|
||||||
|
* - Avoid duplicates
|
||||||
|
*/
|
||||||
|
$step0_applied = false;
|
||||||
|
|
||||||
|
if ($action2_index === false || $action3_index === false || $action4_index === false) {
|
||||||
|
error_log(
|
||||||
|
"Unable to apply routine step 0. Missing columns - action2: '$action2' (index: " . var_export($action2_index, true) . ")" .
|
||||||
|
", action3: '$action3' (index: " . var_export($action3_index, true) . ")" .
|
||||||
|
", action4: '$action4' (index: " . var_export($action4_index, true) . ")"
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
foreach ($excelData as $index => $row) {
|
||||||
|
if (!isset($row['data']) || !is_array($row['data'])) {
|
||||||
|
error_log("Riga non valida nello step 0, manca 'data' all'indice $index");
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
$combinedValue = trim((string)($row['data'][$action2_index] ?? ''));
|
||||||
|
$currentStyleCode = trim((string)($row['data'][$action3_index] ?? ''));
|
||||||
|
$currentStyleDescription = trim((string)($row['data'][$action4_index] ?? ''));
|
||||||
|
|
||||||
|
if ($combinedValue === '') {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
[$parsedStyleCode, $parsedStyleDescription] = $splitStyleCombined($combinedValue);
|
||||||
|
|
||||||
|
$styleCodeValues = $splitJoinedValues($currentStyleCode);
|
||||||
|
$styleDescriptionValues = $splitJoinedValues($currentStyleDescription);
|
||||||
|
|
||||||
|
if ($parsedStyleCode !== '' && !in_array($parsedStyleCode, $styleCodeValues, true)) {
|
||||||
|
$styleCodeValues[] = $parsedStyleCode;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($parsedStyleDescription !== '' && !in_array($parsedStyleDescription, $styleDescriptionValues, true)) {
|
||||||
|
$styleDescriptionValues[] = $parsedStyleDescription;
|
||||||
|
}
|
||||||
|
|
||||||
|
$excelData[$index]['data'][$action3_index] = $joinUniqueValues($styleCodeValues);
|
||||||
|
$excelData[$index]['data'][$action4_index] = $joinUniqueValues($styleDescriptionValues);
|
||||||
|
|
||||||
|
error_log(
|
||||||
|
"Step 0 - excelrow " . ($row['excelrow'] ?? 'N/A') .
|
||||||
|
" | combined: '" . $combinedValue . "'" .
|
||||||
|
" | style_code_result: '" . $excelData[$index]['data'][$action3_index] . "'" .
|
||||||
|
" | style_description_result: '" . $excelData[$index]['data'][$action4_index] . "'"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
$step0_applied = true;
|
||||||
|
error_log("Routine step 0 completato - Normalizzazione style applicata su " . count($excelData) . " righe");
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* STEP 1
|
||||||
|
* Aggregate rows by action1 using normalized STYLE CODE and STYLE DESCRIPTION
|
||||||
|
* If it fails, continue with STEP 2 only
|
||||||
|
*/
|
||||||
|
$step1_applied = false;
|
||||||
|
|
||||||
|
if ($action1_index === false || $action3_index === false || $action4_index === false) {
|
||||||
|
error_log(
|
||||||
|
"Unable to apply routine step 1. Package merge logic has been applied only. " .
|
||||||
|
"Missing columns - action1: '$action1' (index: " . var_export($action1_index, true) . ")" .
|
||||||
|
", action3: '$action3' (index: " . var_export($action3_index, true) . ")" .
|
||||||
|
", action4: '$action4' (index: " . var_export($action4_index, true) . ")"
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
$grouped_data = [];
|
||||||
|
|
||||||
|
foreach ($excelData as $row) {
|
||||||
|
if (!isset($row['data']) || !is_array($row['data'])) {
|
||||||
|
error_log("Riga non valida, manca 'data' per excelrow " . ($row['excelrow'] ?? 'N/A'));
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
$key = $row['data'][$action1_index] ?? '';
|
||||||
|
$key = trim((string)$key);
|
||||||
|
$key = $key === '' ? '_empty_' : $key;
|
||||||
|
|
||||||
|
if (!isset($grouped_data[$key])) {
|
||||||
|
$grouped_data[$key] = [
|
||||||
|
'data' => $row['data'],
|
||||||
|
'excelrow' => [($row['excelrow'] ?? '')],
|
||||||
|
'style_codes' => [],
|
||||||
|
'style_descriptions' => []
|
||||||
|
];
|
||||||
|
} else {
|
||||||
|
$grouped_data[$key]['excelrow'][] = ($row['excelrow'] ?? '');
|
||||||
|
}
|
||||||
|
|
||||||
|
// Collect normalized STYLE CODE values
|
||||||
|
$styleCodeValues = $splitJoinedValues($row['data'][$action3_index] ?? '');
|
||||||
|
foreach ($styleCodeValues as $styleCodeValue) {
|
||||||
|
if (!in_array($styleCodeValue, $grouped_data[$key]['style_codes'], true)) {
|
||||||
|
$grouped_data[$key]['style_codes'][] = $styleCodeValue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Collect normalized STYLE DESCRIPTION values
|
||||||
|
$styleDescriptionValues = $splitJoinedValues($row['data'][$action4_index] ?? '');
|
||||||
|
foreach ($styleDescriptionValues as $styleDescriptionValue) {
|
||||||
|
if (!in_array($styleDescriptionValue, $grouped_data[$key]['style_descriptions'], true)) {
|
||||||
|
$grouped_data[$key]['style_descriptions'][] = $styleDescriptionValue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
$new_excel_data = [];
|
||||||
|
|
||||||
|
foreach ($grouped_data as $key => $group) {
|
||||||
|
$row_data = $group['data'];
|
||||||
|
|
||||||
|
// Update STYLE CODE and STYLE DESCRIPTION with aggregated unique values
|
||||||
|
$row_data[$action3_index] = $joinUniqueValues($group['style_codes']);
|
||||||
|
$row_data[$action4_index] = $joinUniqueValues($group['style_descriptions']);
|
||||||
|
|
||||||
|
// Concatenate excelrow values with "+"
|
||||||
|
$excelrow_clean = array_filter($group['excelrow'], function ($value) {
|
||||||
|
return $value !== null && $value !== '';
|
||||||
|
});
|
||||||
|
|
||||||
|
$excelrow_value = count($excelrow_clean) > 1
|
||||||
|
? implode('+', $excelrow_clean)
|
||||||
|
: (reset($excelrow_clean) ?: '');
|
||||||
|
|
||||||
|
$new_excel_data[] = [
|
||||||
|
'data' => $row_data,
|
||||||
|
'excelrow' => $excelrow_value
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
$excelData = $new_excel_data;
|
||||||
|
$step1_applied = true;
|
||||||
|
|
||||||
|
error_log("Routine step 1 completato - Righe aggregate: " . count($new_excel_data));
|
||||||
|
error_log("Excelrow aggregati step 1: " . print_r(array_column($new_excel_data, 'excelrow'), true));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* STEP 2
|
||||||
|
* Merge package-related columns into PACKAGE
|
||||||
|
* Always applied if PACKAGE exists
|
||||||
|
*/
|
||||||
|
foreach ($excelData as $index => $row) {
|
||||||
|
if (!isset($row['data']) || !is_array($row['data'])) {
|
||||||
|
error_log("Riga non valida nello step 2, manca 'data' all'indice $index");
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
$package_values = [];
|
||||||
|
|
||||||
|
$value_package = trim((string)($row['data'][$package_index] ?? ''));
|
||||||
|
$value_other_test = $other_test_index !== false ? trim((string)($row['data'][$other_test_index] ?? '')) : '';
|
||||||
|
$value_only_colorfastness = $only_colorfastness_index !== false ? trim((string)($row['data'][$only_colorfastness_index] ?? '')) : '';
|
||||||
|
$value_only_chemical = $only_chemical_index !== false ? trim((string)($row['data'][$only_chemical_index] ?? '')) : '';
|
||||||
|
|
||||||
|
if ($value_package !== '') {
|
||||||
|
$package_values[] = $value_package;
|
||||||
|
}
|
||||||
|
if ($value_other_test !== '') {
|
||||||
|
$package_values[] = $value_other_test;
|
||||||
|
}
|
||||||
|
if ($value_only_colorfastness !== '') {
|
||||||
|
$package_values[] = $value_only_colorfastness;
|
||||||
|
}
|
||||||
|
if ($value_only_chemical !== '') {
|
||||||
|
$package_values[] = $value_only_chemical;
|
||||||
|
}
|
||||||
|
|
||||||
|
$package_values = array_unique($package_values);
|
||||||
|
|
||||||
|
$excelData[$index]['data'][$package_index] = implode(' - ', $package_values);
|
||||||
|
}
|
||||||
|
|
||||||
|
error_log("Routine step 2 completato - Merge package applicato su " . count($excelData) . " righe");
|
||||||
|
|
||||||
|
if (!$step0_applied) {
|
||||||
|
error_log("Warning finale: routine step 0 non applicata");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!$step1_applied) {
|
||||||
|
error_log("Warning finale: routine step 1 non applicata, routine step 2 applicata correttamente");
|
||||||
|
}
|
||||||
|
|
||||||
|
error_log("Routine Moncler completata con successo");
|
||||||
|
} catch (Exception $e) {
|
||||||
|
error_log("Eccezione nella routine Moncler: " . $e->getMessage());
|
||||||
|
throw $e;
|
||||||
|
}
|
||||||
|
}
|
||||||
212
public/userarea/routines/moncler_supplier_fabric.php
Normal file
212
public/userarea/routines/moncler_supplier_fabric.php
Normal file
@ -0,0 +1,212 @@
|
|||||||
|
<?php
|
||||||
|
ini_set('log_errors', 1);
|
||||||
|
ini_set('error_log', __DIR__ . '/routine_debug.log');
|
||||||
|
|
||||||
|
function applyRoutine(&$excelData, $routineData)
|
||||||
|
{
|
||||||
|
try {
|
||||||
|
// Initial log
|
||||||
|
error_log("Inizio esecuzione nuova routine: " . date('Y-m-d H:i:s'));
|
||||||
|
error_log("Dati routine: " . print_r($routineData, true));
|
||||||
|
error_log("Dati excel_data: " . print_r($excelData, true));
|
||||||
|
|
||||||
|
// Check if excelData is empty
|
||||||
|
if (empty($excelData)) {
|
||||||
|
throw new Exception("excelData è vuoto o non valido.");
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get headers from routine data
|
||||||
|
$headers = $routineData['xls_headers'] ?? [];
|
||||||
|
|
||||||
|
if (empty($headers)) {
|
||||||
|
throw new Exception("Nessun header trovato per la routine.");
|
||||||
|
}
|
||||||
|
|
||||||
|
// If headers arrive as JSON string, decode them
|
||||||
|
if (!is_array($headers) && is_string($headers)) {
|
||||||
|
$decoded_headers = json_decode($headers, true);
|
||||||
|
if (json_last_error() === JSON_ERROR_NONE && is_array($decoded_headers)) {
|
||||||
|
$headers = $decoded_headers;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!is_array($headers) || empty($headers)) {
|
||||||
|
throw new Exception("Gli header della routine non sono validi.");
|
||||||
|
}
|
||||||
|
|
||||||
|
error_log("Header ricevuti: " . print_r($headers, true));
|
||||||
|
|
||||||
|
// Normalize headers
|
||||||
|
$normalized_headers = array_map(function ($header) {
|
||||||
|
return trim((string)$header);
|
||||||
|
}, $headers);
|
||||||
|
|
||||||
|
error_log("Header normalizzati: " . print_r($normalized_headers, true));
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Define source and target headers
|
||||||
|
*/
|
||||||
|
$parte_moncler_header = 'PARTE MONCLER';
|
||||||
|
$parte_fornitore_header = 'PARTE FORNITORE';
|
||||||
|
|
||||||
|
$descr_parte_moncler_header = 'DESCRIZIONE PARTE MONCLER';
|
||||||
|
$descr_parte_fornitore_header = 'DESCRIZIONE PARTE FORNITORE';
|
||||||
|
|
||||||
|
$colore_moncler_header = 'COLORE MONCLER';
|
||||||
|
$colore_fornitore_header = 'COLORE FORNITORE';
|
||||||
|
|
||||||
|
$composizione_tessile_header = 'COMPOSIZIONE TESSILE';
|
||||||
|
$composizione_totale_header = 'COMPOSIZIONE TOTALE';
|
||||||
|
|
||||||
|
$pacchetti_test_header = 'PACCHETTI TEST';
|
||||||
|
$test_addizionali_header = 'TEST ADDIZIONALI (A5 solo se richiesti dal cliente)';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Find indexes
|
||||||
|
*/
|
||||||
|
$parte_moncler_index = array_search($parte_moncler_header, $normalized_headers, true);
|
||||||
|
$parte_fornitore_index = array_search($parte_fornitore_header, $normalized_headers, true);
|
||||||
|
|
||||||
|
$descr_parte_moncler_index = array_search($descr_parte_moncler_header, $normalized_headers, true);
|
||||||
|
$descr_parte_fornitore_index = array_search($descr_parte_fornitore_header, $normalized_headers, true);
|
||||||
|
|
||||||
|
$colore_moncler_index = array_search($colore_moncler_header, $normalized_headers, true);
|
||||||
|
$colore_fornitore_index = array_search($colore_fornitore_header, $normalized_headers, true);
|
||||||
|
|
||||||
|
$composizione_tessile_index = array_search($composizione_tessile_header, $normalized_headers, true);
|
||||||
|
$composizione_totale_index = array_search($composizione_totale_header, $normalized_headers, true);
|
||||||
|
|
||||||
|
$pacchetti_test_index = array_search($pacchetti_test_header, $normalized_headers, true);
|
||||||
|
$test_addizionali_index = array_search($test_addizionali_header, $normalized_headers, true);
|
||||||
|
|
||||||
|
error_log(
|
||||||
|
"Indici trovati - " .
|
||||||
|
"PARTE MONCLER: " . var_export($parte_moncler_index, true) .
|
||||||
|
", PARTE FORNITORE: " . var_export($parte_fornitore_index, true) .
|
||||||
|
", DESCRIZIONE PARTE MONCLER: " . var_export($descr_parte_moncler_index, true) .
|
||||||
|
", DESCRIZIONE PARTE FORNITORE: " . var_export($descr_parte_fornitore_index, true) .
|
||||||
|
", COLORE MONCLER: " . var_export($colore_moncler_index, true) .
|
||||||
|
", COLORE FORNITORE: " . var_export($colore_fornitore_index, true) .
|
||||||
|
", COMPOSIZIONE TESSILE: " . var_export($composizione_tessile_index, true) .
|
||||||
|
", COMPOSIZIONE TOTALE: " . var_export($composizione_totale_index, true) .
|
||||||
|
", PACCHETTI TEST: " . var_export($pacchetti_test_index, true) .
|
||||||
|
", TEST ADDIZIONALI: " . var_export($test_addizionali_index, true)
|
||||||
|
);
|
||||||
|
|
||||||
|
// Required target columns must exist
|
||||||
|
if ($parte_moncler_index === false) {
|
||||||
|
throw new Exception("Colonna PARTE MONCLER non trovata.");
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($descr_parte_moncler_index === false) {
|
||||||
|
throw new Exception("Colonna DESCRIZIONE PARTE MONCLER non trovata.");
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($colore_moncler_index === false) {
|
||||||
|
throw new Exception("Colonna COLORE MONCLER non trovata.");
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($composizione_tessile_index === false) {
|
||||||
|
throw new Exception("Colonna COMPOSIZIONE TESSILE non trovata.");
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($pacchetti_test_index === false) {
|
||||||
|
throw new Exception("Colonna PACCHETTI TEST non trovata.");
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Apply merge row by row
|
||||||
|
*/
|
||||||
|
foreach ($excelData as $index => $row) {
|
||||||
|
if (!isset($row['data']) || !is_array($row['data'])) {
|
||||||
|
error_log("Riga non valida, manca 'data' all'indice $index");
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 1. PARTE MONCLER + PARTE FORNITORE -> PARTE MONCLER
|
||||||
|
$parte_values = [];
|
||||||
|
|
||||||
|
$value_parte_moncler = trim((string)($row['data'][$parte_moncler_index] ?? ''));
|
||||||
|
$value_parte_fornitore = $parte_fornitore_index !== false ? trim((string)($row['data'][$parte_fornitore_index] ?? '')) : '';
|
||||||
|
|
||||||
|
if ($value_parte_moncler !== '') {
|
||||||
|
$parte_values[] = $value_parte_moncler;
|
||||||
|
}
|
||||||
|
if ($value_parte_fornitore !== '') {
|
||||||
|
$parte_values[] = $value_parte_fornitore;
|
||||||
|
}
|
||||||
|
|
||||||
|
$parte_values = array_unique($parte_values);
|
||||||
|
$excelData[$index]['data'][$parte_moncler_index] = implode(' - ', $parte_values);
|
||||||
|
|
||||||
|
// 2. DESCRIZIONE PARTE MONCLER + DESCRIZIONE PARTE FORNITORE -> DESCRIZIONE PARTE MONCLER
|
||||||
|
$descr_values = [];
|
||||||
|
|
||||||
|
$value_descr_moncler = trim((string)($row['data'][$descr_parte_moncler_index] ?? ''));
|
||||||
|
$value_descr_fornitore = $descr_parte_fornitore_index !== false ? trim((string)($row['data'][$descr_parte_fornitore_index] ?? '')) : '';
|
||||||
|
|
||||||
|
if ($value_descr_moncler !== '') {
|
||||||
|
$descr_values[] = $value_descr_moncler;
|
||||||
|
}
|
||||||
|
if ($value_descr_fornitore !== '') {
|
||||||
|
$descr_values[] = $value_descr_fornitore;
|
||||||
|
}
|
||||||
|
|
||||||
|
$descr_values = array_unique($descr_values);
|
||||||
|
$excelData[$index]['data'][$descr_parte_moncler_index] = implode(' - ', $descr_values);
|
||||||
|
|
||||||
|
// 3. COLORE MONCLER + COLORE FORNITORE -> COLORE MONCLER
|
||||||
|
$colore_values = [];
|
||||||
|
|
||||||
|
$value_colore_moncler = trim((string)($row['data'][$colore_moncler_index] ?? ''));
|
||||||
|
$value_colore_fornitore = $colore_fornitore_index !== false ? trim((string)($row['data'][$colore_fornitore_index] ?? '')) : '';
|
||||||
|
|
||||||
|
if ($value_colore_moncler !== '') {
|
||||||
|
$colore_values[] = $value_colore_moncler;
|
||||||
|
}
|
||||||
|
if ($value_colore_fornitore !== '') {
|
||||||
|
$colore_values[] = $value_colore_fornitore;
|
||||||
|
}
|
||||||
|
|
||||||
|
$colore_values = array_unique($colore_values);
|
||||||
|
$excelData[$index]['data'][$colore_moncler_index] = implode(' - ', $colore_values);
|
||||||
|
|
||||||
|
// 4. COMPOSIZIONE TESSILE + COMPOSIZIONE TOTALE -> COMPOSIZIONE TESSILE
|
||||||
|
$composizione_values = [];
|
||||||
|
|
||||||
|
$value_composizione_tessile = trim((string)($row['data'][$composizione_tessile_index] ?? ''));
|
||||||
|
$value_composizione_totale = $composizione_totale_index !== false ? trim((string)($row['data'][$composizione_totale_index] ?? '')) : '';
|
||||||
|
|
||||||
|
if ($value_composizione_tessile !== '') {
|
||||||
|
$composizione_values[] = $value_composizione_tessile;
|
||||||
|
}
|
||||||
|
if ($value_composizione_totale !== '') {
|
||||||
|
$composizione_values[] = $value_composizione_totale;
|
||||||
|
}
|
||||||
|
|
||||||
|
$composizione_values = array_unique($composizione_values);
|
||||||
|
$excelData[$index]['data'][$composizione_tessile_index] = implode(' - ', $composizione_values);
|
||||||
|
|
||||||
|
// 5. PACCHETTI TEST + TEST ADDIZIONALI -> PACCHETTI TEST
|
||||||
|
$pacchetti_values = [];
|
||||||
|
|
||||||
|
$value_pacchetti_test = trim((string)($row['data'][$pacchetti_test_index] ?? ''));
|
||||||
|
$value_test_addizionali = $test_addizionali_index !== false ? trim((string)($row['data'][$test_addizionali_index] ?? '')) : '';
|
||||||
|
|
||||||
|
if ($value_pacchetti_test !== '') {
|
||||||
|
$pacchetti_values[] = $value_pacchetti_test;
|
||||||
|
}
|
||||||
|
if ($value_test_addizionali !== '') {
|
||||||
|
$pacchetti_values[] = $value_test_addizionali;
|
||||||
|
}
|
||||||
|
|
||||||
|
$pacchetti_values = array_unique($pacchetti_values);
|
||||||
|
$excelData[$index]['data'][$pacchetti_test_index] = implode(' - ', $pacchetti_values);
|
||||||
|
}
|
||||||
|
|
||||||
|
error_log("Nuova routine completata con successo. Righe elaborate: " . count($excelData));
|
||||||
|
} catch (Exception $e) {
|
||||||
|
error_log("Eccezione nella nuova routine: " . $e->getMessage());
|
||||||
|
throw $e;
|
||||||
|
}
|
||||||
|
}
|
||||||
130
public/userarea/saveAll.js
Normal file
130
public/userarea/saveAll.js
Normal file
@ -0,0 +1,130 @@
|
|||||||
|
/**
|
||||||
|
* saveAll.js — Save All functionality using gridData
|
||||||
|
*/
|
||||||
|
(function() {
|
||||||
|
'use strict';
|
||||||
|
|
||||||
|
let saveAllRunning = false;
|
||||||
|
|
||||||
|
function isBusy() {
|
||||||
|
return saveAllRunning || window.batchRunning;
|
||||||
|
}
|
||||||
|
|
||||||
|
// ── Save single row ──────────────────────────────────────────────────
|
||||||
|
$(document).on('click', '.save-btn', async function() {
|
||||||
|
const btn = this;
|
||||||
|
const rowIndex = parseInt(btn.dataset.row);
|
||||||
|
const row = window.gridData?.[rowIndex];
|
||||||
|
if (!row) return;
|
||||||
|
|
||||||
|
const origHtml = btn.innerHTML;
|
||||||
|
btn.innerHTML = '<i class="fas fa-spinner fa-spin"></i>';
|
||||||
|
btn.disabled = true;
|
||||||
|
|
||||||
|
try {
|
||||||
|
const formData = window.buildSavePayload(rowIndex);
|
||||||
|
const resp = await fetch('save_edited_row.php', { method: 'POST', body: formData });
|
||||||
|
const result = await resp.json();
|
||||||
|
|
||||||
|
if (result.success) {
|
||||||
|
row._dirty = false;
|
||||||
|
if (window.gridRenderer?.clearDirty) window.gridRenderer.clearDirty(rowIndex);
|
||||||
|
// Flash success on row without re-rendering (preserves Select2 state)
|
||||||
|
const gridRow = document.querySelector(`.grid-row[data-id="${row.iddatadb}"]`);
|
||||||
|
if (gridRow) {
|
||||||
|
gridRow.classList.remove('row-dirty');
|
||||||
|
gridRow.querySelectorAll('.grid-cell').forEach(cell => {
|
||||||
|
cell.classList.remove('cell-changed');
|
||||||
|
cell.classList.add('flash-success');
|
||||||
|
});
|
||||||
|
setTimeout(() => gridRow.querySelectorAll('.flash-success').forEach(c => c.classList.remove('flash-success')), 500);
|
||||||
|
}
|
||||||
|
const toastEl = document.getElementById('saveSuccessToast');
|
||||||
|
if (toastEl) bootstrap.Toast.getOrCreateInstance(toastEl).show();
|
||||||
|
} else {
|
||||||
|
alert('Errore: ' + result.message);
|
||||||
|
}
|
||||||
|
} catch (e) {
|
||||||
|
alert('Errore: ' + e.message);
|
||||||
|
} finally {
|
||||||
|
btn.innerHTML = origHtml;
|
||||||
|
btn.disabled = false;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// ── Save All ─────────────────────────────────────────────────────────
|
||||||
|
$(document).on('click', '.save-all-btn', function(e) {
|
||||||
|
e.preventDefault();
|
||||||
|
if (isBusy()) return;
|
||||||
|
const modalEl = document.getElementById('saveAllConfirmModal');
|
||||||
|
if (!modalEl) return;
|
||||||
|
new bootstrap.Modal(modalEl, { keyboard: false }).show();
|
||||||
|
});
|
||||||
|
|
||||||
|
$(document).on('click', '#saveAllConfirmBtn', async function() {
|
||||||
|
const confirmModal = bootstrap.Modal.getInstance(document.getElementById('saveAllConfirmModal'));
|
||||||
|
if (confirmModal) confirmModal.hide();
|
||||||
|
saveAllRunning = true;
|
||||||
|
|
||||||
|
const bar = document.getElementById('batchExportBar');
|
||||||
|
const statusEl = document.getElementById('batchExportStatus');
|
||||||
|
const cancelBtn = document.getElementById('exportBatchCancelBtn');
|
||||||
|
if (bar) bar.style.display = '';
|
||||||
|
if (cancelBtn) cancelBtn.style.display = 'none';
|
||||||
|
if (statusEl) statusEl.textContent = 'Saving...';
|
||||||
|
|
||||||
|
const data = window.gridData || [];
|
||||||
|
const dirtyRows = data.map((r, i) => r._dirty ? i : -1).filter(i => i >= 0);
|
||||||
|
|
||||||
|
if (dirtyRows.length === 0) {
|
||||||
|
saveAllRunning = false;
|
||||||
|
if (bar) bar.style.display = 'none';
|
||||||
|
const msgEl = document.getElementById('saveAllResultMessage');
|
||||||
|
if (msgEl) msgEl.textContent = 'No changes to save.';
|
||||||
|
new bootstrap.Modal(document.getElementById('saveAllResultModal')).show();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
let success = 0, fail = 0;
|
||||||
|
|
||||||
|
for (const idx of dirtyRows) {
|
||||||
|
if (statusEl) statusEl.textContent = `Saving ${success + fail + 1} / ${dirtyRows.length}...`;
|
||||||
|
try {
|
||||||
|
const formData = window.buildSavePayload(idx);
|
||||||
|
const resp = await fetch('save_edited_row.php', { method: 'POST', body: formData });
|
||||||
|
const result = await resp.json();
|
||||||
|
if (result.success) {
|
||||||
|
data[idx]._dirty = false;
|
||||||
|
if (window.gridRenderer?.clearDirty) window.gridRenderer.clearDirty(idx);
|
||||||
|
success++;
|
||||||
|
} else {
|
||||||
|
fail++;
|
||||||
|
}
|
||||||
|
} catch (e) {
|
||||||
|
fail++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
saveAllRunning = false;
|
||||||
|
if (bar) bar.style.display = 'none';
|
||||||
|
|
||||||
|
const gr = window.gridRenderer;
|
||||||
|
if (gr) gr.renderVisibleRows();
|
||||||
|
|
||||||
|
const msg = `Saved: ${success}` + (fail > 0 ? `, Errors: ${fail}` : '');
|
||||||
|
const msgEl = document.getElementById('saveAllResultMessage');
|
||||||
|
if (msgEl) msgEl.textContent = msg;
|
||||||
|
new bootstrap.Modal(document.getElementById('saveAllResultModal')).show();
|
||||||
|
});
|
||||||
|
|
||||||
|
// ── beforeunload ─────────────────────────────────────────────────────
|
||||||
|
window.addEventListener('beforeunload', function(e) {
|
||||||
|
if (window.gridData && window.gridData.some(r => r._dirty)) {
|
||||||
|
e.preventDefault();
|
||||||
|
e.returnValue = '';
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// Public
|
||||||
|
window.saveAllRunning = () => saveAllRunning;
|
||||||
|
})();
|
||||||
@ -23,7 +23,7 @@ try {
|
|||||||
foreach ($_POST as $key => $value) {
|
foreach ($_POST as $key => $value) {
|
||||||
if ($key !== 'iddatadb' && !in_array($key, $excludeFields)) {
|
if ($key !== 'iddatadb' && !in_array($key, $excludeFields)) {
|
||||||
$updates[] = "$key = ?";
|
$updates[] = "$key = ?";
|
||||||
$values[] = htmlspecialchars($value);
|
$values[] = $value;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
$values[] = $iddatadb;
|
$values[] = $iddatadb;
|
||||||
|
|||||||
@ -12,7 +12,8 @@ try {
|
|||||||
|
|
||||||
$iddatadb = intval($_POST['iddatadb']);
|
$iddatadb = intval($_POST['iddatadb']);
|
||||||
$idclient = isset($_POST['idclient']) ? (is_numeric($_POST['idclient']) ? intval($_POST['idclient']) : null) : null;
|
$idclient = isset($_POST['idclient']) ? (is_numeric($_POST['idclient']) ? intval($_POST['idclient']) : null) : null;
|
||||||
|
$clienteFornitoreId = isset($_POST['cliente_fornitore_id']) ? (is_numeric($_POST['cliente_fornitore_id']) ? intval($_POST['cliente_fornitore_id']) : null) : null;
|
||||||
|
$testedComponent = isset($_POST['tested_component']) ? trim((string)$_POST['tested_component']) : null;
|
||||||
$db = DBHandlerSelect::getInstance();
|
$db = DBHandlerSelect::getInstance();
|
||||||
$pdo = $db->getConnection();
|
$pdo = $db->getConnection();
|
||||||
|
|
||||||
@ -22,10 +23,11 @@ try {
|
|||||||
// (NON tocchiamo MySQL, gestiamo solo qui la traduzione)
|
// (NON tocchiamo MySQL, gestiamo solo qui la traduzione)
|
||||||
$fixedAliasMap = [
|
$fixedAliasMap = [
|
||||||
'ClienteResponsabile' => 'cliente_responsabile_id',
|
'ClienteResponsabile' => 'cliente_responsabile_id',
|
||||||
|
'ClienteFornitore' => 'cliente_fornitore_id',
|
||||||
|
'ClienteAnalisi' => 'clienteAnalisi',
|
||||||
'MoltiplicatorePrezzo' => 'moltiplicatore_prezzo_id',
|
'MoltiplicatorePrezzo' => 'moltiplicatore_prezzo_id',
|
||||||
'AnagraficaCertestObject' => 'anagrafica_certest_object_id',
|
'AnagraficaCertestObject' => 'anagrafica_certest_object_id',
|
||||||
'AnagraficaCertestService' => 'anagrafica_certest_service_id',
|
'AnagraficaCertestService' => 'anagrafica_certest_service_id',
|
||||||
'ClienteFornitore' => 'cliente_fornitore_id',
|
|
||||||
'ConsegnaRichiesta' => 'consegna_richiesta',
|
'ConsegnaRichiesta' => 'consegna_richiesta',
|
||||||
];
|
];
|
||||||
|
|
||||||
@ -137,6 +139,16 @@ try {
|
|||||||
$params[] = $idclient;
|
$params[] = $idclient;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 5a2) cliente_fornitore_id (se presente)
|
||||||
|
if (isset($clienteFornitoreId)) {
|
||||||
|
$setParts[] = "cliente_fornitore_id = ?";
|
||||||
|
$params[] = $clienteFornitoreId;
|
||||||
|
}
|
||||||
|
// 5a3) tested_component (se presente)
|
||||||
|
if (isset($testedComponent)) {
|
||||||
|
$setParts[] = "tested_component = ?";
|
||||||
|
$params[] = ($testedComponent === '') ? null : $testedComponent;
|
||||||
|
}
|
||||||
// 5b) fixed fields dal POST
|
// 5b) fixed fields dal POST
|
||||||
// QUI è il punto chiave: accettiamo SOLO colonne reali (realWhitelist),
|
// QUI è il punto chiave: accettiamo SOLO colonne reali (realWhitelist),
|
||||||
// ma se dal JS arrivassero ancora key logiche, funzionerebbe uguale
|
// ma se dal JS arrivassero ancora key logiche, funzionerebbe uguale
|
||||||
|
|||||||
@ -16,9 +16,11 @@ if (!$data || !isset($data['id'])) {
|
|||||||
exit;
|
exit;
|
||||||
}
|
}
|
||||||
|
|
||||||
$mappingId = $data['id'];
|
$mappingId = (int)$data['id'];
|
||||||
$mappingType = $data['mapping_type'] ?? '';
|
$mappingType = $data['mapping_type'] ?? '';
|
||||||
|
|
||||||
$excelColumn = $data['excel_column'] ?? null;
|
$excelColumn = $data['excel_column'] ?? null;
|
||||||
|
$jsonNode = $data['json_node'] ?? null;
|
||||||
$manualDefault = $data['manual_default'] ?? null;
|
$manualDefault = $data['manual_default'] ?? null;
|
||||||
|
|
||||||
$autoValue = $data['auto_value'] ?? 'none';
|
$autoValue = $data['auto_value'] ?? 'none';
|
||||||
@ -26,7 +28,7 @@ $tablename = $data['tablename'] ?? '';
|
|||||||
|
|
||||||
try {
|
try {
|
||||||
// Normalize mapping type
|
// Normalize mapping type
|
||||||
$allowedTypes = ['', 'xls', 'manual', 'auto'];
|
$allowedTypes = ['', 'xls', 'json', 'manual', 'auto'];
|
||||||
if (!in_array($mappingType, $allowedTypes, true)) {
|
if (!in_array($mappingType, $allowedTypes, true)) {
|
||||||
echo json_encode(["success" => false, "message" => "Invalid mapping_type"]);
|
echo json_encode(["success" => false, "message" => "Invalid mapping_type"]);
|
||||||
exit;
|
exit;
|
||||||
@ -41,43 +43,67 @@ try {
|
|||||||
// Decide what to persist based on mapping_type
|
// Decide what to persist based on mapping_type
|
||||||
$isManual = 0;
|
$isManual = 0;
|
||||||
$excelToSave = null;
|
$excelToSave = null;
|
||||||
|
$jsonNodeToSave = null;
|
||||||
$manualToSave = null;
|
$manualToSave = null;
|
||||||
$autoToSave = 'none';
|
$autoToSave = 'none';
|
||||||
|
|
||||||
if ($mappingType === 'xls') {
|
if ($mappingType === 'xls') {
|
||||||
|
|
||||||
$isManual = 0;
|
$isManual = 0;
|
||||||
$excelToSave = $excelColumn ?: null;
|
$excelToSave = $excelColumn ?: null;
|
||||||
|
$jsonNodeToSave = null;
|
||||||
|
$manualToSave = null;
|
||||||
|
$autoToSave = 'none';
|
||||||
|
} elseif ($mappingType === 'json') {
|
||||||
|
|
||||||
|
$isManual = 0;
|
||||||
|
$excelToSave = null;
|
||||||
|
$jsonNodeToSave = $jsonNode ?: null;
|
||||||
$manualToSave = null;
|
$manualToSave = null;
|
||||||
$autoToSave = 'none';
|
$autoToSave = 'none';
|
||||||
} elseif ($mappingType === 'manual') {
|
} elseif ($mappingType === 'manual') {
|
||||||
|
|
||||||
$isManual = 1;
|
$isManual = 1;
|
||||||
$excelToSave = null;
|
$excelToSave = null;
|
||||||
|
$jsonNodeToSave = null;
|
||||||
$manualToSave = $manualDefault;
|
$manualToSave = $manualDefault;
|
||||||
$autoToSave = 'none';
|
$autoToSave = 'none';
|
||||||
} elseif ($mappingType === 'auto') {
|
} elseif ($mappingType === 'auto') {
|
||||||
|
|
||||||
$isManual = 0;
|
$isManual = 0;
|
||||||
$excelToSave = null;
|
$excelToSave = null;
|
||||||
|
$jsonNodeToSave = null;
|
||||||
$manualToSave = null;
|
$manualToSave = null;
|
||||||
$autoToSave = $autoValue ?: 'none';
|
$autoToSave = $autoValue ?: 'none';
|
||||||
} else {
|
} else {
|
||||||
// reset
|
|
||||||
|
// Reset mapping
|
||||||
$isManual = 0;
|
$isManual = 0;
|
||||||
$excelToSave = null;
|
$excelToSave = null;
|
||||||
|
$jsonNodeToSave = null;
|
||||||
$manualToSave = null;
|
$manualToSave = null;
|
||||||
$autoToSave = 'none';
|
$autoToSave = 'none';
|
||||||
}
|
}
|
||||||
|
|
||||||
$stmt = $pdo->prepare("
|
$stmt = $pdo->prepare("
|
||||||
UPDATE template_mapping
|
UPDATE template_mapping
|
||||||
SET
|
SET
|
||||||
is_manual = ?,
|
is_manual = ?,
|
||||||
excel_column = ?,
|
excel_column = ?,
|
||||||
manual_default = ?,
|
json_node = ?,
|
||||||
auto_value = ?
|
manual_default = ?,
|
||||||
WHERE id = ?
|
auto_value = ?
|
||||||
");
|
WHERE id = ?
|
||||||
|
");
|
||||||
|
|
||||||
$result = $stmt->execute([$isManual, $excelToSave, $manualToSave, $autoToSave, $mappingId]);
|
$result = $stmt->execute([
|
||||||
|
$isManual,
|
||||||
|
$excelToSave,
|
||||||
|
$jsonNodeToSave,
|
||||||
|
$manualToSave,
|
||||||
|
$autoToSave,
|
||||||
|
$mappingId
|
||||||
|
]);
|
||||||
|
|
||||||
if (!$result) {
|
if (!$result) {
|
||||||
echo json_encode(["success" => false, "message" => "Database update failed"]);
|
echo json_encode(["success" => false, "message" => "Database update failed"]);
|
||||||
@ -88,15 +114,17 @@ try {
|
|||||||
"success" => true,
|
"success" => true,
|
||||||
"message" => "Mapping updated successfully",
|
"message" => "Mapping updated successfully",
|
||||||
"saved" => [
|
"saved" => [
|
||||||
"id" => (int)$mappingId,
|
"id" => $mappingId,
|
||||||
"mapping_type" => $mappingType,
|
"mapping_type" => $mappingType,
|
||||||
"is_manual" => $isManual,
|
"is_manual" => $isManual,
|
||||||
"excel_column" => $excelToSave,
|
"excel_column" => $excelToSave,
|
||||||
|
"json_node" => $jsonNodeToSave,
|
||||||
"manual_default" => $manualToSave,
|
"manual_default" => $manualToSave,
|
||||||
"auto_value" => $autoToSave
|
"auto_value" => $autoToSave
|
||||||
]
|
]
|
||||||
]);
|
]);
|
||||||
} catch (Exception $e) {
|
} catch (Throwable $e) {
|
||||||
echo json_encode(["success" => false, "message" => "Error: " . $e->getMessage()]);
|
echo json_encode(["success" => false, "message" => "Error: " . $e->getMessage()]);
|
||||||
}
|
}
|
||||||
|
|
||||||
exit;
|
exit;
|
||||||
|
|||||||
90
public/userarea/save_part_analysis.php
Normal file
90
public/userarea/save_part_analysis.php
Normal file
@ -0,0 +1,90 @@
|
|||||||
|
<?php
|
||||||
|
require_once dirname(__DIR__, 2) . '/vendor/autoload.php';
|
||||||
|
require_once __DIR__ . '/class/db-functions.php';
|
||||||
|
|
||||||
|
header('Content-Type: application/json');
|
||||||
|
ini_set('display_errors', '0');
|
||||||
|
error_reporting(E_ALL);
|
||||||
|
|
||||||
|
try {
|
||||||
|
if ($_SERVER['REQUEST_METHOD'] !== 'POST') {
|
||||||
|
http_response_code(405);
|
||||||
|
echo json_encode(['success' => false, 'message' => 'Method not allowed']);
|
||||||
|
exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
$partId = isset($_POST['part_id']) ? (int)$_POST['part_id'] : 0;
|
||||||
|
$iddatadb = isset($_POST['iddatadb']) ? (int)$_POST['iddatadb'] : 0;
|
||||||
|
$idmatrice = isset($_POST['idmatrice']) ? (int)$_POST['idmatrice'] : 0;
|
||||||
|
$analysisRecordkey = trim($_POST['analysis_recordkey'] ?? '');
|
||||||
|
$analysisName = trim($_POST['analysis_name'] ?? '');
|
||||||
|
$analysisMethod = trim($_POST['analysis_method'] ?? '');
|
||||||
|
$analysisLevel = isset($_POST['analysis_level']) && $_POST['analysis_level'] !== '' ? (int)$_POST['analysis_level'] : null;
|
||||||
|
$isWebSelectable = isset($_POST['is_web_selectable']) ? (int)$_POST['is_web_selectable'] : 0;
|
||||||
|
$isAccredited = isset($_POST['is_accredited']) ? (int)$_POST['is_accredited'] : 0;
|
||||||
|
|
||||||
|
if ($partId <= 0 || $analysisRecordkey === '') {
|
||||||
|
http_response_code(400);
|
||||||
|
echo json_encode(['success' => false, 'message' => 'Missing required data']);
|
||||||
|
exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
$db = DBHandlerSelect::getInstance();
|
||||||
|
$pdo = $db->getConnection();
|
||||||
|
|
||||||
|
$stmt = $pdo->prepare("
|
||||||
|
INSERT INTO identification_parts_analyses (
|
||||||
|
part_id,
|
||||||
|
iddatadb,
|
||||||
|
idmatrice,
|
||||||
|
analysis_recordkey,
|
||||||
|
analysis_name,
|
||||||
|
analysis_method,
|
||||||
|
analysis_level,
|
||||||
|
is_web_selectable,
|
||||||
|
is_accredited
|
||||||
|
) VALUES (
|
||||||
|
:part_id,
|
||||||
|
:iddatadb,
|
||||||
|
:idmatrice,
|
||||||
|
:analysis_recordkey,
|
||||||
|
:analysis_name,
|
||||||
|
:analysis_method,
|
||||||
|
:analysis_level,
|
||||||
|
:is_web_selectable,
|
||||||
|
:is_accredited
|
||||||
|
)
|
||||||
|
ON DUPLICATE KEY UPDATE
|
||||||
|
analysis_name = VALUES(analysis_name),
|
||||||
|
analysis_method = VALUES(analysis_method),
|
||||||
|
analysis_level = VALUES(analysis_level),
|
||||||
|
is_web_selectable = VALUES(is_web_selectable),
|
||||||
|
is_accredited = VALUES(is_accredited),
|
||||||
|
iddatadb = VALUES(iddatadb),
|
||||||
|
idmatrice = VALUES(idmatrice),
|
||||||
|
updated_at = CURRENT_TIMESTAMP
|
||||||
|
");
|
||||||
|
|
||||||
|
$stmt->execute([
|
||||||
|
':part_id' => $partId,
|
||||||
|
':iddatadb' => $iddatadb ?: null,
|
||||||
|
':idmatrice' => $idmatrice ?: null,
|
||||||
|
':analysis_recordkey' => $analysisRecordkey,
|
||||||
|
':analysis_name' => $analysisName ?: null,
|
||||||
|
':analysis_method' => $analysisMethod ?: null,
|
||||||
|
':analysis_level' => $analysisLevel,
|
||||||
|
':is_web_selectable' => $isWebSelectable,
|
||||||
|
':is_accredited' => $isAccredited,
|
||||||
|
]);
|
||||||
|
|
||||||
|
echo json_encode([
|
||||||
|
'success' => true,
|
||||||
|
'message' => 'Association saved'
|
||||||
|
]);
|
||||||
|
} catch (Throwable $e) {
|
||||||
|
http_response_code(500);
|
||||||
|
echo json_encode([
|
||||||
|
'success' => false,
|
||||||
|
'message' => $e->getMessage()
|
||||||
|
]);
|
||||||
|
}
|
||||||
File diff suppressed because one or more lines are too long
@ -653,7 +653,7 @@
|
|||||||
"IdSchemaCustomFields": 163,
|
"IdSchemaCustomFields": 163,
|
||||||
"ConteggioClienti": 0,
|
"ConteggioClienti": 0,
|
||||||
"Nome": "Devred",
|
"Nome": "Devred",
|
||||||
"Descrizione": "Schema creato per cliente DEVRED\r\nGR 18\/03\/2024\r\n\r\n"
|
"Descrizione": "Schema creato per cliente DEVRED\r\nGR 18\/03\/2024 aggiornato il 23\/04\/2026\r\n\r\n"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"IdSchemaCustomFields": 164,
|
"IdSchemaCustomFields": 164,
|
||||||
@ -760,8 +760,8 @@
|
|||||||
{
|
{
|
||||||
"IdSchemaCustomFields": 182,
|
"IdSchemaCustomFields": 182,
|
||||||
"ConteggioClienti": 0,
|
"ConteggioClienti": 0,
|
||||||
"Nome": "Ralph Lauren - All testing V.6",
|
"Nome": "Ralph Lauren - All testing V.10",
|
||||||
"Descrizione": null
|
"Descrizione": "AGGIORNAMENTO AL 16\/03\/2026 per estrazione fatturazione"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"IdSchemaCustomFields": 183,
|
"IdSchemaCustomFields": 183,
|
||||||
@ -876,6 +876,12 @@
|
|||||||
"ConteggioClienti": 0,
|
"ConteggioClienti": 0,
|
||||||
"Nome": "ROSSIMODA",
|
"Nome": "ROSSIMODA",
|
||||||
"Descrizione": "Per tutti i campioni di ROSSIMODA\r\n\r\n"
|
"Descrizione": "Per tutti i campioni di ROSSIMODA\r\n\r\n"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"IdSchemaCustomFields": 202,
|
||||||
|
"ConteggioClienti": 0,
|
||||||
|
"Nome": "LIMS-CIM - MAX MARA",
|
||||||
|
"Descrizione": "Schema per MAX MARA scambio dati Database"
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
118
public/userarea/search_clienti.php
Normal file
118
public/userarea/search_clienti.php
Normal file
@ -0,0 +1,118 @@
|
|||||||
|
<?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);
|
||||||
|
|
||||||
|
$q = mb_strtolower(trim($_GET['q'] ?? ''));
|
||||||
|
$limit = max(1, min(50, intval($_GET['limit'] ?? 20)));
|
||||||
|
$id = isset($_GET['id']) ? intval($_GET['id']) : null;
|
||||||
|
|
||||||
|
function formatClientLabel(array $client): string
|
||||||
|
{
|
||||||
|
$name = trim($client['Nominativo'] ?? '');
|
||||||
|
$id = trim((string)($client['IdCliente'] ?? ''));
|
||||||
|
$code = trim((string)($client['CodiceCliente'] ?? ''));
|
||||||
|
|
||||||
|
$parts = explode('_', $code);
|
||||||
|
$suffix = trim($parts[1] ?? '');
|
||||||
|
|
||||||
|
if ($suffix === '' && $code !== '') {
|
||||||
|
$suffix = substr($code, 0, 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($suffix === '') {
|
||||||
|
$suffix = '--';
|
||||||
|
}
|
||||||
|
|
||||||
|
return $name . ' - ' . $suffix . ' (ID: ' . $id . ')';
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
// Load from cache or API
|
||||||
|
$cacheFile = __DIR__ . '/cache/clienti.json';
|
||||||
|
|
||||||
|
if (file_exists($cacheFile) && (time() - filemtime($cacheFile) < 3600)) {
|
||||||
|
$data = json_decode(file_get_contents($cacheFile), true);
|
||||||
|
} else {
|
||||||
|
$api = VisualLimsApiClient::getInstance();
|
||||||
|
|
||||||
|
$params = [
|
||||||
|
'$select' => 'IdCliente,Nominativo,CodiceCliente',
|
||||||
|
'$orderby' => 'Nominativo asc'
|
||||||
|
];
|
||||||
|
|
||||||
|
$data = $api->get("Cliente?" . http_build_query($params));
|
||||||
|
|
||||||
|
if (!is_dir(__DIR__ . '/cache')) {
|
||||||
|
mkdir(__DIR__ . '/cache', 0777, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
file_put_contents($cacheFile, json_encode($data));
|
||||||
|
}
|
||||||
|
|
||||||
|
$clients = $data['value'] ?? [];
|
||||||
|
|
||||||
|
// If requesting by specific ID, used for loading selected value
|
||||||
|
if ($id !== null) {
|
||||||
|
foreach ($clients as $c) {
|
||||||
|
if ((int)$c['IdCliente'] === $id) {
|
||||||
|
echo json_encode([
|
||||||
|
'results' => [[
|
||||||
|
'id' => $c['IdCliente'],
|
||||||
|
'text' => formatClientLabel($c),
|
||||||
|
'IdCliente' => $c['IdCliente'],
|
||||||
|
'Nominativo' => trim($c['Nominativo'] ?? ''),
|
||||||
|
'CodiceCliente' => trim($c['CodiceCliente'] ?? '')
|
||||||
|
]]
|
||||||
|
]);
|
||||||
|
exit;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
echo json_encode(['results' => []]);
|
||||||
|
exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Search by query
|
||||||
|
$results = [];
|
||||||
|
|
||||||
|
foreach ($clients as $c) {
|
||||||
|
$name = trim($c['Nominativo'] ?? '');
|
||||||
|
$code = trim($c['CodiceCliente'] ?? '');
|
||||||
|
|
||||||
|
if (
|
||||||
|
$q === '' ||
|
||||||
|
mb_strpos(mb_strtolower($name), $q) !== false ||
|
||||||
|
mb_strpos(mb_strtolower($code), $q) !== false
|
||||||
|
) {
|
||||||
|
$results[] = [
|
||||||
|
'id' => $c['IdCliente'],
|
||||||
|
'text' => formatClientLabel($c),
|
||||||
|
'IdCliente' => $c['IdCliente'],
|
||||||
|
'Nominativo' => $name,
|
||||||
|
'CodiceCliente' => $code
|
||||||
|
];
|
||||||
|
|
||||||
|
if (count($results) >= $limit) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
echo json_encode(['results' => $results]);
|
||||||
|
} catch (Exception $e) {
|
||||||
|
http_response_code(500);
|
||||||
|
echo json_encode(['error' => $e->getMessage()]);
|
||||||
|
}
|
||||||
66
public/userarea/search_customfield_values.php
Normal file
66
public/userarea/search_customfield_values.php
Normal file
@ -0,0 +1,66 @@
|
|||||||
|
<?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);
|
||||||
|
|
||||||
|
$fieldId = intval($_GET['field_id'] ?? 0);
|
||||||
|
$q = mb_strtolower(trim($_GET['q'] ?? ''));
|
||||||
|
$id = isset($_GET['id']) ? intval($_GET['id']) : null;
|
||||||
|
$limit = max(1, min(50, intval($_GET['limit'] ?? 20)));
|
||||||
|
|
||||||
|
if (!$fieldId) {
|
||||||
|
echo json_encode(['results' => []]);
|
||||||
|
exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
$cacheDir = __DIR__ . '/cache';
|
||||||
|
$cacheFile = $cacheDir . '/customfield_' . $fieldId . '.json';
|
||||||
|
|
||||||
|
if (file_exists($cacheFile) && (time() - filemtime($cacheFile) < 3600)) {
|
||||||
|
$values = json_decode(file_get_contents($cacheFile), true);
|
||||||
|
} else {
|
||||||
|
$api = VisualLimsApiClient::getInstance();
|
||||||
|
$data = $api->get("CustomField($fieldId)?\$expand=CustomFieldsValues");
|
||||||
|
$values = $data['CustomFieldsValues'] ?? [];
|
||||||
|
if (!is_dir($cacheDir)) mkdir($cacheDir, 0777, true);
|
||||||
|
file_put_contents($cacheFile, json_encode($values));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Lookup by ID
|
||||||
|
if ($id !== null) {
|
||||||
|
foreach ($values as $v) {
|
||||||
|
if ((int)($v['IdCustomFieldsValue'] ?? 0) === $id) {
|
||||||
|
echo json_encode(['results' => [['id' => $v['IdCustomFieldsValue'], 'text' => $v['Valore'] ?? '']]]);
|
||||||
|
exit;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
echo json_encode(['results' => []]);
|
||||||
|
exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Search by query
|
||||||
|
$results = [];
|
||||||
|
foreach ($values as $v) {
|
||||||
|
$text = $v['Valore'] ?? '';
|
||||||
|
if ($q === '' || mb_strpos(mb_strtolower($text), $q) !== false) {
|
||||||
|
$results[] = ['id' => $v['IdCustomFieldsValue'], 'text' => $text];
|
||||||
|
if (count($results) >= $limit) break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Sort alphabetically
|
||||||
|
usort($results, fn($a, $b) => strcasecmp($a['text'], $b['text']));
|
||||||
|
|
||||||
|
echo json_encode(['results' => $results]);
|
||||||
|
} catch (Exception $e) {
|
||||||
|
http_response_code(500);
|
||||||
|
echo json_encode(['error' => $e->getMessage()]);
|
||||||
|
}
|
||||||
73
public/userarea/search_matrici.php
Normal file
73
public/userarea/search_matrici.php
Normal file
@ -0,0 +1,73 @@
|
|||||||
|
<?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; }
|
||||||
|
|
||||||
|
header('Content-Type: application/json');
|
||||||
|
ini_set('display_errors', '0');
|
||||||
|
|
||||||
|
$q = mb_strtolower(trim($_GET['q'] ?? ''));
|
||||||
|
$id = isset($_GET['id']) ? intval($_GET['id']) : null;
|
||||||
|
$limit = max(1, min(50, intval($_GET['limit'] ?? 20)));
|
||||||
|
$macro = trim($_GET['macro'] ?? '');
|
||||||
|
|
||||||
|
$cacheFile = __DIR__ . '/cache/matrici.json';
|
||||||
|
|
||||||
|
if (!file_exists($cacheFile)) {
|
||||||
|
// Trigger cache creation
|
||||||
|
require_once dirname(__DIR__, 2) . '/vendor/autoload.php';
|
||||||
|
require_once __DIR__ . '/class/VisualLimsApiClient.class.php';
|
||||||
|
$api = VisualLimsApiClient::getInstance();
|
||||||
|
$data = $api->get('Matrice');
|
||||||
|
$matrici = [];
|
||||||
|
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']));
|
||||||
|
if (!is_dir(__DIR__ . '/cache')) mkdir(__DIR__ . '/cache', 0777, true);
|
||||||
|
file_put_contents($cacheFile, json_encode(['success' => true, 'value' => $matrici]));
|
||||||
|
} else {
|
||||||
|
$cached = json_decode(file_get_contents($cacheFile), true);
|
||||||
|
$matrici = $cached['value'] ?? [];
|
||||||
|
}
|
||||||
|
|
||||||
|
// Lookup by ID
|
||||||
|
if ($id !== null) {
|
||||||
|
foreach ($matrici as $m) {
|
||||||
|
if ((int)$m['IdMatrice'] === $id) {
|
||||||
|
echo json_encode(['results' => [['id' => $m['IdMatrice'], 'text' => $m['NomeMatrice']]]]);
|
||||||
|
exit;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
echo json_encode(['results' => []]);
|
||||||
|
exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Return unique MacroMatrice list
|
||||||
|
if (isset($_GET['macro_list'])) {
|
||||||
|
$macros = [];
|
||||||
|
foreach ($matrici as $m) {
|
||||||
|
$mv = $m['MacroMatrice'] ?? '';
|
||||||
|
if ($mv !== '' && !in_array($mv, $macros, true)) $macros[] = $mv;
|
||||||
|
}
|
||||||
|
sort($macros);
|
||||||
|
echo json_encode(['success' => true, 'value' => $macros]);
|
||||||
|
exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Search (with optional MacroMatrice filter)
|
||||||
|
$results = [];
|
||||||
|
foreach ($matrici as $m) {
|
||||||
|
$nome = $m['NomeMatrice'] ?? '';
|
||||||
|
if ($macro !== '' && ($m['MacroMatrice'] ?? '') !== $macro) continue;
|
||||||
|
if ($q === '' || mb_strpos(mb_strtolower($nome), $q) !== false) {
|
||||||
|
$results[] = ['id' => $m['IdMatrice'], 'text' => $nome];
|
||||||
|
if (count($results) >= $limit) break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
echo json_encode(['results' => $results]);
|
||||||
Some files were not shown because too many files have changed in this diff Show More
Loading…
x
Reference in New Issue
Block a user