При разработке в среде 1С:Предприятие 8.3 на управляемых формах часто возникает задача создания нового документа на основании предопределенного шаблона. Шаблоном обычно выступает элемент специального справочника, содержащий тот же набор реквизитов и табличных частей, что и сам документ. Начинающие разработчики часто совершают архитектурную ошибку, пытаясь заполнить реквизиты нового документа непосредственно на клиенте сразу после выбора шаблона. В этой статье мы подробно разберем, почему такой подход неэффективен, и рассмотрим правильные, соответствующие стандартам фирмы «1С» методы реализации этой задачи.
В сообщениях на форуме автору предлагают использовать метод ПолучитьФорму и напрямую присваивать значения реквизитам формы на клиенте:
Формадок = ПолучитьФорму("Документ.Соглашение.ФормаОбъекта");
Формадок.Объект.Контрагент = Результат.Контрагент;
Проанализируем ситуацию и выясним, почему этот код является антипаттерном и приведет к проблемам в реальной базе данных:
Результат.Контрагент) на клиенте заставляет платформу выполнять неявный синхронный запрос к серверу для получения данных из базы. Если реквизитов много, форма будет открываться крайне медленно, создавая избыточную нагрузку на сеть и сервер.Для решения этой задачи в платформе 1С предусмотрен штатный, быстрый и безопасный механизм заполнения через параметр ЗначенияЗаполнения и обработчик события ОбработкаЗаполнения на стороне сервера.
Рассмотрим самый правильный способ реализации. Он заключается в том, что мы передаем ссылку на выбранный шаблон в параметрах открытия формы нового документа. Платформа 1С при этом автоматически создает новый объект на сервере и передает эту ссылку в обработчик заполнения модуля объекта.
Сначала настроим открытие формы выбора шаблона и последующее открытие формы нового документа с передачей параметров. Разберем код:
&НаКлиенте
Процедура СоздатьПоШаблону(Команда)
// Открываем форму выбора справочника-шаблона
ПараметрыВыбора = Новый Структура;
ОписаниеОповещения = Новый ОписаниеОповещения("ВыборШаблонаЗавершение", ЭтотОбъект);
ОткрытьФорму(
"Справочник.ШаблонСоглашение.ФормаВыбора",
ПараметрыВыбора,
ЭтотОбъект,
УникальныйИдентификатор,
,
,
ОписаниеОповещения,
РежимОткрытияОкнаФормы.БлокироватьОкноВладельца
);
КонецПроцедуры
&НаКлиенте
Процедура ВыборШаблонаЗавершение(РезультатВыбора, ДополнительныеПараметры) Экспорт
// Если пользователь закрыл форму выбора без выбора значения, прерываем выполнение
Если РезультатВыбора = Неопределено Тогда
Возврат;
КонецЕсли;
// Помещаем ссылку на шаблон в специальную структуру параметров заполнения.
// Ключ "Шаблон" мы будем обрабатывать в модуле объекта документа.
ПараметрыЗаполнения = Новый Структура("Шаблон", РезультатВыбора);
// Оборачиваем структуру в предопределенный параметр платформы "ЗначенияЗаполнения"
ПараметрыФормы = Новый Структура("ЗначенияЗаполнения", ПараметрыЗаполнения);
// Открываем форму нового документа. Платформа сама инициирует создание объекта на сервере.
ОткрытьФорму("Документ.Соглашение.ФормаОбъекта", ПараметрыФормы);
КонецПроцедуры
Теперь перейдем в конфигуратор, откроем Модуль объекта документа Соглашение и найдем (или создадим) предопределенную процедуру ОбработкаЗаполнения. Вся серверная логика по чтению данных шаблона и переносу их в новый документ должна находиться здесь:
Процедура ОбработкаЗаполнения(ДанныеЗаполнения, ТекстЗаполнения, СтандартнаяОбработка)
// Проверяем, что к нам пришла именно структура параметров и в ней передан наш шаблон
Если ТипЗнч(ДанныеЗаполнения) = Тип("Структура")
И ДанныеЗаполнения.Свойство("Шаблон") Тогда
ШаблонСсылка = ДанныеЗаполнения.Шаблон;
// Убедимся, что ссылка на шаблон заполнена и существует в базе данных
Если ЗначениеЗаполнено(ШаблонСсылка) Тогда
// Отключаем стандартную обработку платформы, так как заполнение мы контролируем полностью сами
СтандартнаяОбработка = Ложь;
// Заполняем реквизиты шапки документа значениями реквизитов шаблона.
// Четвертым параметром функции ЗаполнитьЗначенияСвойств исключаем служебные реквизиты,
// которые у нового документа должны формироваться индивидуально.
ЗаполнитьЗначенияСвойств(ЭтотОбъект, ШаблонСсылка, , "Ссылка, Номер, Дата, Проведен, ПометкаУдаления");
// Заполняем табличную часть "Товары" из шаблона
Товары.Очистить();
Для Каждого СтрокаШаблона Из ШаблонСсылка.Товары Цикл
НоваяСтрока = Товары.Добавить();
ЗаполнитьЗначенияСвойств(НоваяСтрока, СтрокаШаблона);
КонецЦикла;
// Если в документе есть другие табличные части, заполняем их аналогично
// Для Каждого СтрокаУслуг Из ШаблонСсылка.Услуги Цикл
// НоваяСтрока = Услуги.Добавить();
// ЗаполнитьЗначенияСвойств(НоваяСтрока, СтрокаУслуг);
// КонецЦикла;
КонецЕсли;
КонецЕсли;
КонецПроцедуры
Преимущества данного метода: этот алгоритм работает на сервере в рамках единой транзакции создания объекта. Мы не дергаем базу данных по сети ради каждого реквизита. Функция ЗаполнитьЗначенияСвойств работает максимально быстро, автоматически сопоставляя имена и типы реквизитов источника и приемника.
Если ваш документ Соглашение простой, не содержит табличных частей, а вам нужно перенести только реквизиты шапки (например, Контрагент, Склад, Организация), вы можете воспользоваться встроенной возможностью платформы 1С вообще без написания кода в модуле объекта.
Выполним следующие шаги:
Соглашение.Контрагент) и откройте его палитру свойств.
&НаКлиенте
Процедура ВыборШаблонаПростойЗавершение(РезультатВыбора, ДополнительныеПараметры) Экспорт
Если РезультатВыбора = Неопределено Тогда
Возврат;
КонецЕсли;
// Получаем данные реквизитов шаблона за один быстрый серверный вызов
ДанныеШаблона = ПолучитьДанныеШаблонаНаСервере(РезультатВыбора);
// Ключи структуры должны совпадать с именами реквизитов документа
ПараметрыЗаполнения = Новый Структура;
ПараметрыЗаполнения.Вставить("Контрагент", ДанныеШаблона.Контрагент);
ПараметрыЗаполнения.Вставить("Склад", ДанныеШаблона.Склад);
ПараметрыФормы = Новый Структура("ЗначенияЗаполнения", ПараметрыЗаполнения);
ОткрытьФорму("Документ.Соглашение.ФормаОбъекта", ПараметрыФормы);
КонецПроцедуры
&НаСервереБезКонтекста
Функция ПолучитьДанныеШаблонаНаСервере(СсылкаНаШаблон)
Данные = Новый Структура("Контрагент, Склад");
// Считываем данные объекта за один запрос
ЗаполнитьЗначенияСвойств(Данные, СсылкаНаШаблон);
Возврат Данные;
EndФункции
При открытии формы платформа 1С автоматически проанализирует структуру ЗначенияЗаполнения, найдет реквизиты документа с включенным свойством «Заполнять из данных заполнения» и подставит туда переданные значения.
Бывают ситуации, когда конфигурация находится на строгой поддержке, и мы не можем вносить изменения в модуль объекта документа (например, при отсутствии возможности использовать расширения конфигурации). В этом случае можно прочитать данные шаблона на сервере, передать их на клиент в виде структуры и заполнить реквизиты формы сразу после ее открытия.
Посмотрим, как реализовать этот обходной сценарий правильно и безопасно:
&НаКлиенте
Процедура ПослеВыбораШаблона(РезультатВыбора)
Если РезультатВыбора = Неопределено Тогда
Возврат;
КонецЕсли;
// Шаг 1: Получаем все необходимые данные шаблона с сервера за один вызов
СтруктураДанных = ПолучитьВсеДанныеШаблона(РезультатВыбора);
// Шаг 2: Получаем форму нового документа (она создается пустой)
ФормаДокумента = ПолучитьФорму("Документ.Соглашение.ФормаОбъекта");
// Шаг 3: Копируем данные из структуры в реквизиты формы на клиенте
// Важно: Мы обращаемся к реквизитам структуры, а не к ссылке базы данных! Это быстро.
ЗаполнитьЗначенияСвойств(ФормаДокумента.Объект, СтруктураДанных);
// Шаг 4: Заполняем табличную часть на клиенте
Для Каждого СтрокаТаблицы Из СтруктураДанных.ТаблицаТовары Цикл
НоваяСтрокаФормы = ФормаДокумента.Объект.Товары.Добавить();
ЗаполнитьЗначенияСвойств(НоваяСтрокаФормы, СтрокаТаблицы);
КонецЦикла;
// Шаг 5: Открываем готовую и заполненную форму пользователю
ФормаДокумента.Открыть();
КонецПроцедуры
&НаСервереБезКонтекста
Функция ПолучитьВсеДанныеШаблона(ШаблонСсылка)
Результат = Новый Структура;
// Считываем реквизиты шапки
Результат.Вставить("Контрагент", ШаблонСсылка.Контрагент);
Результат.Вставить("Склад", ШаблонСсылка.Склад);
Результат.Вставить("Организация", ШаблонСсылка.Организация);
// Считываем табличную часть в массив структур для передачи на клиент
МассивТоваров = Новый Массив;
Для Каждого СтрокаТЧ Из ШаблонСсылка.Товары Цикл
СтруктураСтроки = Новый Структура("Номенклатура, Количество, Цена, Сумма");
ЗаполнитьЗначенияСвойств(СтруктураСтроки, СтрокаТЧ);
МассивТоваров.Добавить(СтруктураСтроки);
КонецЦикла;
Результат.Вставить("ТаблицаТовары", МассивТоваров);
Возврат Результат;
КонецФункции
Этот метод уступает первому варианту в изящности, однако он полностью решает проблему «треш-вызовов» сервера, так как все данные из базы считываются за одно серверное обращение ПолучитьВсеДанныеШаблона, а на клиенте происходит лишь заполнение локальных структур формы.
Подводя итоги, сформулируем основные правила создания документов по шаблонам на управляемых формах 1С:
ВыбранныйШаблон.Контрагент). Всегда получайте эти данные на сервере.ЗначенияЗаполнения при открытии формы и обработчик события ОбработкаЗаполнения в модуле объекта.ЗаполнитьЗначенияСвойств для быстрого копирования реквизитов между объектами с похожей структурой. Не забывайте исключать служебные реквизиты (такие как Номер, Дата, Ссылка) из списка копирования.СтандартнаяОбработка = Ложь.