При переходе с обычного приложения 1С (ОФ) на управляемое (УФ) начинающие разработчики часто сталкиваются с тем, что их привычные алгоритмы перестают работать или приводят к катастрофическому падению производительности системы. Один из классических примеров такого поведения — попытка получить данные документа или справочника путем обращения к его форме через метод ПолучитьФорму.
В этой статье мы подробно разберем, почему старый подход «в лоб» больше не применим в управляемом приложении, проанализируем возникающие проблемы и рассмотрим три правильных, оптимизированных сценария решения этой задачи согласно стандартам разработки фирмы «1С».
Рассмотрим код, который отлично функционировал в обычном приложении, но вызывает серьезные вопросы в управляемом:
ФормаЗалога = Документы.ЛМБ_ОперацияПоЗалогу.ПолучитьФорму("ФормаДокументаУправляемая");
ВидОперации = ФормаЗалога.Объект.ВидОперации;
Имущество = ФормаЗалога.Объект.Имущество;
ЗалогБилет = ФормаЗалога.Объект.ЗалоговыйБилет;
Дата = ФормаЗалога.Объект.Дата;
Попытка использовать этот алгоритм в управляемом интерфейсе приводит к следующим критическим последствиям:
ПолучитьФорму без передачи параметров конкретного объекта, платформа создаст форму абсолютно нового, еще не записанного документа. Все реквизиты (дата, вид операции, табличные части) окажутся пустыми или заполненными значениями по умолчанию. Именно поэтому автор темы на форуме столкнулся с тем, что код возвращает пустые данные.ПриСозданииНаСервере), сериализует весь программный интерфейс, элементы управления и условное оформление, а затем передает этот массив данных по сети на клиент. Выполнять эту титаническую работу ради чтения двух-трех реквизитов шапки — грубейшая ошибка оптимизации.Форма.Объект возвращает тип ДанныеФормыСтруктура, а его табличные части — ДанныеФормыКоллекция. Это не реальные объекты базы данных (не ДокументОбъект), а лишь интерфейсная проекция. Прямая работа с ними в общих модулях без конвертации невозможна.Выяснив причину проблемы, перейдем к рассмотрению правильных архитектурных решений.
Если перед вами стоит задача создать новый элемент справочника (например, Номенклатура) на основе существующего документа, и этот документ уже записан в базе данных, вам категорически запрещено обращаться к форме этого документа. Вместо этого мы должны работать исключительно с базой данных на сервере.
Разберем по шагам правильную реализацию с использованием механизма запросов. Этот подход гарантирует мгновенное извлечение данных без лишней нагрузки на сеть и сервер.
&НаСервереБезКонтекста
Процедура СоздатьНоменклатуруНаОснованииЗалогаНаСервере(СсылкаНаЗалог)
// Инициализируем запрос для получения только нужных полей
Запрос = Новый Запрос;
Запрос.Текст =
"ВЫБРАТЬ
| ЗалогИмущество.Ссылка.ВидОперации КАК ВидОперации,
| ЗалогИмущество.Ссылка.ЗалоговыйБилет КАК ЗалоговыйБилет,
| ЗалогИмущество.Ссылка.Дата КАК Дата,
| ЗалогИмущество.Вещь КАК Вещь,
| ЗалогИмущество.НомерСтроки КАК НомерСтроки
|ИЗ
| Документ.ЛМБ_ОперацияПоЗалогу.Имущество КАК ЗалогИмущество
|ГДЕ
| ЗалогИмущество.Ссылка = &СсылкаНаЗалог";
Запрос.УстановитьПараметр("СсылкаНаЗалог", СсылкаНаЗалог);
РезультатЗапроса = Запрос.Выполнить();
Если РезультатЗапроса.Пустой() Тогда
Возврат;
КонецЕсли;
Выборка = РезультатЗапроса.Выбрать();
// Обходим результаты запроса и программно создаем элементы справочника
Пока Выборка.Следующий() Цикл
НоменклатураОбъект = Справочники.Номенклатура.СоздатьЭлемент();
// Формируем наименование и артикул на основе данных документа
Префикс = Лев(СокрЛП(Выборка.ЗалоговыйБилет.Код), 2);
НомерЧислом = Выборка.ЗалоговыйБилет.Код; // Здесь может быть ваша функция очистки номера
ГодДокумента = Формат(Год(Выборка.Дата), "ЧГ=0");
АртикулНоменклатуры = Префикс + НомерЧислом + "/" + Выборка.НомерСтроки + "-" + ГодДокумента;
// Заполняем реквизиты нового элемента
НоменклатураОбъект.Артикул = АртикулНоменклатуры;
НоменклатураОбъект.Наименование = СокрЛП(Выборка.Вещь) + " (Залог)";
Попытка
НоменклатураОбъект.Записать();
Исключение
ЗаписьЖурналаРегистрации("Создание Номенклатуры", УровеньЖурналаРегистрации.Ошибка, , , ОписаниеОшибки());
КонецПопытки;
КонецЦикла;
КонецПроцедуры
Преимущества данного подхода: данные считываются напрямую из СУБД, игнорируя интерфейсные надстройки. Вы получаете колоссальный прирост производительности, особенно при пакетной обработке документов.
Проанализируем другую ситуацию: что делать, если пользователь прямо сейчас работает в открытой форме документа ЛМБ_ОперацияПоЗалогу и нажимает кнопку «Создать номенклатуру»?
В этом случае вам вообще не нужно вызывать метод получения формы, потому что вы уже находитесь внутри контекста этой формы! Все данные текущего документа доступны через встроенный реквизит управляемой формы Объект.
Рассмотрим, как правильно реализовать обработку нажатия кнопки в модуле формы документа:
&НаКлиенте
Процедура СоздатьНоменклатуруНажатие(Команда)
// Передаем управление на серверный контекст формы
СоздатьНоменклатуруНаСервере();
КонецПроцедуры
&НаСервере
Процедура СоздатьНоменклатуруНаСервере()
// Реквизит "Объект" содержит все актуальные данные формы (даже еще не записанные в базу данных!)
ТекущаяДата = Объект.Date;
БилетКод = Объект.ЗалоговыйБилет.Код;
Для Каждого СтрокаИмущества Из Объект.Имущество Цикл
НовыйЭлемент = Справочники.Номенклатура.СоздатьЭлемент();
НовыйЭлемент.Наименование = СтрокаИмущества.Вещь.Наименование;
НовыйЭлемент.Артикул = БилетКод + "/" + СтрокаИмущества.НомерСтроки;
// Дополнительное заполнение системных реквизитов...
НовыйЭлемент.Записать();
КонецЦикла;
ПоказатьОповещениеПользователя("Успешно", , "Элементы номенклатуры успешно созданы!");
КонецПроцедуры
Этот способ идеален для интерактивной работы, так как он позволяет использовать измененные пользователем, но еще не сохраненные в базу данных значения реквизитов формы.
Если бизнес-логика вашей конфигурации предполагает, что номенклатура рождается «из» документа залога, наиболее грамотным решением с точки зрения стандартов проектирования 1С является использование встроенного события ОбработкаЗаполнения.
Посмотрим на этот процесс по шагам:
Номенклатура.ЛМБ_ОперацияПоЗалогу.Теперь проанализируем пример кода, который необходимо написать в модуле объекта справочника Номенклатура:
Процедура ОбработкаЗаполнения(ДанныеЗаполнения, ТекстЗаполнения, СтандартнаяОбработка)
// Проверяем тип источника данных
Если ТипЗнч(ДанныеЗаполнения) = Тип("ДокументСсылка.ЛМБ_ОперацияПоЗалогу") Тогда
// Отключаем стандартный механизм платформы, чтобы полностью контролировать процесс
СтандартнаяОбработка = Ложь;
// Заполняем реквизиты создаваемой номенклатуры из переданной ссылки
ЭтотОбъект.Артикул = ДанныеЗаполнения.ЗалоговыйБилет.Код + "/1";
ЭтотОбъект.Наименование = "Номенклатура по залогу № " + ДанныеЗаполнения.ЗалоговыйБилет.Код;
// Установка значений по умолчанию
ЭтотОбъект.СтавкаНДС = Справочники.СтавкиНДС.БезНДС;
КонецЕсли;
КонецПроцедуры
Чтобы открыть форму создания новой номенклатуры на клиенте с уже заполненными данными, вам достаточно выполнить следующий изящный код (например, из формы списка документов):
&НаКлиенте
Процедура СоздатьНоменклатуруНаОсновании(СсылкаНаДокумент)
// Формируем параметры заполнения для новой формы
ПараметрыЗаполнения = Новый Структура("Основание", СсылкаНаДокумент);
ПараметрыФормы = Новый Структура("ЗначенияЗаполнения", ПараметрыЗаполнения);
// Открываем форму создания нового элемента
ОткрытьФорму("Справочник.Номенклатура.ФормаОбъекта", ПараметрыФормы);
КонецПроцедуры
Если вам все же по каким-то уникальным причинам интеграции или динамического построения интерфейса абсолютно необходимо открыть именно форму конкретного существующего документа и прочитать ее реквизиты, вам обязательно нужно передать параметр Ключ. Это укажет платформе, что нужно инициализировать форму не нового, а конкретного сохраненного в базе данных документа.
Давайте посмотрим, как выглядит этот код:
&НаКлиенте
Процедура ПолучитьДанныеФормыЧерезКлюч(СсылкаНаДокумент)
ПараметрыФормы = Новый Структура;
ПараметрыФормы.Вставить("Ключ", СсылкаНаДокумент);
// Получаем форму конкретного документа
ФормаДокумента = ПолучитьФорму("Документ.ЛМБ_ОперацияПоЗалогу.Форма.ФормаДокументаУправляемая", ПараметрыФормы);
// Теперь реквизиты формы инициализированы данными из БД
ВидОперации = ФормаДокумента.Объект.ВидОперации;
ДатаДокумента = ФормаДокумента.Объект.Дата;
// Внимание: метод ПолучитьФорму не открывает ее на экране автоматически.
// Если вам нужно показать форму пользователю, используйте: ФормаДокумента.Открыть();
КонецПроцедуры
Важное предупреждение: используйте этот метод только в крайних случаях, когда обойтись запросом к базе данных или механизмами ввода на основании действительно невозможно. Во всех остальных ситуациях всегда отдавайте предпочтение Сценарию №1 (Запросам).
Применяя данные стандарты разработки, вы избавите свои конфигурации от лишних серверных вызовов, зависаний интерфейса и гарантируете корректную работу логики базы данных при любых обновлениях платформы 1С.