WooCommerce: multi magazzino e giacenze da CSV

Quarta puntata di «Faccio cose, vedo gente»: il multi magazzino WooCommerce che va a prendere i CSV del gestionale, somma le giacenze di tre depositi nello stock nativo e toglie moltissime ore di lavoro a mano, con backorder forzati a runtime e WP-CLI nel cron.

Quarta puntata di Faccio cose, vedo gente, la serie sui plugin WordPress fatti su misura, e seconda metà della storia in cui ho raccontato la gestione dei listini prezzi. Stesso cliente, stesso gestionale, problema diverso: la merce sta in più depositi, la logistica esporta le giacenze deposito per deposito e WooCommerce conosce un solo numero di stock per articolo. Serviva un sistema multi magazzino per WooCommerce che leggesse quei file, tenesse la giacenza di ogni deposito e scrivesse la somma nello stock nativo, senza toccare il resto del negozio.

In questo articolo ti racconto come un lavoro che a mano avrebbe richiesto moltissime ore uomo si è trasformato in un processo che parte da solo ogni notte, e perché per costruire questo multi magazzino è servito un plugin su misura: era WordPress a dover andare a prendere i file del gestionale. Poi vediamo come ho modellato le giacenze con un meta per deposito, come l’import a lotti funziona dall’admin e da WP-CLI con un cron, perché ho forzato i backorder a runtime e come il plugin si aggiorna da solo dalle release di GitHub. Il codice è pubblico su GitHub.



Un magazzino, poi tre, poi il gestionale

La prima versione sincronizzava un solo deposito da un CSV. Poi i depositi sono diventati tre e lo stock andava sommato.

Il brief iniziale era di una riga: «il gestionale di logistica esporta ogni notte un file con le giacenze del magazzino e vorremmo che WooCommerce le leggesse e aggiornasse le quantità dei prodotti». Un CSV con due colonne, codice articolo e quantità, pubblicato su un URL. La prima versione del plugin faceva solo quello, con un pulsante nell’admin, una barra di avanzamento e tre contatori: righe elaborate, SKU non trovati, SKU aggiornati. Scriveva la quantità in un meta del prodotto, _stock_CMT, dal nome del deposito, e per un paio di settimane è bastato.

Poi, come succede sempre, il problema vero è uscito a rate. I depositi erano tre, non uno, ciascuno con il suo file. Il cliente voleva vedere la giacenza di ogni deposito nell’admin ma vendere sulla somma. Uno strumento esterno, che sincronizza altre cose dal gestionale, continuava a marcare come esauriti gli articoli a quantità zero, mentre il cliente vuole vendere sempre, anche in arretrato, perché il riassortimento è questione di giorni.

Ogni richiesta è diventata una versione, con il suo changelog, e il plugin che era nato come un pulsante è diventato un multi magazzino WooCommerce vero, con una tabella delle giacenze, i comandi da terminale per il cron, un interruttore per i backorder e l’aggiornamento automatico. È il modo in cui preferisco lavorare: una base piccola che funziona, e poi le funzioni che servono davvero, nell’ordine in cui il cliente le scopre.

La scheda Configurazione del multi magazzino WooCommerce: tre depositi con URL del CSV, interruttore dei backorder forzati e pulsanti di sincronizzazione
La scheda Configurazione del multi magazzino WooCommerce: tre depositi con l’URL del CSV e l’anteprima della chiave meta, l’interruttore per i backorder forzati, i pulsanti di sincronizzazione con le barre di avanzamento e il «Sync All» verso lo stock nativo.

Multi magazzino: ore di lavoro risparmiate

Aggiornare a mano le giacenze di tre depositi, articolo per articolo e variante per variante, è un lavoro che nessuno farebbe davvero ogni giorno. Il multi magazzino lo ha trasformato in un processo che parte da solo di notte e non richiede nessuno.

Prova a immaginare questo multi magazzino senza plugin. Ogni notte il gestionale produce tre file, uno per deposito, con migliaia tra prodotti e varianti. Per portare quei numeri nel negozio a mano dovresti aprire ogni scheda, leggere la quantità dei tre depositi, sommarle, ricopiare il totale nel campo di stock di WooCommerce e passare alla successiva. Poi ripetere tutto la mattina dopo, perché le giacenze cambiano ogni giorno. Sono moltissime ore uomo, da rifare in continuazione, con un errore di battitura ogni tanto che diventa un cliente che compra un articolo che non c’è, o che non ne compra uno che c’è.

