Risolvere il problema dei quantitativi minimi di ordine (MOQ)

Risolvere il problema dei quantitativi minimi di ordine (MOQ)


Home » Knowledgebase » Qui
di Joannès Vermorel, gennaio 2016

I quantitativi minimi di ordine (MOQ) rappresentano un vincolo piuttosto frequente in logistica, che consiste nell'imporre all'acquirente di ordinare una quantità minima, di solito espressa in unità o in valuta, per poter effettuare un ordine. Capita spesso, inoltre, che più vincoli MOQ debbano essere soddisfatti contemporaneamente. Per risolvere il problema, è necessario mettere a punto un ordine di acquisto (quasi) ottimale che soddisfi tutti i vincoli MOQ e che allo stesso tempo massimizzi il ritorno economico associato alle unità acquistate.

Il problema MOQ viene formalizzato come un problema di ottimizzazione non lineare complesso, perché possiamo dimostrare che calcolare una soluzione ottimale non rientra nelle nostre capacità computazionali. Sebbene non possiamo arrivare a una soluzione ottimale, possiamo però trovare una soluzione quasi ottimale, con l'aiuto di algoritmi avanzati per la risoluzione di problemi non lineari. In particolare, faremo qui riferimento a moqsolv, un risolutore numerico avanzato sviluppato da Lokad proprio per far fronte al problema MOQ.

I vincoli MOQ più frequenti

Un vincolo MOQ può presentarsi in forme diverse. Tra quelle più comuni che possiamo incontrare in una catena logistica, ricordiamo:

  • un quantitativo minimo espresso in unità per SKU, di solito per gli articoli troppo economici per poter essere venduti singolarmente;
  • un quantitativo minimo espresso in dollari per l'intero ordine di acquisto, di solito quando il fornitore non applica spese di consegna;
  • un quantitativo minimo espresso in unità per categoria di articoli, di solito per i prodotti su ordinazione che prevedono una partita di produzione minima.

Gestire un vincolo alla volta è piuttosto semplice nella pratica. Quando però i vincoli da tenere in conto sono più di uno alla volta, mettere a punto un ordine di acquisto che li soddisfi tutti diventa molto più complicato.

MOQ: concetti da tenere a mente

Prima di passare all'ottimizzazione numerica, rivediamo i concetti più importanti da tenere a mente per risolvere il problema MOQ:

  • gli articoli, che rappresentano quello che possiamo acquistare. La quantità di un articolo è spesso espressa da un numero intero, ma sono possibili eccezioni;
  • le quantità ordinate per ogni articolo (eventualmente zero), una potenziale soluzione del problema MOQ;
  • il rendimento associato a ogni unità extra per ogni articolo: in sostanza, quello che si ottiene con la funzione stockrwd (rendimento delle scorte), anche se non è necessario usare una funzione;
  • i costi associati alle unità da acquistare. Lo scopo è quello di massimizzare il rendimento di un budget di spesa espresso in costi. I costi sono di solito fissi per unità, ma è bene considerare anche variazioni di prezzo;
  • gli obiettivi, che servono a specificare un criterio di chiusura dell'ordine eventualmente diverso dai costi. Questo è forse il concetto più complesso, che vedremo perciò nel dettaglio.

Il criterio classico che determina la chiusura dell'ordine quando si stila una lista di priorità degli acquisti è il budget massimo: le unità sono acquistate in ordine di ROI decrescente fino a che il budget di spesa non viene esaurito. Basarsi unicamente sul budget, però, non ci dà alcuna indicazione sulla performance generale dell'inventario. Quindi, fermo restando che il nostro scopo rimane quello di ottimizzare il ROI indipendentemente dal criterio usato, è molto pratico considerare criteri alternativi, come un obiettivo, ad esempio il fill rate.

