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

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

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

Принцип работы метода Свернуть()

Для объединения одинаковых строк и суммирования числовых показателей в платформе 1С предназначен метод Свернуть() объекта ТаблицаЗначений. Синтаксис метода принимает два строковых параметра:

ТаблицаЗначений.Свернуть("КолонкиГруппировки", "КолонкиСуммирования");

Проанализируем базовые правила работы данного метода:

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

Типичные ошибки при группировке табличной части

Рассмотрим частые ошибки, возникающие при попытке свернуть данные напрямую из выборки или реквизитов документа.

1. Ошибка «Объект недоступен для изменения». Если обращение к строкам документа идет через ссылку (например, Выборка.Ссылка.Товары или через выборку данных), то табличная часть получена в режиме «только для чтения». Метод Свернуть() модифицирует саму таблицу значений «на месте», поэтому применить его к неизменяемой коллекции невозможно.

2. Ошибка «Метод Скопировать не обнаружен». Объект типа ТабличнаяЧасть не содержит метода Скопировать(). Чтобы превратить строки табличной части в независимую редактируемую таблицу значений, используется метод Выгрузить().

3. Искажение цены при группировке. Если включить поле Цена в список группировки, то одна и та же номенклатура, проданная по разным ценам, разделится на разные строки. Если же указать Цена в списке суммирования, платформа просто сложит цены (например, 10 + 20 = 30), что является грубой расчетной ошибкой. Правильное решение — исключить цену из параметров метода Свернуть(), а затем пересчитать среднюю цену делением свернутой суммы на свернутое количество.

4. Перезапись параметров макета методом Заполнить(). Вызов ОбластьСтрока.Параметры.Заполнить(...) выполняет автоматическую подстановку одноименных свойств. Если вызвать его после ручного присвоения параметров, ранее рассчитанные значения будут перезаписаны исходными данными выборки.

Решение 1: Выгрузка в ТаблицуЗначений и использование метода Свернуть()

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


// Получаем строки табличной части в виде отдельной ТаблицыЗначений
ТаблицаТоваров = Выборка.Товары.Выгрузить();

// Сворачиваем по номенклатуре, суммируя количество и сумму.
// Все прочие колонки (включая исходную цену и номер строки) будут удалены.
ТаблицаТоваров.Свернуть("Номенклатура", "Количество, Сумма");

НомерСтроки = 0;
СуммаВсего = 0;

Для Каждого СтрокаТЧ Из ТаблицаТоваров Цикл
    НомерСтроки = НомерСтроки + 1;
    
    ОбластьСтрока.Параметры.НомерСтроки = НомерСтроки;
    ОбластьСтрока.Параметры.Товар = СтрокаТЧ.Номенклатура;
    ОбластьСтрока.Параметры.Количество = СтрокаТЧ.Количество;
    ОбластьСтрока.Параметры.Сумма = СтрокаТЧ.Сумма;
    
    // Рассчитываем актуальную средневзвешенную цену
    Если СтрокаТЧ.Количество <> 0 Тогда
        ОбластьСтрока.Параметры.Цена = СтрокаТЧ.Сумма / СтрокаТЧ.Количество;
    Иначе
        ОбластьСтрока.Параметры.Цена = 0;
    КонецЕсли;
    
    СуммаВсего = СуммаВсего + СтрокаТЧ.Сумма;
    
    ТабДок.Вывести(ОбластьСтрока);
КонецЦикла;

Решение 2: Группировка через коллекцию «Соответствие» (работает на клиенте)

Посмотрим на альтернативный способ, который особенно полезен при работе на тонком клиенте, где объект ТаблицаЗначений недоступен платформой. Используем объект Соответствие, в котором ключом выступает номенклатура, а значением — структура с накопленными показателями.


СводныеДанные = Новый Соответствие;

// Обходим исходные строки табличной части документа
Для Каждого СтрокаИсходная Из Выборка.Товары Цикл
    НоменклатураТекущая = СтрокаИсходная.Номенклатура;
    
    ДанныеСтроки = СводныеДанные[НоменклатураТекущая];
    Если ДанныеСтроки = Неопределено Тогда
        // Создаем новую запись показателей
        ДанныеСтроки = Новый Структура("Количество, Сумма", 
            СтрокаИсходная.Количество, 
            СтрокаИсходная.Сумма);
        СводныеДанные.Вставить(НоменклатураТекущая, ДанныеСтроки);
    Иначе
        // Накапливаем показатели для существующей номенклатуры
        ДанныеСтроки.Количество = ДанныеСтроки.Количество + СтрокаИсходная.Количество;
        ДанныеСтроки.Сумма = ДанныеСтроки.Сумма + СтрокаИсходная.Сумма;
    КонецЕсли;
КонецЦикла;

// Выводим сгруппированные строки в табличный документ
НомерСтроки = 0;
Для Каждого КлючИЗначение Из СводныеДанные Цикл
    НомерСтроки = НомерСтроки + 1;
    
    НоменклатураЭлемент = КлючИЗначение.Ключ;
    Показатели = КлючИЗначение.Значение;
    
    ОбластьСтрока.Параметры.НомерСтроки = НомерСтроки;
    ОбластьСтрока.Параметры.Товар = НоменклатураЭлемент;
    ОбластьСтрока.Параметры.Количество = Показатели.Количество;
    ОбластьСтрока.Параметры.Сумма = Показатели.Сумма;
    
    Если Показатели.Количество <> 0 Тогда
        ОбластьСтрока.Параметры.Цена = Показатели.Сумма / Показатели.Количество;
    Иначе
        ОбластьСтрока.Параметры.Цена = 0;
    КонецЕсли;
    
    ТабДок.Вывести(ОбластьСтрока);
КонецЦикла;

Таким образом, для получения сгруппированного отчета без запросов достаточно корректно выгрузить табличную часть методом Выгрузить() и применить Свернуть(), либо организовать программное аккумулирование с помощью Соответствия.

← На главную