Как заполнить дерево значений на форме создаваемого документа в 1С

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

При переходе на управляемые формы в 1С:Предприятии 8 разработчики часто сталкиваются с трудностями при работе с интерфейсными объектами. Типичный сценарий — необходимость программно создать документ из внешней обработки и заполнить специфический реквизит формы, например, ДеревоЗначений, который не связан напрямую с реквизитами самого объекта (документа). Попытка напрямую присвоить данные одной формы другой на клиенте приводит к ошибке выполнения.

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

Почему возникает ошибка при прямом присвоении?

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


ФормаДокументаБюджет = ПолучитьФорму("Документ.МойДокумент.Форма.ФормаДокумента", Структура,, Истина);
ФормаДокументаБюджет.ДеревоЗначений = ЭтаФорма.ДеревоБюджет; // Вызывает ошибку!

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

  1. Различие типов данных на клиенте и сервере. Объект типа ДеревоЗначений является чисто серверным типом данных. На клиенте в управляемом приложении его использовать нельзя. Для отображения дерева на форме используется специальный интерфейсный тип — ДанныеФормыДерево. Напрямую присвоить один тип другому невозможно.
  2. Ограничение на изменение реквизитов чужой формы «извне». Платформа 1С строго разграничивает контексты выполнения. На клиенте у вас нет прямого доступа к объектной структуре другой формы для записи сложных коллекций данных.
  3. Данные формы не тождественны данным базы данных. Элементы формы (включая деревья) существуют только в оперативной памяти запущенного сеанса. Само по себе заполнение дерева на форме не гарантирует его сохранение в СУБД, если не настроены механизмы сериализации и записи в физические таблицы базы данных.

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

Решение 1: Передача дерева значений через Временное хранилище (UI-метод)

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

Разберем этот процесс по шагам.

Шаг 1. Преобразование интерфейсного дерева в объект и помещение в хранилище

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


&НаСервере
Функция ПоместитьДеревоВХранилище()
    // Преобразуем интерфейсный реквизит "ДеревоБюджет" (тип ДанныеФормыДерево) в реальное ДеревоЗначений
    ДеревоОбъект = РеквизитФормыВЗначение("ДеревоБюджет"); 
    
    // Помещаем объект во временное хранилище сеанса
    АдресХранилища = ПоместитьВоВременноеХранилище(ДеревоОбъект, УникальныйИдентификатор);
    Возврат АдресХранилища;
КонецФункции

Шаг 2. Передача адреса хранилища в параметрах открытия формы

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


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

Шаг 3. Прием данных в форме документа

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


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

Этот способ надежен и полностью соответствует стандартам разработки 1С при использовании интерактивных сценариев.

Решение 2: Оптимальный архитектурный подход (Без открытия форм)

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

Золотое правило разработки в 1С: Пакетное (массовое) создание и проведение документов должно выполняться исключительно на сервере в фоновом режиме или транзакции, без какого-либо обращения к экранным формам.

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

Следовательно, нам не нужно открывать форму документа. Достаточно напрямую заполнить соответствующую табличную часть создаваемого объекта ДокументОбъект на сервере и записать его.

Пример рекурсивного обхода дерева и заполнения табличной части документа

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


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

&НаСервере
Процедура ЗаполнитьТабличнуюЧастьИзДерева(ТабличнаяЧастьДокумента, СтрокиДерева, ТекущийУровень)
    // Обходим строки текущего уровня дерева значений
    Для Каждого СтрокаДерева Из СтрокиДерева Цикл
        
        // Добавляем строку в плоскую табличную часть документа
        НоваяСтрока = ТабличнаяЧастьДокумента.Добавить();
        
        // Копируем значения совпадающих реквизитов (Статья, Сумма, ЦФО и т.д.)
        ЗаполнитьЗначенияСвойств(НоваяСтрока, СтрокаДерева);
        
        // Если в табличной части предусмотрено хранение иерархии (например, уровень вложенности)
        Если ТабличнаяЧастьДокумента.Колонки.Найти("Уровень") <> Неопределено Тогда
            НоваяСтрока.Уровень = ТекущийУровень;
        КонецЕсли;
        
        // Если у текущей строки дерева есть подчиненные строки, обрабатываем их рекурсивно
        Если СтрокаДерева.Строки.Количество() > 0 Тогда
            ЗаполнитьТабличнуюЧастьИзДерева(ТабличнаяЧастьДокумента, СтрокаДерева.Строки, ТекущийУровень + 1);
        КонецЕсли;
        
    КонецЦикла;
КонецПроцедуры

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

← На главную