extrac prices pdfco
This commit is contained in:
parent
6b65b31932
commit
1804605cad
@ -108,9 +108,9 @@ class LoginController extends Controller
|
||||
|
||||
// Reindirizza in base al ruolo
|
||||
if ($user->hasRole('Admin')) {
|
||||
return redirect()->to('userarea/import_dashboard.php');
|
||||
return redirect()->to('userarea/import_xls.php');
|
||||
} elseif ($user->hasRole('User')) {
|
||||
return redirect()->to('userarea/import_dashboard.php');
|
||||
return redirect()->to('userarea/import_xls.php');
|
||||
}
|
||||
|
||||
// Se il ruolo non è specificato, reindirizza alla home predefinita
|
||||
|
||||
@ -47,6 +47,7 @@
|
||||
"smalot/pdfparser": "^2.12",
|
||||
"socialiteproviders/microsoft": "^4.7",
|
||||
"spatie/laravel-query-builder": "^5.0",
|
||||
"spatie/pdf-to-text": "^1.54",
|
||||
"vanguardapp/activity-log": "^6.0",
|
||||
"vanguardapp/announcements": "^6.0",
|
||||
"vanguardapp/plugins": "^6.0",
|
||||
|
||||
60
composer.lock
generated
60
composer.lock
generated
@ -4,7 +4,7 @@
|
||||
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
|
||||
"This file is @generated automatically"
|
||||
],
|
||||
"content-hash": "97382155de516648df8ab1f1c50ab1d5",
|
||||
"content-hash": "d55364bafa22e40dedff454d54442d59",
|
||||
"packages": [
|
||||
{
|
||||
"name": "akaunting/laravel-setting",
|
||||
@ -5290,6 +5290,64 @@
|
||||
],
|
||||
"time": "2024-05-10T08:19:35+00:00"
|
||||
},
|
||||
{
|
||||
"name": "spatie/pdf-to-text",
|
||||
"version": "1.54.1",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/spatie/pdf-to-text.git",
|
||||
"reference": "ad2a792c7e1e68f1bc9b038dc73cdcf760595fbb"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/spatie/pdf-to-text/zipball/ad2a792c7e1e68f1bc9b038dc73cdcf760595fbb",
|
||||
"reference": "ad2a792c7e1e68f1bc9b038dc73cdcf760595fbb",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
"php": "^7.4|^8.0",
|
||||
"symfony/process": "^4.0|^5.0|^6.0|^7.0"
|
||||
},
|
||||
"require-dev": {
|
||||
"pestphp/pest-plugin-laravel": "^1.3",
|
||||
"phpunit/phpunit": "^9.5"
|
||||
},
|
||||
"type": "library",
|
||||
"autoload": {
|
||||
"psr-4": {
|
||||
"Spatie\\PdfToText\\": "src"
|
||||
}
|
||||
},
|
||||
"notification-url": "https://packagist.org/downloads/",
|
||||
"license": [
|
||||
"MIT"
|
||||
],
|
||||
"authors": [
|
||||
{
|
||||
"name": "Freek Van der Herten",
|
||||
"email": "freek@spatie.be",
|
||||
"homepage": "https://spatie.be",
|
||||
"role": "Developer"
|
||||
}
|
||||
],
|
||||
"description": "Extract text from a pdf",
|
||||
"homepage": "https://github.com/spatie/pdf-to-text",
|
||||
"keywords": [
|
||||
"pdf-to-text",
|
||||
"spatie"
|
||||
],
|
||||
"support": {
|
||||
"issues": "https://github.com/spatie/pdf-to-text/issues",
|
||||
"source": "https://github.com/spatie/pdf-to-text/tree/1.54.1"
|
||||
},
|
||||
"funding": [
|
||||
{
|
||||
"url": "https://spatie.be/open-source/support-us",
|
||||
"type": "custom"
|
||||
}
|
||||
],
|
||||
"time": "2025-06-13T15:23:19+00:00"
|
||||
},
|
||||
{
|
||||
"name": "symfony/clock",
|
||||
"version": "v7.1.1",
|
||||
|
||||
Binary file not shown.
|
Before Width: | Height: | Size: 10 KiB After Width: | Height: | Size: 6.0 KiB |
73
public/userarea/extract_prices.php
Normal file
73
public/userarea/extract_prices.php
Normal file
@ -0,0 +1,73 @@
|
||||
<?php
|
||||
|
||||
require_once __DIR__ . "/../../vendor/autoload.php";
|
||||
|
||||
use Spatie\PdfToText\Pdf;
|
||||
|
||||
// ---------------------------------------------------------
|
||||
// 1) Percorso PDF
|
||||
// ---------------------------------------------------------
|
||||
$pdfFile = __DIR__ . "/listino.pdf";
|
||||
|
||||
if (!file_exists($pdfFile)) {
|
||||
die(json_encode(["error" => "PDF non trovato: $pdfFile"]));
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------
|
||||
// 2) Estrazione testo usando Poppler
|
||||
// ---------------------------------------------------------
|
||||
$poppler = 'C:\poppler\Library\bin\pdftotext.exe';
|
||||
|
||||
try {
|
||||
$text = Pdf::getText($pdfFile, $poppler);
|
||||
} catch (Exception $e) {
|
||||
die(json_encode(["error" => $e->getMessage()]));
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------
|
||||
// 3) Normalizzazione
|
||||
// ---------------------------------------------------------
|
||||
$text = preg_replace('/[ ]{2,}/', ' ', $text);
|
||||
$text = str_replace("\t", " ", $text);
|
||||
|
||||
// ---------------------------------------------------------
|
||||
// 4) REGEX PER IL LISTINO CASSINA
|
||||
// ---------------------------------------------------------
|
||||
$regex = '/(\d{3}\s+\d{2,3}\s*[A-Z]?)\s+([^0-9]+?)\s+((?:\d{1,2}[.,]\d{2,3}\s+)+)/';
|
||||
|
||||
preg_match_all($regex, $text, $matches, PREG_SET_ORDER);
|
||||
|
||||
$results = [];
|
||||
|
||||
foreach ($matches as $m) {
|
||||
|
||||
$codice = trim($m[1]);
|
||||
$descr = trim($m[2]);
|
||||
$priceChunk = trim($m[3]);
|
||||
|
||||
// Trova tutti i prezzi
|
||||
preg_match_all('/\d{1,2}[.,]\d{2,3}/', $priceChunk, $nums);
|
||||
|
||||
foreach ($nums[0] as $i => $val) {
|
||||
|
||||
// 10,80 → 10.80
|
||||
$val = str_replace(',', '.', $val);
|
||||
|
||||
$results[] = [
|
||||
"codice" => $codice,
|
||||
"descrizione" => $descr,
|
||||
"colonna" => "COL_" . ($i + 1),
|
||||
"prezzo" => floatval($val)
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------
|
||||
// 5) Output JSON
|
||||
// ---------------------------------------------------------
|
||||
header("Content-Type: application/json");
|
||||
|
||||
echo json_encode([
|
||||
"items" => count($results),
|
||||
"data" => $results
|
||||
], JSON_PRETTY_PRINT);
|
||||
218
public/userarea/extract_prices_pdfco.php
Normal file
218
public/userarea/extract_prices_pdfco.php
Normal file
@ -0,0 +1,218 @@
|
||||
<?php
|
||||
header("Content-Type: application/json; charset=utf-8");
|
||||
|
||||
// ---------------------------------------------------------
|
||||
// 1) CONFIGURAZIONE
|
||||
// ---------------------------------------------------------
|
||||
$apiKey = "info@claudiosironi.com_Qfh02D7sAvi2tcx3ZchHpusNaBquCKhJw81fEnkHe2ersQDVOex4IokhCCzaFAz1";
|
||||
$fileToken = "filetoken://37bb07a8561409d281b32bcd023ff3dc33de92a8f65aae5af7";
|
||||
|
||||
// opzionale: passare un token via GET
|
||||
if (isset($_GET['token']) && $_GET['token'] !== "") {
|
||||
$fileToken = $_GET['token'];
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------
|
||||
// 2) PDF.CO → JSON2
|
||||
// ---------------------------------------------------------
|
||||
$endpoint = "https://api.pdf.co/v1/pdf/convert/to/json2";
|
||||
|
||||
$payload = [
|
||||
"url" => $fileToken,
|
||||
"inline" => true,
|
||||
"detectTables" => true,
|
||||
"pages" => ""
|
||||
];
|
||||
|
||||
$ch = curl_init($endpoint);
|
||||
curl_setopt($ch, CURLOPT_POST, true);
|
||||
curl_setopt($ch, CURLOPT_HTTPHEADER, [
|
||||
"Content-Type: application/json",
|
||||
"x-api-key: $apiKey"
|
||||
]);
|
||||
curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode($payload));
|
||||
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
|
||||
|
||||
$response = curl_exec($ch);
|
||||
|
||||
if (!$response) {
|
||||
echo json_encode(["error" => "Errore CURL: " . curl_error($ch)]);
|
||||
curl_close($ch);
|
||||
exit;
|
||||
}
|
||||
|
||||
curl_close($ch);
|
||||
|
||||
$json = json_decode($response, true);
|
||||
|
||||
if (!isset($json["body"]["document"]["page"]["row"])) {
|
||||
echo json_encode([
|
||||
"error" => true,
|
||||
"message" => "JSON PDF.co non contiene tabelle utili",
|
||||
"raw" => $json
|
||||
]);
|
||||
exit;
|
||||
}
|
||||
|
||||
$rows = $json["body"]["document"]["page"]["row"];
|
||||
|
||||
// ---------------------------------------------------------
|
||||
// 3) FUNZIONI UTILITARIE
|
||||
// ---------------------------------------------------------
|
||||
|
||||
// Formato A – 291 02F
|
||||
function isCodeTypeA($txt)
|
||||
{
|
||||
return preg_match('/^\d{3}\s+[0-9A-Z]{2,3}$/', $txt);
|
||||
}
|
||||
|
||||
// Estrai blocchi tipo "499 3A 14 5.475"
|
||||
function extractTypeCBlocks($line)
|
||||
{
|
||||
preg_match_all(
|
||||
'/(\d{3})\s+([0-9A-Z]{1,3})\s+(\d{2})\s+(\d{1,2}\.\d{3})/',
|
||||
$line,
|
||||
$m,
|
||||
PREG_SET_ORDER
|
||||
);
|
||||
|
||||
$out = [];
|
||||
foreach ($m as $b) {
|
||||
$out[] = [
|
||||
"codice" => $b[1],
|
||||
"variante" => $b[2],
|
||||
"dimensione" => $b[3],
|
||||
"prezzo" => floatval(str_replace(".", "", $b[4]))
|
||||
];
|
||||
}
|
||||
return $out;
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------
|
||||
// 4) PARSER UNICO (GESTISCE TUTTI I FORMATI)
|
||||
// ---------------------------------------------------------
|
||||
$items = [];
|
||||
|
||||
$currentCode = null;
|
||||
$currentDesc = "";
|
||||
$currentPrices = [];
|
||||
|
||||
foreach ($rows as $row) {
|
||||
|
||||
// Ricostruisco la riga
|
||||
$line = "";
|
||||
foreach ($row["column"] as $col) {
|
||||
if (!isset($col["text"]["text"])) continue;
|
||||
$t = trim($col["text"]["text"]);
|
||||
if ($t !== "") $line .= " " . $t;
|
||||
}
|
||||
$line = trim($line);
|
||||
if ($line === "") continue;
|
||||
|
||||
// ---------------------------------------------------------
|
||||
// 4A — FORMATO CASSINA A (FIX prezzi concatenati)
|
||||
// ---------------------------------------------------------
|
||||
if (preg_match('/^(\d{3}\s+[0-9A-Z]{2,3})\s+(.*)$/', $line, $mA)) {
|
||||
|
||||
$codice = trim($mA[1]);
|
||||
$resto = trim($mA[2]);
|
||||
|
||||
// Estrae TUTTI i prezzi concatenati (es. "10,808.9906.460...")
|
||||
preg_match_all('/\d{1,2}[.,]\d{2,3}/', $resto, $matchesPrezzi);
|
||||
|
||||
if (count($matchesPrezzi[0]) >= 2) {
|
||||
|
||||
// Descrizione SENZA prezzi
|
||||
$descr = trim(preg_replace('/\d{1,2}[.,]\d{2,3}/', '', $resto));
|
||||
|
||||
// Conversione valori
|
||||
$vals = array_map(function ($v) {
|
||||
return floatval(str_replace(",", ".", str_replace(".", "", $v)));
|
||||
}, $matchesPrezzi[0]);
|
||||
|
||||
$items[] = [
|
||||
"type" => "A",
|
||||
"codice" => $codice,
|
||||
"descrizione" => $descr,
|
||||
"prezzi" => $vals
|
||||
];
|
||||
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------
|
||||
// 4B — FORMATO VECCHIO CASSINA (003 BC cromata … 8 prezzi)
|
||||
// ---------------------------------------------------------
|
||||
if (preg_match(
|
||||
'/^(\d{3}\s+[A-Z]{1,3})\s+([A-Za-zÀ-ù\s]+?)\s+((?:\d{1,2}[.,]\d{2,3}\s*){8,})$/',
|
||||
$line,
|
||||
$mB
|
||||
)) {
|
||||
$codice = trim($mB[1]);
|
||||
$descr = trim($mB[2]);
|
||||
$valuesString = trim($mB[3]);
|
||||
|
||||
preg_match_all('/\d{1,2}[.,]\d{2,3}/', $valuesString, $vv);
|
||||
|
||||
$vals = array_map(function ($v) {
|
||||
return floatval(str_replace(",", ".", str_replace(".", "", $v)));
|
||||
}, $vv[0]);
|
||||
|
||||
$categorie = ["Z", "Y", "X", "O", "L", "F/COL", "E/COM", "ALTRO"];
|
||||
|
||||
$prezziMappati = [];
|
||||
foreach ($vals as $i => $v) {
|
||||
$key = $categorie[$i] ?? ("COL_" . ($i + 1));
|
||||
$prezziMappati[$key] = $v;
|
||||
}
|
||||
|
||||
$items[] = [
|
||||
"type" => "B1",
|
||||
"codice" => $codice,
|
||||
"descrizione" => $descr,
|
||||
"prezzi" => $prezziMappati
|
||||
];
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------
|
||||
// 4C — FORMATO LISTINO2 (499 3A 14 5.475 ripetuti)
|
||||
// ---------------------------------------------------------
|
||||
$blocks = extractTypeCBlocks($line);
|
||||
|
||||
if (!empty($blocks)) {
|
||||
|
||||
$desc = $line;
|
||||
foreach ($blocks as $b) {
|
||||
$pattern = sprintf(
|
||||
'/%s\s+%s\s+%s\s+\d{1,2}\.\d{3}/',
|
||||
$b["codice"],
|
||||
$b["variante"],
|
||||
$b["dimensione"]
|
||||
);
|
||||
$desc = preg_replace($pattern, "", $desc);
|
||||
}
|
||||
|
||||
$desc = trim($desc);
|
||||
|
||||
$items[] = [
|
||||
"type" => "C",
|
||||
"descrizione" => $desc,
|
||||
"varianti" => $blocks
|
||||
];
|
||||
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// ---------------------------------------------------------
|
||||
// 5) OUTPUT FINALE
|
||||
// ---------------------------------------------------------
|
||||
echo json_encode([
|
||||
"status" => "ok",
|
||||
"total" => count($items),
|
||||
"items" => $items
|
||||
], JSON_PRETTY_PRINT);
|
||||
137
public/userarea/extract_prices_table.php
Normal file
137
public/userarea/extract_prices_table.php
Normal file
@ -0,0 +1,137 @@
|
||||
<?php
|
||||
header("Content-Type: application/json; charset=utf-8");
|
||||
|
||||
/******************************************************
|
||||
* CONFIG
|
||||
******************************************************/
|
||||
$apiKey = "info@claudiosironi.com_Qfh02D7sAvi2tcx3ZchHpusNaBquCKhJw81fEnkHe2ersQDVOex4IokhCCzaFAz1";
|
||||
$fileToken = "filetoken://61a780917907f86a340290d22c449357dc68950e9066bd67b2";
|
||||
|
||||
// Se passi ?token=xxx usa quello
|
||||
if (!empty($_GET['token'])) {
|
||||
$fileToken = $_GET['token'];
|
||||
}
|
||||
|
||||
/******************************************************
|
||||
* 1) ESTRAGGO CSV (solo pagine con tabelle vere)
|
||||
******************************************************/
|
||||
$endpointCsv = "https://api.pdf.co/v1/pdf/convert/to/csv";
|
||||
|
||||
$payloadCsv = [
|
||||
"url" => $fileToken,
|
||||
"inline" => true,
|
||||
"detectTables" => true,
|
||||
"pages" => "all",
|
||||
"extractColumnBy" => "vertical_lines",
|
||||
"csvSeparator" => ";"
|
||||
];
|
||||
|
||||
$ch = curl_init($endpointCsv);
|
||||
curl_setopt($ch, CURLOPT_POST, true);
|
||||
curl_setopt($ch, CURLOPT_HTTPHEADER, [
|
||||
"Content-Type: application/json",
|
||||
"x-api-key: $apiKey"
|
||||
]);
|
||||
curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode($payloadCsv));
|
||||
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
|
||||
|
||||
$csvResponse = curl_exec($ch);
|
||||
curl_close($ch);
|
||||
|
||||
$csvJson = json_decode($csvResponse, true);
|
||||
|
||||
/******************************************************
|
||||
* 2) ESTRAGGO ANCHE TESTO (per pagine NON tabellari)
|
||||
******************************************************/
|
||||
$endpointText = "https://api.pdf.co/v1/pdf/convert/to/text";
|
||||
|
||||
$payloadText = [
|
||||
"url" => $fileToken,
|
||||
"inline" => true,
|
||||
"pages" => "all"
|
||||
];
|
||||
|
||||
$ch = curl_init($endpointText);
|
||||
curl_setopt($ch, CURLOPT_POST, true);
|
||||
curl_setopt($ch, CURLOPT_HTTPHEADER, [
|
||||
"Content-Type: application/json",
|
||||
"x-api-key: $apiKey"
|
||||
]);
|
||||
curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode($payloadText));
|
||||
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
|
||||
|
||||
$textResponse = curl_exec($ch);
|
||||
curl_close($ch);
|
||||
|
||||
$textJson = json_decode($textResponse, true);
|
||||
|
||||
/******************************************************
|
||||
* 3) PARSER CSV → TABELLE FORMATO A
|
||||
******************************************************/
|
||||
$formatoA = [];
|
||||
|
||||
if (isset($csvJson["csv"])) {
|
||||
$rows = explode("\n", $csvJson["csv"]);
|
||||
|
||||
foreach ($rows as $r) {
|
||||
$cols = str_getcsv($r, ";");
|
||||
|
||||
if (count($cols) < 2) continue;
|
||||
if (!preg_match('/^\d{3}\s+[0-9A-Z]{2,3}$/', trim($cols[0]))) continue;
|
||||
|
||||
$formatoA[] = [
|
||||
"codice" => trim($cols[0]),
|
||||
"descrizione" => trim($cols[1]),
|
||||
"prezzi" => array_map(fn($x) => is_numeric(str_replace(",", ".", $x))
|
||||
? floatval(str_replace(",", ".", $x))
|
||||
: null, array_slice($cols, 2))
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
/******************************************************
|
||||
* 4) PARSER TESTUALE → FORMATO B (pagina tipo 499…)
|
||||
******************************************************/
|
||||
$formatoB = [];
|
||||
|
||||
if (isset($textJson["body"])) {
|
||||
|
||||
$text = $textJson["body"];
|
||||
|
||||
// pattern codice tipo “499 3A 14”
|
||||
$codeRegex = '/\b(\d{3}\s+[A-Z0-9]{1,2}\s+\d{2})\b/';
|
||||
|
||||
// pattern prezzo “5.475” o “5475” o “6.085”
|
||||
$priceRegex = '/\b\d{1,2}\.?\d{3}\b/';
|
||||
|
||||
// Cerca combinazioni CODICE + PREZZO ripetute sulla stessa riga
|
||||
$linee = explode("\n", $text);
|
||||
|
||||
foreach ($linee as $line) {
|
||||
|
||||
if (!preg_match_all($codeRegex, $line, $codici)) continue;
|
||||
if (!preg_match_all($priceRegex, $line, $prezzi)) continue;
|
||||
|
||||
$codici = $codici[1];
|
||||
$prezzi = $prezzi[0];
|
||||
|
||||
// Se quantità corrispondono → coppie 1:1
|
||||
if (count($codici) == count($prezzi)) {
|
||||
for ($i = 0; $i < count($codici); $i++) {
|
||||
$formatoB[] = [
|
||||
"codice" => $codici[$i],
|
||||
"prezzo" => floatval(str_replace(".", "", $prezzi[$i]))
|
||||
];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/******************************************************
|
||||
* 5) OUTPUT
|
||||
******************************************************/
|
||||
echo json_encode([
|
||||
"status" => "ok",
|
||||
"formatoA" => $formatoA,
|
||||
"formatoB" => $formatoB,
|
||||
], JSON_PRETTY_PRINT);
|
||||
@ -45,139 +45,140 @@ $pdo = $db->getConnection();
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<?php include('include/navbar.php'); ?>
|
||||
<div class="dashboard-main-wrapper wrapper">
|
||||
<?php include('include/topbar.php'); ?>
|
||||
<div class="wrapper">
|
||||
<?php include('include/navbar.php'); ?>
|
||||
<div class="dashboard-main-wrapper wrapper">
|
||||
<?php include('include/topbar.php'); ?>
|
||||
|
||||
<div class="dashboard-body">
|
||||
<div class="card shadow-sm">
|
||||
<div class="card-body">
|
||||
<div class="d-flex flex-wrap justify-content-between align-items-center mb-3">
|
||||
<h4 class="mb-0">Imported Data</h4>
|
||||
<div class="d-flex align-items-center gap-2">
|
||||
<label for="clientFilter" class="form-label mb-0 me-2">Filter by Client:</label>
|
||||
<select id="clientFilter" class="form-select form-select-sm" style="width: 220px;">
|
||||
<option value="">All Clients</option>
|
||||
<?php
|
||||
// carichiamo i clienti dal DB
|
||||
$clients = $pdo->query("SELECT idclient, client_name FROM clients ORDER BY client_name")->fetchAll(PDO::FETCH_ASSOC);
|
||||
foreach ($clients as $cl) {
|
||||
echo '<option value="' . htmlspecialchars($cl['idclient']) . '">' . htmlspecialchars($cl['client_name']) . '</option>';
|
||||
}
|
||||
?>
|
||||
</select>
|
||||
<div class="dashboard-body">
|
||||
<div class="card shadow-sm">
|
||||
<div class="card-body">
|
||||
<div class="d-flex flex-wrap justify-content-between align-items-center mb-3">
|
||||
<h4 class="mb-0">Imported Data</h4>
|
||||
<div class="d-flex align-items-center gap-2">
|
||||
<label for="clientFilter" class="form-label mb-0 me-2">Filter by Client:</label>
|
||||
<select id="clientFilter" class="form-select form-select-sm" style="width: 220px;">
|
||||
<option value="">All Clients</option>
|
||||
<?php
|
||||
// carichiamo i clienti dal DB
|
||||
$clients = $pdo->query("SELECT idclient, client_name FROM clients ORDER BY client_name")->fetchAll(PDO::FETCH_ASSOC);
|
||||
foreach ($clients as $cl) {
|
||||
echo '<option value="' . htmlspecialchars($cl['idclient']) . '">' . htmlspecialchars($cl['client_name']) . '</option>';
|
||||
}
|
||||
?>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="d-flex justify-content-between align-items-center mb-2">
|
||||
<div>
|
||||
<button id="updatePricesBtn" class="btn btn-warning btn-sm" style="display:none;">
|
||||
<i class="fas fa-sync-alt"></i> Aggiorna Prezzi
|
||||
</button>
|
||||
<div class="d-flex justify-content-between align-items-center mb-2">
|
||||
<div>
|
||||
<button id="updatePricesBtn" class="btn btn-warning btn-sm" style="display:none;">
|
||||
<i class="fas fa-sync-alt"></i> Aggiorna Prezzi
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="table-responsive">
|
||||
<table id="importTable" class="table table-striped table-bordered w-100">
|
||||
<div class="table-responsive">
|
||||
<table id="importTable" class="table table-striped table-bordered w-100">
|
||||
|
||||
<thead>
|
||||
<tr>
|
||||
<th></th> <!-- Checkbox column -->
|
||||
<th>ID</th>
|
||||
<th>Collection</th>
|
||||
<th>Category</th>
|
||||
<th>Model</th>
|
||||
<th>Model Description</th>
|
||||
<th>Mod.Code</th>
|
||||
<th>Description</th>
|
||||
<th>Finish.Code</th>
|
||||
<th>Finishing Description</th>
|
||||
<th>Available Qty</th>
|
||||
<th>WH</th>
|
||||
<th>Price</th>
|
||||
<th>Actions</th>
|
||||
</tr>
|
||||
</thead>
|
||||
</table>
|
||||
<thead>
|
||||
<tr>
|
||||
<th></th> <!-- Checkbox column -->
|
||||
<th>ID</th>
|
||||
<th>Collection</th>
|
||||
<th>Category</th>
|
||||
<th>Model</th>
|
||||
<th>Model Description</th>
|
||||
<th>Mod.Code</th>
|
||||
<th>Description</th>
|
||||
<th>Finish.Code</th>
|
||||
<th>Finishing Description</th>
|
||||
<th>Available Qty</th>
|
||||
<th>WH</th>
|
||||
<th>Price</th>
|
||||
<th>Actions</th>
|
||||
</tr>
|
||||
</thead>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<?php include('include/footer.php'); ?>
|
||||
</div>
|
||||
|
||||
<?php include('include/footer.php'); ?>
|
||||
</div>
|
||||
<?php include('jsinclude.php'); ?>
|
||||
<script src="https://cdn.datatables.net/1.13.6/js/jquery.dataTables.min.js"></script>
|
||||
<script src="https://cdn.datatables.net/1.13.6/js/dataTables.bootstrap5.min.js"></script>
|
||||
|
||||
<?php include('jsinclude.php'); ?>
|
||||
<script src="https://cdn.datatables.net/1.13.6/js/jquery.dataTables.min.js"></script>
|
||||
<script src="https://cdn.datatables.net/1.13.6/js/dataTables.bootstrap5.min.js"></script>
|
||||
|
||||
<script>
|
||||
$(document).ready(function() {
|
||||
let table = $('#importTable').DataTable({
|
||||
ajax: {
|
||||
url: 'import_list_data.php',
|
||||
dataSrc: ''
|
||||
},
|
||||
columns: [{
|
||||
data: null,
|
||||
orderable: false,
|
||||
searchable: false,
|
||||
className: 'text-center',
|
||||
render: function() {
|
||||
return '<input type="checkbox" class="row-check" />';
|
||||
}
|
||||
<script>
|
||||
$(document).ready(function() {
|
||||
let table = $('#importTable').DataTable({
|
||||
ajax: {
|
||||
url: 'import_list_data.php',
|
||||
dataSrc: ''
|
||||
},
|
||||
{
|
||||
data: 'id'
|
||||
},
|
||||
{
|
||||
data: 'collection',
|
||||
className: 'editable'
|
||||
},
|
||||
{
|
||||
data: 'category',
|
||||
className: 'noedit text-muted'
|
||||
},
|
||||
{
|
||||
data: 'model',
|
||||
className: 'editable'
|
||||
},
|
||||
{
|
||||
data: 'model_description',
|
||||
className: 'editable'
|
||||
},
|
||||
{
|
||||
data: 'mod_code',
|
||||
className: 'text-muted'
|
||||
},
|
||||
{
|
||||
data: 'description',
|
||||
className: 'editable'
|
||||
},
|
||||
{
|
||||
data: 'finish_code',
|
||||
className: 'editable'
|
||||
},
|
||||
{
|
||||
data: 'finishing_description',
|
||||
className: 'editable'
|
||||
},
|
||||
{
|
||||
data: 'available_qty',
|
||||
className: 'editable'
|
||||
},
|
||||
{
|
||||
data: 'wh',
|
||||
className: 'editable'
|
||||
},
|
||||
{
|
||||
data: 'price',
|
||||
className: 'editable'
|
||||
},
|
||||
{
|
||||
data: null,
|
||||
orderable: false,
|
||||
render: function(data, type, row) {
|
||||
return `
|
||||
columns: [{
|
||||
data: null,
|
||||
orderable: false,
|
||||
searchable: false,
|
||||
className: 'text-center',
|
||||
render: function() {
|
||||
return '<input type="checkbox" class="row-check" />';
|
||||
}
|
||||
},
|
||||
{
|
||||
data: 'id'
|
||||
},
|
||||
{
|
||||
data: 'collection',
|
||||
className: 'editable'
|
||||
},
|
||||
{
|
||||
data: 'category',
|
||||
className: 'noedit text-muted'
|
||||
},
|
||||
{
|
||||
data: 'model',
|
||||
className: 'editable'
|
||||
},
|
||||
{
|
||||
data: 'model_description',
|
||||
className: 'editable'
|
||||
},
|
||||
{
|
||||
data: 'mod_code',
|
||||
className: 'text-muted'
|
||||
},
|
||||
{
|
||||
data: 'description',
|
||||
className: 'editable'
|
||||
},
|
||||
{
|
||||
data: 'finish_code',
|
||||
className: 'editable'
|
||||
},
|
||||
{
|
||||
data: 'finishing_description',
|
||||
className: 'editable'
|
||||
},
|
||||
{
|
||||
data: 'available_qty',
|
||||
className: 'editable'
|
||||
},
|
||||
{
|
||||
data: 'wh',
|
||||
className: 'editable'
|
||||
},
|
||||
{
|
||||
data: 'price',
|
||||
className: 'editable'
|
||||
},
|
||||
{
|
||||
data: null,
|
||||
orderable: false,
|
||||
render: function(data, type, row) {
|
||||
return `
|
||||
<button class="btn btn-success btn-sm action-btn save-row" data-id="${row.id}">
|
||||
<i class="fas fa-save"></i>
|
||||
</button>
|
||||
@ -185,228 +186,239 @@ $pdo = $db->getConnection();
|
||||
<i class="fas fa-trash"></i>
|
||||
</button>
|
||||
`;
|
||||
}
|
||||
}
|
||||
}
|
||||
],
|
||||
pageLength: 25,
|
||||
order: [
|
||||
[1, 'desc']
|
||||
],
|
||||
responsive: true,
|
||||
autoWidth: false
|
||||
});
|
||||
],
|
||||
pageLength: 25,
|
||||
order: [
|
||||
[1, 'desc']
|
||||
],
|
||||
responsive: true,
|
||||
autoWidth: false
|
||||
});
|
||||
|
||||
|
||||
// === Filtro per Client ===
|
||||
$('#clientFilter').on('change', function() {
|
||||
const selectedClient = $(this).val();
|
||||
// === Filtro per Client ===
|
||||
$('#clientFilter').on('change', function() {
|
||||
const selectedClient = $(this).val();
|
||||
|
||||
// Ricarica i dati passando il parametro idclient
|
||||
table.ajax.url('import_list_data.php' + (selectedClient ? '?idclient=' + selectedClient : '')).load();
|
||||
});
|
||||
// Ricarica i dati passando il parametro idclient
|
||||
table.ajax.url('import_list_data.php' + (selectedClient ? '?idclient=' + selectedClient : '')).load();
|
||||
});
|
||||
|
||||
|
||||
// === Inline Editing ===
|
||||
$('#importTable tbody').on('click', 'td.editable', function() {
|
||||
const cell = table.cell(this);
|
||||
const rowData = table.row(cell.index().row).data();
|
||||
const colName = table.column(cell.index().column).dataSrc();
|
||||
const originalValue = rowData[colName] ?? '';
|
||||
// === Inline Editing ===
|
||||
$('#importTable tbody').on('click', 'td.editable', function() {
|
||||
const cell = table.cell(this);
|
||||
const rowData = table.row(cell.index().row).data();
|
||||
const colName = table.column(cell.index().column).dataSrc();
|
||||
const originalValue = rowData[colName] ?? '';
|
||||
|
||||
// Evita doppia apertura
|
||||
if ($(this).find('input').length > 0) return;
|
||||
// Evita doppia apertura
|
||||
if ($(this).find('input').length > 0) return;
|
||||
|
||||
const input = $('<input type="text" class="form-control form-control-sm"/>')
|
||||
.val(originalValue)
|
||||
.css({
|
||||
'min-width': '120px',
|
||||
'height': '24px',
|
||||
'padding': '0 4px'
|
||||
const input = $('<input type="text" class="form-control form-control-sm"/>')
|
||||
.val(originalValue)
|
||||
.css({
|
||||
'min-width': '120px',
|
||||
'height': '24px',
|
||||
'padding': '0 4px'
|
||||
});
|
||||
|
||||
$(this).html(input);
|
||||
input.focus();
|
||||
|
||||
// Gestione conferma o uscita
|
||||
input.on('blur keydown', function(e) {
|
||||
if (e.type === 'blur' || e.key === 'Enter') {
|
||||
const newValue = $(this).val();
|
||||
// Aggiorna in memoria e interfaccia
|
||||
rowData[colName] = newValue;
|
||||
table.row(cell.index().row).data(rowData).draw(false);
|
||||
|
||||
const td = $(table.cell(cell.index()).node());
|
||||
if (newValue !== originalValue) {
|
||||
td.addClass('modified'); // evidenzia modificato
|
||||
} else {
|
||||
td.removeClass('modified');
|
||||
}
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
// === Salvataggio singola riga ===
|
||||
$('#importTable').on('click', '.save-row', function() {
|
||||
const rowData = table.row($(this).closest('tr')).data();
|
||||
$.ajax({
|
||||
url: 'import_list_save.php',
|
||||
type: 'POST',
|
||||
data: {
|
||||
row: JSON.stringify(rowData)
|
||||
},
|
||||
success: function(res) {
|
||||
Swal.close();
|
||||
|
||||
// Se il server restituisce già JSON, non serve JSON.parse()
|
||||
let json = (typeof res === 'object') ? res : null;
|
||||
if (!json) {
|
||||
try {
|
||||
json = JSON.parse(res);
|
||||
} catch (e) {
|
||||
console.error("Invalid JSON from server:", res);
|
||||
Swal.fire('Errore', 'Risposta non valida dal server.', 'error');
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if (json.status === 'ok') {
|
||||
Swal.fire({
|
||||
title: 'Successo',
|
||||
text: `Aggiornati ${json.updated} prezzi.`,
|
||||
icon: 'success',
|
||||
timer: 2000,
|
||||
showConfirmButton: false
|
||||
});
|
||||
table.ajax.reload(null, false); // 🔁 aggiorna tabella
|
||||
} else {
|
||||
console.error("Errore backend:", json);
|
||||
Swal.fire('Errore', json.message || 'Errore sconosciuto.', 'error');
|
||||
}
|
||||
},
|
||||
error: function(xhr, status, err) {
|
||||
Swal.fire('Errore', 'Errore di rete o server non raggiungibile.', 'error');
|
||||
console.error("AJAX error:", status, err, xhr.responseText);
|
||||
}
|
||||
|
||||
});
|
||||
});
|
||||
|
||||
// === Eliminazione con conferma ===
|
||||
$('#importTable').on('click', '.delete-row', function() {
|
||||
const id = $(this).data('id');
|
||||
Swal.fire({
|
||||
title: 'Are you sure?',
|
||||
text: 'This record will be permanently deleted.',
|
||||
icon: 'warning',
|
||||
showCancelButton: true,
|
||||
confirmButtonText: 'Yes, delete it',
|
||||
cancelButtonText: 'Cancel'
|
||||
}).then(result => {
|
||||
if (result.isConfirmed) {
|
||||
$.ajax({
|
||||
url: 'import_list_delete.php',
|
||||
type: 'POST',
|
||||
data: {
|
||||
id: id
|
||||
},
|
||||
success: function(res) {
|
||||
let json;
|
||||
try {
|
||||
json = JSON.parse(res);
|
||||
} catch (e) {
|
||||
Swal.fire('Error', res, 'error');
|
||||
return;
|
||||
}
|
||||
if (json.status === 'ok') {
|
||||
Swal.fire('Deleted', 'Row removed successfully.', 'success');
|
||||
table.ajax.reload(null, false);
|
||||
} else {
|
||||
Swal.fire('Error', json.message, 'error');
|
||||
}
|
||||
},
|
||||
error: function() {
|
||||
Swal.fire('Error', 'Request failed.', 'error');
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
// === Gestione selezione righe e pulsante Aggiorna Prezzi ===
|
||||
$('#importTable').on('change', '.row-check', function() {
|
||||
const checkedCount = $('#importTable .row-check:checked').length;
|
||||
$('#updatePricesBtn').toggle(checkedCount > 0);
|
||||
});
|
||||
|
||||
// === Aggiornamento prezzi con ChatPDF ===
|
||||
$('#updatePricesBtn').on('click', function() {
|
||||
const selectedRows = [];
|
||||
|
||||
$('#importTable .row-check:checked').each(function() {
|
||||
const row = table.row($(this).closest('tr')).data();
|
||||
if (row) selectedRows.push(row);
|
||||
});
|
||||
|
||||
$(this).html(input);
|
||||
input.focus();
|
||||
|
||||
// Gestione conferma o uscita
|
||||
input.on('blur keydown', function(e) {
|
||||
if (e.type === 'blur' || e.key === 'Enter') {
|
||||
const newValue = $(this).val();
|
||||
// Aggiorna in memoria e interfaccia
|
||||
rowData[colName] = newValue;
|
||||
table.row(cell.index().row).data(rowData).draw(false);
|
||||
|
||||
const td = $(table.cell(cell.index()).node());
|
||||
if (newValue !== originalValue) {
|
||||
td.addClass('modified'); // evidenzia modificato
|
||||
} else {
|
||||
td.removeClass('modified');
|
||||
}
|
||||
if (selectedRows.length === 0) {
|
||||
Swal.fire('Notice', 'Seleziona almeno una riga.', 'info');
|
||||
return;
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
// === Salvataggio singola riga ===
|
||||
$('#importTable').on('click', '.save-row', function() {
|
||||
const rowData = table.row($(this).closest('tr')).data();
|
||||
$.ajax({
|
||||
url: 'import_list_save.php',
|
||||
type: 'POST',
|
||||
data: {
|
||||
row: JSON.stringify(rowData)
|
||||
},
|
||||
success: function(res) {
|
||||
Swal.close();
|
||||
const clientId = $('#clientFilter').val();
|
||||
if (!clientId) {
|
||||
Swal.fire('Notice', 'Seleziona prima un cliente.', 'info');
|
||||
return;
|
||||
}
|
||||
|
||||
// Se il server restituisce già JSON, non serve JSON.parse()
|
||||
let json = (typeof res === 'object') ? res : null;
|
||||
if (!json) {
|
||||
try {
|
||||
json = JSON.parse(res);
|
||||
} catch (e) {
|
||||
console.error("Invalid JSON from server:", res);
|
||||
Swal.fire('Errore', 'Risposta non valida dal server.', 'error');
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if (json.status === 'ok') {
|
||||
Swal.fire({
|
||||
title: 'Aggiornare i prezzi?',
|
||||
text: `Verranno interrogati i prezzi per ${selectedRows.length} prodotti.`,
|
||||
icon: 'question',
|
||||
showCancelButton: true,
|
||||
confirmButtonText: 'Procedi',
|
||||
cancelButtonText: 'Annulla'
|
||||
}).then(result => {
|
||||
if (result.isConfirmed) {
|
||||
Swal.fire({
|
||||
title: 'Successo',
|
||||
text: `Aggiornati ${json.updated} prezzi.`,
|
||||
icon: 'success',
|
||||
timer: 2000,
|
||||
showConfirmButton: false
|
||||
title: 'Aggiornamento in corso...',
|
||||
allowOutsideClick: false,
|
||||
didOpen: () => Swal.showLoading()
|
||||
});
|
||||
|
||||
$.ajax({
|
||||
url: 'update_prices_chatpdf.php',
|
||||
type: 'POST',
|
||||
data: {
|
||||
rows: JSON.stringify(selectedRows),
|
||||
idclient: clientId
|
||||
},
|
||||
success: function(res) {
|
||||
Swal.close();
|
||||
|
||||
// Se il server ha già restituito un oggetto JSON, usiamolo direttamente
|
||||
let json = (typeof res === "object") ? res : null;
|
||||
|
||||
// Se invece è una stringa JSON, parse
|
||||
if (!json) {
|
||||
try {
|
||||
json = JSON.parse(res);
|
||||
} catch (e) {
|
||||
console.error("JSON parse error:", res);
|
||||
Swal.fire('Errore', 'Risposta non valida dal server.', 'error');
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// Ora siamo sicuri che json è un oggetto valido
|
||||
if (json.status === 'ok') {
|
||||
Swal.fire('Successo', `Aggiornati ${json.updated} prezzi.`, 'success');
|
||||
table.ajax.reload(null, false);
|
||||
} else {
|
||||
Swal.fire('Errore', json.message || 'Errore sconosciuto.', 'error');
|
||||
}
|
||||
},
|
||||
|
||||
error: function() {
|
||||
Swal.fire('Errore', 'Aggiornamento fallito.', 'error');
|
||||
}
|
||||
});
|
||||
table.ajax.reload(null, false); // 🔁 aggiorna tabella
|
||||
} else {
|
||||
console.error("Errore backend:", json);
|
||||
Swal.fire('Errore', json.message || 'Errore sconosciuto.', 'error');
|
||||
}
|
||||
},
|
||||
error: function(xhr, status, err) {
|
||||
Swal.fire('Errore', 'Errore di rete o server non raggiungibile.', 'error');
|
||||
console.error("AJAX error:", status, err, xhr.responseText);
|
||||
}
|
||||
|
||||
});
|
||||
});
|
||||
|
||||
// === Eliminazione con conferma ===
|
||||
$('#importTable').on('click', '.delete-row', function() {
|
||||
const id = $(this).data('id');
|
||||
Swal.fire({
|
||||
title: 'Are you sure?',
|
||||
text: 'This record will be permanently deleted.',
|
||||
icon: 'warning',
|
||||
showCancelButton: true,
|
||||
confirmButtonText: 'Yes, delete it',
|
||||
cancelButtonText: 'Cancel'
|
||||
}).then(result => {
|
||||
if (result.isConfirmed) {
|
||||
$.ajax({
|
||||
url: 'import_list_delete.php',
|
||||
type: 'POST',
|
||||
data: {
|
||||
id: id
|
||||
},
|
||||
success: function(res) {
|
||||
let json;
|
||||
try {
|
||||
json = JSON.parse(res);
|
||||
} catch (e) {
|
||||
Swal.fire('Error', res, 'error');
|
||||
return;
|
||||
}
|
||||
if (json.status === 'ok') {
|
||||
Swal.fire('Deleted', 'Row removed successfully.', 'success');
|
||||
table.ajax.reload(null, false);
|
||||
} else {
|
||||
Swal.fire('Error', json.message, 'error');
|
||||
}
|
||||
},
|
||||
error: function() {
|
||||
Swal.fire('Error', 'Request failed.', 'error');
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
// === Gestione selezione righe e pulsante Aggiorna Prezzi ===
|
||||
$('#importTable').on('change', '.row-check', function() {
|
||||
const checkedCount = $('#importTable .row-check:checked').length;
|
||||
$('#updatePricesBtn').toggle(checkedCount > 0);
|
||||
});
|
||||
|
||||
// === Aggiornamento prezzi con ChatPDF ===
|
||||
$('#updatePricesBtn').on('click', function() {
|
||||
const selectedRows = [];
|
||||
|
||||
$('#importTable .row-check:checked').each(function() {
|
||||
const row = table.row($(this).closest('tr')).data();
|
||||
if (row) selectedRows.push(row);
|
||||
});
|
||||
});
|
||||
|
||||
if (selectedRows.length === 0) {
|
||||
Swal.fire('Notice', 'Seleziona almeno una riga.', 'info');
|
||||
return;
|
||||
}
|
||||
|
||||
const clientId = $('#clientFilter').val();
|
||||
if (!clientId) {
|
||||
Swal.fire('Notice', 'Seleziona prima un cliente.', 'info');
|
||||
return;
|
||||
}
|
||||
|
||||
Swal.fire({
|
||||
title: 'Aggiornare i prezzi?',
|
||||
text: `Verranno interrogati i prezzi per ${selectedRows.length} prodotti.`,
|
||||
icon: 'question',
|
||||
showCancelButton: true,
|
||||
confirmButtonText: 'Procedi',
|
||||
cancelButtonText: 'Annulla'
|
||||
}).then(result => {
|
||||
if (result.isConfirmed) {
|
||||
Swal.fire({
|
||||
title: 'Aggiornamento in corso...',
|
||||
allowOutsideClick: false,
|
||||
didOpen: () => Swal.showLoading()
|
||||
});
|
||||
|
||||
$.ajax({
|
||||
url: 'update_prices_chatpdf.php',
|
||||
type: 'POST',
|
||||
data: {
|
||||
rows: JSON.stringify(selectedRows),
|
||||
idclient: clientId
|
||||
},
|
||||
success: function(res) {
|
||||
Swal.close();
|
||||
let json;
|
||||
try {
|
||||
json = JSON.parse(res);
|
||||
} catch (e) {
|
||||
Swal.fire('Errore', res, 'error');
|
||||
return;
|
||||
}
|
||||
if (json.status === 'ok') {
|
||||
Swal.fire('Successo', `Aggiornati ${json.updated} prezzi.`, 'success');
|
||||
table.ajax.reload(null, false);
|
||||
} else {
|
||||
Swal.fire('Errore', json.message, 'error');
|
||||
}
|
||||
},
|
||||
error: function() {
|
||||
Swal.fire('Errore', 'Aggiornamento fallito.', 'error');
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
});
|
||||
</script>
|
||||
</script>
|
||||
</div> <!-- end wrapper -->
|
||||
</body>
|
||||
|
||||
</html>
|
||||
@ -22,9 +22,10 @@
|
||||
<ul>
|
||||
<!-- <li> <a href="index.php"><i class='bx bx-radio-circle'></i>Default</a>
|
||||
</li> -->
|
||||
<li> <a href="import_dashboard.php"><i class='bx bx-radio-circle'></i>XLS Import</a>
|
||||
<li> <a href="import_xls.php"><i class='bx bx-radio-circle'></i>XLS Import</a>
|
||||
</li>
|
||||
<li> <a href="import_list.php"><i class='bx bx-radio-circle'></i>Imported LIST</a>
|
||||
</li>
|
||||
|
||||
|
||||
</ul>
|
||||
</li>
|
||||
|
||||
217
public/userarea/jsonraw.php
Normal file
217
public/userarea/jsonraw.php
Normal file
@ -0,0 +1,217 @@
|
||||
<?php
|
||||
header("Content-Type: application/json; charset=utf-8");
|
||||
|
||||
// ---------------------------------------------------------
|
||||
// 1) CONFIGURAZIONE
|
||||
// ---------------------------------------------------------
|
||||
$apiKey = "info@claudiosironi.com_Qfh02D7sAvi2tcx3ZchHpusNaBquCKhJw81fEnkHe2ersQDVOex4IokhCCzaFAz1";
|
||||
$fileToken = "filetoken://61a780917907f86a340290d22c449357dc68950e9066bd67b2";
|
||||
|
||||
// opzionale: passare un token via GET
|
||||
if (isset($_GET['token']) && $_GET['token'] !== "") {
|
||||
$fileToken = $_GET['token'];
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------
|
||||
// 2) PDF.CO → JSON2 (TUTTE LE PAGINE)
|
||||
// ---------------------------------------------------------
|
||||
$endpoint = "https://api.pdf.co/v1/pdf/convert/to/json2";
|
||||
|
||||
$payload = [
|
||||
"url" => $fileToken,
|
||||
"inline" => true,
|
||||
"detectTables" => true,
|
||||
"pages" => "all"
|
||||
];
|
||||
|
||||
$ch = curl_init($endpoint);
|
||||
curl_setopt($ch, CURLOPT_POST, true);
|
||||
curl_setopt($ch, CURLOPT_HTTPHEADER, [
|
||||
"Content-Type: application/json",
|
||||
"x-api-key: $apiKey"
|
||||
]);
|
||||
curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode($payload));
|
||||
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
|
||||
|
||||
$response = curl_exec($ch);
|
||||
|
||||
if (!$response) {
|
||||
echo json_encode(["error" => "Errore CURL: " . curl_error($ch)]);
|
||||
exit;
|
||||
}
|
||||
|
||||
curl_close($ch);
|
||||
|
||||
$json = json_decode($response, true);
|
||||
|
||||
// ---------------------------------------------------------
|
||||
// 3) VALIDAZIONE
|
||||
// ---------------------------------------------------------
|
||||
if (!isset($json["body"]["document"]["page"])) {
|
||||
echo json_encode([
|
||||
"error" => true,
|
||||
"message" => "PDF.co JSON senza pagine valide",
|
||||
"raw" => $json
|
||||
]);
|
||||
exit;
|
||||
}
|
||||
|
||||
$pages = $json["body"]["document"]["page"];
|
||||
|
||||
// ---------------------------------------------------------
|
||||
// 4) FUNZIONI UTILI
|
||||
// ---------------------------------------------------------
|
||||
|
||||
// Formato A – 291 02F
|
||||
function isCodeTypeA($txt)
|
||||
{
|
||||
return preg_match('/^\d{3}\s+[0-9A-Z]{2,3}$/', $txt);
|
||||
}
|
||||
|
||||
// Formato C – blocchi ripetuti (499 3A 14 5.475)
|
||||
function extractTypeCBlocks($line)
|
||||
{
|
||||
preg_match_all(
|
||||
'/(\d{3})\s+([0-9A-Z]{1,3})\s+(\d{2})\s+(\d{1,3}\.\d{3})/',
|
||||
$line,
|
||||
$m,
|
||||
PREG_SET_ORDER
|
||||
);
|
||||
|
||||
$out = [];
|
||||
foreach ($m as $b) {
|
||||
$out[] = [
|
||||
"codice" => $b[1],
|
||||
"variante" => $b[2],
|
||||
"dimensione" => $b[3],
|
||||
"prezzo" => floatval(str_replace(".", "", $b[4]))
|
||||
];
|
||||
}
|
||||
return $out;
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------
|
||||
// 5) PARSER PER UNA SINGOLA RIGA (ricostruita)
|
||||
// ---------------------------------------------------------
|
||||
function parseLine($line)
|
||||
{
|
||||
// FORMATO A
|
||||
if (preg_match('/^(\d{3}\s+[0-9A-Z]{2,3})\s+(.*)$/', $line, $mA)) {
|
||||
|
||||
$codice = trim($mA[1]);
|
||||
$resto = trim($mA[2]);
|
||||
|
||||
preg_match_all('/\d{1,3}[.,]\d{2,3}/', $resto, $matchesPrezzi);
|
||||
|
||||
if (count($matchesPrezzi[0]) >= 2) {
|
||||
|
||||
$descr = trim(preg_replace('/\d{1,3}[.,]\d{2,3}/', '', $resto));
|
||||
|
||||
$vals = array_map(function ($v) {
|
||||
return floatval(str_replace(",", ".", str_replace(".", "", $v)));
|
||||
}, $matchesPrezzi[0]);
|
||||
|
||||
return [
|
||||
"type" => "A",
|
||||
"codice" => $codice,
|
||||
"descrizione" => $descr,
|
||||
"prezzi" => $vals
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
// FORMATO B1
|
||||
if (preg_match(
|
||||
'/^(\d{3}\s+[A-Z]{1,3})\s+([A-Za-zÀ-ù0-9\s]+?)\s+((?:\d{1,3}[.,]\d{2,3}\s*){4,})$/',
|
||||
$line,
|
||||
$mB
|
||||
)) {
|
||||
$codice = trim($mB[1]);
|
||||
$descr = trim($mB[2]);
|
||||
$valuesString = trim($mB[3]);
|
||||
|
||||
preg_match_all('/\d{1,3}[.,]\d{2,3}/', $valuesString, $vv);
|
||||
|
||||
$vals = array_map(function ($v) {
|
||||
return floatval(str_replace(",", ".", str_replace(".", "", $v)));
|
||||
}, $vv[0]);
|
||||
|
||||
return [
|
||||
"type" => "B1",
|
||||
"codice" => $codice,
|
||||
"descrizione" => $descr,
|
||||
"prezzi" => $vals
|
||||
];
|
||||
}
|
||||
|
||||
// FORMATO C (ripetuto in riga)
|
||||
$blocks = extractTypeCBlocks($line);
|
||||
|
||||
if (!empty($blocks)) {
|
||||
|
||||
$desc = $line;
|
||||
foreach ($blocks as $b) {
|
||||
$pattern = sprintf(
|
||||
'/%s\s+%s\s+%s\s+\d{1,3}\.\d{3}/',
|
||||
$b["codice"],
|
||||
$b["variante"],
|
||||
$b["dimensione"]
|
||||
);
|
||||
$desc = preg_replace($pattern, "", $desc);
|
||||
}
|
||||
|
||||
$desc = trim($desc);
|
||||
|
||||
return [
|
||||
"type" => "C",
|
||||
"descrizione" => $desc,
|
||||
"varianti" => $blocks
|
||||
];
|
||||
}
|
||||
|
||||
// Nessun match
|
||||
return null;
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------
|
||||
// 6) PROCESSAMENTO DI TUTTE LE PAGINE
|
||||
// ---------------------------------------------------------
|
||||
$items = [];
|
||||
|
||||
foreach ($pages as $p) {
|
||||
|
||||
if (!isset($p["row"])) continue;
|
||||
|
||||
// Ricostruisco tutte le vere righe (“flat lines”)
|
||||
$flatLines = [];
|
||||
|
||||
foreach ($p["row"] as $row) {
|
||||
$line = "";
|
||||
foreach ($row["column"] as $col) {
|
||||
if (!empty($col["text"]["text"])) {
|
||||
$line .= " " . trim($col["text"]["text"]);
|
||||
}
|
||||
}
|
||||
$line = trim($line);
|
||||
if ($line !== "") {
|
||||
$flatLines[] = $line;
|
||||
}
|
||||
}
|
||||
|
||||
// Applico i parser su ogni riga ricostruita
|
||||
foreach ($flatLines as $l) {
|
||||
$parsed = parseLine($l);
|
||||
if ($parsed !== null) {
|
||||
$items[] = $parsed;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------
|
||||
// 7) OUTPUT FINALE
|
||||
// ---------------------------------------------------------
|
||||
echo json_encode([
|
||||
"status" => "ok",
|
||||
"total" => count($items),
|
||||
"items" => $items
|
||||
], JSON_PRETTY_PRINT);
|
||||
BIN
public/userarea/listino.pdf
Normal file
BIN
public/userarea/listino.pdf
Normal file
Binary file not shown.
4
vendor/composer/autoload_classmap.php
vendored
4
vendor/composer/autoload_classmap.php
vendored
@ -6660,6 +6660,10 @@ return array(
|
||||
'Spatie\\LaravelPackageTools\\Exceptions\\InvalidPackage' => $vendorDir . '/spatie/laravel-package-tools/src/Exceptions/InvalidPackage.php',
|
||||
'Spatie\\LaravelPackageTools\\Package' => $vendorDir . '/spatie/laravel-package-tools/src/Package.php',
|
||||
'Spatie\\LaravelPackageTools\\PackageServiceProvider' => $vendorDir . '/spatie/laravel-package-tools/src/PackageServiceProvider.php',
|
||||
'Spatie\\PdfToText\\Exceptions\\BinaryNotFoundException' => $vendorDir . '/spatie/pdf-to-text/src/Exceptions/BinaryNotFoundException.php',
|
||||
'Spatie\\PdfToText\\Exceptions\\CouldNotExtractText' => $vendorDir . '/spatie/pdf-to-text/src/Exceptions/CouldNotExtractText.php',
|
||||
'Spatie\\PdfToText\\Exceptions\\PdfNotFound' => $vendorDir . '/spatie/pdf-to-text/src/Exceptions/PdfNotFound.php',
|
||||
'Spatie\\PdfToText\\Pdf' => $vendorDir . '/spatie/pdf-to-text/src/Pdf.php',
|
||||
'Spatie\\QueryBuilder\\AllowedFilter' => $vendorDir . '/spatie/laravel-query-builder/src/AllowedFilter.php',
|
||||
'Spatie\\QueryBuilder\\AllowedInclude' => $vendorDir . '/spatie/laravel-query-builder/src/AllowedInclude.php',
|
||||
'Spatie\\QueryBuilder\\AllowedSort' => $vendorDir . '/spatie/laravel-query-builder/src/AllowedSort.php',
|
||||
|
||||
1
vendor/composer/autoload_psr4.php
vendored
1
vendor/composer/autoload_psr4.php
vendored
@ -54,6 +54,7 @@ return array(
|
||||
'Symfony\\Component\\Clock\\' => array($vendorDir . '/symfony/clock'),
|
||||
'Spatie\\QueryBuilder\\Database\\Factories\\' => array($vendorDir . '/spatie/laravel-query-builder/database/factories'),
|
||||
'Spatie\\QueryBuilder\\' => array($vendorDir . '/spatie/laravel-query-builder/src'),
|
||||
'Spatie\\PdfToText\\' => array($vendorDir . '/spatie/pdf-to-text/src'),
|
||||
'Spatie\\LaravelPackageTools\\' => array($vendorDir . '/spatie/laravel-package-tools/src'),
|
||||
'Spatie\\LaravelIgnition\\' => array($vendorDir . '/spatie/laravel-ignition/src', $vendorDir . '/spatie/error-solutions/legacy/laravel-ignition'),
|
||||
'Spatie\\Ignition\\' => array($vendorDir . '/spatie/ignition/src', $vendorDir . '/spatie/error-solutions/legacy/ignition'),
|
||||
|
||||
9
vendor/composer/autoload_static.php
vendored
9
vendor/composer/autoload_static.php
vendored
@ -114,6 +114,7 @@ class ComposerStaticInitc91cd9c5b1e6a9e8573a14b799ea9342
|
||||
'Symfony\\Component\\Clock\\' => 24,
|
||||
'Spatie\\QueryBuilder\\Database\\Factories\\' => 39,
|
||||
'Spatie\\QueryBuilder\\' => 20,
|
||||
'Spatie\\PdfToText\\' => 17,
|
||||
'Spatie\\LaravelPackageTools\\' => 27,
|
||||
'Spatie\\LaravelIgnition\\' => 23,
|
||||
'Spatie\\Ignition\\' => 16,
|
||||
@ -440,6 +441,10 @@ class ComposerStaticInitc91cd9c5b1e6a9e8573a14b799ea9342
|
||||
array (
|
||||
0 => __DIR__ . '/..' . '/spatie/laravel-query-builder/src',
|
||||
),
|
||||
'Spatie\\PdfToText\\' =>
|
||||
array (
|
||||
0 => __DIR__ . '/..' . '/spatie/pdf-to-text/src',
|
||||
),
|
||||
'Spatie\\LaravelPackageTools\\' =>
|
||||
array (
|
||||
0 => __DIR__ . '/..' . '/spatie/laravel-package-tools/src',
|
||||
@ -7483,6 +7488,10 @@ class ComposerStaticInitc91cd9c5b1e6a9e8573a14b799ea9342
|
||||
'Spatie\\LaravelPackageTools\\Exceptions\\InvalidPackage' => __DIR__ . '/..' . '/spatie/laravel-package-tools/src/Exceptions/InvalidPackage.php',
|
||||
'Spatie\\LaravelPackageTools\\Package' => __DIR__ . '/..' . '/spatie/laravel-package-tools/src/Package.php',
|
||||
'Spatie\\LaravelPackageTools\\PackageServiceProvider' => __DIR__ . '/..' . '/spatie/laravel-package-tools/src/PackageServiceProvider.php',
|
||||
'Spatie\\PdfToText\\Exceptions\\BinaryNotFoundException' => __DIR__ . '/..' . '/spatie/pdf-to-text/src/Exceptions/BinaryNotFoundException.php',
|
||||
'Spatie\\PdfToText\\Exceptions\\CouldNotExtractText' => __DIR__ . '/..' . '/spatie/pdf-to-text/src/Exceptions/CouldNotExtractText.php',
|
||||
'Spatie\\PdfToText\\Exceptions\\PdfNotFound' => __DIR__ . '/..' . '/spatie/pdf-to-text/src/Exceptions/PdfNotFound.php',
|
||||
'Spatie\\PdfToText\\Pdf' => __DIR__ . '/..' . '/spatie/pdf-to-text/src/Pdf.php',
|
||||
'Spatie\\QueryBuilder\\AllowedFilter' => __DIR__ . '/..' . '/spatie/laravel-query-builder/src/AllowedFilter.php',
|
||||
'Spatie\\QueryBuilder\\AllowedInclude' => __DIR__ . '/..' . '/spatie/laravel-query-builder/src/AllowedInclude.php',
|
||||
'Spatie\\QueryBuilder\\AllowedSort' => __DIR__ . '/..' . '/spatie/laravel-query-builder/src/AllowedSort.php',
|
||||
|
||||
61
vendor/composer/installed.json
vendored
61
vendor/composer/installed.json
vendored
@ -8827,6 +8827,67 @@
|
||||
],
|
||||
"install-path": "../spatie/laravel-query-builder"
|
||||
},
|
||||
{
|
||||
"name": "spatie/pdf-to-text",
|
||||
"version": "1.54.1",
|
||||
"version_normalized": "1.54.1.0",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/spatie/pdf-to-text.git",
|
||||
"reference": "ad2a792c7e1e68f1bc9b038dc73cdcf760595fbb"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/spatie/pdf-to-text/zipball/ad2a792c7e1e68f1bc9b038dc73cdcf760595fbb",
|
||||
"reference": "ad2a792c7e1e68f1bc9b038dc73cdcf760595fbb",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
"php": "^7.4|^8.0",
|
||||
"symfony/process": "^4.0|^5.0|^6.0|^7.0"
|
||||
},
|
||||
"require-dev": {
|
||||
"pestphp/pest-plugin-laravel": "^1.3",
|
||||
"phpunit/phpunit": "^9.5"
|
||||
},
|
||||
"time": "2025-06-13T15:23:19+00:00",
|
||||
"type": "library",
|
||||
"installation-source": "dist",
|
||||
"autoload": {
|
||||
"psr-4": {
|
||||
"Spatie\\PdfToText\\": "src"
|
||||
}
|
||||
},
|
||||
"notification-url": "https://packagist.org/downloads/",
|
||||
"license": [
|
||||
"MIT"
|
||||
],
|
||||
"authors": [
|
||||
{
|
||||
"name": "Freek Van der Herten",
|
||||
"email": "freek@spatie.be",
|
||||
"homepage": "https://spatie.be",
|
||||
"role": "Developer"
|
||||
}
|
||||
],
|
||||
"description": "Extract text from a pdf",
|
||||
"homepage": "https://github.com/spatie/pdf-to-text",
|
||||
"keywords": [
|
||||
"pdf-to-text",
|
||||
"spatie"
|
||||
],
|
||||
"support": {
|
||||
"issues": "https://github.com/spatie/pdf-to-text/issues",
|
||||
"source": "https://github.com/spatie/pdf-to-text/tree/1.54.1"
|
||||
},
|
||||
"funding": [
|
||||
{
|
||||
"url": "https://spatie.be/open-source/support-us",
|
||||
"type": "custom"
|
||||
}
|
||||
],
|
||||
"install-path": "../spatie/pdf-to-text"
|
||||
},
|
||||
{
|
||||
"name": "symfony/clock",
|
||||
"version": "v7.1.1",
|
||||
|
||||
13
vendor/composer/installed.php
vendored
13
vendor/composer/installed.php
vendored
@ -3,7 +3,7 @@
|
||||
'name' => 'loshmis/vanguard',
|
||||
'pretty_version' => 'dev-main',
|
||||
'version' => 'dev-main',
|
||||
'reference' => 'e75be99e43b28088315b19f86eee2e6869c7c844',
|
||||
'reference' => '6b65b31932e62bc55f5c539e7856ca8eb8440782',
|
||||
'type' => 'project',
|
||||
'install_path' => __DIR__ . '/../../',
|
||||
'aliases' => array(),
|
||||
@ -688,7 +688,7 @@
|
||||
'loshmis/vanguard' => array(
|
||||
'pretty_version' => 'dev-main',
|
||||
'version' => 'dev-main',
|
||||
'reference' => 'e75be99e43b28088315b19f86eee2e6869c7c844',
|
||||
'reference' => '6b65b31932e62bc55f5c539e7856ca8eb8440782',
|
||||
'type' => 'project',
|
||||
'install_path' => __DIR__ . '/../../',
|
||||
'aliases' => array(),
|
||||
@ -1391,6 +1391,15 @@
|
||||
0 => '*',
|
||||
),
|
||||
),
|
||||
'spatie/pdf-to-text' => array(
|
||||
'pretty_version' => '1.54.1',
|
||||
'version' => '1.54.1.0',
|
||||
'reference' => 'ad2a792c7e1e68f1bc9b038dc73cdcf760595fbb',
|
||||
'type' => 'library',
|
||||
'install_path' => __DIR__ . '/../spatie/pdf-to-text',
|
||||
'aliases' => array(),
|
||||
'dev_requirement' => false,
|
||||
),
|
||||
'symfony/clock' => array(
|
||||
'pretty_version' => 'v7.1.1',
|
||||
'version' => '7.1.1.0',
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user