Il plugin multi magazzino sposta tutto questo fuori dall’attenzione di chiunque. Di notte il cron scarica i tre file, aggiorna la giacenza di ogni deposito e scrive il totale nello stock nativo; al mattino il negozio è già allineato e in azienda trovano la Panoramica Stock pronta da consultare, senza aver premuto nulla. L’intervento umano si riduce a guardare la tabella quando serve e a ricontrollare un singolo articolo con il pulsante Sync. Quella che era una mansione quotidiana è diventata un processo automatico che gira da solo.

Il valore di un multi magazzino così non sta in una funzione vistosa: si misura nelle ore di lavoro che non si fanno più, ogni giorno. E la parte che le fa risparmiare, quella che va a prendere i file del gestionale, è anche la più su misura: te la racconto nella sezione dopo.

Il CSV lo va a prendere WordPress

Il gestionale pubblica un CSV per deposito e non spedisce niente a nessuno: è WordPress che deve andare a prendere le giacenze del multi magazzino, ogni notte, e poi sapere che cosa farne. Una necessità così specifica si risolve con uno sviluppo su misura.

Il gestionale di logistica pubblica semplicemente un file CSV per ciascun deposito a un indirizzo, e finisce lì. Non conosce WooCommerce, non chiama il negozio, non ha un pulsante «esporta verso WordPress». Se il negozio vuole le giacenze, deve andarle a prendere lui e capire che cosa c’è scritto. È il contrario di come ragionano gli strumenti di importazione che conosco, pensati per mappare le colonne di un file sui campi del prodotto, dove di solito il file lo porti tu.

Per il multi magazzino serviva qualcosa di più preciso: WordPress che scarica da solo un file diverso per ogni deposito, ne ripulisce le stranezze, lo elabora a lotti senza andare in timeout, scrive ogni quantità nel meta del suo deposito, somma tutto nello stock nativo e lo fa di notte da cron. Ogni pezzo, preso da solo, si trova in giro; la combinazione completa, nei plugin che conosco, non l’ho trovata. È il tipo di problema per cui ha senso lo sviluppo su misura: un bisogno concreto e ripetitivo che un plugin generico non copre senza chiedere al cliente di cambiare il modo in cui lavora.

Il CSV delle giacenze è come tutti i CSV che escono da un gestionale: separatore punto e virgola, quantità scritte come 1,0000000000000, intestazione a volte presente e a volte no, e in testa al file il BOM UTF-8 che Excel aggiunge quando qualcuno lo apre e lo risalva. Senza toglierlo, i tre byte invisibili si attaccano al primo SKU e quell’articolo non viene mai trovato, senza errori, in silenzio. Il plugin lo rimuove, converte la virgola, salta l’intestazione e produce righe pulite.

La sincronizzazione dall’admin è in due fasi. La prima scarica il file con wp_remote_get, lo analizza e mette le righe in un transient che dura un’ora. La seconda è un ciclo di richieste AJAX, ciascuna delle quali elabora cinquanta righe, cerca i prodotti per SKU, scrive il meta del deposito e risponde con i contatori; il JavaScript aggiorna la barra e chiama il lotto successivo. Così un file da migliaia di righe non va mai in timeout, il browser mostra cosa sta succedendo e un errore su un lotto non perde il lavoro fatto prima.

Un meta per deposito, la somma nello stock

Nel multi magazzino ogni deposito scrive la propria giacenza in un meta dedicato, _stock_ seguito dal nome, su prodotti e varianti. Un passaggio separato somma i meta e aggiorna lo stock nativo di WooCommerce, solo se il totale è cambiato.

WooCommerce ha un solo campo di stock per prodotto, _stock, con la gestione delle scorte che gira attorno a quel numero: stato di disponibilità, soglie, email di scorte basse, riduzione all’ordine. Non volevo riscrivere quella logica né aggiungere tabelle: volevo affiancarla. Così ogni deposito scrive la propria quantità in un meta con un nome derivato dall’etichetta, _stock_Torino, _stock_Milano, _stock_Bologna, su prodotti semplici e su ogni variante. L’etichetta si può cambiare, l’identificativo interno del deposito no, e la vecchia chiave del primo deposito è rimasta compatibile.

