Распределение товара в условиях дефицита и низкого спроса

(история одной разработки)

  Посвящается Татьяне: вдохновителю, строгому критику
и гениальному тестировщику :)))
  
(Статья написана для сайта "Автоматизация магазина: сложное сделать удобным")

Постановка задачи

Когда товар в избытке на центральном складе, а в магазинах его разбирают как горячие пирожки - довольно просто собрать статистику и спрогнозировать дальнейшую динамику спроса в каждом магазине. А вы попробуйте проделать то же самое, когда у вас 50 магазинов, в каждом товар покупается в среднем по 1-2 шт в неделю, а на центральном складе запас всего в 10 единиц. Кому выделить товар в первую очередь?

Итак, имеем на входе:

  • Центральный склад (ЦС), на который поступают товары (прямых поставок на магазины нет)
    • Магазины в том же городе, что и центральный склад (на них товар распределяется прямо с ЦС)
    • Несколько регионов, в каждом из которых определен "главный" магазин
      • Магазины внутри региона (товар сначала поставляется на "главный" магазин региона, а потом развозится по остальным магазинам)
  • Ассортимент товаров
    • Товар простой (штучный), должен анализироваться по-артикульно
    • Товар составной "Бренд" (компьютеры), собираемый на ЦС из простого товара и поставляемый в таком виде в магазины. Данный товар может немного различаться по конфигурации (т.е. это товары с разными артикулами), но имеет в составе общий признак - "Код модели", при расчете потребности необходимо опираться на "Код модели", который впоследствии должен быть преобразован в набор артикулов, имеющихся в данный момент на складах
    • Товар составной "Самосбор", собираемый в магазине из простого товара. Данный товар необходимо учитывать в статистике как продажу входящих в него простых товаров, как будто их продали по отдельности
    • Некоторые из простых товаров (например, CD-диски) могли поставляться только в упаковках по N штук
  • Наличие товаров на складах (текущее)
  • Поставки, ожидаемые в ближайшие дни - их необходимо было учитывать при расчетах (количество дней задавалось в настройках)
Требовалось разработать программу для магазина (центрального офиса), автоматически формирующую поставки с центрального склада на магазины, а также заявки на закупку товара у поставщика.

Решение

Довольно долго мы изучали текущую схему работы отдела товароведения, пытаясь понять алгоритм, по которому человек распределяет товар по магазинам, и автоматизировать его. Решение никак не складывалось: программа то переоценивала спрос для конкретного магазина, то недооценивала его. Все встало на место, когда кто-то из товароведов обронил гениальную фразу: "Товар надо дать тому, кто быстрее его продаст". Это, казалось бы, очевидное правило, сотрудники выполняли интуитивно, уменьшая или увеличивая количество товара, рассчитанное в Excel в поставке, но никак не могли описать в виде формулы.

Начальные действия

Первым шагом на пути создания программы для распределения было формирование дерева Групп распределения.

Весь ассортимент товаров был разделен на группы, приблизительно одинаковые с точки зрения распределения. Если для товара почему-либо невозможно было спрогнозировать продажи, то прогноз рассчитывался по группе в целом и делился на количество активных (продававшихся в течение последней недели) товаров в группе. Таким образом решалось сразу две задачи: ввод в систему новых товаров (для которых отсутствовала статистика) и замена товара его аналогами.

(рисунок: Дерево групп распределения)

Норма запаса для группы распределения (НЗГ) устанавливалась как количество дней (по умолчанию 14), в течение которых магазин мог бы торговать данных товаром без поставок с центрального склада. Данный параметр влиял на все товары в группе и дополнительно регулировался специальным множителем для учета динамики спроса в предпраздничные дни.

Вес группы распределения (ВГР) определял приоритет (артикул/группа) при расчете среднего времени продажи для группы. Больше вес – выше приоритет группы. Вес артикула равен (1 – вес группы распределения). По умолчанию вес группы равен 0,5, т.е. группа и артикул вносили приблизительно равный вклад в расчет динамики продаж.

Все склады были выстроены в Дерево поставок. Корнем дерева являлся Центральный склад, его подчиненными были объекты, на которые шли непосредственные поставки (центральные магазины и "главные" магазины регионов), далее шли магазины регионов, получающие товар с "главных" магазинов. Большей глубины в нашем случае не было.

(рисунок: Дерево поставок)

Для каждого склада задавался определенный "вес распределения" (по умолчанию =1), корректирующий потребность по всем товарам для данного магазина в сторону повышения или понижения. Вес распределения равный 0 означал запрет распределения всех товаров на данный склад. Так учитывалось расширение, или наоборот, планируемое закрытие магазина.

Важным параметром для склада являлся срок поставки - количество дней доставки (ДД) товара до регионального распределительного склада от времени создания регионом заявки на поставку. 

Блокировки распределения

Поскольку не все магазины торговали полным ассортиментом товаров (например, телефонные карты московского региона продавались только в Москве), то необходимо было запретить распределение таких товаров на остальные магазины. Было введено два вида блокировок: Блокировки группы и Блокировки товара. Суть их одинакова, только в первом случае блокировалась вся группа распределения, а во втором - конкретный товар.

