Solving the general MOQ problem (Minimal Order Quantities)

Solving the general MOQ problem (Minimal Order Quantities)

Home » Knowledgebase » Here
By Joannès Vermorel, January 2016

MOQ (Minimal Order Quantities) are a ubiquitous form of ordering constraint in supply chain. An MOQ constraint indicates that a supplier won't accept a purchase order below a specified threshold typically expressed in units or in dollars. Frequently, multiple MOQ constraints coexist and must be satisfied together. The general MOQ problem consists of computing the (near) optimal purchase orders that both satisfy all the MOQ constraints while maximizing the economic returns associated to the units purchased.

The general MOQ problem is formalized as a hard non-linear optimization problem. This problem is qualified as hard because it can be proven that computing the optimal solution is typically out of computational reach. However, while optimal solutions can't typically be obtained, near-optimal solutions can be obtained through advanced non-linear constraint-solving algorithms. In particular, we present below moqsolv, the advanced numeric solver provided by Lokad to address the general MOQ problem.

Usual MOQ constraints

The MOQ constraints can take many forms. Among the most frequent MOQ constraints encountered in real supply chains, we have:

  • a minimal quantity expressed in units per SKU, which typically reflects items that are too cheap to be sold individually.
  • a minimal quantity expressed in dollars for the purchase order as a whole, which is frequently encountered when the supplier does not charge for the delivery.
  • a minimal quantity expressed in units per category of items, which is frequently found for products that are made to order with minimal size on production batches.

Dealing with one constraint at a time is usually reasonably straightforward in practice. However, as soon as multiple MOQ constraints need to be taken into account together at the same time, composing a purchase order that satisfies all those constraints becomes a lot harder.

MOQ concepts

Before delving into the numerical optimization problem, let's introduce the most salient concepts relevant for the general MOQ problem. We have:

  • the items which represent what can actually be purchased. Item quantities are frequently integral numbers; although there is no restriction here for them to be.
  • the ordered quantities for every item (possibly zero) which represent a potential solution to the MOQ problem.
  • the rewards associated to each extra unit for each item - basically what is obtained through the stockrwd function (stock reward), although using this function is not a requirement.
  • the costs associated to the units to be acquired. The goal is indeed to maximize the reward for a given spending budget expressed in costs. Costs are typically expected to be flat per unit, but here we don't make assumptions; thus, price breaks may be taken into account.
  • the targets which represent a way of specifying a stopping criteria which may not be the actual costs. This one is quite subtle, and covered in greater detail below.

The canonical stopping criteria while purchasing according to a purchase priority list is to define a maximal budget: units are purchased in the decreasing ROI order until the whole purchasing budget is spent. However, a budget capping says little about absolute inventory performance that can be obtained. Thus, while the goal remains to optimize ROI, no matter which stopping criterion is considered it is still highly practical to be able to consider alternative criteria such as a target overall fill rate for example.

The concept of the target is introduced as a generic mechanism to define alternative stopping criteria while working with a prioritized ordering policy. Simply put, the goal becomes to find the purchase orders that deliver the highest ROI for the smallest investment while still meeting the target goal. Below, we give a more precise definition of this optimization process.

Ex: Frank the supply chain manager puts a target at 90% fill rate. The solving the MOQ problem consists of computing the smallest order - in costs - while maximizing the rewards that delivers a 90% fill rate. This order is NOT the smallest order possible to achieve the 90% fill rate - as this would be a pure fill-rate prioritization. Instead, it's the smallest order that, while prioritizing the rewards, is large enough to deliver a 90% fill rate. A pure fill-rate prioritization would have been a mistake because, unlike the stock reward, it does not take into account the cost associated to the generation of dead stock.

Formal definition of the general MOQ problem

This section introduces the general MOQ problem as a formal non-linear optimization problem. It's relatively straightforward to show that this problem is NP-hard. Indeed, the general MOQ problem extends the bin packing problem, which is also NP-hard. Thus, the general MOQ problem is at least as difficult as the bin-packing problem. Although, while the problem is NP-hard, it must be noted that very good solutions can be computed in practice.

Let $I$ be the set of items being considered for ordering. Let $q_i$ with $i \in I$ the quantity to be ordered for the item $i$.

Then, we define a series of functions.

  • Let $r_i(q)$ be the reward when holding $q$ units of the item $i$.
  • Let $c_i(q)$ be the cost when buying $q$ units of the item $i$.
  • Let $t_i(q)$ be the target when holding $q$ units of the item $i$.

