filled('search')) { $search = $request->search; $query->where(function ($q) use ($search) { $q->where('codice_articolo', 'like', "%{$search}%") ->orWhere('descrizione', 'like', "%{$search}%") ->orWhere('ciclo', 'like', "%{$search}%"); }); } $articoli = $query->orderBy('created_at', 'desc')->paginate(15); return view('admin.articoli.index', compact('articoli')); } public function create() { return view('admin.articoli.create'); } public function store(Request $request) { $validated = $request->validate([ 'codice_articolo' => 'required|string|max:255|unique:articoli', 'ciclo' => 'nullable|string|max:255', 'hole_diameter' => 'nullable|string|max:255', 'diametro' => 'nullable|string|max:255', 'descrizione' => 'nullable|string', 'posizione' => 'nullable|string|max:255', 'quantita' => 'nullable|integer|min:0', 'tipo_lavorazione' => 'nullable|string|max:255', 'materiale_lavorare' => 'nullable|string|max:255', 'maximum_thickness' => 'nullable|string|max:255', 'speed_rpm' => 'nullable|integer|min:0', 'feed' => 'nullable|numeric|min:0', 'max_thrust_a' => 'nullable|string|max:255', 'min_torque_a' => 'nullable|string|max:255', 'quantita_fori' => 'nullable|integer|min:0', ]); $articolo = Articolo::create($validated); return redirect()->route('admin.articoli.show', $articolo) ->with('success', 'Articolo creato con successo.'); } public function show(Articolo $articolo) { return view('admin.articoli.show', compact('articolo')); } public function edit(Articolo $articolo) { return view('admin.articoli.edit', compact('articolo')); } public function update(Request $request, Articolo $articolo) { $validated = $request->validate([ 'codice_articolo' => 'required|string|max:255|unique:articoli,codice_articolo,' . $articolo->id, 'ciclo' => 'nullable|string|max:255', 'hole_diameter' => 'nullable|string|max:255', 'diametro' => 'nullable|string|max:255', 'descrizione' => 'nullable|string', 'posizione' => 'nullable|string|max:255', 'quantita' => 'nullable|integer|min:0', 'tipo_lavorazione' => 'nullable|string|max:255', 'materiale_lavorare' => 'nullable|string|max:255', 'maximum_thickness' => 'nullable|string|max:255', 'speed_rpm' => 'nullable|integer|min:0', 'feed' => 'nullable|numeric|min:0', 'max_thrust_a' => 'nullable|string|max:255', 'min_torque_a' => 'nullable|string|max:255', 'quantita_fori' => 'nullable|integer|min:0', 'immagine_articolo' => 'nullable|image|mimes:jpeg,png,jpg,gif,webp|max:4096', 'immagine_thrust' => 'nullable|image|mimes:jpeg,png,jpg,gif,webp|max:4096', 'immagine_tourque' => 'nullable|image|mimes:jpeg,png,jpg,gif,webp|max:4096', 'rimuovi_immagine_articolo' => 'nullable|boolean', 'rimuovi_immagine_thrust' => 'nullable|boolean', 'rimuovi_immagine_tourque' => 'nullable|boolean', ]); $imageFields = ['immagine_articolo', 'immagine_thrust', 'immagine_tourque']; foreach ($imageFields as $field) { $removeKey = 'rimuovi_' . $field; if ($request->boolean($removeKey)) { if ($articolo->$field) { Storage::disk('public')->delete($articolo->$field); } $validated[$field] = null; } elseif ($request->hasFile($field)) { if ($articolo->$field) { Storage::disk('public')->delete($articolo->$field); } $validated[$field] = $request->file($field)->store('articoli', 'public'); } else { unset($validated[$field]); } unset($validated[$removeKey]); } $articolo->update($validated); return redirect()->route('admin.articoli.show', $articolo) ->with('success', 'Articolo aggiornato con successo.'); } public function destroy(Articolo $articolo) { $articolo->delete(); return redirect()->route('admin.articoli.index') ->with('success', 'Articolo eliminato con successo.'); } public function qrCode(Articolo $articolo) { $qrCode = QrCode::format('svg') ->size(300) ->errorCorrection('H') ->generate($articolo->qr_url); return response($qrCode) ->header('Content-Type', 'image/svg+xml'); } public function downloadQrCode(Articolo $articolo) { $qrCode = QrCode::format('png') ->size(300) ->errorCorrection('H') ->generate($articolo->qr_url); return response($qrCode) ->header('Content-Type', 'image/png') ->header('Content-Disposition', 'attachment; filename="qr-' . $articolo->codice_articolo . '.png"'); } public function printQrCodes(Request $request) { $ids = explode(',', $request->get('ids', '')); $ids = array_filter($ids, fn($id) => is_numeric($id)); if (empty($ids)) { return redirect()->route('admin.articoli.index') ->with('error', 'Nessun articolo selezionato.'); } $articoli = Articolo::whereIn('id', $ids)->get(); // Generate QR codes as base64 for each article $articoliWithQr = $articoli->map(function ($articolo) { $qrCode = QrCode::format('png') ->size(100) // 100px ~ 10mm at 254 DPI ->errorCorrection('H') ->margin(0) ->generate($articolo->qr_url); return [ 'articolo' => $articolo, 'qr_base64' => base64_encode($qrCode), ]; }); $pdf = Pdf::loadView('admin.articoli.pdf-qrcodes', [ 'articoliWithQr' => $articoliWithQr, ]); $pdf->setPaper('a4', 'portrait'); return $pdf->stream('qrcodes.pdf'); } public function import(Request $request) { $request->validate([ 'file' => 'required|file|mimetypes:application/vnd.openxmlformats-officedocument.spreadsheetml.sheet,application/vnd.ms-excel,application/octet-stream', 'clean_import' => 'nullable|boolean', ]); $file = $request->file('file'); $cleanImport = $request->boolean('clean_import', false); try { $result = $this->importFromExcel($file->getPathname(), $cleanImport); $message = "Import completato! "; $message .= "Articoli importati: {$result['imported']}, "; $message .= "Articoli aggiornati: {$result['updated']}, "; $message .= "Totale articoli: {$result['total']}"; if (!empty($result['errors'])) { $message .= ". Errori: " . count($result['errors']); } return response()->json([ 'success' => true, 'message' => $message, 'data' => $result, ]); } catch (\Exception $e) { return response()->json([ 'success' => false, 'message' => 'Errore durante l\'importazione: ' . $e->getMessage(), ], 500); } } /** * Import articoli from Excel file. */ public function importFromExcel(string $filePath, bool $cleanImport = false): array { $spreadsheet = IOFactory::load($filePath); $worksheet = $spreadsheet->getActiveSheet(); $rows = $worksheet->toArray(); $imported = 0; $updated = 0; $errors = []; // If clean import, delete all existing records if ($cleanImport) { Articolo::truncate(); } // Skip header rows (first 2 rows) // Column layout (v2): 0=Codice Articolo, 1=Immagine Articolo (skip), 2=Ciclo, // 3=Hole Diameter, 4=Diametro, 5=Descrizione, 6=Posizione, 7=Quantità, // 8=Tipo lavorazione, 9=Materiale, 10=Max Thickness, 11=Speed RPM, 12=Feed, // 13=Max Thrust A, 14=Immagine Thrust (skip), 15=Min Torque A, // 16=Immagine Tourque (skip), 17=Quantità fori foreach (array_slice($rows, 2) as $index => $row) { // Skip empty rows if (empty($row[0])) { continue; } try { $exists = Articolo::where('codice_articolo', $row[0])->exists(); Articolo::updateOrCreate( ['codice_articolo' => $row[0]], [ 'ciclo' => $row[2] ?? null, 'hole_diameter' => $row[3] ?? null, 'diametro' => $row[4] ?? null, 'descrizione' => $row[5] ?? null, 'posizione' => $row[6] ?? null, 'quantita' => is_numeric($row[7]) ? (int) round((float) $row[7]) : 0, 'tipo_lavorazione' => $row[8] ?? null, 'materiale_lavorare' => $row[9] ?? null, 'maximum_thickness' => $row[10] ?? null, 'speed_rpm' => is_numeric($row[11]) ? (int) round((float) $row[11]) : null, 'feed' => is_numeric($row[12]) ? round((float) $row[12], 2) : null, 'max_thrust_a' => $row[13] ?? null, 'min_torque_a' => $row[15] ?? null, 'quantita_fori' => is_numeric($row[17]) ? (int) round((float) $row[17]) : null, ] ); if ($exists && !$cleanImport) { $updated++; } else { $imported++; } } catch (\Exception $e) { $errors[] = "Riga " . ($index + 3) . ": " . $e->getMessage(); } } return [ 'imported' => $imported, 'updated' => $updated, 'errors' => $errors, 'total' => Articolo::count(), ]; } }