Как отменить запись документа, если пользователь отказался от создания связанного объекта?

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

В практике разработки на платформе 1С:Предприятие 8.3 часто возникает ситуация, когда бизнес-логика требует неразрывной связи между созданием двух объектов. Например, при записи текущего «Взаимодействия» система должна предложить пользователю создать «Сделку». Если пользователь соглашается, открывается форма новой сделки. Но что делать, если пользователь передумал и закрыл форму сделки, не записав её? Как в этот момент «откатить» запись исходного взаимодействия?

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

Проблема использования стандартного события ПередЗаписью

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

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

Решение №1: Подмена стандартной команды записи

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

  1. В свойствах формы документа (например, Взаимодействия) перейдем в список команд и найдем стандартные команды Записать и ЗаписатьИЗакрыть.
  2. Уберем видимость стандартных кнопок на командной панели.
  3. Создадим свою команду, назовем её, например, ЗаписатьСПроверкойСделки, и вынесем её на форму как основную кнопку.
  4. В обработчике этой команды мы будем сначала задавать вопрос пользователю, а саму запись инициировать программно только при выполнении всех условий.

Посмотрим на пример кода для такой команды:


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

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

Решение №2: Использование ОписанияОповещения при закрытии формы

Для того чтобы понять, была ли создана сделка, мы используем механизм ОписаниеОповещения. Когда форма сделки закрывается, она может вернуть результат в вызывающую форму (наше Взаимодействие). Выясним причину, по которой форма была закрыта: была ли это запись или просто нажатие на «крестик».

В форме документа «Сделка» в процедуре ПослеЗаписи можно установить признак успешного завершения:


&НаКлиенте
Процедура ПослеЗаписи(ПараметрыЗаписи)
    // Указываем, что объект был успешно сохранен
    ОповеститьОЗаписи(Объект.Ссылка);
КонецПроцедуры

А в основной форме взаимодействия мы обрабатываем результат в процедуре, указанной в ОткрытьФорму:


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

Решение №3: Глобальный механизм Оповестить()

Если формы открываются независимо, можно использовать метод Оповестить(). Рассмотрим подробнее этот сценарий. Форма взаимодействия «подписывается» на события системы. Когда в любой части программы создается сделка с нужным основанием, взаимодействие получает сигнал.

В форме взаимодействия добавим обработчик события ОбработкаОповещения:


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

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

Решение №4: Паттерн «Черновик» (Draft)

В некоторых крупных системах (например, ERP или Документооборот) используется архитектурный подход со статусами. Проанализируем его преимущества. Вместо того чтобы мучительно пытаться не записать документ, мы записываем его со статусом Черновик или Подготовка.

Алгоритм работы следующий:

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

Этот метод считается наиболее надежным в распределенных системах и веб-клиенте, так как он устойчив к обрывам связи и закрытиям браузера. Пользователь может вернуться к созданию сделки позже, так как «черновик» взаимодействия уже существует.

Важные рекомендации по реализации

При выборе любого из методов следует помнить о нескольких критически важных моментах:

1. Модифицированность формы. Если вы используете программную запись ЭтотОбъект.Записать(), убедитесь, что форма не «зациклится». При вызове метода записи снова сработают события ПередЗаписью и ПриЗаписи. Поэтому в коде часто используют флаг-переменную (например, ПроверкаУжеВыполнена), чтобы избежать повторных вопросов пользователю.

2. Контроль транзакций. Никогда не пытайтесь открыть форму другого документа внутри серверной транзакции (в модуле объекта). Это приведет к ошибке, так как интерактивные действия на сервере запрещены.

3. Уникальный идентификатор. Если у пользователя может быть открыто несколько окон «Взаимодействий», всегда передавайте УникальныйИдентификатор формы во все вложенные структуры. Это позволит оповещениям находить именно то окно, которое инициировало процесс.

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

← На главную