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