В практике разработки на платформе 1С:Предприятие 8.3 часто встречается ситуация, которая ставит в тупик даже опытных специалистов: в клиентском обработчике события происходит явная ошибка выполнения (например, деление на ноль), окно с ошибкой появляется, но документ всё равно записывается или проводится. Давайте вместе разберем, почему так происходит, и как правильно спроектировать код, чтобы избежать подобных «сюрпризов».
Рассмотрим ситуацию, описанную разработчиком: в обработчике ПередЗаписью на форме документа намеренно вставлен ошибочный код. При попытке записи платформа выдает предупреждение об ошибке, однако после закрытия этого окна выполнение программы продолжается, и документ оказывается успешно записанным в базу данных.
Для того чтобы понять причины, нам нужно заглянуть «под капот» взаимодействия встроенного языка и ядра платформы. Когда мы запускаем процедуру ПередЗаписью(Отказ, ПараметрыЗаписи), управление передается от ядра платформы к нашему коду. В этот момент переменная Отказ по умолчанию имеет значение Ложь.
Проанализируем цепочку событий при возникновении ошибки:
ПередЗаписью.Отказ присваивается значение Истина, платформа считает, что расширение логики не возражает против записи.Таким образом, для платформы событие считается «отработанным». Ошибка прервала выполнение вашего скрипта, но не подала системе сигнал о необходимости отмены действия. Это фундаментальное отличие клиентских событий от серверных транзакций.
Выясним, почему на сервере поведение отличается. Это критически важный момент для обеспечения целостности данных:
1. На клиенте (в форме): Событие ПередЗаписью происходит до начала транзакции базы данных. Здесь еще нет активной транзакции, которую можно было бы откатить (Rollback). Ошибка здесь — это просто прерванный скрипт в интерфейсе.
2. На сервере (в модуле объекта): Если аналогичная ошибка произойдет в процедуре ПередЗаписью модуля объекта, ситуация будет иной. Этот код выполняется уже внутри открытой транзакции записи. Любое необработанное исключение внутри транзакции помечает её как недействительную, и платформа автоматически выполняет откат всех изменений.
Посмотрим на пример кода, который демонстрирует проблему:
&НаКлиенте
Процедура ПередЗаписью(Отказ, ПараметрыЗаписи)
// Ошибочный код
Результат = 1 / 0;
// Этот код никогда не выполнится из-за ошибки выше
Если Результат < 10 Тогда
Отказ = Истина;
Возврат;
КонецЕсли;
КонецПроцедуры
В данном примере, так как Отказ = Истина не было выполнено, платформа продолжит запись, несмотря на деление на ноль.
Самый надежный способ гарантировать остановку процесса записи при возникновении непредвиденных ошибок в клиентском коде — явная обработка исключений. Разберем, как это реализовать правильно:
&НаКлиенте
Процедура ПередЗаписью(Отказ, ПараметрыЗаписи)
Попытка
// Здесь выполняется логика проверок
ВыполнитьСложныеПроверкиНаКлиенте();
Исключение
// В случае любой ошибки принудительно устанавливаем Отказ
Отказ = Истина;
// Информируем пользователя о том, что пошло не так
ОбщегоНазначенияКлиентСервер.СообщитьПользователю(КраткоеПредставлениеОшибки(ИнформацияОбОшибке()));
// Записываем ошибку в журнал регистрации для разработчика
ЗаписатьОшибкуВЖурнал(ИнформацияОбОшибке());
КонецПопытки;
КонецПроцедуры
В этом случае, даже если произойдет ошибка, управление перейдет в блок Исключение, где мы явно установим Отказ = Истина, тем самым предотвратив запись некорректных данных.
Рассмотрим еще один интересный подход, который часто применяется в типовых конфигурациях для критически важных проверок. Его суть заключается в инверсии логики установки флага Отказ.
Алгоритм действий:
Отказ = Истина.Отказ = Ложь.Посмотрим на пример:
&НаКлиенте
Процедура ПередЗаписью(Отказ, ПараметрыЗаписи)
// Сразу блокируем запись
Отказ = Истина;
// Выполняем полезную работу
Если Не ПроверитьЗаполнениеДанныхНаКлиенте() Тогда
Возврат;
КонецЕсли;
// Если произойдет ошибка в этой строке, код прервется,
// но Отказ останется равным Истина!
РезультатРасчета = ВычислитьСложныйПоказатель();
// И только в самом конце разрешаем запись
Отказ = Ложь;
КонецПроцедуры
Внимание: Этот метод следует использовать с осторожностью в формах, где могут быть другие обработчики или расширения, так как вы можете случайно перекрыть результат работы других процедур. Однако для изолированных проверок это отличный способ гарантировать безопасность данных.
Проанализировав данную особенность платформы, мы можем сформулировать основные правила безопасной разработки:
ПередЗаписью, ПередЗакрытием), всегда оборачивайте его в Попытка...Исключение с явной установкой Отказ = Истина.ЖурналРегистрации, чтобы иметь возможность оперативно исправить баг, приведший к ошибке.Подводя итог, можно сказать, что описанное поведение — это не баг версии 8.3.25, а особенность работы клиентского приложения. Платформа разделяет «ошибку в скрипте» и «бизнес-логику отмены». Понимая этот механизм, вы сможете писать более отказоустойчивый и надежный код.