Скидки от поставщиков в Envision

Скидки от поставщиков












Главная » Ресурсы » Здесь

Акции или оптовые скидки — это ситуации, когда предельная цена на товар изменяется в зависимости от приобретаемого объема. Обычно цена на товар снижается при увеличении объема заказа. Это делается, чтобы побудить клиента купить больше. Когда поставщики предлагают скидки, есть смысл воспользоваться ими и приобрести товар в соответствующем количестве. Envision предлагает широкую поддержку для скидок от поставщиков. В данном разделе мы рассказываем, как моделировать скидки с точки зрения оптимизации закупки товаров.


Представление скидок в таблице

Чаще всего скидки на некоторый список товаров представляются в таблице с тремя колонками:

  • Id код товара
  • MinQuantity минимальное количество товара, необходимое для применения цены со скидкой
  • Price цена закупки товара

Чтобы таблицу было проще читать, строки обычно сортируются по количеству в возрастающем порядке. Такой порядок позволяет разобраться с величиной скидок.

Такое представление может привести к отрицательной предельной цене на товар'’ — при покупке дополнительной единицы товара общая цена может снизиться.

Возьмем товар A, который стоит 1 евро за штуку. Скидка на товар А начинается при объеме заказа от 50 шт. — тогда цена опускается до 0,9 евро за штуку. 46 единиц товара будет стоить 46 евро, а 50 — 45 евро. Таким образом, с экономической точки зрения бессмысленно приобретать 46–49 единиц товара, так как дешевле купить 50 штук. Таким образом, предельная цена на 50-ю единицу товара составляет -4 евро.

Негативная предельная цена появляется из-за модели данных, которая используется для скидок. Существуют и более сложные модели, которые могут убирать негативную предельную цену, однако мы не будем рассматривать их в данном разделе.

Далее мы будем исходить из предположения, что данные о скидках представляются в таблице, описанной выше.

Функция pricebrk()

Цель функции pricebrk() в Envision — трансформация табличных данных о скидках в распределение, которое отражает предельную цену на товар. Синтаксис:
expect Prices[Id, *] // «Prices» — таблица с данными о скидках
B = pricebrk(Demand, Price, Prices.MinQuantity, Prices.Price, Stock, StockPrice)
Функция возвращает распределение предельной цены на единицу товара, то есть цену на покупку k-ой единицы товары. Данная функция достаточно сложна для понимания. Мы подробно опишем эту функцию и поясним сложные моменты ниже.

Аргументы:
  • Demand — распределение для для выбора носителя (математический термин) — это нужно для возврата распределения. Значения данного распределения не используются, но возвращенное распределение рассчитывается так, чтобы оно было не менее точным, чем Demand.
  • Price — число, цена на единицу товара по умолчанию. Данное значение используется, если скидка не применяется к минимальному количеству (1); либо потому что скидки начинаются со значения большего 1, либо потому что на данный товар вообще не распространяются скидки.
  • Prices.MinQuantity — минимальное количество товара, к которому применяются скидки. Это должно быть целое число больше или равное 1. Дубликаты не допускаются.
  • Prices.Price — цена за одну единицу с учетом скидки. Применяется ко всем приобретаемым единицам товара, не только к тем, которые выходят за рамки Prices.MinQuantity. Это должна быть убывающая функция Prices.MinQuantity.
  • Stock — запас товара в наличии. Ненулевые значения показывают, что для достижения указанного уровня запасов нужно приобрести меньшее количество единиц товара, а значит скидку будет получить сложнее. Значение должно быть неотрицательным целым числом. Данный аргумент является дополнительным. Если данный аргумент опущен, нужно также опустить StockPrice. Когда данный аргумент опущен, значение по умолчанию устанавливается на ноль.
  • StockPrice — цена за единицу товара для товаров, которые уже есть в наличии. Данный аргумент является дополнительным. Если он опущен, значение по умолчанию равно нулю.

Аргумент Price — это просто синтаксический прием, позволяющий работать с ситуациями, когда таблица с данными о скидках содержит только данные о товарах, на которые распространяются скидки. Благодаря данному аргументу не требуется разложение таблицы Prices до тех пор, пока каждому товару не будет соответствовать не менее одной строки.

Резольвента распределения

В качестве первого аргумента pricebrk() должно выступать распределение, потому что распределения в Envision недостаточно точны. Существуют практические ограничения резольвенты распределения в Envision. Поставщик может предоставлять скидки с 1 единицы товара до (теоретически) 10 миллионов единиц товара. Распределение Demand используется функцией pricebrk() для подгонки резольвенты возвращенного распределения до нужного диапазона.

