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