При разработке отчетов на базе Библиотеки стандартных подсистем (БСП) программисты часто сталкиваются с ситуацией, когда данные, помещенные во временное хранилище на сервере, внезапно становятся недоступными («Неопределено») при попытке их извлечь внутри схемы компоновки данных. Особенно остро эта проблема проявляется, когда источником данных служит внешний файл (например, Excel), который пользователь выбирает в форме, а отчет должен сформироваться с использованием механизмов БСП (фоновое формирование, рассылка отчетов и т.д.).
В этой статье мы подробно разберем, почему так происходит, как работают сеансы в 1С и какими способами можно гарантированно передать таблицу значений или массив в отчет, работающий в фоновом режиме.
Первым делом проанализируем ситуацию с точки зрения архитектуры платформы «1С:Предприятие 8.3». Когда мы вызываем метод ПоместитьВоВременноеХранилище(Данные, УникальныйИдентификатор), данные привязываются к конкретному сеансу пользователя. Если в качестве второго параметра передается Новый УникальныйИдентификатор(), то время жизни этих данных ограничено текущим серверным вызовом или сеансом (в зависимости от версии платформы и контекста).
Основная сложность при работе с БСП заключается в том, что формирование отчета по умолчанию происходит в фоновом задании. С точки зрения системы, фоновое задание — это отдельный сеанс. Согласно правилам платформы, временное хранилище одного сеанса недоступно из другого. Именно поэтому, когда основной сеанс пользователя поместил таблицу в хранилище, а фоновый сеанс отчета попытался её прочитать по адресу, он получил Неопределено.
Рассмотрим стандартный механизм передачи данных между сеансом-родителем и фоновым заданием. Платформа делает исключение для временного хранилища, если оно привязано к идентификатору формы. Выясним, как правильно реализовать этот механизм:
Однако в БСП процесс запуска фонового задания инкапсулирован внутри системных модулей. Чтобы этот метод сработал, нам нужно убедиться, что адрес хранилища передается в структуру параметров формирования отчета. Посмотрим на пример кода помещения данных:
// В контексте формы отчета
АдресХранилища = ПоместитьВоВременноеХранилище(ТаблицаСДанными, ЭтаФорма.УникальныйИдентификатор);
// Теперь этот АдресХранилища нужно передать в параметры СКД или настройки БСП
Важный нюанс: Данные, помещенные по адресу формы, будут доступны фоновому заданию только в том случае, если фоновое задание было запущено из этой формы и адрес был явно передан в его параметры. В типовых отчетах БСП это не всегда удобно реализовать «в лоб».
Если наша задача — передать готовую таблицу значений в отчет СКД, проанализируем возможность использования внешних наборов данных. Это наиболее «чистый» способ с точки зрения разработки. Вместо того чтобы пытаться достать данные из хранилища внутри модуля объекта отчета, мы можем передать их напрямую в процедуру компоновки.
Для этого в схеме компоновки данных (СКД) создается набор данных типа Объект. В модуле объекта отчета, в обработчике ПриКомпоновкеРезультата, мы можем заполнить этот набор данных. Но как получить саму таблицу в модуле объекта, если она была создана в форме?
Рассмотрим вариант передачи через дополнительные свойства или параметры:
Объект.ДополнительныеСвойства (если это позволяет контекст БСП при передаче объекта на сервер).ПараметрыДанных самой схемы компоновки, если тип параметра позволяет хранить объект (хотя для ТЗ это редко применимо напрямую).Проанализируем ситуацию, упомянутую в обсуждении: если используется кластер серверов, временные файлы могут быть недоступны, так как фоновое задание может запуститься на другом рабочем сервере. В таком случае самым надежным способом передачи данных между разными сеансами является использование базы данных.
Выделим шаги для реализации этого метода:
Идентификатор (тип УникальныйИдентификатор) и ресурсом Данные (тип ХранилищеЗначения).УникальныйИдентификатор записи в качестве параметра отчета.Пример кода для записи данных:
КлючСвязи = Новый УникальныйИдентификатор();
ЗаписьРегистра = РегистрыСведений.ВременныйБуферДанных.СоздатьМенеджерЗаписи();
ЗаписьРегистра.Идентификатор = КлючСвязи;
ЗаписьРегистра.Данные = Новый ХранилищеЗначения(ТаблицаExcel, Новый СжатиеДанных(9));
ЗаписьРегистра.Записать();
// Передаем КлючСвязи в параметры отчета
Выясним еще одну важную деталь. Иногда ПолучитьИзВременногоХранилища возвращает Неопределено не из-за сеансов, а из-за невозможности сериализовать объект. ТаблицаЗначений успешно передается между контекстами только в том случае, если все типы данных в её колонках поддерживают сериализацию (примитивные типы, ссылки, структуры, массивы).
Если в вашей таблице, прочитанной из Excel, присутствуют специфические COM-объекты или типы, которые нельзя передать на сервер, очистите их или преобразуйте в массив структур перед помещением в хранилище. Рассмотрим пример преобразования:
Функция ТЗвМассивСтруктур(ТаблицаДанных)
МассивРезультат = Новый Массив;
Колонки = ТаблицаДанных.Колонки;
Для Каждого СтрокаТЗ Из ТаблицаДанных Цикл
СтруктураСтроки = Новый Структура;
Для Каждого Колонка Из Колонки Цикл
СтруктураСтроки.Вставить(Колонка.Имя, СтрокаТЗ[Колонка.Имя]);
КонецЦикла;
МассивРезультат.Добавить(СтруктураСтроки);
КонецЦикла;
Возврат МассивРезультат;
КонецФункции
Подведем итог: Для стабильной работы отчета в БСП при передаче внешних данных лучше всего отказаться от использования временного хранилища с коротким временем жизни. Оптимальным выбором будет либо использование идентификатора формы (при условии правильной передачи адреса в параметры формирования), либо использование промежуточной таблицы в базе данных (регистра сведений), что гарантирует доступность данных в любом узле кластера серверов.