Как передать ТаблицуЗначений из модуля формы в процедуру ПриКомпоновкеРезультата внешнего отчета 1С?

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

При разработке внешних отчетов в системе 1С:Предприятие 8.3 часто возникает необходимость передать динамически формируемые данные, такие как ТаблицаЗначений, из контекста модуля формы, где происходит начальная инициализация или интерактивные действия, в процедуру ПриКомпоновкеРезультата модуля объекта отчета, которая отвечает за окончательное формирование структуры отчета. Эта задача может оказаться нетривиальной из-за особенностей жизненного цикла объектов на сервере 1С.

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

Проблема передачи данных между контекстами: почему это происходит?

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

Наш коллега столкнулся с этой проблемой: он формировал ТаблицуЗначений в экспортной функции модуля объекта, вызывал ее из процедуры ПриСозданииНаСервере модуля формы, присваивал результат реквизиту отчета (ЭтотОбъект.РеквизитТаблица = Таб;), но в ПриКомпоновкеРезультата этот реквизит оказывался пустым. Попытка поместить таблицу в КомпоновщикНастроек.Настройки.ДополнительныеСвойства также не увенчалась успехом.

Рассмотрим несколько эффективных подходов к решению этой задачи.

Решение 1: Использование временного хранилища (надежный и проверенный способ)

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

Принцип работы

  1. Мы формируем ТаблицуЗначений.
  2. Помещаем ее во временное хранилище на сервере, получая строковый адрес.
  3. Возвращаем этот строковый адрес из функции, которая формирует таблицу.
  4. В модуле формы, после вызова этой функции, мы присваиваем полученный адрес специально созданному реквизиту отчета. Критически важно: этот реквизит должен быть реквизитом самого объекта отчета (не формы).
  5. В процедуре ПриКомпоновкеРезультата мы получаем сохраненный адрес из реквизита отчета и используем его для извлечения ТаблицыЗначений из временного хранилища.

Пошаговая реализация

  1. Создаем строковый реквизит отчета:

    Для начала, нам потребуется реквизит у самого объекта отчета, который будет хранить адрес нашей ТаблицыЗначений во временном хранилище. Создадим его, например, с именем АдресТаблицы и типом Строка. Это можно сделать в конфигураторе в свойствах отчета на закладке "Реквизиты".

  2. Формируем таблицу и помещаем ее в хранилище в модуле объекта:

    Создадим экспортную функцию в модуле объекта отчета, которая будет формировать нашу ТаблицуЗначений и помещать ее во временное хранилище. Функция должна возвращать строковый адрес хранилища.

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

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

    
    // Модуль формы отчета
    
    Процедура ПриСозданииНаСервере(Отказ, СтандартнаяОбработка)
    
        // Вызываем экспортную функцию из модуля объекта
        // и получаем адрес ТаблицыЗначений во временном хранилище
        АдресТаблицыВоВременномХранилище = Отчет.ПолучитьТаблицуИПоместитьВоВременноеХранилище();
    
        // Присваиваем полученный адрес реквизиту объекта отчета.
        // Это ключевой шаг для сохранения адреса между вызовами сервера.
        Отчет.АдресТаблицы = АдресТаблицыВоВременномХранилище;
    
    КонецПроцедуры
    

    Обратите внимание на использование Отчет.АдресТаблицы. Это гарантирует, что мы обращаемся к реквизиту именно того объекта отчета, который будет использоваться далее.

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

    Теперь, когда отчет будет формироваться, в процедуре ПриКомпоновкеРезультата мы сможем легко получить нашу ТаблицуЗначений, используя сохраненный адрес.

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

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

Решение 2: Использование ДополнительныхСвойств КомпоновщикаНастроек (с учетом нюансов)

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

