Как реализовать групповое изменение объектов в 1С с возможностью отмены и отката изменений?

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

Реализация групповой обработки данных — одна из классических задач программиста 1С. Однако дьявол кроется в деталях: как обеспечить целостность данных, если в процессе записи одного из тысячи объектов произошла ошибка? Что делать, если пользователь нажал кнопку «Отмена» на середине процесса? В данной статье мы разберем несколько стратегий реализации этого механизма, проанализируем работу транзакций и научимся сохранять состояние объектов для последующего отката.

Вариант 1. Использование единой транзакции

Самый простой и логичный на первый взгляд способ — обернуть весь цикл обработки в одну большую транзакцию. Рассмотрим этот подход подробнее. Если в процессе записи любого объекта возникнет исключение или пользователь инициирует отмену, мы просто не фиксируем транзакцию, и база данных возвращается к исходному состоянию силами самой СУБД.

Разберем по шагам пример кода:


НачатьТранзакцию();
Попытка
    Для Каждого СсылкаИзСписка Из МассивОбъектов Цикл
        // Проверяем, не прервал ли пользователь фоновое задание
        Если ФоновыеЗадания.ПолучитьТекущее().ПроверитьВыполнение() Тогда
             ВызватьИсключение "Операция прервана пользователем";
        КонецЕсли;

        Объект = СсылкаИзСписка.ПолучитьОбъект();
        Объект.РеквизитДляИзменения = НовоеЗначение;
        
        // Попытка записи
        Объект.Записать();
    КонецЦикла;
    
    ЗафиксироватьТранзакцию();
Исключение
    ОтменитьТранзакцию();
    ЗаписатьЛогОшибки(ИнформацияОбОшибке());
    // Здесь данные в БД не изменились
КонецПопытки;

Проанализируем ситуацию: у этого метода есть существенный недостаток. В 1С любая ошибка при вызове метода Записать() внутри транзакции помечает всю транзакцию как «обреченную». Даже если вы перехватите ошибку внутри блока Попытка...Исключение, вы больше не сможете вызвать ЗафиксироватьТранзакцию() — система выдаст ошибку о том, что транзакция уже отменена. Это означает, что один «битый» элемент справочника заблокирует обновление остальных десяти тысяч корректных элементов.

Вариант 2. Сохранение состояния объектов (Snapshot)

Если нам нужно, чтобы корректные объекты записывались, а откат происходил только по требованию пользователя, нам придется «запоминать» состояние объектов до их изменения. Рассмотрим вариант с использованием сериализации в XML, который упоминался в обсуждении.

Алгоритм действий будет следующим:

  1. Перед изменением объекта мы считываем его текущее состояние.
  2. Сериализуем объект в строку XML или сохраняем структуру его реквизитов.
  3. Помещаем эти данные во временное хранилище или специальный технический регистр сведений.
  4. Если пользователь нажимает «Откат», мы десериализуем данные и записываем их обратно.

Посмотрим на пример кода для сохранения состояния:


Функция ПолучитьСлепокОбъекта(Объект)
    ЗаписьXML = Новый ЗаписьXML;
    ЗаписьXML.УстановитьСтроку();
    ЗаписатьXML(ЗаписьXML, Объект);
    Возврат ЗаписьXML.Закрыть();
КонецФункции

// При откате изменений:
Процедура ВосстановитьОбъектИзСлепка(СлепокXML)
    ЧтениеXML = Новый ЧтениеXML;
    ЧтениеXML.УстановитьСтроку(СлепокXML);
    Объект = ПрочитатьXML(ЧтениеXML);
    
    // Важно использовать ОбменДанными.Загрузка, чтобы обойти лишние проверки
    Объект.ОбменДанными.Загрузка = Истина;
    Объект.Записать();
КонецПроцедуры

Важный момент: при использовании этого метода следует учитывать нагрузку на оперативную память (rphost). Если объектов десятки тысяч, хранение их XML-копий в памяти может привести к падению рабочего процесса. В таких случаях лучше использовать временные таблицы базы данных.

Вариант 3. Пакетная обработка (Чанкинг)

Чтобы найти баланс между скоростью и надежностью, проанализируем стратегию обработки пакетами. Мы делим весь массив данных на порции (например, по 100 объектов). Каждая порция обрабатывается в своей транзакции.

Это дает нам следующие преимущества:

Особенности работы с фоновыми заданиями и отменой

Когда мы реализуем групповое изменение в фоновом задании, кнопка «Отмена» в интерфейсе обычно просто вызывает метод ФоновыеЗадания.Отменить(). Однако это жесткое прерывание. Чтобы реализовать «мягкий» откат, наше фоновое задание должно само мониторить флаг отмены.

Рассмотрим правильную структуру цикла в фоновом задании:


Для Каждого ТекОбъект Из СписокОбъектов Цикл
    // Проверка внешнего сигнала на остановку
    Если НужноПрерватьРаботу() Тогда 
        Если ТребуетсяПолныйОткат Тогда
            ВыполнитьПроцедуруОткатаПоЛогу();
        КонецЕсли;
        Возврат;
    КонецЕсли;
    
    // Логика изменения...
КонецЦикла;

Выясним причину, по которой профессионалы рекомендуют использовать ОбменДанными.Загрузка = Истина при откате. Когда вы возвращаете объект в исходное состояние, бизнес-логика системы (проверки в модуле объекта ПередЗаписью) может измениться или войти в противоречие с текущим состоянием других объектов. Режим загрузки позволяет гарантированно «вернуть как было», игнорируя прикладные проверки заполнения и корректности.

Использование механизмов БСП

Если вы работаете в современной конфигурации, обязательно проанализируйте возможности подсистемы «Групповое изменение объектов» из состава БСП. В ней уже реализованы:

  1. Выполнение в фоновом задании с индикацией прогресса.
  2. Возможность пропускать объекты, на которых возникла ошибка записи, с формированием подробного отчета.
  3. Настройка дополнительных параметров записи (обработка дочерних объектов, проверка прав).

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

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

← На главную