Surrika

Матрица ABC-XYZ анализа ассортимента по выручке

ABC-XYZ анализ ассортимента по выручке совмещает две оси и дает девять групп. Первая буква показывает вклад позиции в выручку, вторая стабильность продаж по месяцам. Матрица нужна для правил закупки по каждой группе.

Продажи и ассортимент

Как считать

  1. Берут продажи за последние 12 месяцев и выручку по каждому артикулу считают за вычетом возвратов.
  2. Артикулы сортируют по убыванию выручки и делят на A, B и C по накопленной доле 80% и 95%.
  3. Для каждой позиции считают выручку по месяцам, среднее и коэффициент вариации.
  4. По коэффициенту вариации позиция получает X при 10% и меньше, Y до 25%, Z выше.
  5. Обе буквы считают на одном периоде, затем совмещают и считают число артикулов и долю выручки в каждой группе.

Расчет на выгрузках магазина

Пример посчитан на выгрузках вымышленного мебельного магазина: продажи сайта и трех маркетплейсов, реклама, сделки CRM и остатки за 2024-2025 годы.

Как разложить ассортимент по выручке и стабильности продаж, чтобы получить матрицу ABC-XYZ и правила закупки для каждой группы?

Матрица ABC-XYZ по выручке

Выручка, ₽ Артикулов Группа Доля выручки
0 ₽ 0 AX 0 %
194 877 150 ₽ 7 AY 56,4 %
64 798 471 ₽ 3 AZ 18,8 %
31 394 853 ₽ 2 BX 9,1 %
18 950 373 ₽ 1 BY 5,5 %
16 297 960 ₽ 1 BZ 4,7 %
11 337 180 ₽ 1 CX 3,3 %
0 ₽ 0 CY 0 %
7 998 966 ₽ 2 CZ 2,3 %

SQL, по которому посчитано

WITH bounds AS (
  SELECT
    date_trunc('month', max(date))::DATE AS last_month,
    (date_trunc('month', max(date))::DATE - INTERVAL 11 MONTH)::DATE AS first_month
  FROM sales
),
months AS (
  SELECT unnest(
    generate_series(first_month::TIMESTAMP, last_month::TIMESTAMP, INTERVAL 1 MONTH)
  )::DATE AS month
  FROM bounds
),
monthly_sales AS (
  SELECT
    date_trunc('month', date)::DATE AS month,
    sku,
    any_value(product) AS product,
    any_value(category) AS category,
    sum(revenue - returned_revenue) AS revenue
  FROM sales
  CROSS JOIN bounds
  WHERE date >= first_month
    AND date < (last_month + INTERVAL 1 MONTH)::DATE
  GROUP BY 1, 2
),
skus AS (
  SELECT
    sku,
    any_value(product) AS product,
    any_value(category) AS category
  FROM monthly_sales
  GROUP BY sku
),
monthly_full AS (
  SELECT
    s.sku,
    s.product,
    s.category,
    m.month,
    coalesce(ms.revenue, 0) AS revenue
  FROM skus s
  CROSS JOIN months m
  LEFT JOIN monthly_sales ms
    ON ms.sku = s.sku
   AND ms.month = m.month
),
sku_stats AS (
  SELECT
    sku,
    any_value(product) AS product,
    any_value(category) AS category,
    sum(revenue) AS total_revenue,
    avg(revenue) AS avg_monthly_revenue,
    stddev_samp(revenue) AS std_monthly_revenue,
    CASE
      WHEN avg(revenue) = 0 THEN NULL
      ELSE stddev_samp(revenue) / avg(revenue)
    END AS cv
  FROM monthly_full
  GROUP BY sku
  HAVING sum(revenue) > 0
),
abc_ranked AS (
  SELECT
    *,
    sum(total_revenue) OVER (
      ORDER BY total_revenue DESC, sku
      ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW
    ) AS cumulative_revenue,
    sum(total_revenue) OVER () AS all_revenue
  FROM sku_stats
),
abc_class AS (
  SELECT
    *,
    CASE
      WHEN cumulative_revenue / all_revenue <= 0.8 THEN 'A'
      WHEN cumulative_revenue / all_revenue <= 0.95 THEN 'B'
      ELSE 'C'
    END AS abc,
    CASE
      WHEN cv IS NULL THEN 'Z'
      WHEN cv <= 0.10 THEN 'X'
      WHEN cv <= 0.25 THEN 'Y'
      ELSE 'Z'
    END AS xyz
  FROM abc_ranked
),
matrix AS (
  SELECT
    g.abc,
    g.xyz,
    g.abc || g.xyz AS group_code,
    count(a.sku) AS sku_count,
    coalesce(sum(a.total_revenue), 0) AS revenue
  FROM (
    SELECT abc, xyz
    FROM (VALUES ('A'), ('B'), ('C')) AS a(abc)
    CROSS JOIN (VALUES ('X'), ('Y'), ('Z')) AS x(xyz)
  ) g
  LEFT JOIN abc_class a
    ON a.abc = g.abc
   AND a.xyz = g.xyz
  GROUP BY g.abc, g.xyz
)
SELECT
  group_code,
  sku_count,
  round(revenue, 0) AS revenue,
  round(revenue / NULLIF((SELECT sum(total_revenue) FROM abc_class), 0), 4) AS revenue_share
