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