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