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