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