Un obiettivo è un meccanismo generico che serve a definire un criterio alternativo di chiusura dell'ordine, sulla base di una strategia di ordine per priorità. In parole più semplici, il nostro scopo è quello di creare un ordine di acquisto che offra il rendimento più elevato possibile per l'investimento minore possibile, pur raggiungendo l' obiettivo prefissato. Qui di seguito daremo una definizione più precisa di questo processo di ottimizzazione.

Esempio: Il responsabile della logistica di un'azienda fissa l'obiettivo di raggiungere un fill rate del 90%. Risolvere il problema MOQ significa calcolare un ordine che abbia il minor costo possibile, ma che al contempo massimizzi il rendimento di un fill rate del 90%. In questo caso, l'ordine NON sarà l'ordine minore possibile (in termini di costi) per raggiungere un fill rate del 90%: se così fosse, l'ordine avrebbe come unica priorità quella di ottimizzare il fill rate. Invece, il nostro ordine è quello minore possibile che, avendo come priorità il rendimento delle scorte, è anche abbastanza grande da fornire un fill rate del 90%. Usare come unica priorità il fill rate sarebbe un errore, perché, a differenza del rendimento delle scorte, il fill rate non tiene conto dei costi associati all'eventuale stock morto generato.

Definizione formale del problema MOQ

In questa sezione vedremo perché il problema MOQ è un problema formale di ottimizzazione non lineare. Si tratta di un problema arduo, per la precisione NP-arduo: il problema MOQ estende infatti il problema bin packing, che è già di per sé un problema NP-arduo. Il problema MOQ è quindi almeno altrettanto difficile del problema bin packing, ma, nonostante sia NP-arduo, esistono delle ottime soluzioni che possono essere applicate anche nella pratica.

Sia $I$ l'insieme degli articoli da considerare per l'ordine. Sia $q_i$ con $i \in I$ la quantità da ordinare per l'articolo $i$.

Definiamo quindi una serie di funzioni.

  • Sia $r_i(q)$ il rendimento associato al tenere a magazzino $q$ unità dell'articolo $i$.
  • Sia $c_i(q)$ il costo associato all'acquisto di $q$ unità dell'articolo $i$.
  • Sia $t_i(q)$ l' obiettivo associato al tenere a magazzino $q$ unità dell'articolo $i$.

La funzione di rendimento può restituire un valore positivo o negativo, mentre le funzioni di costo e di obiettivo sono solo positive: $$\forall i, \forall q, c_i(q) > 0 \text{ e } t_i(q) >0$$ Sia $M$ la serie di vincoli MOQ. Per ogni $m \in M$, abbiamo $I_m$, la lista degli articoli che fanno riferimento al vincolo $m$, e $Q_m$, il quantitativo minimo da raggiungere per soddisfare il vincolo. Sia $m_i(q)$ la funzione che definisce il contributo dell'articolo $i$ al vincolo MOQ $m$ quando vengono acquistate $q$ unità. Il vincolo $m$ è soddisfatto se: $$\forall i \in I_m, q_i = 0 \text{ o } \sum_{i \in I_m}m_i(q_i) \geq Q_m$$ Con questa funzione, quindi, tutti i vincoli MOQ possono essere soddisfatti in due modi: raggiungendo la soglia MOQ, oppure portando a zero la quantità di tutti gli articoli.

Poi, sia $C$ il costo massimo che può essere sostenuto per l'ordine di acquisto. Definiamo il miglior ordine di acquisto $\textbf{q}_C=(q_i)_i$ come: $$\textbf{q}_C = \underset{q}{\operatorname{argmax}} \left\{ \sum_i r_i(q_i) \text{ con $m$ soddisfatto } \forall m\in M \right\}$$ L'ordine di acquisto è il "migliore" nel senso che massimizza il rendimento di un certo budget. La soluzione $\textbf{q}_C$ non è l'unica possibile (il problema MOQ è troppo arduo per una soluzione esatta, comunque), ma, per maggiore semplicità, procederemo come se fosse l'unica.

