Как получить движения документа при установке пометки на удаление в 1С:ERP

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

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

Выясняем причину: Оптимизация работы платформы 1С

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

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

Решение №1: Явное использование метода Прочитать()

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

Разберем пример кода, который можно разместить в процедуре ПередЗаписью модуля документа:


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

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

Решение №2: Использование запроса к физическим таблицам

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

Рассмотрим пример реализации через запрос:


Запрос = Новый Запрос;
Запрос.Текст = 
    "ВЫБРАТЬ
    |   МойРегистр.Сумма КАК Сумма,
    |   МойРегистр.Номенклатура КАК Номенклатура
    |ИЗ
    |   РегистрНакопления.МойСамописныйРегистр КАК МойРегистр
    |ГДЕ
    |   МойРегистр.Регистратор = &Регистратор";

Запрос.УстановитьПараметр("Регистратор", ЭтотОбъект.Ссылка);

РезультатЗапроса = Запрос.Выполнить();
Если Не РезультатЗапроса.Пустой() Тогда
    Выборка = РезультатЗапроса.Выбрать();
    Пока Выборка.Следующий() Цикл
        // Обработка данных
    КонецЦикла;
КонецЕсли;

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

Механизмы очистки движений в 1С:ERP

В конфигурациях на базе ERP (таких как 1С:УХ или 1С:КА) механизмы проведения и отмены проведения сильно переработаны по сравнению с «простыми» конфигурациями. Выясним, на что стоит обратить внимание:

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

Рекомендации по отладке

Если вы установили точку останова в ПередЗаписью, используйте окно Вычислить выражение (Shift+F9). Не ограничивайтесь просмотром количества строк в коллекции. Попробуйте прямо в окне вычисления вызвать Движения.ИмяВашегоРегистра.Прочитать(), а затем снова проверьте количество строк. Если после вызова метода Прочитать() строки появились — значит, проблема была исключительно в ленивой загрузке данных платформой.

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

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

← На главную