Compare commits
1 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 9969cc9edc |
+18
-7
@@ -7,9 +7,9 @@ LOG_CHANNEL=stack
|
||||
|
||||
DB_CONNECTION=mysql
|
||||
DB_HOST="localhost"
|
||||
DB_DATABASE="xxxx"
|
||||
DB_USERNAME="xxxx"
|
||||
DB_PASSWORD="xxxxx"
|
||||
DB_DATABASE="trfcertest"
|
||||
DB_USERNAME="solocla"
|
||||
DB_PASSWORD="xxxxxxx"
|
||||
DB_PREFIX="auth_"
|
||||
|
||||
BROADCAST_DRIVER=log
|
||||
@@ -41,8 +41,19 @@ MIX_PUSHER_APP_CLUSTER="${PUSHER_APP_CLUSTER}"
|
||||
|
||||
# Credenziali API VisualLims
|
||||
SIMULATE_EXPORT_LIMS=true
|
||||
API_BASE_URL=https://bvcpsitaly-elims.com/limsapi
|
||||
API_USERNAME=WebApiUser
|
||||
API_PASSWORD=webapiuser01
|
||||
API_BASE_URL=https://93.43.5.102/limsapi
|
||||
API_USERNAME=xxxx
|
||||
API_PASSWORD=XXXX
|
||||
|
||||
BASE_URL=http://localhost:8000/userarea/
|
||||
BASE_URL=http://localhost:8000/userarea/
|
||||
|
||||
# Credenziali ENTRAID
|
||||
|
||||
AZURE_CLIENT_ID=your-client-id
|
||||
AZURE_CLIENT_SECRET=your-client-secret
|
||||
AZURE_REDIRECT_URI=https://your-app.com/auth/azure/callback
|
||||
AZURE_TENANT_ID=
|
||||
|
||||
MICROSOFT_CLIENT_ID=your_client_id_here
|
||||
MICROSOFT_CLIENT_SECRET=your_client_secret_here
|
||||
MICROSOFT_REDIRECT_URI="${APP_URL}/auth/microsoft/callback"
|
||||
+43
-34
@@ -1,57 +1,66 @@
|
||||
.DS_Store
|
||||
|
||||
/node_modules
|
||||
/public/hot
|
||||
/public/storage
|
||||
/storage/*.key
|
||||
/vendor
|
||||
|
||||
/.idea
|
||||
/.fleet
|
||||
/.vscode
|
||||
/.vagrant
|
||||
|
||||
/public/hot
|
||||
/public/storage
|
||||
/public/build
|
||||
|
||||
/storage/*.key
|
||||
|
||||
Homestead.json
|
||||
Homestead.yaml
|
||||
npm-debug.log
|
||||
yarn-error.log
|
||||
.env
|
||||
.phpunit.result.cache
|
||||
.php_cs.cache
|
||||
/documentation
|
||||
/.phpunit.cache
|
||||
/public/build
|
||||
.env.backup
|
||||
.env.production
|
||||
auth.json
|
||||
|
||||
.phpunit.result.cache
|
||||
.php_cs.cache
|
||||
/.phpunit.cache
|
||||
|
||||
npm-debug.log
|
||||
yarn-error.log
|
||||
|
||||
/documentation
|
||||
|
||||
# --- Runtime / Debug (userarea) ---
|
||||
# File di debug e temporanei JSON e log
|
||||
/public/userarea/*.json
|
||||
/public/userarea/*.log
|
||||
/public/userarea/*.txt
|
||||
/public/userarea/*_response.json
|
||||
/public/userarea/customfield_values_response.json
|
||||
/public/userarea/error_log.txt
|
||||
/public/userarea/import_debug.log
|
||||
/public/userarea/last_url.txt
|
||||
/public/userarea/logaspi/
|
||||
/public/userarea/logs/api/
|
||||
/public/userarea/logs/api/**
|
||||
/public/userarea/photostrf/
|
||||
|
||||
# File di log nella sottocartella class
|
||||
/public/userarea/class/*.log
|
||||
/public/userarea/class/curl_auth_debug.log
|
||||
/public/userarea/class/curl_request_debug.log
|
||||
|
||||
# File XLSX temporanei importati
|
||||
/public/userarea/imported_trf/*.xlsx
|
||||
/public/userarea/xlstemplates/*.xlsx
|
||||
|
||||
# Cartelle foto generate
|
||||
# Ignora cartelle di foto generate
|
||||
/public/photostrf/
|
||||
/public/photostrf/qrcodes/
|
||||
public/userarea/import_debug.log
|
||||
public/userarea/last_url.txt
|
||||
public/userarea/class/curl_auth_debug.log
|
||||
public/userarea/class/curl_request_debug.log
|
||||
public/userarea/last_url.txt
|
||||
public/userarea/class/curl_auth_debug.log
|
||||
public/userarea/class/curl_request_debug.log
|
||||
|
||||
# Ignora tutti i log ovunque
|
||||
*.log
|
||||
# Ignora tutti i log
|
||||
*.log
|
||||
|
||||
|
||||
# Ignora cartella photostrf in public/userarea
|
||||
/public/userarea/photostrf/
|
||||
public/userarea/customfield_values_response.json
|
||||
/public/userarea/logaspi/
|
||||
|
||||
public/userarea/logsapi/campione_762_1.json
|
||||
public/userarea/logsapi/campione_763_1.json
|
||||
public/userarea/logsapi/campione_762_2.json
|
||||
public/userarea/logsapi/campione_763_2.json
|
||||
public/userarea/logsapi/commessaweb_create_762.json
|
||||
public/userarea/logsapi/commessaweb_create_763.json
|
||||
public/userarea/logsapi/commessaweb_customfields_762.json
|
||||
public/userarea/logsapi/commessaweb_customfields_763.json
|
||||
public/userarea/logsapi/commessaweb_invia_762.json
|
||||
public/userarea/logsapi/commessaweb_invia_763.json
|
||||
public/userarea/logsapi/last_auth_url.txt
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,6 +0,0 @@
|
||||
# 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
|
||||
@@ -1,91 +0,0 @@
|
||||
<?php
|
||||
header('Content-Type: application/json');
|
||||
|
||||
ini_set('display_errors', 1);
|
||||
error_reporting(E_ALL);
|
||||
|
||||
require_once(__DIR__ . '/include/headscript.php'); // Auth + $iduserlogin + DBHandlerSelect
|
||||
|
||||
try {
|
||||
if ($_SERVER['REQUEST_METHOD'] !== 'POST') {
|
||||
throw new Exception('Invalid request method');
|
||||
}
|
||||
|
||||
$iddatadb = isset($_POST['iddatadb']) ? (int)$_POST['iddatadb'] : 0;
|
||||
|
||||
if ($iddatadb <= 0) {
|
||||
throw new Exception('Missing iddatadb');
|
||||
}
|
||||
|
||||
// ✅ Supporta sia singola stringa (con |) che array part_descriptions[]
|
||||
$parts = [];
|
||||
|
||||
if (isset($_POST['part_descriptions']) && is_array($_POST['part_descriptions'])) {
|
||||
$parts = $_POST['part_descriptions'];
|
||||
} else {
|
||||
$raw = isset($_POST['part_description']) ? (string)$_POST['part_description'] : '';
|
||||
// split su "|" per multi-part
|
||||
$parts = preg_split('/\s*\|\s*/', $raw);
|
||||
}
|
||||
|
||||
// normalizza: trim + rimuovi vuoti + dedup
|
||||
$cleanParts = [];
|
||||
foreach ($parts as $p) {
|
||||
$p = trim((string)$p);
|
||||
if ($p === '') continue;
|
||||
|
||||
// limita lunghezza come da DB varchar(255)
|
||||
if (mb_strlen($p) > 255) {
|
||||
$p = mb_substr($p, 0, 255);
|
||||
}
|
||||
$cleanParts[] = $p;
|
||||
}
|
||||
$cleanParts = array_values(array_unique($cleanParts));
|
||||
|
||||
if (count($cleanParts) === 0) {
|
||||
throw new Exception('Part description is empty');
|
||||
}
|
||||
|
||||
$db = DBHandlerSelect::getInstance();
|
||||
$pdo = $db->getConnection();
|
||||
|
||||
$pdo->beginTransaction();
|
||||
|
||||
try {
|
||||
// prende il prossimo part_number per iddatadb
|
||||
$stmtMax = $pdo->prepare("SELECT COALESCE(MAX(part_number), 0) AS maxnum FROM identification_parts WHERE iddatadb = ?");
|
||||
$stmtMax->execute([$iddatadb]);
|
||||
$nextNumber = (int)$stmtMax->fetchColumn() + 1;
|
||||
|
||||
$stmtIns = $pdo->prepare("
|
||||
INSERT INTO identification_parts (iddatadb, part_number, part_description)
|
||||
VALUES (?, ?, ?)
|
||||
");
|
||||
|
||||
$insertedIds = [];
|
||||
|
||||
foreach ($cleanParts as $p) {
|
||||
$ok = $stmtIns->execute([$iddatadb, $nextNumber, $p]);
|
||||
if (!$ok) {
|
||||
throw new Exception('Insert failed');
|
||||
}
|
||||
$insertedIds[] = $pdo->lastInsertId();
|
||||
$nextNumber++;
|
||||
}
|
||||
|
||||
$pdo->commit();
|
||||
|
||||
echo json_encode([
|
||||
'success' => true,
|
||||
'message' => 'Parts added',
|
||||
'count' => count($insertedIds),
|
||||
'ids' => $insertedIds
|
||||
]);
|
||||
} catch (Exception $e) {
|
||||
if ($pdo->inTransaction()) $pdo->rollBack();
|
||||
throw $e;
|
||||
}
|
||||
} catch (Exception $e) {
|
||||
echo json_encode(['success' => false, 'message' => $e->getMessage()]);
|
||||
}
|
||||
exit;
|
||||
@@ -1,73 +0,0 @@
|
||||
<?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()]);
|
||||
}
|
||||
+119
-244
@@ -12,16 +12,12 @@ $(document).ready(function () {
|
||||
|
||||
let photoAnnotations = {};
|
||||
let partColors = {};
|
||||
let partSizes = {}; // memorizza la dimensione specifica per parte
|
||||
let selectedPartNumber = null;
|
||||
let unsavedChanges = false;
|
||||
let fabricCanvas = null;
|
||||
let descriptionTextbox = null;
|
||||
let markerObjects = {};
|
||||
let nextMarkerId = 1;
|
||||
let partsListData = [];
|
||||
// DIMENSIONE GLOBALE MARKER
|
||||
let globalMarkerSize = 16;
|
||||
|
||||
// ===================
|
||||
// MODAL INITIALIZATION
|
||||
@@ -33,8 +29,6 @@ $(document).ready(function () {
|
||||
trfHeader,
|
||||
});
|
||||
|
||||
$("#annotationsModal").attr("data-iddatadb", iddatadb);
|
||||
|
||||
if (!iddatadb && !idquotations) {
|
||||
const errorMsg = $(
|
||||
'<div class="alert alert-danger temp-alert" role="alert">Errore: ID TRF mancante. Impossibile inizializzare il modale delle annotazioni.</div>',
|
||||
@@ -73,9 +67,6 @@ $(document).ready(function () {
|
||||
});
|
||||
}
|
||||
modal.show();
|
||||
// Inizializza slider dimensione marker
|
||||
$("#markerSizeSlider").val(globalMarkerSize);
|
||||
$("#markerSizeValue").text(globalMarkerSize + "px");
|
||||
|
||||
// Debug: Verifica presenza elementi DOM
|
||||
console.log(
|
||||
@@ -137,7 +128,6 @@ $(document).ready(function () {
|
||||
}
|
||||
descriptionTextbox = null;
|
||||
markerObjects = {};
|
||||
globalMarkerSize = 16;
|
||||
$("#photoSelectorContainerAnnotations").empty().hide();
|
||||
$("#samplePhotoAnnotations").attr("src", "");
|
||||
$("#partsListAnnotations").empty();
|
||||
@@ -154,30 +144,6 @@ $(document).ready(function () {
|
||||
$(":focus").blur();
|
||||
});
|
||||
|
||||
// SLIDER DIMENSIONE MARKER
|
||||
$(document)
|
||||
.off("input", "#markerSizeSlider")
|
||||
.on("input", "#markerSizeSlider", function () {
|
||||
globalMarkerSize = parseInt($(this).val());
|
||||
$("#markerSizeValue").text(globalMarkerSize + "px");
|
||||
|
||||
partsListData.forEach(function (part) {
|
||||
if (part && part.part_number != null) {
|
||||
partSizes[part.part_number] = globalMarkerSize;
|
||||
}
|
||||
});
|
||||
|
||||
$("#partsListAnnotations .marker-size-slider").val(
|
||||
globalMarkerSize,
|
||||
);
|
||||
$("#partsListAnnotations .marker-size-value").text(
|
||||
globalMarkerSize + "px",
|
||||
);
|
||||
|
||||
updateMarkers();
|
||||
markUnsaved();
|
||||
});
|
||||
|
||||
// ===================
|
||||
// PHOTO LOADERS
|
||||
// ===================
|
||||
@@ -381,13 +347,22 @@ $(document).ready(function () {
|
||||
|
||||
const partColor =
|
||||
partColors[selectedPartNumber] || "#ff0000";
|
||||
photoAnnotations[currentPhoto].markers.push({
|
||||
id: nextMarkerId++,
|
||||
partNumber: selectedPartNumber,
|
||||
x,
|
||||
y,
|
||||
color: partColor,
|
||||
});
|
||||
const existingMarker = photoAnnotations[
|
||||
currentPhoto
|
||||
].markers.find((m) => m.partNumber == selectedPartNumber);
|
||||
|
||||
if (existingMarker) {
|
||||
existingMarker.x = x;
|
||||
existingMarker.y = y;
|
||||
existingMarker.color = partColor;
|
||||
} else {
|
||||
photoAnnotations[currentPhoto].markers.push({
|
||||
partNumber: selectedPartNumber,
|
||||
x,
|
||||
y,
|
||||
color: partColor,
|
||||
});
|
||||
}
|
||||
|
||||
console.log("Marker aggiunto/spostato:", {
|
||||
partNumber: selectedPartNumber,
|
||||
@@ -397,6 +372,8 @@ $(document).ready(function () {
|
||||
});
|
||||
updateMarkers();
|
||||
markUnsaved();
|
||||
selectedPartNumber = null;
|
||||
$("#partsListAnnotations li").removeClass("active");
|
||||
});
|
||||
|
||||
fabricCanvas.upperCanvasEl.focus();
|
||||
@@ -488,89 +465,113 @@ $(document).ready(function () {
|
||||
);
|
||||
|
||||
// ===================
|
||||
// TORNA AL MODALE PARTI (modal_partsTable.php)
|
||||
// BACK TO PARTS MODAL
|
||||
// ===================
|
||||
$(document)
|
||||
.off("click.backToParts", "#backToPartsBtnAnnotations")
|
||||
.on("click.backToParts", "#backToPartsBtnAnnotations", function (e) {
|
||||
e.preventDefault();
|
||||
e.stopPropagation();
|
||||
console.log(
|
||||
"Evento click su #backToPartsBtnAnnotations, ID elemento:",
|
||||
$(this).attr("id"),
|
||||
);
|
||||
if (!$("#backToPartsBtnAnnotations").length) {
|
||||
console.error(
|
||||
"Pulsante #backToPartsBtnAnnotations non trovato nel DOM.",
|
||||
);
|
||||
const errorMsg = $(
|
||||
'<div class="alert alert-danger temp-alert" role="alert">Errore: Pulsante #backToPartsBtnAnnotations non trovato nel DOM.</div>',
|
||||
);
|
||||
$("#annotationsModal .modal-body").prepend(errorMsg);
|
||||
setTimeout(function () {
|
||||
errorMsg.fadeOut(500, function () {
|
||||
$(this).remove();
|
||||
});
|
||||
}, 5000);
|
||||
return;
|
||||
}
|
||||
|
||||
// Controlla modifiche non salvate
|
||||
if (
|
||||
unsavedChanges &&
|
||||
!confirm(
|
||||
"Hai modifiche non salvate. Vuoi davvero tornare al modale delle parti?",
|
||||
)
|
||||
) {
|
||||
console.log(
|
||||
"Tornare al modale delle parti annullato a causa di modifiche non salvate.",
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
// Chiudi annotationsModal
|
||||
const annotationsModalElement =
|
||||
document.getElementById("annotationsModal");
|
||||
const annotationsModal = bootstrap.Modal.getInstance(
|
||||
annotationsModalElement,
|
||||
);
|
||||
if (annotationsModal) {
|
||||
annotationsModal.hide();
|
||||
} else {
|
||||
console.error(
|
||||
"Impossibile trovare l'istanza del modale #annotationsModal.",
|
||||
);
|
||||
}
|
||||
|
||||
// Apri partsModal
|
||||
const iddatadb = $("#annotationsModal").data("iddatadb");
|
||||
const idquotations = $("#annotationsModal").data("idquotations");
|
||||
const trfHeader = $("#trfHeaderAnnotations").text();
|
||||
|
||||
// CHIUDI MODALE ANNOTAZIONI IN MODO SICURO
|
||||
const annotationsModalElement =
|
||||
document.getElementById("annotationsModal");
|
||||
if (annotationsModalElement) {
|
||||
const modalInstance = bootstrap.Modal.getInstance(
|
||||
annotationsModalElement,
|
||||
);
|
||||
if (modalInstance) {
|
||||
modalInstance.hide();
|
||||
} else {
|
||||
$(annotationsModalElement).modal("hide"); // fallback jQuery
|
||||
}
|
||||
}
|
||||
console.log("Torno a modal_partsTable.php con:", {
|
||||
console.log("Apertura partsModal con:", {
|
||||
iddatadb,
|
||||
idquotations,
|
||||
trfHeader,
|
||||
});
|
||||
// CARICA E APRI MODALE PARTI
|
||||
$.get(
|
||||
"modal_partsTable.php",
|
||||
{
|
||||
iddatadb: iddatadb || "",
|
||||
idquotations: idquotations || "",
|
||||
trfHeader: trfHeader,
|
||||
},
|
||||
function (data) {
|
||||
// Rimuovi vecchio modale (con ID corretto)
|
||||
$("#partsTableModal").remove();
|
||||
$(".modal-backdrop").remove();
|
||||
|
||||
// Aggiungi nuovo modale
|
||||
$("body").append(data);
|
||||
|
||||
// Apri con Bootstrap 5
|
||||
const partsModalElement =
|
||||
document.getElementById("partsTableModal");
|
||||
if (partsModalElement) {
|
||||
const modal = new bootstrap.Modal(partsModalElement, {
|
||||
backdrop: true,
|
||||
keyboard: true,
|
||||
focus: true,
|
||||
});
|
||||
modal.show();
|
||||
} else {
|
||||
let iddatadb =
|
||||
$("#annotationsModal").attr("data-iddatadb");
|
||||
|
||||
$(
|
||||
"button.parts-btn[data-iddatadb='" +
|
||||
iddatadb +
|
||||
"']",
|
||||
).trigger("click");
|
||||
}
|
||||
},
|
||||
).fail(function (xhr) {
|
||||
console.error("Errore caricamento modale parti:", xhr);
|
||||
alert(
|
||||
"Errore 404: modal_partsTable.php non trovato o errore server.",
|
||||
const partsModalElement = document.getElementById("partsModal");
|
||||
if (!partsModalElement) {
|
||||
console.error("Elemento #partsModal non trovato nel DOM.");
|
||||
const errorMsg = $(
|
||||
'<div class="alert alert-danger temp-alert" role="alert">Errore: Il modale delle parti non è presente nel DOM.</div>',
|
||||
);
|
||||
});
|
||||
$("#annotationsModal .modal-body").prepend(errorMsg);
|
||||
setTimeout(function () {
|
||||
errorMsg.fadeOut(500, function () {
|
||||
$(this).remove();
|
||||
});
|
||||
}, 5000);
|
||||
return;
|
||||
}
|
||||
|
||||
let partsModal = bootstrap.Modal.getInstance(partsModalElement);
|
||||
if (!partsModal) {
|
||||
partsModal = new bootstrap.Modal(partsModalElement, {
|
||||
backdrop: true,
|
||||
keyboard: true,
|
||||
focus: true,
|
||||
});
|
||||
}
|
||||
|
||||
// Inizializza il modale delle parti
|
||||
if (typeof window.initPartsModal === "function") {
|
||||
window.initPartsModal(iddatadb, idquotations, trfHeader);
|
||||
partsModal.show();
|
||||
console.log("partsModal aperto con successo.");
|
||||
} else {
|
||||
console.error("Funzione initPartsModal non definita.");
|
||||
const errorMsg = $(
|
||||
'<div class="alert alert-danger temp-alert" role="alert">Errore: Funzione initPartsModal non trovata.</div>',
|
||||
);
|
||||
$("#annotationsModal .modal-body").prepend(errorMsg);
|
||||
setTimeout(function () {
|
||||
errorMsg.fadeOut(500, function () {
|
||||
$(this).remove();
|
||||
});
|
||||
}, 5000);
|
||||
}
|
||||
});
|
||||
|
||||
// ===================
|
||||
// PARTS LIST
|
||||
// ===================
|
||||
@@ -632,19 +633,12 @@ $(document).ready(function () {
|
||||
)
|
||||
.join("");
|
||||
const listItem = `
|
||||
<li class="list-group-item" data-part-number="${partNumber}">
|
||||
<span class="part-info" style="cursor: pointer;">${partNumber} - ${partDescription}</span>
|
||||
<div class="color-picker-container" style="display:inline-block; position: relative; overflow: visible; z-index: 2000; margin-left:5px;">
|
||||
<div class="color-option selected-color" style="background-color: ${partColor}; width: 20px; height: 20px; display: inline-block; cursor: pointer; pointer-events: auto;"></div>
|
||||
<div class="color-picker" style="display: none; position: absolute; right: 0; z-index: 2000; background: #fff; border: 1px solid #ccc; padding: 5px;">${colorOptions}</div>
|
||||
</div>
|
||||
<div style="display:inline-flex; align-items:center; gap:4px; margin-left:5px;">
|
||||
<input type="range" class="marker-size-slider"
|
||||
min="12" max="48" step="2"
|
||||
value="${partSizes[partNumber] || globalMarkerSize}"
|
||||
style="width:70px;">
|
||||
<span class="marker-size-value" style="font-size:0.8rem;">${partSizes[partNumber] || globalMarkerSize}px</span>
|
||||
</div>
|
||||
<li class="list-group-item" data-part-number="${partNumber}">
|
||||
<span class="part-info" style="cursor: pointer;">${partNumber} - ${partDescription}</span>
|
||||
<div class="color-picker-container" style="display: inline-block; position: relative; overflow: visible; z-index: 2000;">
|
||||
<div class="color-option selected-color" style="background-color: ${partColor}; width: 20px; height: 20px; display: inline-block; margin-left: 5px; cursor: pointer; pointer-events: auto;"></div>
|
||||
<div class="color-picker" style="display: none; position: absolute; right: 0; z-index: 2000; background: #fff; border: 1px solid #ccc; padding: 5px;">${colorOptions}</div>
|
||||
</div>
|
||||
</li>`;
|
||||
partsListElement.append(listItem);
|
||||
}
|
||||
@@ -669,21 +663,8 @@ $(document).ready(function () {
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
const $listItem = $(this);
|
||||
const partNumber = $listItem.data("part-number");
|
||||
|
||||
if (
|
||||
selectedPartNumber == partNumber &&
|
||||
$listItem.hasClass("active")
|
||||
) {
|
||||
selectedPartNumber = null;
|
||||
$listItem.removeClass("active");
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
selectedPartNumber = partNumber;
|
||||
selectedPartNumber = $listItem.data("part-number");
|
||||
$listItem.addClass("active").siblings().removeClass("active");
|
||||
console.log(
|
||||
"Parte selezionata tramite riga:",
|
||||
@@ -706,88 +687,30 @@ $(document).ready(function () {
|
||||
$picker.toggle();
|
||||
});
|
||||
|
||||
// === Gestione cambio colore ===
|
||||
partsListElement
|
||||
.off("click.colorOption")
|
||||
.on("click.colorOption", ".color-option", function (e) {
|
||||
e.stopPropagation();
|
||||
e.preventDefault();
|
||||
|
||||
const $this = $(this);
|
||||
const color = $this.data("color");
|
||||
const $listItem = $this.closest("li");
|
||||
const partNumber = $listItem.data("part-number");
|
||||
|
||||
console.log(
|
||||
"Cliccato .color-option, colore:",
|
||||
color,
|
||||
"per parte:",
|
||||
partNumber,
|
||||
);
|
||||
|
||||
// Salva il nuovo colore
|
||||
partColors[partNumber] = color;
|
||||
|
||||
// Aggiorna il colore visivo nel selettore
|
||||
$listItem
|
||||
.find(".selected-color")
|
||||
.css("background-color", color);
|
||||
|
||||
let currentPhoto = $("#samplePhotoAnnotations").attr("src");
|
||||
let annotations = photoAnnotations[currentPhoto];
|
||||
|
||||
if (annotations) {
|
||||
annotations.markers.forEach(function (m) {
|
||||
if (m.partNumber == partNumber) {
|
||||
m.color = color;
|
||||
let group = markerObjects[m.id];
|
||||
|
||||
if (group) {
|
||||
let circle = group.item(0);
|
||||
|
||||
circle.set("fill", color);
|
||||
circle.set("stroke", color);
|
||||
}
|
||||
}
|
||||
});
|
||||
fabricCanvas.renderAll();
|
||||
}
|
||||
|
||||
// Chiudi la palette e aggiorna canvas
|
||||
$this.closest(".color-picker").hide();
|
||||
updateMarkers();
|
||||
markUnsaved();
|
||||
});
|
||||
|
||||
// === Slider locale per dimensione marker ===
|
||||
partsListElement
|
||||
.off("input.localMarkerSize")
|
||||
.on("input.localMarkerSize", ".marker-size-slider", function (e) {
|
||||
e.stopPropagation();
|
||||
e.preventDefault();
|
||||
|
||||
// Riferimenti alla parte e valore scelto
|
||||
const $slider = $(this);
|
||||
const partNumber = $slider.closest("li").data("part-number");
|
||||
const newSize = parseInt($slider.val());
|
||||
|
||||
// Aggiorna il valore visivo accanto allo slider
|
||||
$slider.siblings(".marker-size-value").text(newSize + "px");
|
||||
|
||||
// Memorizza la nuova dimensione per quella parte
|
||||
partSizes[partNumber] = newSize;
|
||||
|
||||
// Aggiorna i marker sul canvas
|
||||
updateMarkers();
|
||||
|
||||
// Segnala modifiche non salvate
|
||||
markUnsaved();
|
||||
|
||||
console.log(
|
||||
`Dimensione marker aggiornata per parte ${partNumber}: ${newSize}px`,
|
||||
);
|
||||
});
|
||||
|
||||
$(document)
|
||||
.off("click.colorPicker")
|
||||
.on("click.colorPicker", function (e) {
|
||||
@@ -798,48 +721,6 @@ $(document).ready(function () {
|
||||
});
|
||||
}
|
||||
|
||||
function enableDragDropPartsList() {
|
||||
const list = $("#partsListAnnotations");
|
||||
if (!list.length || typeof $.ui === "undefined" || !$.ui.sortable) {
|
||||
console.warn("jQuery UI o .sortable non disponibile. Ritento...");
|
||||
setTimeout(enableDragDropPartsList, 100);
|
||||
return;
|
||||
}
|
||||
|
||||
if (list.hasClass("ui-sortable")) {
|
||||
list.sortable("destroy"); // evita duplicati
|
||||
}
|
||||
|
||||
list.sortable({
|
||||
items: "li.list-group-item",
|
||||
placeholder: "list-group-item placeholder",
|
||||
axis: "y",
|
||||
containment: "parent",
|
||||
tolerance: "pointer",
|
||||
start: function (e, ui) {
|
||||
ui.item.addClass("dragging");
|
||||
},
|
||||
stop: function (e, ui) {
|
||||
ui.item.removeClass("dragging");
|
||||
const newOrder = [];
|
||||
list.find("li").each(function () {
|
||||
const partNumber = $(this).data("part-number");
|
||||
const part = partsListData.find(
|
||||
(p) => p.part_number == partNumber,
|
||||
);
|
||||
if (part) newOrder.push(part);
|
||||
});
|
||||
partsListData = newOrder;
|
||||
console.log(
|
||||
"Ordine parti aggiornato:",
|
||||
partsListData.map((p) => p.part_number),
|
||||
);
|
||||
markUnsaved();
|
||||
updateMarkers();
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
// Delegazione evento per il checkbox
|
||||
$(document)
|
||||
.off("change.showMix", "#showMixPartsAnnotations")
|
||||
@@ -872,7 +753,6 @@ $(document).ready(function () {
|
||||
}
|
||||
updatePartsList();
|
||||
updateMarkers();
|
||||
setTimeout(enableDragDropPartsList, 50);
|
||||
if (
|
||||
photoAnnotations[$("#samplePhotoAnnotations").attr("src")]
|
||||
?.hasDescriptions
|
||||
@@ -980,9 +860,9 @@ $(document).ready(function () {
|
||||
"updateMarkers chiamato, markerObjects:",
|
||||
Object.keys(markerObjects),
|
||||
);
|
||||
for (let markerId in markerObjects) {
|
||||
fabricCanvas.remove(markerObjects[markerId]);
|
||||
delete markerObjects[markerId];
|
||||
for (let partNumber in markerObjects) {
|
||||
fabricCanvas.remove(markerObjects[partNumber]);
|
||||
delete markerObjects[partNumber];
|
||||
}
|
||||
markerObjects = {};
|
||||
|
||||
@@ -1012,10 +892,8 @@ $(document).ready(function () {
|
||||
return;
|
||||
}
|
||||
|
||||
const size = partSizes[marker.partNumber] || globalMarkerSize;
|
||||
const radius = size / 2;
|
||||
const fontSize = Math.max(10, Math.round(size * 0.65));
|
||||
|
||||
const radius = 12;
|
||||
const fontSize = 16;
|
||||
const markerColor =
|
||||
marker.color || partColors[marker.partNumber] || "#ff0000";
|
||||
|
||||
@@ -1063,9 +941,6 @@ $(document).ready(function () {
|
||||
lockRotation: true,
|
||||
});
|
||||
|
||||
group.markerId = marker.id;
|
||||
group.partNumber = marker.partNumber;
|
||||
|
||||
group.on("moving", function () {
|
||||
marker.x = this.left / photoData.scale;
|
||||
marker.y = this.top / photoData.scale;
|
||||
@@ -1078,7 +953,7 @@ $(document).ready(function () {
|
||||
});
|
||||
|
||||
fabricCanvas.add(group);
|
||||
markerObjects[marker.id] = group;
|
||||
markerObjects[marker.partNumber] = group;
|
||||
});
|
||||
|
||||
fabricCanvas.renderAll();
|
||||
@@ -1138,7 +1013,7 @@ $(document).ready(function () {
|
||||
scaleY: 1,
|
||||
backgroundColor: "transparent",
|
||||
fontFamily: "Arial",
|
||||
fontSize: Math.max(16, Math.round(globalMarkerSize * 0.8)),
|
||||
fontSize: 24,
|
||||
fill: "#000000",
|
||||
padding: 10,
|
||||
editable: false,
|
||||
@@ -1232,9 +1107,9 @@ $(document).ready(function () {
|
||||
}
|
||||
}
|
||||
|
||||
for (let markerId in markerObjects) {
|
||||
fabricCanvas.remove(markerObjects[markerId]);
|
||||
delete markerObjects[markerId];
|
||||
for (let partNumber in markerObjects) {
|
||||
fabricCanvas.remove(markerObjects[partNumber]);
|
||||
delete markerObjects[partNumber];
|
||||
}
|
||||
markerObjects = {};
|
||||
|
||||
@@ -1269,9 +1144,9 @@ $(document).ready(function () {
|
||||
photoAnnotations[currentPhoto].markers.length > 0
|
||||
) {
|
||||
const lastMarker = photoAnnotations[currentPhoto].markers.pop();
|
||||
if (markerObjects[lastMarker.id]) {
|
||||
fabricCanvas.remove(markerObjects[lastMarker.id]);
|
||||
delete markerObjects[lastMarker.id];
|
||||
if (markerObjects[lastMarker.partNumber]) {
|
||||
fabricCanvas.remove(markerObjects[lastMarker.partNumber]);
|
||||
delete markerObjects[lastMarker.partNumber];
|
||||
fabricCanvas.renderAll();
|
||||
}
|
||||
console.log("Ultimo marker rimosso:", lastMarker);
|
||||
|
||||
@@ -24,17 +24,7 @@ class VisualLimsApiClient
|
||||
public static function getInstance()
|
||||
{
|
||||
if (self::$instance === null) {
|
||||
$dotenv = Dotenv::createImmutable(dirname(__DIR__, 3));
|
||||
$dotenv->load();
|
||||
|
||||
$simulate = ($_ENV['SIMULATE_EXPORT_LIMS'] ?? '') === 'true';
|
||||
|
||||
if ($simulate) {
|
||||
require_once __DIR__ . '/VisualLimsApiClientMock.class.php';
|
||||
self::$instance = new VisualLimsApiClientMock();
|
||||
} else {
|
||||
self::$instance = new VisualLimsApiClient();
|
||||
}
|
||||
self::$instance = new VisualLimsApiClient();
|
||||
}
|
||||
return self::$instance;
|
||||
}
|
||||
@@ -203,54 +193,6 @@ class VisualLimsApiClient
|
||||
return json_decode($response, true);
|
||||
}
|
||||
|
||||
/**
|
||||
* POST a file as multipart/form-data (used for photo/attachment uploads).
|
||||
*
|
||||
* @param string $endpoint OData endpoint, e.g. "Campione(613388)/UploadCampioneFile"
|
||||
* @param string $filePath Absolute path to the file on disk
|
||||
* @param string $fileName Original file name to send
|
||||
* @param array $extraFields Additional form fields to include
|
||||
* @return array|null Decoded JSON response
|
||||
*/
|
||||
public function postMultipart($endpoint, $filePath, $fileName, array $extraFields = [])
|
||||
{
|
||||
$token = $this->getToken();
|
||||
$url = "{$this->baseUrl}/api/odata/{$endpoint}";
|
||||
|
||||
$cfile = new CURLFile($filePath, mime_content_type($filePath) ?: 'application/octet-stream', $fileName);
|
||||
|
||||
$payload = array_merge($extraFields, [
|
||||
'file' => $cfile,
|
||||
]);
|
||||
|
||||
$ch = curl_init($url);
|
||||
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
|
||||
curl_setopt($ch, CURLOPT_POST, true);
|
||||
curl_setopt($ch, CURLOPT_POSTFIELDS, $payload);
|
||||
curl_setopt($ch, CURLOPT_HTTPHEADER, [
|
||||
"Authorization: Bearer {$token}",
|
||||
"Accept: application/json",
|
||||
// Content-Type is set automatically to multipart/form-data by cURL
|
||||
]);
|
||||
curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);
|
||||
curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, false);
|
||||
|
||||
$response = curl_exec($ch);
|
||||
$http_code = curl_getinfo($ch, CURLINFO_HTTP_CODE);
|
||||
$curl_error = curl_error($ch);
|
||||
curl_close($ch);
|
||||
|
||||
if ($response === false) {
|
||||
throw new Exception("Errore nella richiesta POST multipart: {$curl_error}");
|
||||
}
|
||||
|
||||
if ($http_code < 200 || $http_code >= 300) {
|
||||
throw new Exception("POST multipart fallito: HTTP {$http_code}, Risposta: " . substr($response, 0, 1000));
|
||||
}
|
||||
|
||||
return json_decode($response, true);
|
||||
}
|
||||
|
||||
public function getBaseUrl()
|
||||
{
|
||||
return $this->baseUrl;
|
||||
|
||||
@@ -1,135 +0,0 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* Mock implementation of VisualLimsApiClient.
|
||||
* Activated when SIMULATE_EXPORT_LIMS=true in .env.
|
||||
* All HTTP calls are skipped; fake but structurally valid data is returned.
|
||||
* Every simulated call is logged via error_log() with a [SIMULATE] prefix.
|
||||
*/
|
||||
class VisualLimsApiClientMock
|
||||
{
|
||||
private int $fakeCommessaId;
|
||||
|
||||
public function __construct()
|
||||
{
|
||||
// Stable fake ID for the lifetime of a single request
|
||||
$this->fakeCommessaId = mt_rand(90001, 99999);
|
||||
error_log("[SIMULATE] VisualLimsApiClientMock initialised (fakeCommessaId={$this->fakeCommessaId})");
|
||||
}
|
||||
|
||||
public function get(string $endpoint): array
|
||||
{
|
||||
error_log("[SIMULATE] GET {$endpoint}");
|
||||
|
||||
// --- Fixed-field dropdown lists ---
|
||||
|
||||
if (str_starts_with($endpoint, 'MoltiplicatorePrezzi')) {
|
||||
return ['value' => [
|
||||
['IdMoltiplicatorePrezzo' => 1, 'Codice' => 'MP-01', 'Descrizione' => 'Standard (1x)'],
|
||||
['IdMoltiplicatorePrezzo' => 2, 'Codice' => 'MP-02', 'Descrizione' => 'Urgente (1.5x)'],
|
||||
['IdMoltiplicatorePrezzo' => 3, 'Codice' => 'MP-03', 'Descrizione' => 'Extra Urgente (2x)'],
|
||||
]];
|
||||
}
|
||||
|
||||
if (str_starts_with($endpoint, 'AnagraficaCertestObject')) {
|
||||
return ['value' => [
|
||||
['IdAnagrafica' => 1, 'Codice' => 'OBJ-01', 'NomeAnagrafica' => 'Articolo Tessile'],
|
||||
['IdAnagrafica' => 2, 'Codice' => 'OBJ-02', 'NomeAnagrafica' => 'Componente Meccanico'],
|
||||
['IdAnagrafica' => 3, 'Codice' => 'OBJ-03', 'NomeAnagrafica' => 'Materiale Plastico'],
|
||||
]];
|
||||
}
|
||||
|
||||
if (str_starts_with($endpoint, 'AnagraficaCertestService')) {
|
||||
return ['value' => [
|
||||
['IdAnagrafica' => 1, 'Codice' => 'SRV-01', 'NomeAnagrafica' => 'Analisi Chimica'],
|
||||
['IdAnagrafica' => 2, 'Codice' => 'SRV-02', 'NomeAnagrafica' => 'Test Meccanico'],
|
||||
['IdAnagrafica' => 3, 'Codice' => 'SRV-03', 'NomeAnagrafica' => 'Prova Ambientale'],
|
||||
]];
|
||||
}
|
||||
|
||||
// Cliente? list — get_clienti.php exits early in simulate mode, but guard here too
|
||||
if (str_starts_with($endpoint, 'Cliente?')) {
|
||||
return ['value' => []];
|
||||
}
|
||||
|
||||
// Cliente(N)?$expand=Responsabili
|
||||
if (str_starts_with($endpoint, 'Cliente(')) {
|
||||
preg_match('/Cliente\((\d+)\)/', $endpoint, $m);
|
||||
$clienteId = isset($m[1]) ? (int) $m[1] : 0;
|
||||
return [
|
||||
'IdCliente' => $clienteId,
|
||||
'Responsabili' => [
|
||||
['IdClienteResponsabile' => 1, 'Nominativo' => 'Marco Bianchi'],
|
||||
['IdClienteResponsabile' => 2, 'Nominativo' => 'Giulia Ferrari'],
|
||||
['IdClienteResponsabile' => 3, 'Nominativo' => 'Andrea Russo'],
|
||||
],
|
||||
];
|
||||
}
|
||||
|
||||
// --- CustomField dropdown values (get_customfield_values.php) ---
|
||||
|
||||
if (str_starts_with($endpoint, 'CustomField(')) {
|
||||
preg_match('/CustomField\((\d+)\)/', $endpoint, $m);
|
||||
$fieldId = isset($m[1]) ? (int) $m[1] : 0;
|
||||
return [
|
||||
'CustomFieldsValues' => [
|
||||
['IdCustomFieldsValue' => $fieldId * 10 + 1, 'Valore' => 'Opzione A'],
|
||||
['IdCustomFieldsValue' => $fieldId * 10 + 2, 'Valore' => 'Opzione B'],
|
||||
['IdCustomFieldsValue' => $fieldId * 10 + 3, 'Valore' => 'Opzione C'],
|
||||
],
|
||||
];
|
||||
}
|
||||
|
||||
// --- CommessaWeb OData calls (STEP 7 GET + STEP 10 verification) ---
|
||||
|
||||
preg_match('/\((\d+)\)/', $endpoint, $m);
|
||||
$id = isset($m[1]) ? (int) $m[1] : $this->fakeCommessaId;
|
||||
|
||||
return [
|
||||
'IdCommessa' => $id,
|
||||
'CodiceCommessa' => "SIM-{$id}",
|
||||
'CommesseCustomFields' => [], // Empty → PATCH step is skipped correctly
|
||||
];
|
||||
}
|
||||
|
||||
public function post(string $endpoint, array $payload): array
|
||||
{
|
||||
error_log("[SIMULATE] POST {$endpoint} payload=" . json_encode($payload));
|
||||
|
||||
// CommessaWeb creation
|
||||
if ($endpoint === 'CommessaWeb') {
|
||||
return [
|
||||
'IdCommessa' => $this->fakeCommessaId,
|
||||
'CodiceCommessa' => "SIM-{$this->fakeCommessaId}",
|
||||
'Richiedente' => $payload['Richiedente'] ?? '',
|
||||
'Descrizione' => $payload['Descrizione'] ?? '',
|
||||
];
|
||||
}
|
||||
|
||||
// Campione creation
|
||||
if ($endpoint === 'Campione') {
|
||||
return [
|
||||
'IdCampione' => mt_rand(10001, 19999),
|
||||
'Commessa' => $payload['Commessa'] ?? null,
|
||||
'Matrice' => $payload['Matrice'] ?? null,
|
||||
];
|
||||
}
|
||||
|
||||
// InviaCommessa / ImportaCommessa (currently commented out upstream)
|
||||
return ['simulated' => true, 'endpoint' => $endpoint];
|
||||
}
|
||||
|
||||
public function patch(string $endpoint, array $payload): array
|
||||
{
|
||||
error_log("[SIMULATE] PATCH {$endpoint} payload=" . json_encode($payload));
|
||||
|
||||
return [];
|
||||
}
|
||||
|
||||
public function postMultipart(string $endpoint, string $filePath, string $fileName, array $extraFields = []): array
|
||||
{
|
||||
error_log("[SIMULATE] POST multipart {$endpoint} file={$fileName}");
|
||||
|
||||
return ['simulated' => true, 'file' => $fileName];
|
||||
}
|
||||
}
|
||||
@@ -1,79 +0,0 @@
|
||||
<?php
|
||||
header('Content-Type: application/json');
|
||||
|
||||
require_once(__DIR__ . '/include/headscript.php');
|
||||
|
||||
$db = DBHandlerSelect::getInstance();
|
||||
$pdo = $db->getConnection();
|
||||
|
||||
$input = json_decode(file_get_contents('php://input'), true);
|
||||
$templateId = (int)($input['template_id'] ?? 0);
|
||||
|
||||
if ($templateId <= 0) {
|
||||
echo json_encode(['success' => false, 'message' => 'Invalid template_id']);
|
||||
exit;
|
||||
}
|
||||
|
||||
// If already exists, do nothing
|
||||
$stmt = $pdo->prepare("SELECT COUNT(*) FROM template_fixed_mapping WHERE template_id = ?");
|
||||
$stmt->execute([$templateId]);
|
||||
if ((int)$stmt->fetchColumn() > 0) {
|
||||
echo json_encode(['success' => true, 'created' => 0, 'message' => 'Fixed fields already exist']);
|
||||
exit;
|
||||
}
|
||||
|
||||
/**
|
||||
* FIXED FIELDS STANDARD (no UI selection)
|
||||
* is_manual always 1
|
||||
* is_visible_import default 1
|
||||
*/
|
||||
$fixedFields = [
|
||||
// fixed_field_key, data_type
|
||||
['ClienteResponsabile', 'INT'],
|
||||
['ClienteFornitore', 'INT'],
|
||||
['ClienteAnalisi', 'INT'],
|
||||
['MoltiplicatorePrezzo', 'INT'],
|
||||
['AnagraficaCertestObject', 'INT'],
|
||||
['AnagraficaCertestService', 'INT'],
|
||||
['ConsegnaRichiesta', 'DATE'],
|
||||
];
|
||||
|
||||
|
||||
try {
|
||||
$pdo->beginTransaction();
|
||||
|
||||
$ins = $pdo->prepare("
|
||||
INSERT INTO template_fixed_mapping
|
||||
(template_id, fixed_field_key, is_manual, data_type, is_required, default_value, is_visible_import)
|
||||
VALUES
|
||||
(:template_id, :fixed_field_key, 1, :data_type, 1, NULL, 1)
|
||||
");
|
||||
|
||||
|
||||
foreach ($fixedFields as $f) {
|
||||
$ins->execute([
|
||||
':template_id' => $templateId,
|
||||
':fixed_field_key' => $f[0],
|
||||
':data_type' => $f[1],
|
||||
]);
|
||||
}
|
||||
|
||||
|
||||
$pdo->commit();
|
||||
|
||||
// Return rows (for client render)
|
||||
$stmt = $pdo->prepare("
|
||||
SELECT id, template_id, fixed_field_key, is_manual, data_type, is_required, default_value, is_visible_import
|
||||
FROM template_fixed_mapping
|
||||
WHERE template_id = ?
|
||||
ORDER BY id ASC
|
||||
");
|
||||
|
||||
$stmt->execute([$templateId]);
|
||||
$rows = $stmt->fetchAll(PDO::FETCH_ASSOC);
|
||||
|
||||
echo json_encode(['success' => true, 'created' => count($rows), 'rows' => $rows]);
|
||||
} catch (Exception $e) {
|
||||
if ($pdo->inTransaction()) $pdo->rollBack();
|
||||
echo json_encode(['success' => false, 'message' => $e->getMessage()]);
|
||||
}
|
||||
File diff suppressed because one or more lines are too long
@@ -1,290 +0,0 @@
|
||||
<?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
|
||||
-->
|
||||
@@ -12,309 +12,3 @@
|
||||
2025-08-26 16:49:24 - Risposta non JSON valida: <?xml version="1.0" encoding="utf-8"?><edmx:Edmx Version="4.0" xmlns:edmx="http://docs.oasis-open.org/odata/ns/edmx"><edmx:DataServices><Schema Namespace="DevExpress.ExpressApp.SystemModule" xmlns="http://docs.oasis-open.org/odata/ns/edm"><EntityType Name="DashboardViewItemDescriptor"><Key><PropertyRef Name="ViewId" /></Key><Property Name="ViewId" Type="Edm.String" Nullable="false" /></EntityType><EntityType Name="DashboardOrganizationItem" BaseType="DevExpress.ExpressApp.NonPersistentLiteObject" Abstract="true"><Property Name="Visibility" Type="DevExpress.ExpressApp.Editors.ViewItemVisibility" Nullable="false" /></EntityType><EntityType Name="DashboardOrganizationItem_1OfIModelDashboardViewItem" BaseType="DevExpress.ExpressApp.SystemModule.DashboardOrganizationItem" Abstract="true" /><EntityType Name="ViewDashboardOrganizationItem" BaseType="DevExpress.ExpressApp.SystemModule.DashboardOrganizationItem_1OfIModelDashboardViewItem"><Property Name="ObjectType" Type="System.Type" /><Proper
|
||||
2025-08-26 16:50:23 - Risposta non JSON valida: <?xml version="1.0" encoding="utf-8"?><edmx:Edmx Version="4.0" xmlns:edmx="http://docs.oasis-open.org/odata/ns/edmx"><edmx:DataServices><Schema Namespace="DevExpress.ExpressApp.SystemModule" xmlns="http://docs.oasis-open.org/odata/ns/edm"><EntityType Name="DashboardViewItemDescriptor"><Key><PropertyRef Name="ViewId" /></Key><Property Name="ViewId" Type="Edm.String" Nullable="false" /></EntityType><EntityType Name="DashboardOrganizationItem" BaseType="DevExpress.ExpressApp.NonPersistentLiteObject" Abstract="true"><Property Name="Visibility" Type="DevExpress.ExpressApp.Editors.ViewItemVisibility" Nullable="false" /></EntityType><EntityType Name="DashboardOrganizationItem_1OfIModelDashboardViewItem" BaseType="DevExpress.ExpressApp.SystemModule.DashboardOrganizationItem" Abstract="true" /><EntityType Name="ViewDashboardOrganizationItem" BaseType="DevExpress.ExpressApp.SystemModule.DashboardOrganizationItem_1OfIModelDashboardViewItem"><Property Name="ObjectType" Type="System.Type" /><Proper
|
||||
2025-09-08 08:39:17 - Risposta non JSON valida: <?xml version="1.0" encoding="utf-8"?><edmx:Edmx Version="4.0" xmlns:edmx="http://docs.oasis-open.org/odata/ns/edmx"><edmx:DataServices><Schema Namespace="DevExpress.ExpressApp.SystemModule" xmlns="http://docs.oasis-open.org/odata/ns/edm"><EntityType Name="DashboardViewItemDescriptor"><Key><PropertyRef Name="ViewId" /></Key><Property Name="ViewId" Type="Edm.String" Nullable="false" /></EntityType><EntityType Name="DashboardOrganizationItem" BaseType="DevExpress.ExpressApp.NonPersistentLiteObject" Abstract="true"><Property Name="Visibility" Type="DevExpress.ExpressApp.Editors.ViewItemVisibility" Nullable="false" /></EntityType><EntityType Name="DashboardOrganizationItem_1OfIModelDashboardViewItem" BaseType="DevExpress.ExpressApp.SystemModule.DashboardOrganizationItem" Abstract="true" /><EntityType Name="ViewDashboardOrganizationItem" BaseType="DevExpress.ExpressApp.SystemModule.DashboardOrganizationItem_1OfIModelDashboardViewItem"><Property Name="ObjectType" Type="System.Type" /><Proper
|
||||
2026-01-27 15:33:53 - Errore nel recupero dati: HTTP 404, Risposta:
|
||||
2026-01-27 15:34:06 - Errore nel recupero dati: HTTP 404, Risposta:
|
||||
2026-01-27 15:34:10 - Errore nel recupero dati: HTTP 404, Risposta:
|
||||
2026-01-27 15:35:13 - Errore nel recupero dati: HTTP 404, Risposta:
|
||||
2026-01-29 14:33:38 [ClienteResponsabile] Errore nel recupero dati: HTTP 404, Risposta:
|
||||
2026-01-29 14:33:39 [MoltiplicatorePrezzo] 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-01-29 14:34:04 [ClienteResponsabile] Errore nel recupero dati: HTTP 404, Risposta:
|
||||
2026-01-29 14:37:29 [ClienteResponsabile] Errore nel recupero dati: HTTP 404, Risposta:
|
||||
2026-01-29 14:41:55 [ClienteResponsabile] Errore nel recupero dati: HTTP 404, Risposta:
|
||||
2026-01-29 14:42:03 [ClienteResponsabile] Errore nel recupero dati: HTTP 404, Risposta:
|
||||
2026-01-29 14:42:52 [ClienteResponsabile] Errore nel recupero dati: HTTP 404, Risposta:
|
||||
2026-01-29 14:43:00 [ClienteResponsabile] Errore nel recupero dati: HTTP 404, Risposta:
|
||||
2026-01-30 10:50:43 [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-01-30 10:50:43 [MoltiplicatorePrezzo] 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-01-30 11:09:22 [MoltiplicatorePrezzo] 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-19 10:00:13 [MoltiplicatorePrezzo] 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-19 10:00:42 [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-19 10:00:44 [MoltiplicatorePrezzo] 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-23 10:44:04 - Autenticazione fallita: HTTP 404, Errore cURL: , Risposta: <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN""http://www.w3.org/TR/html4/strict.dtd">
|
||||
<HTML><HEAD><TITLE>Not Found</TITLE>
|
||||
<META HTTP-EQUIV="Content-Type" Content="text/html; charset=us-ascii"></HEAD>
|
||||
<BODY><h2>Not Found</h2>
|
||||
<hr><p>HTTP Error 404. The requested resource is not found.</p>
|
||||
</BODY></HTML>
|
||||
|
||||
2026-02-23 10:44:42 [AnagraficaCertestService] Autenticazione fallita: HTTP 404, Errore cURL: , Risposta: <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN""http://www.w3.org/TR/html4/strict.dtd">
|
||||
<HTML><HEAD><TITLE>Not Found</TITLE>
|
||||
<META HTTP-EQUIV="Content-Type" Content="text/html; charset=us-ascii"></HEAD>
|
||||
<BODY><h2>Not Found</h2>
|
||||
<hr><p>HTTP Error 404. The requested resource is not found.</p>
|
||||
</BODY></HTML>
|
||||
|
||||
2026-02-23 10:44:42 [AnagraficaCertestObject] Autenticazione fallita: HTTP 404, Errore cURL: , Risposta: <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN""http://www.w3.org/TR/html4/strict.dtd">
|
||||
<HTML><HEAD><TITLE>Not Found</TITLE>
|
||||
<META HTTP-EQUIV="Content-Type" Content="text/html; charset=us-ascii"></HEAD>
|
||||
<BODY><h2>Not Found</h2>
|
||||
<hr><p>HTTP Error 404. The requested resource is not found.</p>
|
||||
</BODY></HTML>
|
||||
|
||||
2026-02-23 10:44:42 [MoltiplicatorePrezzo] Autenticazione fallita: HTTP 404, Errore cURL: , Risposta: <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN""http://www.w3.org/TR/html4/strict.dtd">
|
||||
<HTML><HEAD><TITLE>Not Found</TITLE>
|
||||
<META HTTP-EQUIV="Content-Type" Content="text/html; charset=us-ascii"></HEAD>
|
||||
<BODY><h2>Not Found</h2>
|
||||
<hr><p>HTTP Error 404. The requested resource is not found.</p>
|
||||
</BODY></HTML>
|
||||
|
||||
2026-02-23 10:44:42 [AnagraficaCertestService] Autenticazione fallita: HTTP 404, Errore cURL: , Risposta: <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN""http://www.w3.org/TR/html4/strict.dtd">
|
||||
<HTML><HEAD><TITLE>Not Found</TITLE>
|
||||
<META HTTP-EQUIV="Content-Type" Content="text/html; charset=us-ascii"></HEAD>
|
||||
<BODY><h2>Not Found</h2>
|
||||
<hr><p>HTTP Error 404. The requested resource is not found.</p>
|
||||
</BODY></HTML>
|
||||
|
||||
2026-02-23 10:44:42 [AnagraficaCertestObject] Autenticazione fallita: HTTP 404, Errore cURL: , Risposta: <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN""http://www.w3.org/TR/html4/strict.dtd">
|
||||
<HTML><HEAD><TITLE>Not Found</TITLE>
|
||||
<META HTTP-EQUIV="Content-Type" Content="text/html; charset=us-ascii"></HEAD>
|
||||
<BODY><h2>Not Found</h2>
|
||||
<hr><p>HTTP Error 404. The requested resource is not found.</p>
|
||||
</BODY></HTML>
|
||||
|
||||
2026-02-23 10:44:42 [MoltiplicatorePrezzo] Autenticazione fallita: HTTP 404, Errore cURL: , Risposta: <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN""http://www.w3.org/TR/html4/strict.dtd">
|
||||
<HTML><HEAD><TITLE>Not Found</TITLE>
|
||||
<META HTTP-EQUIV="Content-Type" Content="text/html; charset=us-ascii"></HEAD>
|
||||
<BODY><h2>Not Found</h2>
|
||||
<hr><p>HTTP Error 404. The requested resource is not found.</p>
|
||||
</BODY></HTML>
|
||||
|
||||
2026-02-23 10:44:42 [AnagraficaCertestService] Autenticazione fallita: HTTP 404, Errore cURL: , Risposta: <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN""http://www.w3.org/TR/html4/strict.dtd">
|
||||
<HTML><HEAD><TITLE>Not Found</TITLE>
|
||||
<META HTTP-EQUIV="Content-Type" Content="text/html; charset=us-ascii"></HEAD>
|
||||
<BODY><h2>Not Found</h2>
|
||||
<hr><p>HTTP Error 404. The requested resource is not found.</p>
|
||||
</BODY></HTML>
|
||||
|
||||
2026-02-23 10:44:42 [AnagraficaCertestObject] Autenticazione fallita: HTTP 404, Errore cURL: , Risposta: <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN""http://www.w3.org/TR/html4/strict.dtd">
|
||||
<HTML><HEAD><TITLE>Not Found</TITLE>
|
||||
<META HTTP-EQUIV="Content-Type" Content="text/html; charset=us-ascii"></HEAD>
|
||||
<BODY><h2>Not Found</h2>
|
||||
<hr><p>HTTP Error 404. The requested resource is not found.</p>
|
||||
</BODY></HTML>
|
||||
|
||||
2026-02-23 10:44:42 [MoltiplicatorePrezzo] Autenticazione fallita: HTTP 404, Errore cURL: , Risposta: <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN""http://www.w3.org/TR/html4/strict.dtd">
|
||||
<HTML><HEAD><TITLE>Not Found</TITLE>
|
||||
<META HTTP-EQUIV="Content-Type" Content="text/html; charset=us-ascii"></HEAD>
|
||||
<BODY><h2>Not Found</h2>
|
||||
<hr><p>HTTP Error 404. The requested resource is not found.</p>
|
||||
</BODY></HTML>
|
||||
|
||||
2026-02-23 10:44:43 [MoltiplicatorePrezzo] Autenticazione fallita: HTTP 404, Errore cURL: , Risposta: <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN""http://www.w3.org/TR/html4/strict.dtd">
|
||||
<HTML><HEAD><TITLE>Not Found</TITLE>
|
||||
<META HTTP-EQUIV="Content-Type" Content="text/html; charset=us-ascii"></HEAD>
|
||||
<BODY><h2>Not Found</h2>
|
||||
<hr><p>HTTP Error 404. The requested resource is not found.</p>
|
||||
</BODY></HTML>
|
||||
|
||||
2026-02-23 10:44:43 [AnagraficaCertestObject] Autenticazione fallita: HTTP 404, Errore cURL: , Risposta: <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN""http://www.w3.org/TR/html4/strict.dtd">
|
||||
<HTML><HEAD><TITLE>Not Found</TITLE>
|
||||
<META HTTP-EQUIV="Content-Type" Content="text/html; charset=us-ascii"></HEAD>
|
||||
<BODY><h2>Not Found</h2>
|
||||
<hr><p>HTTP Error 404. The requested resource is not found.</p>
|
||||
</BODY></HTML>
|
||||
|
||||
2026-02-23 10:44:43 [AnagraficaCertestService] Autenticazione fallita: HTTP 404, Errore cURL: , Risposta: <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN""http://www.w3.org/TR/html4/strict.dtd">
|
||||
<HTML><HEAD><TITLE>Not Found</TITLE>
|
||||
<META HTTP-EQUIV="Content-Type" Content="text/html; charset=us-ascii"></HEAD>
|
||||
<BODY><h2>Not Found</h2>
|
||||
<hr><p>HTTP Error 404. The requested resource is not found.</p>
|
||||
</BODY></HTML>
|
||||
|
||||
2026-02-23 10:44:52 [AnagraficaCertestObject] Autenticazione fallita: HTTP 404, Errore cURL: , Risposta: <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN""http://www.w3.org/TR/html4/strict.dtd">
|
||||
<HTML><HEAD><TITLE>Not Found</TITLE>
|
||||
<META HTTP-EQUIV="Content-Type" Content="text/html; charset=us-ascii"></HEAD>
|
||||
<BODY><h2>Not Found</h2>
|
||||
<hr><p>HTTP Error 404. The requested resource is not found.</p>
|
||||
</BODY></HTML>
|
||||
|
||||
2026-02-23 10:44:52 [MoltiplicatorePrezzo] Autenticazione fallita: HTTP 404, Errore cURL: , Risposta: <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN""http://www.w3.org/TR/html4/strict.dtd">
|
||||
<HTML><HEAD><TITLE>Not Found</TITLE>
|
||||
<META HTTP-EQUIV="Content-Type" Content="text/html; charset=us-ascii"></HEAD>
|
||||
<BODY><h2>Not Found</h2>
|
||||
<hr><p>HTTP Error 404. The requested resource is not found.</p>
|
||||
</BODY></HTML>
|
||||
|
||||
2026-02-23 10:44:52 [AnagraficaCertestService] Autenticazione fallita: HTTP 404, Errore cURL: , Risposta: <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN""http://www.w3.org/TR/html4/strict.dtd">
|
||||
<HTML><HEAD><TITLE>Not Found</TITLE>
|
||||
<META HTTP-EQUIV="Content-Type" Content="text/html; charset=us-ascii"></HEAD>
|
||||
<BODY><h2>Not Found</h2>
|
||||
<hr><p>HTTP Error 404. The requested resource is not found.</p>
|
||||
</BODY></HTML>
|
||||
|
||||
2026-02-23 10:44:52 [AnagraficaCertestObject] Autenticazione fallita: HTTP 404, Errore cURL: , Risposta: <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN""http://www.w3.org/TR/html4/strict.dtd">
|
||||
<HTML><HEAD><TITLE>Not Found</TITLE>
|
||||
<META HTTP-EQUIV="Content-Type" Content="text/html; charset=us-ascii"></HEAD>
|
||||
<BODY><h2>Not Found</h2>
|
||||
<hr><p>HTTP Error 404. The requested resource is not found.</p>
|
||||
</BODY></HTML>
|
||||
|
||||
2026-02-23 10:44:52 [MoltiplicatorePrezzo] Autenticazione fallita: HTTP 404, Errore cURL: , Risposta: <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN""http://www.w3.org/TR/html4/strict.dtd">
|
||||
<HTML><HEAD><TITLE>Not Found</TITLE>
|
||||
<META HTTP-EQUIV="Content-Type" Content="text/html; charset=us-ascii"></HEAD>
|
||||
<BODY><h2>Not Found</h2>
|
||||
<hr><p>HTTP Error 404. The requested resource is not found.</p>
|
||||
</BODY></HTML>
|
||||
|
||||
2026-02-23 10:44:52 [AnagraficaCertestService] Autenticazione fallita: HTTP 404, Errore cURL: , Risposta: <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN""http://www.w3.org/TR/html4/strict.dtd">
|
||||
<HTML><HEAD><TITLE>Not Found</TITLE>
|
||||
<META HTTP-EQUIV="Content-Type" Content="text/html; charset=us-ascii"></HEAD>
|
||||
<BODY><h2>Not Found</h2>
|
||||
<hr><p>HTTP Error 404. The requested resource is not found.</p>
|
||||
</BODY></HTML>
|
||||
|
||||
2026-02-23 10:44:52 [AnagraficaCertestObject] Autenticazione fallita: HTTP 404, Errore cURL: , Risposta: <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN""http://www.w3.org/TR/html4/strict.dtd">
|
||||
<HTML><HEAD><TITLE>Not Found</TITLE>
|
||||
<META HTTP-EQUIV="Content-Type" Content="text/html; charset=us-ascii"></HEAD>
|
||||
<BODY><h2>Not Found</h2>
|
||||
<hr><p>HTTP Error 404. The requested resource is not found.</p>
|
||||
</BODY></HTML>
|
||||
|
||||
2026-02-23 10:44:52 [MoltiplicatorePrezzo] Autenticazione fallita: HTTP 404, Errore cURL: , Risposta: <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN""http://www.w3.org/TR/html4/strict.dtd">
|
||||
<HTML><HEAD><TITLE>Not Found</TITLE>
|
||||
<META HTTP-EQUIV="Content-Type" Content="text/html; charset=us-ascii"></HEAD>
|
||||
<BODY><h2>Not Found</h2>
|
||||
<hr><p>HTTP Error 404. The requested resource is not found.</p>
|
||||
</BODY></HTML>
|
||||
|
||||
2026-02-23 10:44:52 [AnagraficaCertestService] Autenticazione fallita: HTTP 404, Errore cURL: , Risposta: <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN""http://www.w3.org/TR/html4/strict.dtd">
|
||||
<HTML><HEAD><TITLE>Not Found</TITLE>
|
||||
<META HTTP-EQUIV="Content-Type" Content="text/html; charset=us-ascii"></HEAD>
|
||||
<BODY><h2>Not Found</h2>
|
||||
<hr><p>HTTP Error 404. The requested resource is not found.</p>
|
||||
</BODY></HTML>
|
||||
|
||||
2026-02-23 10:44:52 [MoltiplicatorePrezzo] Autenticazione fallita: HTTP 404, Errore cURL: , Risposta: <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN""http://www.w3.org/TR/html4/strict.dtd">
|
||||
<HTML><HEAD><TITLE>Not Found</TITLE>
|
||||
<META HTTP-EQUIV="Content-Type" Content="text/html; charset=us-ascii"></HEAD>
|
||||
<BODY><h2>Not Found</h2>
|
||||
<hr><p>HTTP Error 404. The requested resource is not found.</p>
|
||||
</BODY></HTML>
|
||||
|
||||
2026-02-23 10:44:52 [AnagraficaCertestObject] Autenticazione fallita: HTTP 404, Errore cURL: , Risposta: <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN""http://www.w3.org/TR/html4/strict.dtd">
|
||||
<HTML><HEAD><TITLE>Not Found</TITLE>
|
||||
<META HTTP-EQUIV="Content-Type" Content="text/html; charset=us-ascii"></HEAD>
|
||||
<BODY><h2>Not Found</h2>
|
||||
<hr><p>HTTP Error 404. The requested resource is not found.</p>
|
||||
</BODY></HTML>
|
||||
|
||||
2026-02-23 10:44:53 [AnagraficaCertestService] Autenticazione fallita: HTTP 404, Errore cURL: , Risposta: <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN""http://www.w3.org/TR/html4/strict.dtd">
|
||||
<HTML><HEAD><TITLE>Not Found</TITLE>
|
||||
<META HTTP-EQUIV="Content-Type" Content="text/html; charset=us-ascii"></HEAD>
|
||||
<BODY><h2>Not Found</h2>
|
||||
<hr><p>HTTP Error 404. The requested resource is not found.</p>
|
||||
</BODY></HTML>
|
||||
|
||||
2026-02-23 10:48:26 - Autenticazione fallita: HTTP 404, Errore cURL: , Risposta: <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN""http://www.w3.org/TR/html4/strict.dtd">
|
||||
<HTML><HEAD><TITLE>Not Found</TITLE>
|
||||
<META HTTP-EQUIV="Content-Type" Content="text/html; charset=us-ascii"></HEAD>
|
||||
<BODY><h2>Not Found</h2>
|
||||
<hr><p>HTTP Error 404. The requested resource is not found.</p>
|
||||
</BODY></HTML>
|
||||
|
||||
2026-02-23 10:48:26 - Autenticazione fallita: HTTP 404, Errore cURL: , Risposta: <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN""http://www.w3.org/TR/html4/strict.dtd">
|
||||
<HTML><HEAD><TITLE>Not Found</TITLE>
|
||||
<META HTTP-EQUIV="Content-Type" Content="text/html; charset=us-ascii"></HEAD>
|
||||
<BODY><h2>Not Found</h2>
|
||||
<hr><p>HTTP Error 404. The requested resource is not found.</p>
|
||||
</BODY></HTML>
|
||||
|
||||
2026-02-23 10:48:27 - Autenticazione fallita: HTTP 404, Errore cURL: , Risposta: <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN""http://www.w3.org/TR/html4/strict.dtd">
|
||||
<HTML><HEAD><TITLE>Not Found</TITLE>
|
||||
<META HTTP-EQUIV="Content-Type" Content="text/html; charset=us-ascii"></HEAD>
|
||||
<BODY><h2>Not Found</h2>
|
||||
<hr><p>HTTP Error 404. The requested resource is not found.</p>
|
||||
</BODY></HTML>
|
||||
|
||||
2026-02-23 10:48:27 - Autenticazione fallita: HTTP 404, Errore cURL: , Risposta: <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN""http://www.w3.org/TR/html4/strict.dtd">
|
||||
<HTML><HEAD><TITLE>Not Found</TITLE>
|
||||
<META HTTP-EQUIV="Content-Type" Content="text/html; charset=us-ascii"></HEAD>
|
||||
<BODY><h2>Not Found</h2>
|
||||
<hr><p>HTTP Error 404. The requested resource is not found.</p>
|
||||
</BODY></HTML>
|
||||
|
||||
2026-02-23 10:48:27 - Autenticazione fallita: HTTP 404, Errore cURL: , Risposta: <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN""http://www.w3.org/TR/html4/strict.dtd">
|
||||
<HTML><HEAD><TITLE>Not Found</TITLE>
|
||||
<META HTTP-EQUIV="Content-Type" Content="text/html; charset=us-ascii"></HEAD>
|
||||
<BODY><h2>Not Found</h2>
|
||||
<hr><p>HTTP Error 404. The requested resource is not found.</p>
|
||||
</BODY></HTML>
|
||||
|
||||
2026-02-23 10:49:04 - Autenticazione fallita: HTTP 404, Errore cURL: , Risposta: <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN""http://www.w3.org/TR/html4/strict.dtd">
|
||||
<HTML><HEAD><TITLE>Not Found</TITLE>
|
||||
<META HTTP-EQUIV="Content-Type" Content="text/html; charset=us-ascii"></HEAD>
|
||||
<BODY><h2>Not Found</h2>
|
||||
<hr><p>HTTP Error 404. The requested resource is not found.</p>
|
||||
</BODY></HTML>
|
||||
|
||||
2026-02-23 10:49:04 - Autenticazione fallita: HTTP 404, Errore cURL: , Risposta: <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN""http://www.w3.org/TR/html4/strict.dtd">
|
||||
<HTML><HEAD><TITLE>Not Found</TITLE>
|
||||
<META HTTP-EQUIV="Content-Type" Content="text/html; charset=us-ascii"></HEAD>
|
||||
<BODY><h2>Not Found</h2>
|
||||
<hr><p>HTTP Error 404. The requested resource is not found.</p>
|
||||
</BODY></HTML>
|
||||
|
||||
2026-02-23 10:49:04 - Autenticazione fallita: HTTP 404, Errore cURL: , Risposta: <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN""http://www.w3.org/TR/html4/strict.dtd">
|
||||
<HTML><HEAD><TITLE>Not Found</TITLE>
|
||||
<META HTTP-EQUIV="Content-Type" Content="text/html; charset=us-ascii"></HEAD>
|
||||
<BODY><h2>Not Found</h2>
|
||||
<hr><p>HTTP Error 404. The requested resource is not found.</p>
|
||||
</BODY></HTML>
|
||||
|
||||
2026-02-23 10:49:05 - Autenticazione fallita: HTTP 404, Errore cURL: , Risposta: <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN""http://www.w3.org/TR/html4/strict.dtd">
|
||||
<HTML><HEAD><TITLE>Not Found</TITLE>
|
||||
<META HTTP-EQUIV="Content-Type" Content="text/html; charset=us-ascii"></HEAD>
|
||||
<BODY><h2>Not Found</h2>
|
||||
<hr><p>HTTP Error 404. The requested resource is not found.</p>
|
||||
</BODY></HTML>
|
||||
|
||||
2026-02-23 10:49:05 - Autenticazione fallita: HTTP 404, Errore cURL: , Risposta: <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN""http://www.w3.org/TR/html4/strict.dtd">
|
||||
<HTML><HEAD><TITLE>Not Found</TITLE>
|
||||
<META HTTP-EQUIV="Content-Type" Content="text/html; charset=us-ascii"></HEAD>
|
||||
<BODY><h2>Not Found</h2>
|
||||
<hr><p>HTTP Error 404. The requested resource is not found.</p>
|
||||
</BODY></HTML>
|
||||
|
||||
2026-02-23 14:26:52 - Autenticazione fallita: HTTP 404, Errore cURL: , Risposta: <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN""http://www.w3.org/TR/html4/strict.dtd">
|
||||
<HTML><HEAD><TITLE>Not Found</TITLE>
|
||||
<META HTTP-EQUIV="Content-Type" Content="text/html; charset=us-ascii"></HEAD>
|
||||
<BODY><h2>Not Found</h2>
|
||||
<hr><p>HTTP Error 404. The requested resource is not found.</p>
|
||||
</BODY></HTML>
|
||||
|
||||
2026-02-23 14:26:59 - Autenticazione fallita: HTTP 404, Errore cURL: , Risposta: <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN""http://www.w3.org/TR/html4/strict.dtd">
|
||||
<HTML><HEAD><TITLE>Not Found</TITLE>
|
||||
<META HTTP-EQUIV="Content-Type" Content="text/html; charset=us-ascii"></HEAD>
|
||||
<BODY><h2>Not Found</h2>
|
||||
<hr><p>HTTP Error 404. The requested resource is not found.</p>
|
||||
</BODY></HTML>
|
||||
|
||||
2026-02-23 15:54:16 - Autenticazione fallita: HTTP 404, Errore cURL: , Risposta: <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN""http://www.w3.org/TR/html4/strict.dtd">
|
||||
<HTML><HEAD><TITLE>Not Found</TITLE>
|
||||
<META HTTP-EQUIV="Content-Type" Content="text/html; charset=us-ascii"></HEAD>
|
||||
<BODY><h2>Not Found</h2>
|
||||
<hr><p>HTTP Error 404. The requested resource is not found.</p>
|
||||
</BODY></HTML>
|
||||
|
||||
2026-02-23 16:22:45 - Autenticazione fallita: HTTP 404, Errore cURL: , Risposta: <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN""http://www.w3.org/TR/html4/strict.dtd">
|
||||
<HTML><HEAD><TITLE>Not Found</TITLE>
|
||||
<META HTTP-EQUIV="Content-Type" Content="text/html; charset=us-ascii"></HEAD>
|
||||
<BODY><h2>Not Found</h2>
|
||||
<hr><p>HTTP Error 404. The requested resource is not found.</p>
|
||||
</BODY></HTML>
|
||||
|
||||
2026-02-23 16:32:17 - Autenticazione fallita: HTTP 404, Errore cURL: , Risposta: <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN""http://www.w3.org/TR/html4/strict.dtd">
|
||||
<HTML><HEAD><TITLE>Not Found</TITLE>
|
||||
<META HTTP-EQUIV="Content-Type" Content="text/html; charset=us-ascii"></HEAD>
|
||||
<BODY><h2>Not Found</h2>
|
||||
<hr><p>HTTP Error 404. The requested resource is not found.</p>
|
||||
</BODY></HTML>
|
||||
|
||||
2026-02-25 15:23:12 [AnagraficaCertestObject] Autenticazione fallita: HTTP 400, Errore cURL: , Risposta: {"title":"Bad Request","status":400,"detail":"Cannot persist the object. It was modified or deleted (purged) by another application.","instance":"POST /api/authentication/authenticate","errorCode":"96bfc1252b"}
|
||||
2026-02-26 15:01:32 [AnagraficaCertestObject] Autenticazione fallita: HTTP 400, Errore cURL: , Risposta: {"title":"Bad Request","status":400,"detail":"Cannot persist the object. It was modified or deleted (purged) by another application.","instance":"POST /api/authentication/authenticate","errorCode":"96bfc1252b"}
|
||||
2026-02-28 21:01:27 [AnagraficaCertestService] Autenticazione fallita: HTTP 400, Errore cURL: , Risposta: {"title":"Bad Request","status":400,"detail":"Cannot persist the object. It was modified or deleted (purged) by another application.","instance":"POST /api/authentication/authenticate","errorCode":"96bfc1252b"}
|
||||
2026-03-01 19:11:58 [AnagraficaCertestObject] Autenticazione fallita: HTTP 400, Errore cURL: , Risposta: {"title":"Bad Request","status":400,"detail":"Cannot persist the object. It was modified or deleted (purged) by another application.","instance":"POST /api/authentication/authenticate","errorCode":"96bfc1252b"}
|
||||
2026-03-18 15:51:45 [AnagraficaCertestService] Autenticazione fallita: HTTP 400, Errore cURL: , Risposta: {"title":"Bad Request","status":400,"detail":"Cannot persist the object. It was modified or deleted (purged) by another application.","instance":"POST /api/authentication/authenticate","errorCode":"96bfc1252b"}
|
||||
2026-03-19 09:50:34 [AnagraficaCertestService] Autenticazione fallita: HTTP 400, Errore cURL: , Risposta: {"title":"Bad Request","status":400,"detail":"Cannot persist the object. It was modified or deleted (purged) by another application.","instance":"POST /api/authentication/authenticate","errorCode":"96bfc1252b"}
|
||||
2026-03-25 14:13:34 - Autenticazione fallita: HTTP 503, Errore cURL: , Risposta: The service is unavailable.
|
||||
2026-03-25 14:13:34 - Autenticazione fallita: HTTP 503, Errore cURL: , Risposta: The service is unavailable.
|
||||
|
||||
@@ -1,555 +0,0 @@
|
||||
/**
|
||||
* 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 });
|
||||
})();
|
||||
}
|
||||
})();
|
||||
+205
-766
File diff suppressed because it is too large
Load Diff
@@ -13,31 +13,9 @@ if (!is_dir($logDir)) {
|
||||
mkdir($logDir, 0755, true);
|
||||
}
|
||||
|
||||
$uploadDir = realpath(__DIR__ . '/../photostrf') . '/';
|
||||
|
||||
// 🔹 Base URL API
|
||||
$apiBaseUrl = 'https://93.43.5.102/limsapi/api/odata/';
|
||||
|
||||
// 🔹 Batch UUID — if present, all logs go to a single file
|
||||
$batchUuid = $_POST['batch_uuid'] ?? null;
|
||||
|
||||
$writeLog = (function () use ($batchUuid, $logDir) {
|
||||
$batchLogFile = $batchUuid ? $logDir . "batch_export_{$batchUuid}.log" : null;
|
||||
|
||||
return function ($individualPath, $content, $stepLabel = null) use ($batchLogFile) {
|
||||
if ($batchLogFile) {
|
||||
$header = "\n" . str_repeat("=", 60) . "\n";
|
||||
if ($stepLabel) {
|
||||
$header .= "[{$stepLabel}] " . date('Y-m-d H:i:s') . "\n";
|
||||
}
|
||||
$header .= str_repeat("=", 60) . "\n";
|
||||
file_put_contents($batchLogFile, $header . $content . "\n", FILE_APPEND);
|
||||
} else {
|
||||
file_put_contents($individualPath, $content);
|
||||
}
|
||||
};
|
||||
})();
|
||||
|
||||
// 🔹 Funzione per validare e convertire date
|
||||
function validateDate($value)
|
||||
{
|
||||
@@ -49,38 +27,15 @@ function validateDate($value)
|
||||
return null; // Imposta null se non è una data valida
|
||||
}
|
||||
|
||||
// 🔹 Funzione per validare e convertire date
|
||||
function formatDateToExport($value)
|
||||
{
|
||||
$date = DateTime::createFromFormat('Y-m-d', $value) ?: DateTime::createFromFormat('Y-m-d H:i:s', $value);
|
||||
if ($date) {
|
||||
return $date->format('d/m/Y');
|
||||
}
|
||||
return null; // Imposta null se non è una data valida
|
||||
}
|
||||
|
||||
try {
|
||||
$iddatadb = $_POST['iddatadb'] ?? null;
|
||||
if (!$iddatadb) {
|
||||
throw new Exception("Missing iddatadb");
|
||||
}
|
||||
|
||||
// TEMP: simulate error on every other row for testing
|
||||
if (env('SIMULATE_EXPORT_LIMS') && $iddatadb % 2 === 0) {
|
||||
throw new Exception("Simulated error for iddatadb $iddatadb");
|
||||
}
|
||||
|
||||
// 🔹 STEP 1+2: Fetch Cliente ID from datadb and Schema ID from excel_templates
|
||||
// Also fetch fixed fields stored in datadb
|
||||
$stmt = $pdo->prepare("
|
||||
SELECT d.idclient AS clienteId, et.idschema AS schemaId,
|
||||
d.cliente_responsabile_id,
|
||||
d.moltiplicatore_prezzo_id,
|
||||
d.anagrafica_certest_object_id,
|
||||
d.anagrafica_certest_service_id,
|
||||
d.cliente_fornitore_id,
|
||||
d.clienteAnalisi,
|
||||
d.consegna_richiesta
|
||||
SELECT d.idclient AS clienteId, et.idschema AS schemaId
|
||||
FROM datadb as d
|
||||
INNER JOIN excel_templates as et ON d.templateid = et.id
|
||||
WHERE d.iddatadb = :iddatadb
|
||||
@@ -96,57 +51,15 @@ try {
|
||||
$clienteId = (int) $result['clienteId'];
|
||||
$schemaId = (int) $result['schemaId'];
|
||||
|
||||
// Extract fixed fields (nullable INTs)
|
||||
$clienteResponsabile = !empty($result['cliente_responsabile_id']) ? (int) $result['cliente_responsabile_id'] : null;
|
||||
$moltiplicatorePrezzo = !empty($result['moltiplicatore_prezzo_id']) ? (int) $result['moltiplicatore_prezzo_id'] : null;
|
||||
$anagraficaObject = !empty($result['anagrafica_certest_object_id']) ? (int) $result['anagrafica_certest_object_id'] : null;
|
||||
$anagraficaService = !empty($result['anagrafica_certest_service_id']) ? (int) $result['anagrafica_certest_service_id'] : null;
|
||||
$clienteFornitore = !empty($result['cliente_fornitore_id']) ? (int) $result['cliente_fornitore_id'] : null;
|
||||
$clienteAnalisi = !empty($result['clienteAnalisi']) ? (int) $result['clienteAnalisi'] : null;
|
||||
$consegnaRichiesta = !empty($result['consegna_richiesta']) ? $result['consegna_richiesta'] : null;
|
||||
|
||||
// 🔹 STEP 3: Fetch Parts (including idmatrice and part id for custom fields)
|
||||
// 🔹 STEP 3: Fetch Parts (including idmatrice)
|
||||
$stmt = $pdo->prepare("
|
||||
SELECT id AS part_id, part_number, part_description, material, color, mix, idmatrice, dateexpiry
|
||||
SELECT part_number, part_description, material, color, mix, idmatrice
|
||||
FROM identification_parts
|
||||
WHERE iddatadb = :iddatadb
|
||||
");
|
||||
$stmt->execute(['iddatadb' => $iddatadb]);
|
||||
$parts = $stmt->fetchAll(PDO::FETCH_ASSOC);
|
||||
|
||||
// 🔹 STEP 3.1: Fetch custom field values per part from identification_parts_customfields
|
||||
$partIds = array_column($parts, 'part_id');
|
||||
$partsCustomFields = []; // part_id => [ { field_id, value_id, value_text }, ... ]
|
||||
if (!empty($partIds)) {
|
||||
$placeholders = implode(',', array_fill(0, count($partIds), '?'));
|
||||
$cfStmt = $pdo->prepare("
|
||||
SELECT part_id, field_id, value_id, value_text
|
||||
FROM identification_parts_customfields
|
||||
WHERE part_id IN ({$placeholders})
|
||||
");
|
||||
$cfStmt->execute($partIds);
|
||||
foreach ($cfStmt->fetchAll(PDO::FETCH_ASSOC) as $cfRow) {
|
||||
$partsCustomFields[(int)$cfRow['part_id']][] = $cfRow;
|
||||
}
|
||||
}
|
||||
|
||||
// 🔹 STEP 4a: Auto-populate export_date / export_time fields
|
||||
$stmt = $pdo->prepare("
|
||||
UPDATE import_data_details idd
|
||||
JOIN template_mapping m ON idd.mapping_id = m.id
|
||||
SET idd.field_value = CASE m.auto_value
|
||||
WHEN 'export_date' THEN :export_date
|
||||
WHEN 'export_time' THEN :export_time
|
||||
END
|
||||
WHERE idd.id = :iddatadb
|
||||
AND m.auto_value IN ('export_date', 'export_time')
|
||||
");
|
||||
$stmt->execute([
|
||||
'iddatadb' => $iddatadb,
|
||||
'export_date' => date('Y-m-d'),
|
||||
'export_time' => date('H:i'),
|
||||
]);
|
||||
|
||||
// 🔹 STEP 4: Fetch Field Values with Labels
|
||||
$stmt = $pdo->prepare("
|
||||
SELECT
|
||||
@@ -180,21 +93,12 @@ try {
|
||||
// 🔹 Initialize API client
|
||||
$api = VisualLimsApiClient::getInstance();
|
||||
|
||||
// 🔹 STEP 5: Create CommessaWeb
|
||||
// Fixed fields are sent as direct properties — they are navigation properties on Commessa
|
||||
// accepted by the XAF API even though not explicitly listed in OData $metadata
|
||||
// 🔹 STEP 5: Create CommessaWeb (NOT WebOrder)
|
||||
$commessaWebPayload = [
|
||||
"Cliente" => $clienteId,
|
||||
"SchemaCustomField" => $schemaId,
|
||||
"Richiedente" => "From TRFSmart Application", // TODO: replace with real value
|
||||
"Descrizione" => "From TRFSmart Application", // TODO: replace with real value
|
||||
"ClienteResponsabile" => $clienteResponsabile,
|
||||
"MoltiplicatorePrezzo" => $moltiplicatorePrezzo,
|
||||
"AnagraficaCertestObject" => $anagraficaObject,
|
||||
"AnagraficaCertestService" => $anagraficaService,
|
||||
"ClienteFornitore" => $clienteFornitore, // PLACEHOLDER — to be implemented
|
||||
"ClienteAnalisi" => $clienteAnalisi, // PLACEHOLDER — to be implemented
|
||||
// DeliveryRequest goes to Campione, not CommessaWeb
|
||||
"Cliente" => $clienteId,
|
||||
"SchemaCustomField" => $schemaId,
|
||||
"Richiedente" => "Test Web Import",
|
||||
"Descrizione" => "TEST CommessaWeb",
|
||||
];
|
||||
|
||||
// Costruisci log curl-like per STEP 5
|
||||
@@ -210,7 +114,7 @@ try {
|
||||
|
||||
// Salva log
|
||||
$logFileStep5 = $logDir . "commessa_create_step5_" . $iddatadb . "_" . time() . ".txt";
|
||||
$writeLog($logFileStep5, $logContentStep5, "STEP 5 - Create CommessaWeb (iddatadb={$iddatadb})");
|
||||
file_put_contents($logFileStep5, $logContentStep5);
|
||||
|
||||
$commessaId = $commessaWeb["IdCommessa"];
|
||||
$commessaWebCode = substr($commessaWeb["CodiceCommessa"] ?? "TEST CommessaWeb", 0, 30); // Limite a 30 caratteri
|
||||
@@ -227,13 +131,11 @@ try {
|
||||
}
|
||||
|
||||
$campionePayload = [
|
||||
"Commessa" => $commessaId,
|
||||
"Matrice" => $matriceId,
|
||||
"SottoMatrice" => null,
|
||||
"Commessa" => $commessaId,
|
||||
"Matrice" => $matriceId,
|
||||
"SottoMatrice" => null,
|
||||
"SchemaCustomField" => $schemaId,
|
||||
// "Riferimento" => $part["part_description"] ?? "",
|
||||
"NoteWeb" => $part["part_description"] ?? "",
|
||||
"ConsegnaRichiesta" => !empty($part["dateexpiry"]) ? $part["dateexpiry"] : $consegnaRichiesta,
|
||||
"NoteWeb" => $part["part_description"] ?? ""
|
||||
];
|
||||
|
||||
// Costruisci curl-like per questo campione
|
||||
@@ -258,191 +160,20 @@ try {
|
||||
|
||||
// Salva log per STEP 6
|
||||
$logFileStep6 = $logDir . "commessa_{$commessaId}_campioni_step6_" . time() . ".txt";
|
||||
$writeLog($logFileStep6, $logContentStep6, "STEP 6 - Campioni (commessa={$commessaId})");
|
||||
|
||||
// 🔹 STEP 6.0: PATCH each Campione custom fields:
|
||||
// - field 189 (Tested Component) = part_description
|
||||
// - additional fields from identification_parts_customfields (field_id → value_id/value_text)
|
||||
$logContentStep63 = "";
|
||||
foreach ($campioni as $index => $campione) {
|
||||
$campioneId = (int)($campione['IdCampione'] ?? 0);
|
||||
$partDescription = $parts[$index]['part_description'] ?? '';
|
||||
$partId = (int)($parts[$index]['part_id'] ?? 0);
|
||||
|
||||
if ($campioneId <= 0) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// Build a map of overrides: IdCustomField => value
|
||||
$overrides = [];
|
||||
|
||||
// Override 1: Tested Component (field 189) = part_description
|
||||
if ($partDescription !== '') {
|
||||
$overrides[189] = $partDescription;
|
||||
}
|
||||
|
||||
// Override 2: values from identification_parts_customfields
|
||||
$partCFs = $partsCustomFields[$partId] ?? [];
|
||||
foreach ($partCFs as $pcf) {
|
||||
$cfFieldId = (int)$pcf['field_id'];
|
||||
$cfValue = $pcf['value_text'] ?? $pcf['value_id'] ?? null;
|
||||
if ($cfFieldId > 0 && $cfValue !== null && $cfValue !== '') {
|
||||
$overrides[$cfFieldId] = (string)$cfValue;
|
||||
}
|
||||
}
|
||||
|
||||
// Skip if nothing to override
|
||||
if (empty($overrides)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// GET campione custom fields
|
||||
$campioneWithFields = $api->get("Campione({$campioneId})?\$expand=CampioniCustomFields(\$expand=CustomField)");
|
||||
|
||||
$logContentStep63 .= "GET Campione({$campioneId}) CustomFields:\n" .
|
||||
json_encode($campioneWithFields['CampioniCustomFields'] ?? [], JSON_PRETTY_PRINT) . "\n\n";
|
||||
$logContentStep63 .= "Overrides for part {$partId}: " . json_encode($overrides) . "\n\n";
|
||||
|
||||
// Build PATCH payload — apply overrides where IdCustomField matches
|
||||
$campioniCustomFields = [];
|
||||
foreach ($campioneWithFields["CampioniCustomFields"] ?? [] as $cf) {
|
||||
$definitionId = (int)($cf["CustomField"]["IdCustomField"] ?? 0);
|
||||
$fieldInstanceId = (int)$cf["IdCampioniCustomFields"];
|
||||
$currentValue = $cf["Valore"] ?? '';
|
||||
|
||||
$newValue = $overrides[$definitionId] ?? $currentValue;
|
||||
|
||||
$campioniCustomFields[] = [
|
||||
"IdCampioniCustomFields" => $fieldInstanceId,
|
||||
"Valore" => $newValue
|
||||
];
|
||||
}
|
||||
|
||||
if (!empty($campioniCustomFields)) {
|
||||
$patchPayload = ["CampioniCustomFields" => $campioniCustomFields];
|
||||
$patchJson = json_encode($patchPayload, JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES);
|
||||
|
||||
$logContentStep63 .= "PATCH Campione({$campioneId}):\n" .
|
||||
"curl --location --request PATCH '{$apiBaseUrl}Campione({$campioneId})' \\\n" .
|
||||
"--header 'Content-Type: application/json' \\\n" .
|
||||
"--header 'Authorization: Bearer ••••••' \\\n" .
|
||||
"--data '{$patchJson}'\n\n";
|
||||
|
||||
$patchResult = $api->patch("Campione({$campioneId})", $patchPayload);
|
||||
|
||||
$logContentStep63 .= "RESPONSE:\n" . json_encode($patchResult, JSON_PRETTY_PRINT) . "\n\n---\n";
|
||||
}
|
||||
}
|
||||
|
||||
$logFileStep63 = $logDir . "commessa_{$commessaId}_campioni_customfields_step60_" . time() . ".txt";
|
||||
$writeLog($logFileStep63, $logContentStep63, "STEP 6.0 - Campioni CustomFields (commessa={$commessaId})");
|
||||
|
||||
// 🔹 STEP 6.1: Fetch photos linked to this iddatadb
|
||||
$stmtPhotos = $pdo->prepare("
|
||||
SELECT id, file_path, file_name, StampaNelRapporto, PrimaPagina
|
||||
FROM datadb_photos
|
||||
WHERE iddatadb = :iddatadb
|
||||
ORDER BY id ASC
|
||||
");
|
||||
$stmtPhotos->execute(['iddatadb' => $iddatadb]);
|
||||
$photos = $stmtPhotos->fetchAll(PDO::FETCH_ASSOC);
|
||||
|
||||
|
||||
// 🔹 STEP 6.2: Upload photos to Campione .01 (fetched from API)
|
||||
$photosUploaded = 0;
|
||||
$logContentPhotos = "Photos for CommessaWeb {$commessaId} (iddatadb={$iddatadb}):\n";
|
||||
$logContentPhotos .= "Total photos found: " . count($photos) . ", campioni: " . count($campioni) . "\n\n";
|
||||
|
||||
if (!empty($campioni) && !empty($photos)) {
|
||||
// Fetch campioni list from API to find the .01 campione
|
||||
$commessaCampioni = $api->get("CommessaWeb({$commessaId})?\$expand=Campioni");
|
||||
$apiCampioni = $commessaCampioni['Campioni'] ?? [];
|
||||
|
||||
// Sort by CodiceCampione to find .01
|
||||
usort($apiCampioni, fn($a, $b) => strcmp($a['CodiceCampione'] ?? '', $b['CodiceCampione'] ?? ''));
|
||||
|
||||
$mainCampione = $apiCampioni[0] ?? null;
|
||||
$campioneId = (int)($mainCampione['IdCampione'] ?? 0);
|
||||
|
||||
$logContentPhotos .= "API Campioni order:\n";
|
||||
foreach ($apiCampioni as $ac) {
|
||||
$logContentPhotos .= " - {$ac['CodiceCampione']} (IdCampione: {$ac['IdCampione']})\n";
|
||||
}
|
||||
$logContentPhotos .= "Selected .01 campione: IdCampione={$campioneId}\n\n";
|
||||
|
||||
if ($campioneId > 0) {
|
||||
$logContentPhotos .= "=== Campione {$campioneId} (main) ===\n";
|
||||
|
||||
foreach ($photos as $photo) {
|
||||
$photoPath = $uploadDir . '/' . ltrim($photo['file_path'], './');
|
||||
$fullPath = realpath($photoPath);
|
||||
|
||||
if (!$fullPath || !file_exists($fullPath)) {
|
||||
$logContentPhotos .= "SKIP (file not found): {$photoPath}\n";
|
||||
continue;
|
||||
}
|
||||
|
||||
$photoEndpoint = "Campione({$campioneId})/UploadCampioneFile";
|
||||
$stampaNelRapporto = !empty($photo['StampaNelRapporto']);
|
||||
$primaPagina = !empty($photo['PrimaPagina']);
|
||||
|
||||
$logContentPhotos .= "curl --location --request POST '{$apiBaseUrl}{$photoEndpoint}' \\\n" .
|
||||
"--header 'Authorization: Bearer ••••••' \\\n" .
|
||||
"--form 'file=@{$fullPath}'\n\n";
|
||||
|
||||
// Step 1: Upload file (flags are ignored by API during upload)
|
||||
$photoResult = $api->postMultipart($photoEndpoint, $fullPath, $photo['file_name']);
|
||||
$logContentPhotos .= "UPLOAD RESPONSE:\n" . json_encode($photoResult, JSON_PRETTY_PRINT) . "\n\n";
|
||||
|
||||
// Step 2: PATCH CampioneFile to set flags (StampaNelRapporto, PrimaPagina)
|
||||
$campioneFileId = (int)($photoResult['IdCampioneFile'] ?? 0);
|
||||
if ($campioneFileId > 0 && ($stampaNelRapporto || $primaPagina)) {
|
||||
$patchPayload = [];
|
||||
|
||||
if ($stampaNelRapporto) {
|
||||
$patchPayload['StampaNelRapporto'] = true;
|
||||
}
|
||||
|
||||
if ($primaPagina) {
|
||||
$patchPayload['PrimaPagina'] = true;
|
||||
}
|
||||
|
||||
$patchEndpoint = "CampioneFile({$campioneFileId})";
|
||||
$patchJsonLog = json_encode($patchPayload, JSON_PRETTY_PRINT);
|
||||
$logContentPhotos .= "curl --location --request PATCH '{$apiBaseUrl}{$patchEndpoint}' \\\n" .
|
||||
"--header 'Content-Type: application/json' \\\n" .
|
||||
"--header 'Authorization: Bearer ••••••' \\\n" .
|
||||
"--data '{$patchJsonLog}'\n\n";
|
||||
|
||||
$patchResult = $api->patch($patchEndpoint, $patchPayload);
|
||||
$logContentPhotos .= "PATCH RESPONSE:\n" . json_encode($patchResult, JSON_PRETTY_PRINT) . "\n\n";
|
||||
}
|
||||
|
||||
$logContentPhotos .= "---\n";
|
||||
$photosUploaded++;
|
||||
}
|
||||
} else {
|
||||
$logContentPhotos .= "SKIP: main campione has invalid IdCampione\n";
|
||||
}
|
||||
} elseif (empty($campioni)) {
|
||||
$logContentPhotos .= "SKIP: no campioni created, cannot upload photos\n";
|
||||
}
|
||||
|
||||
$logFilePhotos = $logDir . "commessa_{$commessaId}_photos_step5_2_" . time() . ".txt";
|
||||
$writeLog($logFilePhotos, $logContentPhotos, "STEP 6.2 - Photos (commessa={$commessaId})");
|
||||
file_put_contents($logFileStep6, $logContentStep6);
|
||||
|
||||
// 🔹 STEP 7: Update Custom Fields for CommessaWeb
|
||||
if (!empty($fieldValues)) {
|
||||
// GET con espansione per CustomField
|
||||
$expand = "CommesseCustomFields(\$expand=CustomField)";
|
||||
$commessaWithFields = $api->get("CommessaWeb({$commessaId})?\$expand={$expand}");
|
||||
$commessaWithFields = $api->get("CommessaWeb(" . $commessaId . ")?\$expand=" . $expand);
|
||||
|
||||
// Logga il GET
|
||||
$logContentGet = "curl --location --request GET '{$apiBaseUrl}CommessaWeb({$commessaId})?\$expand=CommesseCustomFields(\$expand=CustomField)' \\\n" .
|
||||
"--header 'Authorization: Bearer ••••••'\n\n" .
|
||||
"RESPONSE:\n" . json_encode($commessaWithFields, JSON_PRETTY_PRINT);
|
||||
$logFileGet = $logDir . "commessa_{$commessaId}_get_step7_" . time() . ".txt";
|
||||
$writeLog($logFileGet, $logContentGet, "STEP 7 - GET CustomFields (commessa={$commessaId})");
|
||||
file_put_contents($logFileGet, $logContentGet);
|
||||
|
||||
// Prepara payload PATCH
|
||||
$commessaCustomFields = [];
|
||||
@@ -455,7 +186,7 @@ try {
|
||||
|
||||
// Valida se il campo è di tipo Data
|
||||
if ($fieldType === 'Data' && $newValue !== $currentValue) {
|
||||
$newValue = formatDateToExport($newValue);
|
||||
$newValue = validateDate($newValue);
|
||||
}
|
||||
|
||||
$commessaCustomFields[] = [
|
||||
@@ -464,7 +195,6 @@ try {
|
||||
];
|
||||
}
|
||||
|
||||
$logFileStep7 = null;
|
||||
if (!empty($commessaCustomFields)) {
|
||||
$updatePayload = ["CommesseCustomFields" => $commessaCustomFields];
|
||||
|
||||
@@ -479,7 +209,7 @@ try {
|
||||
|
||||
$logContentStep7 .= "\n\nRESPONSE:\n" . json_encode($patchResponse, JSON_PRETTY_PRINT);
|
||||
$logFileStep7 = $logDir . "commessa_{$commessaId}_update_step7_" . time() . ".txt";
|
||||
$writeLog($logFileStep7, $logContentStep7, "STEP 7 - PATCH CustomFields (commessa={$commessaId})");
|
||||
file_put_contents($logFileStep7, $logContentStep7);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -496,34 +226,18 @@ try {
|
||||
]);
|
||||
|
||||
// 🔹 STEP 9: Send CommessaWeb to laboratory (commentato come richiesto)
|
||||
|
||||
/*
|
||||
$sendResult = $api->post("CommessaWeb({$commessaId})/InviaCommessa", []);
|
||||
|
||||
// Logga il POST
|
||||
$logContentStep9 = "curl --location --request POST '{$apiBaseUrl}CommessaWeb({$commessaId})/InviaCommessa' \\\n" .
|
||||
"--header 'Content-Type: application/json' \\\n" .
|
||||
"--header 'Authorization: Bearer ••••••' \\\n" .
|
||||
"--data '{}'\n\n" .
|
||||
"RESPONSE:\n" . json_encode($sendResult, JSON_PRETTY_PRINT);
|
||||
"--header 'Content-Type: application/json' \\\n" .
|
||||
"--header 'Authorization: Bearer ••••••' \\\n" .
|
||||
"--data '{}'\n\n" .
|
||||
"RESPONSE:\n" . json_encode($sendResult, JSON_PRETTY_PRINT);
|
||||
$logFileStep9 = $logDir . "commessa_{$commessaId}_send_step9_" . time() . ".txt";
|
||||
$writeLog($logFileStep9, $logContentStep9, "STEP 9 - InviaCommessa (commessa={$commessaId})");
|
||||
|
||||
|
||||
// 🔹 STEP 9.5: Importazione da CommessaWeb a Commessa (commentato come richiesto)
|
||||
// Supplier call: POST api/odata/CommessaWeb(XXX)/ImportaCommessa
|
||||
|
||||
$importPayload = ["IdUtente" => 285]; // user-id
|
||||
$importResult = $api->post("CommessaWeb({$commessaId})/ImportaCommessa", $importPayload);
|
||||
|
||||
$importPayloadLog = json_encode($importPayload, JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES);
|
||||
// Logga il POST
|
||||
$logContentStep91 = "curl --location --request POST '{$apiBaseUrl}CommessaWeb({$commessaId})/ImportaCommessa' \\\n" .
|
||||
"--header 'Content-Type: application/json' \\\n" .
|
||||
"--header 'Authorization: Bearer ••••••' \\\n" .
|
||||
"--data '{$importPayloadLog}'\n\n" .
|
||||
"RESPONSE:\n" . json_encode($importResult, JSON_PRETTY_PRINT);
|
||||
$logFileStep91 = $logDir . "commessa_{$commessaId}_importa_step91_" . time() . ".txt";
|
||||
$writeLog($logFileStep91, $logContentStep91, "STEP 9.5 - ImportaCommessa (commessa={$commessaId})");
|
||||
file_put_contents($logFileStep9, $logContentStep9);
|
||||
*/
|
||||
|
||||
// 🔹 STEP 10: GET di controllo post-PATCH
|
||||
$expand = "CommesseCustomFields(\$expand=CustomField)";
|
||||
@@ -534,7 +248,7 @@ try {
|
||||
"--header 'Authorization: Bearer ••••••'\n\n" .
|
||||
"RESPONSE:\n" . json_encode($commessaAfterPatch, JSON_PRETTY_PRINT);
|
||||
$logFileStep10 = $logDir . "commessa_{$commessaId}_get_step10_" . time() . ".txt";
|
||||
$writeLog($logFileStep10, $logContentStep10, "STEP 10 - GET verify (commessa={$commessaId})");
|
||||
file_put_contents($logFileStep10, $logContentStep10);
|
||||
|
||||
// 🔹 STEP 11: Prepare final response
|
||||
$finalCommessa = [
|
||||
@@ -548,22 +262,17 @@ try {
|
||||
];
|
||||
|
||||
echo json_encode([
|
||||
"success" => true,
|
||||
"idcommessaweb" => $commessaId,
|
||||
"commessaweb" => $commessaWebCode,
|
||||
"commessaWeb" => $finalCommessa,
|
||||
"success" => true,
|
||||
"commessaWeb" => $finalCommessa,
|
||||
"commessaWebApiResponse" => $commessaWeb, // Incluso per debug
|
||||
"totalCampioni" => count($campioni),
|
||||
"totalCustomFields" => count($commessaAfterPatch["CommesseCustomFields"] ?? []),
|
||||
"totalPhotos" => count($photos),
|
||||
"message" => "Export successful",
|
||||
"logFiles" => [
|
||||
"step5_create" => $logFileStep5,
|
||||
"step5_2_photos" => $logFilePhotos,
|
||||
"step6_campioni" => $logFileStep6,
|
||||
"step7_patch" => $logFileStep7 ?? null,
|
||||
"step9_1_importa" => $logFileStep91,
|
||||
"step10_get" => $logFileStep10
|
||||
"totalCampioni" => count($campioni),
|
||||
"totalCustomFields" => count($commessaAfterPatch["CommesseCustomFields"] ?? []),
|
||||
"message" => "Export successful",
|
||||
"logFiles" => [
|
||||
"step5_create" => $logFileStep5,
|
||||
"step6_campioni" => $logFileStep6,
|
||||
"step7_patch" => $logFileStep7,
|
||||
"step10_get" => $logFileStep10
|
||||
]
|
||||
]);
|
||||
} catch (Exception $e) {
|
||||
@@ -573,12 +282,10 @@ try {
|
||||
"success" => false,
|
||||
"message" => "Export failed: " . $e->getMessage(),
|
||||
"logFiles" => [
|
||||
"step5_create" => $logFileStep5 ?? null,
|
||||
"step5_2_photos" => $logFilePhotos ?? null,
|
||||
"step6_campioni" => $logFileStep6 ?? null,
|
||||
"step7_patch" => $logFileStep7 ?? null,
|
||||
"step9_1_importa" => $logFileStep91 ?? null,
|
||||
"step10_get" => $logFileStep10 ?? null
|
||||
"step5_create" => $logFileStep5 ?? null,
|
||||
"step6_campioni" => $logFileStep6 ?? null,
|
||||
"step7_patch" => $logFileStep7 ?? null,
|
||||
"step10_get" => $logFileStep10 ?? null
|
||||
]
|
||||
]);
|
||||
}
|
||||
|
||||
@@ -1,40 +0,0 @@
|
||||
<?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: GET api/odata/AnagraficaCertestObject
|
||||
$endpoint = 'AnagraficaCertestObject';
|
||||
|
||||
// Opzionale: parametri OData ($top, $filter, $orderby, ecc.)
|
||||
$options = []; // es: ['$top' => 100]
|
||||
|
||||
// Debug: salva URL usata
|
||||
$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_url.txt', $full_url . PHP_EOL, FILE_APPEND);
|
||||
|
||||
// Chiamata API
|
||||
$data = $api->get($endpoint, $options);
|
||||
|
||||
// Salva il JSON in locale
|
||||
file_put_contents(__DIR__ . '/anagrafica_certest_object_response.json', json_encode($data, JSON_PRETTY_PRINT));
|
||||
|
||||
echo json_encode($data);
|
||||
} catch (Exception $e) {
|
||||
file_put_contents(
|
||||
__DIR__ . '/error_log.txt',
|
||||
date('Y-m-d H:i:s') . ' - ' . $e->getMessage() . PHP_EOL,
|
||||
FILE_APPEND
|
||||
);
|
||||
http_response_code(500);
|
||||
echo json_encode(['error' => $e->getMessage()]);
|
||||
}
|
||||
@@ -1,40 +0,0 @@
|
||||
<?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: GET api/odata/AnagraficaCertestService
|
||||
$endpoint = 'AnagraficaCertestService';
|
||||
|
||||
// Opzionale: parametri OData ($top, $filter, $orderby, ecc.)
|
||||
$options = []; // es: ['$top' => 100]
|
||||
|
||||
// Debug: salva URL usata
|
||||
$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_url.txt', $full_url . PHP_EOL, FILE_APPEND);
|
||||
|
||||
// Chiamata API
|
||||
$data = $api->get($endpoint, $options);
|
||||
|
||||
// Salva il JSON in locale
|
||||
file_put_contents(__DIR__ . '/anagrafica_certest_service_response.json', json_encode($data, JSON_PRETTY_PRINT));
|
||||
|
||||
echo json_encode($data);
|
||||
} catch (Exception $e) {
|
||||
file_put_contents(
|
||||
__DIR__ . '/error_log.txt',
|
||||
date('Y-m-d H:i:s') . ' - ' . $e->getMessage() . PHP_EOL,
|
||||
FILE_APPEND
|
||||
);
|
||||
http_response_code(500);
|
||||
echo json_encode(['error' => $e->getMessage()]);
|
||||
}
|
||||
@@ -1,40 +0,0 @@
|
||||
<?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;
|
||||
if ($idCliente <= 0) {
|
||||
http_response_code(400);
|
||||
echo json_encode(['error' => 'Missing or invalid id_cliente']);
|
||||
exit;
|
||||
}
|
||||
|
||||
$api = VisualLimsApiClient::getInstance();
|
||||
|
||||
// Build endpoint with OData $expand WITHOUT encoding '$' as %24
|
||||
$endpoint = "Cliente($idCliente)?\$expand=Responsabili";
|
||||
|
||||
// Debug URL (real final URL)
|
||||
$base_url = 'https://93.43.5.102/limsapi/api/odata/';
|
||||
$full_url = $base_url . $endpoint;
|
||||
file_put_contents(__DIR__ . '/last_url.txt', $full_url . PHP_EOL, FILE_APPEND);
|
||||
|
||||
// Call API: options must be empty because expand is already in endpoint
|
||||
$data = $api->get($endpoint, []);
|
||||
|
||||
|
||||
file_put_contents(__DIR__ . '/cliente_responsabili_response.json', json_encode($data, JSON_PRETTY_PRINT));
|
||||
|
||||
// Return only the list (standard shape used by the frontend)
|
||||
$responsabili = $data['Responsabili'] ?? [];
|
||||
echo json_encode(['value' => $responsabili]);
|
||||
} catch (Exception $e) {
|
||||
file_put_contents(__DIR__ . '/error_log.txt', date('Y-m-d H:i:s') . ' - ' . $e->getMessage() . PHP_EOL, FILE_APPEND);
|
||||
http_response_code(500);
|
||||
echo json_encode(['error' => $e->getMessage()]);
|
||||
}
|
||||
@@ -9,28 +9,11 @@ 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.
|
||||
// This ensures client dropdowns auto-select the correct row idclient, which in turn
|
||||
// triggers the ClienteResponsabile select to populate via the mock.
|
||||
if (($_ENV['SIMULATE_EXPORT_LIMS'] ?? '') === 'true') {
|
||||
require_once __DIR__ . '/class/db-functions.php';
|
||||
$pdo = DBHandlerSelect::getInstance()->getConnection();
|
||||
$stmt = $pdo->query("SELECT DISTINCT idclient FROM datadb WHERE idclient IS NOT NULL AND idclient > 0 ORDER BY idclient ASC");
|
||||
$ids = $stmt->fetchAll(PDO::FETCH_COLUMN);
|
||||
$fakeClients = array_map(fn($id) => [
|
||||
'IdCliente' => (int) $id,
|
||||
'Nominativo' => "Cliente Simulato {$id}",
|
||||
'CodiceCliente' => "SIM_{$id}",
|
||||
], $ids);
|
||||
echo json_encode(['value' => $fakeClients]);
|
||||
exit;
|
||||
}
|
||||
$api = VisualLimsApiClient::getInstance();
|
||||
|
||||
// Parametri OData
|
||||
$params = [
|
||||
'$select' => 'IdCliente,Nominativo,CodiceCliente',
|
||||
'$select' => 'IdCliente,Nominativo,CodiceNazioneFatturazione',
|
||||
'$orderby' => 'Nominativo asc'
|
||||
];
|
||||
|
||||
@@ -74,21 +57,10 @@ try {
|
||||
throw new Exception("Massimo numero di tentativi raggiunto per $endpoint");
|
||||
}
|
||||
|
||||
// Cache file (1 hour TTL)
|
||||
$cacheFile = __DIR__ . '/cache/clienti.json';
|
||||
if (file_exists($cacheFile) && (time() - filemtime($cacheFile) < 3600)) {
|
||||
readfile($cacheFile);
|
||||
exit;
|
||||
}
|
||||
|
||||
// Esegui la chiamata con retry
|
||||
$data = makeApiRequest($api, $endpoint);
|
||||
|
||||
$json = json_encode($data);
|
||||
if (!is_dir(__DIR__ . '/cache')) mkdir(__DIR__ . '/cache', 0777, true);
|
||||
file_put_contents($cacheFile, $json);
|
||||
|
||||
echo $json;
|
||||
echo json_encode($data);
|
||||
} catch (Exception $e) {
|
||||
http_response_code(500);
|
||||
$errorResponse = [
|
||||
|
||||
@@ -21,26 +21,17 @@ try {
|
||||
}
|
||||
|
||||
$results = [];
|
||||
$cacheDir = __DIR__ . '/cache';
|
||||
if (!is_dir($cacheDir)) mkdir($cacheDir, 0777, true);
|
||||
|
||||
foreach ($fieldIds as $customFieldId) {
|
||||
$cacheFile = $cacheDir . '/customfield_' . $customFieldId . '.json';
|
||||
|
||||
// Use cache if fresh (1 hour)
|
||||
if (file_exists($cacheFile) && (time() - filemtime($cacheFile) < 3600)) {
|
||||
$results[$customFieldId] = json_decode(file_get_contents($cacheFile), true);
|
||||
continue;
|
||||
}
|
||||
|
||||
$endpoint = "CustomField($customFieldId)?\$expand=CustomFieldsValues";
|
||||
$data = $api->get($endpoint);
|
||||
$values = $data['CustomFieldsValues'] ?? [];
|
||||
$results[$customFieldId] = $values;
|
||||
|
||||
file_put_contents($cacheFile, json_encode($values));
|
||||
$results[$customFieldId] = $data['CustomFieldsValues'] ?? [];
|
||||
}
|
||||
|
||||
// Debug ფაილი
|
||||
file_put_contents(__DIR__ . '/customfield_values_response.json', json_encode($results));
|
||||
|
||||
echo json_encode($results);
|
||||
} catch (Exception $e) {
|
||||
http_response_code(500);
|
||||
|
||||
@@ -1,93 +0,0 @@
|
||||
<?php
|
||||
// get_fixed_field_data.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);
|
||||
|
||||
$field = $_GET['field'] ?? ''; // es: MoltiplicatorePrezzo, AnagraficaCertestObject, ...
|
||||
|
||||
if (!$field) {
|
||||
http_response_code(400);
|
||||
echo json_encode(['error' => 'Parametro "field" mancante']);
|
||||
exit;
|
||||
}
|
||||
|
||||
$api = VisualLimsApiClient::getInstance(); // also loads dotenv as a side-effect
|
||||
$simulate = ($_ENV['SIMULATE_EXPORT_LIMS'] ?? '') === 'true';
|
||||
$base_url = 'https://93.43.5.102/limsapi/api/odata/';
|
||||
|
||||
$data = null;
|
||||
$endpoint = null;
|
||||
$cache_file = null;
|
||||
$options = []; // qui puoi aggiungere filtri/ordering per campo se serve
|
||||
|
||||
try {
|
||||
switch ($field) {
|
||||
case 'MoltiplicatorePrezzo':
|
||||
$endpoint = 'MoltiplicatorePrezzi';
|
||||
$cache_file = __DIR__ . '/cache/moltiplicatori_prezzo.json';
|
||||
break;
|
||||
|
||||
case 'AnagraficaCertestObject':
|
||||
$endpoint = 'AnagraficaCertestObject';
|
||||
$cache_file = __DIR__ . '/cache/anagrafica_certest_object.json';
|
||||
break;
|
||||
|
||||
case 'AnagraficaCertestService':
|
||||
$endpoint = 'AnagraficaCertestService';
|
||||
$cache_file = __DIR__ . '/cache/anagrafica_certest_service.json';
|
||||
break;
|
||||
|
||||
case 'ClienteResponsabile':
|
||||
$id_cliente = (int)($_GET['id_cliente'] ?? 0);
|
||||
if ($id_cliente <= 0) {
|
||||
if (!$simulate) {
|
||||
http_response_code(400);
|
||||
echo json_encode(['error' => 'Manca o invalido id_cliente']);
|
||||
exit;
|
||||
}
|
||||
$id_cliente = 1; // dummy — mock ignores the actual ID
|
||||
}
|
||||
$endpoint = "Cliente($id_cliente)?\$expand=Responsabili";
|
||||
$cache_file = __DIR__ . '/cache/cliente_responsabili_' . $id_cliente . '.json';
|
||||
break;
|
||||
|
||||
// case 'FuturoCampo5':
|
||||
// $endpoint = 'QualcosaElse';
|
||||
// break;
|
||||
|
||||
default:
|
||||
http_response_code(400);
|
||||
echo json_encode(['error' => "Campo non supportato: $field"]);
|
||||
exit;
|
||||
}
|
||||
|
||||
// Caching skipped in simulate mode to avoid polluting real-data cache files
|
||||
if (!$simulate && $cache_file && file_exists($cache_file) && (time() - filemtime($cache_file) < 3600)) { // 1 ora
|
||||
$data = json_decode(file_get_contents($cache_file), true);
|
||||
} else {
|
||||
$data = $api->get($endpoint, $options);
|
||||
|
||||
if (!$simulate && $cache_file) {
|
||||
file_put_contents($cache_file, json_encode($data, JSON_PRETTY_PRINT));
|
||||
}
|
||||
}
|
||||
|
||||
// Log ultimo URL (opzionale, per debug)
|
||||
$full_url = $base_url . $endpoint . ($options ? '?' . http_build_query($options) : '');
|
||||
file_put_contents(__DIR__ . '/last_urls.log', date('c') . " - $field - $full_url\n", FILE_APPEND);
|
||||
|
||||
echo json_encode($data);
|
||||
} catch (Exception $e) {
|
||||
file_put_contents(
|
||||
__DIR__ . '/error_log.txt',
|
||||
date('Y-m-d H:i:s') . " [$field] " . $e->getMessage() . PHP_EOL,
|
||||
FILE_APPEND
|
||||
);
|
||||
http_response_code(500);
|
||||
echo json_encode(['error' => $e->getMessage()]);
|
||||
}
|
||||
@@ -1,43 +0,0 @@
|
||||
<?php
|
||||
require_once dirname(__DIR__, 2) . '/vendor/autoload.php';
|
||||
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()]);
|
||||
}
|
||||
@@ -1,55 +0,0 @@
|
||||
<?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 i Moltiplicatori Prezzo
|
||||
// (dal documento: GET api/odata/MoltiplicatorePrezzi)
|
||||
$endpoint = 'MoltiplicatorePrezzi';
|
||||
|
||||
// Opzionale: parametri OData ($top, $filter, $orderby, ecc.)
|
||||
$options = [
|
||||
'$orderby' => 'Descrizione asc'
|
||||
];
|
||||
|
||||
// Debug: salva URL usata
|
||||
$base_url = 'https://93.43.5.102/limsapi/api/odata/';
|
||||
$queryParts = [];
|
||||
foreach ($options as $k => $v) {
|
||||
// mantieni il $ nella chiave, encoda solo il valore
|
||||
$queryParts[] = $k . '=' . rawurlencode($v);
|
||||
}
|
||||
$query = implode('&', $queryParts);
|
||||
$full_url = $base_url . $endpoint . ($query ? '?' . $query : '');
|
||||
file_put_contents(__DIR__ . '/last_url.txt', $full_url . PHP_EOL, FILE_APPEND);
|
||||
|
||||
// Chiamata API
|
||||
$data = $api->get($endpoint, $options);
|
||||
// ✅ Force sort locally by "Descrizione" (A→Z)
|
||||
if (isset($data['value']) && is_array($data['value'])) {
|
||||
usort($data['value'], function ($a, $b) {
|
||||
$da = isset($a['Descrizione']) ? trim((string)$a['Descrizione']) : '';
|
||||
$db = isset($b['Descrizione']) ? trim((string)$b['Descrizione']) : '';
|
||||
return strcasecmp($da, $db); // case-insensitive alphabetical
|
||||
});
|
||||
}
|
||||
// Salva il JSON in locale
|
||||
file_put_contents(__DIR__ . '/moltiplicatori_prezzo_response.json', json_encode($data, JSON_PRETTY_PRINT));
|
||||
|
||||
echo json_encode($data);
|
||||
} catch (Exception $e) {
|
||||
file_put_contents(
|
||||
__DIR__ . '/error_log.txt',
|
||||
date('Y-m-d H:i:s') . ' - ' . $e->getMessage() . PHP_EOL,
|
||||
FILE_APPEND
|
||||
);
|
||||
http_response_code(500);
|
||||
echo json_encode(['error' => $e->getMessage()]);
|
||||
}
|
||||
@@ -1,38 +0,0 @@
|
||||
<?php
|
||||
require_once __DIR__ . '/include/headscript.php'; // o il tuo bootstrap standard
|
||||
|
||||
header('Content-Type: application/json');
|
||||
|
||||
try {
|
||||
if (!isset($_GET['iddatadb']) || !is_numeric($_GET['iddatadb'])) {
|
||||
echo json_encode(['success' => true, 'field' => null]);
|
||||
exit;
|
||||
}
|
||||
|
||||
$iddatadb = (int)$_GET['iddatadb'];
|
||||
|
||||
$db = DBHandlerSelect::getInstance();
|
||||
$pdo = $db->getConnection();
|
||||
|
||||
// 1) prendo templateid da datadb
|
||||
// 2) cerco max 1 record in template_mapping con is_visible_parts=1
|
||||
$sql = "
|
||||
SELECT tm.field_id, tm.field_label, tm.data_type, tm.has_list
|
||||
FROM datadb d
|
||||
JOIN template_mapping tm ON tm.template_id = d.templateid
|
||||
WHERE d.iddatadb = ?
|
||||
AND tm.is_visible_parts = 1
|
||||
ORDER BY tm.id ASC
|
||||
LIMIT 1
|
||||
";
|
||||
$stmt = $pdo->prepare($sql);
|
||||
$stmt->execute([$iddatadb]);
|
||||
$row = $stmt->fetch(PDO::FETCH_ASSOC);
|
||||
|
||||
echo json_encode([
|
||||
'success' => true,
|
||||
'field' => $row ? $row : null
|
||||
]);
|
||||
} catch (Throwable $e) {
|
||||
echo json_encode(['success' => false, 'message' => $e->getMessage()]);
|
||||
}
|
||||
@@ -1,94 +0,0 @@
|
||||
<?php
|
||||
require_once dirname(__DIR__, 2) . '/vendor/autoload.php';
|
||||
require_once __DIR__ . '/class/VisualLimsApiClient.class.php';
|
||||
|
||||
header('Content-Type: application/json');
|
||||
|
||||
// Disable PHP error display
|
||||
ini_set('display_errors', '0');
|
||||
error_reporting(E_ALL);
|
||||
|
||||
try {
|
||||
$api = VisualLimsApiClient::getInstance(); // also loads dotenv
|
||||
|
||||
// In simulate mode: return fake users
|
||||
if (($_ENV['SIMULATE_EXPORT_LIMS'] ?? '') === 'true') {
|
||||
$fakeUsers = [
|
||||
[
|
||||
'IdUtente' => 1001,
|
||||
'Nominativo' => 'Utente Simulato 1001'
|
||||
],
|
||||
[
|
||||
'IdUtente' => 1002,
|
||||
'Nominativo' => 'Utente Simulato 1002'
|
||||
]
|
||||
];
|
||||
|
||||
echo json_encode(['value' => $fakeUsers]);
|
||||
exit;
|
||||
}
|
||||
|
||||
// OData parameters
|
||||
$params = [];
|
||||
|
||||
// Build query string
|
||||
$queryString = http_build_query($params);
|
||||
|
||||
// Final endpoint
|
||||
$endpoint = "Utente?$queryString";
|
||||
|
||||
// Function to execute request with retry
|
||||
function makeApiRequest($api, $endpoint, $maxRetries = 3)
|
||||
{
|
||||
for ($retry = 0; $retry < $maxRetries; $retry++) {
|
||||
try {
|
||||
$data = $api->get($endpoint);
|
||||
|
||||
// Save response for debug
|
||||
file_put_contents(__DIR__ . '/utenti_response.json', json_encode($data, JSON_PRETTY_PRINT));
|
||||
|
||||
return $data;
|
||||
} catch (Exception $e) {
|
||||
$errorMessage = $e->getMessage();
|
||||
|
||||
// Retry only on token/auth-related issue
|
||||
if (
|
||||
strpos($errorMessage, 'HTTP 400') !== false &&
|
||||
strpos($errorMessage, 'Cannot persist the object') !== false
|
||||
) {
|
||||
try {
|
||||
$api->refreshToken(); // must exist in VisualLimsApiClient
|
||||
error_log("Tentativo $retry: Refresh token eseguito per endpoint $endpoint");
|
||||
} catch (Exception $refreshEx) {
|
||||
error_log("Errore durante il refresh del token: " . $refreshEx->getMessage());
|
||||
throw new Exception("Impossibile eseguire il refresh del token: " . $refreshEx->getMessage());
|
||||
}
|
||||
|
||||
usleep(500000); // 500 ms
|
||||
continue;
|
||||
}
|
||||
|
||||
throw $e;
|
||||
}
|
||||
}
|
||||
|
||||
throw new Exception("Massimo numero di tentativi raggiunto per $endpoint");
|
||||
}
|
||||
|
||||
// Execute request
|
||||
$data = makeApiRequest($api, $endpoint);
|
||||
|
||||
echo json_encode($data);
|
||||
} catch (Exception $e) {
|
||||
http_response_code(500);
|
||||
|
||||
$errorResponse = [
|
||||
'error' => $e->getMessage(),
|
||||
'file' => $e->getFile(),
|
||||
'line' => $e->getLine(),
|
||||
'trace' => $e->getTraceAsString()
|
||||
];
|
||||
|
||||
error_log("Errore in get_utenti.php: " . json_encode($errorResponse));
|
||||
echo json_encode($errorResponse);
|
||||
}
|
||||
@@ -1,979 +0,0 @@
|
||||
/**
|
||||
* gridRenderer.js — Data-driven grid renderer for imported.php
|
||||
*
|
||||
* Reads window.gridData (array of row objects) and window.gridMeta (column defs, config).
|
||||
* Renders only visible rows into DOM. Propagate/edit operates on gridData array,
|
||||
* then re-renders visible rows. Save reads from gridData, not DOM.
|
||||
*/
|
||||
(function () {
|
||||
'use strict';
|
||||
|
||||
const PAGE_SIZE = 20;
|
||||
let revealedCount = PAGE_SIZE;
|
||||
let dropdownOptionsCache = {}; // fieldId -> [{id, text}] — used only for small lists
|
||||
const dropdownNameCache = {}; // "fieldId_valueId" -> label
|
||||
let clientData = []; // loaded from get_clienti.php
|
||||
let fixedFieldCache = window.fixedFieldDataCache || {};
|
||||
window.fixedFieldDataCache = fixedFieldCache;
|
||||
|
||||
const data = window.gridData || [];
|
||||
const meta = window.gridMeta || {};
|
||||
const columns = meta.columns || [];
|
||||
let totalRows = data.length;
|
||||
|
||||
// ── DOM refs ────────────────────────────────────────────────────────────
|
||||
let rowContainer = null;
|
||||
let headerContainer = null;
|
||||
let topContainer = null;
|
||||
let statusEl = null;
|
||||
|
||||
// ── Helpers ─────────────────────────────────────────────────────────────
|
||||
|
||||
function esc(str) {
|
||||
if (str === null || str === undefined) return '';
|
||||
const d = document.createElement('div');
|
||||
d.textContent = String(str);
|
||||
return d.innerHTML;
|
||||
}
|
||||
|
||||
function getDetailValue(rowIndex, mappingId) {
|
||||
return data[rowIndex].details[String(mappingId)] ?? '';
|
||||
}
|
||||
|
||||
function setDetailValue(rowIndex, mappingId, value) {
|
||||
data[rowIndex].details[String(mappingId)] = value;
|
||||
data[rowIndex]._dirty = true;
|
||||
}
|
||||
|
||||
function getFixedValue(rowIndex, key) {
|
||||
return data[rowIndex].fixedFields[key] ?? '';
|
||||
}
|
||||
|
||||
function setFixedValue(rowIndex, key, value) {
|
||||
data[rowIndex].fixedFields[key] = value;
|
||||
data[rowIndex]._dirty = true;
|
||||
}
|
||||
|
||||
// ── Client data (AJAX Select2, no bulk loading) ──────────────────────
|
||||
|
||||
function formatClientLabel(client) {
|
||||
return (client.Nominativo || '').trim();
|
||||
}
|
||||
|
||||
// Cache of resolved client names: id → name
|
||||
const clientNameCache = {};
|
||||
|
||||
// Build select with only the selected option
|
||||
function buildClientOptionsHTML(selectedId) {
|
||||
let html = '<option value="">Select a client...</option>';
|
||||
if (selectedId) {
|
||||
const label = clientNameCache[selectedId] || selectedId;
|
||||
html += `<option value="${esc(String(selectedId))}" selected>${esc(String(label))}</option>`;
|
||||
}
|
||||
return html;
|
||||
}
|
||||
|
||||
// Pre-resolve all unique client IDs used in data rows, then re-render
|
||||
async function resolveClientNames() {
|
||||
const ids = new Set();
|
||||
data.forEach(row => {
|
||||
if (row.idclient) ids.add(String(row.idclient));
|
||||
if (row.cliente_fornitore_id) ids.add(String(row.cliente_fornitore_id));
|
||||
// Fixed fields that are client-sourced
|
||||
if (row.fixedFields) {
|
||||
for (const [key, val] of Object.entries(row.fixedFields)) {
|
||||
const cfg = fixedFieldApiConfig[key];
|
||||
if (cfg && cfg.source === 'clients' && val) ids.add(String(val));
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
// Batch resolve via single request per ID
|
||||
const unresolvedIds = [...ids].filter(id => !clientNameCache[id]);
|
||||
if (unresolvedIds.length === 0) return;
|
||||
|
||||
await Promise.all(unresolvedIds.map(async (id) => {
|
||||
try {
|
||||
const resp = await fetch('search_clienti.php?id=' + encodeURIComponent(id));
|
||||
const json = await resp.json();
|
||||
const item = (json.results || [])[0];
|
||||
if (item) clientNameCache[id] = item.text;
|
||||
} catch (e) { /* ignore */ }
|
||||
}));
|
||||
}
|
||||
|
||||
// Select2 AJAX config for client selects
|
||||
const clientSelect2Config = {
|
||||
placeholder: 'Search client...',
|
||||
allowClear: true,
|
||||
width: '100%',
|
||||
minimumInputLength: 0,
|
||||
dropdownCssClass: 'select2-dropdown-smaller',
|
||||
ajax: {
|
||||
url: 'search_clienti.php',
|
||||
dataType: 'json',
|
||||
delay: 150,
|
||||
data: function(params) { return { q: params.term || '', limit: 20 }; },
|
||||
processResults: function(data) { return { results: data.results || [] }; },
|
||||
cache: true
|
||||
}
|
||||
};
|
||||
|
||||
async function loadClientData() {
|
||||
// No longer loads all clients — AJAX Select2 handles search
|
||||
// Just resolve names for pre-selected values
|
||||
await resolveClientNames();
|
||||
}
|
||||
|
||||
// ── Fixed field data loading ───────────────────────────────────────────
|
||||
|
||||
const fixedFieldApiConfig = {
|
||||
MoltiplicatorePrezzo: { endpoint: 'MoltiplicatorePrezzo', idKey: 'IdMoltiplicatorePrezzo', textKey: 'Descrizione' },
|
||||
AnagraficaCertestObject: { endpoint: 'AnagraficaCertestObject', idKey: 'IdAnagrafica', textKey: 'NomeAnagrafica' },
|
||||
AnagraficaCertestService: { endpoint: 'AnagraficaCertestService', idKey: 'IdAnagrafica', textKey: 'NomeAnagrafica' },
|
||||
ClienteResponsabile: { endpoint: 'ClienteResponsabile', idKey: 'IdClienteResponsabile', textKey: 'Nominativo', dependsOn: 'idclient', getParams: (cid) => ({ id_cliente: cid }) },
|
||||
ClienteFornitore: { source: 'clients' },
|
||||
ClienteAnalisi: { source: 'clients' },
|
||||
};
|
||||
|
||||
let _pendingFixed = {};
|
||||
|
||||
async function loadFixedFieldOptions(fieldKey, clientId) {
|
||||
const config = fixedFieldApiConfig[fieldKey];
|
||||
if (!config) return [];
|
||||
|
||||
// Client-sourced fields — handled by AJAX Select2, skip preloading
|
||||
if (config.source === 'clients') {
|
||||
fixedFieldCache[fieldKey] = [];
|
||||
return [];
|
||||
}
|
||||
|
||||
const cacheKey = fieldKey + (clientId ? '_' + clientId : '');
|
||||
if (fixedFieldCache[cacheKey]) return fixedFieldCache[cacheKey];
|
||||
if (_pendingFixed[cacheKey]) return _pendingFixed[cacheKey];
|
||||
|
||||
const params = { field: config.endpoint };
|
||||
if (config.dependsOn && clientId) {
|
||||
Object.assign(params, config.getParams(clientId));
|
||||
}
|
||||
|
||||
_pendingFixed[cacheKey] = (async () => {
|
||||
try {
|
||||
const resp = await fetch('get_fixed_field_data.php?' + new URLSearchParams(params));
|
||||
const json = await resp.json();
|
||||
let items = (fieldKey === 'ClienteResponsabile') ? (json.Responsabili || []) : (json.value || json.d?.results || json || []);
|
||||
const results = items.map(item => ({
|
||||
id: item[config.idKey],
|
||||
text: (item.Codice ? item.Codice + ' - ' : '') + (item[config.textKey] || '')
|
||||
})).sort((a, b) => String(a.text).localeCompare(String(b.text), 'it', { sensitivity: 'base' }));
|
||||
fixedFieldCache[cacheKey] = results;
|
||||
delete _pendingFixed[cacheKey];
|
||||
return results;
|
||||
} catch (e) {
|
||||
delete _pendingFixed[cacheKey];
|
||||
console.error('Failed to load fixed field ' + fieldKey, e);
|
||||
return [];
|
||||
}
|
||||
})();
|
||||
|
||||
return _pendingFixed[cacheKey];
|
||||
}
|
||||
|
||||
// ── Custom field dropdown data loading ─────────────────────────────────
|
||||
|
||||
|
||||
// Select2 AJAX config factory for SceltaMultipla
|
||||
function sceltaSelect2Config(fieldId) {
|
||||
return {
|
||||
placeholder: 'Search...',
|
||||
allowClear: true,
|
||||
width: '100%',
|
||||
minimumInputLength: 0,
|
||||
ajax: {
|
||||
url: 'search_customfield_values.php',
|
||||
dataType: 'json',
|
||||
delay: 150,
|
||||
data: function(params) { return { field_id: fieldId, q: params.term || '', limit: 10 }; },
|
||||
processResults: function(data) { return { results: data.results || [] }; },
|
||||
cache: true
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
// ── Preload all data ───────────────────────────────────────────────────
|
||||
|
||||
async function preloadAllData() {
|
||||
// 1. Clients
|
||||
await loadClientData();
|
||||
|
||||
// 2. Non-dependent fixed fields
|
||||
const nonDependent = Object.keys(fixedFieldApiConfig).filter(k => !fixedFieldApiConfig[k].dependsOn && !fixedFieldApiConfig[k].source);
|
||||
await Promise.all(nonDependent.map(k => loadFixedFieldOptions(k)));
|
||||
|
||||
// Client-sourced fixed fields
|
||||
const clientSourced = Object.keys(fixedFieldApiConfig).filter(k => fixedFieldApiConfig[k].source === 'clients');
|
||||
await Promise.all(clientSourced.map(k => loadFixedFieldOptions(k)));
|
||||
|
||||
// 3. Dependent fixed fields — collect unique clientIds
|
||||
const clientIds = new Set();
|
||||
data.forEach(row => {
|
||||
if (row.idclient) clientIds.add(String(row.idclient));
|
||||
});
|
||||
const dependent = Object.keys(fixedFieldApiConfig).filter(k => fixedFieldApiConfig[k].dependsOn);
|
||||
for (const fieldKey of dependent) {
|
||||
for (const cid of clientIds) {
|
||||
await loadFixedFieldOptions(fieldKey, cid);
|
||||
}
|
||||
}
|
||||
|
||||
// 4. Warm server cache + resolve SceltaMultipla names in one request
|
||||
const allFieldIds = [...new Set(
|
||||
columns.filter(c => (c.type === 'detail' || c.type === 'main_field') && c.dataType === 'SceltaMultipla' && c.fieldId)
|
||||
.map(c => String(c.fieldId))
|
||||
)];
|
||||
if (allFieldIds.length > 0) {
|
||||
try {
|
||||
const resp = await fetch('get_customfield_values.php?field_ids=' + allFieldIds.join(','));
|
||||
const json = await resp.json();
|
||||
const entries = json.data ? json.data : json;
|
||||
for (const [fid, values] of Object.entries(entries)) {
|
||||
if (Array.isArray(values)) {
|
||||
values.forEach(v => {
|
||||
dropdownNameCache[fid + '_' + v.IdCustomFieldsValue] = v.Valore || '';
|
||||
});
|
||||
}
|
||||
}
|
||||
} catch (e) {
|
||||
console.error('Failed to preload dropdown data:', e);
|
||||
}
|
||||
}
|
||||
|
||||
console.log('[gridRenderer] preload done:', {
|
||||
clients: clientData.length,
|
||||
fixedFieldCache: Object.keys(fixedFieldCache),
|
||||
dropdownOptionsCache: Object.keys(dropdownOptionsCache),
|
||||
});
|
||||
}
|
||||
|
||||
// ── Cell rendering ─────────────────────────────────────────────────────
|
||||
|
||||
function createCell(col, rowIndex, cellIndex) {
|
||||
const div = document.createElement('div');
|
||||
div.className = 'grid-cell editable-cell';
|
||||
div.dataset.col = col.key;
|
||||
div.dataset.colType = col.type;
|
||||
div.dataset.row = rowIndex;
|
||||
div.dataset.index = cellIndex;
|
||||
div.style.flex = `0 0 ${col.width}px`;
|
||||
|
||||
const row = data[rowIndex];
|
||||
|
||||
switch (col.type) {
|
||||
case 'main_field':
|
||||
div.innerHTML = createInputHTML(col, row.mainFieldValue || '', rowIndex);
|
||||
break;
|
||||
|
||||
case 'status': {
|
||||
const st = row.status || 'i';
|
||||
const label = st === 'i' ? 'Imported' : (st === 'P' ? 'In Progress' : 'To LIMS');
|
||||
let html = `<span class="status-badge status-${esc(st)}">${label}</span>`;
|
||||
if (row.commessaweb) {
|
||||
html += `<span class="commessaweb-code" style="display:block; font-size:0.75em; color:#555; margin-top:2px;">${esc(row.commessaweb)}</span>`;
|
||||
}
|
||||
div.innerHTML = html;
|
||||
div.classList.remove('editable-cell');
|
||||
break;
|
||||
}
|
||||
|
||||
case 'idclient': {
|
||||
const sel = document.createElement('select');
|
||||
sel.className = 'cell-input dropdown-select client-select searchable-client';
|
||||
sel.dataset.currentValue = row.idclient || '';
|
||||
sel.innerHTML = buildClientOptionsHTML(row.idclient);
|
||||
div.appendChild(sel);
|
||||
break;
|
||||
}
|
||||
|
||||
case 'cliente_fornitore_id': {
|
||||
const sel = document.createElement('select');
|
||||
sel.className = 'cell-input dropdown-select client-select searchable-client fornitore-select';
|
||||
sel.dataset.currentValue = row.cliente_fornitore_id || '';
|
||||
sel.innerHTML = buildClientOptionsHTML(row.cliente_fornitore_id);
|
||||
div.appendChild(sel);
|
||||
break;
|
||||
}
|
||||
|
||||
case 'detail': {
|
||||
const val = getDetailValue(rowIndex, col.key);
|
||||
div.innerHTML = createInputHTML(col, val, rowIndex);
|
||||
break;
|
||||
}
|
||||
|
||||
case 'tested_component':
|
||||
div.style.overflow = 'visible';
|
||||
div.innerHTML = `<div style="display:flex; align-items:center; gap:4px; width:100%; height:100%;"><input type="text" class="cell-input manual-input" style="flex:1; min-width:0; height:28px;"><button type="button" class="add-part-btn btn btn-sm btn-primary" data-row="${rowIndex}" data-iddatadb="${row.iddatadb}" style="display:inline-flex; align-items:center; justify-content:center; min-width:28px; width:28px; height:28px; padding:0; font-size:12px; flex-shrink:0; text-align:center;"><i class="fas fa-plus" style="margin:0; padding:0;"></i></button></div>`;
|
||||
break;
|
||||
|
||||
case 'awb':
|
||||
div.innerHTML = `<select class="carrier-select"><option value="tnt-it">TNT Italy</option><option value="dhl">DHL</option><option value="gls">GLS</option><option value="sda">SDA</option><option value="ups">UPS</option></select>` +
|
||||
`<input type="text" class="cell-input awb-input" placeholder="AWB Number">` +
|
||||
`<button type="button" class="go-btn" data-row="${rowIndex}"><i class="fas fa-play"></i></button>`;
|
||||
break;
|
||||
|
||||
case 'tracking':
|
||||
div.className = 'grid-cell tracking-info';
|
||||
div.dataset.row = rowIndex;
|
||||
div.innerHTML = '<span class="tracking-result">Shipment Info</span>';
|
||||
break;
|
||||
|
||||
case 'fixed': {
|
||||
const val = getFixedValue(rowIndex, col.key);
|
||||
div.innerHTML = createFixedFieldHTML(col, val, rowIndex);
|
||||
break;
|
||||
}
|
||||
|
||||
case 'static': {
|
||||
const val = row[col.key] || '';
|
||||
div.classList.remove('editable-cell');
|
||||
if (col.key === 'filename_import' && val) {
|
||||
div.innerHTML = `<a href="imported_trf/${esc(val)}" target="_blank">File</a>`;
|
||||
} else {
|
||||
div.innerHTML = `<span>${esc(val)}</span>`;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return div;
|
||||
}
|
||||
|
||||
function createInputHTML(col, value, rowIndex) {
|
||||
const cls = col.isManual ? 'manual-input' : 'auto-input';
|
||||
const reqCls = col.isRequired ? ' required-input' : '';
|
||||
const req = col.isRequired ? ' required' : '';
|
||||
const v = esc(value);
|
||||
|
||||
if (col.dataType === 'SceltaMultipla') {
|
||||
const options = buildDropdownOptionsHTML(col.fieldId, value);
|
||||
return `<select class="cell-input dropdown-select searchable-dropdown ${cls}${reqCls}" data-field-id="${col.fieldId}"${req}>${options}</select>`;
|
||||
}
|
||||
if (col.dataType === 'Data') {
|
||||
return `<input type="text" class="cell-input date-picker ${cls}${reqCls}" value="${v}"${req}>`;
|
||||
}
|
||||
if (col.dataType === 'INT') {
|
||||
return `<input type="number" class="cell-input ${cls}${reqCls}" value="${v}"${req}>`;
|
||||
}
|
||||
if (col.autoValue === 'import_time' || (meta.timeLabels || []).includes(col.label)) {
|
||||
return `<input type="time" class="cell-input ${cls}${reqCls}" value="${v}"${req}>`;
|
||||
}
|
||||
return `<input type="text" class="cell-input ${cls}${reqCls}" value="${v}"${req}>`;
|
||||
}
|
||||
|
||||
function createFixedFieldHTML(col, value, rowIndex) {
|
||||
if (col.dataType === 'DATE') {
|
||||
const reqCls = col.isRequired ? ' required-input' : '';
|
||||
const req = col.isRequired ? ' required' : '';
|
||||
return `<input type="text" class="cell-input date-picker manual-input${reqCls} fixed-input" data-fixed-key="${col.key}" value="${esc(value)}"${req}>`;
|
||||
}
|
||||
|
||||
// Client-sourced fields → AJAX Select2 (like idclient)
|
||||
const config = fixedFieldApiConfig[col.key];
|
||||
if (config && config.source === 'clients') {
|
||||
const reqCls = col.isRequired ? ' required-input' : '';
|
||||
const req = col.isRequired ? ' required' : '';
|
||||
let opts = '<option value="">Select...</option>';
|
||||
if (value) {
|
||||
const label = clientNameCache[value] || value;
|
||||
opts += `<option value="${esc(String(value))}" selected>${esc(String(label))}</option>`;
|
||||
}
|
||||
return `<select class="cell-input manual-input fixed-input searchable-client api-fixed-select${reqCls}" data-fixed-key="${col.key}" data-current-value="${esc(value)}"${req}>${opts}</select>`;
|
||||
}
|
||||
|
||||
// Select — build from cache
|
||||
const isApiField = !!config;
|
||||
const selectClass = isApiField ? 'api-fixed-select' : '';
|
||||
let options = '<option value="">Seleziona...</option>';
|
||||
const cacheKey = config?.dependsOn
|
||||
? col.key + '_' + (data[rowIndex].idclient || '')
|
||||
: col.key;
|
||||
const items = fixedFieldCache[cacheKey] || [];
|
||||
items.forEach(item => {
|
||||
const sel = String(item.id) === String(value) ? ' selected' : '';
|
||||
options += `<option value="${item.id}"${sel}>${esc(item.text)}</option>`;
|
||||
});
|
||||
|
||||
const reqCls = col.isRequired ? ' required-input' : '';
|
||||
const req = col.isRequired ? ' required' : '';
|
||||
return `<select class="cell-input manual-input fixed-input ${selectClass}${reqCls}" data-fixed-key="${col.key}" data-current-value="${esc(value)}"${req}>${options}</select>`;
|
||||
}
|
||||
|
||||
function buildDropdownOptionsHTML(fieldId, selectedValue) {
|
||||
let html = '<option value="">Seleziona...</option>';
|
||||
if (selectedValue) {
|
||||
const label = dropdownNameCache[fieldId + '_' + selectedValue] || selectedValue;
|
||||
html += `<option value="${esc(String(selectedValue))}" selected>${esc(String(label))}</option>`;
|
||||
}
|
||||
return html;
|
||||
}
|
||||
|
||||
// ── Row rendering ──────────────────────────────────────────────────────
|
||||
|
||||
function renderActionButtons(rowIndex) {
|
||||
const row = data[rowIndex];
|
||||
const isExported = row.status === 'l';
|
||||
const div = document.createElement('div');
|
||||
div.className = 'grid-cell button-cell';
|
||||
div.style.flex = '0 0 auto';
|
||||
div.style.position = 'relative';
|
||||
|
||||
let html = '';
|
||||
if (meta.isAdmin) {
|
||||
html += `<button type="button" class="export-lims-btn action-btn" data-row="${rowIndex}" data-iddatadb="${row.iddatadb}" title="${isExported ? 'Already exported' : '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>`;
|
||||
}
|
||||
html += `<button type="button" class="save-btn action-btn" data-row="${rowIndex}" title="Save" style="background:#28a745; color:white; border:none; border-radius:5px; cursor:pointer;"><i class="fas fa-save"></i></button>`;
|
||||
html += `<button type="button" class="photos-btn action-btn" data-row="${rowIndex}" data-iddatadb="${row.iddatadb}" title="Photos" style="background:#007bff; color:white; border:none; border-radius:5px; cursor:pointer;"><i class="fas fa-camera"></i></button>`;
|
||||
html += `<button type="button" class="parts-btn action-btn" data-row="${rowIndex}" data-iddatadb="${row.iddatadb}" title="Parts" style="background:#ffc107; color:white; border:none; border-radius:5px; cursor:pointer;"><i class="fas fa-puzzle-piece"></i></button>`;
|
||||
html += `<button type="button" class="delete-btn action-btn" data-row="${rowIndex}" data-iddatadb="${row.iddatadb}" title="Delete" style="background:#dc3545; color:white; border:none; border-radius:5px; cursor:pointer;"><i class="fas fa-trash"></i></button>`;
|
||||
|
||||
div.innerHTML = html;
|
||||
return div;
|
||||
}
|
||||
|
||||
function renderRow(rowIndex) {
|
||||
const row = data[rowIndex];
|
||||
const rowDiv = document.createElement('div');
|
||||
rowDiv.className = 'grid-row';
|
||||
rowDiv.dataset.id = row.iddatadb;
|
||||
|
||||
if (row._dirty) rowDiv.classList.add('row-dirty');
|
||||
|
||||
// Action buttons
|
||||
rowDiv.appendChild(renderActionButtons(rowIndex));
|
||||
|
||||
// All columns
|
||||
let cellIndex = 1;
|
||||
columns.forEach(col => {
|
||||
rowDiv.appendChild(createCell(col, rowIndex, cellIndex));
|
||||
cellIndex++;
|
||||
});
|
||||
|
||||
// Restore validation errors if present
|
||||
if (row._validationErrors && row._validationErrors.length > 0) {
|
||||
rowDiv.classList.add('validation-row-error');
|
||||
const messages = [];
|
||||
row._validationErrors.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 = rowDiv.querySelector(`.grid-cell[data-index="${targetIndex}"]`);
|
||||
} else {
|
||||
cell = rowDiv.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;
|
||||
}
|
||||
});
|
||||
// Show error msg on button cell
|
||||
const btnCell2 = rowDiv.querySelector('.button-cell');
|
||||
if (btnCell2) {
|
||||
const errorEl = document.createElement('div');
|
||||
errorEl.className = 'batch-error-msg';
|
||||
errorEl.textContent = 'Warning — click for details';
|
||||
errorEl.addEventListener('click', () => {
|
||||
document.getElementById('exportResponseMessage').innerHTML = messages.join('<br>');
|
||||
document.getElementById('exportResponseModalLabel').textContent = 'Validation Error (id: ' + row.iddatadb + ')';
|
||||
new bootstrap.Modal(document.getElementById('exportResponseModal'), { keyboard: false }).show();
|
||||
});
|
||||
btnCell2.appendChild(errorEl);
|
||||
}
|
||||
}
|
||||
|
||||
// Restore export error indicator if present
|
||||
if (row._exportError) {
|
||||
rowDiv.classList.add('batch-row-error');
|
||||
const btnCell = rowDiv.querySelector('.button-cell');
|
||||
if (btnCell) {
|
||||
const errorEl = document.createElement('div');
|
||||
errorEl.className = 'batch-error-msg';
|
||||
errorEl.textContent = 'Warning — click for details';
|
||||
errorEl.addEventListener('click', () => {
|
||||
document.getElementById('exportResponseMessage').innerHTML = row._exportError.replace(/\n/g, '<br>');
|
||||
document.getElementById('exportResponseModalLabel').textContent = 'Error (id: ' + row.iddatadb + ')';
|
||||
new bootstrap.Modal(document.getElementById('exportResponseModal'), { keyboard: false }).show();
|
||||
});
|
||||
btnCell.appendChild(errorEl);
|
||||
}
|
||||
}
|
||||
|
||||
return rowDiv;
|
||||
}
|
||||
|
||||
// ── Render visible rows ────────────────────────────────────────────────
|
||||
|
||||
function renderVisibleRows() {
|
||||
if (!rowContainer) return;
|
||||
|
||||
// Refresh totalRows in case data was modified externally
|
||||
totalRows = data.length;
|
||||
if (revealedCount < totalRows) revealedCount = totalRows;
|
||||
|
||||
// Destroy Select2 on existing rows
|
||||
$(rowContainer).find('.select2-hidden-accessible').select2('destroy');
|
||||
|
||||
rowContainer.innerHTML = '';
|
||||
const end = Math.min(revealedCount, totalRows);
|
||||
for (let i = 0; i < end; i++) {
|
||||
rowContainer.appendChild(renderRow(i));
|
||||
}
|
||||
|
||||
// Init flatpickr on visible date pickers
|
||||
initFlatpickr();
|
||||
|
||||
updateStatus();
|
||||
updateDirtyIndicator();
|
||||
}
|
||||
|
||||
function renderSingleRow(rowIndex) {
|
||||
const existing = rowContainer.querySelector(`.grid-row[data-id="${data[rowIndex].iddatadb}"]`);
|
||||
if (!existing) return;
|
||||
|
||||
// Destroy Select2 before removing
|
||||
$(existing).find('.select2-hidden-accessible').select2('destroy');
|
||||
|
||||
const newRow = renderRow(rowIndex);
|
||||
existing.replaceWith(newRow);
|
||||
|
||||
// Init flatpickr
|
||||
$(newRow).find('.date-picker').each(function () {
|
||||
flatpickr(this, { dateFormat: 'Y-m-d', allowInput: true });
|
||||
});
|
||||
}
|
||||
|
||||
function revealNextBatch() {
|
||||
if (revealedCount >= totalRows) return;
|
||||
const start = revealedCount;
|
||||
revealedCount = Math.min(revealedCount + PAGE_SIZE, totalRows);
|
||||
for (let i = start; i < revealedCount; i++) {
|
||||
rowContainer.appendChild(renderRow(i));
|
||||
}
|
||||
initFlatpickr();
|
||||
updateStatus();
|
||||
}
|
||||
|
||||
function initFlatpickr() {
|
||||
$(rowContainer).find('.date-picker:not(.flatpickr-input)').each(function () {
|
||||
flatpickr(this, { dateFormat: 'Y-m-d', allowInput: true });
|
||||
});
|
||||
}
|
||||
|
||||
// ── Headers & Propagate row ────────────────────────────────────────────
|
||||
|
||||
function renderHeaders() {
|
||||
if (!headerContainer) return;
|
||||
headerContainer.innerHTML = '';
|
||||
|
||||
// Actions header
|
||||
const actH = document.createElement('div');
|
||||
actH.className = 'grid-header button-header';
|
||||
actH.style.flex = '0 0 380px';
|
||||
actH.textContent = 'Actions';
|
||||
headerContainer.appendChild(actH);
|
||||
|
||||
let idx = 1;
|
||||
columns.forEach(col => {
|
||||
const h = document.createElement('div');
|
||||
h.className = 'grid-header';
|
||||
h.dataset.index = idx;
|
||||
h.style.flex = `0 0 ${col.width}px`;
|
||||
h.style.position = 'relative';
|
||||
h.innerHTML = esc(col.label) + '<div class="resizer"></div>';
|
||||
headerContainer.appendChild(h);
|
||||
idx++;
|
||||
});
|
||||
}
|
||||
|
||||
function renderTopRow() {
|
||||
if (!topContainer) return;
|
||||
topContainer.innerHTML = '';
|
||||
|
||||
// Empty cell for actions column
|
||||
const empty = document.createElement('div');
|
||||
empty.className = 'grid-cell save-all-cell';
|
||||
topContainer.appendChild(empty);
|
||||
|
||||
columns.forEach((col, colIdx) => {
|
||||
const cell = document.createElement('div');
|
||||
cell.className = 'grid-cell grid-top-cell';
|
||||
cell.style.flex = `0 0 ${col.width}px`;
|
||||
|
||||
if (col.editable === false || col.type === 'static' || col.type === 'tracking') {
|
||||
// Empty top cell
|
||||
} else if (col.type === 'idclient') {
|
||||
cell.innerHTML = `<select class="custom-field dropdown-select client-select searchable-client" data-column="idclient" id="clientSelect"><option value="">Select a client...</option></select>` +
|
||||
`<button type="button" class="propagate-btn" data-column="idclient" data-col-index="${colIdx}"><i class="fas fa-arrow-down"></i></button>`;
|
||||
} else if (col.type === 'cliente_fornitore_id') {
|
||||
cell.innerHTML = `<select class="custom-field dropdown-select client-select searchable-client" data-column="cliente_fornitore_id" id="clienteFornitoreSelect"><option value="">Select a supplier...</option></select>` +
|
||||
`<button type="button" class="propagate-btn" data-column="cliente_fornitore_id" data-col-index="${colIdx}"><i class="fas fa-arrow-down"></i></button>`;
|
||||
} else if (col.type === 'fixed' && col.dataType === 'DATE') {
|
||||
cell.innerHTML = `<input type="text" class="custom-field date-picker manual-input fixed-top" data-column="fixed_${col.key}" data-fixed-key="${col.key}" value="">` +
|
||||
`<button type="button" class="propagate-btn" data-column="fixed_${col.key}" data-col-index="${colIdx}"><i class="fas fa-arrow-down"></i></button>`;
|
||||
} else if (col.type === 'fixed') {
|
||||
const isApiField = !!fixedFieldApiConfig[col.key];
|
||||
if (isApiField) {
|
||||
cell.innerHTML = `<select class="custom-field dropdown-select api-fixed-select fixed-top" data-column="fixed_${col.key}" data-fixed-key="${col.key}"><option value="">Seleziona...</option></select>` +
|
||||
`<button type="button" class="propagate-btn" data-column="fixed_${col.key}" data-col-index="${colIdx}"><i class="fas fa-arrow-down"></i></button>`;
|
||||
} else {
|
||||
cell.innerHTML = `<input type="number" class="custom-field manual-input fixed-top" data-column="fixed_${col.key}" data-fixed-key="${col.key}" value="">` +
|
||||
`<button type="button" class="propagate-btn" data-column="fixed_${col.key}" data-col-index="${colIdx}"><i class="fas fa-arrow-down"></i></button>`;
|
||||
}
|
||||
} else if (col.type === 'detail' || col.type === 'main_field') {
|
||||
if (col.dataType === 'SceltaMultipla') {
|
||||
cell.innerHTML = `<select class="custom-field dropdown-select" data-column="col_${colIdx}" data-field-id="${col.fieldId}"><option value="">Seleziona...</option></select>` +
|
||||
`<button type="button" class="propagate-btn" data-column="col_${colIdx}" data-col-index="${colIdx}"><i class="fas fa-arrow-down"></i></button>`;
|
||||
} else if (col.dataType === 'Data') {
|
||||
cell.innerHTML = `<input type="text" class="custom-field date-picker" data-column="col_${colIdx}" value="">` +
|
||||
`<button type="button" class="propagate-btn" data-column="col_${colIdx}" data-col-index="${colIdx}"><i class="fas fa-arrow-down"></i></button>`;
|
||||
} else {
|
||||
cell.innerHTML = `<input type="text" class="custom-field" data-column="col_${colIdx}" value="">` +
|
||||
`<button type="button" class="propagate-btn" data-column="col_${colIdx}" data-col-index="${colIdx}"><i class="fas fa-arrow-down"></i></button>`;
|
||||
}
|
||||
}
|
||||
|
||||
topContainer.appendChild(cell);
|
||||
});
|
||||
|
||||
// Populate top row selects
|
||||
populateTopRowSelects();
|
||||
}
|
||||
|
||||
async function populateTopRowSelects() {
|
||||
// Client selects in top row — AJAX mode
|
||||
const clientSel = document.getElementById('clientSelect');
|
||||
if (clientSel) {
|
||||
clientSel.innerHTML = buildClientOptionsHTML(meta.defaultIdclient);
|
||||
$(clientSel).select2(clientSelect2Config);
|
||||
}
|
||||
const fornitSel = document.getElementById('clienteFornitoreSelect');
|
||||
if (fornitSel) {
|
||||
fornitSel.innerHTML = buildClientOptionsHTML('');
|
||||
$(fornitSel).select2(clientSelect2Config);
|
||||
}
|
||||
|
||||
// Fixed field selects in top row
|
||||
topContainer.querySelectorAll('.api-fixed-select').forEach(sel => {
|
||||
const fieldKey = sel.dataset.fixedKey;
|
||||
const config = fixedFieldApiConfig[fieldKey];
|
||||
|
||||
// Client-sourced → init as AJAX Select2
|
||||
if (config && config.source === 'clients') {
|
||||
$(sel).select2(clientSelect2Config);
|
||||
return;
|
||||
}
|
||||
|
||||
if (config && config.dependsOn) {
|
||||
// For dependent fields: merge all cached values across all clientIds
|
||||
const allItems = new Map();
|
||||
for (const [key, items] of Object.entries(fixedFieldCache)) {
|
||||
if (key.startsWith(fieldKey + '_')) {
|
||||
items.forEach(item => allItems.set(String(item.id), item));
|
||||
}
|
||||
}
|
||||
sel.innerHTML = '<option value="">Seleziona...</option>';
|
||||
[...allItems.values()]
|
||||
.sort((a, b) => String(a.text).localeCompare(String(b.text), 'it', { sensitivity: 'base' }))
|
||||
.forEach(item => sel.add(new Option(item.text, item.id)));
|
||||
} else {
|
||||
const items = fixedFieldCache[fieldKey] || [];
|
||||
sel.innerHTML = '<option value="">Seleziona...</option>';
|
||||
items.forEach(item => sel.add(new Option(item.text, item.id)));
|
||||
}
|
||||
});
|
||||
|
||||
// Custom field dropdowns in top row — AJAX Select2
|
||||
topContainer.querySelectorAll('.dropdown-select[data-field-id]').forEach(sel => {
|
||||
const fieldId = sel.dataset.fieldId;
|
||||
if (fieldId) $(sel).select2(sceltaSelect2Config(fieldId));
|
||||
});
|
||||
|
||||
// Flatpickr in top row
|
||||
topContainer.querySelectorAll('.date-picker').forEach(el => {
|
||||
flatpickr(el, { dateFormat: 'Y-m-d', allowInput: true });
|
||||
});
|
||||
}
|
||||
|
||||
// ── Event delegation ───────────────────────────────────────────────────
|
||||
|
||||
function attachEvents() {
|
||||
if (!rowContainer) return;
|
||||
|
||||
// Cell value changes → write to gridData
|
||||
rowContainer.addEventListener('change', function (e) {
|
||||
const cell = e.target.closest('.grid-cell');
|
||||
if (!cell || !cell.dataset.row) return;
|
||||
const rowIndex = parseInt(cell.dataset.row);
|
||||
const colType = cell.dataset.colType;
|
||||
const colKey = cell.dataset.col;
|
||||
const value = e.target.value;
|
||||
|
||||
if (colType === 'detail' || colType === 'main_field') {
|
||||
if (colType === 'main_field') {
|
||||
data[rowIndex].mainFieldValue = value;
|
||||
}
|
||||
setDetailValue(rowIndex, colKey, value);
|
||||
} else if (colType === 'fixed') {
|
||||
setFixedValue(rowIndex, colKey, value);
|
||||
} else if (colType === 'idclient') {
|
||||
data[rowIndex].idclient = value;
|
||||
data[rowIndex]._dirty = true;
|
||||
} else if (colType === 'cliente_fornitore_id') {
|
||||
data[rowIndex].cliente_fornitore_id = value;
|
||||
data[rowIndex]._dirty = true;
|
||||
console.log('[gridRenderer] cliente_fornitore_id changed:', rowIndex, value);
|
||||
}
|
||||
|
||||
// Visual feedback
|
||||
cell.classList.add('cell-changed');
|
||||
updateDirtyIndicator();
|
||||
});
|
||||
|
||||
// Propagate buttons
|
||||
document.addEventListener('click', function (e) {
|
||||
const btn = e.target.closest('.propagate-btn');
|
||||
if (!btn) return;
|
||||
|
||||
const colIndex = parseInt(btn.dataset.colIndex);
|
||||
const column = btn.dataset.column;
|
||||
if (isNaN(colIndex) && !column) return;
|
||||
|
||||
// Get value from the input/select in the same cell
|
||||
const cell = btn.closest('.grid-cell') || btn.closest('.grid-top-cell');
|
||||
if (!cell) return;
|
||||
const input = cell.querySelector('select, input');
|
||||
if (!input) return;
|
||||
const value = $(input).hasClass('select2-hidden-accessible') ? $(input).val() : input.value;
|
||||
|
||||
// Cache Select2 label so re-render shows name not ID
|
||||
if (value && $(input).hasClass('select2-hidden-accessible')) {
|
||||
const label = $(input).find('option:selected').text();
|
||||
if (label && label !== value) {
|
||||
clientNameCache[value] = label;
|
||||
// Also cache for SceltaMultipla
|
||||
const fieldId = input.dataset?.fieldId;
|
||||
if (fieldId) dropdownNameCache[fieldId + '_' + value] = label;
|
||||
}
|
||||
}
|
||||
|
||||
const col = columns[colIndex] || null;
|
||||
|
||||
if (column === 'idclient') {
|
||||
data.forEach(row => { row.idclient = value; row._dirty = true; });
|
||||
} else if (column === 'cliente_fornitore_id') {
|
||||
data.forEach(row => { row.cliente_fornitore_id = value; row._dirty = true; });
|
||||
} else if (column && column.startsWith('fixed_')) {
|
||||
const fixedKey = column.replace('fixed_', '');
|
||||
data.forEach(row => { row.fixedFields[fixedKey] = value; row._dirty = true; });
|
||||
} else if (col) {
|
||||
if (col.type === 'detail' || col.type === 'main_field') {
|
||||
data.forEach(row => {
|
||||
row.details[col.key] = value;
|
||||
if (col.type === 'main_field') row.mainFieldValue = value;
|
||||
row._dirty = true;
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
renderVisibleRows();
|
||||
});
|
||||
|
||||
// Select2 change events (don't bubble via native addEventListener)
|
||||
$(rowContainer).on('change', '.searchable-client', function () {
|
||||
const cell = this.closest('.grid-cell');
|
||||
if (!cell || !cell.dataset.row) return;
|
||||
const rowIndex = parseInt(cell.dataset.row);
|
||||
const colType = cell.dataset.colType;
|
||||
const value = $(this).val() || '';
|
||||
|
||||
// Cache selected label
|
||||
if (value) {
|
||||
const label = $(this).find('option:selected').text();
|
||||
if (label && label !== value) clientNameCache[value] = label;
|
||||
}
|
||||
|
||||
if (colType === 'idclient') {
|
||||
data[rowIndex].idclient = value;
|
||||
data[rowIndex]._dirty = true;
|
||||
} else if (colType === 'cliente_fornitore_id') {
|
||||
data[rowIndex].cliente_fornitore_id = value;
|
||||
data[rowIndex]._dirty = true;
|
||||
}
|
||||
cell.classList.add('cell-changed');
|
||||
updateDirtyIndicator();
|
||||
});
|
||||
|
||||
// Cache labels on SceltaMultipla change
|
||||
$(rowContainer).on('change', '.searchable-dropdown', function () {
|
||||
const val = $(this).val();
|
||||
const fieldId = this.dataset.fieldId;
|
||||
if (val && fieldId) {
|
||||
const label = $(this).find('option:selected').text();
|
||||
if (label && label !== val) dropdownNameCache[fieldId + '_' + val] = label;
|
||||
}
|
||||
});
|
||||
|
||||
// Select2 change on fixed field selects
|
||||
$(rowContainer).on('change', '.api-fixed-select', function () {
|
||||
const cell = this.closest('.grid-cell');
|
||||
if (!cell || !cell.dataset.row) return;
|
||||
const rowIndex = parseInt(cell.dataset.row);
|
||||
const key = this.dataset.fixedKey || cell.dataset.col;
|
||||
const value = $(this).val() || '';
|
||||
|
||||
if (key) {
|
||||
data[rowIndex].fixedFields[key] = value;
|
||||
data[rowIndex]._dirty = true;
|
||||
cell.classList.add('cell-changed');
|
||||
updateDirtyIndicator();
|
||||
}
|
||||
});
|
||||
|
||||
// Scroll
|
||||
function onScroll() {
|
||||
if (revealedCount >= totalRows) return;
|
||||
const docEl = document.documentElement;
|
||||
const scrollBottom = Math.max(docEl.scrollTop + docEl.clientHeight, document.body.scrollTop + window.innerHeight);
|
||||
if (scrollBottom >= docEl.scrollHeight - 300) {
|
||||
revealNextBatch();
|
||||
}
|
||||
}
|
||||
window.addEventListener('scroll', onScroll, { passive: true });
|
||||
document.querySelector('.page-wrapper')?.addEventListener('scroll', onScroll, { passive: true });
|
||||
}
|
||||
|
||||
// ── Save ───────────────────────────────────────────────────────────────
|
||||
|
||||
window.buildSavePayload = function (rowIndex) {
|
||||
const row = data[rowIndex];
|
||||
const formData = new FormData();
|
||||
formData.append('iddatadb', row.iddatadb);
|
||||
|
||||
// Details
|
||||
for (const [mappingId, value] of Object.entries(row.details)) {
|
||||
formData.append(`details${mappingId}field_value`, value);
|
||||
}
|
||||
|
||||
// Client
|
||||
if (row.idclient) formData.append('idclient', row.idclient);
|
||||
formData.append('cliente_fornitore_id', row.cliente_fornitore_id || '');
|
||||
|
||||
// Fixed fields → real column names
|
||||
const aliasMap = meta.fixedAliasMap || {};
|
||||
for (const [logicalKey, value] of Object.entries(row.fixedFields)) {
|
||||
const realKey = aliasMap[logicalKey] || logicalKey;
|
||||
formData.append(realKey, value);
|
||||
}
|
||||
|
||||
return formData;
|
||||
};
|
||||
|
||||
// ── Dirty indicator ────────────────────────────────────────────────────
|
||||
|
||||
function updateDirtyIndicator() {
|
||||
const dirtyCount = data.filter(r => r._dirty).length;
|
||||
const indicator = document.getElementById('unsavedChanges');
|
||||
const changedEl = document.getElementById('changedRows');
|
||||
if (indicator) {
|
||||
indicator.style.display = dirtyCount > 0 ? '' : 'none';
|
||||
}
|
||||
if (changedEl) {
|
||||
changedEl.textContent = dirtyCount > 0 ? `(${dirtyCount} rows)` : '';
|
||||
}
|
||||
}
|
||||
|
||||
function updateStatus() {
|
||||
if (!statusEl) return;
|
||||
const shown = Math.min(revealedCount, totalRows);
|
||||
if (shown >= totalRows) {
|
||||
statusEl.textContent = `All ${totalRows} rows loaded`;
|
||||
setTimeout(() => statusEl.style.display = 'none', 2000);
|
||||
} else {
|
||||
statusEl.textContent = `Showing ${shown} of ${totalRows} rows — scroll down for more`;
|
||||
}
|
||||
}
|
||||
|
||||
// ── Lazy Select2 for row selects ───────────────────────────────────────
|
||||
|
||||
function initLazySelect2() {
|
||||
$(document).on('mouseenter', '.grid-row .grid-cell', function () {
|
||||
$(this).find('.searchable-client:not(.select2-hidden-accessible)').each(function () {
|
||||
$(this).select2(clientSelect2Config);
|
||||
});
|
||||
$(this).find('.searchable-dropdown:not(.select2-hidden-accessible)').each(function () {
|
||||
const fieldId = this.dataset.fieldId;
|
||||
if (fieldId) $(this).select2(sceltaSelect2Config(fieldId));
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
// ── Init ───────────────────────────────────────────────────────────────
|
||||
|
||||
async function init() {
|
||||
rowContainer = document.getElementById('gridRowContainer');
|
||||
headerContainer = document.getElementById('gridHeaderContainer');
|
||||
topContainer = document.getElementById('gridTopContainer');
|
||||
|
||||
if (!rowContainer) {
|
||||
console.error('gridRenderer: #gridRowContainer not found');
|
||||
return;
|
||||
}
|
||||
|
||||
// Status bar
|
||||
statusEl = document.createElement('div');
|
||||
statusEl.style.cssText = 'text-align:center; padding:8px; color:#666; font-size:12px;';
|
||||
if (totalRows > PAGE_SIZE) {
|
||||
statusEl.textContent = `Loading data...`;
|
||||
rowContainer.parentElement.appendChild(statusEl);
|
||||
}
|
||||
|
||||
// Preload all dropdown/field data
|
||||
await preloadAllData();
|
||||
|
||||
// Render
|
||||
renderHeaders();
|
||||
renderTopRow();
|
||||
renderVisibleRows();
|
||||
|
||||
// Events
|
||||
attachEvents();
|
||||
initLazySelect2();
|
||||
}
|
||||
|
||||
// Start when DOM ready
|
||||
if (document.readyState === 'loading') {
|
||||
document.addEventListener('DOMContentLoaded', init);
|
||||
} else {
|
||||
init();
|
||||
}
|
||||
|
||||
// ── Public API ─────────────────────────────────────────────────────────
|
||||
window.gridRenderer = {
|
||||
renderVisibleRows,
|
||||
renderSingleRow,
|
||||
revealNextBatch,
|
||||
buildSavePayload: window.buildSavePayload,
|
||||
getData: () => data,
|
||||
getMeta: () => meta,
|
||||
getClientData: () => clientData,
|
||||
getDirtyRows: () => data.filter(r => r._dirty).map((r, i) => i),
|
||||
clearDirty: (rowIndex) => { data[rowIndex]._dirty = false; updateDirtyIndicator(); },
|
||||
};
|
||||
|
||||
})();
|
||||
@@ -59,8 +59,7 @@ foreach ($allMappings as $mapping) {
|
||||
}
|
||||
}
|
||||
if (!$mainFieldMapping) {
|
||||
$filtered = array_filter($allMappings, fn($m) => !$m['is_manual']);
|
||||
$mainFieldMapping = reset($filtered);
|
||||
$mainFieldMapping = reset(array_filter($allMappings, fn($m) => !$m['is_manual']));
|
||||
}
|
||||
|
||||
// Retrieve data from datadb
|
||||
|
||||
+321
-1794
File diff suppressed because it is too large
Load Diff
@@ -20,10 +20,9 @@ if ($_SERVER['REQUEST_METHOD'] !== 'POST' || !isset($_POST['template_id']) || !i
|
||||
|
||||
$template_id = intval($_POST['template_id']);
|
||||
$selected_rows = array_map('intval', $_POST['selected_rows']);
|
||||
$columns = json_decode(urldecode($_POST['columns'] ?? '[]'), true);
|
||||
$rows = json_decode(urldecode($_POST['rows'] ?? '[]'), true);
|
||||
$excelrows = json_decode(urldecode($_POST['excelrows'] ?? '[]'), true);
|
||||
|
||||
$columns = json_decode($_POST['columns'], true);
|
||||
$rows = json_decode($_POST['rows'], true);
|
||||
$excelrows = json_decode($_POST['excelrows'], true);
|
||||
$newFilename = htmlspecialchars($_POST['filename']);
|
||||
|
||||
$_SESSION['template_id'] = $template_id;
|
||||
@@ -47,7 +46,7 @@ $pdo = $db->getConnection();
|
||||
$importReferenceCode = date('YmdHis') . '-' . uniqid();
|
||||
|
||||
// Recupera tutti i mapping dal template
|
||||
$stmt = $pdo->prepare("SELECT id, excel_column, data_type, is_required, manual_default, is_manual, field_label, field_id, main_field, auto_value 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 FROM template_mapping WHERE template_id = ?");
|
||||
$stmt->execute([$template_id]);
|
||||
$allMappings = $stmt->fetchAll(PDO::FETCH_ASSOC);
|
||||
|
||||
@@ -126,7 +125,7 @@ foreach ($selected_rows as $rowIndex) {
|
||||
case 'Testo':
|
||||
case 'VARCHAR':
|
||||
default:
|
||||
$fieldValue = !empty($fieldValue) ? (string)$fieldValue : ($mapping['manual_default'] ?? '');
|
||||
$fieldValue = !empty($fieldValue) ? htmlspecialchars((string)$fieldValue) : ($mapping['manual_default'] ?? '');
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
@@ -135,15 +134,6 @@ foreach ($selected_rows as $rowIndex) {
|
||||
$fieldValue = date('Y-m-d');
|
||||
}
|
||||
}
|
||||
// Apply auto_value if field is still empty
|
||||
if (($fieldValue === null || $fieldValue === '') && !empty($mapping['auto_value']) && $mapping['auto_value'] !== 'none') {
|
||||
if ($mapping['auto_value'] === 'import_date') {
|
||||
$fieldValue = date('Y-m-d');
|
||||
} elseif ($mapping['auto_value'] === 'import_time') {
|
||||
$fieldValue = date('H:i');
|
||||
}
|
||||
}
|
||||
|
||||
if ($mapping['is_required'] && (is_null($fieldValue) || $fieldValue === '')) {
|
||||
error_log("Required field missing for mapping ID: " . $mapping['id'] . ", field: " . $mapping['field_label']);
|
||||
}
|
||||
@@ -156,6 +146,28 @@ foreach ($selected_rows as $rowIndex) {
|
||||
|
||||
$_SESSION['inserted_ids'] = $insertedIds;
|
||||
|
||||
header("Location: imported.php?id=" . urlencode($template_id) . "&importref=" . urlencode($importReferenceCode));
|
||||
$params = [
|
||||
'template_id' => $template_id,
|
||||
'filename' => $newFilename,
|
||||
];
|
||||
|
||||
?>
|
||||
<form id="redirectForm" action="import_edit2.php" method="post">
|
||||
<input type="hidden" name="template_id" value="<?= htmlspecialchars($template_id) ?>">
|
||||
<input type="hidden" name="filename" value="<?= htmlspecialchars($newFilename) ?>">
|
||||
<?php foreach ($selected_rows as $row): ?>
|
||||
<input type="hidden" name="selected_rows[]" value="<?= htmlspecialchars($row) ?>">
|
||||
<?php endforeach; ?>
|
||||
<?php foreach ($insertedIds as $id): ?>
|
||||
<input type="hidden" name="inserted_ids[]" value="<?= htmlspecialchars($id) ?>">
|
||||
<?php endforeach; ?>
|
||||
<input type="hidden" name="columns" value='<?= json_encode($columns) ?>'>
|
||||
<input type="hidden" name="rows" value='<?= json_encode($rows) ?>'>
|
||||
<input type="hidden" name="excelrows" value='<?= json_encode($excelrows) ?>'>
|
||||
</form>
|
||||
<script>
|
||||
document.getElementById('redirectForm').submit();
|
||||
</script>
|
||||
<?php
|
||||
exit;
|
||||
?>
|
||||
+44
-127
@@ -121,16 +121,6 @@ error_log("Loaded template: " . print_r($template, true));
|
||||
transform: rotate(360deg);
|
||||
}
|
||||
}
|
||||
|
||||
.column-filters th {
|
||||
background: #ffffff;
|
||||
cursor: default;
|
||||
}
|
||||
|
||||
.column-filters input {
|
||||
width: 100%;
|
||||
min-width: 80px;
|
||||
}
|
||||
</style>
|
||||
<title><?= htmlspecialchars($template['name']) ?> - <?= htmlspecialchars($titlewebsite, ENT_QUOTES, 'UTF-8'); ?></title>
|
||||
</head>
|
||||
@@ -143,8 +133,9 @@ error_log("Loaded template: " . print_r($template, true));
|
||||
<div class="page-content">
|
||||
<?php include('top_stat_widget.php'); ?>
|
||||
<div class="mb-3 text">
|
||||
<a href="imported.php?id=<?= $id ?>" class="btn btn-warning me-2">Imported (i)</a>
|
||||
<a href="tolims.php?id=<?= $id ?>" class="btn btn-success">To LIMS (l)</a>
|
||||
<a href="historical_trf.php?id=<?= $id ?>&status=i" class="btn btn-warning me-2">Imported (i)</a>
|
||||
<a href="historical_trf.php?id=<?= $id ?>&status=P" class="btn btn-primary me-2">In Progress (P)</a>
|
||||
<a href="historical_trf.php?id=<?= $id ?>&status=l" class="btn btn-success">To LIMS (l)</a>
|
||||
</div>
|
||||
<div class="card radius-10">
|
||||
<div class="card-header">
|
||||
@@ -312,96 +303,57 @@ error_log("Loaded template: " . print_r($template, true));
|
||||
function showTable(data) {
|
||||
console.log('Mostro tabella con dati:', data);
|
||||
let html = `
|
||||
<form id="selectRowsForm" action="import_insert.php" method="POST">
|
||||
<input type="hidden" name="template_id" value="${data.template_id}">
|
||||
<input type="hidden" name="columns" value="${encodeURIComponent(JSON.stringify(data.columns))}">
|
||||
<input type="hidden" name="rows" value="${encodeURIComponent(JSON.stringify(data.rows))}">
|
||||
<input type="hidden" name="excelrows" value="${encodeURIComponent(JSON.stringify(data.excel_data.map(r => r.excelrow)))}">
|
||||
<input type="hidden" name="filename" value="${data.filename}">
|
||||
|
||||
<!-- TOP BUTTON -->
|
||||
<div class="d-flex justify-content-end mb-3">
|
||||
<button type="submit" class="btn btn-primary" id="proceedButtonTop" disabled>Prosegui</button>
|
||||
</div>
|
||||
|
||||
<div class="table-container">
|
||||
<table class="table table-striped table-bordered">
|
||||
<thead>
|
||||
<tr>
|
||||
<th><input type="checkbox" id="selectAll"> Seleziona</th>
|
||||
${data.columns.map(col => {
|
||||
const label = !col ? 'Colonna senza nome' : (col.match(/^__empty_\d+__$/) ? 'Colonna senza nome' : col);
|
||||
return `<th>${label}<div class="resize-handle"></div></th>`;
|
||||
}).join('')}
|
||||
</tr>
|
||||
<tr class="column-filters">
|
||||
<th></th>
|
||||
${data.columns.map((col, i) => `
|
||||
<th>
|
||||
<input type="text"
|
||||
class="form-control form-control-sm column-filter"
|
||||
data-col-index="${i}"
|
||||
placeholder="Filter...">
|
||||
</th>
|
||||
`).join('')}
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
${data.excel_data.map((row, index) => `
|
||||
<tr>
|
||||
<td><input type="checkbox" class="row-checkbox" name="selected_rows[]" value="${index}" data-excelrow="${row.excelrow}"></td>
|
||||
${row.data.map(cell => `<td>${cell}</td>`).join('')}
|
||||
</tr>
|
||||
`).join('')}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
|
||||
<!-- BOTTOM BUTTON -->
|
||||
<button type="submit" class="btn btn-primary mt-3" id="proceedButtonBottom" disabled>Prosegui</button>
|
||||
</form>
|
||||
`;
|
||||
<form id="selectRowsForm" action="import_insert.php" method="POST">
|
||||
<input type="hidden" name="template_id" value="${data.template_id}">
|
||||
<input type="hidden" name="columns" value='${JSON.stringify(data.columns)}'>
|
||||
<input type="hidden" name="rows" value='${JSON.stringify(data.rows)}'>
|
||||
<input type="hidden" name="excelrows" value='${JSON.stringify(data.excel_data.map(row => row.excelrow))}'>
|
||||
<input type="hidden" name="filename" value="${data.filename}">
|
||||
<div class="search-container">
|
||||
<input type="text" id="searchInput" class="form-control" placeholder="Cerca nelle righe...">
|
||||
</div>
|
||||
<div class="table-container">
|
||||
<table class="table table-striped table-bordered">
|
||||
<thead>
|
||||
<tr>
|
||||
<th><input type="checkbox" id="selectAll"> Seleziona</th>
|
||||
${data.columns.map(col => `<th>${col || 'Colonna senza nome'}<div class="resize-handle"></div></th>`).join('')}
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
${data.excel_data.map((row, index) => `
|
||||
<tr>
|
||||
<td><input type="checkbox" class="row-checkbox" name="selected_rows[]" value="${index}" data-excelrow="${row.excelrow}"></td>
|
||||
${row.data.map(cell => `<td>${cell}</td>`).join('')}
|
||||
</tr>
|
||||
`).join('')}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
<button type="submit" class="btn btn-primary mt-3" id="proceedButton" disabled>Prosegui</button>
|
||||
</form>
|
||||
`;
|
||||
tableContainer.innerHTML = html;
|
||||
|
||||
const proceedButtonTop = document.getElementById('proceedButtonTop');
|
||||
const proceedButtonBottom = document.getElementById('proceedButtonBottom');
|
||||
const proceedButton = document.getElementById('proceedButton');
|
||||
const selectAllCheckbox = document.getElementById('selectAll');
|
||||
const checkboxes = document.querySelectorAll('.row-checkbox');
|
||||
|
||||
function updateProceedButton() {
|
||||
const enabled = Array.from(checkboxes).some(cb => cb.checked);
|
||||
|
||||
if (proceedButtonTop) proceedButtonTop.disabled = !enabled;
|
||||
if (proceedButtonBottom) proceedButtonBottom.disabled = !enabled;
|
||||
proceedButton.disabled = !Array.from(checkboxes).some(cb => cb.checked);
|
||||
}
|
||||
|
||||
selectAllCheckbox.addEventListener('change', function() {
|
||||
const visibleRows = Array.from(document.querySelectorAll('.table tbody tr'))
|
||||
.filter(row => row.style.display !== 'none');
|
||||
|
||||
visibleRows.forEach(row => {
|
||||
const checkbox = row.querySelector('.row-checkbox');
|
||||
if (checkbox) {
|
||||
checkbox.checked = this.checked;
|
||||
}
|
||||
checkboxes.forEach(checkbox => {
|
||||
checkbox.checked = this.checked;
|
||||
});
|
||||
|
||||
updateProceedButton();
|
||||
});
|
||||
|
||||
checkboxes.forEach(checkbox => {
|
||||
checkbox.addEventListener('change', function() {
|
||||
console.log('Checkbox changed, checked:', this.checked, 'excelrow:', this.dataset.excelrow);
|
||||
|
||||
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);
|
||||
|
||||
selectAllCheckbox.checked = Array.from(checkboxes).every(cb => cb.checked);
|
||||
updateProceedButton();
|
||||
});
|
||||
});
|
||||
@@ -441,49 +393,14 @@ error_log("Loaded template: " . print_r($template, true));
|
||||
}
|
||||
});
|
||||
|
||||
const searchInput = document.getElementById('searchInput');
|
||||
const rows = document.querySelectorAll('.table tbody tr');
|
||||
const filterInputs = document.querySelectorAll('.column-filter');
|
||||
|
||||
// Stato filtri: key = colIndex, value = testo
|
||||
const activeFilters = {};
|
||||
|
||||
function applyColumnFilters() {
|
||||
searchInput.addEventListener('input', function() {
|
||||
const searchTerm = this.value.toLowerCase();
|
||||
rows.forEach(row => {
|
||||
let visible = true;
|
||||
|
||||
for (const [colIndexStr, filterValue] of Object.entries(activeFilters)) {
|
||||
const colIndex = parseInt(colIndexStr, 10);
|
||||
const cell = row.cells[colIndex + 1];
|
||||
|
||||
const cellText = (cell?.textContent || '').toLowerCase();
|
||||
const searchText = (filterValue || '').toLowerCase().trim();
|
||||
|
||||
if (searchText && !cellText.includes(searchText)) {
|
||||
visible = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
row.style.display = visible ? '' : 'none';
|
||||
});
|
||||
|
||||
const visibleCheckboxes = Array.from(document.querySelectorAll('.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 => {
|
||||
input.addEventListener('input', function() {
|
||||
const colIndex = this.dataset.colIndex; // string
|
||||
activeFilters[colIndex] = this.value;
|
||||
applyColumnFilters();
|
||||
const text = Array.from(row.cells).slice(1).map(cell => cell.textContent.toLowerCase()).join(' ');
|
||||
row.style.display = text.includes(searchTerm) ? '' : 'none';
|
||||
});
|
||||
});
|
||||
|
||||
@@ -493,4 +410,4 @@ error_log("Loaded template: " . print_r($template, true));
|
||||
</script>
|
||||
</body>
|
||||
|
||||
</html>
|
||||
</html>
|
||||
File diff suppressed because it is too large
Load Diff
@@ -26,8 +26,6 @@ $nameuser = $user->present()->first_name;
|
||||
$surnameuser = $user->present()->last_name;
|
||||
$emailuser = $user->present()->email;
|
||||
$avatar = $user->present()->avatar;
|
||||
$lims_user_id = $user->lims_user_id ?? '';
|
||||
$lims_global_user_id = $user->lims_global_user_id ?? '';
|
||||
|
||||
$kindofrole = $user->present()->role_id;
|
||||
|
||||
@@ -37,6 +35,8 @@ $kindofrole = $user->present()->role_id;
|
||||
//$iduserlogin="1";
|
||||
//$nameuser="Claudio";
|
||||
//$emailuser="info@claudiosironi.com";
|
||||
?>
|
||||
<?php
|
||||
if (session_status() == PHP_SESSION_NONE) {
|
||||
session_start();
|
||||
}
|
||||
@@ -49,8 +49,13 @@ $_SESSION["emailuser"] = $emailuser;
|
||||
$_SESSION["photouser"] = $avatar;
|
||||
$photouser = $_SESSION["photouser"];
|
||||
$photousername = basename($avatar);
|
||||
?>
|
||||
|
||||
|
||||
<?php //include files
|
||||
|
||||
//include files
|
||||
require_once(__DIR__ . '/../../languages/en/general.php');
|
||||
|
||||
//include("generalsettings.php");
|
||||
//include("generalsettings.php");
|
||||
|
||||
?>
|
||||
@@ -14,57 +14,11 @@ if (!$iddatadb) {
|
||||
}
|
||||
|
||||
try {
|
||||
// 1) prendo templateid da datadb
|
||||
$stmtTpl = $pdo->prepare("SELECT templateid FROM datadb WHERE iddatadb = :iddatadb LIMIT 1");
|
||||
$stmtTpl->execute([':iddatadb' => $iddatadb]);
|
||||
$templateid = $stmtTpl->fetchColumn();
|
||||
|
||||
// 2) prendo (max 1) field_id visibile in parts
|
||||
$extraFieldId = null;
|
||||
if ($templateid) {
|
||||
$stmtEF = $pdo->prepare("SELECT field_id FROM template_mapping WHERE template_id = :templateid AND is_visible_parts = 1 ORDER BY id ASC LIMIT 1");
|
||||
$stmtEF->execute([':templateid' => $templateid]);
|
||||
$extraFieldId = $stmtEF->fetchColumn();
|
||||
if ($extraFieldId !== false) $extraFieldId = (int)$extraFieldId;
|
||||
else $extraFieldId = null;
|
||||
}
|
||||
|
||||
// 3) carico parts + join su tabella figlia
|
||||
if ($extraFieldId) {
|
||||
$stmt = $pdo->prepare("
|
||||
SELECT
|
||||
p.id, p.iddatadb, p.part_number, p.part_description, p.idmatrice, p.note, p.dateexpiry,
|
||||
cf.value_id AS extra_value_id,
|
||||
cf.value_text AS extra_value_text
|
||||
FROM identification_parts p
|
||||
LEFT JOIN identification_parts_customfields cf
|
||||
ON cf.part_id = p.id AND cf.field_id = :extraFieldId
|
||||
WHERE p.iddatadb = :iddatadb
|
||||
ORDER BY p.part_number ASC
|
||||
");
|
||||
$stmt->execute([
|
||||
':iddatadb' => $iddatadb,
|
||||
':extraFieldId' => $extraFieldId
|
||||
]);
|
||||
} else {
|
||||
$stmt = $pdo->prepare("
|
||||
SELECT id, iddatadb, part_number, part_description, idmatrice, note, dateexpiry,
|
||||
NULL AS extra_value_id, NULL AS extra_value_text
|
||||
FROM identification_parts
|
||||
WHERE iddatadb = :iddatadb
|
||||
ORDER BY part_number ASC
|
||||
");
|
||||
$stmt->execute([':iddatadb' => $iddatadb]);
|
||||
}
|
||||
|
||||
$stmt = $pdo->prepare("SELECT id, iddatadb, part_number, part_description, idmatrice, note, dateexpiry FROM identification_parts WHERE iddatadb = :iddatadb ORDER BY part_number ASC");
|
||||
$stmt->execute([':iddatadb' => $iddatadb]);
|
||||
$parts = $stmt->fetchAll(PDO::FETCH_ASSOC);
|
||||
|
||||
echo json_encode([
|
||||
'success' => true,
|
||||
'parts' => $parts,
|
||||
'extra_field_id' => $extraFieldId,
|
||||
'debug_sql' => ($extraFieldId ? 'WITH_CF_JOIN' : 'NO_CF')
|
||||
]);
|
||||
echo json_encode(['success' => true, 'parts' => $parts]);
|
||||
} catch (PDOException $e) {
|
||||
echo json_encode(['success' => false, 'message' => 'Errore nel caricamento: ' . $e->getMessage()]);
|
||||
}
|
||||
|
||||
@@ -1,32 +0,0 @@
|
||||
<?php
|
||||
header('Content-Type: application/json');
|
||||
|
||||
include('include/headscript.php');
|
||||
|
||||
$dbHandler = DBHandlerSelect::getInstance();
|
||||
$pdo = $dbHandler->getConnection();
|
||||
|
||||
// Recupera l'ID dell'utente loggato
|
||||
$user_id = $iduserlogin ?? 1;
|
||||
|
||||
if (!$user_id) {
|
||||
echo json_encode(['success' => false, 'message' => "ID dell'utente autenticato mancante"]);
|
||||
exit;
|
||||
}
|
||||
|
||||
try {
|
||||
$stmt = $pdo->prepare(
|
||||
"SELECT DISTINCT q.*
|
||||
FROM quotations q
|
||||
INNER JOIN identification_parts ip
|
||||
ON ip.idquotations = q.id
|
||||
AND ip.iddatadb IS NULL
|
||||
WHERE q.iduser = :iduser"
|
||||
);
|
||||
$stmt->execute([':iduser' => $user_id]);
|
||||
$quotations = $stmt->fetchAll(PDO::FETCH_ASSOC);
|
||||
|
||||
echo json_encode(['success' => true, 'quotations' => $quotations]);
|
||||
} catch (PDOException $e) {
|
||||
echo json_encode(['success' => false, 'message' => 'Errore nel caricamento delle quotations: ' . $e->getMessage()]);
|
||||
}
|
||||
@@ -1,60 +0,0 @@
|
||||
CAMPIONE #0
|
||||
curl --location --request POST 'https://93.43.5.102/limsapi/api/odata/Campione' \
|
||||
--header 'Content-Type: application/json' \
|
||||
--header 'Authorization: Bearer ••••••' \
|
||||
--data '{
|
||||
"Commessa": 563528,
|
||||
"Matrice": 3879,
|
||||
"SottoMatrice": null,
|
||||
"SchemaCustomField": 82,
|
||||
"NoteWeb": "Parte AAA",
|
||||
"ConsegnaRichiesta": "2026-01-30"
|
||||
}'
|
||||
|
||||
RESPONSE:
|
||||
{
|
||||
"@odata.context": "https:\/\/bvcpsitaly-elims.com\/limsapi\/api\/odata\/$metadata#Campione\/$entity",
|
||||
"IdCampione": 734582,
|
||||
"CodiceCampione": "10978",
|
||||
"CodiceCampioneWeb": "10978",
|
||||
"StatoCampione": "Nuovo",
|
||||
"DataCreazioneWeb": "2026-03-11T14:10:02.1595894+01:00",
|
||||
"RiferimentoWeb": "",
|
||||
"ConsegnaRichiesta": "2026-01-30T00:00:00+01:00",
|
||||
"DataAccettazioneLims": null,
|
||||
"Riferimento": null,
|
||||
"NoteWeb": "Parte AAA",
|
||||
"GruppiRicercati": null
|
||||
}
|
||||
|
||||
---
|
||||
CAMPIONE #1
|
||||
curl --location --request POST 'https://93.43.5.102/limsapi/api/odata/Campione' \
|
||||
--header 'Content-Type: application/json' \
|
||||
--header 'Authorization: Bearer ••••••' \
|
||||
--data '{
|
||||
"Commessa": 563528,
|
||||
"Matrice": 3879,
|
||||
"SottoMatrice": null,
|
||||
"SchemaCustomField": 82,
|
||||
"NoteWeb": "PARE BBB",
|
||||
"ConsegnaRichiesta": "2026-01-30"
|
||||
}'
|
||||
|
||||
RESPONSE:
|
||||
{
|
||||
"@odata.context": "https:\/\/bvcpsitaly-elims.com\/limsapi\/api\/odata\/$metadata#Campione\/$entity",
|
||||
"IdCampione": 734583,
|
||||
"CodiceCampione": "10979",
|
||||
"CodiceCampioneWeb": "10979",
|
||||
"StatoCampione": "Nuovo",
|
||||
"DataCreazioneWeb": "2026-03-11T14:10:03.9972635+01:00",
|
||||
"RiferimentoWeb": "",
|
||||
"ConsegnaRichiesta": "2026-01-30T00:00:00+01:00",
|
||||
"DataAccettazioneLims": null,
|
||||
"Riferimento": null,
|
||||
"NoteWeb": "PARE BBB",
|
||||
"GruppiRicercati": null
|
||||
}
|
||||
|
||||
---
|
||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -1,20 +0,0 @@
|
||||
curl --location --request POST 'https://93.43.5.102/limsapi/api/odata/CommessaWeb(563528)/ImportaCommessa' \
|
||||
--header 'Content-Type: application/json' \
|
||||
--header 'Authorization: Bearer ••••••' \
|
||||
--data '{
|
||||
"IdUtente": 285
|
||||
}'
|
||||
|
||||
RESPONSE:
|
||||
{
|
||||
"@odata.context": "https:\/\/bvcpsitaly-elims.com\/limsapi\/api\/odata\/$metadata#CommessaWeb\/$entity",
|
||||
"IdCommessa": 563528,
|
||||
"CodiceCommessa": "26C0029",
|
||||
"RiferimentoCertificato": null,
|
||||
"StatoCommessaWeb": "Elaborata",
|
||||
"DataCreazioneWeb": "2026-03-11T14:10:00.897+01:00",
|
||||
"CodiceCommessaWeb": "26C0029",
|
||||
"DataInviatoWeb": "2026-03-11T14:10:11.76+01:00",
|
||||
"Richiedente": "Test Web Import",
|
||||
"Descrizione": "TEST CommessaWeb"
|
||||
}
|
||||
@@ -1,44 +0,0 @@
|
||||
Photos for CommessaWeb 563528 (iddatadb=1259):
|
||||
Total photos found: 2, campioni: 2
|
||||
|
||||
=== Campione 734582 (main) ===
|
||||
curl --location --request POST 'https://93.43.5.102/limsapi/api/odata/Campione(734582)/UploadCampioneFile' \
|
||||
--header 'Authorization: Bearer ••••••' \
|
||||
--form 'file=@C:\xampp\htdocs\trf_certest\public\photostrf\1259-20260311130930-104a2ca5-8a4c-4df8-b2ca-a28f518b7b25.jpg'
|
||||
|
||||
RESPONSE:
|
||||
{
|
||||
"@odata.context": "https:\/\/bvcpsitaly-elims.com\/limsapi\/api\/odata\/$metadata#CampioneFiles\/$entity",
|
||||
"IdCampioneFile": 1779561,
|
||||
"FileName": "1259-20260311130930-104a2ca5-8a4c-4df8-b2ca-a28f518b7b25.jpg",
|
||||
"Web": false,
|
||||
"AllegaAlRapporto": false,
|
||||
"StampaNelRapporto": false,
|
||||
"ReportGermania": false,
|
||||
"PrimaPagina": false,
|
||||
"Duplica": false,
|
||||
"LastUpdate": "2026-03-11T14:10:06.7051549+01:00",
|
||||
"Titolo": null
|
||||
}
|
||||
|
||||
---
|
||||
curl --location --request POST 'https://93.43.5.102/limsapi/api/odata/Campione(734582)/UploadCampioneFile' \
|
||||
--header 'Authorization: Bearer ••••••' \
|
||||
--form 'file=@C:\xampp\htdocs\trf_certest\public\photostrf\1259-20260311130930-1e672dd9-5420-4432-b422-02d8d271c178.jpg'
|
||||
|
||||
RESPONSE:
|
||||
{
|
||||
"@odata.context": "https:\/\/bvcpsitaly-elims.com\/limsapi\/api\/odata\/$metadata#CampioneFiles\/$entity",
|
||||
"IdCampioneFile": 1779562,
|
||||
"FileName": "1259-20260311130930-1e672dd9-5420-4432-b422-02d8d271c178.jpg",
|
||||
"Web": false,
|
||||
"AllegaAlRapporto": false,
|
||||
"StampaNelRapporto": false,
|
||||
"ReportGermania": false,
|
||||
"PrimaPagina": false,
|
||||
"Duplica": false,
|
||||
"LastUpdate": "2026-03-11T14:10:08.5442313+01:00",
|
||||
"Titolo": null
|
||||
}
|
||||
|
||||
---
|
||||
@@ -1,18 +0,0 @@
|
||||
curl --location --request POST 'https://93.43.5.102/limsapi/api/odata/CommessaWeb(563528)/InviaCommessa' \
|
||||
--header 'Content-Type: application/json' \
|
||||
--header 'Authorization: Bearer ••••••' \
|
||||
--data '{}'
|
||||
|
||||
RESPONSE:
|
||||
{
|
||||
"@odata.context": "https:\/\/bvcpsitaly-elims.com\/limsapi\/api\/odata\/$metadata#CommessaWeb\/$entity",
|
||||
"IdCommessa": 563528,
|
||||
"CodiceCommessa": "26C0029",
|
||||
"RiferimentoCertificato": null,
|
||||
"StatoCommessaWeb": "Nuova",
|
||||
"DataCreazioneWeb": "2026-03-11T14:10:00.897+01:00",
|
||||
"CodiceCommessaWeb": "26C0029",
|
||||
"DataInviatoWeb": "2026-03-11T14:10:11.7602299+01:00",
|
||||
"Richiedente": "Test Web Import",
|
||||
"Descrizione": "TEST CommessaWeb"
|
||||
}
|
||||
@@ -1,226 +0,0 @@
|
||||
curl --location --request PATCH 'https://93.43.5.102/limsapi/api/odata/CommessaWeb(563528)' \
|
||||
--header 'Content-Type: application/json' \
|
||||
--header 'Authorization: Bearer ••••••' \
|
||||
--data '{
|
||||
"CommesseCustomFields": [
|
||||
{
|
||||
"IdCommesseCustomFields": 23254249,
|
||||
"Valore": ""
|
||||
},
|
||||
{
|
||||
"IdCommesseCustomFields": 23254250,
|
||||
"Valore": ""
|
||||
},
|
||||
{
|
||||
"IdCommesseCustomFields": 23254251,
|
||||
"Valore": ""
|
||||
},
|
||||
{
|
||||
"IdCommesseCustomFields": 23254252,
|
||||
"Valore": ""
|
||||
},
|
||||
{
|
||||
"IdCommesseCustomFields": 23254253,
|
||||
"Valore": ""
|
||||
},
|
||||
{
|
||||
"IdCommesseCustomFields": 23254254,
|
||||
"Valore": ""
|
||||
},
|
||||
{
|
||||
"IdCommesseCustomFields": 23254260,
|
||||
"Valore": ""
|
||||
},
|
||||
{
|
||||
"IdCommesseCustomFields": 23254261,
|
||||
"Valore": ""
|
||||
},
|
||||
{
|
||||
"IdCommesseCustomFields": 23254262,
|
||||
"Valore": ""
|
||||
},
|
||||
{
|
||||
"IdCommesseCustomFields": 23254264,
|
||||
"Valore": ""
|
||||
},
|
||||
{
|
||||
"IdCommesseCustomFields": 23254265,
|
||||
"Valore": ""
|
||||
},
|
||||
{
|
||||
"IdCommesseCustomFields": 23254266,
|
||||
"Valore": ""
|
||||
},
|
||||
{
|
||||
"IdCommesseCustomFields": 23254267,
|
||||
"Valore": ""
|
||||
},
|
||||
{
|
||||
"IdCommesseCustomFields": 23254268,
|
||||
"Valore": ""
|
||||
},
|
||||
{
|
||||
"IdCommesseCustomFields": 23254228,
|
||||
"Valore": "13526"
|
||||
},
|
||||
{
|
||||
"IdCommesseCustomFields": 23254229,
|
||||
"Valore": "20262"
|
||||
},
|
||||
{
|
||||
"IdCommesseCustomFields": 23254230,
|
||||
"Valore": "ART. PEACH"
|
||||
},
|
||||
{
|
||||
"IdCommesseCustomFields": 23254269,
|
||||
"Valore": ""
|
||||
},
|
||||
{
|
||||
"IdCommesseCustomFields": 23254270,
|
||||
"Valore": ""
|
||||
},
|
||||
{
|
||||
"IdCommesseCustomFields": 23254271,
|
||||
"Valore": ""
|
||||
},
|
||||
{
|
||||
"IdCommesseCustomFields": 23254272,
|
||||
"Valore": ""
|
||||
},
|
||||
{
|
||||
"IdCommesseCustomFields": 23254273,
|
||||
"Valore": ""
|
||||
},
|
||||
{
|
||||
"IdCommesseCustomFields": 23254274,
|
||||
"Valore": ""
|
||||
},
|
||||
{
|
||||
"IdCommesseCustomFields": 23254275,
|
||||
"Valore": ""
|
||||
},
|
||||
{
|
||||
"IdCommesseCustomFields": 23254276,
|
||||
"Valore": ""
|
||||
},
|
||||
{
|
||||
"IdCommesseCustomFields": 23254277,
|
||||
"Valore": ""
|
||||
},
|
||||
{
|
||||
"IdCommesseCustomFields": 23254278,
|
||||
"Valore": ""
|
||||
},
|
||||
{
|
||||
"IdCommesseCustomFields": 23254279,
|
||||
"Valore": ""
|
||||
},
|
||||
{
|
||||
"IdCommesseCustomFields": 23254280,
|
||||
"Valore": ""
|
||||
},
|
||||
{
|
||||
"IdCommesseCustomFields": 23254281,
|
||||
"Valore": ""
|
||||
},
|
||||
{
|
||||
"IdCommesseCustomFields": 23254231,
|
||||
"Valore": "L209A4M00130M7280"
|
||||
},
|
||||
{
|
||||
"IdCommesseCustomFields": 23254232,
|
||||
"Valore": "BLACK"
|
||||
},
|
||||
{
|
||||
"IdCommesseCustomFields": 23254233,
|
||||
"Valore": "PE007J"
|
||||
},
|
||||
{
|
||||
"IdCommesseCustomFields": 23254234,
|
||||
"Valore": "262"
|
||||
},
|
||||
{
|
||||
"IdCommesseCustomFields": 23254235,
|
||||
"Valore": ""
|
||||
},
|
||||
{
|
||||
"IdCommesseCustomFields": 23254236,
|
||||
"Valore": ""
|
||||
},
|
||||
{
|
||||
"IdCommesseCustomFields": 23254237,
|
||||
"Valore": ""
|
||||
},
|
||||
{
|
||||
"IdCommesseCustomFields": 23254238,
|
||||
"Valore": ""
|
||||
},
|
||||
{
|
||||
"IdCommesseCustomFields": 23254239,
|
||||
"Valore": ""
|
||||
},
|
||||
{
|
||||
"IdCommesseCustomFields": 23254240,
|
||||
"Valore": "Oggi"
|
||||
},
|
||||
{
|
||||
"IdCommesseCustomFields": 23254241,
|
||||
"Valore": ""
|
||||
},
|
||||
{
|
||||
"IdCommesseCustomFields": 23254242,
|
||||
"Valore": "MONCLER"
|
||||
},
|
||||
{
|
||||
"IdCommesseCustomFields": 23254243,
|
||||
"Valore": "236"
|
||||
},
|
||||
{
|
||||
"IdCommesseCustomFields": 23254244,
|
||||
"Valore": ""
|
||||
},
|
||||
{
|
||||
"IdCommesseCustomFields": 23254245,
|
||||
"Valore": "BBB"
|
||||
},
|
||||
{
|
||||
"IdCommesseCustomFields": 23254246,
|
||||
"Valore": "solocla"
|
||||
},
|
||||
{
|
||||
"IdCommesseCustomFields": 23254247,
|
||||
"Valore": ""
|
||||
},
|
||||
{
|
||||
"IdCommesseCustomFields": 23254248,
|
||||
"Valore": ""
|
||||
},
|
||||
{
|
||||
"IdCommesseCustomFields": 23254255,
|
||||
"Valore": ""
|
||||
},
|
||||
{
|
||||
"IdCommesseCustomFields": 23254256,
|
||||
"Valore": "MONCLER"
|
||||
},
|
||||
{
|
||||
"IdCommesseCustomFields": 23254257,
|
||||
"Valore": ""
|
||||
},
|
||||
{
|
||||
"IdCommesseCustomFields": 23254258,
|
||||
"Valore": ""
|
||||
},
|
||||
{
|
||||
"IdCommesseCustomFields": 23254259,
|
||||
"Valore": ""
|
||||
},
|
||||
{
|
||||
"IdCommesseCustomFields": 23254263,
|
||||
"Valore": ""
|
||||
}
|
||||
]
|
||||
}'
|
||||
|
||||
RESPONSE:
|
||||
null
|
||||
@@ -1,60 +0,0 @@
|
||||
CAMPIONE #0
|
||||
curl --location --request POST 'https://93.43.5.102/limsapi/api/odata/Campione' \
|
||||
--header 'Content-Type: application/json' \
|
||||
--header 'Authorization: Bearer ••••••' \
|
||||
--data '{
|
||||
"Commessa": 564663,
|
||||
"Matrice": 3028,
|
||||
"SottoMatrice": null,
|
||||
"SchemaCustomField": 82,
|
||||
"NoteWeb": "Parte 1",
|
||||
"ConsegnaRichiesta": "2026-03-31"
|
||||
}'
|
||||
|
||||
RESPONSE:
|
||||
{
|
||||
"@odata.context": "https:\/\/bvcpsitaly-elims.com\/limsapi\/api\/odata\/$metadata#Campione\/$entity",
|
||||
"IdCampione": 736244,
|
||||
"CodiceCampione": "11004",
|
||||
"CodiceCampioneWeb": "11004",
|
||||
"StatoCampione": "Nuovo",
|
||||
"DataCreazioneWeb": "2026-03-17T14:52:30.3143366+01:00",
|
||||
"RiferimentoWeb": "",
|
||||
"ConsegnaRichiesta": "2026-03-31T00:00:00+02:00",
|
||||
"DataAccettazioneLims": null,
|
||||
"Riferimento": null,
|
||||
"NoteWeb": "Parte 1",
|
||||
"GruppiRicercati": null
|
||||
}
|
||||
|
||||
---
|
||||
CAMPIONE #1
|
||||
curl --location --request POST 'https://93.43.5.102/limsapi/api/odata/Campione' \
|
||||
--header 'Content-Type: application/json' \
|
||||
--header 'Authorization: Bearer ••••••' \
|
||||
--data '{
|
||||
"Commessa": 564663,
|
||||
"Matrice": 3028,
|
||||
"SottoMatrice": null,
|
||||
"SchemaCustomField": 82,
|
||||
"NoteWeb": "Parte 2",
|
||||
"ConsegnaRichiesta": "2026-03-31"
|
||||
}'
|
||||
|
||||
RESPONSE:
|
||||
{
|
||||
"@odata.context": "https:\/\/bvcpsitaly-elims.com\/limsapi\/api\/odata\/$metadata#Campione\/$entity",
|
||||
"IdCampione": 736245,
|
||||
"CodiceCampione": "11005",
|
||||
"CodiceCampioneWeb": "11005",
|
||||
"StatoCampione": "Nuovo",
|
||||
"DataCreazioneWeb": "2026-03-17T14:52:31.9631757+01:00",
|
||||
"RiferimentoWeb": "",
|
||||
"ConsegnaRichiesta": "2026-03-31T00:00:00+02:00",
|
||||
"DataAccettazioneLims": null,
|
||||
"Riferimento": null,
|
||||
"NoteWeb": "Parte 2",
|
||||
"GruppiRicercati": null
|
||||
}
|
||||
|
||||
---
|
||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -1,20 +0,0 @@
|
||||
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"
|
||||
}
|
||||
@@ -1,48 +0,0 @@
|
||||
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
|
||||
}
|
||||
|
||||
---
|
||||
@@ -1,18 +0,0 @@
|
||||
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"
|
||||
}
|
||||
@@ -1,226 +0,0 @@
|
||||
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
|
||||
@@ -1,120 +0,0 @@
|
||||
CAMPIONE #0
|
||||
curl --location --request POST 'https://93.43.5.102/limsapi/api/odata/Campione' \
|
||||
--header 'Content-Type: application/json' \
|
||||
--header 'Authorization: Bearer ••••••' \
|
||||
--data '{
|
||||
"Commessa": 564817,
|
||||
"Matrice": 3028,
|
||||
"SottoMatrice": null,
|
||||
"SchemaCustomField": 82,
|
||||
"NoteWeb": "AAA",
|
||||
"ConsegnaRichiesta": "2026-01-29"
|
||||
}'
|
||||
|
||||
RESPONSE:
|
||||
{
|
||||
"@odata.context": "https:\/\/bvcpsitaly-elims.com\/limsapi\/api\/odata\/$metadata#Campione\/$entity",
|
||||
"IdCampione": 736487,
|
||||
"CodiceCampione": "11016",
|
||||
"CodiceCampioneWeb": "11016",
|
||||
"StatoCampione": "Nuovo",
|
||||
"DataCreazioneWeb": "2026-03-18T10:54:03.4537462+01:00",
|
||||
"RiferimentoWeb": "",
|
||||
"ConsegnaRichiesta": "2026-01-29T00:00:00+01:00",
|
||||
"DataAccettazioneLims": null,
|
||||
"Riferimento": null,
|
||||
"NoteWeb": "AAA",
|
||||
"GruppiRicercati": null
|
||||
}
|
||||
|
||||
---
|
||||
CAMPIONE #1
|
||||
curl --location --request POST 'https://93.43.5.102/limsapi/api/odata/Campione' \
|
||||
--header 'Content-Type: application/json' \
|
||||
--header 'Authorization: Bearer ••••••' \
|
||||
--data '{
|
||||
"Commessa": 564817,
|
||||
"Matrice": 3028,
|
||||
"SottoMatrice": null,
|
||||
"SchemaCustomField": 82,
|
||||
"NoteWeb": "CCC",
|
||||
"ConsegnaRichiesta": "2026-01-29"
|
||||
}'
|
||||
|
||||
RESPONSE:
|
||||
{
|
||||
"@odata.context": "https:\/\/bvcpsitaly-elims.com\/limsapi\/api\/odata\/$metadata#Campione\/$entity",
|
||||
"IdCampione": 736488,
|
||||
"CodiceCampione": "11017",
|
||||
"CodiceCampioneWeb": "11017",
|
||||
"StatoCampione": "Nuovo",
|
||||
"DataCreazioneWeb": "2026-03-18T10:54:04.8623753+01:00",
|
||||
"RiferimentoWeb": "",
|
||||
"ConsegnaRichiesta": "2026-01-29T00:00:00+01:00",
|
||||
"DataAccettazioneLims": null,
|
||||
"Riferimento": null,
|
||||
"NoteWeb": "CCC",
|
||||
"GruppiRicercati": null
|
||||
}
|
||||
|
||||
---
|
||||
CAMPIONE #2
|
||||
curl --location --request POST 'https://93.43.5.102/limsapi/api/odata/Campione' \
|
||||
--header 'Content-Type: application/json' \
|
||||
--header 'Authorization: Bearer ••••••' \
|
||||
--data '{
|
||||
"Commessa": 564817,
|
||||
"Matrice": 3028,
|
||||
"SottoMatrice": null,
|
||||
"SchemaCustomField": 82,
|
||||
"NoteWeb": "DDD",
|
||||
"ConsegnaRichiesta": "2026-01-29"
|
||||
}'
|
||||
|
||||
RESPONSE:
|
||||
{
|
||||
"@odata.context": "https:\/\/bvcpsitaly-elims.com\/limsapi\/api\/odata\/$metadata#Campione\/$entity",
|
||||
"IdCampione": 736489,
|
||||
"CodiceCampione": "11018",
|
||||
"CodiceCampioneWeb": "11018",
|
||||
"StatoCampione": "Nuovo",
|
||||
"DataCreazioneWeb": "2026-03-18T10:54:06.2840533+01:00",
|
||||
"RiferimentoWeb": "",
|
||||
"ConsegnaRichiesta": "2026-01-29T00:00:00+01:00",
|
||||
"DataAccettazioneLims": null,
|
||||
"Riferimento": null,
|
||||
"NoteWeb": "DDD",
|
||||
"GruppiRicercati": null
|
||||
}
|
||||
|
||||
---
|
||||
CAMPIONE #3
|
||||
curl --location --request POST 'https://93.43.5.102/limsapi/api/odata/Campione' \
|
||||
--header 'Content-Type: application/json' \
|
||||
--header 'Authorization: Bearer ••••••' \
|
||||
--data '{
|
||||
"Commessa": 564817,
|
||||
"Matrice": 3028,
|
||||
"SottoMatrice": null,
|
||||
"SchemaCustomField": 82,
|
||||
"NoteWeb": "EEE",
|
||||
"ConsegnaRichiesta": "2026-01-29"
|
||||
}'
|
||||
|
||||
RESPONSE:
|
||||
{
|
||||
"@odata.context": "https:\/\/bvcpsitaly-elims.com\/limsapi\/api\/odata\/$metadata#Campione\/$entity",
|
||||
"IdCampione": 736490,
|
||||
"CodiceCampione": "11019",
|
||||
"CodiceCampioneWeb": "11019",
|
||||
"StatoCampione": "Nuovo",
|
||||
"DataCreazioneWeb": "2026-03-18T10:54:07.8853452+01:00",
|
||||
"RiferimentoWeb": "",
|
||||
"ConsegnaRichiesta": "2026-01-29T00:00:00+01:00",
|
||||
"DataAccettazioneLims": null,
|
||||
"Riferimento": null,
|
||||
"NoteWeb": "EEE",
|
||||
"GruppiRicercati": null
|
||||
}
|
||||
|
||||
---
|
||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -1,20 +0,0 @@
|
||||
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"
|
||||
}
|
||||
@@ -1,48 +0,0 @@
|
||||
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
|
||||
}
|
||||
|
||||
---
|
||||
@@ -1,18 +0,0 @@
|
||||
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"
|
||||
}
|
||||
@@ -1,226 +0,0 @@
|
||||
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
|
||||
@@ -1,90 +0,0 @@
|
||||
CAMPIONE #0
|
||||
curl --location --request POST 'https://93.43.5.102/limsapi/api/odata/Campione' \
|
||||
--header 'Content-Type: application/json' \
|
||||
--header 'Authorization: Bearer ••••••' \
|
||||
--data '{
|
||||
"Commessa": 565085,
|
||||
"Matrice": 7714,
|
||||
"SottoMatrice": null,
|
||||
"SchemaCustomField": 82,
|
||||
"NoteWeb": "Parte AAA",
|
||||
"ConsegnaRichiesta": "2026-04-08"
|
||||
}'
|
||||
|
||||
RESPONSE:
|
||||
{
|
||||
"@odata.context": "https:\/\/bvcpsitaly-elims.com\/limsapi\/api\/odata\/$metadata#Campione\/$entity",
|
||||
"IdCampione": 736899,
|
||||
"CodiceCampione": "11038",
|
||||
"CodiceCampioneWeb": "11038",
|
||||
"StatoCampione": "Nuovo",
|
||||
"DataCreazioneWeb": "2026-03-19T10:03:19.8547279+01:00",
|
||||
"RiferimentoWeb": "",
|
||||
"ConsegnaRichiesta": "2026-04-08T00:00:00+02:00",
|
||||
"DataAccettazioneLims": null,
|
||||
"Riferimento": null,
|
||||
"NoteWeb": "Parte AAA",
|
||||
"GruppiRicercati": null
|
||||
}
|
||||
|
||||
---
|
||||
CAMPIONE #1
|
||||
curl --location --request POST 'https://93.43.5.102/limsapi/api/odata/Campione' \
|
||||
--header 'Content-Type: application/json' \
|
||||
--header 'Authorization: Bearer ••••••' \
|
||||
--data '{
|
||||
"Commessa": 565085,
|
||||
"Matrice": 7714,
|
||||
"SottoMatrice": null,
|
||||
"SchemaCustomField": 82,
|
||||
"NoteWeb": "Parte BBB",
|
||||
"ConsegnaRichiesta": "2026-04-08"
|
||||
}'
|
||||
|
||||
RESPONSE:
|
||||
{
|
||||
"@odata.context": "https:\/\/bvcpsitaly-elims.com\/limsapi\/api\/odata\/$metadata#Campione\/$entity",
|
||||
"IdCampione": 736900,
|
||||
"CodiceCampione": "11039",
|
||||
"CodiceCampioneWeb": "11039",
|
||||
"StatoCampione": "Nuovo",
|
||||
"DataCreazioneWeb": "2026-03-19T10:03:22.0431135+01:00",
|
||||
"RiferimentoWeb": "",
|
||||
"ConsegnaRichiesta": "2026-04-08T00:00:00+02:00",
|
||||
"DataAccettazioneLims": null,
|
||||
"Riferimento": null,
|
||||
"NoteWeb": "Parte BBB",
|
||||
"GruppiRicercati": null
|
||||
}
|
||||
|
||||
---
|
||||
CAMPIONE #2
|
||||
curl --location --request POST 'https://93.43.5.102/limsapi/api/odata/Campione' \
|
||||
--header 'Content-Type: application/json' \
|
||||
--header 'Authorization: Bearer ••••••' \
|
||||
--data '{
|
||||
"Commessa": 565085,
|
||||
"Matrice": 7714,
|
||||
"SottoMatrice": null,
|
||||
"SchemaCustomField": 82,
|
||||
"NoteWeb": "Parte CCC",
|
||||
"ConsegnaRichiesta": "2026-04-08"
|
||||
}'
|
||||
|
||||
RESPONSE:
|
||||
{
|
||||
"@odata.context": "https:\/\/bvcpsitaly-elims.com\/limsapi\/api\/odata\/$metadata#Campione\/$entity",
|
||||
"IdCampione": 736901,
|
||||
"CodiceCampione": "11040",
|
||||
"CodiceCampioneWeb": "11040",
|
||||
"StatoCampione": "Nuovo",
|
||||
"DataCreazioneWeb": "2026-03-19T10:03:23.6170597+01:00",
|
||||
"RiferimentoWeb": "",
|
||||
"ConsegnaRichiesta": "2026-04-08T00:00:00+02:00",
|
||||
"DataAccettazioneLims": null,
|
||||
"Riferimento": null,
|
||||
"NoteWeb": "Parte CCC",
|
||||
"GruppiRicercati": null
|
||||
}
|
||||
|
||||
---
|
||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -1,20 +0,0 @@
|
||||
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"
|
||||
}
|
||||
@@ -1,48 +0,0 @@
|
||||
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
|
||||
}
|
||||
|
||||
---
|
||||
@@ -1,18 +0,0 @@
|
||||
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"
|
||||
}
|
||||
@@ -1,226 +0,0 @@
|
||||
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
|
||||
@@ -1,40 +0,0 @@
|
||||
CAMPIONE #0
|
||||
curl --location --request POST 'https://93.43.5.102/limsapi/api/odata/Campione' \
|
||||
--header 'Content-Type: application/json' \
|
||||
--header 'Authorization: Bearer ••••••' \
|
||||
--data '{
|
||||
"Commessa": 95833,
|
||||
"Matrice": 8413,
|
||||
"SottoMatrice": null,
|
||||
"SchemaCustomField": 82,
|
||||
"NoteWeb": "aaa"
|
||||
}'
|
||||
|
||||
RESPONSE:
|
||||
{
|
||||
"IdCampione": 14445,
|
||||
"Commessa": 95833,
|
||||
"Matrice": 8413
|
||||
}
|
||||
|
||||
---
|
||||
CAMPIONE #1
|
||||
curl --location --request POST 'https://93.43.5.102/limsapi/api/odata/Campione' \
|
||||
--header 'Content-Type: application/json' \
|
||||
--header 'Authorization: Bearer ••••••' \
|
||||
--data '{
|
||||
"Commessa": 95833,
|
||||
"Matrice": 3879,
|
||||
"SottoMatrice": null,
|
||||
"SchemaCustomField": 82,
|
||||
"NoteWeb": "bbb"
|
||||
}'
|
||||
|
||||
RESPONSE:
|
||||
{
|
||||
"IdCampione": 15750,
|
||||
"Commessa": 95833,
|
||||
"Matrice": 3879
|
||||
}
|
||||
|
||||
---
|
||||
@@ -1,9 +0,0 @@
|
||||
curl --location --request GET 'https://93.43.5.102/limsapi/api/odata/CommessaWeb(95833)?$expand=CommesseCustomFields($expand=CustomField)' \
|
||||
--header 'Authorization: Bearer ••••••'
|
||||
|
||||
RESPONSE:
|
||||
{
|
||||
"IdCommessa": 95833,
|
||||
"CodiceCommessa": "SIM-95833",
|
||||
"CommesseCustomFields": []
|
||||
}
|
||||
@@ -1,9 +0,0 @@
|
||||
curl --location --request GET 'https://93.43.5.102/limsapi/api/odata/CommessaWeb(95833)?$expand=CommesseCustomFields($expand=CustomField)' \
|
||||
--header 'Authorization: Bearer ••••••'
|
||||
|
||||
RESPONSE:
|
||||
{
|
||||
"IdCommessa": 95833,
|
||||
"CodiceCommessa": "SIM-95833",
|
||||
"CommesseCustomFields": []
|
||||
}
|
||||
@@ -1,10 +0,0 @@
|
||||
curl --location --request POST 'https://93.43.5.102/limsapi/api/odata/CommessaWeb(95833)/ImportaCommessa' \
|
||||
--header 'Content-Type: application/json' \
|
||||
--header 'Authorization: Bearer ••••••' \
|
||||
--data '{}'
|
||||
|
||||
RESPONSE:
|
||||
{
|
||||
"simulated": true,
|
||||
"endpoint": "CommessaWeb(95833)\/ImportaCommessa"
|
||||
}
|
||||
@@ -1,15 +0,0 @@
|
||||
Photos for CommessaWeb 95833 (iddatadb=1237):
|
||||
Total photos found: 1
|
||||
|
||||
curl --location --request POST 'https://93.43.5.102/limsapi/api/odata/AllegatoCommessaWeb' \
|
||||
--header 'Authorization: Bearer ••••••' \
|
||||
--form 'IdCommessa=95833' \
|
||||
--form 'file=@C:\xampp\htdocs\trf_certest\public\photostrf\1237-20260228192737-Blue and White Simple Daily Vlogger YouTube Banner (15) (1) (1).png'
|
||||
|
||||
RESPONSE:
|
||||
{
|
||||
"simulated": true,
|
||||
"file": "1237-20260228192737-Blue and White Simple Daily Vlogger YouTube Banner (15) (1) (1).png"
|
||||
}
|
||||
|
||||
---
|
||||
@@ -1,22 +0,0 @@
|
||||
curl --location --request POST 'https://93.43.5.102/limsapi/api/odata/CommessaWeb' \
|
||||
--header 'Content-Type: application/json' \
|
||||
--header 'Authorization: Bearer ••••••' \
|
||||
--data '{
|
||||
"Cliente": 3378,
|
||||
"SchemaCustomField": 82,
|
||||
"Richiedente": "Test Web Import",
|
||||
"Descrizione": "TEST CommessaWeb",
|
||||
"ClienteResponsabile": 3,
|
||||
"MoltiplicatorePrezzo": 1,
|
||||
"AnagraficaCertestObject": 2,
|
||||
"AnagraficaCertestService": 1,
|
||||
"ClienteFornitore": null
|
||||
}'
|
||||
|
||||
RESPONSE:
|
||||
{
|
||||
"IdCommessa": 95833,
|
||||
"CodiceCommessa": "SIM-95833",
|
||||
"Richiedente": "Test Web Import",
|
||||
"Descrizione": "TEST CommessaWeb"
|
||||
}
|
||||
@@ -1,28 +0,0 @@
|
||||
curl --location --request POST 'https://93.43.5.102/limsapi/api/odata/CommessaWeb' \
|
||||
--header 'Content-Type: application/json' \
|
||||
--header 'Authorization: Bearer ••••••' \
|
||||
--data '{
|
||||
"Cliente": 3378,
|
||||
"SchemaCustomField": 82,
|
||||
"Richiedente": "Test Web Import",
|
||||
"Descrizione": "TEST CommessaWeb",
|
||||
"ClienteResponsabile": 7586,
|
||||
"MoltiplicatorePrezzo": 3,
|
||||
"AnagraficaCertestObject": 8963,
|
||||
"AnagraficaCertestService": 9007,
|
||||
"ClienteFornitore": null
|
||||
}'
|
||||
|
||||
RESPONSE:
|
||||
{
|
||||
"@odata.context": "https:\/\/bvcpsitaly-elims.com\/limsapi\/api\/odata\/$metadata#CommessaWeb\/$entity",
|
||||
"IdCommessa": 562479,
|
||||
"CodiceCommessa": "26C0013",
|
||||
"RiferimentoCertificato": null,
|
||||
"StatoCommessaWeb": "Nuova",
|
||||
"DataCreazioneWeb": "2026-03-06T09:09:16.9338074+01:00",
|
||||
"CodiceCommessaWeb": "26C0013",
|
||||
"DataInviatoWeb": null,
|
||||
"Richiedente": "Test Web Import",
|
||||
"Descrizione": "TEST CommessaWeb"
|
||||
}
|
||||
@@ -1,28 +0,0 @@
|
||||
curl --location --request POST 'https://93.43.5.102/limsapi/api/odata/CommessaWeb' \
|
||||
--header 'Content-Type: application/json' \
|
||||
--header 'Authorization: Bearer ••••••' \
|
||||
--data '{
|
||||
"Cliente": 3378,
|
||||
"SchemaCustomField": 82,
|
||||
"Richiedente": "Test Web Import",
|
||||
"Descrizione": "TEST CommessaWeb",
|
||||
"ClienteResponsabile": 10653,
|
||||
"MoltiplicatorePrezzo": 1,
|
||||
"AnagraficaCertestObject": 8960,
|
||||
"AnagraficaCertestService": 9010,
|
||||
"ClienteFornitore": null
|
||||
}'
|
||||
|
||||
RESPONSE:
|
||||
{
|
||||
"@odata.context": "https:\/\/bvcpsitaly-elims.com\/limsapi\/api\/odata\/$metadata#CommessaWeb\/$entity",
|
||||
"IdCommessa": 563528,
|
||||
"CodiceCommessa": "26C0029",
|
||||
"RiferimentoCertificato": null,
|
||||
"StatoCommessaWeb": "Nuova",
|
||||
"DataCreazioneWeb": "2026-03-11T14:10:00.8960358+01:00",
|
||||
"CodiceCommessaWeb": "26C0029",
|
||||
"DataInviatoWeb": null,
|
||||
"Richiedente": "Test Web Import",
|
||||
"Descrizione": "TEST CommessaWeb"
|
||||
}
|
||||
@@ -1,28 +0,0 @@
|
||||
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"
|
||||
}
|
||||
@@ -1,28 +0,0 @@
|
||||
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"
|
||||
}
|
||||
@@ -1,28 +0,0 @@
|
||||
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"
|
||||
}
|
||||
@@ -1,29 +0,0 @@
|
||||
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"
|
||||
}
|
||||
@@ -97,31 +97,6 @@ if (!$template) {
|
||||
<ul id="associationsList" class="list-group border p-3"></ul>
|
||||
</div>
|
||||
|
||||
<!-- Default Values (Fixed fields) -->
|
||||
<div class="mt-4">
|
||||
<h5>Default Values (Fixed fields)</h5>
|
||||
|
||||
<div class="row g-3">
|
||||
<div class="col-md-6">
|
||||
<label class="form-label">MoltiplicatorePrezzo (default)</label>
|
||||
<select id="defaultMoltiplicatorePrezzo" class="form-select">
|
||||
<option value="">Loading...</option>
|
||||
</select>
|
||||
<small class="text-muted">Loaded from API: get_moltiplicatoreprezzo.php</small>
|
||||
</div>
|
||||
|
||||
<div class="col-md-6">
|
||||
<label class="form-label">ConsegnaRichiesta (default)</label>
|
||||
<input type="date" id="defaultConsegnaRichiesta" class="form-control">
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="mt-3">
|
||||
<button class="btn btn-success" id="saveDefaultsBtn">💾 Save defaults</button>
|
||||
<span id="defaultsStatus" class="ms-2 text-muted"></span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Save Button -->
|
||||
<div class="mt-4 text-end">
|
||||
<a href="templates_dashboard.php" class="btn btn-primary">⬅ Back to Template Dashboard</a>
|
||||
@@ -298,7 +273,7 @@ if (!$template) {
|
||||
}
|
||||
})
|
||||
.catch(error => console.error('Error loading table columns:', error));
|
||||
*/
|
||||
*/
|
||||
/** =======================
|
||||
* CARICAMENTO MAPPATURE GIÀ ESISTENTI
|
||||
* ======================= */
|
||||
@@ -466,97 +441,6 @@ if (!$template) {
|
||||
})
|
||||
.catch(error => console.error("❌ Fetch error:", error));
|
||||
}
|
||||
|
||||
/** =======================
|
||||
* DEFAULTS (Fixed fields)
|
||||
* - stored in localStorage for now
|
||||
* ======================= */
|
||||
|
||||
const defaultsKey = "tpl_defaults_" + templateId;
|
||||
|
||||
function loadDefaultsFromLocal() {
|
||||
try {
|
||||
const raw = localStorage.getItem(defaultsKey);
|
||||
if (!raw) return;
|
||||
const saved = JSON.parse(raw);
|
||||
|
||||
if (saved && typeof saved === "object") {
|
||||
if (saved.moltiplicatore_prezzo_id !== undefined) {
|
||||
document.getElementById('defaultMoltiplicatorePrezzo').value = saved.moltiplicatore_prezzo_id || "";
|
||||
}
|
||||
if (saved.consegna_richiesta !== undefined) {
|
||||
document.getElementById('defaultConsegnaRichiesta').value = saved.consegna_richiesta || "";
|
||||
}
|
||||
}
|
||||
} catch (e) {
|
||||
console.warn("⚠️ Cannot parse saved defaults:", e);
|
||||
}
|
||||
}
|
||||
|
||||
function saveDefaultsToLocal() {
|
||||
const moltiplicatoreId = document.getElementById('defaultMoltiplicatorePrezzo').value || "";
|
||||
const consegna = document.getElementById('defaultConsegnaRichiesta').value || "";
|
||||
|
||||
const payload = {
|
||||
moltiplicatore_prezzo_id: moltiplicatoreId,
|
||||
consegna_richiesta: consegna
|
||||
};
|
||||
|
||||
localStorage.setItem(defaultsKey, JSON.stringify(payload));
|
||||
|
||||
const status = document.getElementById('defaultsStatus');
|
||||
status.textContent = "✅ Defaults saved for this template (local)";
|
||||
setTimeout(() => status.textContent = "", 2500);
|
||||
}
|
||||
|
||||
function loadMoltiplicatoriPrezzo() {
|
||||
const select = document.getElementById('defaultMoltiplicatorePrezzo');
|
||||
select.innerHTML = `<option value="">-- Select --</option>`;
|
||||
|
||||
fetch('get_moltiplicatoreprezzo.php')
|
||||
.then(r => r.json())
|
||||
.then(data => {
|
||||
// OData tipico: { value: [...] }
|
||||
const rows = Array.isArray(data?.value) ? data.value : (Array.isArray(data) ? data : []);
|
||||
if (!rows.length) {
|
||||
select.innerHTML = `<option value="">(No data)</option>`;
|
||||
return;
|
||||
}
|
||||
|
||||
rows.forEach(item => {
|
||||
// ⚠️ Qui i nomi campi dipendono dal JSON reale:
|
||||
// - id: item.Id / item.ID / item.idMoltiplicatorePrezzo
|
||||
// - label: item.Descrizione / item.Nome / item.Codice
|
||||
const id = item.IdMoltiplicatorePrezzo;
|
||||
const label = `${item.Descrizione} (x${item.Fattore})`;
|
||||
|
||||
|
||||
if (id === undefined || id === null) return;
|
||||
|
||||
const opt = document.createElement('option');
|
||||
opt.value = String(id);
|
||||
opt.textContent = String(label);
|
||||
select.appendChild(opt);
|
||||
});
|
||||
|
||||
// dopo aver popolato, prova a rimettere il valore salvato
|
||||
loadDefaultsFromLocal();
|
||||
})
|
||||
.catch(err => {
|
||||
console.error("❌ Error loading MoltiplicatorePrezzo:", err);
|
||||
select.innerHTML = `<option value="">(Load error)</option>`;
|
||||
});
|
||||
}
|
||||
|
||||
// Events
|
||||
document.getElementById('saveDefaultsBtn').addEventListener('click', function() {
|
||||
saveDefaultsToLocal();
|
||||
});
|
||||
|
||||
// Init
|
||||
loadMoltiplicatoriPrezzo();
|
||||
loadDefaultsFromLocal();
|
||||
|
||||
});
|
||||
</script>
|
||||
</body>
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,22 +1,13 @@
|
||||
<!-- Modal per la gestione delle annotazioni -->
|
||||
<div class="modal fade" id="annotationsModal" tabindex="-1" aria-labelledby="annotationsModalLabel" aria-hidden="true">
|
||||
<div class="modal-dialog modal-xl" style="max-width: 90% !important; width: 90% !important;">
|
||||
<div class="modal-dialog modal-xl" style="max-width: 80% !important; width: 80% !important;">
|
||||
<div class="modal-content">
|
||||
<div class="modal-header">
|
||||
<h5 class="modal-title" id="annotationsModalLabel">Annotazioni per TRF: <span id="trfHeaderAnnotations"></span></h5>
|
||||
|
||||
<!-- SLIDER PER DIMENSIONE MARKER -->
|
||||
<div style="display: flex; align-items: center; gap: 10px; margin-left: 20px;">
|
||||
<label for="markerSizeSlider" style="margin: 0; font-size: 0.9rem; white-space: nowrap;">Dimensione marker:</label>
|
||||
<input type="range" id="markerSizeSlider" min="16" max="48" value="16" step="2" style="width: 120px;">
|
||||
<span id="markerSizeValue" style="font-weight: bold; min-width: 30px;">24px</span>
|
||||
</div>
|
||||
|
||||
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
|
||||
</div>
|
||||
<div class="modal-body">
|
||||
<div class="row">
|
||||
<!-- COLONNA SINISTRA RIDOTTA -->
|
||||
<div class="col-md-6">
|
||||
<div style="display: flex; justify-content: space-between; align-items: center; margin-bottom: 10px;">
|
||||
<h6 style="margin: 0;">Elenco Parti</h6>
|
||||
@@ -25,17 +16,15 @@
|
||||
<label for="showMixPartsAnnotations" style="font-size: 0.9rem;">Mix</label>
|
||||
</div>
|
||||
</div>
|
||||
<ul id="partsListAnnotations" class="list-group" style="max-height: 500px; overflow-y: auto;"></ul>
|
||||
<ul id="partsListAnnotations" class="list-group"></ul>
|
||||
</div>
|
||||
|
||||
<!-- COLONNA DESTRA PIÙ GRANDE -->
|
||||
<div class="col-md-6">
|
||||
<h6>Foto del Campione</h6>
|
||||
<div style="display: flex; align-items: center; margin-bottom: 10px;">
|
||||
<button type="button" class="btn btn-primary btn-sm" id="downloadPhotoBtnAnnotations" style="padding: 0.1rem 0.5rem; font-size: 0.8rem; margin-right: 10px;"><i class="fas fa-download"></i></button>
|
||||
<div id="photoSelectorContainerAnnotations" style="display: none;"></div>
|
||||
</div>
|
||||
<div style="position: relative; width: 100%; min-height: 500px; border: 1px solid #ddd; border-radius: 4px; overflow: hidden;">
|
||||
<div style="position: relative; width: 100%; min-height: 400px;">
|
||||
<img id="samplePhotoAnnotations" src="" alt="Foto del campione" style="max-width: 100%; max-height: 100%; object-fit: contain; position: absolute; top: 0; left: 0;">
|
||||
<canvas id="photoCanvasAnnotations" style="position: absolute; top: 0; left: 0; width: 100%; height: 100%;"></canvas>
|
||||
<canvas id="overlayCanvasAnnotations" style="position: absolute; top: 0; left: 0; width: 100%; height: 100%; z-index: 1000;"></canvas>
|
||||
@@ -227,48 +216,4 @@
|
||||
transform: translateX(-50%);
|
||||
z-index: 3000;
|
||||
}
|
||||
|
||||
/* Stile per lo slider */
|
||||
#markerSizeSlider {
|
||||
-webkit-appearance: none;
|
||||
height: 6px;
|
||||
border-radius: 3px;
|
||||
background: #ddd;
|
||||
outline: none;
|
||||
}
|
||||
|
||||
#markerSizeSlider::-webkit-slider-thumb {
|
||||
-webkit-appearance: none;
|
||||
appearance: none;
|
||||
width: 16px;
|
||||
height: 16px;
|
||||
border-radius: 50%;
|
||||
background: #007bff;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
#markerSizeSlider::-moz-range-thumb {
|
||||
width: 16px;
|
||||
height: 16px;
|
||||
border-radius: 50%;
|
||||
background: #007bff;
|
||||
cursor: pointer;
|
||||
border: none;
|
||||
}
|
||||
|
||||
#partsListAnnotations .list-group-item.dragging {
|
||||
opacity: 0.6;
|
||||
background-color: #d1e7dd !important;
|
||||
transform: rotate(3deg);
|
||||
box-shadow: 0 4px 8px rgba(0, 0, 0, 0.2);
|
||||
}
|
||||
|
||||
#partsListAnnotations .placeholder {
|
||||
background-color: #f8d7da !important;
|
||||
border: 2px dashed #dc3545;
|
||||
height: 40px;
|
||||
margin: 2px 0;
|
||||
border-radius: 4px;
|
||||
}
|
||||
</style>
|
||||
<script src="https://code.jquery.com/ui/1.13.2/jquery-ui.min.js"></script>
|
||||
</style>
|
||||
@@ -307,23 +307,4 @@
|
||||
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>
|
||||
@@ -1,12 +1,12 @@
|
||||
<div class="modal fade" id="partsModal" tabindex="-1" aria-labelledby="partsModalLabel" aria-hidden="true">
|
||||
<div class="modal-dialog modal-xl" style="max-width: 95vw !important;">
|
||||
<div class="modal-dialog modal-xl" style="max-width: 80% !important;">
|
||||
<div class="modal-content">
|
||||
<div class="modal-header">
|
||||
<h5 class="modal-title" id="partsModalLabel">Parti per TRF: <span id="trfHeader"></span></h5>
|
||||
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
|
||||
</div>
|
||||
<div class="modal-body">
|
||||
<div class="row parts-row">
|
||||
<div class="row">
|
||||
<div class="col-md-9">
|
||||
<!-- Prima riga: Elenco Parti, Rinumera, Voce -->
|
||||
<div style="display: flex; justify-content: space-between; align-items: center; margin-bottom: 10px;">
|
||||
@@ -14,27 +14,22 @@
|
||||
<div style="display: flex; align-items: center;">
|
||||
<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-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-info btn-sm ms-2 d-none" id="quotationeBtn" style="padding: 0.1rem 0.5rem; font-size: 0.8rem;">Add Quotation</button>
|
||||
<button type="button" class="btn btn-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-image ms-1" style="font-size: 0.8rem;"></i>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
<!-- Seconda riga: +, M, MacroMatrice, Matrice globale, Propaga -->
|
||||
<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"></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 fa-xs"></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>
|
||||
<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>
|
||||
</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"></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 fa-xs"></i> Propaga a tutte</button>
|
||||
</div>
|
||||
<table class="table table-striped table-sm" id="partsTable">
|
||||
<thead>
|
||||
<tr>
|
||||
<th style="width: 55px;">Num</th>
|
||||
<th style="width: 80px;">Numero</th>
|
||||
<th>Descrizione</th>
|
||||
<th style="width: 200px;">Matrice</th>
|
||||
<th style="width: 150px;">
|
||||
@@ -51,11 +46,11 @@
|
||||
</thead>
|
||||
<tbody id="partsTableBody">
|
||||
<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: 80px;"></td>
|
||||
<td><input type="text" class="form-control form-control-sm part-description" placeholder="Inserisci descrizione"></td>
|
||||
<td>
|
||||
<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"></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 fa-xs"></i></button>
|
||||
<select class="part-matrice form-control form-control-sm" style="width: 150px;"></select>
|
||||
</div>
|
||||
</td>
|
||||
@@ -63,9 +58,9 @@
|
||||
<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-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"></i></button>
|
||||
<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"></i></span>
|
||||
<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>
|
||||
<span class="save-status text-success" style="display: none; margin-left: 5px;"><i class="fas fa-check fa-xs"></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>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
@@ -146,24 +141,6 @@
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<!-- Finestra modale "Aggiungi preventivo" -->
|
||||
<div class="modal fade" id="addQuotationModal" tabindex="-1" aria-labelledby="addQuotationModalLabel" aria-hidden="true">
|
||||
<div class="modal-dialog modal-md">
|
||||
<div class="modal-content">
|
||||
<div class="modal-header">
|
||||
<h5 class="modal-title" id="addQuotationModalLabel">Choose a quotation</h5>
|
||||
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
|
||||
</div>
|
||||
<div class="modal-body">
|
||||
<select id="addQuotationSelect" class="form-control form-control-sm" style="width: 100% !important; min-width: 100% !important"></select>
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
<button type="button" class="btn btn-secondary btn-sm" data-bs-dismiss="modal">Annulla</button>
|
||||
<button type="button" class="btn btn-primary btn-sm" id="addQuotationBtn">Confirm</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<style>
|
||||
/* --- Base --- */
|
||||
@@ -180,22 +157,6 @@
|
||||
max-width: 100% !important
|
||||
}
|
||||
|
||||
#addQuotationModal {
|
||||
z-index: 1060 !important
|
||||
}
|
||||
|
||||
#addQuotationModal .modal-backdrop {
|
||||
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 */
|
||||
#partsTable tr {
|
||||
display: table-row !important
|
||||
@@ -295,13 +256,9 @@
|
||||
/* 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 {
|
||||
text-align: center;
|
||||
width: 350 !important;
|
||||
min-width: 350px !important;
|
||||
max-width: 350px !important;
|
||||
}
|
||||
|
||||
#partsTable td:nth-child(2) .part-description {
|
||||
|
||||
@@ -1,218 +0,0 @@
|
||||
/**
|
||||
* 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 || '');
|
||||
$("#partsModal").data("iddatadb", iddatadb).data("idquotations", idquotations);
|
||||
|
||||
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 });
|
||||
}
|
||||
alert(`Added ${uniqueParts.length} part(s).`);
|
||||
$input.val('');
|
||||
} 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 (matrici.length === 0) {
|
||||
$.ajax({
|
||||
url: "get_matrici.php",
|
||||
url: "get_matrici_db.php",
|
||||
method: "GET",
|
||||
dataType: "json",
|
||||
success: function (data) {
|
||||
|
||||
+138
-525
@@ -6,9 +6,6 @@ $(document).ready(function () {
|
||||
let unsavedChanges = false;
|
||||
let matrici = [];
|
||||
let macroMatrici = [];
|
||||
let quotations = [];
|
||||
let partsExtraField = null; // {field_id, field_label} oppure null
|
||||
let extraFieldOptions = []; // [{id,label}]
|
||||
|
||||
// --- ROW ID helpers: niente più cache impazzita di jQuery .data() ---
|
||||
function getPartId($row) {
|
||||
@@ -31,202 +28,6 @@ $(document).ready(function () {
|
||||
return id === "new" || id === "" || id === null ? null : id;
|
||||
}
|
||||
|
||||
function loadPartsExtraField(iddatadb, done) {
|
||||
partsExtraField = null;
|
||||
|
||||
if (!iddatadb) return done();
|
||||
|
||||
$.ajax({
|
||||
url: "get_parts_extra_field.php",
|
||||
method: "GET",
|
||||
dataType: "json",
|
||||
data: { iddatadb },
|
||||
success: function (res) {
|
||||
partsExtraField =
|
||||
res && res.success && res.field ? res.field : null;
|
||||
|
||||
if (
|
||||
partsExtraField &&
|
||||
(partsExtraField.data_type || "").toLowerCase() ===
|
||||
"sceltamultipla"
|
||||
) {
|
||||
loadExtraFieldOptions(
|
||||
String(partsExtraField.field_id),
|
||||
function () {
|
||||
applyExtraFieldColumn();
|
||||
done();
|
||||
},
|
||||
);
|
||||
} else {
|
||||
applyExtraFieldColumn();
|
||||
done();
|
||||
}
|
||||
},
|
||||
error: function () {
|
||||
partsExtraField = null;
|
||||
applyExtraFieldColumn();
|
||||
done();
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
function loadExtraFieldOptions(fieldId, done) {
|
||||
extraFieldOptions = [];
|
||||
if (!fieldId) return done();
|
||||
|
||||
$.ajax({
|
||||
url: "get_customfield_values.php",
|
||||
method: "GET",
|
||||
dataType: "json",
|
||||
data: { field_ids: fieldId },
|
||||
success: function (res) {
|
||||
const arr = res && res[fieldId] ? res[fieldId] : [];
|
||||
|
||||
extraFieldOptions = (arr || [])
|
||||
.map((x) => ({
|
||||
id: x.IdCustomFieldsValue,
|
||||
label: x.Valore || "",
|
||||
}))
|
||||
.sort((a, b) =>
|
||||
a.label.localeCompare(b.label, "it", {
|
||||
sensitivity: "base",
|
||||
numeric: true,
|
||||
}),
|
||||
);
|
||||
|
||||
done();
|
||||
},
|
||||
error: function () {
|
||||
extraFieldOptions = [];
|
||||
done();
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
function buildExtraFieldCellHtml($row = null) {
|
||||
if (!partsExtraField) return "";
|
||||
|
||||
const selectedValueId = $row ? $row.data("extra-value-id") || "" : "";
|
||||
|
||||
// SceltaMultipla -> select + hidden value id
|
||||
if (
|
||||
(partsExtraField.data_type || "").toLowerCase() === "sceltamultipla"
|
||||
) {
|
||||
const opts = [`<option value="">Select…</option>`]
|
||||
.concat(
|
||||
extraFieldOptions.map(
|
||||
(o) => `<option value="${o.id}">${o.label}</option>`,
|
||||
),
|
||||
)
|
||||
.join("");
|
||||
|
||||
return `
|
||||
<td class="extra-field-td" style="width:200px;">
|
||||
<input type="hidden" class="part-extra-field-id" value="${partsExtraField.field_id}">
|
||||
<input type="hidden" class="part-extra-value-id" value="">
|
||||
<select class="form-control form-control-sm part-extra-select" data-selected="${selectedValueId || ""}">${opts}</select>
|
||||
</td>`;
|
||||
}
|
||||
|
||||
// Testo -> input + hidden field_id
|
||||
return `
|
||||
<td class="extra-field-td" style="width:200px;">
|
||||
<input type="hidden" class="part-extra-field-id" value="${partsExtraField.field_id}">
|
||||
<input type="text" class="form-control form-control-sm part-extra-field" data-type="${partsExtraField.data_type || ""}">
|
||||
</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 () {
|
||||
const valId = $(this).val() || "";
|
||||
const $row = $(this).closest("tr");
|
||||
|
||||
// salva nello stato della riga (così sopravvive ai re-render)
|
||||
$row.data("extra-value-id", valId);
|
||||
|
||||
$(this).closest("td").find(".part-extra-value-id").val(valId);
|
||||
saveRow($row);
|
||||
});
|
||||
|
||||
function applyExtraFieldColumn() {
|
||||
const $theadRow = $("#partsTable thead tr");
|
||||
|
||||
// 1) rimuovi header extra
|
||||
$theadRow.find("th.extra-field-th").remove();
|
||||
|
||||
// 2) rimuovi celle extra esistenti
|
||||
$("#partsTableBody tr").each(function () {
|
||||
$(this).find("td.extra-field-td").remove();
|
||||
});
|
||||
|
||||
// 3) se non c'è campo extra -> fine
|
||||
if (!partsExtraField) return;
|
||||
|
||||
// 4) inserisci header prima di "Azioni" (ultima colonna)
|
||||
$theadRow
|
||||
.find("th:last")
|
||||
.before(
|
||||
`<th class="extra-field-th" style="width:200px;">${partsExtraField.field_label}</th>`,
|
||||
);
|
||||
|
||||
// 5) aggiungi cella a ogni riga già presente (una sola volta)
|
||||
$("#partsTableBody tr").each(function () {
|
||||
const $row = $(this);
|
||||
$row.find("td:last").before(buildExtraFieldCellHtml($row));
|
||||
|
||||
const selected = ($row.data("extra-value-id") || "").toString();
|
||||
if (selected) {
|
||||
$row.find(".part-extra-value-id").val(selected);
|
||||
$row.find(".part-extra-select").attr("data-selected", selected);
|
||||
}
|
||||
});
|
||||
|
||||
initializeExtraFieldSelect2($("#partsTableBody"));
|
||||
}
|
||||
|
||||
function setPartId($row, id) {
|
||||
if (!id) return;
|
||||
// Sincronizza TUTTO: attributo + cache jQuery + nostra cache
|
||||
@@ -340,30 +141,26 @@ $(document).ready(function () {
|
||||
// ===================
|
||||
// MODAL HANDLING
|
||||
// ===================
|
||||
function loadParts(iddatadb, idquotations, callback = null) {
|
||||
function loadParts(iddatadb, idquotations) {
|
||||
if (iddatadb) {
|
||||
if (matrici.length === 0) {
|
||||
$.ajax({
|
||||
url: "get_matrici.php",
|
||||
url: "get_matrici_db.php",
|
||||
method: "GET",
|
||||
dataType: "json",
|
||||
success: function (data) {
|
||||
matrici = data.value || [];
|
||||
loadMacroMatrici();
|
||||
initializeGlobalSelect2();
|
||||
loadPartsExtraField(iddatadb, function () {
|
||||
loadPhoto(iddatadb, idquotations);
|
||||
loadExistingParts(iddatadb, idquotations, callback);
|
||||
});
|
||||
loadPhoto(iddatadb, idquotations);
|
||||
loadExistingParts(iddatadb, idquotations);
|
||||
},
|
||||
error: function (xhr, status, error) {
|
||||
matrici = [];
|
||||
loadMacroMatrici();
|
||||
initializeGlobalSelect2();
|
||||
loadPartsExtraField(iddatadb, function () {
|
||||
loadPhoto(iddatadb, idquotations);
|
||||
loadExistingParts(iddatadb, idquotations, callback);
|
||||
});
|
||||
loadPhoto(iddatadb, idquotations);
|
||||
loadExistingParts(iddatadb, idquotations);
|
||||
const errorMsg = $(
|
||||
'<div class="alert alert-danger temp-alert" role="alert">Errore nel caricamento delle matrici: ' +
|
||||
error +
|
||||
@@ -382,16 +179,12 @@ $(document).ready(function () {
|
||||
} else {
|
||||
loadMacroMatrici();
|
||||
initializeGlobalSelect2();
|
||||
loadPartsExtraField(iddatadb, function () {
|
||||
loadPhoto(iddatadb, idquotations);
|
||||
loadExistingParts(iddatadb, idquotations, callback);
|
||||
});
|
||||
loadPhoto(iddatadb, idquotations);
|
||||
loadExistingParts(iddatadb, idquotations);
|
||||
}
|
||||
} else {
|
||||
loadPartsExtraField(iddatadb, function () {
|
||||
loadPhoto(iddatadb, idquotations);
|
||||
loadExistingParts(iddatadb, idquotations, callback);
|
||||
});
|
||||
loadPhoto(iddatadb, idquotations);
|
||||
loadExistingParts(iddatadb, idquotations);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -720,11 +513,6 @@ $(document).ready(function () {
|
||||
if (response.new_id) return response.new_id;
|
||||
if (response.partId) return response.partId;
|
||||
|
||||
// ✅ RISPOSTA DI save_parts.php: { success:true, results:[{part_id:...}] }
|
||||
if (Array.isArray(response.results) && response.results[0]) {
|
||||
if (response.results[0].part_id) return response.results[0].part_id;
|
||||
if (response.results[0].id) return response.results[0].id;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@@ -738,13 +526,6 @@ $(document).ready(function () {
|
||||
const iddatadb = $("#partsModal").data("iddatadb");
|
||||
const idquotations = $("#partsModal").data("idquotations");
|
||||
const isMix = partDescription.startsWith("Mix") ? "Y" : "N";
|
||||
|
||||
// EXTRA FIELD (0/1)
|
||||
const extra_field_id = $row.find(".part-extra-field-id").val() || null;
|
||||
const extra_value_id = $row.find(".part-extra-value-id").val() || null;
|
||||
const extra_value_text = $row.find(".part-extra-field").length
|
||||
? ($row.find(".part-extra-field").val() || "").trim()
|
||||
: null;
|
||||
let partId = getPartId($row);
|
||||
if (partId === "new") partId = null; // difesa extra (non dovrebbe più servire, ma sicura)
|
||||
const endpoint = idquotations
|
||||
@@ -777,14 +558,8 @@ $(document).ready(function () {
|
||||
part_number: partNumber,
|
||||
part_description: partDescription,
|
||||
mix: isMix,
|
||||
idmatrice: $row.find(".part-matrice").val() || null,
|
||||
dateexpiry: dateexpiry || null,
|
||||
note: note,
|
||||
|
||||
// extra custom field
|
||||
extra_field_id: extra_field_id,
|
||||
extra_value_id: extra_value_id,
|
||||
extra_value_text: extra_value_text,
|
||||
},
|
||||
],
|
||||
}),
|
||||
@@ -814,9 +589,6 @@ $(document).ready(function () {
|
||||
if (response.success) {
|
||||
$saveStatus.show();
|
||||
setTimeout(() => $saveStatus.hide(), 2000);
|
||||
|
||||
if (!$("#quotationeBtn").hasClass("d-none"))
|
||||
$("#quotationeBtn").addClass("d-none");
|
||||
} else {
|
||||
const errorMsg = $(
|
||||
'<div class="alert alert-danger temp-alert" role="alert">Errore nel salvataggio: ' +
|
||||
@@ -941,28 +713,23 @@ $(document).ready(function () {
|
||||
<td><input type="text" class="form-control form-control-sm part-description" value="${description}" placeholder="Inserisci descrizione"></td>
|
||||
<td>
|
||||
<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"></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 fa-xs"></i></button>
|
||||
<select class="part-matrice form-control form-control-sm" style="width: 150px;"></select>
|
||||
</div>
|
||||
</td>
|
||||
<td><input type="date" class="form-control form-control-sm part-dateexpiry" style="width: 130px;"></td>
|
||||
${partsExtraField ? buildExtraFieldCellHtml(null) : ``}
|
||||
<td>
|
||||
<td><input type="date" class="form-control form-control-sm part-dateexpiry" style="width: 130px;"></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-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"></i></button>
|
||||
<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"></i></span>
|
||||
<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>
|
||||
<span class="save-status text-success" style="display: none; margin-left: 5px;"><i class="fas fa-check fa-xs"></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>
|
||||
</td>
|
||||
</tr>`;
|
||||
$("#partsTableBody").append(newRow);
|
||||
const $newRow = $("#partsTableBody tr:last");
|
||||
const $select = $newRow.find(".part-matrice");
|
||||
const $select = $("#partsTableBody tr:last .part-matrice");
|
||||
const selectedMacro = $("#macro-matrice-filter").val() || "";
|
||||
|
||||
initializeSelect2($select, nextPartNumber, "", null, selectedMacro);
|
||||
initializeExtraFieldSelect2($newRow);
|
||||
|
||||
updateRowButtons();
|
||||
markUnsaved();
|
||||
}
|
||||
@@ -1384,15 +1151,11 @@ $(document).ready(function () {
|
||||
}
|
||||
});
|
||||
|
||||
$(document).on(
|
||||
"blur",
|
||||
".part-description, .part-number, .part-extra-field",
|
||||
function () {
|
||||
saveRow($(this).closest("tr"));
|
||||
},
|
||||
);
|
||||
$(document).on("blur", ".part-description, .part-number", function () {
|
||||
saveRow($(this).closest("tr"));
|
||||
});
|
||||
|
||||
function loadExistingParts(iddatadb, idquotations, callback = null) {
|
||||
function loadExistingParts(iddatadb, idquotations) {
|
||||
const endpoint = idquotations
|
||||
? "load_parts_quotation.php"
|
||||
: "load_parts.php";
|
||||
@@ -1438,41 +1201,20 @@ $(document).ready(function () {
|
||||
<td><input type="text" class="form-control form-control-sm part-description" value="${escapedDescription}" placeholder="Inserisci descrizione"></td>
|
||||
<td>
|
||||
<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"></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 fa-xs"></i></button>
|
||||
<select class="part-matrice form-control form-control-sm" style="width: 150px;" ${idquotations && !part.idmatrice ? "disabled" : ""}></select>
|
||||
</div>
|
||||
</td>
|
||||
<td><input type="date" class="form-control form-control-sm part-dateexpiry" value="${part.dateexpiry || ""}" style="width: 130px;"></td>
|
||||
${partsExtraField ? buildExtraFieldCellHtml() : ``}
|
||||
<td>
|
||||
<td><input type="date" class="form-control form-control-sm part-dateexpiry" value="${part.dateexpiry || ""}" style="width: 130px;"></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-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"></i></button>
|
||||
<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"></i></span>
|
||||
<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>
|
||||
<span class="save-status text-success" style="display: none; margin-left: 5px;"><i class="fas fa-check fa-xs"></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>
|
||||
</td>
|
||||
</tr>`;
|
||||
$("#partsTableBody").append(newRow);
|
||||
|
||||
const $row = $(
|
||||
`#partsTableBody tr[data-part-id="${part.id}"]`,
|
||||
);
|
||||
|
||||
if (
|
||||
part.extra_value_id !== undefined &&
|
||||
part.extra_value_id !== null
|
||||
) {
|
||||
const vid = String(part.extra_value_id);
|
||||
$row.data("extra-value-id", vid);
|
||||
$row.find(".part-extra-value-id").val(vid);
|
||||
$row.find(".part-extra-select").attr(
|
||||
"data-selected",
|
||||
vid,
|
||||
);
|
||||
}
|
||||
|
||||
initializeExtraFieldSelect2($row);
|
||||
|
||||
const $select = $("#partsTableBody").find(
|
||||
`tr[data-part-id="${part.id}"] .part-matrice`,
|
||||
);
|
||||
@@ -1491,11 +1233,8 @@ $(document).ready(function () {
|
||||
});
|
||||
} else {
|
||||
addNewRow(1, false); // Riga iniziale normale
|
||||
$("#quotationeBtn").removeClass("d-none");
|
||||
}
|
||||
updateRowButtons();
|
||||
|
||||
if (callback) callback();
|
||||
},
|
||||
error: function (xhr, status, error) {
|
||||
const errorMsg = $(
|
||||
@@ -1600,7 +1339,6 @@ $(document).ready(function () {
|
||||
partId,
|
||||
currentValue,
|
||||
selectedMacro,
|
||||
true,
|
||||
);
|
||||
});
|
||||
});
|
||||
@@ -1615,6 +1353,18 @@ $(document).ready(function () {
|
||||
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
|
||||
const currentValue = $select.val();
|
||||
|
||||
@@ -1623,39 +1373,39 @@ $(document).ready(function () {
|
||||
$select.select2("destroy");
|
||||
}
|
||||
|
||||
// Inizializza Select2 con AJAX
|
||||
// Inizializza Select2
|
||||
$select.empty().select2({
|
||||
placeholder: "Seleziona matrice globale",
|
||||
placeholder: filteredMatrici.length
|
||||
? "Seleziona matrice globale"
|
||||
: "Nessuna matrice disponibile",
|
||||
allowClear: true,
|
||||
data: options,
|
||||
dropdownParent: $("#partsModal"),
|
||||
minimumInputLength: 0,
|
||||
ajax: {
|
||||
url: "search_matrici.php",
|
||||
dataType: "json",
|
||||
delay: 150,
|
||||
data: function (params) {
|
||||
return { q: params.term || "", limit: 20, macro: selectedMacro || "" };
|
||||
},
|
||||
processResults: function (data) {
|
||||
return { results: data.results || [] };
|
||||
},
|
||||
cache: true,
|
||||
minimumResultsForSearch: 0, // Abilita ricerca senza limite minimo di caratteri
|
||||
matcher: function (params, data) {
|
||||
if (!params.term) return data; // Mostra tutte le opzioni se non c'è termine di ricerca
|
||||
const term = params.term.toUpperCase();
|
||||
if (data.text.toUpperCase().indexOf(term) >= 0) return data;
|
||||
return null;
|
||||
},
|
||||
});
|
||||
|
||||
// Ripristina il valore corrente
|
||||
if (currentValue) {
|
||||
$.ajax({
|
||||
url: "search_matrici.php",
|
||||
data: { id: currentValue },
|
||||
dataType: "json",
|
||||
}).then(function (data) {
|
||||
const item = (data.results || [])[0];
|
||||
if (item) {
|
||||
const option = new Option(item.text, item.id, true, true);
|
||||
$select.append(option).trigger("change");
|
||||
}
|
||||
});
|
||||
// Ripristina il valore corrente se valido
|
||||
if (
|
||||
currentValue &&
|
||||
filteredMatrici.some((m) => m.IdMatrice == currentValue)
|
||||
) {
|
||||
$select.val(currentValue).trigger("change");
|
||||
} else if (filteredMatrici.length === 0) {
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1665,7 +1415,6 @@ $(document).ready(function () {
|
||||
partId,
|
||||
idmatrice,
|
||||
selectedMacro = null,
|
||||
fromFilter = false,
|
||||
) {
|
||||
if (typeof $.fn.select2 === "undefined") {
|
||||
$select.replaceWith(
|
||||
@@ -1674,6 +1423,32 @@ $(document).ready(function () {
|
||||
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
|
||||
const currentValue = idmatrice || $select.val();
|
||||
|
||||
@@ -1682,51 +1457,47 @@ $(document).ready(function () {
|
||||
$select.select2("destroy");
|
||||
}
|
||||
|
||||
// Inizializza Select2 con AJAX
|
||||
// Inizializza Select2
|
||||
$select.empty().select2({
|
||||
placeholder: "Seleziona matrice",
|
||||
placeholder: filteredMatrici.length
|
||||
? "Seleziona matrice"
|
||||
: "Nessuna matrice disponibile",
|
||||
allowClear: true,
|
||||
data: options,
|
||||
dropdownParent: $("#partsModal"),
|
||||
minimumInputLength: 0,
|
||||
ajax: {
|
||||
url: "search_matrici.php",
|
||||
dataType: "json",
|
||||
delay: 150,
|
||||
data: function (params) {
|
||||
return { q: params.term || "", limit: 20, macro: selectedMacro || "" };
|
||||
},
|
||||
processResults: function (data) {
|
||||
return { results: data.results || [] };
|
||||
},
|
||||
cache: true,
|
||||
minimumResultsForSearch: 0, // Abilita ricerca senza limite minimo di caratteri
|
||||
matcher: function (params, data) {
|
||||
if (!params.term) return data; // Mostra tutte le opzioni se non c'è termine di ricerca
|
||||
const term = params.term.toUpperCase();
|
||||
if (data.text.toUpperCase().indexOf(term) >= 0) return data;
|
||||
return null;
|
||||
},
|
||||
});
|
||||
|
||||
// Ripristina il valore se valido
|
||||
if (partId && partId !== "new" && currentValue) {
|
||||
$.ajax({
|
||||
url: "search_matrici.php",
|
||||
data: { id: currentValue },
|
||||
dataType: "json",
|
||||
}).then(function (data) {
|
||||
const item = (data.results || [])[0];
|
||||
if (item) {
|
||||
const option = new Option(item.text, item.id, true, true);
|
||||
if (!fromFilter) $select.append(option).trigger("change");
|
||||
else $select.append(option);
|
||||
partMatrice[partNumber] = item.id;
|
||||
} else {
|
||||
if (!fromFilter) $select.val(null).trigger("change");
|
||||
partMatrice[partNumber] = null;
|
||||
}
|
||||
});
|
||||
// === MODIFICA CHIRURGICA: solo qui ===
|
||||
if (currentValue && currentValue !== "new") {
|
||||
const matrice = matrici.find((m) => m.IdMatrice == currentValue);
|
||||
if (matrice) {
|
||||
const option = new Option(
|
||||
matrice.NomeMatrice,
|
||||
matrice.IdMatrice,
|
||||
true,
|
||||
true,
|
||||
);
|
||||
$select.append(option).trigger("change");
|
||||
partMatrice[partNumber] = matrice.IdMatrice;
|
||||
} else {
|
||||
$select.val(null).trigger("change");
|
||||
partMatrice[partNumber] = null;
|
||||
}
|
||||
} else {
|
||||
$select.val(null).trigger("change", [{ skipHandler: true }]);
|
||||
// Nessun valore: assicurati che sia vuoto
|
||||
$select.val(null).trigger("change");
|
||||
partMatrice[partNumber] = null;
|
||||
}
|
||||
// === FINE MODIFICA ===
|
||||
|
||||
$select.on("change", function (event, data) {
|
||||
if (data && data?.skipHandler) return;
|
||||
|
||||
$select.on("change", function () {
|
||||
const idmatrice = $(this).val();
|
||||
const $row = $(this).closest("tr");
|
||||
const partId = $row.data("part-id");
|
||||
@@ -1796,7 +1567,18 @@ $(document).ready(function () {
|
||||
}
|
||||
});
|
||||
|
||||
// Messaggio se macro selezionata ma nessun risultato sarà gestito dal placeholder Select2
|
||||
// Mostra messaggio se non ci sono matrici
|
||||
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
|
||||
console.log(
|
||||
@@ -1812,13 +1594,8 @@ $(document).ready(function () {
|
||||
$(document).on("click", ".propagate-matrice-btn", function () {
|
||||
const $row = $(this).closest("tr");
|
||||
const globalVal = $("#global-matrice").val();
|
||||
const globalText = $("#global-matrice").find("option:selected").text();
|
||||
if (globalVal) {
|
||||
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");
|
||||
$row.find(".part-matrice").val(globalVal).trigger("change");
|
||||
} else {
|
||||
const errorMsg = $(
|
||||
'<div class="alert alert-danger temp-alert" role="alert">Seleziona una matrice globale prima di propagare.</div>',
|
||||
@@ -1834,15 +1611,8 @@ $(document).ready(function () {
|
||||
|
||||
$(document).on("click", ".propagate-all-btn", function () {
|
||||
const globalVal = $("#global-matrice").val();
|
||||
const globalText = $("#global-matrice").find("option:selected").text();
|
||||
if (globalVal) {
|
||||
$("#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");
|
||||
});
|
||||
$("#partsTableBody .part-matrice").val(globalVal).trigger("change");
|
||||
} else {
|
||||
const errorMsg = $(
|
||||
'<div class="alert alert-danger temp-alert" role="alert">Seleziona una matrice globale prima di propagare.</div>',
|
||||
@@ -2011,141 +1781,6 @@ $(document).ready(function () {
|
||||
|
||||
// Esporta la funzione loadParts per essere usata da import_Edit2.php
|
||||
window.loadParts = loadParts;
|
||||
|
||||
$(document).on("click", "#quotationeBtn", function () {
|
||||
$("#addQuotationModal").modal("show");
|
||||
|
||||
if (quotations.length > 0) {
|
||||
reloadQuotations();
|
||||
} else {
|
||||
$.ajax({
|
||||
url: "load_quotations.php",
|
||||
method: "GET",
|
||||
success: function (response) {
|
||||
quotations = response?.quotations || [];
|
||||
|
||||
reloadQuotations();
|
||||
},
|
||||
error: function (xhr, status, error) {
|
||||
console.error(
|
||||
"Errore AJAX caricamento quotations:",
|
||||
error,
|
||||
xhr.responseText,
|
||||
);
|
||||
let message = `
|
||||
<div class="alert alert-danger temp-alert" role="alert">
|
||||
Errore nel caricamento delle quotations: ${error} (${xhr.status})
|
||||
</div>
|
||||
`;
|
||||
|
||||
$("#partsModal .modal-body").prepend(errorMsg);
|
||||
|
||||
setTimeout(function () {
|
||||
message.fadeOut(500, function () {
|
||||
$(this).remove();
|
||||
});
|
||||
}, 5000);
|
||||
},
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
$(document).on("click", "#addQuotationBtn", function () {
|
||||
const quotationId = $("#addQuotationSelect").val();
|
||||
|
||||
if (
|
||||
quotationId &&
|
||||
confirm("Confermo di collegare la quotazione al campione?")
|
||||
) {
|
||||
$("#addQuotationModal").modal("hide");
|
||||
|
||||
loadParts(null, quotationId, () => {
|
||||
$("#quotationeBtn").addClass("d-none");
|
||||
|
||||
setTimeout(() => {
|
||||
$("#partsTableBody tr").each(function () {
|
||||
$(this).find(".save-loading").show();
|
||||
});
|
||||
|
||||
let photoList = $("#photoSelector option")
|
||||
.map(function () {
|
||||
return this.value?.split("/")?.pop();
|
||||
})
|
||||
.get();
|
||||
|
||||
if (!photoList.length) {
|
||||
let path = $("#samplePhoto")
|
||||
.attr("src")
|
||||
?.split("/")
|
||||
?.pop();
|
||||
|
||||
if (path) photoList = [path];
|
||||
}
|
||||
|
||||
$.ajax({
|
||||
url: "save_parts_photo_iddatadb.php",
|
||||
method: "POST",
|
||||
data: JSON.stringify({
|
||||
iddatadb: $("#partsModal").data("iddatadb"),
|
||||
photoList,
|
||||
partIds: $("#partsTableBody tr")
|
||||
.map(function () {
|
||||
return $(this).data("part-id");
|
||||
})
|
||||
.get(),
|
||||
}),
|
||||
success: function () {
|
||||
$("#partsTableBody tr").each(function () {
|
||||
let $row = $(this);
|
||||
let $saveStatus = $row.find(".save-status");
|
||||
let $saveLoading = $row.find(".save-loading");
|
||||
|
||||
$saveLoading.hide();
|
||||
$saveStatus.show();
|
||||
|
||||
setTimeout(() => $saveStatus.hide(), 2000);
|
||||
});
|
||||
},
|
||||
error: function (xhr, status, error) {
|
||||
let message = `
|
||||
<div class="alert alert-danger temp-alert" role="alert">
|
||||
Errore di salvataggio: ${error} (${xhr.status})
|
||||
</div>
|
||||
`;
|
||||
|
||||
$("#partsModal .modal-body").prepend(message);
|
||||
|
||||
setTimeout(function () {
|
||||
message.fadeOut(500, function () {
|
||||
$(this).remove();
|
||||
});
|
||||
}, 5000);
|
||||
},
|
||||
});
|
||||
}, 100);
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
function reloadQuotations() {
|
||||
if (quotations.length > 0) {
|
||||
$("#addQuotationSelect").empty();
|
||||
|
||||
$("#addQuotationSelect").append(
|
||||
"<option value=''>Seleziona Quotation</option>",
|
||||
);
|
||||
|
||||
quotations.forEach((quotation) => {
|
||||
if (quotation?.description) {
|
||||
$("#addQuotationSelect").append(
|
||||
`<option value='${quotation?.id}'>${quotation?.description}</option>`,
|
||||
);
|
||||
}
|
||||
});
|
||||
|
||||
$("#addQuotationSelect").select2();
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
$(document).on("change", ".propagate-date-input", function () {
|
||||
@@ -2369,25 +2004,3 @@ $(document).on("click", ".save-common-note-btn", function () {
|
||||
markUnsaved();
|
||||
}
|
||||
});
|
||||
|
||||
$(document).on("click", "#showHideImageBtn", function () {
|
||||
let mainRow = $(this).closest(".parts-row");
|
||||
let photoContainer = mainRow.find(".col-md-3");
|
||||
let tableContainer = mainRow
|
||||
.find("#partsTable")
|
||||
.closest("div[class*='col-md']");
|
||||
|
||||
if (photoContainer.hasClass("d-none")) {
|
||||
photoContainer.removeClass("d-none");
|
||||
tableContainer.removeClass("col-md-12").addClass("col-md-9");
|
||||
$(this).html(
|
||||
"<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>",
|
||||
);
|
||||
} else {
|
||||
photoContainer.addClass("d-none");
|
||||
tableContainer.removeClass("col-md-9").addClass("col-md-12");
|
||||
$(this).html(
|
||||
"<i class='fas fa-eye' style='font-size: 0.8rem;'></i><i class='fas fa-image ms-1' style='font-size: 0.8rem;'></i>",
|
||||
);
|
||||
}
|
||||
});
|
||||
|
||||
+4
-165
@@ -1,5 +1,5 @@
|
||||
document.addEventListener("DOMContentLoaded", function () {
|
||||
// Funzione per caricare il contenuto del popup (exported for external use)
|
||||
// Funzione per caricare il contenuto del popup
|
||||
async function loadPopupContent(iddatadb, idquotations) {
|
||||
const popupContent = document.getElementById("popupContent");
|
||||
if (!popupContent) {
|
||||
@@ -349,69 +349,6 @@ document.addEventListener("DOMContentLoaded", function () {
|
||||
});
|
||||
});
|
||||
|
||||
document
|
||||
.querySelectorAll(".photo-flag-checkbox")
|
||||
.forEach((checkbox) => {
|
||||
checkbox.addEventListener("change", async function () {
|
||||
const photoId = this.getAttribute("data-photo-id");
|
||||
const field = this.getAttribute("data-field");
|
||||
const value = this.checked ? 1 : 0;
|
||||
const currentCheckbox = this;
|
||||
|
||||
// Only one PrimaPagina visually in the current popup
|
||||
if (field === "PrimaPagina" && value === 1) {
|
||||
document
|
||||
.querySelectorAll(
|
||||
".photo-flag-checkbox[data-field='PrimaPagina']",
|
||||
)
|
||||
.forEach((cb) => {
|
||||
if (cb !== currentCheckbox) {
|
||||
cb.checked = false;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
try {
|
||||
const formData = new URLSearchParams();
|
||||
formData.append("photo_id", photoId);
|
||||
formData.append("field", field);
|
||||
formData.append("value", value);
|
||||
|
||||
const response = await fetch("update_photo_flags.php", {
|
||||
method: "POST",
|
||||
headers: {
|
||||
"Content-Type":
|
||||
"application/x-www-form-urlencoded",
|
||||
},
|
||||
body: formData.toString(),
|
||||
});
|
||||
|
||||
const result = await response.json();
|
||||
|
||||
if (!result.success) {
|
||||
throw new Error(
|
||||
result.message || "Errore salvataggio flag",
|
||||
);
|
||||
}
|
||||
|
||||
// Safety refresh for PrimaPagina to ensure UI matches DB
|
||||
if (field === "PrimaPagina") {
|
||||
loadPopupContent(iddatadb, idquotations);
|
||||
}
|
||||
} catch (error) {
|
||||
alert(
|
||||
"Errore durante il salvataggio del flag: " +
|
||||
error.message,
|
||||
);
|
||||
|
||||
// rollback current checkbox
|
||||
currentCheckbox.checked = !currentCheckbox.checked;
|
||||
|
||||
// reload popup to restore coherent state
|
||||
loadPopupContent(iddatadb, idquotations);
|
||||
}
|
||||
});
|
||||
});
|
||||
document.querySelectorAll(".thumbnail").forEach((img) => {
|
||||
img.addEventListener("click", function () {
|
||||
const enlargedImage = document.getElementById("enlargedImage");
|
||||
@@ -475,11 +412,6 @@ document.addEventListener("DOMContentLoaded", function () {
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
if (canvas) {
|
||||
canvas.dispose();
|
||||
}
|
||||
|
||||
canvas = new fabric.Canvas("collageCanvas", {
|
||||
backgroundColor: "#fff",
|
||||
selection: true,
|
||||
@@ -588,7 +520,6 @@ document.addEventListener("DOMContentLoaded", function () {
|
||||
if (isCropping || isRemovingBackground) {
|
||||
return;
|
||||
}
|
||||
|
||||
const state = JSON.stringify(
|
||||
canvas.toJSON([
|
||||
"cornerColor",
|
||||
@@ -596,16 +527,12 @@ document.addEventListener("DOMContentLoaded", function () {
|
||||
"cornerSize",
|
||||
"borderColor",
|
||||
"transparentCorners",
|
||||
"backgroundImage",
|
||||
]),
|
||||
);
|
||||
|
||||
history.push(state);
|
||||
|
||||
if (history.length > maxHistory) {
|
||||
history.shift();
|
||||
}
|
||||
|
||||
updateButtons();
|
||||
}
|
||||
|
||||
@@ -613,11 +540,10 @@ document.addEventListener("DOMContentLoaded", function () {
|
||||
if (history.length <= 1) {
|
||||
return;
|
||||
}
|
||||
|
||||
history.pop();
|
||||
const previousState = history[history.length - 1];
|
||||
|
||||
if (previousState) {
|
||||
canvas.clear();
|
||||
canvas.loadFromJSON(previousState, () => {
|
||||
canvas.renderAll();
|
||||
updateLayersPanel();
|
||||
@@ -626,11 +552,8 @@ document.addEventListener("DOMContentLoaded", function () {
|
||||
} else {
|
||||
console.warn("Nessuno stato precedente disponibile");
|
||||
canvas.clear();
|
||||
canvas.backgroundImage = null;
|
||||
canvas.setBackgroundColor(
|
||||
"#fff",
|
||||
canvas.renderAll.bind(canvas),
|
||||
);
|
||||
canvas.setBackgroundColor("#fff");
|
||||
canvas.renderAll();
|
||||
updateLayersPanel();
|
||||
updateButtons();
|
||||
}
|
||||
@@ -923,61 +846,6 @@ document.addEventListener("DOMContentLoaded", function () {
|
||||
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");
|
||||
if (addToCanvasBtn) {
|
||||
addToCanvasBtn.addEventListener("click", () => {
|
||||
@@ -1017,32 +885,6 @@ 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");
|
||||
if (saveCollageBtn) {
|
||||
saveCollageBtn.addEventListener("click", async () => {
|
||||
@@ -1218,7 +1060,4 @@ document.addEventListener("DOMContentLoaded", function () {
|
||||
closeBtn,
|
||||
});
|
||||
}
|
||||
|
||||
// Export for external use (gridData pages)
|
||||
window.loadPopupContent = loadPopupContent;
|
||||
});
|
||||
|
||||
@@ -117,24 +117,12 @@ $code = $row[$field] ?? 'Non disponibile';
|
||||
|
||||
// Recupera le foto associate alla riga
|
||||
try {
|
||||
$stmt = $pdo->prepare("
|
||||
SELECT
|
||||
id,
|
||||
file_path,
|
||||
file_name,
|
||||
uploaded_at,
|
||||
description,
|
||||
StampaNelRapporto,
|
||||
PrimaPagina
|
||||
FROM {$photoTable}
|
||||
WHERE {$photoParamName} = ?
|
||||
ORDER BY uploaded_at DESC
|
||||
");
|
||||
$stmt = $pdo->prepare("SELECT id, file_path, file_name, description, uploaded_at FROM {$photoTable} WHERE {$photoParamName} = ? ORDER BY uploaded_at DESC");
|
||||
$stmt->execute([$paramValue]);
|
||||
$photos = $stmt->fetchAll(PDO::FETCH_ASSOC);
|
||||
} catch (Exception $e) {
|
||||
error_log("Errore query foto: " . $e->getMessage());
|
||||
$photos = [];
|
||||
$photos = []; // Imposta array vuoto in caso di errore
|
||||
}
|
||||
|
||||
// Definisci il percorso base per le foto
|
||||
@@ -146,7 +134,7 @@ $uploadUrl = $iddatadb
|
||||
? $baseUrl . "/upload_photos_mobile.php?iddatadb=" . $iddatadb
|
||||
: $baseUrl . "/upload_photos_mobile.php?idquotations=" . $idquotations;
|
||||
|
||||
// Genera il QR code con endroid/qr-code
|
||||
// Genera il QR code con endroid/qr-code 6.0.6
|
||||
$qrCodeDir = '../photostrf/qrcodes/';
|
||||
if (!is_dir($qrCodeDir)) {
|
||||
mkdir($qrCodeDir, 0755, true);
|
||||
@@ -176,7 +164,6 @@ $result->saveToFile($qrCodeFile);
|
||||
<p>Caricamento in corso...</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<h3>Manage Photos</h3>
|
||||
<p><strong>ID:</strong> <?= htmlspecialchars($id) ?></p>
|
||||
<p><strong>Code:</strong> <?= htmlspecialchars($code) ?></p>
|
||||
@@ -195,7 +182,6 @@ $result->saveToFile($qrCodeFile);
|
||||
<p>Drag the photo here or click to select</p>
|
||||
<input type="file" id="photoInput" multiple accept="image/*" style="display: none;">
|
||||
</div>
|
||||
|
||||
<!-- Area per la webcam -->
|
||||
<div id="webcamArea" style="display: none; text-align: center; margin-bottom: 20px;">
|
||||
<p>Webcam Preview</p>
|
||||
@@ -208,9 +194,7 @@ $result->saveToFile($qrCodeFile);
|
||||
<button id="closeWebcamBtn" style="padding: 10px 20px; background: #dc3545; color: white; border: none; cursor: pointer;">Close Webcam</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<button id="openWebcamBtn" style="padding: 10px 20px; background: #007bff; color: white; border: none; cursor: pointer; margin-bottom: 20px;">Take Photo with Webcam</button>
|
||||
|
||||
<!-- Elenco delle foto -->
|
||||
<div id="photosList">
|
||||
<?php if (empty($photos)): ?>
|
||||
@@ -220,57 +204,25 @@ $result->saveToFile($qrCodeFile);
|
||||
<?php
|
||||
$filePath = $photoBasePath . $photo['file_path'];
|
||||
$fileExists = file_exists($filePath);
|
||||
$stampaNelRapporto = !empty($photo['StampaNelRapporto']) ? 1 : 0;
|
||||
$primaPagina = !empty($photo['PrimaPagina']) ? 1 : 0;
|
||||
?>
|
||||
<div class="photo-item" data-photo-id="<?= (int)$photo['id'] ?>">
|
||||
<div class="photo-thumb-area">
|
||||
<div class="photo-item" data-photo-id="<?= $photo['id'] ?>" style="display: flex; align-items: center; margin-bottom: 10px; border-bottom: 1px solid #eee; padding-bottom: 10px;">
|
||||
<div style="flex: 1;">
|
||||
<?php if ($fileExists): ?>
|
||||
<img src="../photostrf/<?= htmlspecialchars($photo['file_path']) ?>"
|
||||
alt="<?= htmlspecialchars($photo['file_name']) ?>"
|
||||
class="thumbnail"
|
||||
style="max-width: 100px; max-height: 100px; cursor: pointer;">
|
||||
<img src="../photostrf/<?= htmlspecialchars($photo['file_path']) ?>" alt="<?= htmlspecialchars($photo['file_name']) ?>" class="thumbnail" style="max-width: 100px; max-height: 100px; margin-right: 10px; cursor: pointer;">
|
||||
<?php else: ?>
|
||||
<div class="missing-file-box">[File non trovato]</div>
|
||||
<p style="color: red;">[File non trovato]</p>
|
||||
<?php endif; ?>
|
||||
</div>
|
||||
|
||||
<div class="photo-info-area">
|
||||
<p class="photo-name">
|
||||
<strong>Name:</strong> <?= htmlspecialchars($photo['file_name']) ?>
|
||||
<p style="margin: 0;">
|
||||
<strong>Nome:</strong> <?= htmlspecialchars($photo['file_name']) ?><br>
|
||||
<strong>Caricata il:</strong> <?= htmlspecialchars($photo['uploaded_at']) ?><br>
|
||||
<strong>Descrizione:</strong> <?= htmlspecialchars($photo['description'] ?? 'Nessuna descrizione') ?>
|
||||
</p>
|
||||
|
||||
<div class="photo-flags">
|
||||
<label class="flag-label">
|
||||
<input
|
||||
type="checkbox"
|
||||
class="photo-flag-checkbox"
|
||||
data-photo-id="<?= (int)$photo['id'] ?>"
|
||||
data-field="StampaNelRapporto"
|
||||
<?= $stampaNelRapporto ? 'checked' : '' ?>>
|
||||
Print in Report
|
||||
</label>
|
||||
|
||||
<label class="flag-label">
|
||||
<input
|
||||
type="checkbox"
|
||||
class="photo-flag-checkbox prima-pagina-checkbox"
|
||||
data-photo-id="<?= (int)$photo['id'] ?>"
|
||||
data-field="PrimaPagina"
|
||||
<?= $primaPagina ? 'checked' : '' ?>>
|
||||
First Page
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="photo-actions-area">
|
||||
<button class="delete-photo-btn" data-photo-id="<?= (int)$photo['id'] ?>" type="button">
|
||||
<i class="fas fa-trash"></i>
|
||||
</button>
|
||||
</div>
|
||||
<button class="delete-photo-btn" data-photo-id="<?= $photo['id'] ?>" style="background: none; border: none; color: #dc3545; cursor: pointer;">
|
||||
<i class="fas fa-trash"></i>
|
||||
</button>
|
||||
</div>
|
||||
<?php endforeach; ?>
|
||||
|
||||
<!-- Bottone Crea Collage -->
|
||||
<button id="createCollageBtn" style="padding: 10px 20px; background: #ffc107; color: white; border: none; cursor: pointer; margin-top: 20px;">Crea Collage</button>
|
||||
<?php endif; ?>
|
||||
@@ -298,11 +250,8 @@ $result->saveToFile($qrCodeFile);
|
||||
<?php endforeach; ?>
|
||||
</div>
|
||||
|
||||
<!-- Bottoni azioni collage -->
|
||||
<div style="display: flex; gap: 10px; flex-wrap: wrap; margin-bottom: 10px;">
|
||||
<button id="addToCanvasBtn">Aggiungi Selezionate al Canvas</button>
|
||||
<button id="setBackgroundBtn" type="button">Imposta Selezionata come Sfondo</button>
|
||||
</div>
|
||||
<!-- Bottone per aggiungere selezionate al canvas -->
|
||||
<button id="addToCanvasBtn">Aggiungi Selezionate al Canvas</button>
|
||||
|
||||
<!-- Canvas per editing -->
|
||||
<canvas id="collageCanvas" width="960" height="720" style="border: 1px solid #ccc; margin-top: 20px;"></canvas>
|
||||
@@ -334,12 +283,6 @@ $result->saveToFile($qrCodeFile);
|
||||
|
||||
<style>
|
||||
.photo-item {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 15px;
|
||||
margin-bottom: 12px;
|
||||
border-bottom: 1px solid #eee;
|
||||
padding: 12px 0;
|
||||
transition: background-color 0.3s;
|
||||
}
|
||||
|
||||
@@ -347,68 +290,6 @@ $result->saveToFile($qrCodeFile);
|
||||
background-color: #f8f9fa;
|
||||
}
|
||||
|
||||
.photo-thumb-area {
|
||||
flex: 0 0 110px;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.photo-info-area {
|
||||
flex: 1;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 8px;
|
||||
}
|
||||
|
||||
.photo-actions-area {
|
||||
flex: 0 0 40px;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.photo-name {
|
||||
margin: 0;
|
||||
word-break: break-word;
|
||||
}
|
||||
|
||||
.photo-flags {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
gap: 18px;
|
||||
margin-top: 4px;
|
||||
}
|
||||
|
||||
.flag-label {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 6px;
|
||||
margin: 0;
|
||||
font-size: 14px;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.flag-label input[type="checkbox"] {
|
||||
transform: scale(1.1);
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.delete-photo-btn {
|
||||
background: none;
|
||||
border: none;
|
||||
color: #dc3545;
|
||||
cursor: pointer;
|
||||
font-size: 18px;
|
||||
}
|
||||
|
||||
.delete-photo-btn:hover {
|
||||
color: #b02a37;
|
||||
}
|
||||
|
||||
.missing-file-box {
|
||||
color: red;
|
||||
font-size: 13px;
|
||||
}
|
||||
|
||||
#dropArea.highlight {
|
||||
border-color: #28a745;
|
||||
background-color: #e9ecef;
|
||||
|
||||
@@ -70,7 +70,7 @@ try {
|
||||
$columnLetter = \PhpOffice\PhpSpreadsheet\Cell\Coordinate::stringFromColumnIndex($col);
|
||||
$cell = $worksheet->getCell($columnLetter . $header_row);
|
||||
$cellValue = $cell ? $cell->getCalculatedValue() : ''; // Usa getCalculatedValue per le formule
|
||||
$headerRowData[] = $cellValue ?: '';
|
||||
$headerRowData[] = htmlspecialchars($cellValue ?: '');
|
||||
}
|
||||
|
||||
// Estrai i dati a partire dalla riga successiva
|
||||
@@ -80,7 +80,7 @@ try {
|
||||
$columnLetter = \PhpOffice\PhpSpreadsheet\Cell\Coordinate::stringFromColumnIndex($col);
|
||||
$cell = $worksheet->getCell($columnLetter . $row);
|
||||
$cellValue = $cell ? $cell->getCalculatedValue() : ''; // Usa getCalculatedValue per le formule
|
||||
$rowData[] = $cellValue ?: '';
|
||||
$rowData[] = htmlspecialchars($cellValue ?: '');
|
||||
}
|
||||
if (!empty(array_filter($rowData))) {
|
||||
$excelData[] = $rowData;
|
||||
|
||||
@@ -74,16 +74,6 @@ try {
|
||||
$startRow = max(1, $header_row);
|
||||
$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
|
||||
error_log("Processing - template_id: $template_id, startRow: $startRow, startColumn: $startColumn, highestRow: $highestRow, highestColumn: $highestColumn, highestColumnIndex: $highestColumnIndex");
|
||||
|
||||
@@ -94,78 +84,25 @@ try {
|
||||
$response['error'] = "La colonna di partenza ($startColumn) supera il numero totale di colonne ($highestColumnIndex).";
|
||||
} else {
|
||||
$excelData = [];
|
||||
|
||||
// Build merge map for header row: physCol -> mergeStartCol
|
||||
$mergeStartMap = [];
|
||||
foreach ($worksheet->getMergeCells() as $range) {
|
||||
[$startCell, $endCell] = explode(':', $range);
|
||||
$mStartCol = \PhpOffice\PhpSpreadsheet\Cell\Coordinate::columnIndexFromString(preg_replace('/\d+/', '', $startCell));
|
||||
$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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 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
|
||||
// Estrai la riga degli header
|
||||
$headerRowData = [];
|
||||
$logicalNum = 0;
|
||||
foreach ($logicalCols as $physCol) {
|
||||
$logicalNum++;
|
||||
$columnLetter = \PhpOffice\PhpSpreadsheet\Cell\Coordinate::stringFromColumnIndex($physCol);
|
||||
for ($col = $startColumn; $col <= $highestColumnIndex; $col++) {
|
||||
$columnLetter = \PhpOffice\PhpSpreadsheet\Cell\Coordinate::stringFromColumnIndex($col);
|
||||
$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 . '__';
|
||||
$cellValue = $cell ? $cell->getCalculatedValue() : '';
|
||||
$headerRowData[] = htmlspecialchars($cellValue ?: '');
|
||||
}
|
||||
|
||||
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
|
||||
// Estrai i dati a partire dalla riga successiva, includendo excelrow
|
||||
for ($row = $startRow + 1; $row <= $highestRow; $row++) {
|
||||
$rowData = [];
|
||||
foreach ($logicalCols as $physCol) {
|
||||
$columnLetter = \PhpOffice\PhpSpreadsheet\Cell\Coordinate::stringFromColumnIndex($physCol);
|
||||
for ($col = $startColumn; $col <= $highestColumnIndex; $col++) {
|
||||
$columnLetter = \PhpOffice\PhpSpreadsheet\Cell\Coordinate::stringFromColumnIndex($col);
|
||||
$cell = $worksheet->getCell($columnLetter . $row);
|
||||
$cellValue = $cell ? $cell->getCalculatedValue() : '';
|
||||
$rowData[] = $cellValue ?: '';
|
||||
$rowData[] = htmlspecialchars($cellValue ?: '');
|
||||
}
|
||||
|
||||
// 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) {
|
||||
if (!empty(array_filter($rowData))) {
|
||||
$excelData[] = ['data' => $rowData, 'excelrow' => $row];
|
||||
}
|
||||
}
|
||||
|
||||
@@ -399,18 +399,14 @@ if (isset($_GET['edit_id'])) {
|
||||
<a href="javaScript:;" class="back-to-top"><i class='bx bxs-up-arrow-alt'></i></a>
|
||||
<?php include('include/footer.php'); ?>
|
||||
</div>
|
||||
<div id="partsModalContainer"></div>
|
||||
<div id="annotationsModalContainer"></div>
|
||||
<?php include 'photos_functions.php'; ?>
|
||||
|
||||
<?php include('modal_parts.php'); ?>
|
||||
<?php include('photos_functions.php'); ?>
|
||||
<?php include('jsinclude.php'); ?>
|
||||
<script src="https://code.jquery.com/jquery-3.6.0.min.js"></script>
|
||||
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/js/bootstrap.bundle.min.js"></script>
|
||||
<script src="https://cdn.datatables.net/1.13.4/js/jquery.dataTables.min.js"></script>
|
||||
<script src="photos.js"></script>
|
||||
<script src="annotationsModal.js"></script>
|
||||
<script src="partsTable.js"></script>
|
||||
|
||||
<script src="parts.js"></script>
|
||||
<script>
|
||||
document.addEventListener("DOMContentLoaded", function() {
|
||||
// Mostra messaggi di stato se presenti
|
||||
@@ -427,45 +423,6 @@ if (isset($_GET['edit_id'])) {
|
||||
}, 5000);
|
||||
}
|
||||
|
||||
$(document).on('click', '.parts-btn', function() {
|
||||
const idquotations = $(this).data('idquotations');
|
||||
$.ajax({
|
||||
url: 'modal_partsTable.php',
|
||||
method: 'GET',
|
||||
data: {
|
||||
idquotations: idquotations
|
||||
},
|
||||
success: function(response) {
|
||||
$('#partsModalContainer').html(response);
|
||||
const modalElement = document.getElementById('partsModal');
|
||||
if (!modalElement) return;
|
||||
$("#trfHeader").text(`Quotation #${idquotations}`);
|
||||
$("#partsModal").data("idquotations", idquotations);
|
||||
let modal = bootstrap.Modal.getInstance(modalElement) || new bootstrap.Modal(modalElement, {
|
||||
backdrop: true
|
||||
});
|
||||
modal.show();
|
||||
if (typeof window.loadParts === 'function') window.loadParts(null, idquotations);
|
||||
},
|
||||
error: function(xhr, status, error) {
|
||||
alert('Errore nel caricamento del modale: ' + error);
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
$(document).on('hidden.bs.modal', '#partsModal', function() {
|
||||
$('#partsModalContainer').empty();
|
||||
$('.modal-backdrop').remove();
|
||||
$('body').removeClass('modal-open').css('padding-right', '');
|
||||
});
|
||||
|
||||
$(document).on('hidden.bs.modal', '#annotationsModal', function() {
|
||||
$('#annotationsModalContainer').empty();
|
||||
$('.modal-backdrop').remove();
|
||||
$('body').removeClass('modal-open').css('padding-right', '');
|
||||
});
|
||||
|
||||
|
||||
// Inizializza DataTables se non siamo in modalità modifica
|
||||
if (!document.querySelector('#editForm')) {
|
||||
$('#quotationsTable').DataTable({
|
||||
@@ -567,7 +524,12 @@ if (isset($_GET['edit_id'])) {
|
||||
</script>
|
||||
|
||||
<!-- Modale per le foto in quotations.php -->
|
||||
|
||||
<div class="modal" id="photosModal">
|
||||
<div class="modal-content">
|
||||
<span class="close-btn">×</span>
|
||||
<div class="popup-content"></div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</body>
|
||||
|
||||
|
||||
@@ -1,130 +0,0 @@
|
||||
/**
|
||||
* 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) {
|
||||
if ($key !== 'iddatadb' && !in_array($key, $excludeFields)) {
|
||||
$updates[] = "$key = ?";
|
||||
$values[] = $value;
|
||||
$values[] = htmlspecialchars($value);
|
||||
}
|
||||
}
|
||||
$values[] = $iddatadb;
|
||||
|
||||
@@ -12,75 +12,9 @@ try {
|
||||
|
||||
$iddatadb = intval($_POST['iddatadb']);
|
||||
$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;
|
||||
|
||||
$db = DBHandlerSelect::getInstance();
|
||||
$db = DBHandlerSelect::getInstance();
|
||||
$pdo = $db->getConnection();
|
||||
|
||||
// ---------------- FIXED FIELDS (template_fixed_mapping) ----------------
|
||||
|
||||
// ALIAS: fixed_field_key "logico" -> colonna reale su datadb
|
||||
// (NON tocchiamo MySQL, gestiamo solo qui la traduzione)
|
||||
$fixedAliasMap = [
|
||||
'ClienteResponsabile' => 'cliente_responsabile_id',
|
||||
'ClienteFornitore' => 'cliente_fornitore_id',
|
||||
'ClienteAnalisi' => 'clienteAnalisi',
|
||||
'MoltiplicatorePrezzo' => 'moltiplicatore_prezzo_id',
|
||||
'AnagraficaCertestObject' => 'anagrafica_certest_object_id',
|
||||
'AnagraficaCertestService' => 'anagrafica_certest_service_id',
|
||||
'ConsegnaRichiesta' => 'consegna_richiesta',
|
||||
];
|
||||
|
||||
// 1) Recupera templateid dalla riga datadb (serve per sapere quali fixed_field_key sono permessi)
|
||||
$stmtTpl = $pdo->prepare("SELECT templateid FROM datadb WHERE iddatadb = ?");
|
||||
$stmtTpl->execute([$iddatadb]);
|
||||
$tplRow = $stmtTpl->fetch(PDO::FETCH_ASSOC);
|
||||
|
||||
$templateId = isset($tplRow['templateid']) ? (int)$tplRow['templateid'] : 0;
|
||||
if ($templateId <= 0) {
|
||||
throw new Exception("Template non trovato per iddatadb=$iddatadb");
|
||||
}
|
||||
|
||||
// 2) Recupera elenco fixed fields visibili per quel template
|
||||
$fxStmt = $pdo->prepare("
|
||||
SELECT fixed_field_key, data_type, is_required, default_value
|
||||
FROM template_fixed_mapping
|
||||
WHERE template_id = ? AND is_visible_import = 1
|
||||
");
|
||||
$fxStmt->execute([$templateId]);
|
||||
$fixedList = $fxStmt->fetchAll(PDO::FETCH_ASSOC);
|
||||
|
||||
// 3) Crea whitelist LOGICA: fixed_field_key => metadata
|
||||
$fixedWhitelist = [];
|
||||
foreach ($fixedList as $fx) {
|
||||
$k = (string)$fx['fixed_field_key'];
|
||||
|
||||
// sicurezza: key ammessa solo se "safe"
|
||||
if (!preg_match('/^[a-zA-Z0-9_]+$/', $k)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$fixedWhitelist[$k] = [
|
||||
'data_type' => (string)$fx['data_type'], // INT | DATE
|
||||
'is_required' => (int)$fx['is_required'],
|
||||
'default_value' => $fx['default_value'] ?? null
|
||||
];
|
||||
}
|
||||
|
||||
// 3b) Crea whitelist REALE: colonna datadb reale => metadata
|
||||
$realWhitelist = [];
|
||||
foreach ($fixedWhitelist as $logicalKey => $meta) {
|
||||
$realCol = $fixedAliasMap[$logicalKey] ?? $logicalKey;
|
||||
|
||||
// sicurezza: anche la colonna reale deve essere "safe"
|
||||
if (!preg_match('/^[a-zA-Z0-9_]+$/', $realCol)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$realWhitelist[$realCol] = $meta;
|
||||
}
|
||||
|
||||
// ---------------- DETAILS (import_data_details) ----------------
|
||||
$data = $_POST;
|
||||
$details = [];
|
||||
|
||||
@@ -122,88 +56,27 @@ try {
|
||||
|
||||
foreach ($changed as $mappingId => $values) {
|
||||
$updateStmt->execute([
|
||||
':newValue' => $values['new'],
|
||||
':iddatadb' => $iddatadb,
|
||||
':mappingId' => $mappingId
|
||||
':newValue' => $values['new'],
|
||||
':iddatadb' => $iddatadb,
|
||||
':mappingId' => $mappingId
|
||||
]);
|
||||
}
|
||||
}
|
||||
|
||||
// ---------------- UPDATE datadb: idclient + FIXED FIELDS ----------------
|
||||
$setParts = [];
|
||||
$params = [];
|
||||
|
||||
// 5a) idclient (se presente)
|
||||
// 5. Aggiorna idclient in datadb
|
||||
if (isset($idclient)) {
|
||||
$setParts[] = "idclient = ?";
|
||||
$params[] = $idclient;
|
||||
}
|
||||
|
||||
// 5a2) cliente_fornitore_id (se presente)
|
||||
if (isset($clienteFornitoreId)) {
|
||||
$setParts[] = "cliente_fornitore_id = ?";
|
||||
$params[] = $clienteFornitoreId;
|
||||
}
|
||||
|
||||
// 5b) fixed fields dal POST
|
||||
// QUI è il punto chiave: accettiamo SOLO colonne reali (realWhitelist),
|
||||
// ma se dal JS arrivassero ancora key logiche, funzionerebbe uguale
|
||||
// perché sotto controlliamo anche l'alias.
|
||||
foreach ($realWhitelist as $realCol => $meta) {
|
||||
|
||||
$postKeyToRead = null;
|
||||
|
||||
// Caso 1: POST contiene già la colonna reale
|
||||
if (array_key_exists($realCol, $_POST)) {
|
||||
$postKeyToRead = $realCol;
|
||||
} else {
|
||||
// Caso 2: POST contiene la key logica -> troviamo quale logica mappa su questa colonna reale
|
||||
// (fallback, utile se non hai ancora aggiornato il JS)
|
||||
foreach ($fixedAliasMap as $logical => $mappedReal) {
|
||||
if ($mappedReal === $realCol && array_key_exists($logical, $_POST)) {
|
||||
$postKeyToRead = $logical;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if ($postKeyToRead === null) {
|
||||
continue; // non inviato
|
||||
}
|
||||
|
||||
$val = $_POST[$postKeyToRead];
|
||||
|
||||
// Normalizzazione per tipo
|
||||
if ($meta['data_type'] === 'DATE') {
|
||||
$val = trim((string)$val);
|
||||
$val = ($val === '') ? null : $val; // atteso formato Y-m-d
|
||||
} else { // INT
|
||||
$val = trim((string)$val);
|
||||
$val = ($val === '') ? null : (int)$val;
|
||||
}
|
||||
|
||||
$setParts[] = "`$realCol` = ?";
|
||||
$params[] = $val;
|
||||
}
|
||||
|
||||
// esegui update solo se c'è qualcosa da aggiornare
|
||||
if (!empty($setParts)) {
|
||||
$params[] = $iddatadb;
|
||||
|
||||
$sqlUpd = "UPDATE datadb SET " . implode(", ", $setParts) . " WHERE iddatadb = ?";
|
||||
$updStmt = $pdo->prepare($sqlUpd);
|
||||
$updStmt->execute($params);
|
||||
}
|
||||
|
||||
// Messaggio risposta (mantengo la tua logica ma includo fixed)
|
||||
if (!empty($setParts) && !empty($changed)) {
|
||||
$response['message'] = "Updated details and datadb fields successfully";
|
||||
} elseif (!empty($setParts)) {
|
||||
$response['message'] = "Updated datadb fields successfully";
|
||||
} elseif (!empty($changed)) {
|
||||
$response['message'] = "Updated details successfully";
|
||||
$updateStmt = $pdo->prepare("
|
||||
UPDATE datadb
|
||||
SET idclient = :idclient
|
||||
WHERE iddatadb = :iddatadb
|
||||
");
|
||||
$updateStmt->execute([
|
||||
':idclient' => $idclient,
|
||||
':iddatadb' => $iddatadb
|
||||
]);
|
||||
$response['message'] = !empty($changed) ? "Updated details and idclient successfully" : "Updated idclient successfully";
|
||||
} else {
|
||||
$response['message'] = "No changes found";
|
||||
$response['message'] = !empty($changed) ? "Updated details successfully" : "No changes found";
|
||||
}
|
||||
|
||||
$response['success'] = true;
|
||||
|
||||
@@ -20,82 +20,19 @@ $mappingId = $data['id'];
|
||||
$mappingType = $data['mapping_type'] ?? '';
|
||||
$excelColumn = $data['excel_column'] ?? null;
|
||||
$manualDefault = $data['manual_default'] ?? null;
|
||||
|
||||
$autoValue = $data['auto_value'] ?? 'none';
|
||||
$tablename = $data['tablename'] ?? '';
|
||||
|
||||
try {
|
||||
// Normalize mapping type
|
||||
$allowedTypes = ['', 'xls', 'manual', 'auto'];
|
||||
if (!in_array($mappingType, $allowedTypes, true)) {
|
||||
echo json_encode(["success" => false, "message" => "Invalid mapping_type"]);
|
||||
exit;
|
||||
}
|
||||
|
||||
// Normalize auto_value
|
||||
$allowedAuto = ['none', 'import_date', 'import_time', 'export_date', 'export_time'];
|
||||
if (!in_array($autoValue, $allowedAuto, true)) {
|
||||
$autoValue = 'none';
|
||||
}
|
||||
|
||||
// Decide what to persist based on mapping_type
|
||||
$isManual = 0;
|
||||
$excelToSave = null;
|
||||
$manualToSave = null;
|
||||
$autoToSave = 'none';
|
||||
|
||||
if ($mappingType === 'xls') {
|
||||
$isManual = 0;
|
||||
$excelToSave = $excelColumn ?: null;
|
||||
$manualToSave = null;
|
||||
$autoToSave = 'none';
|
||||
} elseif ($mappingType === 'manual') {
|
||||
$isManual = 1;
|
||||
$excelToSave = null;
|
||||
$manualToSave = $manualDefault;
|
||||
$autoToSave = 'none';
|
||||
} elseif ($mappingType === 'auto') {
|
||||
$isManual = 0;
|
||||
$excelToSave = null;
|
||||
$manualToSave = null;
|
||||
$autoToSave = $autoValue ?: 'none';
|
||||
} else {
|
||||
// reset
|
||||
$isManual = 0;
|
||||
$excelToSave = null;
|
||||
$manualToSave = null;
|
||||
$autoToSave = 'none';
|
||||
}
|
||||
|
||||
$stmt = $pdo->prepare("
|
||||
UPDATE template_mapping
|
||||
SET
|
||||
is_manual = ?,
|
||||
excel_column = ?,
|
||||
manual_default = ?,
|
||||
auto_value = ?
|
||||
WHERE id = ?
|
||||
");
|
||||
|
||||
$result = $stmt->execute([$isManual, $excelToSave, $manualToSave, $autoToSave, $mappingId]);
|
||||
$stmt = $pdo->prepare("UPDATE template_mapping SET is_manual = ?, excel_column = ?, manual_default = ? WHERE id = ?");
|
||||
$isManual = ($mappingType === 'manual') ? 1 : 0;
|
||||
$result = $stmt->execute([$isManual, $excelColumn, $manualDefault, $mappingId]);
|
||||
|
||||
if (!$result) {
|
||||
echo json_encode(["success" => false, "message" => "Database update failed"]);
|
||||
exit;
|
||||
}
|
||||
|
||||
echo json_encode([
|
||||
"success" => true,
|
||||
"message" => "Mapping updated successfully",
|
||||
"saved" => [
|
||||
"id" => (int)$mappingId,
|
||||
"mapping_type" => $mappingType,
|
||||
"is_manual" => $isManual,
|
||||
"excel_column" => $excelToSave,
|
||||
"manual_default" => $manualToSave,
|
||||
"auto_value" => $autoToSave
|
||||
]
|
||||
]);
|
||||
echo json_encode(["success" => true, "message" => "Mapping updated successfully", "data" => $data]); // Aggiunto debug
|
||||
} catch (Exception $e) {
|
||||
echo json_encode(["success" => false, "message" => "Error: " . $e->getMessage()]);
|
||||
}
|
||||
|
||||
+39
-128
@@ -4,7 +4,6 @@ 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);
|
||||
|
||||
@@ -20,21 +19,6 @@ try {
|
||||
$pdo->beginTransaction();
|
||||
$results = [];
|
||||
|
||||
// Custom fields statements (child table)
|
||||
$stmtUpsertCF = $pdo->prepare("
|
||||
INSERT INTO identification_parts_customfields (part_id, field_id, value_id, value_text)
|
||||
VALUES (:part_id, :field_id, :value_id, :value_text)
|
||||
ON DUPLICATE KEY UPDATE
|
||||
value_id = VALUES(value_id),
|
||||
value_text = VALUES(value_text),
|
||||
updated_at = NOW()
|
||||
");
|
||||
|
||||
$stmtDeleteCF = $pdo->prepare("
|
||||
DELETE FROM identification_parts_customfields
|
||||
WHERE part_id = :part_id AND field_id = :field_id
|
||||
");
|
||||
|
||||
foreach ($parts as $part) {
|
||||
$partId = $part['id'] ?? null;
|
||||
$partNumber = $part['part_number'] ?? null;
|
||||
@@ -44,123 +28,50 @@ try {
|
||||
$note = $part['note'] ?? null;
|
||||
$dateexpiry = $part['dateexpiry'] ?? null;
|
||||
|
||||
// Extra field (0/1)
|
||||
$extraFieldId = $part['extra_field_id'] ?? null;
|
||||
$extraValueId = $part['extra_value_id'] ?? null;
|
||||
$extraValueText = $part['extra_value_text'] ?? null;
|
||||
|
||||
// Normalizza vuoti
|
||||
if ($extraFieldId !== null && $extraFieldId !== '') $extraFieldId = (int)$extraFieldId;
|
||||
else $extraFieldId = null;
|
||||
if ($extraValueId !== null && $extraValueId !== '') $extraValueId = (int)$extraValueId;
|
||||
else $extraValueId = null;
|
||||
if ($extraValueText !== null) {
|
||||
$extraValueText = trim((string)$extraValueText);
|
||||
if ($extraValueText === '') $extraValueText = null;
|
||||
}
|
||||
|
||||
if ($partId) {
|
||||
// UPDATE se la parte esiste (sempre)
|
||||
$stmt = $pdo->prepare("UPDATE identification_parts
|
||||
SET part_number = :part_number,
|
||||
part_description = :part_description,
|
||||
mix = :mix,
|
||||
idmatrice = :idmatrice,
|
||||
note = :note,
|
||||
dateexpiry = :dateexpiry,
|
||||
updated_at = NOW()
|
||||
WHERE id = :id");
|
||||
$stmt->execute([
|
||||
':id' => $partId,
|
||||
':part_number' => $partNumber,
|
||||
':part_description' => $partDescription,
|
||||
':mix' => $mix,
|
||||
':idmatrice' => $idmatrice,
|
||||
':note' => $note,
|
||||
':dateexpiry' => $dateexpiry,
|
||||
]);
|
||||
|
||||
// Save extra custom field (if provided)
|
||||
if ($extraFieldId !== null) {
|
||||
if ($extraValueId === null && $extraValueText === null) {
|
||||
$stmtDeleteCF->execute([
|
||||
':part_id' => $partId,
|
||||
':field_id' => $extraFieldId,
|
||||
]);
|
||||
} else {
|
||||
$stmtUpsertCF->execute([
|
||||
':part_id' => $partId,
|
||||
':field_id' => $extraFieldId,
|
||||
':value_id' => $extraValueId,
|
||||
':value_text' => $extraValueText,
|
||||
]);
|
||||
}
|
||||
if ($partDescription || $note || $dateexpiry) {
|
||||
if ($partId) {
|
||||
// UPDATE se la parte esiste
|
||||
$stmt = $pdo->prepare("UPDATE identification_parts
|
||||
SET part_number = :part_number,
|
||||
part_description = :part_description,
|
||||
mix = :mix,
|
||||
idmatrice = :idmatrice,
|
||||
note = :note,
|
||||
dateexpiry = :dateexpiry,
|
||||
updated_at = NOW()
|
||||
WHERE id = :id");
|
||||
$stmt->execute([
|
||||
':id' => $partId,
|
||||
':part_number' => $partNumber,
|
||||
':part_description' => $partDescription,
|
||||
':mix' => $mix,
|
||||
':idmatrice' => $idmatrice,
|
||||
':note' => $note,
|
||||
':dateexpiry' => $dateexpiry,
|
||||
]);
|
||||
$results[] = ['part_id' => $partId, 'part_number' => $partNumber, 'message' => 'Parte aggiornata con successo'];
|
||||
} else {
|
||||
// INSERT per nuova parte
|
||||
$stmt = $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())");
|
||||
$stmt->execute([
|
||||
':iddatadb' => $iddatadb,
|
||||
':part_number' => $partNumber,
|
||||
':part_description' => $partDescription,
|
||||
':mix' => $mix,
|
||||
':idmatrice' => $idmatrice,
|
||||
':note' => $note,
|
||||
':dateexpiry' => $dateexpiry,
|
||||
]);
|
||||
$newId = $pdo->lastInsertId();
|
||||
$results[] = ['part_id' => $newId, 'part_number' => $partNumber, 'message' => 'Parte salvata con successo'];
|
||||
}
|
||||
|
||||
$cf_row = null;
|
||||
if ($extraFieldId !== null) {
|
||||
$chk = $pdo->prepare("SELECT id, value_id, value_text FROM identification_parts_customfields WHERE part_id = ? AND field_id = ?");
|
||||
$chk->execute([$partId, $extraFieldId]);
|
||||
$cf_row = $chk->fetch(PDO::FETCH_ASSOC);
|
||||
}
|
||||
|
||||
$results[] = [
|
||||
'part_id' => $partId,
|
||||
'part_number' => $partNumber,
|
||||
'message' => 'Parte aggiornata con successo',
|
||||
'cf_row' => $cf_row
|
||||
];
|
||||
} else if ($partDescription || $note || $dateexpiry) {
|
||||
// INSERT per nuova parte (solo se ha contenuto)
|
||||
$stmt = $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())");
|
||||
$stmt->execute([
|
||||
':iddatadb' => $iddatadb,
|
||||
':part_number' => $partNumber,
|
||||
':part_description' => $partDescription,
|
||||
':mix' => $mix,
|
||||
':idmatrice' => $idmatrice,
|
||||
':note' => $note,
|
||||
':dateexpiry' => $dateexpiry,
|
||||
]);
|
||||
$newId = (int)$pdo->lastInsertId();
|
||||
|
||||
if ($extraFieldId !== null) {
|
||||
if ($extraValueId === null && $extraValueText === null) {
|
||||
$stmtDeleteCF->execute([
|
||||
':part_id' => $newId,
|
||||
':field_id' => $extraFieldId,
|
||||
]);
|
||||
} else {
|
||||
$stmtUpsertCF->execute([
|
||||
':part_id' => $newId,
|
||||
':field_id' => $extraFieldId,
|
||||
':value_id' => $extraValueId,
|
||||
':value_text' => $extraValueText,
|
||||
]);
|
||||
}
|
||||
}
|
||||
|
||||
$results[] = [
|
||||
'part_id' => $newId,
|
||||
'part_number' => $partNumber,
|
||||
'message' => 'Parte salvata con successo'
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
$pdo->commit();
|
||||
echo json_encode([
|
||||
'success' => true,
|
||||
'results' => $results,
|
||||
'debug_last' => [
|
||||
'extra_field_id' => $extraFieldId ?? null,
|
||||
'extra_value_id' => $extraValueId ?? null,
|
||||
'extra_value_text' => $extraValueText ?? null,
|
||||
'part_id' => $partId ?? ($newId ?? null),
|
||||
]
|
||||
]);
|
||||
echo json_encode(['success' => true, 'results' => $results]);
|
||||
} catch (PDOException $e) {
|
||||
$pdo->rollBack();
|
||||
echo json_encode(['success' => false, 'message' => 'Errore nel salvataggio: ' . $e->getMessage()]);
|
||||
|
||||
@@ -1,48 +0,0 @@
|
||||
<?php
|
||||
header('Content-Type: application/json');
|
||||
include('include/headscript.php');
|
||||
|
||||
$dbHandler = DBHandlerSelect::getInstance();
|
||||
$pdo = $dbHandler->getConnection();
|
||||
|
||||
$data = json_decode(file_get_contents('php://input'), true);
|
||||
|
||||
$iddatadb = $data['iddatadb'] ?? null;
|
||||
$partIds = $data['partIds'] ?? [];
|
||||
$photoList = $data['photoList'] ?? null;
|
||||
|
||||
if ($iddatadb) {
|
||||
if (count($partIds) != 0) {
|
||||
$idList = array_values(array_map('intval', $partIds));
|
||||
$placeholders = [];
|
||||
$parameters = [':iddatadb' => $iddatadb];
|
||||
|
||||
foreach ($idList as $index => $id) {
|
||||
$paramName = ":id{$index}";
|
||||
$placeholders[] = $paramName;
|
||||
$parameters[$paramName] = $id;
|
||||
}
|
||||
|
||||
$sql = 'UPDATE identification_parts SET iddatadb = :iddatadb WHERE id IN (' . implode(',', $placeholders) . ')';
|
||||
$stmt = $pdo->prepare($sql);
|
||||
$stmt->execute($parameters);
|
||||
}
|
||||
|
||||
if (count($photoList) != 0) {
|
||||
$placeholders = [];
|
||||
$parameters = [':iddatadb' => $iddatadb];
|
||||
|
||||
foreach ($photoList as $index => $photo) {
|
||||
$paramName = ":photo{$index}";
|
||||
$placeholders[] = $paramName;
|
||||
$parameters[$paramName] = $photo;
|
||||
}
|
||||
|
||||
$stmt = $pdo->prepare('UPDATE datadb_photos SET iddatadb = :iddatadb WHERE file_path IN (' . implode(',', $placeholders) . ')');
|
||||
$stmt->execute($parameters);
|
||||
}
|
||||
|
||||
echo json_encode(['success' => true, 'message' => '']);
|
||||
} else {
|
||||
echo json_encode(['success' => false, 'message' => 'Dati mancanti']);
|
||||
}
|
||||
File diff suppressed because one or more lines are too long
@@ -1,881 +1,60 @@
|
||||
{
|
||||
"@odata.context": "https:\/\/bvcpsitaly-elims.com\/limsapi\/api\/odata\/$metadata#SchemaCustomField",
|
||||
"@odata.context": "https:\/\/93.43.5.102\/limsapi\/api\/odata\/$metadata#SchemaCustomField",
|
||||
"value": [
|
||||
{
|
||||
"IdSchemaCustomFields": 41,
|
||||
"ConteggioClienti": 0,
|
||||
"Nome": "Labostudio",
|
||||
"Descrizione": null
|
||||
},
|
||||
{
|
||||
"IdSchemaCustomFields": 42,
|
||||
"ConteggioClienti": 0,
|
||||
"Nome": "Schema 1",
|
||||
"Descrizione": null
|
||||
},
|
||||
{
|
||||
"IdSchemaCustomFields": 43,
|
||||
"ConteggioClienti": 0,
|
||||
"Nome": "Schema1",
|
||||
"Descrizione": null
|
||||
},
|
||||
{
|
||||
"IdSchemaCustomFields": 44,
|
||||
"ConteggioClienti": 0,
|
||||
"Nome": "Cuoio\/Pelle Standard \/ Leather",
|
||||
"Descrizione": "Schema per tutti i campioni in cuoio\/pelle per i quali non \u00e8 specificato un destinatario.\r\n"
|
||||
},
|
||||
{
|
||||
"IdSchemaCustomFields": 45,
|
||||
"ConteggioClienti": 0,
|
||||
"Nome": "Borse",
|
||||
"Descrizione": null
|
||||
},
|
||||
{
|
||||
"IdSchemaCustomFields": 46,
|
||||
"ConteggioClienti": 0,
|
||||
"Nome": "Borse Burberry",
|
||||
"Descrizione": null
|
||||
},
|
||||
{
|
||||
"IdSchemaCustomFields": 47,
|
||||
"ConteggioClienti": 0,
|
||||
"Nome": "pelle calzatura",
|
||||
"Descrizione": null
|
||||
},
|
||||
{
|
||||
"IdSchemaCustomFields": 48,
|
||||
"ConteggioClienti": 0,
|
||||
"Nome": "Standard Generico \/ Generic Standard",
|
||||
"Descrizione": "Schema per tutti i campioni di qualsiasi matrice escluso cuoio\/pelle\r\n\r\n"
|
||||
},
|
||||
{
|
||||
"IdSchemaCustomFields": 49,
|
||||
"ConteggioClienti": 0,
|
||||
"Nome": "Accessori Metallici \/ Metallic Accessories",
|
||||
"Descrizione": "Schema per tutti gli Accessori Metallici\r\n\r\n\r\n"
|
||||
},
|
||||
{
|
||||
"IdSchemaCustomFields": 50,
|
||||
"ConteggioClienti": 0,
|
||||
"Nome": "Tessile \/ Textiles",
|
||||
"Descrizione": "Schema per tutti i Tessuti\r\n"
|
||||
},
|
||||
{
|
||||
"IdSchemaCustomFields": 52,
|
||||
"ConteggioClienti": 0,
|
||||
"Nome": "Brasport",
|
||||
"Descrizione": null
|
||||
},
|
||||
{
|
||||
"IdSchemaCustomFields": 54,
|
||||
"ConteggioClienti": 0,
|
||||
"Nome": "Chanel Calzatura",
|
||||
"Descrizione": "\r\n"
|
||||
},
|
||||
{
|
||||
"IdSchemaCustomFields": 55,
|
||||
"ConteggioClienti": 0,
|
||||
"Nome": "Burberry",
|
||||
"Descrizione": null
|
||||
},
|
||||
{
|
||||
"IdSchemaCustomFields": 57,
|
||||
"ConteggioClienti": 0,
|
||||
"Nome": "Z_Ralph Lauren - Tessile",
|
||||
"Descrizione": "Schema per tutti i campioni in tessuto per Ralph Lauren.\r\n\r\n\r\n\r\n"
|
||||
},
|
||||
{
|
||||
"IdSchemaCustomFields": 58,
|
||||
"ConteggioClienti": 0,
|
||||
"Nome": "LIU JO",
|
||||
"Descrizione": null
|
||||
},
|
||||
{
|
||||
"IdSchemaCustomFields": 59,
|
||||
"ConteggioClienti": 0,
|
||||
"Nome": "Migrazione tra Componenti",
|
||||
"Descrizione": null
|
||||
},
|
||||
{
|
||||
"IdSchemaCustomFields": 60,
|
||||
"ConteggioClienti": 0,
|
||||
"Nome": "Prova Outsourcing",
|
||||
"Descrizione": null
|
||||
},
|
||||
{
|
||||
"IdSchemaCustomFields": 61,
|
||||
"ConteggioClienti": 0,
|
||||
"Nome": "Marc Jacobs",
|
||||
"Descrizione": null
|
||||
},
|
||||
{
|
||||
"IdSchemaCustomFields": 62,
|
||||
"ConteggioClienti": 0,
|
||||
"Nome": "A. TESTONI",
|
||||
"Descrizione": null
|
||||
},
|
||||
{
|
||||
"IdSchemaCustomFields": 63,
|
||||
"ConteggioClienti": 0,
|
||||
"Nome": "Chanel CHINA PROJECT",
|
||||
"Descrizione": "\r\n"
|
||||
},
|
||||
{
|
||||
"IdSchemaCustomFields": 66,
|
||||
"ConteggioClienti": 0,
|
||||
"Nome": "GUESS",
|
||||
"Descrizione": null
|
||||
},
|
||||
{
|
||||
"IdSchemaCustomFields": 68,
|
||||
"ConteggioClienti": 0,
|
||||
"Nome": "Valentino",
|
||||
"Descrizione": null
|
||||
},
|
||||
{
|
||||
"IdSchemaCustomFields": 69,
|
||||
"ConteggioClienti": 0,
|
||||
"Nome": "DESIGUAL",
|
||||
"Descrizione": null
|
||||
},
|
||||
{
|
||||
"IdSchemaCustomFields": 70,
|
||||
"ConteggioClienti": 0,
|
||||
"Nome": "FENDI PELLETTERIA",
|
||||
"Descrizione": "Solo per FENDI Pelletteria_INGLESE\r\n"
|
||||
},
|
||||
{
|
||||
"IdSchemaCustomFields": 71,
|
||||
"ConteggioClienti": 0,
|
||||
"Nome": "Z_Cuoio\/Pelle Standard ACCREDIA",
|
||||
"Descrizione": "Schema per tutti i campioni in cuoio\/pelle per i quali non \u00e8 specificato un destinatario.\r\n"
|
||||
},
|
||||
{
|
||||
"IdSchemaCustomFields": 72,
|
||||
"ConteggioClienti": 0,
|
||||
"Nome": "Z_BALLY",
|
||||
"Descrizione": "sia per calzatura che pelletteria. Aggiunto campo TEST DESCRIPTION, compreso nella fatturazione per le divisioni pelletteria.\r\n\r\n"
|
||||
},
|
||||
{
|
||||
"IdSchemaCustomFields": 73,
|
||||
"ConteggioClienti": 0,
|
||||
"Nome": "Z_Ralph Lauren - Leather",
|
||||
"Descrizione": null
|
||||
},
|
||||
{
|
||||
"IdSchemaCustomFields": 74,
|
||||
"ConteggioClienti": 0,
|
||||
"Nome": "Z_Ralph Lauren - Fabric",
|
||||
"Descrizione": null
|
||||
},
|
||||
{
|
||||
"IdSchemaCustomFields": 76,
|
||||
"ConteggioClienti": 0,
|
||||
"Nome": "NORDSTROM",
|
||||
"Descrizione": "\r\n"
|
||||
},
|
||||
{
|
||||
"IdSchemaCustomFields": 77,
|
||||
"ConteggioClienti": 0,
|
||||
"Nome": "ONWARD LUXURY GROUP",
|
||||
"Descrizione": "\r\n"
|
||||
},
|
||||
{
|
||||
"IdSchemaCustomFields": 78,
|
||||
"ConteggioClienti": 0,
|
||||
"Nome": "P&G SRL_Progetto Durability",
|
||||
"Descrizione": "\r\n"
|
||||
},
|
||||
{
|
||||
"IdSchemaCustomFields": 79,
|
||||
"ConteggioClienti": 0,
|
||||
"Nome": "Bally qualsiasi divisione",
|
||||
"Descrizione": null
|
||||
},
|
||||
{
|
||||
"IdSchemaCustomFields": 80,
|
||||
"ConteggioClienti": 0,
|
||||
"Nome": "FENDI CALZATURA",
|
||||
"Descrizione": "\r\n"
|
||||
},
|
||||
{
|
||||
"IdSchemaCustomFields": 81,
|
||||
"ConteggioClienti": 0,
|
||||
"Nome": "LouBoutin",
|
||||
"Descrizione": "\r\n"
|
||||
},
|
||||
{
|
||||
"IdSchemaCustomFields": 82,
|
||||
"ConteggioClienti": 0,
|
||||
"Nome": "MONCLER Brand",
|
||||
"Descrizione": "Da utilizzare solo per il Brand Diretto\r\nGR 19\/03\/2024"
|
||||
},
|
||||
{
|
||||
"IdSchemaCustomFields": 83,
|
||||
"ConteggioClienti": 0,
|
||||
"Nome": "JIMMY CHOO",
|
||||
"Descrizione": null
|
||||
},
|
||||
{
|
||||
"IdSchemaCustomFields": 84,
|
||||
"ConteggioClienti": 0,
|
||||
"Nome": "Yves Saint Laurent",
|
||||
"Descrizione": null
|
||||
},
|
||||
{
|
||||
"IdSchemaCustomFields": 85,
|
||||
"ConteggioClienti": 0,
|
||||
"Nome": "BENETTON",
|
||||
"Descrizione": null
|
||||
},
|
||||
{
|
||||
"IdSchemaCustomFields": 86,
|
||||
"ConteggioClienti": 0,
|
||||
"Nome": "UPLOAD REPORT",
|
||||
"Descrizione": null
|
||||
},
|
||||
{
|
||||
"IdSchemaCustomFields": 87,
|
||||
"ConteggioClienti": 0,
|
||||
"Nome": "Hermes Calzatura",
|
||||
"Descrizione": "\r\n\r\n\r\n"
|
||||
},
|
||||
{
|
||||
"IdSchemaCustomFields": 88,
|
||||
"ConteggioClienti": 0,
|
||||
"Nome": "C&A",
|
||||
"Descrizione": "Schema per fornitori e C&A diretto\r\n"
|
||||
},
|
||||
{
|
||||
"IdSchemaCustomFields": 89,
|
||||
"ConteggioClienti": 0,
|
||||
"Nome": "AMERICAN EAGLE",
|
||||
"Descrizione": "Schema per American Eagle\/Todd snyder\r\n\r\n\r\n\r\n"
|
||||
},
|
||||
{
|
||||
"IdSchemaCustomFields": 91,
|
||||
"ConteggioClienti": 0,
|
||||
"Nome": "Ferragamo",
|
||||
"Descrizione": "\r\n\r\n\r\n"
|
||||
},
|
||||
{
|
||||
"IdSchemaCustomFields": 93,
|
||||
"ConteggioClienti": 0,
|
||||
"Nome": "Z_Alexander McQueen",
|
||||
"Descrizione": "\r\n\r\n"
|
||||
},
|
||||
{
|
||||
"IdSchemaCustomFields": 95,
|
||||
"ConteggioClienti": 0,
|
||||
"Nome": "Balenciaga Pelletteria",
|
||||
"Descrizione": "\r\n"
|
||||
},
|
||||
{
|
||||
"IdSchemaCustomFields": 96,
|
||||
"ConteggioClienti": 0,
|
||||
"Nome": "DUNHILL",
|
||||
"Descrizione": "SCHEMA DUNHILL tutti i tipi di campioni\r\n"
|
||||
},
|
||||
{
|
||||
"IdSchemaCustomFields": 97,
|
||||
"ConteggioClienti": 0,
|
||||
"Nome": "PRELIEVI ACQUE",
|
||||
"Descrizione": null
|
||||
},
|
||||
{
|
||||
"IdSchemaCustomFields": 98,
|
||||
"ConteggioClienti": 0,
|
||||
"Nome": "LouBoutin_Tossicologico",
|
||||
"Descrizione": null
|
||||
},
|
||||
{
|
||||
"IdSchemaCustomFields": 99,
|
||||
"ConteggioClienti": 0,
|
||||
"Nome": "ROBAN'S ",
|
||||
"Descrizione": "tutte le divisioni\r\n\r\n"
|
||||
},
|
||||
{
|
||||
"IdSchemaCustomFields": 100,
|
||||
"ConteggioClienti": 0,
|
||||
"Nome": "PROGETTO LUGGAGE",
|
||||
"Descrizione": "Progetto Luggage Hermes\r\n\r\n\r\n\r\n"
|
||||
},
|
||||
{
|
||||
"IdSchemaCustomFields": 102,
|
||||
"ConteggioClienti": 0,
|
||||
"Nome": "Balmain",
|
||||
"Descrizione": "Schema per tutti i campioni Balmain"
|
||||
},
|
||||
{
|
||||
"IdSchemaCustomFields": 103,
|
||||
"ConteggioClienti": 0,
|
||||
"Nome": "ZDHC",
|
||||
"Descrizione": "Schema di Riferimento ZDHC Wastewaters"
|
||||
},
|
||||
{
|
||||
"IdSchemaCustomFields": 104,
|
||||
"ConteggioClienti": 0,
|
||||
"Nome": "GUCCI CDC 4172",
|
||||
"Descrizione": "\r\n"
|
||||
},
|
||||
{
|
||||
"IdSchemaCustomFields": 105,
|
||||
"ConteggioClienti": 0,
|
||||
"Nome": "Z_ASOS",
|
||||
"Descrizione": null
|
||||
},
|
||||
{
|
||||
"IdSchemaCustomFields": 106,
|
||||
"ConteggioClienti": 0,
|
||||
"Nome": "Z_BOUX AVENUE",
|
||||
"Descrizione": "28\/09 no indicazioni su esigenze di fatturazione (detto da Frosini)\r\n\r\n\r\n"
|
||||
},
|
||||
{
|
||||
"IdSchemaCustomFields": 107,
|
||||
"ConteggioClienti": 0,
|
||||
"Nome": "BONPOINT",
|
||||
"Descrizione": "05\/10 creazione\r\n\r\n\r\n\r\n"
|
||||
},
|
||||
{
|
||||
"IdSchemaCustomFields": 108,
|
||||
"ConteggioClienti": 0,
|
||||
"Nome": "Canada Goose",
|
||||
"Descrizione": null
|
||||
},
|
||||
{
|
||||
"IdSchemaCustomFields": 109,
|
||||
"ConteggioClienti": 0,
|
||||
"Nome": "DIESEL",
|
||||
"Descrizione": null
|
||||
},
|
||||
{
|
||||
"IdSchemaCustomFields": 110,
|
||||
"ConteggioClienti": 0,
|
||||
"Nome": "AQC_Leather",
|
||||
"Descrizione": null
|
||||
},
|
||||
{
|
||||
"IdSchemaCustomFields": 111,
|
||||
"ConteggioClienti": 0,
|
||||
"Nome": "BEAUMANOIR (CC-Mor-Bon-Breal)",
|
||||
"Descrizione": "04\/02\/21_ AGGIORNATO 05\/08\r\n"
|
||||
},
|
||||
{
|
||||
"IdSchemaCustomFields": 112,
|
||||
"ConteggioClienti": 0,
|
||||
"Nome": "BooHoo",
|
||||
"Descrizione": null
|
||||
},
|
||||
{
|
||||
"IdSchemaCustomFields": 114,
|
||||
"ConteggioClienti": 0,
|
||||
"Nome": "Golden Goose",
|
||||
"Descrizione": "Golden goose\r\n"
|
||||
},
|
||||
{
|
||||
"IdSchemaCustomFields": 115,
|
||||
"ConteggioClienti": 0,
|
||||
"Nome": "Z_ZLABEL",
|
||||
"Descrizione": "Schema per ZLABEL"
|
||||
},
|
||||
{
|
||||
"IdSchemaCustomFields": 116,
|
||||
"ConteggioClienti": 0,
|
||||
"Nome": "Zadig & Voltaire",
|
||||
"Descrizione": "Schema per Zadig & Voltaire"
|
||||
},
|
||||
{
|
||||
"IdSchemaCustomFields": 117,
|
||||
"ConteggioClienti": 0,
|
||||
"Nome": "Gemo",
|
||||
"Descrizione": "Schema solo per Gemo\r\n\r\n\r\n\r\n"
|
||||
},
|
||||
{
|
||||
"IdSchemaCustomFields": 118,
|
||||
"ConteggioClienti": 0,
|
||||
"Nome": "PROMOD",
|
||||
"Descrizione": "CHF 06\/08\r\n\r\n\r\n\r\n\r\n"
|
||||
},
|
||||
{
|
||||
"IdSchemaCustomFields": 119,
|
||||
"ConteggioClienti": 0,
|
||||
"Nome": "LA HALLE (BEAUMANOIR)",
|
||||
"Descrizione": "13\/08\/2021 CHF\r\n\r\n"
|
||||
},
|
||||
{
|
||||
"IdSchemaCustomFields": 120,
|
||||
"ConteggioClienti": 0,
|
||||
"Nome": "LA REDOUTE",
|
||||
"Descrizione": "14\/09\/2021 AD\r\n\r\n\r\n\r\n"
|
||||
},
|
||||
{
|
||||
"IdSchemaCustomFields": 121,
|
||||
"ConteggioClienti": 0,
|
||||
"Nome": "TOD'S",
|
||||
"Descrizione": "Schema per materiale gb18401 e GB20400\r\n\r\n\r\n"
|
||||
},
|
||||
{
|
||||
"IdSchemaCustomFields": 122,
|
||||
"ConteggioClienti": 0,
|
||||
"Nome": "ASOS rev 1",
|
||||
"Descrizione": null
|
||||
},
|
||||
{
|
||||
"IdSchemaCustomFields": 123,
|
||||
"ConteggioClienti": 0,
|
||||
"Nome": "Monoprix",
|
||||
"Descrizione": "Schema per Monoprix\r\n\r\n"
|
||||
},
|
||||
{
|
||||
"IdSchemaCustomFields": 124,
|
||||
"ConteggioClienti": 0,
|
||||
"Nome": "DIOR Ita",
|
||||
"Descrizione": "CHF 21\/03\r\n"
|
||||
},
|
||||
{
|
||||
"IdSchemaCustomFields": 125,
|
||||
"ConteggioClienti": 0,
|
||||
"Nome": "GEOX - Raw Material",
|
||||
"Descrizione": "SCHEMA DA USARE SOLO PER GEOX - SOLO PER LE SUOLE E MATERIALI DENOMINATI RPU \/ RAW MATERIAL"
|
||||
},
|
||||
{
|
||||
"IdSchemaCustomFields": 126,
|
||||
"ConteggioClienti": 0,
|
||||
"Nome": "CAMAIEU",
|
||||
"Descrizione": "SCHEMA DA USARE SOLO PER CAMAIEU\r\n"
|
||||
},
|
||||
{
|
||||
"IdSchemaCustomFields": 127,
|
||||
"ConteggioClienti": 0,
|
||||
"Nome": "Z_Golden Goose FTW",
|
||||
"Descrizione": "Golden goose\r\n"
|
||||
},
|
||||
{
|
||||
"IdSchemaCustomFields": 128,
|
||||
"ConteggioClienti": 0,
|
||||
"Nome": "STEVE MADDEN - FOOTWEAR",
|
||||
"Descrizione": "DA UTILIZZARE SOLO PER DESTINAZIONE D'USO CALZATURA\r\n"
|
||||
},
|
||||
{
|
||||
"IdSchemaCustomFields": 129,
|
||||
"ConteggioClienti": 0,
|
||||
"Nome": "Z_Carhartt",
|
||||
"Descrizione": "SCHEMA DA UTILIZZARE SOLO PER RICHIESTE UFFICIALI CARHARTT\r\n\r\n\r\n"
|
||||
},
|
||||
{
|
||||
"IdSchemaCustomFields": 131,
|
||||
"ConteggioClienti": 0,
|
||||
"Nome": "DAMART",
|
||||
"Descrizione": "\r\n\r\n\r\n\r\n"
|
||||
},
|
||||
{
|
||||
"IdSchemaCustomFields": 132,
|
||||
"ConteggioClienti": 0,
|
||||
"Nome": "Gruppo OTB",
|
||||
"Descrizione": "Schema per Staff International, Diesel e Brave kid\r\n\r\n\r\n\r\n\r\n"
|
||||
},
|
||||
{
|
||||
"IdSchemaCustomFields": 133,
|
||||
"ConteggioClienti": 0,
|
||||
"Nome": "FERRARI",
|
||||
"Descrizione": "Schema da usare per Ferrari e fornitori\r\n\r\n"
|
||||
},
|
||||
{
|
||||
"IdSchemaCustomFields": 134,
|
||||
"ConteggioClienti": 0,
|
||||
"Nome": "GAP - ABBIGLIAMENTO",
|
||||
"Descrizione": "Schema GAP da utilizzare solo per destinazione abbigliamento\r\n"
|
||||
},
|
||||
{
|
||||
"IdSchemaCustomFields": 135,
|
||||
"ConteggioClienti": 0,
|
||||
"Nome": "GAP - CALZATURA",
|
||||
"Descrizione": "Schema GAP da utilizzare solo per destinazione calzatura\r\n\r\n"
|
||||
},
|
||||
{
|
||||
"IdSchemaCustomFields": 136,
|
||||
"ConteggioClienti": 0,
|
||||
"Nome": "SALLING GROUP",
|
||||
"Descrizione": "Schema per Salling Group\r\n\r\n\r\n\r\n\r\n"
|
||||
},
|
||||
{
|
||||
"IdSchemaCustomFields": 137,
|
||||
"ConteggioClienti": 0,
|
||||
"Nome": "SOFT SURROUNDINGS",
|
||||
"Descrizione": "Schema da utilizzare per Soft Surroundings\r\n\r\n"
|
||||
},
|
||||
{
|
||||
"IdSchemaCustomFields": 138,
|
||||
"ConteggioClienti": 0,
|
||||
"Nome": "E-LIMS - Cuoio\/Pelle Standard \/ Leather",
|
||||
"Descrizione": "Schema da utilizzare solo per accettazioni online\r\n"
|
||||
},
|
||||
{
|
||||
"IdSchemaCustomFields": 139,
|
||||
"ConteggioClienti": 0,
|
||||
"Nome": "Z_ZALANDO",
|
||||
"Descrizione": "Schema per ZALANDO ------ (NON per ZLABEL che ha il suo)\r\n"
|
||||
},
|
||||
{
|
||||
"IdSchemaCustomFields": 140,
|
||||
"ConteggioClienti": 0,
|
||||
"Nome": "LVMH",
|
||||
"Descrizione": "gennaio 2023\r\n"
|
||||
},
|
||||
{
|
||||
"IdSchemaCustomFields": 141,
|
||||
"ConteggioClienti": 0,
|
||||
"Nome": "Valentino - eLims",
|
||||
"Descrizione": "SCHEMA DA UTILIZZARE SOLO PER L'ACCETTAZIONE ONLINE DI VALENTINO"
|
||||
},
|
||||
{
|
||||
"IdSchemaCustomFields": 142,
|
||||
"ConteggioClienti": 0,
|
||||
"Nome": "FENDI PELLETTERIA COMPLIANCE",
|
||||
"Descrizione": "Solo per FENDI Pelletteria_INGLESE 04\/05\/2023\r\n"
|
||||
},
|
||||
{
|
||||
"IdSchemaCustomFields": 143,
|
||||
"ConteggioClienti": 0,
|
||||
"Nome": "TAPESTRY",
|
||||
"Descrizione": "04\/05\/2023, agg secondo procedura 08\/11\/23\r\n"
|
||||
},
|
||||
{
|
||||
"IdSchemaCustomFields": 144,
|
||||
"ConteggioClienti": 0,
|
||||
"Nome": "ALEXANDER MC QUEEN",
|
||||
"Descrizione": "ALEXANDER MC QUEEN 15\/05\/2023\r\n\r\n\r\n"
|
||||
},
|
||||
{
|
||||
"IdSchemaCustomFields": 145,
|
||||
"ConteggioClienti": 0,
|
||||
"Nome": "Hermes_Chimici_Parigi",
|
||||
"Descrizione": "\r\n"
|
||||
},
|
||||
{
|
||||
"IdSchemaCustomFields": 146,
|
||||
"ConteggioClienti": 0,
|
||||
"Nome": "CELINE ABBIGLIAMENTO_Progetto Speciale",
|
||||
"Descrizione": "IO 202 CELINE ABBIGLIAMENTO_06\/06\/203_CHF\r\n\r\n"
|
||||
},
|
||||
{
|
||||
"IdSchemaCustomFields": 147,
|
||||
"ConteggioClienti": 0,
|
||||
"Nome": "GEOX - Prodotto Finito",
|
||||
"Descrizione": "SCHEMA DA USARE SOLO PER GEOX - SOLO PER PRODOTTO FINITO"
|
||||
},
|
||||
{
|
||||
"IdSchemaCustomFields": 149,
|
||||
"ConteggioClienti": 0,
|
||||
"Nome": "Hermes\/IDO",
|
||||
"Descrizione": "creato il 05\/07\/2023 CHF\r\n"
|
||||
},
|
||||
{
|
||||
"IdSchemaCustomFields": 150,
|
||||
"ConteggioClienti": 0,
|
||||
"Nome": "Tessile \/ Textiles PER CLIENTI CON OBLO",
|
||||
"Descrizione": "Schema per tutti i Tessuti QUANDO IL CLIENTE NECESSITA DI NR OBLO\r\n\r\n\r\n\r\n"
|
||||
},
|
||||
{
|
||||
"IdSchemaCustomFields": 151,
|
||||
"ConteggioClienti": 0,
|
||||
"Nome": "J JIll",
|
||||
"Descrizione": "Schema per cliente J JIll, necessario riportare la recommended fiber content in schema verde\r\n\r\n\r\n\r\n\r\n"
|
||||
},
|
||||
{
|
||||
"IdSchemaCustomFields": 152,
|
||||
"ConteggioClienti": 0,
|
||||
"Nome": "AUCHAN - TAPE A L'OEIL",
|
||||
"Descrizione": "Schema AUCHAN da utilizzare solo per la divisione TAPE A L'OEIL SA, successivamente se uguale da usare anche per le altre divisioni ed uniformarlo come generico"
|
||||
},
|
||||
{
|
||||
"IdSchemaCustomFields": 153,
|
||||
"ConteggioClienti": 0,
|
||||
"Nome": "ALL SAINTS - BUSCEMI",
|
||||
"Descrizione": "SCHE DA USARE SOLO PER ALL SAINTS E BUSCEMI\r\n\r\n\r\n\r\n"
|
||||
},
|
||||
{
|
||||
"IdSchemaCustomFields": 154,
|
||||
"ConteggioClienti": 0,
|
||||
"Nome": "CELINE - LVMH",
|
||||
"Descrizione": "Schema con aggiunta campi LVMH per estrazioni\r\nAD-29\/03\/2024"
|
||||
},
|
||||
{
|
||||
"IdSchemaCustomFields": 155,
|
||||
"ConteggioClienti": 0,
|
||||
"Nome": "SeyMeChamLou",
|
||||
"Descrizione": "DA USARE SOLO PER SEYMECHAMLOU (LOUBOUTIN)\r\n"
|
||||
},
|
||||
{
|
||||
"IdSchemaCustomFields": 156,
|
||||
"ConteggioClienti": 0,
|
||||
"Nome": "PRADA",
|
||||
"Descrizione": "PER ADESSO DA USARE PER ABBIGLIAMENTO E CALZATURA\r\n\r\n\r\n\r\n"
|
||||
},
|
||||
{
|
||||
"IdSchemaCustomFields": 157,
|
||||
"ConteggioClienti": 0,
|
||||
"Nome": "Hermes_HCP",
|
||||
"Descrizione": "Schema per unicamente HCP"
|
||||
},
|
||||
{
|
||||
"IdSchemaCustomFields": 158,
|
||||
"ConteggioClienti": 0,
|
||||
"Nome": "STONE ISLAND",
|
||||
"Descrizione": "Schema per tutti i campioni in cuoio\/pelle per i quali non \u00e8 specificato un destinatario.\r\n"
|
||||
},
|
||||
{
|
||||
"IdSchemaCustomFields": 159,
|
||||
"ConteggioClienti": 0,
|
||||
"Nome": "Chanel_Pierre Damien",
|
||||
"Descrizione": "Schema da usare per Campioni ricevuti da Pierre-Damien Verine e suo staff CHANEL.\r\n"
|
||||
},
|
||||
{
|
||||
"IdSchemaCustomFields": 160,
|
||||
"ConteggioClienti": 0,
|
||||
"Nome": "Z_ACNE",
|
||||
"Descrizione": "\r\n.\r\n"
|
||||
},
|
||||
{
|
||||
"IdSchemaCustomFields": 161,
|
||||
"ConteggioClienti": 0,
|
||||
"Nome": "OBERALP GROUP",
|
||||
"Descrizione": "Schema da usare solo per Oberalp Group\r\n"
|
||||
},
|
||||
{
|
||||
"IdSchemaCustomFields": 162,
|
||||
"ConteggioClienti": 0,
|
||||
"Nome": "Club Monaco",
|
||||
"Descrizione": "da usare solo per Club Monaco GR 20\/02\/2024"
|
||||
},
|
||||
{
|
||||
"IdSchemaCustomFields": 163,
|
||||
"ConteggioClienti": 0,
|
||||
"Nome": "Devred",
|
||||
"Descrizione": "Schema creato per cliente DEVRED\r\nGR 18\/03\/2024\r\n\r\n"
|
||||
},
|
||||
{
|
||||
"IdSchemaCustomFields": 164,
|
||||
"ConteggioClienti": 0,
|
||||
"Nome": "MONCLER Supplier",
|
||||
"Descrizione": "Da utilizzare per fornitori Moncler \r\nGR 19\/03\/2024"
|
||||
},
|
||||
{
|
||||
"IdSchemaCustomFields": 165,
|
||||
"ConteggioClienti": 0,
|
||||
"Nome": "Kering Sunglasses",
|
||||
"Descrizione": "Schema per tutti i campioni di qualsiasi matrice escluso cuoio\/pelle\r\n\r\n"
|
||||
},
|
||||
{
|
||||
"IdSchemaCustomFields": 166,
|
||||
"ConteggioClienti": 0,
|
||||
"Nome": "Giorgio Armani",
|
||||
"Descrizione": "\r\n\r\n"
|
||||
},
|
||||
{
|
||||
"IdSchemaCustomFields": 167,
|
||||
"ConteggioClienti": 0,
|
||||
"Nome": "Chanel USA",
|
||||
"Descrizione": "\r\n"
|
||||
},
|
||||
{
|
||||
"IdSchemaCustomFields": 168,
|
||||
"ConteggioClienti": 0,
|
||||
"Nome": "Staff Calzatura",
|
||||
"Descrizione": "Schema per tutti i campioni di qualsiasi matrice escluso cuoio\/pelle\r\n\r\n"
|
||||
},
|
||||
{
|
||||
"IdSchemaCustomFields": 169,
|
||||
"ConteggioClienti": 0,
|
||||
"Nome": "P&G SRL",
|
||||
"Descrizione": "\r\n"
|
||||
},
|
||||
{
|
||||
"IdSchemaCustomFields": 170,
|
||||
"ConteggioClienti": 0,
|
||||
"Nome": "INCOM",
|
||||
"Descrizione": "Schema da utilizzare per INCOM"
|
||||
},
|
||||
{
|
||||
"IdSchemaCustomFields": 171,
|
||||
"ConteggioClienti": 0,
|
||||
"Nome": "RIVER ISLAND",
|
||||
"Descrizione": "SCHEDA DA USARE SOLO PER RIVER ISLAND - NO CALZATURA\r\n\r\n\r\n"
|
||||
},
|
||||
{
|
||||
"IdSchemaCustomFields": 172,
|
||||
"ConteggioClienti": 0,
|
||||
"Nome": "Longchamp",
|
||||
"Descrizione": "Schema per tutti i campioni con RIchiesta Longchamp ufficiale\r\n"
|
||||
},
|
||||
{
|
||||
"IdSchemaCustomFields": 173,
|
||||
"ConteggioClienti": 0,
|
||||
"Nome": "Bottega Veneta",
|
||||
"Descrizione": "Schema per tutti i campioni di Bottega Veneta di Stella"
|
||||
},
|
||||
{
|
||||
"IdSchemaCustomFields": 174,
|
||||
"ConteggioClienti": 0,
|
||||
"Nome": "Mint Velvet",
|
||||
"Descrizione": "Schema per tutti i campioni di Mint Velvel con richiesta ufficiale.\r\n\r\n"
|
||||
},
|
||||
{
|
||||
"IdSchemaCustomFields": 176,
|
||||
"ConteggioClienti": 0,
|
||||
"Nome": "RIVER ISLAND FOOTWEAR",
|
||||
"Descrizione": "SCHEDA DA USARE SOLO PER RIVER ISLAND - USARE SOLO PER\r\n CALZATURA\r\n"
|
||||
},
|
||||
{
|
||||
"IdSchemaCustomFields": 177,
|
||||
"ConteggioClienti": 0,
|
||||
"Nome": "Phoebe philo ACC",
|
||||
"Descrizione": "(scarpe, borse, cinture, occhiali, gioielleria)\r\n"
|
||||
},
|
||||
{
|
||||
"IdSchemaCustomFields": 178,
|
||||
"ConteggioClienti": 0,
|
||||
"Nome": "New Guards Group",
|
||||
"Descrizione": "\r\n"
|
||||
},
|
||||
{
|
||||
"IdSchemaCustomFields": 179,
|
||||
"ConteggioClienti": 0,
|
||||
"Nome": "Giorgio Armani Operation",
|
||||
"Descrizione": "\r\n\r\n"
|
||||
},
|
||||
{
|
||||
"IdSchemaCustomFields": 180,
|
||||
"ConteggioClienti": 0,
|
||||
"Nome": "Chloe'",
|
||||
"Descrizione": "\r\n"
|
||||
},
|
||||
{
|
||||
"IdSchemaCustomFields": 181,
|
||||
"ConteggioClienti": 0,
|
||||
"Nome": "LVMH non diretti",
|
||||
"Descrizione": "utilizzato ogni qualvolta siano caricati pacchetti \/ prove singole che non rientrano nel programma LVMH \r\n"
|
||||
},
|
||||
{
|
||||
"IdSchemaCustomFields": 182,
|
||||
"ConteggioClienti": 0,
|
||||
"Nome": "Ralph Lauren - All testing V.6",
|
||||
"Descrizione": "AGGIORNAMENTO AL 16\/03\/2026 per estrazione fatturazione"
|
||||
},
|
||||
{
|
||||
"IdSchemaCustomFields": 183,
|
||||
"ConteggioClienti": 0,
|
||||
"Nome": "TWINSET",
|
||||
"Descrizione": "Schema da usare per Twinset - come da mail di EP del 20\/05\/2025\r\n\r\n"
|
||||
},
|
||||
{
|
||||
"IdSchemaCustomFields": 184,
|
||||
"ConteggioClienti": 0,
|
||||
"Nome": "ACNE STUDIOS rev 01",
|
||||
"Descrizione": null
|
||||
},
|
||||
{
|
||||
"IdSchemaCustomFields": 185,
|
||||
"ConteggioClienti": 0,
|
||||
"Nome": "Brunello cucinelli",
|
||||
"Descrizione": null
|
||||
},
|
||||
{
|
||||
"IdSchemaCustomFields": 186,
|
||||
"ConteggioClienti": 0,
|
||||
"Nome": "TWINSET",
|
||||
"Descrizione": "Schema per tutti i campioni di qualsiasi matrice escluso cuoio\/pelle\r\n\r\n"
|
||||
},
|
||||
{
|
||||
"IdSchemaCustomFields": 187,
|
||||
"ConteggioClienti": 0,
|
||||
"Nome": "LORO PIANA",
|
||||
"Descrizione": "Schema per tutti i campioni Loro Piana\r\n"
|
||||
},
|
||||
{
|
||||
"IdSchemaCustomFields": 188,
|
||||
"ConteggioClienti": 0,
|
||||
"Nome": "LEMAIRE",
|
||||
"Descrizione": "Schema per tutti i campioni in cuoio\/pelle per i quali non \u00e8 specificato un destinatario.\r\n"
|
||||
},
|
||||
{
|
||||
"IdSchemaCustomFields": 189,
|
||||
"ConteggioClienti": 0,
|
||||
"Nome": "TEST_IT_SCHEMA",
|
||||
"Descrizione": "Test per IT"
|
||||
},
|
||||
{
|
||||
"IdSchemaCustomFields": 190,
|
||||
"ConteggioClienti": 0,
|
||||
"Nome": "LBS Interscambio",
|
||||
"Descrizione": "Schema per tutti i campioni interscambio LBS\r\n"
|
||||
},
|
||||
{
|
||||
"IdSchemaCustomFields": 191,
|
||||
"ConteggioClienti": 0,
|
||||
"Nome": "KIABI",
|
||||
"Descrizione": "Schema per il cliente Kiabi\r\n"
|
||||
},
|
||||
{
|
||||
"IdSchemaCustomFields": 192,
|
||||
"ConteggioClienti": 0,
|
||||
"Nome": "INDITEX",
|
||||
"Descrizione": "Schema da usare solo per richieste INDITEX"
|
||||
},
|
||||
{
|
||||
"IdSchemaCustomFields": 193,
|
||||
"ConteggioClienti": 0,
|
||||
"Nome": "AQC_Other materials",
|
||||
"Descrizione": null
|
||||
},
|
||||
{
|
||||
"IdSchemaCustomFields": 194,
|
||||
"ConteggioClienti": 0,
|
||||
"Nome": "Hermes ADM & Silk",
|
||||
"Descrizione": "SCHEMA DA USARE SOLO PER LA DIVISIONE ADM & SILK\r\n\r\n\r\n\r\n"
|
||||
},
|
||||
{
|
||||
"IdSchemaCustomFields": 195,
|
||||
"ConteggioClienti": 0,
|
||||
"Nome": "Chloe'_APPAREL",
|
||||
"Descrizione": "Schema aggiornato il 18\/12\/25 secondo modulo excel x codifica Elims\r\nGR"
|
||||
},
|
||||
{
|
||||
"IdSchemaCustomFields": 196,
|
||||
"ConteggioClienti": 0,
|
||||
"Nome": "DUNHILL_DRAFT NON USARE",
|
||||
"Descrizione": "SCHEMA DUNHILL NON USARE STUDIO NUOVA PROCEDURA NOVEMBRE 2025\r\n\r\n"
|
||||
},
|
||||
{
|
||||
"IdSchemaCustomFields": 197,
|
||||
"ConteggioClienti": 0,
|
||||
"Nome": "BERLUTI - LVMH",
|
||||
"Descrizione": "Creato per accettazione Elims\r\n"
|
||||
},
|
||||
{
|
||||
"IdSchemaCustomFields": 198,
|
||||
"ConteggioClienti": 0,
|
||||
"Nome": "PIAGET (gruppo Cartier)",
|
||||
"Descrizione": "Schema per tutti i campioni PIAGET (gruppo Cartier)\r\n\r\n"
|
||||
},
|
||||
{
|
||||
"IdSchemaCustomFields": 199,
|
||||
"ConteggioClienti": 0,
|
||||
"Nome": "HERMES PAP FEMME",
|
||||
"Descrizione": "Schema per tutti i campioni di HERMES PAP FEMME \r\n15\/01\/2025\r\n\r\n\r\n"
|
||||
},
|
||||
{
|
||||
"IdSchemaCustomFields": 200,
|
||||
"ConteggioClienti": 0,
|
||||
"Nome": "CARHARTT ( provvisiorio in revisione)",
|
||||
"Descrizione": "Schema per tutti i campioni CARHARTT provvissorio\r\n\r\n"
|
||||
},
|
||||
{
|
||||
"IdSchemaCustomFields": 201,
|
||||
"ConteggioClienti": 0,
|
||||
"Nome": "ROSSIMODA",
|
||||
"Descrizione": "Per tutti i campioni di ROSSIMODA\r\n\r\n"
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -1,58 +0,0 @@
|
||||
<?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);
|
||||
|
||||
$q = mb_strtolower(trim($_GET['q'] ?? ''));
|
||||
$limit = max(1, min(50, intval($_GET['limit'] ?? 20)));
|
||||
$id = isset($_GET['id']) ? intval($_GET['id']) : null;
|
||||
|
||||
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 (for loading selected value)
|
||||
if ($id !== null) {
|
||||
foreach ($clients as $c) {
|
||||
if ((int)$c['IdCliente'] === $id) {
|
||||
echo json_encode(['results' => [['id' => $c['IdCliente'], 'text' => trim($c['Nominativo'] ?? '')]]]);
|
||||
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' => $name];
|
||||
if (count($results) >= $limit) break;
|
||||
}
|
||||
}
|
||||
|
||||
echo json_encode(['results' => $results]);
|
||||
} catch (Exception $e) {
|
||||
http_response_code(500);
|
||||
echo json_encode(['error' => $e->getMessage()]);
|
||||
}
|
||||
@@ -1,62 +0,0 @@
|
||||
<?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);
|
||||
|
||||
$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()]);
|
||||
}
|
||||
@@ -1,56 +0,0 @@
|
||||
<?php
|
||||
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;
|
||||
}
|
||||
|
||||
// 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]);
|
||||
File diff suppressed because it is too large
Load Diff
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user