В практике разработки на платформе 1С:Предприятие 8.3 часто возникает задача: необходимо программно создать новый элемент справочника или документ, заполнить его реквизиты по определенному алгоритму и открыть его форму пользователю. При этом крайне важно не записывать объект в базу данных преждевременно — пользователь должен сам решить, сохранять этот объект или закрыть форму без записи.
Многие начинающие разработчики используют «наивный» подход: получают форму программно, вытаскивают из нее объект, заполняют его реквизиты на клиенте или сервере, копируют данные обратно в форму и открывают ее. Однако при таком подходе они сталкиваются со следующей проблемой: визуальные элементы формы (видимость полей, доступность кнопок, состав колонок табличных частей) настраиваются некорректно. Выясним причину этой проблемы и рассмотрим правильные архитектурные решения.
Давайте проанализируем жизненный цикл инициализации управляемой формы. Когда мы вызываем метод ПолучитьФорму или ОткрытьФорму, платформа немедленно запускает на сервере обработчик события формы ПриСозданииНаСервере. Именно в этом обработчике в типовых конфигурациях (например, ERP, Комплексная автоматизация, Управление торговлей) сосредоточена вся логика настройки интерфейса. Код анализирует текущие значения реквизитов объекта и на их основе скрывает или отображает нужные поля.
Если мы сначала получаем форму, а уже затем начинаем заполнять ее объект данными через метод КопироватьДанныеФормы, то на момент выполнения ПриСозданииНаСервере объект внутри формы еще абсолютно пуст. Форма настраивается под пустые значения реквизитов. Последующее копирование данных в Форма.Объект заполняет реквизиты, но интерфейс формы автоматически не перестраивается, так как процедуры настройки уже отработали. В результате пользователь видит на экране путаницу: данные в полях есть, но связанные с ними кнопки или зависимые поля остаются скрытыми.
Рассмотрим три надежных и проверенных способа решения этой проблемы, которые позволяют передать данные в объект до момента настройки интерфейса управляемой формы.
Это самый простой и изящный способ, заложенный в саму архитектуру платформы 1С. Мы можем передать структуру заполняемых реквизитов напрямую в параметры открываемой формы. Платформа обработает их автоматически еще до того, как начнется построение интерфейса.
Разберем по шагам, как это выглядит в коде. Вместо получения формы и ручного копирования данных, мы подготавливаем структуру параметров и вызываем функцию ОткрытьФорму:
ПараметрыЗаполнения = Новый Структура;
ПараметрыЗаполнения.Вставить("ТипНоменклатуры", ПредопределенноеЗначение("Перечисление.ТипыНоменклатуры.Товар"));
ПараметрыЗаполнения.Вставить("СтавкаНДС", ПредопределенноеЗначение("Перечисление.СтавкаНДС.НДС20"));
ПараметрыЗаполнения.Вставить("Наименование", "Новый тестовый товар");
ПараметрыФормы = Новый Структура("ЗначенияЗаполнения", ПараметрыЗаполнения);
ОткрытьФорму("Справочник.Номенклатура.ФормаОбъекта", ПараметрыФормы);
При таком подходе платформа выполняет следующие действия:
ЗначенияЗаполнения в стандартный обработчик объекта ОбработкаЗаполнения.ПриСозданииНаСервере. К этому моменту объект уже заполнен нашими значениями, и визуальная настройка интерфейса отрабатывает абсолютно корректно.Метод со стандартным заполнением реквизитов отлично подходит для простых сценариев. Однако, если логика заполнения нового объекта очень сложная (требует обращения к базам данных, расчетов или заполнения связанных табличных частей), простой передачи структуры может не хватить. В этом случае мы можем использовать механизм расширений конфигурации для перехвата модуля создаваемого объекта.
Выясним, как это реализовать. Нам нужно передать в параметр уникальный ключ-признак, по которому мы поймем, что объект создается именно нашей обработкой, и запустить кастомный алгоритм заполнения в модуле объекта.
Шаг 1. На клиенте вызываем открытие формы и передаем структуру параметров с нашим уникальным идентификатором:
ПараметрыЗаполнения = Новый Структура;
ПараметрыЗаполнения.Вставить("КлючСобственногоЗаполнения", Истина);
ПараметрыЗаполнения.Вставить("ИсходныйДокумент", СсылкаНаДокументОснование);
ПараметрыФормы = Новый Структура("ЗначенияЗаполнения", ПараметрыЗаполнения);
ОткрытьФорму("Документ.ЭтапПроизводства2_2.ФормаОбъекта", ПараметрыФормы);
Шаг 2. В расширении для нужного объекта (например, документа «Этап производства») заимствуем процедуру ОбработкаЗаполнения и добавляем аннотацию &Перед:
&Перед("ОбработкаЗаполнения")
Процедура Расш_ОбработкаЗаполнения(ДанныеЗаполнения, СтандартнаяОбработка)
Если ТипЗнч(ДанныеЗаполнения) = Тип("Структура")
И ДанныеЗаполнения.Свойство("КлючСобственногоЗаполнения") Тогда
// Вызываем нашу собственную процедуру заполнения объекта на сервере
ЗаполнитьНовыйДокументПоСвоимПравилам(ДанныеЗаполнения.ИсходныйДокумент);
// Если стандартное заполнение платформы больше не требуется, можно отключить его
// СтандартнаяОбработка = Ложь;
КонецЕсли;
КонецПроцедуры
При такой реализации весь сложный код инициализации объекта отработает в модуле самого объекта непосредственно перед созданием формы. Форма откроется полностью готовой, а все ее элементы интерфейса перестроятся в соответствии со значениями, которые мы записали в объект.
При передаче данных через параметры формы мы можем столкнуться с ограничением платформы: таблицы значений и сложные объекты, не поддерживающие XML-сериализацию, невозможно напрямую передать в параметрах с клиента на сервер. Если нам нужно передать в форму нового незаписанного объекта сложную структуру (например, готовую таблицу с десятками строк для табличной части), на помощь приходит Временное хранилище.
Рассмотрим подробнее этот паттерн проектирования:
Шаг 1. Формируем структуру данных на сервере в вызывающем коде, помещаем ее во временное хранилище и получаем уникальный адрес:
// Выполняется на сервере в модуле нашей внешней обработки или вызова
&НаСервере
Функция ПодготовитьДанныеВоВременномХранилище()
СтруктураДанных = Новый Структура;
СтруктураДанных.Вставить("ОсновнойСклад", Справочники.Склады.НайтиПоКоду("00001"));
// Создаем и заполняем таблицу товаров
ТаблицаТоваров = Новый ТаблицаЗначений;
ТаблицаТоваров.Колонки.Добавить("Номенклатура");
TaблицаТоваров.Колонки.Добавить("Количество");
НоваяСтрока = ТаблицаТоваров.Добавить();
НоваяСтрока.Номенклатура = Справочники.Номенклатура.НайтиПоРеквизиту("Артикул", "ART-001");
НоваяСтрока.Количество = 10;
СтруктураДанных.Вставить("ТаблицаТоваров", ТаблицаТоваров);
// Помещаем структуру во временное хранилище
АдресХранилища = ПоместитьВоВременноеХранилище(СтруктураДанных, Новый УникальныйИдентификатор);
Возврат АдресХранилища;
КонецФункции
Шаг 2. На клиенте передаем полученный адрес хранилища в качестве параметра при открытии формы:
&НаКлиенте
Процедура СоздатьИОткрытьНовыйДокумент()
АдресДанных = ПодготовитьДанныеВоВременномХранилище();
ПараметрыЗаполнения = Новый Структура;
ПараметрыЗаполнения.Вставить("АдресДанныхЗаполнения", АдресДанных);
ПараметрыФормы = Новый Структура("ЗначенияЗаполнения", ПараметрыЗаполнения);
ОткрытьФорму("Документ.ЗаказКлиента.ФормаОбъекта", ПараметрыФормы);
КонецПроцедуры
Шаг 3. В расширении модуля объекта перехватываем ОбработкаЗаполнения, считываем данные из временного хранилища по переданному адресу и переносим их в создаваемый объект:
&Перед("ОбработкаЗаполнения")
Процедура Расш_ОбработкаЗаполненияИзХранилища(ДанныеЗаполнения, СтандартнаяОбработка)
Если ТипЗнч(ДанныеЗаполнения) = Тип("Структура")
И ДанныеЗаполнения.Свойство("АдресДанныхЗаполнения") Тогда
Адрес = ДанныеЗаполнения.АдресДанныхЗаполнения;
Если ЭтоАдресВременногоХранилища(Адрес) Тогда
// Извлекаем структуру из памяти сервера
ДанныеОбъекта = ПолучитьИзВременногоХранилище(Адрес);
// Заполняем реквизиты объекта
ЭтотОбъект.Склад = ДанныеОбъекта.ОсновнойСклад;
// Заполняем табличную часть документа
ЭтотОбъект.Товары.Очистить();
Для Каждого СтрокаТЧ Из ДанныеОбъекта.ТаблицаТоваров Цикл
НоваяСтрока = ЭтотОбъект.Товары.Добавить();
ЗаполнитьЗначенияСвойств(НоваяСтрока, СтрокаТЧ);
КонецЦикла;
КонецЕсли;
КонецЕсли;
КонецПроцедуры
Этот способ является наиболее надежным и производительным для работы в клиент-серверной архитектуре управляемого приложения, так как тяжелые структуры данных не передаются по сети между клиентом и сервером в виде параметров формы, а напрямую обрабатываются в оперативной памяти сервера приложений 1С.
При реализации вышеописанных подходов обратите внимание на следующие важные архитектурные особенности:
ОбработкаЗаполнения, платформа автоматически выставляет открываемой форме признак модифицированности (в заголовке окна появляется символ «*»). Это очень удобно: если пользователь решит закрыть форму, система сама вежливо спросит его, нужно ли сохранять изменения.ЗначенияЗаполнения без необходимости писать какой-либо программный код в обработчиках.Используя данные паттерны разработки, вы избавитесь от необходимости «ручной» доработки визуального отображения элементов формы и сможете гарантировать стабильную работу логики интерфейса даже в сложных типовых конфигурациях.