Как программно открыть созданный документ с определенной формой?

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

При переходе платформы «1С:Предприятие» на управляемое приложение и клиент-серверную архитектуру разработчики столкнулись с важным изменением концепции: данные и интерфейс были строго разделены. Если в классическом «толстом» клиенте можно было легко создать объект на клиенте, заполнить его реквизиты и тут же открыть его форму одной строчкой кода, то в управляемом приложении (в тонком или веб-клиенте) такой подход не сработает. Объект типа ДокументОбъект живет исключительно на сервере, а методы визуального отображения и формы могут вызываться только на стороне клиента.

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

Понимание проблемы: почему старый метод больше не актуален?

В традиционном толстом клиенте задача решалась буквально в одно действие:


НовыйДок = Документы.РеализацияТоваровУслуг.СоздатьДокумент();
// Заполнение реквизитов...
НовыйДок.ПолучитьФорму("ФормаДокумента").Открыть();

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

Давайте подробно разберем современные и правильные сценарии решения этой задачи.

---

Сценарий 1. Документ уже записан в базу данных (имеет ссылку)

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

Для открытия формы уже существующего (записанного) документа по стандартам 1С мы должны использовать глобальный метод ОткрытьФорму(). Чтобы открыть конкретную форму конкретного документа, нам нужно передать в параметры формы структуру с ключом "Ключ", значением которого будет выступать наша ссылка.

Рассмотрим по шагам, как выглядит этот процесс:

  1. На сервере создаем, заполняем и записываем документ, возвращая его ссылку на клиент.
  2. На клиенте формируем параметры открытия формы и вызываем функцию ОткрытьФорму().

Посмотрим на практический пример реализации:


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

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

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

Альтернатива: Если вам заранее не известен тип ссылки (например, вы пишете универсальный отчет или обработку) и требуется открыть основную форму объекта, можно воспользоваться асинхронным методом ПоказатьЗначение(). Этот метод пришел на замену устаревшему и нерекомендуемому модальному методу ОткрытьЗначение().


// Асинхронное открытие значения в управляемом интерфейсе
ОписаниеОповещения = Новый ОписаниеОповещения("ПослеЗакрытияФормы", ЭтотОбъект);
ПоказатьЗначение(ОписаниеОповещения, СсылкаНаНовыйДокумент);

---

Сценарий 2. Документ НЕ записан (создание новой заполненной формы)

Часто бизнес-логика требует иного поведения: нужно создать новый документ, предварительно заполнить в нем кучу реквизитов и табличных частей, но не записывать его в базу данных автоматически. Документ должен открыться на экране интерактивно, чтобы пользователь мог проверить заполненные данные, внести правки ручным вводом и самостоятельно нажать кнопку «Записать и закрыть» либо отказаться от создания документа.

Поскольку объекта в базе данных еще нет, ссылки тоже нет. Мы имеем дело только с набором данных в оперативной памяти. Рассмотрим три основных и наиболее эффективных способа реализации этой задачи.

Вариант А. Передача параметров заполнения (Рекомендуемый стандарт платформы)

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

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

Разберем по шагам реализацию этого механизма:

Шаг 1. Код вызова формы на клиенте:


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

Шаг 2. Код в модуле объекта создаваемого документа (например, РеализацияТоваровУслуг):


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

Вариант Б. Использование Временного Хранилища (Для больших объемов данных)

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

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

Проанализируем этот алгоритм на практическом примере:

Шаг 1. Подготовка и передача данных на клиенте/сервере обработки:


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

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

Шаг 2. Чтение данных в модуле формы открываемого документа (в обработчике ПриСозданииНаСервере):


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

Вариант В. Копирование Данных Формы (Без изменения модуля формы документа)

Этот метод незаменим, если вы разрабатываете внешнюю обработку или отчет для типовой конфигурации (например, 1С:ERP, 1С:КА или 1С:УТ) и у вас нет технической возможности или разрешения изменять стандартные формы и модули объектов конфигурации.

В этом случае мы можем применить технологию заполнения формы «на лету» на сервере через приведение типов к прикладному объекту:

  1. На клиенте мы инициализируем получение формы документа через ПолучитьФорму(). Нам вернется пустая форма.
  2. Передаем данные формы на сервер.
  3. На сервере конвертируем данные формы в полноценный прикладной объект ДокументОбъект с помощью метода ДанныеФормыВЗначение().
  4. Заполняем этот объект абсолютно любыми методами (заполняем реквизиты, табличные части, вызываем любые процедуры заполнения из модуля менеджера или общих модулей).
  5. Конвертируем измененный объект обратно в данные формы методом ЗначениеВДанныеФормы().
  6. Возвращаем управление на клиент и вызываем метод Открыть() у заполненной формы.

Рассмотрим детальный пример реализации:


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

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

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

---

Особенности работы с дополнительными реквизитами БСП в ERP и КА

В сложных современных конфигурациях (1С:ERP, 1С:Комплексная автоматизация, 1С:Управление торговлей) широко используется подсистема «Свойства» из состава Библиотеки Стандартных Подсистем (БСП). Она отвечает за динамическое добавление дополнительных реквизитов и сведений к объектам.

Часто программисты сталкиваются со следующей проблемой: при попытке открыть программно созданный незаписанный объект в режиме «Только просмотр» динамические дополнительные реквизиты на форме не отображаются или выводятся некорректно.

Выясним причину: Отрисовка и инициализация дополнительных реквизитов на форме физически происходит в процедуре ПриСозданииНаСервере открываемой формы с использованием программного интерфейса БСП (модуль УправлениеСвойствами). В этот момент подсистема обращается к табличной части создаваемого объекта ДополнительныеРеквизиты.

Чтобы доп. реквизиты отображались корректно на форме незаписанного документа, выполните следующие правила:

Пример программного заполнения ТЧ дополнительных реквизитов перед открытием формы:


// В коде заполнения объекта на сервере (например, в Варианте В)
НовыйРеквизит = ДокументОбъект.ДополнительныеРеквизиты.Добавить();
НовыйРеквизит.Свойство = ПВХ.ДополнительныеРеквизитыИСведения.НайтиПоНаименованию("Бренд");
НовыйРеквизит.Значение = Справочники.ЗначенияСвойствОбъектов.НайтиПоНаименованию("НашБренд");

---

Резюме: какой метод выбрать?

Подведем итоги анализа различных вариантов программного открытия форм документов:

  1. Если документ записан, всегда используйте метод ОткрытьФорму() с параметром "Ключ". Это самый быстрый и надежный стандартный путь.
  2. Если документ не записан и у вас есть доступ к изменению кода конфигурации, используйте штатный механизм параметров заполнения "ЗначенияЗаполнения".
  3. Если данных для заполнения нового документа очень много (большие таблицы), используйте связку с Временным хранилищем.
  4. Если вы работаете в рамках внешней обработки без изменения конфигурации, используйте связку ДанныеФормыВЗначение() и ЗначениеВДанныеФормы().
  5. Категорически избегайте использования метода ПолучитьФорму().Открыть() в современном коде управляемого приложения, так как он снижает производительность вашей системы и может привести к ошибкам выполнения.
← На главную