FROM matrix
ORDER BY group_code

Где взять выручку и месячные продажи для ABC-XYZ

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

Почему ABC по выручке не совпадает с ABC по штукам

Одна и та же позиция может попасть в A по выручке и в C по количеству, если у нее высокая цена и мало продаж. Для матрицы по выручке опирайтесь на деньги. Штуки дадут другую базу и другое правило закупки. Проверьте несколько крупных чеков: дорогая позиция может дать большую долю выручки при небольшом числе сделок. Если считаете запас по штукам, постройте отдельную матрицу и не смешивайте ее с денежной.

Как читать матрицу ABC-XYZ по выручке

Группа A дает основную выручку, группа X держит ровные продажи по месяцам. Группа B приносит заметную долю, но уступает A по накопленному вкладу. Z означает резкие колебания спроса, даже если средняя выручка высокая. Группа C имеет малую долю в общей выручке. Смотрите на число артикулов в группе: одна позиция в A и десять в C требуют разных усилий.

Что делать с группами после расчета ABC-XYZ

Для AX держите страховой запас и следите за остатком на складе. Для BY планируйте закупку по среднему спросу и пересматривайте матрицу при смене сезона. Для CZ сокращайте заказ, переводите позицию под заказ или выводите из ассортимента. По AY и AZ проверяйте акции и сезонность: высокой выручке нужен запас под пик. Решение принимайте по деньгам в группе и по доле возвратов. Одной буквы для этого мало.

Частые ошибки

Месяцы без продаж выпадают из расчета XYZ

Если оставить только месяцы с продажами, колебания занижаются и нестабильный товар попадает в X. Включайте в месячный ряд нули, чтобы коэффициент вариации отражал провалы.

Неполный месяц в расчете

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

Артикулы и категории в одной матрице

Если в одной матрице лежат и категории, и артикулы, выручка считается дважды и границы A, B, C съезжают. Выберите один уровень анализа и стройте матрицу на нем.

Вопросы

Можно ли строить ABC-XYZ по марже?

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

Как учитывать новые товары без истории за год?

Считайте их отдельно от основной матрицы. В общий расчет добавляйте после сопоставимого периода продаж, иначе группа будет случайной.

Стоит ли строить матрицу отдельно по каналам?

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

Посчитайте это на своих выгрузках

Бросьте выгрузки на доску Surrika и задайте вопрос обычными словами. Ответ придет графиком, а под ним будет SQL, по которому он посчитан.

14 дней бесплатно, карта не нужна.

По теме

Все задачи