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