Как передать таблицу значений в процедуру ПриКомпоновкеРезультата

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

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

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

Постановка задачи и базовая схема работы

Представим сценарий: в отчете необходимо рассчитать точный момент времени создания документов (например, заказов покупателей) на основе их уникального идентификатора (GUID/UUID) и вывести эти данные в отчет СКД. Поскольку рассчитать дату создания по GUID напрямую простым запросом невозможно (требуются сложные побитовые сдвиги и математические операции), данные сначала рассчитываются программно и собираются в таблицу значений ТЗ2 с колонками ЗаказПокупателя и Момент.

Для того чтобы СКД поняла, что данные придут извне, нам нужно выполнить три основных шага:

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

Рассмотрим детально реализацию каждого шага.

Шаг 1. Подготовка схемы компоновки данных (СКД)

Прежде чем писать код, откроем схему компоновки данных нашего отчета:

  1. Перейдем на вкладку Наборы данных.
  2. Добавим новый набор данных типа Набор данных - объект (вместо привычного «Набор данных - запрос»).
  3. В поле Имя объекта содержащего данные введем строковый идентификатор. Пусть это будет ВходящаяТаблицаДанных. Важно: это имя чувствительно к регистру и должно в точности совпадать с именем ключа, который мы передадим в коде.
  4. В табличном поле «Поля» добавим колонки, соответствующие структуре нашей таблицы значений. В нашем примере добавим поля:
    • ЗаказПокупателя (тип: ДокументСсылка.ЗаказПокупателя)
    • Момент (тип: Дата)

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

Шаг 2. Передача таблицы значений через ДополнительныеСвойства

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

Проанализируем пример кода в модуле формы отчета, где мы подготавливаем таблицу значений и помещаем её в настройки компоновщика:


&НаСервере
Процедура ПодготовитьДанныеИЗапуститьНаСервере()
    
    // 1. Создаем структуру таблицы значений
    ТЗ2 = Новый ТаблицаЗначений;
    ТЗ2.Колонки.Добавить("ЗаказПокупателя", Новый ОписаниеТипов("ДокументСсылка.ЗаказПокупателя"));
    ТЗ2.Колонки.Добавить("Момент", Новый ОписаниеТипов("Дата"));
    
    // 2. Наполняем таблицу данными (ваш алгоритм расчета даты по UUID)
    // Для примера выберем ссылки на заказы
    Запрос = Новый Запрос;
    Запрос.Текст = 
        "ВЫБРАТЬ РАЗРЕШЕННЫЕ ССЫЛКА 
        |ИЗ Документ.ЗаказПокупателя КАК ЗаказПокупателя 
        |ГДЕ ЗаказПокупателя.Проведен";
    
    Выборка = Запрос.Выполнить().Выбрать();
    Пока Выборка.Следующий() Цикл
        НоваяСтрока = ТЗ2.Добавить();
        НоваяСтрока.ЗаказПокупателя = Выборка.Ссылка;
        
        // Здесь выполняется расчет даты создания по уникальному идентификатору (UUID)
        УИ = Строка(Выборка.Ссылка.УникальныйИдентификатор());
        УИ = ВРег(СтрЗаменить(УИ, "-", ""));
        Если Сред(УИ, 13, 1) = "1" Тогда
            Значение16 = ВРег(Сред(УИ, 14, 3) + Сред(УИ, 9, 4) + Сред(УИ, 1, 8));
            Цифры16 = "123456789ABCDEF";
            Значение10 = 0;
            Длина16 = СтрДлина(Значение16);
            Для Ном = 1 По Длина16 Цикл
                Значение10 = Значение10 + Pow(16, Длина16 - Ном) * Найти(Цифры16, Сред(Значение16, Ном, 1));
            КонецЦикла;
            Значение10 = Значение10 / 10000000;
            НоваяСтрока.Момент = Дата(1582, 10, 15, 3, 0, 0) + Значение10;
        Иначе
            НоваяСтрока.Момент = Выборка.Ссылка.Дата; // Резервный вариант
        КонецЕсли;
    КонецЦикла;
    
    // 3. Помещаем таблицу в Дополнительные свойства настроек компоновщика
    КомпоновщикНастроек.Настройки.ДополнительныеСвойства.Вставить("ВнешняяТЗ", ТЗ2);
    
КонецПроцедуры

Шаг 3. Реализация процедуры ПриКомпоновкеРезультата

Теперь перейдем в Модуль объекта нашего отчета. Именно здесь переопределяется стандартный вывод компоновщика. Нам необходимо извлечь таблицу из дополнительных свойств, объявить её в качестве внешнего набора данных и запустить процессор вывода.

Рассмотрим пошагово код процедуры ПриКомпоновкеРезультата:


