Как прервать создание документа на основании и почему нельзя использовать ЭтотОбъект.Удалить() в 1С?

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

Часто в практике разработки на платформе 1С:Предприятие возникает задача: запретить пользователю создавать один документ на основании другого, если определенные условия не выполнены. Например, если на основании заказа покупателя уже был создан документ резервирования, повторное создание должно быть заблокировано. Начинающие программисты иногда пытаются решить эту задачу "в лоб", используя метод Удалить() прямо в процедуре ОбработкаЗаполнения, что приводит к ошибке выполнения кода. Разберем эту ситуацию подробно, проанализируем причины возникновения ошибки и посмотрим на правильные способы решения задачи.

Почему вызов ЭтотОбъект.Удалить() выдает ошибку?

Давайте проанализируем логику работы платформы в момент ввода на основании. Когда пользователь нажимает кнопку "Создать на основании", платформа инициализирует новый объект в оперативной памяти. Этот объект еще не записан в базу данных, у него нет ссылки, он не существует в таблицах СУБД.

В этот момент вызывается обработчик модуля объекта ОбработкаЗаполнения. В коде, который приводит к ошибке, разработчик пытается сделать следующее:


Иначе    
    Предупреждение ("На основании этого заказа уже был создан документ!");
    ЭтотОбъект.Удалить();
КонецЕсли; 

Метод Удалить() предназначен для физического удаления объекта из базы данных. Пытаясь вызвать его для нового объекта, мы заставляем платформу искать в базе данных запись, которой там еще нет. Это все равно, что пытаться снять наждачной бумагой информацию с пустого жесткого диска. Именно поэтому платформа выдает ошибку. Если бы мы действительно хотели что-то удалить, нам следовало бы сначала убедиться, что объект существует:


Если Не ЭтотОбъект.ЭтоНовый() Тогда
   ЭтотОбъект.Удалить();
КонецЕсли;

Однако в контексте процедуры заполнения нового документа это бессмысленно, так как объект гарантированно является новым.

Решение 1: Официальный стандарт 1С с использованием исключений

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

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

Посмотрим на пример правильной реализации в модуле объекта:


Если РезультатЗапроса.Пустой() Тогда     
    // Логика заполнения нового документа...
Иначе    
    ТекстОшибки = "На основании этого заказа уже был создан документ *Резервирование товаров по ячейкам*!";
    ВызватьИсключение ТекстОшибки;
КонецЕсли;

Что происходит при вызове исключения? Платформа немедленно прерывает выполнение текущего кода, откатывает транзакцию (если она была начата) и выводит пользователю на экран модальное окно с текстом ошибки. Процесс создания нового документа останавливается, и "мусорный" объект не попадает в базу. Для наглядного исследования последовательности срабатывания процедур и транзакций можно использовать обработку для анализа порядка вызова событий 1С.

Решение 2: Перенос проверки на уровень интерфейса (команды или формы)

Хотя вызов исключения и соответствует стандартам 1С, появление "красного" окна с ошибкой (которое многие пользователи воспринимают как сбой программы) не всегда является лучшим вариантом взаимодействия (UX). Разберем по шагам альтернативные и более "мягкие" способы отмены.

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

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

Важные архитектурные нюансы и подводные камни

Проанализируем ситуацию чуть глубже с точки зрения бизнес-логики и многопользовательской работы.

Во-первых, частичное выполнение основания. Жесткий запрет на создание второго документа часто является "костылем". Выясним причину: а что, если в первом документе зарезервировали не все товары по заказу? Например, на складе не хватило остатков. В таких случаях система должна позволять вводить второй документ, но заполнять его только оставшимися (неотгруженными или незарезервированными) позициями. Запрос в ОбработкаЗаполнения должен проверять не сам факт существования документа, а собирать баланс по регистрам.

Во-вторых, проблема параллельной работы (Race Condition). Если проверка реализована запросом в ОбработкаЗаполнения, существует риск состояния гонки. Представим, что два пользователя одновременно (или с разницей в доли секунды) нажали "Создать на основании" для одного и того же заказа. Запрос у обоих покажет, что документа еще нет, исключение не сработает, и в итоге в базе появятся два дубля. Чтобы этого избежать, в высоконагруженных конфигурациях применяются механизмы объектных (пессимистичных) блокировок или проверка уникальности по бизнес-ключам непосредственно в момент записи (в обработчике ПередЗаписью или при контроле остатков). Для изучения принципов их работы можно использовать материал по механизмам объектных блокировок в 1С.

В-третьих, параметр СтандартнаяОбработка. Некоторые разработчики пытаются установить параметр СтандартнаяОбработка = Ложь внутри процедуры заполнения, надеясь, что это отменит создание. Это заблуждение. Данный параметр лишь отключает автоматические алгоритмы заполнения реквизитов платформой (например, на основе связей параметров выбора), но сам процесс формирования объекта в памяти и открытие его формы продолжатся.

Резюме

Мы выяснили причину ошибки: невозможно механически удалить (ЭтотОбъект.Удалить()) объект из базы данных, пока он туда не записан. Для прерывания процесса создания документа на основании в модуле объекта следует использовать ВызватьИсключение. А для обеспечения более комфортной работы пользователей и чистоты архитектуры, проверки на дублирование рекомендуется переносить на этап до открытия формы (в общие команды) или использовать параметр Отказ в обработчике ПриСозданииНаСервере модуля формы.

← На главную