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