diff --git a/app/Http/Controllers/Web/Auth/LoginController.php b/app/Http/Controllers/Web/Auth/LoginController.php index 69d8716..a1e86e4 100644 --- a/app/Http/Controllers/Web/Auth/LoginController.php +++ b/app/Http/Controllers/Web/Auth/LoginController.php @@ -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 diff --git a/composer.json b/composer.json index 7c6f34f..6c53bc5 100644 --- a/composer.json +++ b/composer.json @@ -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", diff --git a/composer.lock b/composer.lock index 8ad8c93..7996ab1 100644 --- a/composer.lock +++ b/composer.lock @@ -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", diff --git a/public/assets/img/logo.png b/public/assets/img/logo.png index a8568b8..076c4b7 100644 Binary files a/public/assets/img/logo.png and b/public/assets/img/logo.png differ diff --git a/public/userarea/extract_prices.php b/public/userarea/extract_prices.php new file mode 100644 index 0000000..f5d75b4 --- /dev/null +++ b/public/userarea/extract_prices.php @@ -0,0 +1,73 @@ + "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); diff --git a/public/userarea/extract_prices_pdfco.php b/public/userarea/extract_prices_pdfco.php new file mode 100644 index 0000000..2285f5f --- /dev/null +++ b/public/userarea/extract_prices_pdfco.php @@ -0,0 +1,218 @@ + $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); diff --git a/public/userarea/extract_prices_table.php b/public/userarea/extract_prices_table.php new file mode 100644 index 0000000..b68b55b --- /dev/null +++ b/public/userarea/extract_prices_table.php @@ -0,0 +1,137 @@ + $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); diff --git a/public/userarea/import_list.php b/public/userarea/import_list.php index 8b3b4e6..4af6b12 100644 --- a/public/userarea/import_list.php +++ b/public/userarea/import_list.php @@ -45,139 +45,140 @@ $pdo = $db->getConnection();
- -| - | ID | -Collection | -Category | -Model | -Model Description | -Mod.Code | -Description | -Finish.Code | -Finishing Description | -Available Qty | -WH | -Price | -Actions | -
|---|