При разработке отчетов на платформе 1С:Предприятие (особенно в рамках обычного приложения на базе механизма ПостроительОтчета) программисты часто сталкиваются с задачей автоматизации. Одна из таких задач — последовательное применение нескольких файлов настроек (например, с расширением .set или .xml) для автоматического формирования нескольких вариантов отчета.
Типичная проблема выглядит следующим образом: код обходит в цикле файлы настроек, восстанавливает их в построителе и запускает генерацию отчета. Однако на форме отображается только самый последний отчет, а предыдущие результаты бесследно исчезают. Выясним причину этой проблемы, детально разберем ошибки в коде и рассмотрим правильные архитектурные решения.
Для начала проанализируем, почему у автора оригинального вопроса в цикле «срабатывал» только один файл, хотя отладчик показывал успешное прохождение всех итераций. Здесь кроются сразу две важные проблемы платформы 1С.
1. Логическая ошибка при загрузке настроек из файла
В первом варианте кода автора содержалась классическая опечатка невнимательности. Настройки считывались в одну переменную, а применялись из совершенно другой:
// ОШИБКА: Считали данные в СтруктураЭлементов...
СтруктураЭлементов = ЗначениеИзФайла(ФайлПутьКФайлу);
// ...а применили из старой переменной СохраненныеНастройки!
ПостроительОтчета.УстановитьНастройки(СохраненныеНастройки.НастройкиПостроителя);
Эту проблему быстро выявили участники обсуждения. Данные необходимо извлекать непосредственно из того объекта, который был только что прочитан из файла с диска.
2. Блокировка потока отрисовки интерфейса (UI-потока)
Основная причина, почему пользователь не видел промежуточных результатов генерации отчетов в цикле — физические особенности работы графического движка платформы 1С. Когда выполняется длительный цикл на клиенте, интерфейс программы временно «замораживается». Все манипуляции с табличным документом происходят исключительно в оперативной памяти компьютера.
Отрисовка изменений на экране монитора происходит только тогда, когда выполнение встроенного языка полностью завершается и управление передается обратно платформе. Таким образом, на каждой итерации цикла происходило следующее:
Для решения этой проблемы необходимо изменить саму логику обработки результатов. Рассмотрим три практических сценария реализации этой задачи.
---
Если ваша задача — показать пользователю результаты всех настроек одновременно в одном окне, необходимо отказаться от полной очистки поля табличного документа на каждой итерации цикла. Вместо этого мы будем последовательно дописывать новые данные в конец существующего документа.
Разберем этот процесс по шагам в следующем примере кода:
Процедура ВосстановитьИОбъединитьНастройки() Экспорт
КаталогНастроек = "\\tsclient\E\1С\Прайс";
НайденныеФайлы = НайтиФайлы(КаталогНастроек, "*.set");
// Получаем прямую ссылку на табличный документ на форме
ТабДок = ЭлементыФормы.РезультатОтчета;
// Очищаем результирующий документ ОДИН раз перед началом цикла
ТабДок.Очистить();
Для Каждого ТекущийФайл Из НайденныеФайлы Цикл
Попытка
// Извлекаем настройки построителя из файла
НастройкиПостроителя = ЗначениеИзФайла(ТекущийФайл.ПолноеИмя);
Если НастройкиПостроителя = Неопределено Тогда
ВызватьИсключение "Файл поврежден или имеет неверный формат.";
КонецЕсли;
// Инициализируем построитель (восстанавливаем структуру запроса)
ИнициализироватьПостроительОтчета();
// Применяем новые настройки
ПостроительОтчета.УстановитьНастройки(НастройкиПостроителя);
// Выполняем запрос построителя
ПостроительОтчета.Выполнить();
// Выводим результат, дописывая его в конец нашего ТабДок
ПостроительОтчета.Вывести(ТабДок);
// Добавляем визуальный разделитель между отчетами для удобства чтения
ТабДок.ВывестиГоризонтальныйРазделительСтраниц();
Исключение
Сообщить("Не удалось обработать файл: " + ТекущийФайл.Имя + ". Ошибка: " + ОписаниеОшибки(), СтатусСообщения.Внимание);
КонецПопытки;
КонецЦикла;
Сообщить("Пакетное построение отчетов завершено успешно!");
КонецПроцедуры
В данном сценарии метод ПостроительОтчета.Вывести(ТабДок) не затирает предыдущее содержимое, а аккуратно присоединяет новые секции снизу. Метод ВывестиГоризонтальныйРазделительСтраниц() позволяет визуально разграничить прайсы с разными настройками отборов и колонок.
---
Если для каждого файла настроек требуется открыть собственное независимое диалоговое окно, чтобы пользователь мог работать с каждым отчетом отдельно (сворачивать, разворачивать, сравнивать), нам потребуется динамическое создание форм.
Рассмотрим, как это реализовать программно:
Процедура ОткрытьОтчетыВРазныхОкнах() Экспорт
КаталогНастроек = "\\tsclient\E\1С\Прайс";
НайденныеФайлы = НайтиФайлы(КаталогНастроек, "*.set");
Для Каждого ТекущийФайл Из НайденныеФайлы Цикл
Попытка
// Читаем индивидуальные настройки
НастройкиПостроителя = ЗначениеИзФайла(ТекущийФайл.ПолноеИмя);
// Программное создание НОВОГО экземпляра формы отчета
ИмяОтчета = "МойОтчетПоЦенамНоменклатуры";
ФормаНовогоОтчета = Отчеты[ИмяОтчета].ПолучитьФорму("ФормаОтчета");
// Открываем форму на экране (теперь у нее свой собственный контекст и UI-поток)
ФормаНовогоОтчета.Открыть();
// Задаем настройки в построитель созданной формы
ФормаНовогоОтчета.ПостроительОтчета.УстановитьНастройки(НастройкиПостроителя);
// Запускаем обновление данных конкретно в этой форме
ФормаНовогоОтчета.ОбновитьОтчет();
// Опционально: меняем заголовок окна для информативности
ФормаНовогоОтчета.Заголовок = "Отчет: " + ТекущийФайл.БазовоеИмя;
Исключение
Сообщить("Ошибка инициализации формы для файла " + ТекущийФайл.Имя + ": " + ОписаниеОшибки());
КонецПопытки;
КонецЦикла;
КонецПроцедуры
При таком подходе платформа создаст в памяти изолированные объекты форм. Вызов метода Открыть() зарегистрирует их в операционной системе, а последующий вызов процедур построения заполнит индивидуальные табличные документы.
---
Зачастую перед разработчиком стоит задача не выводить отчеты на экран пользователя, а выгружать их на диск в различных форматах (например, Excel или PDF) по расписанию или по кнопке «Выгрузить всё». В этом случае нам вообще не нужны экранные элементы формы.
Посмотрим на пример реализации фоновой генерации файлов:
Процедура ПакетныйЭкспортВExcel() Экспорт
КаталогНастроек = "\\tsclient\E\1С\Прайс";
КаталогЭкспорта = "\\tsclient\E\1С\Прайс\Экспорт";
НайденныеФайлы = НайтиФайлы(КаталогНастроек, "*.set");
Для Каждого ТекущийФайл Из НайденныеФайлы Цикл
Попытка
НастройкиПостроителя = ЗначениеИзФайла(ТекущийФайл.ПолноеИмя);
ИнициализироватьПостроительОтчета();
ПостроительОтчета.УстановитьНастройки(НастройкиПостроителя);
ПостроительОтчета.Выполнить();
// Создаем скрытый табличный документ исключительно в оперативной памяти
ВременныйТабДок = Новый ТабличныйДокумент;
ПостроительОтчета.Вывести(ВременныйТабДок);
// Формируем имя результирующего Excel-файла
ИмяВыходногоФайла = КаталогЭкспорта + "\" + ТекущийФайл.БазовоеИмя + ".xlsx";
// Записываем документ на диск в формате XLSX
ВременныйТабДок.Записать(ИмяВыходногоФайла, ТипФайлаТабличногоДокумента.XLSX);
Сообщить("Файл успешно сформирован: " + ИмяВыходногоФайла);
Исключение
Сообщить("Ошибка экспорта по настройке " + ТекущийФайл.Имя + ": " + ОписаниеОшибки(), СтатусСообщения.Важное);
КонецПопытки;
КонецЦикла;
КонецПроцедуры
---
Чтобы программное сохранение и восстановление настроек работало стабильно и не вызывало труднодиагностируемых ошибок времени выполнения, обязательно соблюдайте следующие технические регламенты:
УстановитьНастройки() никогда не сработает корректно, если в объекте ПостроительОтчета предварительно не задано свойство Текст. Настройки отборов, группировок и порядка накладываются на метаданные схемы запроса. Если запроса нет — настройки сбросятся. Всегда сначала инициализируйте запрос построителя, и только потом применяйте настройки из файла..set), сохраненные до обновления конфигурации, при попытке загрузки могут вызвать критическую ошибку или примениться некорректно. Обеспечивайте обработку исключений через конструкцию Попытка ... Исключение.УстановитьНастройки() принимает дополнительные параметры. По умолчанию он восстанавливает абсолютно всё. Но вы можете управлять этим поведением: ПостроительОтчета.УстановитьНастройки(Настройки, Ложь, Истина, Ложь) (в данном примере будут применены только настройки порядка сортировки, а отборы и измерения останутся текущими).\\tsclient\E\1С\... свидетельствует о работе в терминальной сессии. Доступ к локальным дискам через RDP-клиент может работать медленно или аварийно отваливаться при нестабильном сетевом соединении. Надежнее хранить настройки непосредственно в базе данных 1С (например, в справочнике или регистре сведений с типом поля ХранилищеЗначения).Механизм ПостроительОтчета является устаревшим наследием платформы 1С ранних версий (8.0/8.1) для обычного приложения. Если перед вами стоит задача разработки новых отчетов, настоятельно рекомендуется использовать Систему компоновки данных (СКД).
В СКД механизмы сохранения и переключения вариантов отчетов реализованы на уровне ядра платформы. Пользователю и разработчику больше не требуется вручную писать код работы с файлами на диске. Платформа самостоятельно управляет настройками, позволяет хранить их в системных таблицах базы данных, передавать между пользователями, гибко модифицировать структуру вывода в режиме "1С:Предприятие" и автоматически перерисовывать отчеты на управляемых формах без зависания интерфейса.