Infine, sia $T$ un obiettivo minimo. Definiamo $\textbf{q}^T$ con $$C^T = \underset{C}{\operatorname{min}} \left\{ \left(\sum_{q_i \in \textbf{q}_C} t_i(q_i) \right) \geq T \right\}$$ e $$\mathbf{q}^T = \textbf{q}_{C^T}$$ La soluzione $\mathbf{q}^T$ è costruita a partire da $\textbf{q}_C$, che rappresenta la soluzione ottimale minima (dal punto di vista del budget di spesa) che massimizzi il ritorno economico e allo stesso tempo permetta di raggiungere l'obiettivo.

La funzione moqsolv in Envision

Envision offre un solutore non lineare specifico per il problema MOQ. Il solutore è disponibile attraverso la funzione moqsolv e contribuisce a semplificare enormemente i calcoli relativi ai vincoli MOQ. La funzione moqsolv serve a elaborare una serie di vettori associati a una tabella di tipo Id(*), ottenuta di solito estendendo un vettore di distribuzioni attraverso extend.distrib() (vedi anche come funzionano le distribuzioni).

La funzione si aspetta gli argomenti:

moqsolv(Id, Min, Reward, Cost, Target, threshold, g0, oq0, moq0, g1, oq1, moq1, ...)

dove:

  • Id è la colonna con i gruppi di articoli;
  • Min è la colonna che ordina le unità, simile a Grid.Min;
  • Reward (rendimento) è il rendimento per la riga, ottenuto con la funzione di rendimento delle scorte stockrwd;
  • Cost (costo) è il costo per la riga, di solito PurchasePrice * Grid.Q;
  • Target (obiettivo) è una quantità da raggiungere, che può essere pari a Cost se vogliamo coprire il budget nel modo più semplice. Impostando Target == Cost, chiediamo di ottimizzare il budget (mentre con Target != Cost chiediamo un'ottimizzazione del target);
  • threshold (soglia) è un valore scalare che funge da soglia per il target. Quando ottimizziamo il budget, il valore target è un limite superiore al costo associato alla soluzione. Quando ottimizziamo il target, il valore target è un limite inferiore al costo associato alla soluzione. Se il valore soglia è negativo, allora il segno negativo è interpretato come una variabile booleana per considerare il target (positivo) dal basso, invece che dall'alto. Il segno della threshold è usato solo per controllare la direzione dell'ineguaglianza, mentre il valore è sempre interpretato come positivo;
  • g0, oq0, moq0 rappresenta una tripletta di vettori associati a ogni vincolo MOQ (vedi più avanti per i dettagli).

Vedi anche Ordini per priorità con vincoli di approvvigionamento, un tutorial che illustra come usare la funzione moqsolv per creare un ordine di acquisto che soddisfi i vincoli MOQ.

La funzione restituisce un vettore bool in cui tutte le righe che possono concorrere all'obiettivo sono indicate come vere (true). La combinazione di tutte le righe true soddisfa i vincoli MOQ. Il solutore approssima la soluzione $\textbf{q}^T$ definita nella sezione precedente. Come si evince dalla definizione introdotta nella sezione precedente, gli argomenti Cost, Target e threshold dovranno essere positivi.

Ogni vincolo MOQ è definito da tre vettori:

  • G, i gruppi di articoli che soddisfano i vincoli MOQ, che può essere uguale a Id in caso il MOQ sia per unità.
  • OQ, il contributo della riga al quantitativo d'ordine (Order Quantity) previsto dal vincolo MOQ.
  • MOQ, la soglia da raggiungere per soddisfare il vincolo MOQ.

I gruppi definiti da G devono definire una partizione degli identificativi degli articoli. La soglia MOQ dovrà essere identica per tutte le righe associate allo stesso valore G. Tutti i valori OQ dovranno essere positivi, senza eccezioni. Anche i valori MOQ dovrebbero essere positivi; potrebbero, però, essere pari a zero per riflettere l'assenza di un vincolo MOQ.

I vincoli MOQ multipli possono essere specificati includendo più triplette. Envision supporta al momento fino a 4 vincoli MOQ contemporaneamente.