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