Почему набор записей регистра накопления пуст при чтении в подписке на проведение документа и как это исправить?

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

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


НаборЗаписей = РегистрыНакопления.ТоварыОрганизаций.СоздатьНаборЗаписей();
НаборЗаписей.Отбор.Регистратор.Установить(Источник.Ссылка);
НаборЗаписей.Прочитать();

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

Выясним причину: почему независимый набор записей возвращает пустоту

Проанализируем механику работы транзакции проведения документа в платформе 1С:

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

Способ 1. Модификация движений через коллекцию документа в подписке на проведение

Если логика выполняется в подписке на событие проведения документа (где параметром Источник выступает сам документ), правильным решением является работа с его встроенной коллекцией Движения. Рассмотрим пример:


// Получаем набор записей напрямую из коллекции документа
ДвиженияРегистра = Источник.Движения.ТоварыОрганизаций;

// Если набор еще не инициализирован в памяти, читаем его
Если Не ДвиженияРегистра.Прочитан Тогда
    ДвиженияРегистра.Прочитать();
КонецЕсли;

// Добавляем новую строку в движения
НоваяСтрока = ДвиженияРегистра.Добавить();
НоваяСтрока.ВидДвижения = ВидДвиженияНакопления.Приход;
НоваяСтрока.Период = Источник.Дата;
НоваяСтрока.Организация = Источник.Организация;
НоваяСтрока.Номенклатура = ВыбраннаяНоменклатура;
НоваяСтрока.Количество = 10;

// Обязательно выставляем признак записи набора платформой
ДвиженияРегистра.Записывать = Истина;

Важный момент: установка свойства Записывать = Истина сообщает платформе 1С, что сформированный в оперативной памяти набор записей должен быть сохранен в базу данных при завершении проведения документа.

Способ 2. Использование подписки на события набора записей регистра

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

Чтобы гарантированно перехватить и изменить записи вне зависимости от места их формирования, рассмотрим подписку на событие ПередЗаписью или ПриЗаписи для объекта РегистрНакопленияНаборЗаписей.<ИмяРегистра>:

  1. Создаем подписку на событие ПередЗаписью для набора записей нужного регистра.
  2. В обработчике параметром Источник будет являться сам записываемый набор записей.
  3. Проверяем тип регистратора и модифицируем строки перед записью в базу.

Посмотрим на пример обработчика:


Процедура ПередЗаписьюНабораРегистра(Источник, Отказ, Замещение) Экспорт

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

КонецПроцедуры

Способ 3. Чтение и перезапись движений в событии «ПослеЗаписи»

Если алгоритм жестко требует работы с отдельным набором записей через метод СоздатьНаборЗаписей(), перенесем логику в подписку на событие ПослеЗаписи документа:


Процедура ДокументПослеЗаписи(Источник, Отказ) Экспорт

    // Проверяем, что документ проведен
    Если Не Источник.Проведен Тогда
        Возврат;
    КонецЕсли;

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

    Если НаборЗаписей.Количество() > 0 Тогда
        Для Каждого Движение Из НаборЗаписей Цикл
            // Вносим необходимые правки
        КонецЦикла;
        
        // Записываем измененный набор обратно в базу данных
        НаборЗаписей.Записать();
    КонецЕсли;

КонецПроцедуры

Обратите внимание: перезапись движений в событии ПослеЗаписи создает дополнительную нагрузку на сервер и таблицы итогов, так как инициирует вторую запись набора. По возможности отдавайте предпочтение Способу 1 или Способу 2.

← На главную