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