Правильный подход

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

  1. Установка свойства:

    Мы можем установить нашу ТаблицуЗначений в ДополнительныеСвойства прямо в модуле формы, например, в той же процедуре ПриСозданииНаСервере или перед формированием отчета.

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

    В процедуре ПриКомпоновкеРезультата мы можем получить доступ к этим свойствам через КомпоновщикНастроек.Настройки.ДополнительныеСвойства.

    
    // Модуль объекта отчета
    
    Процедура ПриКомпоновкеРезультата(ДокументРезультат, ДанныеРасшифровки, СтандартнаяОбработка)
    
        // Получаем КомпоновщикНастроек текущего отчета
        // Его можно получить из реквизита объекта отчета, если он там хранится,
        // или создать новый, если настройки берутся из схемы.
        // В данном случае, мы предполагаем, что настройки уже применены к процессу.
        
        // Если в ПриСозданииНаСервере вы передали ТаблицуЗначений в ДополнительныеСвойства
        // через реквизит формы КомпоновщикНастроек, то здесь она будет доступна
        // через КомпоновщикНастроекОтчета.
        // Необходимо иметь доступ к объекту КомпоновщикНастроек, 
        // который использовался для формирования отчета.
        // Часто он передается косвенно через стандартные механизмы СКД.
    
        // Для примера, если мы имеем доступ к компоновщику настроек отчета:
        КомпоновщикНастроекОтчета = ЭтотОбъект.КомпоновщикНастроек; // Если реквизит есть в отчете
    
        Если КомпоновщикНастроекОтчета.Настройки.ДополнительныеСвойства.Свойство("МояТаблицаДанных") Тогда
            МояТаблицаИзДопСвойств = КомпоновщикНастроекОтчета.Настройки.ДополнительныеСвойства.МояТаблицаДанных;
            
            Если МояТаблицаИзДопСвойств <> Неопределено И МояТаблицаИзДопСвойств.Тип() = Тип("ТаблицаЗначений") Тогда
                Сообщить("ТаблицаЗначений успешно получена из ДополнительныхСвойств. Количество строк: " + МояТаблицаИзДопСвойств.Количество());
                // Используем МояТаблицаИзДопСвойств
            Иначе
                Сообщить("Ошибка: МояТаблицаДанных в ДополнительныхСвойствах не является ТаблицейЗначений или неопределена.");
            КонецЕсли;
        Иначе
            Сообщить("Свойство МояТаблицаДанных не найдено в ДополнительныхСвойствах.");
        КонецЕсли;
    
        // ... остальной код ПриКомпоновкеРезультата ...
    
    КонецПроцедура
    

    Для того чтобы КомпоновщикНастроекОтчета был доступен в модуле объекта с сохраненными настройками, его можно либо передать как параметр, либо сохранить как реквизит объекта отчета (если это предусмотрено вашей архитектурой).

Этот метод менее надежен, чем временное хранилище, если вы не уверены в том, что работаете с одним и тем же экземпляром КомпоновщикаНастроек на протяжении всего жизненного цикла формирования отчета. Тем не менее, он может быть полезен для передачи небольших объемов структурированных данных.

Решение 3: Передача ТаблицыЗначений как внешнего набора данных для СКД (самый гибкий способ)

Если ваша ТаблицаЗначений должна выступать в качестве полноценного источника данных для Системы Компоновки Данных (СКД) — то есть, к ней необходимо применять отборы, группировки, сортировки, объединять с другими наборами данных и так далее — то наиболее мощным и рекомендуемым способом является передача ее в качестве внешнего набора данных.

Этот подход позволяет СКД обрабатывать вашу ТаблицуЗначений наравне с данными из базы данных, используя все ее возможности.

Пошаговая реализация

  1. Создаем набор данных типа "Объект" в схеме компоновки данных:

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

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

  2. Программная инициализация ПроцессораКомпоновкиДанных в ПриКомпоновкеРезультата:

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

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

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

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

Заключение

Мы рассмотрели три основных подхода к передаче ТаблицыЗначений из модуля формы в процедуру ПриКомпоновкеРезультата внешнего отчета:

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

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

← На главную