- The Quantitative Supply Chain Manifesto
- The Lokad test of supply chain performance
- Overview of quantitative supply chain
- Generalized probabilistic forecasting
- Decision-driven optimization
- Economic drivers
- Data preparation
- The Supply Chain Scientist
- Timeline of a typical project
- Assessing success
- Antipatterns in supply chain

- Inventory forecasting
- Prioritized ordering report
- Old forecasting input file format
- Old forecasting output file format
- Choosing the service levels
- Managing your inventory settings
- The old Excel forecast report
- Using tags to improve accuracy
- Oddities in classic forecasts
- Oddities in quantile forecasts
- Stock-out's bias on quantile forecasts
- Daily, weekly and monthly aggregations

Home » Resources » Here

Price breaks or volume discounts, represent situations where the margin unit price of goods is varying depending on the quantity considered for the purchase. Usually, unit prices are decreasing when purchase quantities increase in order to give the client an incentive to buy more. When suppliers offer price breaks, there is an economic incentive in having purchase quantities correspondingly adjusted to take advantage of those price breaks. Envision offers extensive support for supplier price breaks. In this section, we detail how to model price breaks from the purchasing optimization perspective.

`Id`

the identifier of the product`MinQuantity`

the minimal quantity that needs to be reached to be eligible to the unit price`Price`

the purchase unit price of the product

In order to keep the table readable, when looking at a given product, the lines are typically sorted by quantities in increasing order. This ordering helps making sense of the magnitude of the discounts.

This representation is prone to

Let's consider the product A sold at 1€ per unit. The product A has a price break at 50 units where the price drops to 0.9€ per unit. Purchasing 46 units cost 46€ while purchasing 50 units cost only 45€. Thus, there is no economic incentive to buy any quantities within the range 46-49 units, as it is cheaper to buy 50 units instead. The marginal unit price of the 50th unit is -4€.

Those negative marginal prices are the consequence of the underlying data model adopted for the price breaks. More elaborate price break models - capable of eliminating those negative marginal prices - exist, however those models are beyond the scope of the present section.

In the following, we assume that the price break data can be made available through a table as detailed above.

`pricebrk()`

function in Envision is to transform a tabular price break data into a distribution that represents the marginal purchase cost of units. The syntax is:
// 'Prices' is the price break table expect Prices[Id, *] B = pricebrk(Demand, Price, Prices.MinQuantity, Prices.Price, Stock, StockPrice)The function returns the distribution of the marginal purchase unit price, that is, the price to be paid to purchase the k

The arguments are:

`Demand`

is a distribution used to pick a*support*- in the mathematical sense - for the distribution to be returned. The values of this distribution are not used, but the returned distribution is adjusted to be least as precise as`Demand`

.`Price`

is a number, interpreted as default unit price of the product. This value is used if there is no price break for a minimum quantity of 1; either because the price break starts at a value greater than 1, or because there is no price break at all for the product.`Prices.MinQuantity`

is the smallest quantity for which the price break line applies. It must be an integer greater or equal to 1. Duplicates are not allowed.`Prices.Price`

is the unit price for this price break line. It applies to all purchased units, not just the ones exceeding`Prices.MinQuantity`

. It must be a decreasing function of`Prices.MinQuantity`

.`Stock`

is the available stock for the product. Non-zero values mean that fewer units need to be purchased to reach a given stock level, which means that price breaks are harder to reach. It must be an non-negative integer. This argument is optional. When this argument is omitted,`StockPrice`

should be omitted as well. When this argument is omitted the default value is zero.`StockPrice`

is the unit price for units already in stock. This argument is optional. When it is omitted, the default value is zero.

The

`Price`

argument is merely a syntactic sugar intended to deal with situations where the price break table only covers the products that actually benefit from a price break. Through this argument, we avoid the need to extend the table `Prices`

to have at least one line per product.`pricebrk()`

because distributions in Envision are not arbitrarily precise. Indeed, there are practical limits to the resolution of a distribution within Envision. Yet, the price breaks obtained from a given supplier can range from a 1 unit purchase to a (theoretical) 10 million unit purchase. The `Demand`

distribution is used by the `pricebrk()`

function in order to adjust the resolution of the returned distribution to the range of interest.Indeed, the one pitfall that Envision prevents is an inventory optimization logic that end's up suggesting to purchase 999 units while the target price break is at 1000 units. Such a situation could happen if the distribution generated by Envision does not internally differentiate the values at 999 units and 1000 units. By passing a distribution to the

`pricebrk()`

function, Envision ensures that this specific scenario is avoided by adapting the resolution of the returned distribution.`pricebrk()`

function translates the price break representation from the original ordering space toward the stocking space.This translation is the reason why

`pricebrk()`

takes two arguments associated to the stock: the stock level and the stock unit price. Those two arguments are used to shift the marginal price distribution to the right by `>>`

on distributions, but once again, this could trigger situations where minor approximations collide with price break thresholds. The `pricebrk()`

function internalizes this shift in order to eliminate those approximations.`pricebrk()`

represent the marginal unit purchase cost. The segment [1;Stock] is associated to current stocks and associated to `StockPrice`

. Then, if `B`

is the distribution returned by `pricebrk()`

, then the integral `int(B, Stock + 1, Stock + N)`

is the total cost of purchasing As pointed out above, price break tables are frequently associated with negative marginal unit costs - i.e. situations where purchasing one more unit comes with a negative cost. The distributions returned by

`pricebrk()`

reflect those situations through local negative values. Those negative values are consistent and the direct consequence of the price break data model.B = pricebrk(Demand, BuyPrice, Prices.MinQuantity, Prices.Price, Stock, StockPrice) // 'M', 'S' and 'C' are distributions M = SellPrice - B S = -0.5 * (SellPrice - B) C = -0.3 * B * mean(Leadtime) / 365 AM = 0.3 AC = 1 - 0.2 * mean(Leadtime) / 365 // point-wise multiplication for 'RM', 'RS' and 'RC' RM = stockrwd.m(Demand, AM) * M RS = stockrwd.s(Demand) * S RC = stockrwd.c(Demand, AC) * C R = RM + RS + RCThe primary difference between this block of code and the original one, when we introduced the stock reward function, is

`BuyPrice`

. The column `BuyPrice`

is turned into a price break distribution `B`

at line 1 through the function `pricebrk()`

. Then, the rest of script boils down to a directly application of the algebra of distribution which is doing all the complicated work for us.At lines 4-6, the economic variables are turned into distributions. Indeed, when a constant is added to a distribution, the result of the addition is a distribution. The same goes for subtraction. Above

`M`

is the margin reward `M`

is expected to be increasing.At lines 12-14, the components of the stock reward functions are associated with the economic