The reward function can return positive or negative values, however both the cost and target functions are strictly positive: $$\forall i, \forall q, c_i(q) > 0 \text{ and } t_i(q) >0$$ Let $M$ be the set of MOQ constraints. For each $m \in M$, we have $I_m$ the list of items that belongs to the constraint $m$ and $Q_m$ the minimal quantity that should be reached to satisfy the constraint. Let $m_i(q)$ the function that defines the contribution of the item $i$ to the MOQ constraint $m$ when $q$ units are purchased. The constraint $m$ is said to be satisfied if: $$\forall i \in I_m, q_i = 0 \text{ or } \sum_{i \in I_m}m_i(q_i) \geq Q_m$$ Thus, all MOQ constraints can be satisfied in two ways: either by reaching the MOQ threshold, or by having all item's quantities at zero.

Then, let $C$ be the maximal cost that can be afforded for the purchase order. We define $\textbf{q}_C=(q_i)_i$ the best purchase order as: $$\textbf{q}_C = \underset{q}{\operatorname{argmax}} \left\{ \sum_i r_i(q_i) \text{ with $m$ satisfied } \forall m\in M \right\}$$ The purchase order is the "best" in the sense that is maximizes the reward for a given budget. The solution $\textbf{q}_C$ is not unique, however, this consideration is rather theoretical because the MOQ problem is too hard for an exact resolution anyway. For the sake of simplicity, we proceed as if the solution was unique in the following.

Finally, let $T$ be a target minimum, we define $\textbf{q}^T$ with $$C^T = \underset{C}{\operatorname{min}} \left\{ \left(\sum_{q_i \in \textbf{q}_C} t_i(q_i) \right) \geq T \right\}$$ and $$\mathbf{q}^T = \textbf{q}_{C^T}$$ The solution $\mathbf{q}^T$ is built on top of $\textbf{q}_C$, that is, it's the smallest optimal (budget-wise) ROI-maximizing solution that is good enough to fulfill the target.

The moqsolv function in Envision

Envision provides a non-linear solver dedicated to the general MOQ problem. This solver can be accessed through the function moqsolv and removes the need for messy numerical recipes while trying to cope with MOQ constraints. The function moqsolv is intended to process a series of vectors associated to a table of type Id(*) typically obtained by extending a vector of distributions through extend.distrib() (see also Creating Tables).

The function expects the following arguments:

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


  • Id the item grouping column
  • Min the unit ordering column - as in Grid.Min
  • Reward the reward for the line - as obtained through stockrwd, the stock reward function
  • Cost the cost for the line - typically PurchasePrice * Grid.Q
  • Target a target quantity - which can be equal to Cost for a simple budget capping. By setting Target == Cost, one is telling the solver to do a budget optimization (resp. Target != Cost a target optimization).
  • threshold a scalar value acting as the threshold for the target. In case of a budget optimization, the target value is a upper bound on the cost associated to the solution. In case of a target optimization, the target value is a lower bound on the target associated to the solution. If the threshold value is negative, then the negative sign is interpreted as a Boolean flag to approach the (positive) target from below, instead of approaching the target from above. The sign of the threshold is only used to control the direction of the inequality, the threshold value is always interpreted as positive.
  • g0, oq0, moq0 represents a triplet of vectors associated to each MOQ constraint (detailed in the following).

See also Prioritized ordering with ordering constraints- a tutorial that illustrates how the moqsolv function can be used to compose a purchase order that satisfies MOQ constraints.

The function returns a bool vector where all lines eligible within the target are flagged as true. The combination of all the lines at true satisfies the MOQ constraints. The solver approximates the solution $\textbf{q}^T$ as defined in the previous section. As expected from the definition introduced in the previous section, the arguments Cost, Target and threshold are expected to be strictly positive.

Each MOQ constraint is defined by a three vectors:

  • G the MOQ grouping, which can be equal to Id in case of per-item MOQ
  • OQ the contribution of the line to the order quantity of the MOQ constraint.
  • MOQ the threshold that should be reached to satisfy the MOQ constraint.

The groups defined by G should define a partition of the item identifiers. The MOQ threshold is expected to be identical across all lines associated to the same G value. All OQ contribution values are expected to be strictly positive. The MOQ values should be positive, but can be zero to reflect the lack of actual MOQ constraint.

Multiple MOQs can be specified by including multiple triplets. Envision supports up to 4 concurrent MOQ constraints.