При разработке сложных механизмов проведения документов начинающие разработчики часто сталкиваются с тем, что данные в регистрах накопления начинают «задваиваться» или «затраиваться». Ситуация выглядит следующим образом: при первом проведении документа движения записываются верно, но после повторного проведения количество записей увеличивается в геометрической прогрессии. Разберем эту проблему подробно, проанализируем причины и способы оптимизации кода.
Самая распространенная причина задвоения данных — неправильно выбранный режим удаления движений в свойствах документа на вкладке «Основные». Рассмотрим, как работают разные режимы:
ОбработкаПроведения платформа сама физически удаляет все старые записи данного регистратора из всех регистров. В этом случае задвоение исключено программно.Проанализируем ситуацию автора темы: у него стоял режим «Удалять автоматически при отмене проведения». При нажатии кнопки «ОК» в документе система видела, что документ уже проведен, не очищала старые движения и добавляла в циклы новые. Решение заключалось в переключении свойства на «Удалять автоматически».
Если вы закомментировали код формирования движений по одному регистру (например, СвободныеОстатки), а записи в нем всё равно появляются — значит, работает «скрытая» логика. Выясним причину:
ПриЗаписи какого-то другого регистра (например, ТоварыНаСкладах), который программно создает записи в СвободныеОстатки.ПередЗаписью или ПриЗаписи может быть прописана логика дублирования данных в соседние регистры.В подобных случаях стоит использовать отладчик, установив точку остановки непосредственно в момент записи набора записей регистра.
Код, представленный в вопросе, содержит множество однотипных циклов Для Каждого по разным табличным частям. Это «антипаттерн» разработки в 1С. Рассмотрим, почему так делать не стоит:
Рассмотрим правильный подход: использование запроса. Мы можем объединить данные всех табличных частей в одну временную таблицу, сгруппировать их и обойти одним циклом.
// Пример правильной структуры проведения
Запрос = Новый Запрос;
Запрос.Текст =
"ВЫБРАТЬ
| ТЧ_Продукция.Номенклатура КАК Номенклатура,
| ТЧ_Продукция.Склад КАК Склад,
| ТЧ_Продукция.Количество КАК Количество
|ПОМЕСТИТЬ ВтДанные
|ИЗ
| &ТЧ_Продукция КАК ТЧ_Продукция
|
|ОБЪЕДИНИТЬ ВСЕ
|
|ВЫБРАТЬ
| ТЧ_Сборка.Номенклатура,
| ТЧ_Сборка.Склад,
| ТЧ_Сборка.Количество
|ИЗ
| &ТЧ_Сборка КАК ТЧ_Сборка
|;
|
|////////////////////////////////////////////////////////////////////////////////
|ВЫБРАТЬ
| ВтДанные.Номенклатура,
| ВтДанные.Склад,
| СУММА(ВтДанные.Количество) КАК Количество
|ИЗ
| ВтДанные КАК ВтДанные
|ГРУППИРОВАТЬ ПО
| ВтДанные.Номенклатура,
| ВтДанные.Склад";
Запрос.УстановитьПараметр("ТЧ_Продукция", ПродукцияТП.Выгрузить());
Запрос.УстановитьПараметр("ТЧ_Сборка", ПродукцияСборки.Выгрузить());
РезультатЗапроса = Запрос.Выполнить();
Выборка = РезультатЗапроса.Выбрать();
Движения.СвободныеОстатки.Записывать = Истина;
Пока Выборка.Следующий() Цикл
Движение = Движения.СвободныеОстатки.ДобавитьПриход();
Движение.Период = Дата;
ЗаполнитьЗначенияСвойств(Движение, Выборка);
КонецЦикла;
Обратите внимание на использование функции ЗаполнитьЗначенияСвойств(Куда, Откуда). Это значительно сокращает код и уменьшает вероятность ошибок. Вместо того чтобы вручную присваивать Движение.Номенклатура = Выборка.Номенклатура для каждого поля, платформа сама найдет совпадающие по именам свойства и скопирует их значения. Это особенно полезно, когда в регистре много измерений и ресурсов.
Если по каким-то причинам вам всё же необходимо использовать режим «Не удалять автоматически», вы должны самостоятельно очищать движения перед записью. Разберем, как это сделать правильно:
// Очистка движений вручную в начале процедуры ОбработкаПроведения
Движения.СвободныеОстатки.Записывать = Истина;
Движения.СвободныеОстатки.Очистить();
// После этого можно добавлять новые движения
Важно помнить: метод Очистить() работает с коллекцией в оперативной памяти. Физическое удаление из базы данных произойдет только тогда, когда набор будет записан (в конце процедуры ОбработкаПроведения или при явном вызове Движения.СвободныеОстатки.Записать()).
Подведем итоги. Чтобы избежать задвоения данных в регистрах накопления:
ЗаполнитьЗначенияСвойств для заполнения полей движений.