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

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

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

Разберем причину проблемы: механизм удаления движений

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

  1. Удалять автоматически: Платформа сама очищает все движения документа по всем регистрам перед началом проведения. Это исключает задвоение, но в «тяжелых» конфигурациях, таких как УПП, этот режим часто отключают для оптимизации производительности.
  2. Не удалять автоматически: Платформа никак не трогает старые записи. В этом случае ответственность за корректность данных полностью ложится на разработчика. Если вы не очистите набор записей программно, новые движения просто добавятся к старым.
  3. Удалять автоматически при отмене проведения: Движения очищаются только при ручной отмене проведения. При обычном перепроведении они остаются в базе.

Если в вашем документе установлено «Не удалять автоматически», то при перепроведении система будет наслаивать новые записи на старые. Именно эту ситуацию мы видим в коде автора темы.

Программное решение проблемы задвоения

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

Рассмотрим правильную структуру кода:


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

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

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

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

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

Особенности регистра «Свободные остатки» в УПП 1.3

Проанализируем ситуацию глубже. В типовой конфигурации УПП регистр СвободныеОстатки является ключевым для контроля остатков. Он показывает разницу между фактическим наличием товара на складе и зарезервированным количеством.

Важный нюанс: Многие документы в УПП используют общие механизмы проведения. Если вы создаете свой документ и вручную прописываете движения по ТоварыНаСкладах, вы обязаны синхронно обновлять и СвободныеОстатки. Если этого не сделать, возникнет разрыв: отчет «Ведомость по товарам на складах» будет показывать одно количество, а «Анализ доступности товаров» — другое.

Рассмотрим, как правильно организовать запись, используя метод Записать(Истина). Это более лаконичный способ, который сразу удаляет старые записи из базы данных:


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

// Далее формируем новые движения
Для Каждого ТекСтрока Из Материалы Цикл
    Движение = Движения.СвободныеОстатки.ДобавитьРасход();
    // ... заполнение реквизитов ...
КонецЦикла;

Движения.СвободныеОстатки.Записывать = Истина;

Параметр Истина в методе Записать() означает «Замещать». Это критически важно, так как именно этот параметр заставляет систему не просто добавить строки, а полностью перезаписать таблицу движений этого документа в базе.

Проблема «пустых» движений и оптимизация

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

Рекомендуется всегда устанавливать флаг Записывать = Истина в начале процедуры или использовать метод Очистить(). Однако, для повышения производительности (чтобы лишний раз не обращаться к серверу БД при перепроведении документа, который и так пустой), можно использовать совет из обсуждения: проверять наличие записей в коллекции перед какими-либо действиями.


Если Движения.СвободныеОстатки.Количество() > 0 Тогда
    Движения.СвободныеОстатки.Очистить();
КонецЕсли;

Хотя стоит заметить, что метод Очистить() работает с коллекцией в памяти и сам по себе не является ресурсоемким. Основная нагрузка ложится на этап записи набора в базу данных.

Влияние на последовательности и итоги

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

Итоговый алгоритм действий

Подведем итог. Чтобы исправить задвоение в регистре СвободныеОстатки (или любом другом):

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

Следуя этим правилам, вы обеспечите целостность данных в управленческом учете и корректную работу всех отчетов по остаткам в системе 1С:Предприятие.

← На главную