import fogli di lavoro
This commit is contained in:
+1
-1
@@ -42,7 +42,7 @@
|
||||
"laravel/tinker": "^2.7",
|
||||
"laravel/ui": "^4.0",
|
||||
"phpmailer/phpmailer": "^6.9",
|
||||
"phpoffice/phpspreadsheet": "^4.1",
|
||||
"phpoffice/phpspreadsheet": "^5.10",
|
||||
"proengsoft/laravel-jsvalidation": "^4.0.0",
|
||||
"robmorgan/phinx": "^0.16.11",
|
||||
"socialiteproviders/microsoft": "^4.7",
|
||||
|
||||
Generated
+22
-16
@@ -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": "076e7721d08cfea8b06ce75dd8c6c576",
|
||||
"content-hash": "7dbd2f50581b518d3ed8feecb1117520",
|
||||
"packages": [
|
||||
{
|
||||
"name": "akaunting/laravel-setting",
|
||||
@@ -4247,16 +4247,16 @@
|
||||
},
|
||||
{
|
||||
"name": "phpoffice/phpspreadsheet",
|
||||
"version": "4.1.0",
|
||||
"version": "5.10.0",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/PHPOffice/PhpSpreadsheet.git",
|
||||
"reference": "6ff18c3a8df3a945492f75ce455d77f7ad55dd5c"
|
||||
"reference": "eb18727acf6b1f4cc67145a52ab04f9fd4c28d53"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/PHPOffice/PhpSpreadsheet/zipball/6ff18c3a8df3a945492f75ce455d77f7ad55dd5c",
|
||||
"reference": "6ff18c3a8df3a945492f75ce455d77f7ad55dd5c",
|
||||
"url": "https://api.github.com/repos/PHPOffice/PhpSpreadsheet/zipball/eb18727acf6b1f4cc67145a52ab04f9fd4c28d53",
|
||||
"reference": "eb18727acf6b1f4cc67145a52ab04f9fd4c28d53",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
@@ -4264,6 +4264,7 @@
|
||||
"ext-ctype": "*",
|
||||
"ext-dom": "*",
|
||||
"ext-fileinfo": "*",
|
||||
"ext-filter": "*",
|
||||
"ext-gd": "*",
|
||||
"ext-iconv": "*",
|
||||
"ext-libxml": "*",
|
||||
@@ -4277,27 +4278,29 @@
|
||||
"maennchen/zipstream-php": "^2.1 || ^3.0",
|
||||
"markbaker/complex": "^3.0",
|
||||
"markbaker/matrix": "^3.0",
|
||||
"php": "^8.1",
|
||||
"psr/http-client": "^1.0",
|
||||
"psr/http-factory": "^1.0",
|
||||
"php": "^8.2",
|
||||
"psr/simple-cache": "^1.0 || ^2.0 || ^3.0"
|
||||
},
|
||||
"require-dev": {
|
||||
"dealerdirect/phpcodesniffer-composer-installer": "dev-main",
|
||||
"dompdf/dompdf": "^2.0 || ^3.0",
|
||||
"ext-intl": "*",
|
||||
"ext-openssl": "*",
|
||||
"friendsofphp/php-cs-fixer": "^3.2",
|
||||
"mitoteam/jpgraph": "^10.3",
|
||||
"mitoteam/jpgraph": "^10.5",
|
||||
"mpdf/mpdf": "^8.1.1",
|
||||
"phpcompatibility/php-compatibility": "^9.3",
|
||||
"phpstan/phpstan": "^1.1",
|
||||
"phpstan/phpstan-phpunit": "^1.0",
|
||||
"phpunit/phpunit": "^10.5",
|
||||
"phpstan/phpstan": "^1.1 || ^2.0",
|
||||
"phpstan/phpstan-deprecation-rules": "^1.0 || ^2.0",
|
||||
"phpstan/phpstan-phpunit": "^1.0 || ^2.0",
|
||||
"phpunit/phpunit": "^10.5 || ^11.0",
|
||||
"squizlabs/php_codesniffer": "^3.7",
|
||||
"tecnickcom/tcpdf": "^6.5"
|
||||
"tecnickcom/tcpdf": ">=6.8.0 <7.0.0"
|
||||
},
|
||||
"suggest": {
|
||||
"dompdf/dompdf": "Option for rendering PDF with PDF Writer",
|
||||
"ext-intl": "PHP Internationalization Functions",
|
||||
"ext-intl": "PHP Internationalization Functions, required for NumberFormat Wizard and StringHelper::setLocale()",
|
||||
"ext-openssl": "Handline Agile-encrypted Xlsx spreadsheets",
|
||||
"mitoteam/jpgraph": "Option for rendering charts, or including charts with PDF or HTML Writers",
|
||||
"mpdf/mpdf": "Option for rendering PDF with PDF Writer",
|
||||
"tecnickcom/tcpdf": "Option for rendering PDF with PDF Writer"
|
||||
@@ -4330,6 +4333,9 @@
|
||||
},
|
||||
{
|
||||
"name": "Adrien Crivelli"
|
||||
},
|
||||
{
|
||||
"name": "Owen Leibman"
|
||||
}
|
||||
],
|
||||
"description": "PHPSpreadsheet - Read, Create and Write Spreadsheet documents in PHP - Spreadsheet engine",
|
||||
@@ -4346,9 +4352,9 @@
|
||||
],
|
||||
"support": {
|
||||
"issues": "https://github.com/PHPOffice/PhpSpreadsheet/issues",
|
||||
"source": "https://github.com/PHPOffice/PhpSpreadsheet/tree/4.1.0"
|
||||
"source": "https://github.com/PHPOffice/PhpSpreadsheet/tree/5.10.0"
|
||||
},
|
||||
"time": "2025-03-02T06:52:24+00:00"
|
||||
"time": "2026-09-17T03:54:50+00:00"
|
||||
},
|
||||
{
|
||||
"name": "phpoption/phpoption",
|
||||
|
||||
@@ -0,0 +1,26 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
use Phinx\Migration\AbstractMigration;
|
||||
|
||||
final class AddSourceXlsxToWorkSheets extends AbstractMigration
|
||||
{
|
||||
public function change(): void
|
||||
{
|
||||
$this->table('work_sheets')
|
||||
->addColumn('source_xlsx_name', 'string', [
|
||||
'limit' => 255,
|
||||
'null' => true,
|
||||
'default' => null,
|
||||
'after' => 'notes',
|
||||
])
|
||||
->addColumn('source_xlsx_path', 'string', [
|
||||
'limit' => 255,
|
||||
'null' => true,
|
||||
'default' => null,
|
||||
'after' => 'source_xlsx_name',
|
||||
])
|
||||
->update();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,83 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* attach_worksheet_file.php
|
||||
* -------------------------------------------------------------------------
|
||||
* Riceve worksheet_id + file .xlsx (campo "xlsx_file"), salva il file in
|
||||
* uploads/worksheets/ e lo collega al foglio (colonne source_xlsx_*).
|
||||
* Chiamato dal flusso di import DOPO il salvataggio della testata.
|
||||
* -------------------------------------------------------------------------
|
||||
*/
|
||||
|
||||
ini_set('display_errors', 0);
|
||||
error_reporting(E_ALL);
|
||||
|
||||
while (ob_get_level()) {
|
||||
ob_end_clean();
|
||||
}
|
||||
header('Content-Type: application/json; charset=utf-8');
|
||||
|
||||
include('include/headscript.php');
|
||||
|
||||
function jexit($a)
|
||||
{
|
||||
echo json_encode($a, JSON_UNESCAPED_UNICODE);
|
||||
exit;
|
||||
}
|
||||
|
||||
try {
|
||||
if ($_SERVER['REQUEST_METHOD'] !== 'POST') {
|
||||
jexit(['success' => false, 'message' => 'Metodo non valido']);
|
||||
}
|
||||
|
||||
$worksheet_id = isset($_POST['worksheet_id']) && is_numeric($_POST['worksheet_id'])
|
||||
? (int)$_POST['worksheet_id'] : 0;
|
||||
if ($worksheet_id <= 0) {
|
||||
jexit(['success' => false, 'message' => 'worksheet_id mancante o non valido']);
|
||||
}
|
||||
|
||||
if (!isset($_FILES['xlsx_file']) || $_FILES['xlsx_file']['error'] !== UPLOAD_ERR_OK) {
|
||||
jexit(['success' => false, 'message' => 'File non ricevuto']);
|
||||
}
|
||||
$file = $_FILES['xlsx_file'];
|
||||
|
||||
if ($file['size'] > 5 * 1024 * 1024) {
|
||||
jexit(['success' => false, 'message' => 'File troppo grande (max 5 MB)']);
|
||||
}
|
||||
$ext = strtolower(pathinfo($file['name'], PATHINFO_EXTENSION));
|
||||
if ($ext !== 'xlsx') {
|
||||
jexit(['success' => false, 'message' => 'Sono ammessi solo file .xlsx']);
|
||||
}
|
||||
|
||||
$db = DBHandlerSelect::getInstance();
|
||||
$pdo = $db->getConnection();
|
||||
|
||||
// il foglio deve esistere
|
||||
$chk = $pdo->prepare("SELECT id FROM work_sheets WHERE id = ?");
|
||||
$chk->execute([$worksheet_id]);
|
||||
if (!$chk->fetchColumn()) {
|
||||
jexit(['success' => false, 'message' => 'Foglio di lavoro inesistente']);
|
||||
}
|
||||
|
||||
// cartella di destinazione (accanto allo script)
|
||||
$dir = __DIR__ . '/uploads/worksheets';
|
||||
if (!is_dir($dir) && !mkdir($dir, 0775, true) && !is_dir($dir)) {
|
||||
jexit(['success' => false, 'message' => 'Impossibile creare la cartella uploads/worksheets']);
|
||||
}
|
||||
|
||||
$origName = $file['name'];
|
||||
$safeStored = 'ws_' . $worksheet_id . '_' . date('Ymd_His') . '.xlsx';
|
||||
$destAbs = $dir . '/' . $safeStored;
|
||||
$destRel = 'uploads/worksheets/' . $safeStored; // relativo alla cartella dello script
|
||||
|
||||
if (!move_uploaded_file($file['tmp_name'], $destAbs)) {
|
||||
jexit(['success' => false, 'message' => 'Salvataggio del file fallito']);
|
||||
}
|
||||
|
||||
$upd = $pdo->prepare("UPDATE work_sheets SET source_xlsx_name = ?, source_xlsx_path = ? WHERE id = ?");
|
||||
$upd->execute([$origName, $destRel, $worksheet_id]);
|
||||
|
||||
jexit(['success' => true, 'name' => $origName, 'path' => $destRel]);
|
||||
} catch (Exception $e) {
|
||||
jexit(['success' => false, 'message' => $e->getMessage()]);
|
||||
}
|
||||
@@ -0,0 +1,51 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* download_worksheet_file.php?id=WORKSHEET_ID
|
||||
* -------------------------------------------------------------------------
|
||||
* Restituisce il file Excel di origine allegato a un foglio di lavoro,
|
||||
* con il nome file originale. Protetto contro path-traversal.
|
||||
* -------------------------------------------------------------------------
|
||||
*/
|
||||
|
||||
include('include/headscript.php');
|
||||
|
||||
$id = isset($_GET['id']) && is_numeric($_GET['id']) ? (int)$_GET['id'] : 0;
|
||||
if ($id <= 0) {
|
||||
http_response_code(400);
|
||||
exit('ID non valido');
|
||||
}
|
||||
|
||||
$db = DBHandlerSelect::getInstance();
|
||||
$pdo = $db->getConnection();
|
||||
|
||||
$stmt = $pdo->prepare("SELECT source_xlsx_name, source_xlsx_path FROM work_sheets WHERE id = ?");
|
||||
$stmt->execute([$id]);
|
||||
$row = $stmt->fetch(PDO::FETCH_ASSOC);
|
||||
|
||||
if (!$row || empty($row['source_xlsx_path'])) {
|
||||
http_response_code(404);
|
||||
exit('Nessun file allegato a questo foglio');
|
||||
}
|
||||
|
||||
// Risoluzione sicura: il file deve stare dentro uploads/worksheets
|
||||
$abs = __DIR__ . '/' . $row['source_xlsx_path'];
|
||||
$real = realpath($abs);
|
||||
$baseDir = realpath(__DIR__ . '/uploads/worksheets');
|
||||
|
||||
if ($real === false || $baseDir === false || strpos($real, $baseDir) !== 0 || !is_file($real)) {
|
||||
http_response_code(404);
|
||||
exit('File non trovato');
|
||||
}
|
||||
|
||||
$downloadName = $row['source_xlsx_name'] !== '' ? $row['source_xlsx_name'] : basename($real);
|
||||
$asciiName = preg_replace('/[^A-Za-z0-9._-]/', '_', $downloadName);
|
||||
|
||||
header('Content-Type: application/vnd.openxmlformats-officedocument.spreadsheetml.sheet');
|
||||
header("Content-Disposition: attachment; filename=\"{$asciiName}\"; filename*=UTF-8''" . rawurlencode($downloadName));
|
||||
header('Content-Length: ' . filesize($real));
|
||||
header('X-Content-Type-Options: nosniff');
|
||||
header('Cache-Control: private, max-age=0, must-revalidate');
|
||||
|
||||
readfile($real);
|
||||
exit;
|
||||
@@ -0,0 +1,505 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* import_worksheet_xlsx.php
|
||||
* -------------------------------------------------------------------------
|
||||
* Riceve un file .xlsx (modulo ZIBOGOMMA "FOGLIO DI LAVORO") via POST
|
||||
* multipart (campo file: "xlsx_file") e restituisce una PROPOSTA in JSON
|
||||
* pronta per essere revisionata dall'utente in manage-worksheet.php.
|
||||
*
|
||||
* NON scrive nulla sul database: fa solo lettura (mescole, packaging_items)
|
||||
* per proporre gli abbinamenti. Il salvataggio resta agli endpoint esistenti.
|
||||
*
|
||||
* Dipendenze: solo estensioni PHP standard (zip, xml/simplexml). Niente Composer.
|
||||
* -------------------------------------------------------------------------
|
||||
*/
|
||||
|
||||
ini_set('display_errors', 0);
|
||||
error_reporting(E_ALL);
|
||||
|
||||
while (ob_get_level()) {
|
||||
ob_end_clean();
|
||||
}
|
||||
header('Content-Type: application/json; charset=utf-8');
|
||||
|
||||
// Connessione DB (stesso pattern di manage-worksheet.php)
|
||||
include('include/headscript.php');
|
||||
require __DIR__ . '/../../vendor/autoload.php';
|
||||
|
||||
use PhpOffice\PhpSpreadsheet\IOFactory;
|
||||
use PhpOffice\PhpSpreadsheet\Shared\Date as XlsDate;
|
||||
|
||||
function fail($msg)
|
||||
{
|
||||
echo json_encode(['success' => false, 'message' => $msg], JSON_UNESCAPED_UNICODE);
|
||||
exit;
|
||||
}
|
||||
|
||||
/* =========================================================================
|
||||
* 1) READER XLSX (senza dipendenze)
|
||||
* ========================================================================= */
|
||||
|
||||
function excel_serial_to_date($serial)
|
||||
{
|
||||
$days = (int)floor((float)$serial);
|
||||
$base = new DateTime('1899-12-30');
|
||||
$base->modify("+{$days} day");
|
||||
return $base->format('Y-m-d');
|
||||
}
|
||||
|
||||
function read_xlsx_grid($path)
|
||||
{
|
||||
try {
|
||||
$reader = IOFactory::createReader('Xlsx');
|
||||
// niente setReadDataOnly(true): serve il formato per riconoscere le date
|
||||
$spreadsheet = $reader->load($path);
|
||||
} catch (\Throwable $e) {
|
||||
throw new Exception('Impossibile leggere il file .xlsx: ' . $e->getMessage());
|
||||
}
|
||||
|
||||
$sheet = $spreadsheet->getSheet(0); // primo foglio
|
||||
$grid = [];
|
||||
|
||||
foreach ($sheet->getRowIterator() as $row) {
|
||||
$rowN = $row->getRowIndex();
|
||||
$it = $row->getCellIterator();
|
||||
$it->setIterateOnlyExistingCells(true);
|
||||
foreach ($it as $c) {
|
||||
$colL = $c->getColumn(); // lettera colonna, es. "D"
|
||||
|
||||
// IMPORTANTE: nel modulo ZIBOGOMMA quasi tutte le celle sono formule
|
||||
// che puntano a un file esterno ('[1] Base2'!...). Il valore reale sta
|
||||
// nella CACHE della cella. Perciò:
|
||||
// - cella formula -> getOldCalculatedValue() (valore in cache, NO ricalcolo)
|
||||
// - cella normale -> getValue()
|
||||
$val = $c->isFormula() ? $c->getOldCalculatedValue() : $c->getValue();
|
||||
|
||||
if ($val === null || $val === '') continue;
|
||||
|
||||
if (XlsDate::isDateTime($c) && is_numeric($val)) {
|
||||
$val = XlsDate::excelToDateTimeObject((float)$val)->format('Y-m-d');
|
||||
} elseif (is_numeric($val)) {
|
||||
$val = $val + 0;
|
||||
}
|
||||
$grid[$rowN][$colL] = $val;
|
||||
}
|
||||
}
|
||||
return $grid;
|
||||
}
|
||||
/* =========================================================================
|
||||
* 2) ESTRATTORE label-anchored (ancorato alle etichette in colonna A)
|
||||
* ========================================================================= */
|
||||
|
||||
function norm($s)
|
||||
{
|
||||
$s = (string)$s;
|
||||
$s = str_replace(
|
||||
['à', 'è', 'é', 'ì', 'ò', 'ù', 'À', 'È', 'É', 'Ì', 'Ò', 'Ù'],
|
||||
['a', 'e', 'e', 'i', 'o', 'u', 'a', 'e', 'e', 'i', 'o', 'u'],
|
||||
$s
|
||||
);
|
||||
$s = strtolower($s);
|
||||
$s = preg_replace('/[^a-z0-9\/ ]+/', ' ', $s);
|
||||
$s = preg_replace('/\s+/', ' ', $s);
|
||||
return trim($s);
|
||||
}
|
||||
|
||||
function cell($grid, $row, $col)
|
||||
{
|
||||
return isset($grid[$row][$col]) ? trim((string)$grid[$row][$col]) : '';
|
||||
}
|
||||
|
||||
function row_by_label($grid, $label, $col = 'A')
|
||||
{
|
||||
$target = norm($label);
|
||||
foreach ($grid as $rn => $cols) {
|
||||
$a = norm($cols[$col] ?? '');
|
||||
if ($a === $target || ($target !== '' && strpos($a, $target) === 0)) return $rn;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
function val_at_label($grid, $label, $valcol = 'D')
|
||||
{
|
||||
$r = row_by_label($grid, $label);
|
||||
return $r ? cell($grid, $r, $valcol) : '';
|
||||
}
|
||||
|
||||
function is_known_label($a)
|
||||
{
|
||||
$known = [
|
||||
'foglio di lavoro',
|
||||
'data',
|
||||
'cliente',
|
||||
'tipo profilo',
|
||||
'peso a metro calcolato g/m',
|
||||
'nome mescola in entrata',
|
||||
'densita richiesta g/cm3',
|
||||
'durezza richiesta shore a',
|
||||
'marchiatura',
|
||||
'lubrificazione',
|
||||
'impostazione misure per il controllo della produzione',
|
||||
'impostazione lunghezza spezzoni',
|
||||
'frequenza di controllo',
|
||||
'frequenza di controllo disegno',
|
||||
'frequenza di controllo in dima',
|
||||
'modalita di controllo in dima',
|
||||
'confezione richiesta',
|
||||
'metri/confezione',
|
||||
'tipo scatola',
|
||||
'n confezioni o pezzi per scatola',
|
||||
'metri/scatola',
|
||||
'tipo di bancale',
|
||||
'n confezioni o scatole per bancale',
|
||||
'velocita di produzione'
|
||||
];
|
||||
foreach ($known as $k) if (strpos($a, $k) === 0) return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
function get_freq_cut($grid)
|
||||
{
|
||||
foreach ($grid as $rn => $cols) {
|
||||
if (norm($cols['A'] ?? '') === 'frequenza di controllo') return cell($grid, $rn, 'D');
|
||||
}
|
||||
return '';
|
||||
}
|
||||
|
||||
function num_or_null($v)
|
||||
{
|
||||
if ($v === '' || $v === null) return null;
|
||||
return is_numeric($v) ? $v + 0 : null;
|
||||
}
|
||||
|
||||
function dotdec_to_comma($v)
|
||||
{
|
||||
// 1.2 -> 1,2 ma lascia intatto "+/-0,02"
|
||||
return preg_replace('/(\d)\.(\d)/', '$1,$2', (string)$v);
|
||||
}
|
||||
|
||||
function extract_fields($grid)
|
||||
{
|
||||
$out = [];
|
||||
|
||||
$wd = val_at_label($grid, 'data', 'D');
|
||||
if ($wd !== '' && is_numeric($wd)) $wd = excel_serial_to_date($wd);
|
||||
$out['worksheet_date'] = $wd;
|
||||
|
||||
$out['customer_name'] = val_at_label($grid, 'cliente', 'D');
|
||||
$out['profile_type_code'] = val_at_label($grid, 'tipo profilo', 'D');
|
||||
$out['marking'] = strtoupper(val_at_label($grid, 'marchiatura', 'D'));
|
||||
|
||||
// prod_control: riga "impostazione misure..." + continuazione senza label + "impostazione lunghezza spezzoni"
|
||||
$lines = [];
|
||||
$r = row_by_label($grid, 'impostazione misure per il controllo');
|
||||
if ($r) {
|
||||
for ($i = $r; $i <= $r + 1; $i++) {
|
||||
$d = cell($grid, $i, 'D');
|
||||
$e = cell($grid, $i, 'E');
|
||||
if ($d !== '' && ($i === $r || norm(cell($grid, $i, 'A')) === '')) {
|
||||
$lines[] = trim($d . ' ' . $e);
|
||||
}
|
||||
}
|
||||
}
|
||||
$rl = row_by_label($grid, 'impostazione lunghezza spezzoni');
|
||||
if ($rl) {
|
||||
$d = cell($grid, $rl, 'D');
|
||||
$e = cell($grid, $rl, 'E');
|
||||
if ($d !== '') $lines[] = trim($d . ' ' . $e);
|
||||
}
|
||||
$out['prod_control_measure_settings'] = implode("\n", $lines);
|
||||
|
||||
// lookup da evidenziare (RAW, value scelto dall'utente)
|
||||
$out['raw_freq_cut'] = get_freq_cut($grid);
|
||||
$out['raw_freq_drawing'] = val_at_label($grid, 'frequenza di controllo disegno', 'D');
|
||||
$out['raw_freq_jig'] = val_at_label($grid, 'frequenza di controllo in dima', 'D');
|
||||
$out['raw_mode_jig'] = val_at_label($grid, 'modalita di controllo in dima', 'D');
|
||||
|
||||
// mescola
|
||||
$out['mescola_raw'] = val_at_label($grid, 'nome mescola in entrata', 'D');
|
||||
$out['mix_weight_g_m'] = num_or_null(val_at_label($grid, 'peso a metro calcolato', 'D'));
|
||||
$rd = row_by_label($grid, 'densita richiesta');
|
||||
$out['required_density'] = $rd ? dotdec_to_comma(trim(cell($grid, $rd, 'D') . ' ' . cell($grid, $rd, 'E'))) : '';
|
||||
$rh = row_by_label($grid, 'durezza richiesta');
|
||||
$out['required_hardness_shore_a'] = $rh ? dotdec_to_comma(trim(cell($grid, $rh, 'D') . ' ' . cell($grid, $rh, 'E'))) : '';
|
||||
$out['lubrication_type'] = strtoupper(val_at_label($grid, 'lubrificazione', 'D'));
|
||||
|
||||
// packaging (3 celle contenitore -> 3 righe)
|
||||
$out['pkg_confezione_raw'] = val_at_label($grid, 'confezione richiesta', 'D');
|
||||
$out['pkg_scatola_raw'] = val_at_label($grid, 'tipo scatola', 'D');
|
||||
$out['pkg_bancale_raw'] = val_at_label($grid, 'tipo di bancale', 'D');
|
||||
|
||||
$out['packages_or_pieces_per_box'] = num_or_null(val_at_label($grid, 'n confezioni o pezzi per scatola', 'D'));
|
||||
$out['boxes_or_packages_per_pallet'] = num_or_null(val_at_label($grid, 'n confezioni o scatole per bancale', 'D'));
|
||||
|
||||
// velocità
|
||||
$out['speed_expected_kg_h'] = null;
|
||||
$out['speed_expected_m_h'] = null;
|
||||
$rv = row_by_label($grid, 'velocita di produzione');
|
||||
if ($rv) {
|
||||
$out['speed_expected_kg_h'] = num_or_null(cell($grid, $rv, 'C'));
|
||||
for ($i = $rv; $i <= $rv + 2; $i++) {
|
||||
if (norm(cell($grid, $i, 'B')) === 'm/h') {
|
||||
$out['speed_expected_m_h'] = num_or_null(cell($grid, $i, 'C'));
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// note: annotazioni libere colonna E + righe istruzione in fondo
|
||||
$noteBits = [];
|
||||
foreach (['confezione richiesta', 'metri/confezione', 'metri/scatola'] as $lbl) {
|
||||
$rr = row_by_label($grid, $lbl);
|
||||
if ($rr && cell($grid, $rr, 'E') !== '') $noteBits[] = cell($grid, $rr, 'E');
|
||||
}
|
||||
foreach ($grid as $rn => $cols) {
|
||||
$a = norm($cols['A'] ?? '');
|
||||
if ($a !== '' && cell($grid, $rn, 'D') === '' && cell($grid, $rn, 'C') === '' && !is_known_label($a)) {
|
||||
if (strlen($a) > 12) $noteBits[] = trim((string)$cols['A']);
|
||||
}
|
||||
}
|
||||
$out['notes'] = implode("\n", array_values(array_unique(array_filter($noteBits))));
|
||||
|
||||
return $out;
|
||||
}
|
||||
|
||||
/* =========================================================================
|
||||
* 3) MATCHER su DB
|
||||
* ========================================================================= */
|
||||
|
||||
function norm_mesc($s)
|
||||
{
|
||||
$s = strtolower((string)$s);
|
||||
$s = preg_replace('/\s+/', ' ', $s);
|
||||
return trim($s);
|
||||
}
|
||||
|
||||
function match_mescola($raw, array $mescole)
|
||||
{
|
||||
$r = norm_mesc($raw);
|
||||
if ($r === '') return [null, 'none', null];
|
||||
foreach ($mescole as $m) if (norm_mesc($m['nome']) === $r) return [(int)$m['id'], 'exact', $m['nome']];
|
||||
foreach ($mescole as $m) {
|
||||
foreach (explode('/', $m['nome']) as $part) {
|
||||
if (norm_mesc($part) === $r) return [(int)$m['id'], 'exact-part', $m['nome']];
|
||||
}
|
||||
}
|
||||
foreach ($mescole as $m) if (norm_mesc($m['nomeuscita'] ?? '') === $r) return [(int)$m['id'], 'exact-uscita', $m['nome']];
|
||||
foreach ($mescole as $m) if ($r !== '' && strpos(norm_mesc($m['nome']), $r) !== false) return [(int)$m['id'], 'contains', $m['nome']];
|
||||
return [null, 'none', null];
|
||||
}
|
||||
|
||||
function dim_tokens($s)
|
||||
{
|
||||
$s = strtolower((string)$s);
|
||||
$s = str_replace(',', '.', $s);
|
||||
preg_match_all('/\d+(?:\.\d+)?/', $s, $m);
|
||||
$nums = $m[0];
|
||||
if (count($nums) < 2) return [];
|
||||
$forms = [];
|
||||
$forms[] = implode('x', $nums);
|
||||
$mm = array_map(function ($n) {
|
||||
$f = (float)$n;
|
||||
if ($f < 10 && strpos($n, '.') !== false) return (string)round($f * 1000);
|
||||
return (string)($f == (int)$f ? (int)$f : $f);
|
||||
}, $nums);
|
||||
$forms[] = implode('x', $mm);
|
||||
return array_values(array_unique($forms));
|
||||
}
|
||||
|
||||
function match_packaging($raw, array $packaging, $preferCat = null)
|
||||
{
|
||||
$cands = dim_tokens($raw);
|
||||
if (!$cands) return [null, 'none', null, null];
|
||||
$best = null;
|
||||
foreach ($packaging as $p) {
|
||||
$itemForms = array_merge(dim_tokens($p['item_name']), dim_tokens($p['item_code']));
|
||||
foreach ($cands as $c) {
|
||||
if (in_array($c, $itemForms, true)) {
|
||||
if ($preferCat && $p['category'] === $preferCat) {
|
||||
return [(int)$p['id'], 'dimension', $p['item_name'], $p['category']];
|
||||
}
|
||||
if ($best === null) $best = $p;
|
||||
}
|
||||
}
|
||||
}
|
||||
if ($best) return [(int)$best['id'], 'dimension', $best['item_name'], $best['category']];
|
||||
return [null, 'none', null, null];
|
||||
}
|
||||
|
||||
|
||||
/* ---- match generico su dropdown (ws_lookup_options) ---- */
|
||||
function lk_norm($s)
|
||||
{
|
||||
$s = (string)$s;
|
||||
$s = str_replace(
|
||||
['à', 'è', 'é', 'ì', 'ò', 'ù', 'À', 'È', 'É', 'Ì', 'Ò', 'Ù'],
|
||||
['a', 'e', 'e', 'i', 'o', 'u', 'a', 'e', 'e', 'i', 'o', 'u'],
|
||||
$s
|
||||
);
|
||||
$s = strtolower($s);
|
||||
$s = preg_replace('/[^a-z0-9 ]+/', ' ', $s);
|
||||
$s = preg_replace('/\s+/', ' ', $s);
|
||||
return trim($s);
|
||||
}
|
||||
function lk_toks($s)
|
||||
{
|
||||
$t = array_filter(explode(' ', lk_norm($s)), fn($x) => $x !== '');
|
||||
return array_values(array_unique($t));
|
||||
}
|
||||
/** abbina il testo Excel a una voce del dropdown. Ritorna [value|null, label|null, confidence] */
|
||||
function match_lookup($raw, array $options)
|
||||
{
|
||||
$r = lk_norm($raw);
|
||||
if ($r === '') return [null, null, 'none'];
|
||||
// tier 1: uguale a value o label
|
||||
foreach ($options as $o) {
|
||||
if (lk_norm($o['value']) === $r || lk_norm($o['label']) === $r) {
|
||||
return [$o['value'], $o['label'], 'exact'];
|
||||
}
|
||||
}
|
||||
// tier 2: sovrapposizione token >= 0.8 (set più piccolo di almeno 2 token)
|
||||
$rt = lk_toks($raw);
|
||||
$best = null;
|
||||
$bestScore = 0.0;
|
||||
foreach ($options as $o) {
|
||||
$ot = lk_toks($o['label']);
|
||||
if (count($ot) < 2 || count($rt) < 2) continue;
|
||||
$score = count(array_intersect($rt, $ot)) / min(count($rt), count($ot));
|
||||
if ($score > $bestScore) {
|
||||
$bestScore = $score;
|
||||
$best = $o;
|
||||
}
|
||||
}
|
||||
if ($best && $bestScore >= 0.8) return [$best['value'], $best['label'], 'fuzzy'];
|
||||
return [null, null, 'none'];
|
||||
}
|
||||
/** costruisce la voce JSON per un lookup di testata */
|
||||
function build_lookup($raw, array $options)
|
||||
{
|
||||
[$v, $lab, $conf] = match_lookup($raw, $options);
|
||||
return [
|
||||
'raw' => $raw,
|
||||
'value' => $v, // null = nessun suggerimento
|
||||
'label' => $lab,
|
||||
'confidence' => $conf,
|
||||
'needs_review' => ($raw !== ''), // sempre da verificare se c'era un valore nel file
|
||||
];
|
||||
}
|
||||
|
||||
/* =========================================================================
|
||||
* 4) MAIN
|
||||
* ========================================================================= */
|
||||
|
||||
try {
|
||||
if ($_SERVER['REQUEST_METHOD'] !== 'POST' || !isset($_FILES['xlsx_file'])) {
|
||||
fail('Nessun file ricevuto.');
|
||||
}
|
||||
$file = $_FILES['xlsx_file'];
|
||||
if ($file['error'] !== UPLOAD_ERR_OK) {
|
||||
fail('Errore nel caricamento del file (codice ' . $file['error'] . ').');
|
||||
}
|
||||
if ($file['size'] > 5 * 1024 * 1024) {
|
||||
fail('File troppo grande (max 5 MB).');
|
||||
}
|
||||
$ext = strtolower(pathinfo($file['name'], PATHINFO_EXTENSION));
|
||||
if ($ext !== 'xlsx') {
|
||||
fail('Formato non supportato: carica un file .xlsx.');
|
||||
}
|
||||
|
||||
$db = DBHandlerSelect::getInstance();
|
||||
$pdo = $db->getConnection();
|
||||
|
||||
$grid = read_xlsx_grid($file['tmp_name']);
|
||||
$fields = extract_fields($grid);
|
||||
|
||||
$warnings = [];
|
||||
|
||||
// enum di sicurezza
|
||||
if ($fields['marking'] !== '' && !in_array($fields['marking'], ['S', 'N'], true)) {
|
||||
$warnings[] = "Marchiatura '{$fields['marking']}' non riconosciuta (attese S/N).";
|
||||
$fields['marking'] = '';
|
||||
}
|
||||
if ($fields['lubrication_type'] !== '' && !in_array($fields['lubrication_type'], ['S', 'G', 'N'], true)) {
|
||||
$warnings[] = "Lubrificazione '{$fields['lubrication_type']}' non riconosciuta (attese S/G/N).";
|
||||
$fields['lubrication_type'] = '';
|
||||
}
|
||||
|
||||
// carica tabelle di riferimento
|
||||
$mescole = $pdo->query("SELECT id, nome, nomeuscita FROM mescole WHERE is_active = 1")->fetchAll(PDO::FETCH_ASSOC);
|
||||
$packaging = $pdo->query("SELECT id, category, item_name, item_code FROM packaging_items WHERE is_active = 1")->fetchAll(PDO::FETCH_ASSOC);
|
||||
|
||||
// opzioni dei dropdown (ws_lookup_options) raggruppate per categoria
|
||||
$lookupOpts = [];
|
||||
foreach ($pdo->query("SELECT category, value, label FROM ws_lookup_options WHERE is_active = 1 ORDER BY sort_order ASC")->fetchAll(PDO::FETCH_ASSOC) as $o) {
|
||||
$lookupOpts[$o['category']][] = $o;
|
||||
}
|
||||
|
||||
// --- mescola (1 riga) ---
|
||||
[$mid, $mconf, $mname] = match_mescola($fields['mescola_raw'], $mescole);
|
||||
$mescoleOut = [];
|
||||
if ($fields['mescola_raw'] !== '') {
|
||||
$mescoleOut[] = [
|
||||
'raw' => $fields['mescola_raw'],
|
||||
'idmescola' => $mid,
|
||||
'matched_name' => $mname,
|
||||
'confidence' => $mconf,
|
||||
'needs_review' => ($mid === null || $mconf === 'contains'),
|
||||
'mix_weight_g_m' => $fields['mix_weight_g_m'],
|
||||
'required_density' => $fields['required_density'],
|
||||
'required_hardness_shore_a' => $fields['required_hardness_shore_a'],
|
||||
'lubrication_type' => $fields['lubrication_type'],
|
||||
];
|
||||
}
|
||||
|
||||
// --- packaging (3 celle contenitore -> righe) ---
|
||||
$packagesOut = [];
|
||||
$pkgSpecs = [
|
||||
['source' => 'Confezione richiesta', 'raw' => $fields['pkg_confezione_raw'], 'cat' => null],
|
||||
['source' => 'Tipo scatola', 'raw' => $fields['pkg_scatola_raw'], 'cat' => 'BOX'],
|
||||
['source' => 'Tipo di bancale', 'raw' => $fields['pkg_bancale_raw'], 'cat' => 'PALLET'],
|
||||
];
|
||||
foreach ($pkgSpecs as $spec) {
|
||||
if ($spec['raw'] === '') continue;
|
||||
[$pid, $pconf, $pname, $pcat] = match_packaging($spec['raw'], $packaging, $spec['cat']);
|
||||
$packagesOut[] = [
|
||||
'source' => $spec['source'],
|
||||
'raw' => $spec['raw'],
|
||||
'package_code' => $pid,
|
||||
'matched_name' => $pname,
|
||||
'category' => $pcat,
|
||||
'confidence' => $pconf,
|
||||
'needs_review' => ($pid === null),
|
||||
];
|
||||
}
|
||||
|
||||
// --- risposta ---
|
||||
echo json_encode([
|
||||
'success' => true,
|
||||
'header' => [
|
||||
'worksheet_date' => $fields['worksheet_date'],
|
||||
'customer_name' => $fields['customer_name'],
|
||||
'profile_type_code' => $fields['profile_type_code'],
|
||||
'marking' => $fields['marking'],
|
||||
'prod_control_measure_settings' => $fields['prod_control_measure_settings'],
|
||||
'packages_or_pieces_per_box' => $fields['packages_or_pieces_per_box'],
|
||||
'boxes_or_packages_per_pallet' => $fields['boxes_or_packages_per_pallet'],
|
||||
'speed_expected_kg_h' => $fields['speed_expected_kg_h'],
|
||||
'speed_expected_m_h' => $fields['speed_expected_m_h'],
|
||||
'notes' => $fields['notes'],
|
||||
],
|
||||
// lookup da EVIDENZIARE: value = null, l'utente sceglie dal menu
|
||||
'header_lookups' => [
|
||||
'control_frequency_cut' => build_lookup($fields['raw_freq_cut'], $lookupOpts['control_frequency_cut'] ?? []),
|
||||
'control_frequency_drawing' => build_lookup($fields['raw_freq_drawing'], $lookupOpts['control_frequency_drawing'] ?? []),
|
||||
'control_frequency_jig' => build_lookup($fields['raw_freq_jig'], $lookupOpts['control_frequency_jig'] ?? []),
|
||||
'control_mode_jig' => build_lookup($fields['raw_mode_jig'], $lookupOpts['control_mode_jig'] ?? []),
|
||||
],
|
||||
'mescole' => $mescoleOut,
|
||||
'packages' => $packagesOut,
|
||||
'warnings' => $warnings,
|
||||
], JSON_UNESCAPED_UNICODE);
|
||||
exit;
|
||||
} catch (Exception $e) {
|
||||
fail($e->getMessage());
|
||||
}
|
||||
@@ -963,6 +963,28 @@ $isEdit = ($worksheet_id > 0);
|
||||
padding: 12px;
|
||||
}
|
||||
}
|
||||
|
||||
.import-review {
|
||||
background: #fff8db !important;
|
||||
border-color: #f59e0b !important;
|
||||
}
|
||||
|
||||
.import-hint {
|
||||
font-size: 0.78rem;
|
||||
color: #92400e;
|
||||
margin-top: 4px;
|
||||
font-weight: 700;
|
||||
}
|
||||
|
||||
.import-banner {
|
||||
background: #ecfeff;
|
||||
border: 1px solid #a5f3fc;
|
||||
color: #155e75;
|
||||
border-radius: 14px;
|
||||
padding: 14px 16px;
|
||||
margin-bottom: 16px;
|
||||
font-size: 0.9rem;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
|
||||
@@ -983,6 +1005,13 @@ $isEdit = ($worksheet_id > 0);
|
||||
</div>
|
||||
|
||||
<div class="top-actions">
|
||||
<input type="file" id="xlsxImportInput" accept=".xlsx" style="display:none">
|
||||
<button type="button" class="btn btn-history" id="btnImportXlsx">📥 Importa da Excel</button>
|
||||
<?php if ($worksheet && !empty($worksheet['source_xlsx_path'])): ?>
|
||||
<a class="btn btn-history" href="download_worksheet_file.php?id=<?= (int)$worksheet_id ?>" target="_blank">
|
||||
📎 Excel di origine
|
||||
</a>
|
||||
<?php endif; ?>
|
||||
<button class="btn btn-history" onclick="location.href='worksheets.php'">↩️ Storico fogli</button>
|
||||
</div>
|
||||
</div>
|
||||
@@ -1594,6 +1623,12 @@ $isEdit = ($worksheet_id > 0);
|
||||
window.history.replaceState({}, '', newUrl);
|
||||
}
|
||||
|
||||
// Import da Excel in corso: salva righe e ricarica
|
||||
if (window.importStaged) {
|
||||
window.finalizeImportIfAny(data.id);
|
||||
return;
|
||||
}
|
||||
|
||||
Swal.fire({
|
||||
icon: 'success',
|
||||
title: 'Testata salvata',
|
||||
@@ -1853,6 +1888,220 @@ $isEdit = ($worksheet_id > 0);
|
||||
});
|
||||
});
|
||||
</script>
|
||||
<script>
|
||||
$(function() {
|
||||
window.importStaged = null;
|
||||
|
||||
function postForm(obj) {
|
||||
return fetch('', {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'application/x-www-form-urlencoded'
|
||||
},
|
||||
body: new URLSearchParams(obj)
|
||||
}).then(r => r.json()).catch(() => ({
|
||||
success: false
|
||||
}));
|
||||
}
|
||||
|
||||
function setVal(name, val) {
|
||||
if (val === null || val === undefined) return;
|
||||
const $el = $('[name="' + name + '"]');
|
||||
if ($el.length) $el.val(val);
|
||||
}
|
||||
|
||||
function markReview(name, info) {
|
||||
const $el = $('#' + name);
|
||||
if (!$el.length) return;
|
||||
info = info || {};
|
||||
$el.addClass('import-review');
|
||||
$el.val(info.value ? info.value : ''); // pre-seleziona il suggerimento se c'è
|
||||
const $wrap = $el.closest('[class*="col-"]');
|
||||
let $tip = $wrap.find('.import-hint');
|
||||
if (!$tip.length) {
|
||||
$tip = $('<div class="import-hint"></div>');
|
||||
$el.after($tip);
|
||||
}
|
||||
$tip.text(info.value ?
|
||||
'Suggerito dal file: ' + (info.label || info.value) + ' — verifica' :
|
||||
'Dal file: ' + (info.raw || '') + ' — scegli il valore');
|
||||
}
|
||||
|
||||
function renderBanner(data) {
|
||||
const bits = [];
|
||||
(data.mescole || []).forEach(m =>
|
||||
bits.push('Mescola “' + m.raw + '” ' + (m.idmescola ? '✓' : '⚠ da abbinare a mano')));
|
||||
(data.packages || []).forEach(p =>
|
||||
bits.push('Imballo “' + p.raw + '” (' + p.source + ') ' + (p.package_code ? '✓' : '⚠ da aggiungere a mano')));
|
||||
let html = '<div class="import-banner"><strong>Import completato.</strong> Controlla i campi evidenziati in giallo, poi premi “Salva testata”.';
|
||||
if (bits.length) html += '<ul style="margin:8px 0 0 18px;">' + bits.map(b => '<li>' + b + '</li>').join('') + '</ul>';
|
||||
if ((data.warnings || []).length) html += '<div style="color:#b45309;margin-top:6px;">' + data.warnings.join('<br>') + '</div>';
|
||||
html += '</div>';
|
||||
let $b = $('#importBanner');
|
||||
if (!$b.length) {
|
||||
$b = $('<div id="importBanner"></div>');
|
||||
$('#worksheetHeaderForm').prepend($b);
|
||||
}
|
||||
$b.html(html);
|
||||
}
|
||||
|
||||
// Salvataggio righe dopo la testata (chiamato dal handler esistente)
|
||||
window.finalizeImportIfAny = async function(newId) {
|
||||
const staged = window.importStaged;
|
||||
if (!staged) return false;
|
||||
const unresolved = [];
|
||||
|
||||
for (const p of (staged.packages || [])) {
|
||||
if (p.package_code) {
|
||||
await postForm({
|
||||
ajax: 1,
|
||||
action: 'save_package_row',
|
||||
worksheet_id: newId,
|
||||
package_code: p.package_code
|
||||
});
|
||||
} else {
|
||||
unresolved.push('Imballo “' + p.raw + '” (' + p.source + ')');
|
||||
}
|
||||
}
|
||||
for (const m of (staged.mescole || [])) {
|
||||
if (m.idmescola) {
|
||||
await postForm({
|
||||
ajax: 1,
|
||||
action: 'save_mix_row',
|
||||
worksheet_id: newId,
|
||||
idmescola: m.idmescola,
|
||||
mix_weight_g_m: m.mix_weight_g_m ?? '',
|
||||
required_density: m.required_density ?? '',
|
||||
required_hardness_shore_a: m.required_hardness_shore_a ?? '',
|
||||
lubrication_type: m.lubrication_type ?? '',
|
||||
lubrication_notes: ''
|
||||
});
|
||||
} else {
|
||||
unresolved.push('Mescola “' + m.raw + '”');
|
||||
}
|
||||
}
|
||||
|
||||
window.importStaged = null;
|
||||
|
||||
// Allega il file Excel di origine al foglio appena creato
|
||||
if (window.importFile) {
|
||||
try {
|
||||
const fd = new FormData();
|
||||
fd.append('worksheet_id', newId);
|
||||
fd.append('xlsx_file', window.importFile);
|
||||
await fetch('attach_worksheet_file.php', {
|
||||
method: 'POST',
|
||||
body: fd
|
||||
});
|
||||
} catch (e) {
|
||||
/* se l'allegato fallisce non blocchiamo il flusso */
|
||||
}
|
||||
window.importFile = null;
|
||||
}
|
||||
|
||||
let msg = 'Testata e righe importate salvate correttamente.';
|
||||
if (unresolved.length) msg += '\n\nDa aggiungere a mano dopo il ricaricamento:\n• ' + unresolved.join('\n• ');
|
||||
await Swal.fire({
|
||||
icon: unresolved.length ? 'warning' : 'success',
|
||||
title: 'Import completato',
|
||||
text: msg
|
||||
});
|
||||
window.location.href = 'manage-worksheet.php?id=' + newId;
|
||||
return true;
|
||||
};
|
||||
|
||||
$('#btnImportXlsx').on('click', function() {
|
||||
const wsId = parseInt($('#wsId').val() || '0', 10);
|
||||
if (wsId > 0) {
|
||||
Swal.fire({
|
||||
icon: 'info',
|
||||
title: 'Import solo su foglio nuovo',
|
||||
text: 'Apri “Nuovo Foglio di Lavoro” per importare un Excel.'
|
||||
});
|
||||
return;
|
||||
}
|
||||
$('#xlsxImportInput').val('').click();
|
||||
});
|
||||
|
||||
$('#xlsxImportInput').on('change', function() {
|
||||
const file = this.files && this.files[0];
|
||||
if (!file) return;
|
||||
window.importFile = file; // conserva il file per allegarlo dopo il salvataggio
|
||||
|
||||
const fd = new FormData();
|
||||
fd.append('xlsx_file', file);
|
||||
|
||||
Swal.fire({
|
||||
title: 'Lettura Excel…',
|
||||
allowOutsideClick: false,
|
||||
didOpen: () => Swal.showLoading()
|
||||
});
|
||||
|
||||
fetch('import_worksheet_xlsx.php', {
|
||||
method: 'POST',
|
||||
body: fd
|
||||
})
|
||||
.then(async r => {
|
||||
const t = await r.text();
|
||||
try {
|
||||
return JSON.parse(t);
|
||||
} catch (e) {
|
||||
throw new Error('Risposta non valida dal parser:\n' + t.slice(0, 300));
|
||||
}
|
||||
})
|
||||
.then(data => {
|
||||
Swal.close();
|
||||
if (!data.success) {
|
||||
Swal.fire({
|
||||
icon: 'error',
|
||||
title: 'Errore import',
|
||||
text: data.message || 'Errore sconosciuto'
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
const h = data.header || {};
|
||||
setVal('worksheet_date', h.worksheet_date);
|
||||
setVal('customer_name', h.customer_name);
|
||||
setVal('profile_type_code', h.profile_type_code);
|
||||
setVal('marking', h.marking);
|
||||
setVal('prod_control_measure_settings', h.prod_control_measure_settings);
|
||||
setVal('packages_or_pieces_per_box', h.packages_or_pieces_per_box);
|
||||
setVal('boxes_or_packages_per_pallet', h.boxes_or_packages_per_pallet);
|
||||
setVal('speed_expected_kg_h', h.speed_expected_kg_h);
|
||||
setVal('speed_expected_m_h', h.speed_expected_m_h);
|
||||
setVal('notes', h.notes);
|
||||
|
||||
const lk = data.header_lookups || {};
|
||||
Object.keys(lk).forEach(name => {
|
||||
if (lk[name] && lk[name].needs_review) markReview(name, lk[name]);
|
||||
});
|
||||
|
||||
window.importStaged = {
|
||||
mescole: data.mescole || [],
|
||||
packages: data.packages || [],
|
||||
warnings: data.warnings || []
|
||||
};
|
||||
renderBanner(data);
|
||||
|
||||
Swal.fire({
|
||||
icon: 'success',
|
||||
title: 'Excel letto',
|
||||
text: 'Controlla i campi evidenziati, poi premi “Salva testata”.',
|
||||
timer: 1800
|
||||
});
|
||||
})
|
||||
.catch(err => {
|
||||
Swal.close();
|
||||
Swal.fire({
|
||||
icon: 'error',
|
||||
title: 'Errore',
|
||||
text: err.message
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
</script>
|
||||
</body>
|
||||
|
||||
</html>
|
||||
Reference in New Issue
Block a user