При разработке в конфигурациях на обычных формах (например, УПП 1.3 или КА 1.1) часто возникает задача: при создании одного документа на основании другого необходимо запросить у пользователя дополнительные данные. Это может быть выбор типа стоимости, склада, ответственного или специфического режима заполнения табличной части. Основная сложность заключается в том, что стандартный механизм 1С разделяет логику заполнения данных и логику отображения интерфейса. Рассмотрим подробно, почему возникают трудности и как их правильно обойти.
Прежде всего, разберем архитектурное ограничение. Процедура ОбработкаЗаполнения находится в модуле объекта документа. Этот модуль предназначен для серверной логики — работы с базой данных, расчетов и заполнения реквизитов. В момент выполнения этой процедуры визуальной формы документа еще не существует в памяти как окна. Более того, документ может создаваться программно (обработкой или фоновым заданием), где никакого пользователя и интерфейса нет вовсе.
Попытка вызвать ОткрытьФорму() или любой другой интерактивный метод в модуле объекта приведет к ошибке выполнения. Поэтому нам необходимо перенести логику взаимодействия с пользователем в модуль формы создаваемого документа или в форму исходного документа.
Этот метод является наиболее распространенным решением, когда нужно вмешаться в процесс уже после того, как документ был создан и его форма начала инициализироваться. Проблема обычных форм в том, что если вызвать открытие другого окна прямо в процедуре ПриОткрытии, основная форма документа может "перекрыть" вспомогательное окно или фокус ввода установится некорректно.
Проанализируем алгоритм действий:
ПриОткрытии.Основание или наличие данных в табличных частях).Рассмотрим пример кода в модуле формы Документа2:
Процедура ПриОткрытии()
// Проверяем, что это новый документ и он создается на основании
Если ЭтоНовый() И ЗначениеЗаполнено(ПараметрОснование) Тогда
// Подключаем одноразовый обработчик ожидания
ПодключитьОбработчикОжидания("ВыполнитьИнтерактивныйВыбор", 0.1, Истина);
КонецЕсли;
КонецПроцедуры
Процедура ВыполнитьИнтерактивныйВыбор() Экспорт
// Настраиваем параметры открытия формы выбора
ПараметрыВыбора = Новый Структура("ВыборНаОсновании", Истина);
// Открываем форму модально или с описанием оповещения
Результат = ОткрытьФормуМодально("Документ.ОприходованиеМатериаловИзПроизводства.Форма.МС_ФормаВыбораТипаСтоимости", ПараметрыВыбора, ЭтаФорма);
// Если пользователь что-то выбрал, заполняем данные
Если Результат <> Неопределено Тогда
Для Каждого СтрокаТЧ Из ЭтотОбъект.Товары Цикл
СтрокаТЧ.ТипСтоимости = Результат;
КонецЦикла;
КонецЕсли;
КонецПроцедуры
Важный момент: использование ОткрытьФормуМодально в обычных формах позволяет остановить выполнение кода до момента закрытия вспомогательного окна, что значительно упрощает логику обработки результата.
Если в вашей конфигурации запрещено использование модальных окон (или вы хотите следовать современным практикам даже в обычных формах), следует использовать параметр РежимОткрытияОкнаФормы.БлокироватьОкноВладельца. Это позволит вспомогательной форме всегда оставаться поверх формы документа и не даст пользователю редактировать документ, пока выбор не сделан.
Рассмотрим, как изменить процедуру вызова:
Процедура ОткрытьФормаВыбораТипаСтоимости() Экспорт
// Указываем владельцем текущую форму
ФормаВыбора = ПолучитьФорму("Документ.ОприходованиеМатериаловИзПроизводства.Форма.МС_ФормаВыбораТипаСтоимости", , ЭтаФорма);
ФормаВыбора.РежимОткрытияОкнаФормы = РежимОткрытияОкнаФормы.БлокироватьОкноВладельца;
ФормаВыбора.Открыть();
КонецПроцедуры
В этом случае в самой форме выбора при нажатии кнопки "ОК" нужно будет обратиться к владельцу формы и вызвать у него экспортную процедуру для обновления данных. Например: ЭтаФорма.ВладелецФормы.ОбновитьДанныеДокумента(ВыбранноеЗначение);.
Проанализируем ситуацию с точки зрения архитектуры. Иногда правильнее вообще не открывать форму нового документа, пока не получены все параметры. Для этого мы можем создать свою команду "Создать на основании" в исходном документе (Документ1).
Пошаговый план реализации:
Заполнить() или через ДополнительныеСвойства.Пример реализации в форме исходного документа:
Процедура КомандаВвестиНаОснованииСВыбором(Кнопка)
// 1. Сначала спрашиваем пользователя
ТипСтоимости = ОткрытьФормуМодально("Документ.ОприходованиеМатериаловИзПроизводства.Форма.МС_ФормаВыбораТипаСтоимости");
Если ТипСтоимости = Неопределено Тогда
Возврат; // Пользователь передумал
КонецЕсли;
// 2. Создаем новый документ программно
НовыйДокумент = Документы.ОприходованиеМатериаловИзПроизводства.СоздатьДокумент();
// Передаем основание и параметры
НовыйДокумент.Заполнить(ЭтотОбъект.Ссылка);
// Дозаполняем выбранным значением
Для Каждого Стр Из НовыйДокумент.Товары Цикл
Стр.ТипСтоимости = ТипСтоимости;
КонецЦикла;
// 3. Открываем форму уже готового документа
ФормаНового = НовыйДокумент.ПолучитьФорму();
ФормаНового.Открыть();
КонецПроцедуры
Выясним причину, по которой пользователи часто жалуются на этот механизм. Часто бывает, что пользователь открыл форму выбора, понял, что создание на основании ему не нужно, и нажал "Отмена". Если вы используете Способ 1 (через форму нового документа), то у пользователя останется открытая форма пустого или недозаполненного документа.
Чтобы избежать этой ситуации, в коде обработки результата выбора добавьте проверку: если выбор не сделан (результат равен Неопределено), следует программно закрыть основную форму документа командой ЭтаФорма.Закрыть(Ложь);. Таким образом, процесс будет выглядеть бесшовным: нажали "Ввести на основании" -> появилось окно выбора -> нажали "Отмена" -> ничего не произошло.
Резюме: для обычных форм УПП наиболее стабильным и простым в реализации является метод с обработчиком ожидания и модальным окном. Он гарантирует, что интерфейс документа будет полностью инициализирован до того, как поверх него появится вспомогательный запрос.