График поставок, поставки под заказ

Существовало еще два фактора, усложнявших задачу:
  • поставки под заказ (по предоплате) - такие поставки должны быть распределены в первую очередь, до запуска основного алгоритма
  • график поставок в магазины по дням недели
График поставок был достаточно сложным. Например, в магазин А товар поставлялся каждый день, в магазины Б и В в понедельник и четверг, в регион Х товар уходил только по средам и т.д. Поэтому необходимо было не просто распределить весь имеющийся запас по "сегодняшним" поставкам, но и оставить что-то "на завтра". Данный график учитывался введением "весов" для дней недели, по умолчанию вес дня задавался в 70%, однако для пятницы (в выходные поставок не было) он устанавливался в 90%. В первом приближении, этот "вес" можно рассматривать как процент распределяемого сегодня товара, однако на самом деле учитывалась реальная потребность магазинов в товарах (см. далее).

(рисунок: График поставок)

Прогнозирование спроса

Информация о продажах поступала в систему из программы учета Storehouse Explorer. Были настроены определенные фильтры импорта. Например, не учитывались продажи со складов уценки, списания на определенных "технических клиентов" и т.д.
Все товары были разделены на три группы в зависимости от продаж за предыдущие четыре недели (названия были предложены руководством):
  • ликвидные (продано более 2 штук)
  • слаболиквидные (продано 1-2 штуки)
  • неликвидные (продаж не было)

Алгоритм расчета потребности был своим для каждой группы.

Типовые склады распределения («Mаячковые» склады) Список складов по продажам, которых возможно прогнозировать продажи по другим складам.

Алгоритм распределения

Все расчеты проводились по-артикульно. Исключения составляли товары "Бренд", для которых расчет велся по "Коду модели", однако сам принцип расчета был тот же.

Поскольку часть товара требуется оставить "на завтра", необходимо было рассчитать, сколько из имеющегося товара мы можем отдать в магазины сегодня. Если обозначить:

  • Sрозн = Есть на складе (штук)
  • Pдня = вес распределения для текущего дня недели в процентах/100
  • Nсегодня = надо сегодня (штук) - потребность магазинов, на которые распределяем сегодня
  • Nзавтра = надо завтра (штук) - потребность всех прочих магазинов (независимо от фактического дня распределения)
  • Доставки = ожидается в ближайшие дни (штук)

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

Sсегодня = Sрозн / [Сумма (Nсегодня )*Pдня + (Сумма (Nзавтра ) - Доставки )* (1–Pдня)] * Сумма (Nсегодня *Pдня)

Замечание: если сумма в […] равна нулю, распределение не требуется.

Если (Сумма (Nзавтра )* (1–Pдня) - Доставки) < 0, то (Сумма (Nзавтра )* (1–Pдня) - Доставки) = 0

Борьба с "пиковыми продажами"

Что такое "пиковая продажа"? Это случайная крупная покупка, вероятность повторения которой очень низка. Например, покупатель приобрел в одном заказе сразу 10 телевизоров для своей новой дачи. Появится ли еще когда-нибудь такой покупатель? Кто знает...
В системе прогнозирования данная покупка приводит к сильной переоценке покупательского спроса. Если ранее продажи товара прогнозировались, например, на уровне 1-2 штук в неделю, то теперь система оценивает спрос в 5-10 единиц товара. Происходит перенасыщение склада и падение оборачиваемости. Бороться с такими пиками можно только нелинейными математическими методами.
На практике, при малом количестве данных, эффективным оказался следующий подход: за длительный период (например, за год) анализируем количество каждого товара в чеках и используем метод квартилей для нахождения "уровня отсечки": сколько штук обычно покупают в одном заказе. Выстраиваем полученные числа по возрастанию и отбрасываем четверть данных с максимальными значениями (можно взять не четверть, а другое количество, например, одну десятую, но существенно на результат это не влияет). Полученную цифру увеличиваем на единицу, чтобы создать некоторый буферный запас.
Допустим, график зависимости количества чеков от количества товара в чеке выглядит следующим образом:
Третий квартиль в нашем примере = 4 штуки товара в одном чеке, т.е. уровень отсечки устанавливаем = 5 штук. Записываем это число в параметры товара, и все продажи, где в чеке больше 5 штук данной номенклатуры, считаем пиковыми. Для сети магазинов компьютерной техники значение "уровня отсечки" оказалось равным 2 для 90% товаров.
Что делать с пиковыми продажами? Хорошие результаты дала замена пиковых продаж значением "уровня отсечки". Таким образом, мы учитываем эти продажи в статистике и не слишком искажаем прогноз.
В рассматриваемой системе уровень отсечки вычислялся не только для каждой номенклатуры, но и для группы номенклатур. В результате все новые номенклатуры на старте получали "уровень отсечки" равный групповому значению.

Алгоритм перераспределения

Интерфейс


Comments