import React, { useState, useEffect } from 'react'; import { Upload, FileText, Download, AlertCircle, RefreshCw, CheckCircle2, Euro } from 'lucide-react'; export default function App() { const [ordersData, setOrdersData] = useState(null); const [processedList, setProcessedList] = useState([]); const [isProcessing, setIsProcessing] = useState(false); const [scriptsLoaded, setScriptsLoaded] = useState(false); const [stats, setStats] = useState({ totalOrders: 0, totalItems: 0, totalValue: 0 }); useEffect(() => { const loadScript = (src) => { return new Promise((resolve, reject) => { const script = document.createElement('script'); script.src = src; script.onload = resolve; script.onerror = reject; document.body.appendChild(script); }); }; Promise.all([ loadScript('https://cdnjs.cloudflare.com/ajax/libs/jspdf/2.5.1/jspdf.umd.min.js'), ]).then(() => { loadScript('https://cdnjs.cloudflare.com/ajax/libs/jspdf-autotable/3.8.1/jspdf.plugin.autotable.min.js') .then(() => setScriptsLoaded(true)); }).catch(err => console.error("Errore caricamento librerie PDF", err)); }, []); const handleFileUpload = (event) => { const file = event.target.files[0]; if (!file) return; const reader = new FileReader(); reader.onload = (e) => { try { const json = JSON.parse(e.target.result); setOrdersData(json); processWooCommerceOrders(json); } catch (error) { alert("Errore nel parsing del file JSON. Assicurati che sia un formato WooCommerce valido."); } }; reader.readAsText(file); }; const loadSampleData = () => { const sampleWooCommerceOrders = [ { id: 32428, number: "32428", payment_method: "cod", payment_method_title: "Pagamento alla consegna", billing: { phone: "+393284104902" }, shipping_total: "8.00", fee_lines: [{ total: "5.00" }], line_items: [ { name: "CGM 126A IPER MONO", quantity: 1, total: "145.00", meta_data: [{ key: "pa_colore", value: "petrolio satinato" }, { key: "pa_taglia", value: "L" }] } ] }, { id: 32426, number: "32426", payment_method: "paypal", payment_method_title: "PayPal", billing: { phone: "3331234567" }, shipping_total: "0.00", fee_lines: [], line_items: [ { name: "CGM 116A AIR MONO", quantity: 1, total: "95.50", meta_data: [{ key: "pa_colore", value: "Bianco gloss" }, { key: "pa_taglia", value: "XL" }] } ] }, { id: 32389, number: "32389", payment_method: "cod", payment_method_title: "Contrassegno", billing: { phone: "+393924734980" }, shipping_total: "10.00", fee_lines: [{ total: "4.50" }], line_items: [ { name: "Origine Aprica", quantity: 1, total: "119.90", meta_data: [{ key: "Variante", value: "Shade Red Black Matt | Nero Rosso Opaco" }, { key: "Taglia", value: "M" }] } ] }, { id: 32418, number: "32418", payment_method: "stripe", payment_method_title: "Carta di Credito", billing: { phone: "3471112223" }, shipping_total: "5.00", fee_lines: [], line_items: [ { name: "Ska-P LUKE MONO", quantity: 2, total: "180.00", meta_data: [{ key: "Colore", value: "Nero Opaco" }, { key: "Misura", value: "XL" }] } ] } ]; setOrdersData(sampleWooCommerceOrders); processWooCommerceOrders(sampleWooCommerceOrders); }; const processWooCommerceOrders = (orders) => { setIsProcessing(true); const groupedItems = {}; let totalItemsCount = 0; let grandTotalValue = 0; const uniqueOrders = new Set(); orders.forEach(order => { uniqueOrders.add(order.id); const isCOD = order.payment_method === 'cod' || (order.payment_method_title && order.payment_method_title.toLowerCase().includes('consegna')) || (order.payment_method_title && order.payment_method_title.toLowerCase().includes('contrassegno')); const phone = order.billing?.phone || ''; // Calcolo costi di spedizione e contrassegno per l'intero ordine const shippingCost = parseFloat(order.shipping_total || 0); const feeCost = (order.fee_lines || []).reduce((sum, fee) => sum + parseFloat(fee.total || 0), 0); const orderShippingAndFees = shippingCost + feeCost; const orderRef = { id: order.id, number: `#${order.number || order.id}`, isCOD: isCOD, phone: phone, shippingAndFees: orderShippingAndFees }; order.line_items.forEach(item => { let color = "Standard"; let size = "N/A"; let model = item.name; const itemTotal = parseFloat(item.total || 0); if (item.meta_data && item.meta_data.length > 0) { item.meta_data.forEach(meta => { const key = meta.key.toLowerCase(); if (key.includes('color') || key.includes('colore') || key.includes('variante')) { color = meta.value; } if (key.includes('size') || key.includes('taglia') || key.includes('misura') || key.includes('pa_taglia')) { size = meta.value; } }); } if (model.includes(' - ')) { const parts = model.split(' - '); model = parts[0]; if (color === "Standard" && parts.length > 1) { const subParts = parts[1].split(','); if(subParts.length > 0) color = subParts[0].trim(); if(subParts.length > 1 && size === "N/A") size = subParts[1].trim(); } } const key = `${model}_${color}_${size}`; if (!groupedItems[key]) { groupedItems[key] = { model: model, color: color, size: size, quantity: 0, itemCostTotal: 0, shippingFeeTotal: 0, orders: [], processedOrdersForShipping: new Set() }; } groupedItems[key].quantity += item.quantity; groupedItems[key].itemCostTotal += itemTotal; groupedItems[key].orders.push(orderRef); // Aggiungiamo i costi di spedizione/COD una sola volta per ordine all'interno di questo raggruppamento if (!groupedItems[key].processedOrdersForShipping.has(order.id)) { groupedItems[key].shippingFeeTotal += orderShippingAndFees; groupedItems[key].processedOrdersForShipping.add(order.id); } totalItemsCount += item.quantity; grandTotalValue += itemTotal; }); }); const list = Object.values(groupedItems).sort((a, b) => a.model.localeCompare(b.model)); setProcessedList(list); setStats({ totalOrders: uniqueOrders.size, totalItems: totalItemsCount, totalValue: grandTotalValue }); setIsProcessing(false); }; const generatePDF = () => { if (!window.jspdf || !scriptsLoaded) { alert("La libreria PDF sta ancora caricando. Riprova tra un secondo."); return; } const { jsPDF } = window.jspdf; const doc = new jsPDF({ orientation: 'portrait', unit: 'mm', format: 'a4' }); doc.setFontSize(16); doc.setFont("helvetica", "bold"); doc.text("DISTINTA DI MAGAZZINO", 14, 20); doc.setFontSize(10); doc.setFont("helvetica", "normal"); const today = new Date().toLocaleDateString('it-IT', { day: 'numeric', month: 'long', year: 'numeric' }); doc.text(`Lista di prelievo aggiornata al ${today}`, 14, 27); doc.setFont("helvetica", "bold"); doc.text(`Totale Ordini da Elaborare: ${stats.totalOrders}`, 14, 35); doc.text(`Totale Articoli da Prelevare: ${stats.totalItems}`, 14, 40); const tableBody = processedList.map(item => { let ordersString = ""; item.orders.forEach((ord, index) => { let textPart = ord.number; if (ord.isCOD && ord.phone) { textPart += ` (${ord.phone})`; } if (index < item.orders.length - 1) textPart += ", "; ordersString += textPart; }); return [ item.model, item.color, item.size, item.quantity.toString(), { content: ordersString, rawData: item.orders }, `€ ${item.itemCostTotal.toFixed(2)}`, `€ ${item.shippingFeeTotal.toFixed(2)}`, "" ]; }); doc.autoTable({ startY: 45, head: [['MARCA & MODELLO', 'COLORE/VARIANTE', 'TAGLIA', 'Q.TÀ', 'ORDINI RIF.', 'COSTO ART.', 'SPED+COD', 'SPUNTA']], body: tableBody, theme: 'grid', headStyles: { fillColor: [40, 40, 40], textColor: 255, fontStyle: 'bold', halign: 'center' }, columnStyles: { 0: { cellWidth: 35 }, 1: { cellWidth: 35 }, 2: { cellWidth: 15, halign: 'center' }, 3: { cellWidth: 10, halign: 'center', fontStyle: 'bold' }, 4: { cellWidth: 40 }, 5: { cellWidth: 18, halign: 'right' }, // Costo Articoli 6: { cellWidth: 18, halign: 'right' }, // Spedizione e COD 7: { cellWidth: 12, halign: 'center' } }, styles: { fontSize: 8, cellPadding: 2, valign: 'middle' }, didDrawCell: function(data) { if (data.column.index === 7 && data.row.section === 'body') { doc.setDrawColor(0); doc.setLineWidth(0.5); const boxSize = 5; const x = data.cell.x + (data.cell.width / 2) - (boxSize / 2); const y = data.cell.y + (data.cell.height / 2) - (boxSize / 2); doc.rect(x, y, boxSize, boxSize); } }, willDrawCell: function(data) { if (data.column.index === 4 && data.row.section === 'body') { const orders = data.cell.raw.rawData; const hasCOD = orders.some(o => o.isCOD); if (hasCOD) { doc.setTextColor(220, 38, 38); } else { doc.setTextColor(40, 40, 40); } } } }); doc.save('Distinta_Magazzino_WooCommerce.pdf'); }; return (

Generatore Distinta Magazzino

Carica il file JSON di WooCommerce per creare il PDF di prelievo.

{!ordersData && (

Carica Ordini WooCommerce (JSON)

Esporta i tuoi ordini in formato JSON dal pannello e caricali qui.

)} {ordersData && (

Ordini

{stats.totalOrders}

Articoli Totali

{stats.totalItems}

Valore Articoli

€{stats.totalValue.toFixed(2)}

Anteprima Dati Estratti

I numeri in rosso indicano Pagamento alla Consegna
{processedList.map((item, idx) => ( ))}
Marca & Modello Colore/Variante Taglia Q.tà Ordini Rif. Costo Articoli Sped. + COD
{item.model} {item.color} {item.size} {item.quantity} {item.orders.map((ord, i) => ( {ord.isCOD ? ( {ord.number} ({ord.phone}) ) : ( {ord.number} )} {i < item.orders.length - 1 ? ", " : ""} ))} € {item.itemCostTotal.toFixed(2)} € {item.shippingFeeTotal.toFixed(2)}
)}
); }