Как использовать конструкцию Попытка...Исключение в Конвертации данных 2 и чем её заменить?

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

При разработке правил обмена в конфигурации «Конвертация данных 2» (КД2) перед разработчиками часто встает вопрос: насколько корректно использовать стандартные конструкции платформы «1С:Предприятие» внутри обработчиков событий? Одним из таких спорных моментов является применение конструкции Попытка...Исключение.

В этой статье мы подробно разберем, как работает этот механизм в КД2, почему его использование для логических проверок считается плохим тоном, к каким критическим ошибкам это может привести при загрузке данных и какими более производительными альтернативами его следует заменить.

Работает ли стандартная конструкция «Попытка...Исключение» в КД2?

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

Дело в том, что при выгрузке или загрузке данных специализированная обработка обмена («Универсальный обмен данными XML») динамически компилирует программный код, написанный вами в правилах конвертации, в обычные модули платформы «1С:Предприятие». Никаких ограничений на использование базовых синтаксических конструкций языка 1С здесь нет. Однако то, что код компилируется и работает, еще не означает, что он написан корректно и оптимально.

Анализ проблемного кода: почему «Попытка» для проверок — это плохой тон?

Рассмотрим пример кода, который часто пытаются использовать начинающие разработчики для обхода ошибок отсутствия реквизитов у объекта-источника:


Попытка
    Если Источник.ВидОперации = Перечисления.ВидыОперацийППИсходящее.ПеречислениеНалога Тогда
        ИмяПКо = "ВидыПлатежейВГосБюджет";
    КонецЕсли;
Исключение
    Отказ = Истина;
КонецПопытки;

Проанализируем, что здесь происходит. Разработчик хочет проверить значение реквизита ВидОперации. Но поскольку данное ПКО (Правило конвертации объектов) может вызываться для разных типов документов (у некоторых из которых реквизита ВидОперации попросту нет), во избежание аварийной остановки обмена весь блок помещается в Попытку.

Этот подход имеет несколько серьезных недостатков:

  1. Критическое падение производительности. Генерация исключения на уровне платформы «1С:Предприятие» — это крайне ресурсоемкая операция. Если вы выгружаете базу данных, содержащую десятки тысяч документов, и у половины из них нет реквизита ВидОперации, платформа будет постоянно генерировать внутренние программные исключения. Это может замедлить процесс выгрузки в несколько раз.
  2. Маскировка непредвиденных ошибок. Блок Исключение в данном примере «молча» перехватывает абсолютно любую ошибку. Если внутри блока Попытка произойдет сбой, не связанный с отсутствием реквизита (например, битая ссылка в самом реквизите или сбой обращения к глобальному контексту), вы об этом никогда не узнаете. Программа просто установит флаг Отказ = Истина, и объект не выгрузится без объяснения причин в журнале регистрации.

Давайте разберем, как решить эту задачу правильно и быстро.

Альтернатива №1: Явная проверка типа источника

Если нам заранее известны типы объектов, у которых есть необходимый реквизит, самым простым и производительным решением будет проверка типа входящего объекта Источник. Выясним, как переписать наш пример с использованием функции ТипЗнч():


Если ТипЗнч(Источник) = Тип("ДокументСсылка.ПлатежноеПоручениеИсходящее") Тогда
    Если Источник.ВидОперации = Перечисления.ВидыОперацийППИсходящее.ПеречислениеНалога Тогда
        ИмяПКо = "ВидыПлатежейВГосБюджет";
    КонецЕсли;
КонецЕсли;

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

Альтернатива №2: Динамический поиск реквизита в метаданных

Что делать, если правил обмена много, а реквизит ВидОперации может присутствовать у абсолютно разных типов документов (например, у платежных поручений, кассовых ордеров, актов взаимозачета)? Прописывать все типы через «ИЛИ» в проверке типов неудобно.

В этом случае на помощь приходит объектная модель метаданных 1С. Мы можем динамически проверить, существует ли у данного объекта необходимый нам реквизит:


МетаданныеИсточника = Источник.Метаданные();
Если МетаданныеИсточника.Реквизиты.Найти("ВидОперации") <> Неопределено Тогда
    Если Источник.ВидОперации = Перечисления.ВидыОперацийППИсходящее.ПеречислениеНалога Тогда
        ИмяПКо = "ВидыПлатежейВГосБюджет";
    КонецЕсли;
КонецЕсли;

Рассмотрим, как работает данный алгоритм:

  1. Мы получаем метаданные объекта Источник с помощью метода Метаданные().
  2. С помощью метода Найти() коллекции реквизитов мы проверяем наличие реквизита с именем "ВидОперации".
  3. Если реквизит найден (результат поиска не равен Неопределено), мы можем безопасно обращаться к его значению в кодовой строке.

Этот способ работает быстро, надежно и не вызывает неявных прерываний выполнения программного кода.

Скрытая угроза: Исключения и «сломанные» транзакции при загрузке

Особенно опасно использовать конструкцию Попытка...Исключение в обработчиках, выполняющихся на этапе загрузки данных в базу-приемник (например, в обработчиках ПКО «После загрузки», «Перед записью» или «При записи»).

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


Попытка
    Объект.Записать(РежимЗаписиДокумента.Проведение);
Исключение
    // Пытаемся обработать ошибку проведения
    Сообщить("Документ " + Объект + " не провелся: " + ОписаниеОшибки());
КонецПопытки;

Почему этот код является крайне опасным?

Процесс загрузки данных XML в КД2 практически всегда выполняется в рамках одной транзакции базы данных. Если при попытке проведения документа СУБД (например, MS SQL Server или PostgreSQL) возвращает ошибку (например, нарушение уникальности индексов, отсутствие остатков на складах, пересечение периодов), то текущая транзакция помечается СУБД как нежизнеспособная (битая).

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

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

Как правильно обрабатывать исключения, если они неизбежны?

Конструкцию Попытка...Исключение стоит использовать только тогда, когда вы обращаетесь к внешним по отношению к 1С ресурсам, поведение которых невозможно предсказать логическими проверками. Например, чтение внешнего файла на диске, обращение к веб-сервису или работа с COM-соединением.

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

Рассмотрим пример правильного логирования ошибок в КД2:


Попытка
    // Работа с внешним файлом или COM-объектом
    ВнешнийОбъект = Новый COMОбъект("Excel.Application");
Исключение
    Отказ = Истина;
    
    // Формируем подробное описание ошибки для разработчика
    ИнформацияОбОшибке = ИнформацияОбОшибке();
    ТекстОшибки = ПодробноеПредставлениеОшибки(ИнформацияОбОшибке);
    
    // Записываем ошибку в системный Журнал регистрации
    ЗаписьЖурналаРегистрации(
        "ОбменДанными.КД2",
        УровеньЖурналаРегистрации.Ошибка,
        ,
        ,
        "Ошибка при инициализации Excel: " + ТекстОшибки
    );
    
    // Выводим краткое сообщение пользователю в окно сообщений обмена
    Сообщить("Не удалось выполнить выгрузку из-за ошибки работы с внешним приложением. Подробности в Журнале регистрации.");
КонецПопытки;

В данном примере мы используем современную функцию платформы ИнформацияОбОшибке() и функцию ПодробноеПредставлениеОшибки(), которые позволяют получить не просто текст системного сбоя, но и точное указание модуля и строки кода, где произошла ошибка. Это в разы сокращает время на диагностику и исправление правил обмена.

Резюме

Подведем итоги нашего разбора:

← На главную