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