La somma è un passaggio esplicito, il pulsante Sync All: raccoglie gli id di tutti i prodotti e varianti che hanno almeno un meta di deposito, somma i valori e scrive il totale nello stock nativo con la funzione di WooCommerce, wc_update_product_stock(), così lo stato di disponibilità e tutto il resto si aggiornano come se la quantità l’avessi cambiata a mano. Sulle varianti, se la gestione delle scorte non è attiva, la attiva, altrimenti WooCommerce non ricalcola lo stato.

Il dettaglio che fa la differenza su un catalogo grande è lo skip: se la somma dei depositi è uguale allo stock attuale, il prodotto viene saltato senza scrivere nulla. Sembra ovvio, ma ogni chiamata a wc_update_product_stock() cancella i transient del prodotto e innesca una catena di ricalcoli; su migliaia di varianti, scrivere solo ciò che è cambiato trasforma una sincronizzazione da minuti in secondi, e i contatori «elaborati, aggiornati, saltati» lo mostrano in tempo reale. È una delle cose che Claud.ia ha proposto da sola quando le ho chiesto perché il Sync All fosse lento.

La Panoramica Stock del multi magazzino: SKU, stock nativo e una colonna per deposito, con ricerca e Sync per riga
La Panoramica Stock del multi magazzino WooCommerce: una riga per prodotto o variante con SKU, SKU del padre, stock nativo e una colonna per deposito, con la ricerca per SKU e il pulsante Sync per singola riga.

Backorder forzati senza toccare il database

Il cliente vende sempre, anche a stock zero, ma uno strumento esterno continuava a marcare gli articoli come esauriti. Un interruttore forza backorder, gestione scorte e stato «in arretrato» con filtri a runtime, senza scrivere nulla nel database.

Questa è la funzione di cui vado più fiero, perché risolve un problema politico con una soluzione tecnica. Nel negozio del cliente un articolo a quantità zero deve restare acquistabile in arretrato: il riassortimento arriva in pochi giorni e perdere l’ordine sarebbe peggio che spedire in ritardo. Il modo normale sarebbe impostare «consenti ordini arretrati» su ogni prodotto. Ma un altro strumento, che sincronizza il catalogo dal gestionale, riscrive periodicamente quei campi e rimette gli articoli a «esaurito». Una guerra di scritture sul database, che avrei perso.

La classe Backorder_Manager non scrive nulla: si aggancia ai filtri con cui WooCommerce legge le proprietà del prodotto, woocommerce_product_get_backorders, get_manage_stock, get_stock_status, e alle loro versioni per le varianti, e risponde sempre «sì, consentiti», «sì, gestito», «in arretrato». A priorità alta, così vince sugli altri. Il database può dire quello che vuole, il negozio vende. Due filtri di sicurezza su is_in_stock e backorders_allowed coprono i punti in cui WooCommerce non passa dai getter.

L’interruttore nell’admin è reversibile nel senso più pieno: spento, il plugin non registra nessun filtro e il negozio torna com’era, senza nulla da ripulire. Un filtro per sviluppatori permette di escludere singoli prodotti dal forzamento. E siccome con il forzamento attivo le email di WooCommerce all’amministratore su scorte basse, esaurito e in arretrato diventano solo rumore, la versione successiva le toglie dalla lista delle email registrate, sempre solo finché l’interruttore è acceso. Le email degli ordini non vengono toccate.

Un prodotto a stock zero nel negozio che resta acquistabile su ordinazione grazie ai backorder forzati del multi magazzino
Un articolo con stock zero nel negozio: con i backorder forzati resta acquistabile e mostra «disponibile su ordinazione», mentre nel database lo strumento esterno continua a segnarlo come esaurito.

WP-CLI, cron e la tabella delle giacenze

Gli stessi metodi dell’admin sono esposti come comandi WP-CLI, wp wms sync e wp wms sync_total, da mettere nel cron di sistema. La tabella delle giacenze fa due query per pagina ed è pensata per cataloghi con migliaia di varianti.

Un multi magazzino WooCommerce che richiede di premere un pulsante ogni mattina non è un’automazione, è una mansione. Per questo la logica di scaricamento, analisi e scrittura vive in metodi pubblici riusati da due facce: i gestori AJAX dell’admin, che sono involucri sottili con i controlli di sicurezza, i transient e la risposta JSON, e i comandi WP-CLI. wp wms sync –warehouse=all scarica i file di tutti i depositi e aggiorna i meta con una barra di avanzamento per deposito; wp wms sync_total fa la somma. Due righe nel crontab del server, una dopo l’altra, e l’aggiornamento notturno è fatto.

