Почему программно сформированный отчет СКД выводится пустым и как это исправить?

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

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

Довольно часто разработчики сталкиваются с ситуацией, когда код написан без видимых ошибок, запросы отрабатывают корректно, но на выходе табличный документ оказывается абсолютно пустым. В этой статье мы подробно разберем, почему возникает такая проблема, исправим ошибку в исходном коде, а также рассмотрим важные аспекты программного вывода СКД, которые сделают ваши отчеты надежными и функциональными.

Анализ исходной проблемы и поиск ошибки

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


СхемаКомпоновки = Новый СхемаКомпоновкиДанных;

Источник = СхемаКомпоновки.ИсточникиДанных.Добавить();
Источник.Имя = "ЛокальнаяБаза";
Источник.СтрокаСоединения = "";
Источник.ТипИсточникаДанных = "Local";

НаборДанных = СхемаКомпоновки.НаборыДанных.Добавить(Тип("НаборДанныхЗапросСхемыКомпоновкиДанных"));
НаборДанных.ИсточникДанных = "ЛокальнаяБаза";
НаборДанных.АвтоЗаполнениеДоступныхПолей = Истина;
НаборДанных.Имя = "ДанныеЗапрос";
НаборДанных.Запрос = "ВЫБРАТЬ
                     |    ПодразделенияОрганизаций.Ссылка КАК Подразделение
                     |ИЗ
                     |    Справочник.ПодразделенияОрганизаций КАК ПодразделенияОрганизаций";

ПолеПодразделение = НаборДанных.Поля.Добавить(Тип("ПолеНабораДанныхСхемыКомпоновкиДанных"));
ПолеПодразделение.Заголовок = "Подразделение";
ПолеПодразделение.ПутьКДанным = "Подразделение";
ПолеПодразделение.Поле = "Подразделение";

Настройки = СхемаКомпоновки.НастройкиПоУмолчанию;

ГруппировкаПодразделение = Настройки.Structure.Добавить(Тип("ГруппировкаКомпоновкиДанных"));
ПолеГруппировки = ГруппировкаПодразделение.ПоляГруппировки.Элементы.Добавить(Тип("ПолеГруппировкиКомпоновкиДанных"));
ПолеГруппировки.Использование = Истина;
ПолеГруппировки.Поле = Новый ПолеКомпоновкиДанных("Подразделение");
ПолеГруппировки.ТипГруппировки = ТипГруппировкиКомпоновкиДанных.Элементы;
    
ВыбранноеПоле = Настройки.Выбор.Элементы.Добавить(Тип("ВыбранноеПолеКомпоновкиДанных"));
ВыбранноеПоле.Заголовок = "Подразделение";
ВыбранноеПоле.Использование = Истина;
ВыбранноеПоле.Поле = Новый ПолеКомпоновкиДанных("Подразделение");

КомпоновщикНастроек = Новый КомпоновщикНастроекКомпоновкиДанных;
КомпоновщикНастроек.Инициализировать(Новый ИсточникДоступныхНастроекКомпоновкиДанных(СхемаКомпоновки));
КомпоновщикНастроек.ЗагрузитьНастройки(Настройки);
    
КомпоновщикМакета = Новый КомпоновщикМакетаКомпоновкиДанных;
Макет = КомпоновщикМакета.Выполнить(СхемаКомпоновки, КомпоновщикНастроек.ПолучитьНастройки());

ПроцессорКомпоновки = Новый ПроцессорКомпоновкиДанных;
ПроцессорКомпоновки.Инициализировать(Макет);

ТабДок = Новый ТабличныйДокумент;
ПроцессорВывода = Новый ПроцессорВыводаРезультатаКомпоновкиДанныхВТабличныйДокумент;
ПроцессорВывода.УстановитьДокумент(TabDoc);
ПроцессорВывода.Вывести(ПроцессорКомпоновки);

На первый взгляд, все объекты созданы логично: схема определена, источник и набор данных добавлены, поля сопоставлены, группировки настроены, а вывод в табличный документ вызывается стандартно. Тем не менее, результатом выполнения этого кода будет пустой лист.

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

