Почему при проведении документа в 1С двоятся записи в регистрах накопления?

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

При разработке сложных механизмов проведения документов начинающие разработчики часто сталкиваются с тем, что данные в регистрах накопления начинают «задваиваться» или «затраиваться». Ситуация выглядит следующим образом: при первом проведении документа движения записываются верно, но после повторного проведения количество записей увеличивается в геометрической прогрессии. Разберем эту проблему подробно, проанализируем причины и способы оптимизации кода.

Причина 1: Свойство документа «Удаление движений»

Самая распространенная причина задвоения данных — неправильно выбранный режим удаления движений в свойствах документа на вкладке «Основные». Рассмотрим, как работают разные режимы:

Проанализируем ситуацию автора темы: у него стоял режим «Удалять автоматически при отмене проведения». При нажатии кнопки «ОК» в документе система видела, что документ уже проведен, не очищала старые движения и добавляла в циклы новые. Решение заключалось в переключении свойства на «Удалять автоматически».

Причина 2: Скрытое влияние других регистров и подписок

Если вы закомментировали код формирования движений по одному регистру (например, СвободныеОстатки), а записи в нем всё равно появляются — значит, работает «скрытая» логика. Выясним причину:

  1. Подписки на события: Проверьте в дереве метаданных узел «Общие -> Подписки на события». Возможно, существует обработчик события ПриЗаписи какого-то другого регистра (например, ТоварыНаСкладах), который программно создает записи в СвободныеОстатки.
  2. Модули наборов записей: Загляните в модуль самого регистра накопления. В процедурах ПередЗаписью или ПриЗаписи может быть прописана логика дублирования данных в соседние регистры.

В подобных случаях стоит использовать отладчик, установив точку остановки непосредственно в момент записи набора записей регистра.

Оптимизация кода: от циклов к запросам

Код, представленный в вопросе, содержит множество однотипных циклов Для Каждого по разным табличным частям. Это «антипаттерн» разработки в 1С. Рассмотрим, почему так делать не стоит:

Рассмотрим правильный подход: использование запроса. Мы можем объединить данные всех табличных частей в одну временную таблицу, сгруппировать их и обойти одним циклом.


// Пример правильной структуры проведения
Запрос = Новый Запрос;
Запрос.Текст = 
    "ВЫБРАТЬ
    |   ТЧ_Продукция.Номенклатура КАК Номенклатура,
    |   ТЧ_Продукция.Склад КАК Склад,
    |   ТЧ_Продукция.Количество КАК Количество
    |ПОМЕСТИТЬ ВтДанные
    |ИЗ
    |   &ТЧ_Продукция КАК ТЧ_Продукция
    |
    |ОБЪЕДИНИТЬ ВСЕ
    |
    |ВЫБРАТЬ
    |   ТЧ_Сборка.Номенклатура,
    |   ТЧ_Сборка.Склад,
    |   ТЧ_Сборка.Количество
    |ИЗ
    |   &ТЧ_Сборка КАК ТЧ_Сборка
    |;
    |
    |////////////////////////////////////////////////////////////////////////////////
    |ВЫБРАТЬ
    |   ВтДанные.Номенклатура,
    |   ВтДанные.Склад,
    |   СУММА(ВтДанные.Количество) КАК Количество
    |ИЗ
    |   ВтДанные КАК ВтДанные
    |ГРУППИРОВАТЬ ПО
    |   ВтДанные.Номенклатура,
    |   ВтДанные.Склад";

Запрос.УстановитьПараметр("ТЧ_Продукция", ПродукцияТП.Выгрузить());
Запрос.УстановитьПараметр("ТЧ_Сборка", ПродукцияСборки.Выгрузить());

РезультатЗапроса = Запрос.Выполнить();
Выборка = РезультатЗапроса.Выбрать();

Движения.СвободныеОстатки.Записывать = Истина;
Пока Выборка.Следующий() Цикл
    Движение = Движения.СвободныеОстатки.ДобавитьПриход();
    Движение.Период = Дата;
    ЗаполнитьЗначенияСвойств(Движение, Выборка);
КонецЦикла;

Использование метода ЗаполнитьЗначенияСвойств

Обратите внимание на использование функции ЗаполнитьЗначенияСвойств(Куда, Откуда). Это значительно сокращает код и уменьшает вероятность ошибок. Вместо того чтобы вручную присваивать Движение.Номенклатура = Выборка.Номенклатура для каждого поля, платформа сама найдет совпадающие по именам свойства и скопирует их значения. Это особенно полезно, когда в регистре много измерений и ресурсов.

Ручное управление набором записей

Если по каким-то причинам вам всё же необходимо использовать режим «Не удалять автоматически», вы должны самостоятельно очищать движения перед записью. Разберем, как это сделать правильно:


// Очистка движений вручную в начале процедуры ОбработкаПроведения
Движения.СвободныеОстатки.Записывать = Истина;
Движения.СвободныеОстатки.Очистить();
// После этого можно добавлять новые движения

Важно помнить: метод Очистить() работает с коллекцией в оперативной памяти. Физическое удаление из базы данных произойдет только тогда, когда набор будет записан (в конце процедуры ОбработкаПроведения или при явном вызове Движения.СвободныеОстатки.Записать()).

Заключение

Подведем итоги. Чтобы избежать задвоения данных в регистрах накопления:

  1. Установите свойство документа «Удаление движений» в значение «Удалять автоматически».
  2. Проверьте наличие сторонних механизмов (подписок или модулей регистров), которые могут влиять на результат.
  3. Оптимизируйте код проведения: используйте запросы для подготовки данных и ЗаполнитьЗначенияСвойств для заполнения полей движений.
  4. Всегда группируйте данные перед записью в регистр, чтобы избежать «лесенки» из одинаковых строк в итогах регистра.
← На главную