Как при записи элемента справочника задать вопрос и открыть форму нового документа в 1С на управляемых формах

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

В практике разработки на платформе 1С:Предприятие 8 часто возникает бизнес-требование: при записи элемента справочника предложить пользователю создать связанный с ним документ. Например, при создании нового контрагента система может предложить сразу зарегистрировать договор с ним, а при записи новой номенклатуры — оформить установку цен.

Реализация этой задачи на управляемых формах (УФ) скрывает в себе несколько неочевидных архитектурных нюансов. С одной стороны, объект должен гарантированно записаться в базу данных, чтобы мы могли получить его ссылку. С другой стороны, нам нужно корректно обработать поведение интерфейса при использовании стандартной кнопки «Записать и закрыть», а также обойти ограничения асинхронной работы платформы. В этой статье мы подробно разберем, как правильно реализовать данный сценарий без «костылей» и лишней нагрузки на базу данных.

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

---

В чем сложность? Разбираем подводные камни

Прежде чем перейти к коду, выясним причину, почему простая вставка вопроса в событие ПослеЗаписи может привести к ошибкам в реальной работе пользователей:

  1. Проблема асинхронности в веб-клиенте и тонком клиенте. Начиная с версии платформы 8.3, стандартные модальные окна заблокированы. Вызов диалогов происходит через асинхронные методы (например, ПоказатьВопрос или ВопросАсинх). Эти методы не останавливают выполнение кода. Платформа отправляет запрос на показ окна пользователю и сразу же продолжает выполнение кода дальше.
  2. Поведение кнопки «Записать и закрыть». Если пользователь нажимает кнопку «Записать и закрыть», платформа отрабатывает транзакцию записи, вызывает событие ПослеЗаписи, инициирует показ вопроса и... закрывает форму элемента. В результате диалоговое окно вопроса закрывается вместе с формой, так и не дождавшись ответа пользователя.
  3. Проблема незаписанного (нового) элемента. Если элемент справочника создается впервые, у него еще нет ссылки в базе данных. Передать незаписанный элемент в новый документ невозможно. Следовательно, логику открытия документа нужно привязывать строго к моменту, когда транзакция записи успешно завершена.

Рассмотрим два надежных способа обхода этих ограничений.

---

Способ 1. Перехват процесса записи в событии «ПередЗаписью» (классический надежный метод)

Суть метода заключается в том, чтобы перехватить первую попытку записи в событии ПередЗаписью на клиенте, программно отменить ее, задать вопрос пользователю в немодальном режиме и затем, в зависимости от ответа, повторно запустить запись с нужными флагами.

Для реализации этого способа нам понадобятся три переменные на уровне модуля управляемой формы справочника. Разместим их в самом начале модуля формы:


&НаКлиенте
Перем ВопросБылЗадан;
Перем СоздатьДокументПослеЗаписи;
Перем СохраненныеПараметрыЗаписи;

Теперь разберем по шагам алгоритм работы интерактивной схемы:

Шаг 1. Перехват в событии ПередЗаписью на клиенте

В обработчике события ПередЗаписью мы проверяем, задавали ли мы уже вопрос пользователю. Если нет — отменяем запись, запоминаем параметры записи и задаем вопрос асинхронно.


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

Шаг 2. Обработка ответа пользователя в процедуре оповещения

В созданной нами процедуре обратного вызова мы анализируем ответ пользователя. Если пользователь согласен, мы устанавливаем флаг СоздатьДокументПослеЗаписи в значение Истина. В любом случае мы взводим флаг ВопросБылЗадан и вызываем запись повторно.


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

Шаг 3. Обработка флага в событии ПослеЗаписи

В событии ПослеЗаписи объект уже гарантированно сохранен в информационной базе. Мы можем безопасно получить ссылку на него через Объект.Ссылка и открыть форму документа.


&НаКлиенте
Процедура ПослеЗаписи(ПараметрыЗаписи)
    
    Если СоздатьДокументПослеЗаписи = Истина Тогда
        СоздатьДокументПослеЗаписи = Ложь; // Сбрасываем флаг
        ОткрытьФормуНовогоДокумента(Объект.Ссылка);
    КонецЕсли;
    
КонецПроцедуры

Этот метод считается наиболее отказоустойчивым, так как он корректно работает со всеми стандартными командами формы (включая закрытие при записи) и не оставляет «подвисших» в памяти окон диалогов.

---

Способ 2. Использование современных асинхронных вызовов в «ПослеЗаписи» (для платформ 8.3.18 и выше)

Если ваша конфигурация работает на современной версии платформы, поддерживающей ключевые слова Асинх и Ждать, мы можем решить задачу гораздо изящнее напрямую в процедуре ПослеЗаписи. Посмотрим на этот пример:


&НаКлиенте
Процедура ПослеЗаписи(ПараметрыЗаписи)
    ЗапуститьДиалогИСозданиеДокумента();
КонецПроцедуры

&НаКлиенте
Асинх Процедура ЗапуститьДиалогИСозданиеДокумента()
    
    // Ждем 1 миллисекунду, чтобы вернуть управление платформе. 
    // Это позволит форме элемента закрыться штатно, если была нажата кнопка "Записать и закрыть"
    Ждать 1; 
    
    Ответ = Ждать ВопросАсинх("Сформировать документ на основании записанного элемента?", РежимДиалогаВопрос.ДаНет);
    
    Если Ответ = КодВозвратаДиалога.Да Тогда
        ОткрытьФормуНовогоДокумента(Объект.Ссылка);
    КонецЕсли;
    
КонецПроцедуры

Конструкция Ждать 1 передает управление обратно платформе на мгновение. За это время форма справочника успевает закрыться (если закрытие требовалось процедурой записи), а последующий вызов ВопросАсинх открывает окно вопроса уже в контексте главного окна приложения, не привязываясь к закрывающейся форме.

---

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

Частая ошибка начинающих разработчиков — программно создавать новый документ на сервере, заполнять его реквизиты, записывать в базу данных и только потом открывать форму этого документа пользователю. Это приводит к засорению базы данных «мусорными» (пустыми) документами, если пользователь в итоге решит отказаться от работы и закроет форму нового документа без сохранения.

Правильный архитектурный подход — открыть форму абсолютно нового, еще не записанного в базу документа на клиенте и передать структуру заполнения через специальный параметр платформы ЗначенияЗаполнения.

Рассмотрим структуру вызова такой формы на клиенте:


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

Когда платформа получает параметр ЗначенияЗаполнения, она автоматически выполняет следующие действия:

  1. Создает в памяти новый объект документа.
  2. Вызывает стандартный обработчик ОбработкаЗаполнения в модуле объекта нового документа, передавая туда нашу структуру в качестве параметра ДанныеЗаполнения.
  3. Инициализирует форму документа, где реквизиты уже заполнены переданными значениями.

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


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

Таким образом, мы получаем идеально чистый интерфейс: пользователь видит заполненный документ на экране, может внести в него корректировки или закрыть форму без сохранения, при этом в базе данных не создается никаких лишних записей.

---

Резюме

Мы подробно разобрали два подхода к реализации интерактивных сценариев при записи объектов в 1С. Использование перехвата в событии ПередЗаписью гарантирует совместимость со старыми версиями платформы и стабильную работу в любых режимах запуска. Применение асинхронного метода с микро-задержкой Ждать 1 упрощает код на современных релизах платформы. Передача данных через ЗначенияЗаполнения сохраняет целостность базы данных и отвечает лучшим стандартам разработки фирмы 1С.

← На главную