# ogni notte alle 3: giacenze dei depositi, poi la somma nello stock nativo
0 3 * * * cd /var/www/html && wp wms sync --warehouse=all --quiet
20 3 * * * cd /var/www/html && wp wms sync_total --quiet

La tabella delle giacenze nell’admin è nata semplice e poi è stata riscritta per i numeri del cliente. La prima versione faceva una WP_Query, poi una get_posts e poi leggeva i meta: tre viaggi al database per pagina. Ora sono due query SQL dirette, una paginata che restituisce id, titolo, SKU e SKU del padre con due join, e una sola query per tutti i meta di stock della pagina. I prodotti variabili padre sono nascosti con un NOT EXISTS sulle varianti, perché lo stock vero sta nelle varianti e la riga del padre era solo rumore.

Nella tabella del multi magazzino si cerca per SKU, anche per SKU del padre per vedere tutte le varianti insieme, e un pulsante Sync per riga ricalcola il totale di un solo prodotto senza scaricare nulla.

I comandi WP-CLI del multi magazzino WooCommerce nel terminale: sincronizzazione dei depositi e somma nello stock nativo
I comandi WP-CLI del multi magazzino WooCommerce in un terminale: sincronizzazione dei tre depositi con le barre di avanzamento e somma finale nello stock nativo con i contatori di prodotti elaborati, aggiornati e saltati.

Aggiornamenti da GitHub senza librerie

Il plugin si aggiorna dalle release del repository pubblico su GitHub leggendo l’API delle release, senza librerie esterne: notifica nella pagina Plugin, popup con il changelog e installazione in loco.

Un plugin su misura installato su un sito del cliente ha un problema che quelli del repository ufficiale non hanno: come arrivano gli aggiornamenti. Per il plugin delle prenotazioni ho usato una libreria nota; qui ho voluto provare a farne a meno, per tenere il plugin senza dipendenze. La classe Updater interroga l’API di GitHub sull’ultima release del repository, con un transient di qualche ora e lo User-Agent che GitHub pretende, e se la versione remota è maggiore di quella installata inietta i dati nel transient degli aggiornamenti di WordPress, come farebbe il repository ufficiale.

Da lì funziona tutto il meccanismo nativo: la notifica nella pagina Plugin, il popup «Visualizza dettagli» con il changelog preso dal corpo della release, e l’aggiornamento con un clic. Il trucco sta in upgrader_source_selection: lo zip che GitHub genera per una release contiene una cartella con un nome tipo owner-repo-sha, e WordPress la installerebbe come plugin nuovo accanto al vecchio. Il filtro la rinomina con il nome della cartella reale, così l’aggiornamento avviene in loco. Nella versione successiva lo stesso filtro gestisce anche l’upload manuale dello zip.

In admin c’è una scheda Aggiornamenti con la versione installata e l’ultima disponibile, il pulsante per controllare subito e l’avviso con il changelog quando c’è qualcosa di nuovo, più i link «Impostazioni» e «GitHub» nella riga del plugin. Se la rete non risponde o l’API cambia, il plugin non mostra nulla: niente avvisi fantasma, niente aggiornamenti verso versioni che non esistono.

Cosa rifarei uguale

Se dovessi rifarlo domani cambierei poco: le scelte che hanno funzionato sono quelle che affiancano WooCommerce invece di sostituirlo.

Quello che rifarei uguale: i meta per deposito accanto allo stock nativo invece di una tabella parallela, la stessa logica per AJAX e WP-CLI, i backorder a runtime e lo skip delle scritture inutili. Sono le scelte che fanno di questo multi magazzino WooCommerce un plugin piccolo che convive con tutto il resto del negozio invece di sostituirlo.

Fine inventario, tutti a casa

Un plugin su misura ha automatizzato un lavoro da moltissime ore uomo e va a prendere i CSV del gestionale senza che nessuno glielo chieda: un multi magazzino WooCommerce piccolo, che convive con il negozio e si aggiorna da solo.

Con questa puntata si chiude la coppia di plugin per questo cliente: i listini prezzi decidono quanto costa un articolo per ciascun cliente, il multi magazzino WooCommerce decide se si può vendere e quanto ce n’è. La lezione tecnica è che su WooCommerce conviene affiancare, non sostituire: un meta accanto allo stock, un filtro accanto ai getter, un comando accanto a un pulsante. La lezione di metodo è quella di tutta la serie: i requisiti veri arrivano a rate, e un plugin piccolo con un changelog serio li assorbe uno alla volta senza diventare un mostro.