По умолчанию у новой программно созданной группировки список выбранных полей пуст. Чтобы группировка наследовала выбранные поля из общих настроек компоновки данных, необходимо добавить специальный системный элемент — автополе.

Решение проблемы: добавление автополя в группировку

Чтобы заставить нашу программную группировку выводить данные, нам нужно обратиться к ее свойству Выбор и добавить туда элемент с типом АвтоВыбранноеПолеКомпоновкиДанных. Давайте посмотрим, как будет выглядеть исправленный участок кода:


// Создаем группировку
ГруппировкаПодразделение = Настройки.Структура.Добавить(Тип("ГруппировкаКомпоновкиДанных"));
ПолеГруппировки = ГруппировкаПодразделение.ПоляГруппировки.Элементы.Добавить(Тип("ПолеГруппировкиКомпоновкиДанных"));
ПолеГруппировки.Использование = Истина;
ПолеГруппировки.Поле = Новый ПолеКомпоновкиДанных("Подразделение");
ПолеГруппировки.ТипГруппировки = ТипГруппировкиКомпоновкиДанных.Элементы;

// КРИТИЧЕСКИ ВАЖНАЯ СТРОКА: Указываем автовыбор полей для группировки
АвтоПоле = ГруппировкаПодразделение.Выбор.Элементы.Добавить(Тип("АвтоВыбранноеПолеКомпоновкиДанных"));
АвтоПоле.Использование = Истина;

После добавления этой строки СКД "поймет", что при выводе группировки Подразделение нужно использовать состав полей, определенный в глобальном выборе отчета (Настройки.Выбор). В результате наш отчет сформируется корректно, и табличный документ заполнится данными из справочника.

Разбор важных нюансов программной работы с СКД

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

1. Реализация механизма расшифровки

В коде, представленном в начале статьи, полностью отсутствует поддержка расшифровки ячеек. Если пользователь сформирует такой отчет, он не сможет дважды кликнуть по подразделению, чтобы открыть карточку справочника или детальный отчет. Стандартная расшифровка при программном формировании "из коробки" не работает.

Для того чтобы включить этот механизм, выполним следующие шаги по шагам:

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

Посмотрим на практическую реализацию этого механизма в коде:


// 1. Создаем объект данных расшифровки
ДанныеРасшифровки = Новый ДанныеРасшифровкиКомпоновкиДанных;

КомпоновщикМакета = Новый КомпоновщикМакетаКомпоновкиДанных;
// 2. Передаем объект третьим параметром в метод Выполнить
Макет = КомпоновщикМакета.Выполнить(СхемаКомпоновки, КомпоновщикНастроек.ПолучитьНастройки(), ДанныеРасшифровки);

ПроцессорКомпоновки = Новый ПроцессорКомпоновкиДанных;
// 3. Передаем данные расшифровки третьим параметром в Инициализировать
ПроцессорКомпоновки.Инициализировать(Макет, , ДанныеРасшифровки);

ТабДок = Новый ТабличныйДокумент;
ПроцессорВывода = Новый ПроцессорВыводаРезультатаКомпоновкиДанныхВТабличныйДокумент;
ПроцессорВывода.УстановитьДокумент(ТабДок);
ПроцессорВывода.Вывести(ПроцессорКомпоновки);

// 4. Помещаем данные во временное хранилище и связываем с формой
АдресДанныхРасшифровки = ПоместитьВоВременноеХранилище(ДанныеРасшифровки, УникальныйИдентификатор);

После выполнения этих действий стандартная расшифровка в табличном документе на форме начнет работать корректно.

2. Решение проблемы с кэшированием настроек

Частая проблема при программном выводе отчетов — когда разработчик вносит изменения в схему компоновки (например, добавляет новое поле в запрос), но пользователи в режиме "1С:Предприятие" продолжают видеть старый вариант отчета. Это происходит из-за агрессивного кэширования настроек платформой.

Чтобы гарантировать применение новой структуры схемы и сбросить пользовательский кэш настроек, мы рекомендуем использовать метод УстановитьСтандартныеНастройки() объекта КомпоновщикНастроекКомпоновкиДанных:


