Задача выделения ключевых клиентов, приносящих основную долю выручки (так называемый ABC-анализ), является классической для управленческого учета. В 1С существует несколько способов решения этой задачи: от классических запросов на временных таблицах до современных оконных функций и встроенных механизмов Системы компоновки данных (СКД). Разберем по шагам, как реализовать этот алгоритм максимально эффективно.
Если ваша задача решается в рамках отчета на СКД, это самый быстрый и надежный путь. Система компоновки данных имеет встроенную функцию КлассификацияABC, которая берет на себя всю математическую сложность расчета долей и нарастающего итога.
Разберем, как настроить эту функцию в схеме компоновки данных. Обычно она используется в вычисляемых полях или непосредственно в выражениях ресурсов. Функция принимает три основных параметра:
СуммаПродаж)."80, 15, 5". Это означает, что в группу 1 (A) попадут те, кто обеспечивает первые 80% продаж.Пример выражения в СКД:
КлассификацияABC(СуммаОборот, 3, "80, 15, 5")
После этого в настройках отчета достаточно наложить отбор: ВычисляемоеПоле1 Равно 1. Это автоматически отсечет всех контрагентов, кроме тех, кто входит в целевые 80%.
Если вы работаете на современной версии платформы, использование оконных функций — это наиболее элегантный способ получить результат одним запросом без сложных соединений. Мы можем рассчитать накопительный итог прямо "на лету".
Проанализируем пример такого запроса. Сначала мы получим общую сумму продаж, а затем рассчитаем нарастающий итог для каждого контрагента, упорядочив их по убыванию суммы.
ВЫБРАТЬ
Продажи.Контрагент КАК Контрагент,
СУММА(Продажи.СуммаОборот) КАК Сумма
ПОМЕСТИТЬ ВТ_Продажи
ИЗ
РегистрНакопления.Продажи.Обороты(&НачалоПериода, &КонецПериода) КАК Продажи
СГРУППИРОВАТЬ ПО
Продажи.Контрагент
;
////////////////////////////////////////////////////////////////////////////////
ВЫБРАТЬ
Т.Контрагент,
Т.Сумма,
СУММА(Т.Сумма) НАД(УПОРЯДОЧИТЬ ПО Т.Сумма УБЫВ) КАК НарастающийИтог,
СУММА(Т.Сумма) НАД() КАК ОбщаяСумма
ПОМЕСТИТЬ ВТ_Расчет
ИЗ
ВТ_Продажи КАК Т
;
////////////////////////////////////////////////////////////////////////////////
ВЫБРАТЬ
Т.Контрагент,
Т.Сумма
ИЗ
ВТ_Расчет КАК Т
ГДЕ
(Т.НарастающийИтог - Т.Сумма) / Т.ОбщаяСумма < 0.8
Разберем логику условия в ГДЕ. Мы используем (НарастающийИтог - Сумма), чтобы включить в выборку того самого "пограничного" контрагента, на котором порог в 80% еще не был достигнут, но после которого он будет превышен. Это стандартная практика бизнес-анализа: если пятый клиент доводит общую сумму с 75% до 82%, он все равно считается важным (входит в группу A).
Если версия платформы не поддерживает оконные функции, придется использовать метод соединения таблицы самой с собой. Проанализируем этот алгоритм, так как он часто встречается в старых конфигурациях и на экзаменах "1С:Специалист".
Посмотрим на шаги реализации:
ВТ_Данные.ВТ_Данные (Т1) с ВТ_Данные (Т2) по условию Т1.Сумма <= Т2.Сумма.Т1.Контрагент и суммируем Т2.Сумма. Это и будет наш нарастающий итог.Рассмотрим пример кода:
ВЫБРАТЬ
Продажи.Контрагент КАК Контрагент,
СУММА(Продажи.СуммаОборот) КАК Сумма
ПОМЕСТИТЬ ВТ_Продажи
ИЗ
РегистрНакопления.Продажи.Обороты КАК Продажи
СГРУППИРОВАТЬ ПО
Продажи.Контрагент
;
////////////////////////////////////////////////////////////////////////////////
ВЫБРАТЬ
Т1.Контрагент КАК Контрагент,
Т1.Сумма КАК Сумма,
СУММА(Т2.Сумма) КАК НарастающийИтог
ПОМЕСТИТЬ ВТ_Итоги
ИЗ
ВТ_Продажи КАК Т1
ВНУТРЕННЕЕ СОЕДИНЕНИЕ ВТ_Продажи КАК Т2
ПО Т1.Сумма < Т2.Сумма
ИЛИ (Т1.Сумма = Т2.Сумма И Т1.Контрагент <= Т2.Контрагент)
СГРУППИРОВАТЬ ПО
Т1.Контрагент,
Т1.Сумма
;
Обратите внимание на дополнительное условие в соединении: Т1.Контрагент <= Т2.Контрагент. Оно критически важно, если у двух разных контрагентов одинаковые суммы продаж. Без этого условия нарастающий итог для них будет рассчитан некорректно (он "прыгнет" сразу на сумму обоих).
Важно понимать, что метод с соединением таблицы самой с собой (Способ 3) имеет квадратичную сложность O(n^2). Если у вас 10 000 контрагентов, запрос может "завесить" систему, так как количество строк при соединении может достичь 100 миллионов.
В таких случаях мы рекомендуем комбинированный подход:
Контрагент - Сумма, отсортированный по убыванию.ТаблицуЗначений.Пример кода на встроенном языке:
ОбщаяСумма = ТаблицаПродаж.Итог("Сумма");
Накоплено = 0;
Порог = ОбщаяСумма * 0.8;
Индекс = 0;
Пока Индекс < ТаблицаПродаж.Количество() Цикл
Строка = ТаблицаПродаж[Индекс];
Если Накоплено >= Порог Тогда
// Все последующие уже не входят в 80%
// (логика включения пограничного элемента зависит от ТЗ)
ТаблицаПродаж.Удалить(Строка);
Иначе
Накоплено = Накоплено + Строка.Сумма;
Индекс = Индекс + 1;
КонецЕсли;
КонецЦикла;
Выясним причину, по которой отчеты разных программистов могут показывать разные данные при одном и том же условии "80%". Все дело в пограничном контрагенте.
Рассмотрим ситуацию:
Должен ли Контрагент В попасть в отчет? Если мы используем строгое условие НарастающийИтог <= 80%, то он не попадет. Однако по методологии ABC-анализа его обычно включают в группу A, так как он начал вносить свой вклад до того, как порог был достигнут. Именно поэтому в запросах мы рекомендуем использовать условие на "предыдущий" нарастающий итог (добавление текущей строки еще не должно превышать 80%).
Для решения задачи выбора 80% продаж в 1С: