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