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 (
{!ordersData && (
)}
{ordersData && (
I numeri in rosso indicano Pagamento alla Consegna
)}
);
}
Generatore Distinta Magazzino
Carica il file JSON di WooCommerce per creare il PDF di prelievo.
Carica Ordini WooCommerce (JSON)
Esporta i tuoi ordini in formato JSON dal pannello e caricali qui.
Ordini
{stats.totalOrders}
Articoli Totali
{stats.totalItems}
Valore Articoli
€{stats.totalValue.toFixed(2)}
Anteprima Dati Estratti
| 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)} |