КомпоновщикНастроек = Новый КомпоновщикНастроекКомпоновкиДанных;
КомпоновщикНастроек.Инициализировать(Новый ИсточникДоступныхНастроекКомпоновкиДанных(СхемаКомпоновки));
// Принудительно сбрасываем настройки на настройки по умолчанию из схемы
КомпоновщикНастроек.УстановитьСтандартныеНастройки();

Этот простой вызов убережет вас от сотен вопросов пользователей в духе "почему у меня не появилось новое поле после обновления".

3. Программная установка параметров СКД

Если в схеме компоновки данных или в тексте запроса используются параметры (например, Период или Организация), отчет выдаст ошибку во время выполнения, если эти параметры не будут инициализированы. Устанавливать их значения программно нужно через компоновщик настроек.

Рассмотрим пример правильной установки параметров, учитывая, что они могут быть вынесены в пользовательские настройки:


// Установка значения параметра в основных настройках
ПараметрОрганизация = КомпоновщикНастроек.Настройки.ПараметрыДанных.Элементы.Найти("Организация");
Если ПараметрОрганизация <> Неопределено Тогда
    ПараметрОрганизация.Значение = ВыбраннаяОрганизация;
    ПараметрОрганизация.Использование = Истина;
КонецЕсли;

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

4. Вывод внешних источников данных (Таблиц значений)

СКД часто используется для удобного и красивого вывода произвольных таблиц значений, которые были рассчитаны сложным алгоритмом в коде. В этом случае мы используем набор данных с типом "Объект" в схеме СКД.

Проанализируем пример передачи внешней таблицы значений в процессор компоновки:


// Предположим, у нас есть сформированная ТаблицаЗначений с именем "ТаблицаРезультат"
// В схеме СКД должен быть добавлен Набор Данных - Объект с именем "ВнешниеДанныеПодразделений"

ВнешниеНаборыДанных = Новый Структура;
ВнешниеНаборыДанных.Вставить("ВнешниеДанныеПодразделений", ТаблицаРезультат);

КомпоновщикМакета = Новый КомпоновщикМакетаКомпоновкиДанных;
Макет = КомпоновщикМакета.Выполнить(СхемаКомпоновки, КомпоновщикНастроек.ПолучитьНастройки());

ПроцессорКомпоновки = Новый ПроцессорКомпоновкиДанных;
// Важно: передаем структуру внешних данных вторым параметром
ПроцессорКомпоновки.Инициализировать(Макет, ВнешниеНаборыДанных, ДанныеРасшифровки, Истина);

ТабДок = Новый ТабличныйДокумент;
ПроцессорВывода = Новый ПроцессорВыводаРезультатаКомпоновкиДанныхВТабличныйДокумент;
ПроцессорВывода.УстановитьДокумент(ТабДок);
ПроцессорВывода.Вывести(ПроцессорКомпоновки);

5. Использование XML-сериализации вместо ручного кодинга схемы

Создавать всю структуру схемы компоновки (поля, источники данных, типы, связи) вручную на встроенном языке 1С, как это сделано в первом примере, — крайне трудоемкая задача. Малейшая опечатка в имени поля или типе объекта приведет к труднодиагностируемым ошибкам.

В реальной разработке гораздо проще и надежнее настроить схему компоновки визуально в конфигураторе, сохранить ее в XML-файл (или поместить в макет конфигурации с типом "Текстовый документ"), а затем программно прочитать ее в коде:


// Чтение схемы компоновки из XML-файла
ЧтениеXML = Новый ЧтениеXML;
ЧтениеXML.ОткрытьФайл("C:\DCS_Schemas\MySimpleReport.xml");

СхемаКомпоновки = СериализаторXDTO.ПрочитатьXML(ЧтениеXML, Тип("СхемаКомпоновкиДанных"));
ЧтениеXML.Закрыть();

// Теперь у нас есть готовая СхемаКомпоновки, и мы можем выводить отчет стандартно

Такой подход позволяет разделять визуальное проектирование структуры отчета и логику его программного вызова, что существенно сокращает время разработки и упрощает поддержку кода.

← На главную