La terza lezione è quella di questa puntata: quando un processo è ripetitivo e ha sempre la stessa forma, conviene pagare una volta lo sviluppo e smettere di pagare ogni giorno il lavoro a mano.

Per approfondire: il repository su GitHub con il changelog che racconta le sette versioni, la documentazione per sviluppatori di WooCommerce per i filtri sui prodotti e la gestione delle scorte, e le puntate precedenti nella categoria WordPress: il plugin delle prenotazioni, i listini per cliente e la migrazione da WPBakery a Gutenberg. Se hai un negozio con la merce in più posti e un gestionale che parla per CSV, prenota una call: il file lo guardiamo insieme, e di solito la soluzione è più piccola di quanto sembri.

Quanti magazzini ha il tuo WooCommerce, e chi li tiene allineati?
Se ogni mattina aggiorni le giacenze a mano, scrivimi: troviamo una soluzione.

Domande frequenti

WooCommerce gestisce più magazzini di suo?

No: WooCommerce ha un solo campo di stock per prodotto o variante. Per un multi magazzino WooCommerce serve un plugin che tenga le giacenze per deposito e decida come arrivare al numero unico che il negozio usa per vendere.

Come funziona il multi magazzino WooCommerce descritto qui?

Ogni deposito ha un CSV con SKU e quantità; il plugin scrive la giacenza in un meta dedicato per deposito e un passaggio separato somma i meta nello stock nativo di WooCommerce, aggiornando solo i prodotti il cui totale è cambiato.

Si può automatizzare la sincronizzazione del multi magazzino?

Sì, con i comandi WP-CLI wp wms sync e wp wms sync_total messi nel cron del server: il negozio si allinea da solo ogni notte. Usano la stessa logica dei pulsanti dell’admin.

Quante ore di lavoro a mano si risparmiano con un multi magazzino automatico?

Tutte quelle che servirebbero ogni giorno per aggiornare a mano le giacenze di ogni deposito, articolo per articolo e variante per variante. Con tre depositi e migliaia di varianti sono moltissime ore uomo, che il cron porta a zero.

Perché un multi magazzino richiede un plugin su misura per importare i CSV dei depositi?

Perché qui il file non arriva da una persona né viene spinto verso WordPress: è WordPress che deve scaricarlo da un indirizzo, deposito per deposito, ripulirlo, elaborarlo a lotti e scrivere le quantità in un meta per ciascun magazzino prima di sommarle. Nella mia esperienza una combinazione così specifica si risolve meglio con uno sviluppo su misura che con un importatore generico.

Cosa vuol dire «backorder forzati a runtime»?

Che il plugin, con un interruttore, risponde ai filtri di WooCommerce dicendo che ogni prodotto consente ordini arretrati ed è «in arretrato» quando lo stock è zero, senza modificare il database. Così sopravvive agli strumenti esterni che riscrivono i prodotti, e spegnendo l’interruttore tutto torna com’era.

Il multi magazzino WooCommerce mostra la giacenza per deposito ai clienti?

Nella versione attuale no: le giacenze per deposito si vedono nell’admin, il cliente vede la disponibilità complessiva. Mostrare «disponibile a Torino» in scheda prodotto è una delle estensioni possibili.

Come si aggiorna un plugin su misura installato sul sito del cliente?

Questo plugin legge le release del suo repository pubblico su GitHub e si inserisce nel sistema di aggiornamento nativo di WordPress, con notifica, changelog e installazione in loco, senza librerie esterne.

Maurizio Pelizzone

Maurizio Pelizzone

Sono Maurizio Pelizzone, sviluppatore WordPress da oltre vent'anni e consulente di intelligenza artificiale a Torino, co-fondatore di Mavida. Realizzo plugin e integrazioni su misura per WordPress e WooCommerce, automazioni dei processi con n8n e agenti AI, e tengo corsi e workshop su Claude Code, prompt engineering e vibe coding, in presenza a Torino o da remoto. Sono speaker ai WordCamp e autore della guida gratuita in italiano a Claude Code; se vuoi parlarmi di un progetto puoi prenotare una call conoscitiva.

Lascia una risposta

Il tuo indirizzo email non sarà pubblicato. I campi obbligatori sono contrassegnati *