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

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

При построении аналитических отчетов часто возникает задача выявить ключевых контрагентов, формирующих основной объем выручки (классический ABC-анализ или правило Парето 80/20). Рассмотрим, как организовать подобный выбор в схеме компоновки данных (СКД), если процент продаж должен регулироваться пользователем.

Особенности задачи и технические ограничения СКД

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

  1. Ограничение на фильтрацию ресурсов: Выражения ресурсов СКД вычисляются на этапе формирования группировок. Наложить стандартный пользовательский отбор или отбор группировки непосредственно на вычисленное значение ресурса средствами настройки отчета нельзя.
  2. Зависимость от порядка сортировки: Вычисление нарастающего итога с помощью функций языка выражений СКД критически зависит от текущей сортировки. Строки должны быть отсортированы строго по убыванию показателя продаж, иначе расчет доли нарушится.
  3. Производительность: Соединения таблиц «сами с собой» в запросе или тяжелые вычисляемые выражения на больших объемах данных могут приводить к задержкам при формировании отчета.

Способ 1. Использование функции «ВычислитьВыражение» в ресурсах СКД

Рассмотри вариант расчета кумулятивной суммы с помощью функций языка выражений СКД. На вкладке Ресурсы для показателя продаж мы можем вычислить накопительный итог по группировке Контрагент.

Выражение для расчета нарастающего итога выглядит следующим образом:

ВычислитьВыражение("Сумма(СуммаПродаж)", "Контрагент", "Первая", "Текущая")

Разберем параметры данной функции:

  1. "Сумма(СуммаПродаж)" — вычисляемое агрегатное выражение.
  2. "Контрагент" — имя группировки, в рамках которой рассчитывается итог.
  3. "Первая" и "Текущая" — границы интервала расчета (от первой записи до текущей), что позволяет получить накопительную сумму.

Кроме того, в СКД есть специальная функция для классификации данных — КлассификацияABC:

КлассификацияABC(Сумма(СуммаПродаж), 3, "80, 15, 5")

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

Способ 2. Расчет нарастающего итога на уровне запроса

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

Алгоритм построения запроса состоит из следующих шагов:

  1. Во временной таблице вычисляется общая сумма продаж за указанный период.
  2. Во второй временной таблице формируются агрегированные данные по каждому контрагенту.
  3. С помощью связи таблицы с самой собой (по условию «сумма продаж больше или равна») рассчитывается нарастающий итог.
  4. В секцию ГДЕ подставляется условие ограничения по кумулятивному проценту.

Способ 3. Программный выбор контрагентов через событие «ПриКомпоновкеРезультата»

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

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


Процедура ПриКомпоновкеРезультата(ИзвестныеНастройки, СтандартнаяОбработка)
    
    СтандартнаяОбработка = Ложь;
    
    // 1. Получаем настройки отчета и значение процента
    Настройки = КомпоновщикНастроек.ПолучитьНастройки();
    ПараметрПроцента = Настройки.ПараметрыДанных.НайтиЗначениеПараметра(Новый ПараметрКомпоновкиДанных("ЦелевойПроцент"));
    
    Если ПараметрПроцента <> Неопределено И ЗначениеЗаполнено(ПараметрПроцента.Значение) Тогда
        ПроцентОтбора = ПараметрПроцента.Значение;
    Иначе
        ПроцентОтбора = 80;
    КонецЕсли;
    
    // 2. Рассчитываем список контрагентов, входящих в заданный процент продаж
    Запрос = Новый Запрос;
    Запрос.Текст = 
        "ВЫБРАТЬ
        |   Продажи.Контрагент КАК Контрагент,
        |   СУММА(Продажи.СуммаОборот) КАК Сумма
        |ПОМЕСТИТЬ ВТ_Продажи
        |ИЗ
        |   РегистрНакопления.Продажи.Обороты(&НачалоПериода, &КонецПериода, Авто, ) КАК Продажи
        |СГРУППИРОВАТЬ ПО
        |   Продажи.Контрагент
        |;
        |
        |ВЫБРАТЬ
        |   Т1.Контрагент КАК Контрагент,
        |   Т1.Сумма КАК Сумма,
        |   СУММА(Т2.Сумма) КАК НарастающийИтог
        |ПОМЕСТИТЬ ВТ_Итоги
        |ИЗ
        |   ВТ_Продажи КАК Т1
        |   ВНУТРЕННЕЕ СОЕДИНЕНИЕ ВТ_Продажи КАК Т2
        |       ПО Т1.Сумма <= Т2.Сумма
        |СГРУППИРОВАТЬ ПО
        |   Т1.Контрагент,
        |   Т1.Сумма
        |;
        |
        |ВЫБРАТЬ
        |   ВТ_Итоги.Контрагент КАК Контрагент
        |ИЗ
        |   ВТ_Итоги КАК ВТ_Итоги,
        |   (ВЫБРАТЬ СУММА(ВТ_Продажи.Сумма) КАК ВсегоПродаж ИЗ ВТ_Продажи КАК ВТ_Продажи) КАК Всего
        |ГДЕ
        |   (ВТ_Итоги.НарастающийИтог / Всего.ВсегоПродаж) * 100 <= &ПроцентОтбора";
        
    Запрос.УстановитьПараметр("НачалоПериода", Настройки.ПараметрыДанных.НайтиЗначениеПараметра(Новый ПараметрКомпоновкиДанных("НачалоПериода")).Значение);
    Запрос.УстановитьПараметр("КонецПериода", Настройки.ПараметрыДанных.НайтиЗначениеПараметра(Новый ПараметрКомпоновкиДанных("КонецПериода")).Значение);
    Запрос.УстановитьПараметр("ПроцентОтбора", ПроцентОтбора);
    
    МассивКонтрагентов = Запрос.Выполнить().Выгрузить().ВыгрузитьКолонку("Контрагент");
    
    // 3. Передаем массив контрагентов в отбор компоновщика
    ЭлементОтбора = Настройки.Отбор.Элементы.Добавить(Тип("ЭлементОтбораКомпоновкиДанных"));
    ЭлементОтбора.ЛевоеЗначение = Новый ПолеКомпоновкиДанных("Контрагент");
    ЭлементОтбора.ВидСравнения = ВидСравненияКомпоновкиДанных.ВСписке;
    ЭлементОтбора.ПравоеЗначение = МассивКонтрагентов;
    ЭлементОтбора.Использование = Истина;
    
    // 4. Компонуем результат отчета
    КомпоновщикМакета = Новый КомпоновщикМакетаКомпоновкиДанных;
    МакетКомпоновки = КомпоновщикМакета.Выполнить(СхемаКомпоновкиДанных, Настройки);
    
    ПроцессорКомпоновки = Новый ПроцессорКомпоновкиДанных;
    ПроцессорКомпоновки.Инициализировать(МакетКомпоновки);
    
    ПроцессорВывода = Новый ПроцессорВыводаРезультатаКомпоновкиДанныхВТабличныйДокумент;
    ПроцессорВывода.УстановитьДокумент(Результат);
    ПроцессорВывода.Вывести(ПроцессорКомпоновки);

КонецПроцедуры

Способ 4. Использование регистра сведений (типовой подход)

В типовых конфигурациях 1С используется подход с предварительным фоновым расчетом:

  1. Регламентное задание периодически выполняет ABC-классификацию контрагентов и записывает результаты в регистр сведений.
  2. В отчете СКД выполняются простые соединения с этим регистром.
  3. Пользователь накладывает стандартный отбор по классу или проценту, что обеспечивает максимальную скорость работы отчета.
← На главную