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