При разработке отчетов и обработок в среде 1С программисты часто сталкиваются с ограничением языка запросов: если в запросе используется секция СГРУППИРОВАТЬ ПО, то все выбираемые поля должны быть либо включены в эту секцию, либо обернуты в агрегатную функцию (такую как СУММА, МАКСИМУМ или МИНИМУМ). Но что делать, если нам нужно сгруппировать данные по контрагенту, сохранив при этом ссылки на конкретные заказы или склеив комментарии в одну строку? В этой статье мы подробно разберем, как решить эту задачу на уровне программного кода и визуального представления.
Рассмотрим стандартную ситуацию: нам необходимо получить список контрагентов с итоговыми суммами их заказов, но при этом мы хотим видеть номера этих заказов или их примечания. Если мы просто добавим поле ЗаказПокупателя.Ссылка в запрос с группировкой по контрагенту, платформа выдаст ошибку. Это логично, так как СУБД не знает, какой именно заказ показать для одной строки контрагента, если их несколько.
Существует несколько стратегий решения этой задачи: программная обработка результата запроса, использование итогов в самом запросе или применение возможностей системы компоновки данных (СКД). Остановимся на самых гибких вариантах.
Этот метод наиболее универсален, когда результат нужно вывести не просто в отчет, а в табличную часть на форме или сложный макет. Суть заключается в том, чтобы получить из базы детальные данные (без группировки в запросе), а затем обработать их в коде 1С.
Сначала подготовим запрос, который выберет все необходимые поля:
Запрос = Новый Запрос;
Запрос.Текст =
"ВЫБРАТЬ
| ЗаказПокупателя.Контрагент КАК Контрагент,
| ЗаказПокупателя.Ссылка КАК Ссылка,
| ЗаказПокупателя.СуммаДокумента КАК СуммаДокумента,
| ЗаказПокупателя.Комментарий КАК Комментарий
|ИЗ
| Документ.ЗаказПокупателя КАК ЗаказПокупателя
|ГДЕ
| ЗаказПокупателя.Проведен = ИСТИНА";
ТаблицаРезультата = Запрос.Выполнить().Выгрузить();
Теперь проанализируем, как нам свернуть эту таблицу по контрагенту, сохранив детали. Мы создадим вспомогательную таблицу с уникальными контрагентами и пройдем по ней циклом, отбирая детальные записи.
// Создаем уникальный список контрагентов
ТаблицаКонтрагентов = ТаблицаРезультата.Скопировать(, "Контрагент");
ТаблицаКонтрагентов.Свернуть("Контрагент");
Для Каждого СтрокаКонтрагента Из ТаблицаКонтрагентов Цикл
// Находим все строки детальных записей для текущего контрагента
ПараметрыОтбора = Новый Структура("Контрагент", СтрокаКонтрагента.Контрагент);
ДетальныеСтроки = ТаблицаРезультата.НайтиСтроки(ПараметрыОтбора);
// Здесь мы можем объединять данные, например, склеивать комментарии
ОбщийКомментарий = "";
Для Каждого Стр Из ДетальныеСтроки Цикл
ОбщийКомментарий = ОбщийКомментарий + ?(ПустаяСтрока(ОбщийКомментарий), "", "; ") + Стр.Комментарий;
КонецЦикла;
// Выводим результат в целевую таблицу или макет
// СтрокаТЧ = ТЧМаршруты.Добавить();
// СтрокаТЧ.Контрагент = СтрокаКонтрагента.Контрагент;
// СтрокаТЧ.Примечание = ОбщийКомментарий;
КонецЦикла;
Обратим внимание на метод Скопировать(). Чтобы получить только одну колонку, первый параметр (фильтр) нужно оставить пустым, передав только имя колонки вторым параметром: ТаблицаРезультата.Скопировать(, "ИмяКолонки"). Ошибка в передаче параметров — частая причина сбоев при работе с коллекциями.
Если задача стоит в формировании печатной формы, где контрагент должен занимать одну большую ячейку, а заказы — несколько мелких строк рядом, мы воспользуемся методом Объединить() объекта ТабличныйДокумент. Разберем по шагам, как это реализовать.
ТабДок = Новый ТабличныйДокумент;
Макет = ПолучитьМакет("МакетОтчета");
ОбластьСтрока = Макет.ПолучитьОбласть("Строка");
НомерПервойСтроки = 0; // Инициализируем счетчик
Для Каждого СтрокаКонтрагента Из ТаблицаКонтрагентов Цикл
ДетальныеСтроки = ТаблицаРезультата.НайтиСтроки(Новый Структура("Контрагент", СтрокаКонтрагента.Контрагент));
// Запоминаем, где начинается блок строк текущего контрагента
НомерПервойСтроки = ТабДок.ВысотаТаблицы + 1;
Для Каждого СтрРезультата Из ДетальныеСтроки Цикл
ОбластьСтрока.Параметры.Заполнить(СтрРезультата);
ТабДок.Вывести(ОбластьСтрока);
КонецЦикла;
// Номер последней строки блока
НомерПоследнейСтроки = ТабДок.ВысотаТаблицы;
// Объединяем ячейки в первой колонке (где Контрагент)
Если НомерПоследнейСтроки >= НомерПервойСтроки Тогда
ТабДок.Область(НомерПервойСтроки, 1, НомерПоследнейСтроки, 1).Объединить();
ТабДок.Область(НомерПервойСтроки, 1, НомерПоследнейСтроки, 1).ВертикальноеПоложение = ВертикальноеПоложение.Верх;
КонецЕсли;
КонецЦикла;
Использование свойства ВысотаТаблицы позволяет динамически определять координаты ячеек без ручного ведения счетчиков строк. Это делает код более надежным при изменении структуры макета.
Проанализируем более современный подход. Если ваш отчет строится на Системе компоновки данных, вам не нужно писать код для склеивания строк. В СКД есть мощный механизм ресурсов. Для поля "Комментарий" можно указать следующее выражение на вкладке "Ресурсы":
СоединитьСтроки(ВычислитьВыражениеТаблицуЗначений("Комментарий"), ", ")
Это позволит системе автоматически собрать все текстовые значения в одну строку через запятую при группировке по контрагенту. Это наиболее производительный и правильный способ с точки зрения архитектуры платформы 8.3.
В процессе работы с данными (например, при импорте контрагентов или создании новых объектов в коде) важно правильно назначать коды справочников. Если вы создаете объект программно, код может остаться пустым, что нарушит автонумерацию. Рассмотрим, как правильно использовать метод УстановитьНовыйКод():
НовыйОбъект = Справочники.Контрагенты.СоздатьЭлемент();
// Заполнение реквизитов...
НовыйОбъект.Наименование = "Новый клиент";
// Правильный вызов метода:
НовыйОбъект.УстановитьНовыйКод();
// Теперь можно записывать
НовыйОбъект.Записать();
Важно помнить, что УстановитьНовыйКод() — это метод объекта, а не процедура глобального контекста. Он должен вызываться именно у того экземпляра, который вы собираетесь записать в базу данных.
Подведем итоги: для обхода ограничений группировки мы либо обрабатываем результат запроса программно, используя поиск в ТаблицеЗначений, либо используем методы визуального объединения в ТабличномДокументе. Для сложных отчетов лучшим выбором будет использование функций агрегации строк в СКД.