Envision позволяет избежать ошибки алгоритма оптимизации запасов, вследствие которой система может предложить купить 999 единиц товара, тогда как скидки начинаются с 1000 единиц. Такая ситуация может возникнуть, если распределение, созданное Envision, не различает значения 999 шт. и 1000 шт. Проводя распределение по функции pricebrk(), Envision подстраивает резольвенту возвращенного распределения во избежание данной ситуации.

Заказ и хранение товаров

Таблица с данными о скидках организована с точки зрения
заказа товаров, и цена закупки единицы товара в ней привязывается к каждому значению объема заказа. В то же самое время оптимизация запасов ориентирована на хранение товаров: при увеличении запаса на +1 единицу мы учитываем доступные запасы, то есть пространство хранения. Функция pricebrk() переводит данные о скидках из пространства заказа в пространство хранения.

Именно благодаря такому преобразованию функция pricebrk() имеет два аргумента, связанных с запасами: уровень запасов и расходы на хранение единицы товара. Эти два аргумента используются для перемещения распределения предельной цены вправо от единиц товара
в запасах. Перемещение можно выполнить с помощью обычного оператора >> при распределении, однако это может привести к ситуациям, когда до получения скидки может не хватить очень небольшого количества единиц товара. Функция pricebrk() поддерживает такое перемещение и исключает возникновение подобных ситуаций.

Предельная стоимость товаров

Распределение, возвращаемое функцией pricebrk(), отражает предельную цену на закупку единицы товара. Сегмент [1;Stock] связан с текущим уровнем запаса и с аргументом StockPrice. Если B — это распределение, возвращенное функцией pricebrk(), то интеграл int(B, Stock + 1, Stock + N) — общая стоимость закупки
N единиц товара, помимо единиц, которые уже есть в наличии.

Как говорилось выше, таблицы с данными о ценах часто связаны с отрицательными предельными ценами на единицы товаров — то есть с ситуациями, когда цена на закупку дополнительной единицы товара уходит в минус. Распределения, возвращаемые функцией pricebrk(), отражают такие ситуация с помощью локальных отрицательных значений. Такие отрицательные значения являются правильными, и они являются следствием использования соответствующей модели данных.

Сочетание скидок и прибыльности запасов

Функция прибыльности запасов позволяет рассчитать распределение предельной экономической прибыли от каждой дополнительной единицы товара в запасах. В предыдущем разделе мы рассмотрели, как данную функцию можно связать с экономическими переменными, отражающими валовую прибыль, убытки из-за дефицита товара и расходы на хранение. Ранее эти экономические переменные были просто числами. Однако при работе со скидками эти переменные варьируются вместе с объемом заказа. Проще всего такие вариации отобразить с помощью распределения.
B = pricebrk(Demand, BuyPrice, Prices.MinQuantity, Prices.Price, Stock, StockPrice)

M = SellPrice - B // 'M’ — распределение
S = -0.5 * (SellPrice - B) // 'S' — распределение
C = -0.3 * B * mean(Leadtime) / 365 // 'C' — распределение
AM = 0.3
AC = 1 - 0.2 * mean(Leadtime) / 365

RM = stockrwd.m(Demand, AM) * M // точечное умножение
RS = stockrwd.s(Demand) * S // то же самое
RC = stockrwd.c(Demand, AC) * C // то же самое
R = RM + RS + RC 
По существу, разница между этим блоком и исходным сценарием, где мы ввели функцию прибыльности запасов, заключается в BuyPrice. СтолбецBuyPrice преобразуется в распределение данных по скидкам B в строке 1 с помощью функции pricebrk(). Далее в сценарии просто применяется алгебра распределения, которая выполняет за нас всю сложную работу.

В строках 3–5 экономические переменные преобразуются в распределение. Когда к распределению прибавляется константа, в результате получается распределение. То же самое справедливо и для вычитания. M выше — предельная прибыль
от каждой единицы товара (предельная прибыль). Вследствие того, что при скидках цена за единицу товара понижается при увеличении объема заказа, значения распределения Mдолжны возрастать.

В строках 9–11 компоненты функции прибыльности запасов привязываются к экономическому
распределению'' (а не к числам), однако синтаксис остается тем же. По сути, между распределениями происходит точечное умножение. Наконец, в 12-ой строке рассчитывается окончательная прибыльность запасов, как мы уже делали ранее.