Процедура ПриКомпоновкеРезультата(ДокументРезультат, ДанныеРасшифровки, СтандартнаяОбработка)
    
    // Отключаем стандартный вывод отчета платформой
    СтандартнаяОбработка = Ложь;
    
    // 1. Извлекаем таблицу значений из дополнительных свойств компоновщика
    ТЗ2 = Неопределено;
    КомпоновщикНастроек.Настройки.ДополнительныеСвойства.Свойство("ВнешняяТЗ", ТЗ2);
    
    // Если таблица по какой-то причине не передана, создаем пустую структуру, чтобы избежать критической ошибки
    Если ТЗ2 = Неопределено Тогда
        ТЗ2 = Новый ТаблицаЗначений;
        ТЗ2.Колонки.Добавить("ЗаказПокупателя", Новый ОписаниеТипов("ДокументСсылка.ЗаказПокупателя"));
        ТЗ2.Колонки.Добавить("Момент", Новый ОписаниеТипов("Дата"));
    КонецЕсли;
    
    // 2. Формируем структуру внешних наборов данных
    // Ключ структуры должен строго совпадать с именем Набора Данных - Объекта в схеме СКД!
    ВнешниеНаборыДанных = Новый Структура;
    ВнешниеНаборыДанных.Вставить("ВходящаяТаблицаДанных", ТЗ2);
    
    // 3. Инициализируем макет компоновки
    СхемаКомпоновкиДанных = ПолучитьМакет("ОсновнаяСхемаКомпоновкиДанных");
    
    КомпоновщикМакета = Новый КомпоновщикМакетаКомпоновкиДанных;
    МакетКомпоновки = КомпоновщикМакета.Выполнить(СхемаКомпоновкиДанных, КомпоновщикНастроек.Настройки, ДанныеРасшифровки);
    
    // 4. Инициализируем процессор компоновки данных и передаем наши внешние наборы данных
    ПроцессорКомпоновкиДанных = Новый ПроцессорКомпоновкиДанных;
    ПроцессорКомпоновкиДанных.Инициализировать(МакетКомпоновки, ВнешниеНаборыДанных, ДанныеРасшифровки, Истина);
    
    // 5. Выводим результат компоновки в Табличный документ отчета
    ДокументРезультат.Очистить();
    ПроцессорВывода = Новый ПроцессорВыводаРезультатаКомпоновкиДанныхВТабличныйДокумент;
    ПроцессорВывода.УстановитьДокумент(ДокументРезультат);
    ПроцессорВывода.Вывести(ПроцессорКомпоновкиДанных);
    
КонецПроцедуры

Продвинутый сценарий: Решение проблемы фонового выполнения и Временное хранилище

В современных конфигурациях 1С (на базе БСП, таких как ERP, УТ, КА, ЗУП) отчеты по умолчанию формируются в **фоновом режиме** на сервере. При запуске фонового задания создается новый сеанс, и свойства компоновщика настроек формы могут потеряться или не сериализоваться должным образом.

Чтобы гарантировать 100% надежность передачи данных в любых условиях, рекомендуется использовать **Временное хранилище**:

  1. В модуле формы (или в серверном вызове формы) мы помещаем готовую таблицу значений во временное хранилище, привязывая её к уникальному идентификатору формы:
    
    АдресХранилища = ПоместитьВоВременноеХранилище(ТЗ2, УникальныйИдентификатор);
    КомпоновщикНастроек.Настройки.ДополнительныеСвойства.Вставить("АдресХранилищаТЗ", АдресХранилища);
    
  2. В процедуре ПриКомпоновкеРезультата модуля объекта мы считываем адрес и восстанавливаем таблицу значений:
    
    АдресХранилища = "";
    Если КомпоновщикНастроек.Настройки.ДополнительныеСвойства.Свойство("АдресХранилищаТЗ", АдресХранилища) Тогда
        Если ЭтоАдресВременногоХранилища(АдресХранилища) Тогда
            ТЗ2 = ПолучитьИзВременногоХранилища(АдресХранилища);
        КонецЕсли;
    КонецЕсли;
    

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

Архитектурная рекомендация: Перенос расчетов в Модуль объекта (Паттерн MVC)

Разработчикам важно помнить о правилах хорошего тона программирования в 1С. Подготовка и расчет данных внутри модуля формы считается плохой практикой. Если ваш отчет будет запускаться автоматически (например, через регламентное задание «Рассылка отчетов» БСП), формы отчета существовать не будет, и весь алгоритм расчетов в форме просто не запустится.

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

Использование Менеджера временных таблиц (МВТ) в СКД как альтернатива

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

Проанализируем пример такого подхода в коде ПриКомпоновкеРезультата:


// Создаем МВТ и помещаем туда ТЗ через стандартный запрос
МВТ = Новый МенеджерВременныхТаблиц;
Запрос = Новый Запрос;
Запрос.МенеджерВременныхТаблиц = МВТ;

Запрос.Текст = 
    "ВЫБРАТЬ * 
    |ПОМЕСТИТЬ ВТ_ВнешниеДанные 
    |ИЗ &ВнешняяТЗ КАК ВнешняяТЗ";
Запрос.УстановитьПараметр("ВнешняяТЗ", ТЗ2);
Запрос.Выполнить();

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

При таком подходе в самой схеме компоновки (СКД) вы можете создать классический "Набор данных - запрос" и обращаться к таблице ВТ_ВнешниеДанные так, словно она уже существует в базе данных: ВЫБРАТЬ * ИЗ ВТ_ВнешниеДанные. СКД автоматически подхватит структуру из переданного менеджера временных таблиц.

Интеграция с БСП: Обработка пустого отчета

Когда вы полностью берете управление выводом на себя (установив СтандартнаяОбработка = Ложь), механизмы Библиотеки стандартных подсистем (БСП) могут не узнать о том, пустой получился отчет или нет. Если переданная таблица значений ТЗ2 не содержит строк, пользователю может вывестись пустой экран вместо стандартной красивой заглушки "Отчет не содержит данных".

Чтобы избежать этой проблемы, обязательно проверяйте количество строк в таблице и передавайте соответствующий флаг в БСП:


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

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

← На главную