Почему функция ВычислитьВБезопасномРежиме стала откатывать изменения и как это исправить?

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

В одной из недавних версий Библиотеки стандартных подсистем (БСП), начиная примерно с релиза 3.1.10.357, многие разработчики столкнулись с неожиданным поведением. Привычные методы ОбщегоНазначения.ВычислитьВБезопасномРежиме и ОбщегоНазначения.ВыполнитьВБезопасномРежиме перестали сохранять изменения в базе данных. Код, который годами работал, особенно в разработках для сервиса "1С:Фреш", внезапно перестал выполнять свою основную функцию — модификацию данных. В этой статье мы подробно разберем, почему так произошло, что это не ошибка, а осознанное изменение со стороны "1С", и как правильно адаптировать свой код к новым реалиям.

В чем суть изменения?

Давайте проанализируем ситуацию. Ранее функции безопасного выполнения кода использовались некоторыми разработчиками как способ обойти ограничения аудита в "1С:Фреш". Можно было поместить код, изменяющий данные (например, создающий или проводящий документы), внутрь вызова ВычислитьВБезопасномРежиме, и такая обработка успешно проходила проверку, так как формально вызов "безопасной" функции не считался нарушением.

Фирма "1С" решила закрыть эту уязвимость, чтобы повысить надежность и безопасность облачного сервиса. Теперь логика этих функций изменилась кардинально:

  1. При вызове функции система проверяет, активна ли в данный момент транзакция.
  2. Если транзакция не активна, функция самостоятельно инициирует новую транзакцию с помощью НачатьТранзакцию().
  3. Затем выполняется код, который был передан в качестве параметра.
  4. И самое главное: по завершении выполнения кода функция принудительно отменяет созданную ею транзакцию вызовом ОтменитьТранзакцию().

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

Как адаптировать код: правильное управление транзакциями

Это изменение вовсе не означает, что теперь невозможно изменять данные в обработках для "1С:Фреш". Оно означает, что теперь разработчик должен делать это явно и осознанно, беря на себя ответственность за управление транзакциями. Ключ к решению проблемы кроется в поведении самой функции: если она вызывается внутри уже активной транзакции, она не создает свою и, соответственно, не пытается ее отменить.

Рассмотрим по шагам, как правильно организовать код сейчас.

Решение 1: Единая транзакция для всего блока кода

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

Шаг 1: Перед выполнением кода, который будет менять данные, необходимо явно начать транзакцию.

Шаг 2: Весь блок кода следует обернуть в конструкцию Попытка ... Исключение ... КонецПопытки для корректной обработки возможных ошибок.

Шаг 3: Внутри блока Попытка вызывайте ОбщегоНазначения.ВычислитьВБезопасномРежиме.

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

Шаг 5: В блоке Исключение обязательно отмените транзакцию с помощью ОтменитьТранзакцию(). Это критически важный шаг для поддержания целостности данных.

Посмотрим на пример. Предположим, у нас есть код, который создает и проводит документ реализации.

Старый код (больше не работает):


// Этот код создавал документ, но теперь все изменения будут отменены
СтрокаКода = "
|НовыйДокумент = Документы.РеализацияТоваровУслуг.СоздатьДокумент();
|НовыйДокумент.Дата = ТекущаяДата();
|// ...заполнение документа...
|НовыйДокумент.Записать(РежимЗаписиДокумента.Проведение);
|";

ОбщегоНазначения.ВычислитьВБезопасномРежиме(СтрокаКода);

Новый, правильный код с явным управлением транзакцией:


НачатьТранзакцию();

Попытка
    // Тот же самый код, который будет выполняться
    СтрокаКода = "
    |НовыйДокумент = Документы.РеализацияТоваровУслуг.СоздатьДокумент();
    |НовыйДокумент.Дата = ТекущаяДата();
    |// ...заполнение документа...
    |НовыйДокумент.Записать(РежимЗаписиДокумента.Проведение);
    |";

    // Вызываем функцию внутри нашей транзакции
    ОбщегоНазначения.ВычислитьВБезопасномРежиме(СтрокаКода);

    // Если ошибок не было, фиксируем изменения
    ЗафиксироватьТранзакцию();
    Сообщить("Документ успешно создан и проведен!");

Исключение
    // В случае любой ошибки откатываем все изменения
    ОтменитьТранзакцию();
    Сообщить("Произошла ошибка. Все изменения отменены.");
    // Здесь можно добавить логирование или вывод подробной информации об ошибке
    ВызватьИсключение ОписаниеОшибки();

КонецПопытки;

Таким образом, мы явно показываем системе, что берем управление транзакцией на себя. Функция ВычислитьВБезопасномРежиме "видит" нашу активную транзакцию и не вмешивается в ее жизненный цикл.

Решение 2: Гранулярные транзакции для обработки наборов данных

Часто возникает задача обработать большой массив данных, например, загрузить 100 документов из Excel. В этом случае откат всей операции из-за ошибки в одном документе нежелателен. Если 99 документов корректны, а один — нет, логично сохранить 99 и сообщить об ошибке в одном.

Для этого мы можем создавать отдельную, "маленькую" транзакцию для обработки каждого элемента в цикле.

Проанализируем пример:


МассивДанныхДляЗагрузки = ПолучитьДанныеИзВнешнегоИсточника();

Для Каждого ЭлементДанных Из МассивДанныхДляЗагрузки Цикл

    // Начинаем транзакцию для одного элемента
    НачатьТранзакцию();
    Попытка
        // Формируем код для выполнения на основе данных текущего элемента
        СтрокаКода = СформироватьКодДляСозданияДокумента(ЭлементДанных);
        
        ОбщегоНазначения.ВычислитьВБезопасномРежиме(СтрокаКода);
        
        // Фиксируем транзакцию только для этого успешного элемента
        ЗафиксироватьТранзакцию();
        Сообщить("Элемент '" + ЭлементДанных.Представление + "' успешно обработан.");

    Исключение
        // Откатываем транзакцию только для сбойного элемента
        ОтменитьТранзакцию();
        Сообщить("Ошибка при обработке элемента '" + ЭлементДанных.Представление + "'. Элемент пропущен.");

    КонецПопытки;

КонецЦикла;

Этот подход делает вашу обработку более устойчивой к ошибкам в исходных данных. Ошибка в одной строке не приведет к отмене всей многочасовой загрузки.

В заключение отметим, что данное изменение — это не "баг", а "фича". Это шаг в сторону более безопасного и предсказуемого кода. "1С" заставляет разработчиков следовать лучшим практикам, явно управляя операциями изменения данных. Хотя это и требует адаптации существующих разработок, в долгосрочной перспективе это повышает надежность и качество программных решений, особенно в критически важной облачной среде.

← На главную