При разработке алгоритмов в 1С часто возникает задача скорректировать или дополнить движения документа по регистрам накопления. Типичная ситуация: разработчик создает подписку на событие проведения (или дорабатывает модуль документа) и пытается прочитать движения через независимый набор записей:
НаборЗаписей = РегистрыНакопления.ТоварыОрганизаций.СоздатьНаборЗаписей();
НаборЗаписей.Отбор.Регистратор.Установить(Источник.Ссылка);
НаборЗаписей.Прочитать();
В результате метод Прочитать() возвращает пустую таблицу, несмотря на то, что документ уже проводится и движения должны быть сформированы. Разберем по шагам, почему так происходит и как правильно получить доступ к движениям для их изменения или добавления новых записей.
Проанализируем механику работы транзакции проведения документа в платформе 1С:
СоздатьНаборЗаписей() менеджера регистра создает автономный объект, который при вызове метода Прочитать() обращается напрямую к физическим таблицам базы данных. Во время выполнения процедуры ОбработкаПроведения или соответствующей подписки на событие проведения движения документа формируются в оперативной памяти (в коллекции Источник.Движения) и еще не зафиксированы в базе данных.Удалять автоматически, платформа в самом начале транзакции проведения физически очищает старые записи регистра в базе. Новые строки попадут в базу только при успешном завершении транзакции. Соответственно, в момент проведения в самой таблице базы записей по текущему регистратору просто нет.Источник.Движения.<ИмяРегистра> также представляет собой набор записей. Если к моменту вызова подписки движения уже были рассчитаны типовым кодом конфигурации, но свойство набора Прочитан имеет значение Ложь, при просмотре в отладчике набор может казаться пустым до тех пор, пока не будет вызван метод Прочитать() непосредственно у этого набора из коллекции объекта.Если логика выполняется в подписке на событие проведения документа (где параметром Источник выступает сам документ), правильным решением является работа с его встроенной коллекцией Движения. Рассмотрим пример:
// Получаем набор записей напрямую из коллекции документа
ДвиженияРегистра = Источник.Движения.ТоварыОрганизаций;
// Если набор еще не инициализирован в памяти, читаем его
Если Не ДвиженияРегистра.Прочитан Тогда
ДвиженияРегистра.Прочитать();
КонецЕсли;
// Добавляем новую строку в движения
НоваяСтрока = ДвиженияРегистра.Добавить();
НоваяСтрока.ВидДвижения = ВидДвиженияНакопления.Приход;
НоваяСтрока.Период = Источник.Дата;
НоваяСтрока.Организация = Источник.Организация;
НоваяСтрока.Номенклатура = ВыбраннаяНоменклатура;
НоваяСтрока.Количество = 10;
// Обязательно выставляем признак записи набора платформой
ДвиженияРегистра.Записывать = Истина;
Важный момент: установка свойства Записывать = Истина сообщает платформе 1С, что сформированный в оперативной памяти набор записей должен быть сохранен в базу данных при завершении проведения документа.
В конфигурациях на обычных формах (например, «Управление торговлей 10.3») движения по некоторым регистрам могут формироваться не в модуле документа, а через служебные процедуры общих модулей с созданием локальных наборов. В этом случае коллекция Источник.Движения может оставаться пустой.
Чтобы гарантированно перехватить и изменить записи вне зависимости от места их формирования, рассмотрим подписку на событие ПередЗаписью или ПриЗаписи для объекта РегистрНакопленияНаборЗаписей.<ИмяРегистра>:
ПередЗаписью для набора записей нужного регистра.Источник будет являться сам записываемый набор записей.Посмотрим на пример обработчика:
Процедура ПередЗаписьюНабораРегистра(Источник, Отказ, Замещение) Экспорт
// Проверяем, что регистратор заполнен и соответствует нужному типу документа
Если Источник.Отбор.Регистратор.Использование И ЗначениеЗаполнено(Источник.Отбор.Регистратор.Значение) Тогда
ДокументРегистратор = Источник.Отбор.Регистратор.Значение;
Если ТипЗнч(ДокументРегистратор) = Тип("ДокументСсылка.РеализацияТоваровУслуг") Тогда
Для Каждого СтрокаДвижения Из Источник Цикл
// Корректируем существующие строки
Если СтрокаДвижения.Количество < 0 Тогда
СтрокаДвижения.Количество = 0;
КонецЕсли;
КонецЦикла;
КонецЕсли;
КонецЕсли;
КонецПроцедуры
Если алгоритм жестко требует работы с отдельным набором записей через метод СоздатьНаборЗаписей(), перенесем логику в подписку на событие ПослеЗаписи документа:
Процедура ДокументПослеЗаписи(Источник, Отказ) Экспорт
// Проверяем, что документ проведен
Если Не Источник.Проведен Тогда
Возврат;
КонецЕсли;
// В этот момент транзакция проведения завершена, данные уже в базе
НаборЗаписей = РегистрыНакопления.ТоварыОрганизаций.СоздатьНаборЗаписей();
НаборЗаписей.Отбор.Регистратор.Установить(Источник.Ссылка);
НаборЗаписей.Прочитать();
Если НаборЗаписей.Количество() > 0 Тогда
Для Каждого Движение Из НаборЗаписей Цикл
// Вносим необходимые правки
КонецЦикла;
// Записываем измененный набор обратно в базу данных
НаборЗаписей.Записать();
КонецЕсли;
КонецПроцедуры
Обратите внимание: перезапись движений в событии ПослеЗаписи создает дополнительную нагрузку на сервер и таблицы итогов, так как инициирует вторую запись набора. По возможности отдавайте предпочтение Способу 1 или Способу 2.