Как программно отменить проведение и удалить документ в 1С без ошибок

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

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

Анализ проблемы: почему возникает «Несоответствие версии»

Рассмотрим ситуацию, описанную в исходном вопросе. Программист пытается выполнить следующий код:


Док = Выборка.ПолучитьОбъект();
Док.Записать(РежимЗаписиДокумента.ОтменаПроведения); 
Док.Удалить();

На шаге Док.Удалить() система выдает ошибку. Чтобы понять причину, проанализируем внутренние механизмы платформы 1С. Каждая запись в таблице базы данных (будь то документ или справочник) имеет специальное скрытое поле — версию данных. Это своеобразный «отпечаток пальца» текущего состояния записи.

Когда мы вызываем метод Записать(РежимЗаписиДокумента.ОтменаПроведения), происходят следующие действия:

  1. Данные записываются в базу.
  2. Признак проведения устанавливается в Ложь.
  3. Движения по регистрам удаляются.
  4. Версия объекта в базе данных обновляется.

При этом объект, находящийся в оперативной памяти (переменная Док), может не обновить информацию о своей версии автоматически. Когда мы следом вызываем Док.Удалить(), платформа сравнивает версию объекта в памяти с версией в базе данных. Видя расхождение, система делает вывод, что данные были изменены кем-то другим (или другим процессом) в промежутке между получением объекта и его удалением, и блокирует операцию для предотвращения потери целостности. Это и называется оптимистической блокировкой.

Решение №1: Переполучение объекта перед удалением

Самый очевидный способ избежать ошибки — «освежить» объект в памяти после того, как он был изменен методом Записать(). Разберем, как это сделать правильно:


// Получаем объект из выборки
Док = Выборка.ПолучитьОбъект();

// Выполняем отмену проведения
Док.Записать(РежимЗаписиДокумента.ОтменаПроведения);

// Теперь объект в памяти 'устарел'. Получаем его заново через ссылку.
ДокОбновленный = Док.Ссылка.ПолучитьОбъект();
ДокОбновленный.Удалить();

В этом случае метод ПолучитьОбъект() считывает из базы данных актуальную версию записи, которая была создана после отмены проведения. После этого вызов Удалить() пройдет успешно, так как версии совпадут.

Решение №2: Использование прямого удаления (рекомендуемый способ)

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

  1. Система автоматически инициирует процесс отмены проведения.
  2. Очищаются все движения документа по регистрам накопления, сведений, бухгалтерии и расчета.
  3. Объект физически удаляется из таблицы базы данных.

Таким образом, предварительный вызов Записать(РежимЗаписиДокумента.ОтменаПроведения) в большинстве случаев является избыточным и только замедляет работу системы, создавая лишнюю нагрузку на сервер и базу данных. Оптимальный код выглядит так:


Пока Выборка.Следующий() Цикл
    // Сразу получаем объект и удаляем его
    ОбъектДляУдаления = Выборка.ПолучитьОбъект();
    ОбъектДляУдаления.Удалить();
КонецЦикла;

Нюансы работы в режиме ОбменДанными.Загрузка

Важно помнить о специфическом режиме записи. Если в коде используется флаг ОбменДанными.Загрузка = Истина, поведение системы меняется. Выясним, почему это критично:

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

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

Практический пример реализации сложной логики

Рассмотрим более сложный сценарий, когда удаление должно происходить только при соблюдении определенных условий внутри документа (например, наличие определенных строк в табличной части). Посмотрим на пример кода обработки:


&НаСервереБезКонтекста
Процедура УдалитьЛишниеДокументыНаСервере()
    
    Выборка = Документы.УстановкаКурсаВалют.Выбрать();
    
    Пока Выборка.Следующий() Цикл
        НужноУдалить = Ложь;
        
        // Проверяем табличную часть документа
        Для каждого СтрокаКурса Из Выборка.Курсы Цикл
            Если НЕ СтрокаКурса.Валюта.Предопределенный Тогда
                НужноУдалить = Истина;
                Прервать;
            КонецЕсли;
        КонецЦикла;
        
        Если НужноУдалить Тогда
            // Получаем объект один раз
            ДокОбъект = Выборка.ПолучитьОбъект();
            
            // Если документ проведен, платформа сама справится с движениями при удалении.
            // Но если мы ХОТИМ сначала отменить проведение (например, для запуска бизнес-логики),
            // то используем безопасный метод:
            Попытка
                ДокОбъект.Удалить();
                Сообщить("Документ " + ДокОбъект.Номер + " успешно удален.");
            Исключение
                Сообщить("Не удалось удалить документ: " + ОписаниеОшибки());
            КонецПопытки;
        КонецЕсли;
    КонецЦикла;
    
КонецПроцедуры

Выводы и рекомендации

Подводя итог, сформулируем основные правила программного удаления документов:

  1. Не делайте лишних действий. Метод Удалить() автоматически очищает движения. Ручная отмена проведения перед удалением нужна только в редких случаях, когда в модуле документа в процедуре ОбработкаУдаленияПроведения прописана специфическая логика, которую нельзя пропускать.
  2. Следите за актуальностью объекта. Если вы все же вызвали Записать(), объект в памяти считается измененным. Для любых дальнейших действий (удаление, повторная запись) необходимо заново вызвать ПолучитьОбъект().
  3. Используйте Попытка...Исключение. Удаление может быть заблокировано другими пользователями или правилами ссылочной целостности. Всегда оборачивайте деструктивные операции в блок обработки ошибок.
  4. Пометка на удаление как альтернатива. В реальных бизнес-системах физическое удаление (Удалить()) используется редко. Рекомендуется использовать УстановитьПометкуУдаления(Истина). Этот метод также снимает документ с проведения, очищает движения, но сохраняет сам объект в базе для возможности восстановления или анализа истории.

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

← На главную