Как в запросе 1С выбрать контрагентов, которые обеспечивают 80% продаж?

Программист 1С v8.3 (Управляемые формы) Управленческий учет Торговля и дистрибуция
← На главную

Задача выделения ключевых клиентов, приносящих основную долю выручки (так называемый ABC-анализ), является классической для управленческого учета. В 1С существует несколько способов решения этой задачи: от классических запросов на временных таблицах до современных оконных функций и встроенных механизмов Системы компоновки данных (СКД). Разберем по шагам, как реализовать этот алгоритм максимально эффективно.

Способ 1. Использование функции КлассификацияABC в СКД

Если ваша задача решается в рамках отчета на СКД, это самый быстрый и надежный путь. Система компоновки данных имеет встроенную функцию КлассификацияABC, которая берет на себя всю математическую сложность расчета долей и нарастающего итога.

Разберем, как настроить эту функцию в схеме компоновки данных. Обычно она используется в вычисляемых полях или непосредственно в выражениях ресурсов. Функция принимает три основных параметра:

  1. Значение — это показатель, по которому мы проводим анализ (в нашем случае — СуммаПродаж).
  2. Количество групп — обычно указывается 3 (для групп A, B и C).
  3. Процентные пороги — для нашей задачи мы укажем "80, 15, 5". Это означает, что в группу 1 (A) попадут те, кто обеспечивает первые 80% продаж.

Пример выражения в СКД:

КлассификацияABC(СуммаОборот, 3, "80, 15, 5")

После этого в настройках отчета достаточно наложить отбор: ВычисляемоеПоле1 Равно 1. Это автоматически отсечет всех контрагентов, кроме тех, кто входит в целевые 80%.

Способ 2. Оконные функции (Платформа 8.3.20 и выше)

Если вы работаете на современной версии платформы, использование оконных функций — это наиболее элегантный способ получить результат одним запросом без сложных соединений. Мы можем рассчитать накопительный итог прямо "на лету".

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


ВЫБРАТЬ
    Продажи.Контрагент КАК Контрагент,
    СУММА(Продажи.СуммаОборот) КАК Сумма
ПОМЕСТИТЬ ВТ_Продажи
ИЗ
    РегистрНакопления.Продажи.Обороты(&НачалоПериода, &КонецПериода) КАК Продажи
СГРУППИРОВАТЬ ПО
    Продажи.Контрагент
;

////////////////////////////////////////////////////////////////////////////////
ВЫБРАТЬ
    Т.Контрагент,
    Т.Сумма,
    СУММА(Т.Сумма) НАД(УПОРЯДОЧИТЬ ПО Т.Сумма УБЫВ) КАК НарастающийИтог,
    СУММА(Т.Сумма) НАД() КАК ОбщаяСумма
ПОМЕСТИТЬ ВТ_Расчет
ИЗ
    ВТ_Продажи КАК Т
;

////////////////////////////////////////////////////////////////////////////////
ВЫБРАТЬ
    Т.Контрагент,
    Т.Сумма
ИЗ
    ВТ_Расчет КАК Т
ГДЕ
    (Т.НарастающийИтог - Т.Сумма) / Т.ОбщаяСумма < 0.8

Разберем логику условия в ГДЕ. Мы используем (НарастающийИтог - Сумма), чтобы включить в выборку того самого "пограничного" контрагента, на котором порог в 80% еще не был достигнут, но после которого он будет превышен. Это стандартная практика бизнес-анализа: если пятый клиент доводит общую сумму с 75% до 82%, он все равно считается важным (входит в группу A).

Способ 3. Классический запрос с нарастающим итогом (универсальный метод)

Если версия платформы не поддерживает оконные функции, придется использовать метод соединения таблицы самой с собой. Проанализируем этот алгоритм, так как он часто встречается в старых конфигурациях и на экзаменах "1С:Специалист".

