Как правильно работать с движениями документа и фоновыми заданиями при отмене проведения в 1С?

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

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

Последовательность вызова событий при отмене проведения

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

  1. ПередЗаписью(): Эта процедура вызывается первой. Она предназначена для подготовки данных документа перед его записью. На этом этапе движения документа еще не затронуты.
  2. ОбработкаУдаленияПроведения(): Далее платформа вызывает этот обработчик. Его основное назначение — предоставить разработчику возможность выполнить дополнительные действия перед тем, как система автоматически удалит движения документа или для удаления движений, если это не происходит автоматически.
  3. Удаление движений: После вызова ОбработкаУдаленияПроведения(), если у документа установлено свойство "Удаление движений" в значение "Удалять автоматически при отмене проведения" (что является стандартным поведением для новых документов в более поздних версиях платформы), платформа самостоятельно удаляет все движения документа из регистров. Если свойство не установлено или реализована нестандартная логика, движения могут оставаться.
  4. ПриЗаписи(): Эта процедура вызывается уже после того, как документ записан в базу данных (с обновленными реквизитами, включая признак проведенности), но до завершения транзакции записи. К этому моменту движения документа, как правило, уже удалены.
  5. Завершение транзакции: Вся эта цепочка событий происходит в рамках одной транзакции. Только после ее успешного фиксирования (commit) изменения становятся постоянными в базе данных.

Состояние движений документа в различных обработчиках

Один из ключевых вопросов при работе с отчетами и движениями — это их актуальное состояние в тот или иной момент выполнения кода. Рассмотрим подробнее:

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

    Например, мы можем прочитать движения регистра накопления:

    
    // В обработчике ОбработкаУдаленияПроведения()
    Движения.РегистрНакопления.Прочитать();
    Для Каждого Движение Из Движения.РегистрНакопления Цикла
        // Здесь Движение.Номенклатура, Движение.Количество или Движение.Сумма еще доступны
        Сообщить("Движение: " + Движение.Номенклатура + ", Количество: " + Движение.Количество);
    КонецЦикла;
    

    Или, если мы хотим удалить движения вручную (при отключенном автоматическом удалении):

    
    // В обработчике ОбработкаУдаленияПроведения()
    Движения.РегистрНакопления.Очистить();
    
  2. В ПриЗаписи(): К моменту вызова ПриЗаписи() движения документа, связанные с его проведением, уже удалены системой (если настроено автоматическое удаление). Документ уже записан в базу данных с признаком "не проведен". Мы можем работать с реквизитами самого документа, но не с его движениями, которые были сформированы при проведении.

Транзакционность при отмене проведения

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

Это означает, что либо все изменения будут успешно зафиксированы в базе данных (commit), либо все они будут отменены (rollback), и состояние системы вернется к исходному. Если в любом из обработчиков произойдет ошибка, транзакция будет откатана, и документ останется в своем первоначальном состоянии (проведенным, если он был таким до попытки отмены проведения).

Важно понимать, что ПриЗаписи() выполняется внутри этой же транзакции, но до ее окончательного фиксирования. Фоновые задания, которые мы можем запустить из этих обработчиков, будут выполняться вне текущей транзакции, что приводит к определенным сложностям.

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

Фоновые задания (ФЗ) выполняются асинхронно на сервере в отдельных потоках и, что критично, в собственном сеансе и в собственной транзакции.

Если мы попытаемся запустить фоновое задание непосредственно из ОбработкаУдаленияПроведения() или ПриЗаписи() и передать ему только ссылку на документ, мы можем столкнуться с проблемой актуальности данных. Фоновое задание может:

Рекомендация: Как обеспечить целостность данных при работе с фоновыми заданиями?

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

Рассмотрим по шагам, как это можно реализовать:

  1. Планируем запуск фонового задания (или регламентного): Это может быть вызов метода ФоновыеЗадания.ВыполнитьВФоне() или планирование регламентного задания.

    Пример планирования фонового задания:

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

    Обратите внимание: мы передаем не просто ссылку, а структуру с дополнительной информацией, которая может быть важна для логики фонового задания.

  2. В обработчике фонового задания делаем выборку (если необходимо) и реализуем логику: В модуле, который будет выполнять фоновое задание, мы принимаем эту структуру данных.

    Пример обработчика в общем модуле:

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

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

Мы рассмотрели ключевые аспекты работы с процедурой ОбработкаУдаленияПроведения() и интеграцию с фоновыми заданиями. Помните, что глубокое понимание последовательности событий и транзакционности — это основа для создания надежных и производительных решений в 1С.

← К списку