Посмотрим на шаги реализации:

  1. Подготовка данных: Собираем продажи по контрагентам во временную таблицу ВТ_Данные.
  2. Расчет итогов: Соединяем ВТ_Данные (Т1) с ВТ_Данные (Т2) по условию Т1.Сумма <= Т2.Сумма.
  3. Группировка: Группируем по Т1.Контрагент и суммируем Т2.Сумма. Это и будет наш нарастающий итог.

Рассмотрим пример кода:


ВЫБРАТЬ
    Продажи.Контрагент КАК Контрагент,
    СУММА(Продажи.СуммаОборот) КАК Сумма
ПОМЕСТИТЬ ВТ_Продажи
ИЗ
    РегистрНакопления.Продажи.Обороты КАК Продажи
СГРУППИРОВАТЬ ПО
    Продажи.Контрагент
;

////////////////////////////////////////////////////////////////////////////////
ВЫБРАТЬ
    Т1.Контрагент КАК Контрагент,
    Т1.Сумма КАК Сумма,
    СУММА(Т2.Сумма) КАК НарастающийИтог
ПОМЕСТИТЬ ВТ_Итоги
ИЗ
    ВТ_Продажи КАК Т1
        ВНУТРЕННЕЕ СОЕДИНЕНИЕ ВТ_Продажи КАК Т2
        ПО Т1.Сумма < Т2.Сумма 
            ИЛИ (Т1.Сумма = Т2.Сумма И Т1.Контрагент <= Т2.Контрагент)
СГРУППИРОВАТЬ ПО
    Т1.Контрагент,
    Т1.Сумма
;

Обратите внимание на дополнительное условие в соединении: Т1.Контрагент <= Т2.Контрагент. Оно критически важно, если у двух разных контрагентов одинаковые суммы продаж. Без этого условия нарастающий итог для них будет рассчитан некорректно (он "прыгнет" сразу на сумму обоих).

Оптимизация производительности на больших объемах

Важно понимать, что метод с соединением таблицы самой с собой (Способ 3) имеет квадратичную сложность O(n^2). Если у вас 10 000 контрагентов, запрос может "завесить" систему, так как количество строк при соединении может достичь 100 миллионов.

В таких случаях мы рекомендуем комбинированный подход:

  1. Выполните простой запрос для получения списка Контрагент - Сумма, отсортированный по убыванию.
  2. Выгрузите результат в ТаблицуЗначений.
  3. В цикле по таблице значений рассчитайте общую сумму и накопительный итог.
  4. Удалите строки, которые не входят в искомые 80%.

Пример кода на встроенном языке:


ОбщаяСумма = ТаблицаПродаж.Итог("Сумма");
Накоплено = 0;
Порог = ОбщаяСумма * 0.8;

Индекс = 0;
Пока Индекс < ТаблицаПродаж.Количество() Цикл
    Строка = ТаблицаПродаж[Индекс];
    Если Накоплено >= Порог Тогда
        // Все последующие уже не входят в 80%
        // (логика включения пограничного элемента зависит от ТЗ)
        ТаблицаПродаж.Удалить(Строка);
    Иначе
        Накоплено = Накоплено + Строка.Сумма;
        Индекс = Индекс + 1;
    КонецЕсли;
КонецЦикла;

Проблема границы в 80%

Выясним причину, по которой отчеты разных программистов могут показывать разные данные при одном и том же условии "80%". Все дело в пограничном контрагенте.

Рассмотрим ситуацию:

Должен ли Контрагент В попасть в отчет? Если мы используем строгое условие НарастающийИтог <= 80%, то он не попадет. Однако по методологии ABC-анализа его обычно включают в группу A, так как он начал вносить свой вклад до того, как порог был достигнут. Именно поэтому в запросах мы рекомендуем использовать условие на "предыдущий" нарастающий итог (добавление текущей строки еще не должно превышать 80%).

Резюме

Для решения задачи выбора 80% продаж в 1С:

← На главную