Как удалить часть записей из регистра сведений, подчиненного регистратору

Программист 1С v8.3 (Управляемые формы) 1С:Зарплата и Управление Персоналом IT и автоматизация бизнеса
← На главную

В практике разработчика 1С часто возникает задача точечной очистки данных в регистрах сведений. Особую сложность представляют регистры, подчиненные регистратору, такие как РеестрКадровыхПриказов. В таких регистрах нельзя просто применить метод «удалить по фильтру», так как платформа жестко контролирует связь записей с документом-источником. В этой статье мы подробно разберем, как корректно удалить записи по конкретному сотруднику, сохранив при этом данные остальных сотрудников в том же документе.

Особенности архитектуры подчиненных регистров

Прежде чем приступать к написанию кода, проанализируем ситуацию. В регистрах сведений с режимом записи Подчинение регистратору отбор в объекте РегистрСведенийНаборЗаписей может быть установлен только по полю Регистратор. Это означает, что мы не можем просто написать НаборЗаписей.Отбор.Сотрудник.Установить(НужныйСотрудник). Попытка сделать это приведет к ошибке платформы. Нам придется работать с полным набором записей конкретного документа, фильтровать его в памяти и записывать обратно.

Способ 1. Программная модификация набора записей

Этот метод считается классическим «техническим» решением. Его суть заключается в том, чтобы прочитать все записи регистратора, найти среди них лишние, удалить их из коллекции и заместить старые данные в базе обновленным набором. Рассмотрим алгоритм по шагам:

  1. Сначала мы находим все регистраторы, в которых есть записи по целевому сотруднику, используя запрос.
  2. Для каждого регистратора создаем объект НаборЗаписей.
  3. Читаем данные, фильтруем массив записей и выполняем запись.

Проанализируем пример кода, реализующий этот подход:


Запрос = Новый Запрос;
Запрос.Текст = 
    "ВЫБРАТЬ РАЗЛИЧНЫЕ
    |   Реестр.Регистратор КАК Регистратор
    |ИЗ
    |   РегистрСведений.РеестрКадровыхПриказов КАК Реестр
    |ГДЕ
    |   Реестр.Сотрудник = &Сотрудник";

Запрос.УстановитьПараметр("Сотрудник", ВыбранныйСотрудник);
РезультатЗапроса = Запрос.Выполнить();
Выборка = РезультатЗапроса.Выбрать();

Пока Выборка.Следующий() Цикл
    
    // Создаем набор записей с отбором по конкретному документу
    НаборЗаписей = РегистрыСведений.РеестрКадровыхПриказов.СоздатьНаборЗаписей();
    НаборЗаписей.Отбор.Регистратор.Установить(Выборка.Регистратор);
    НаборЗаписей.Прочитать();
    
    // Используем промежуточный массив для хранения ссылок на удаляемые строки
    МассивКУдалению = Новый Массив;
    Для Каждого СтрЗаписи Из НаборЗаписей Цикл
        Если СтрЗаписи.Сотрудник = ВыбранныйСотрудник Тогда
            МассивКУдалению.Добавить(СтрЗаписи);
        КонецЕсли;
    КонецЦикла;
    
    // Удаляем помеченные записи из набора
    Для Каждого СтрИзМассива Из МассивКУдалению Цикл
        НаборЗаписей.Удалить(СтрИзМассива);
    КонецЦикла;
    
    // Записываем обновленный набор (параметр Замещать по умолчанию Истина)
    НаборЗаписей.Записать();
    
КонецЦикла;

Важный нюанс: Мы используем МассивКУдалению, потому что нельзя удалять элементы из коллекции непосредственно в процессе обхода этой же коллекции через Для Каждого. Это приведет к непредсказуемому поведению итератора.

Способ 2. Перезапись набора через результат запроса

Если записей в одном регистраторе очень много (например, десятки тысяч в документах типа Перенос данных), обход коллекции в цикле может быть медленным. Рассмотрим альтернативный вариант: выберем запросом все данные регистратора, кроме записей по ненужному сотруднику, и «зальем» их в пустой набор.


// Запрос выбирает все записи регистратора, исключая целевого сотрудника
Запрос.Текст = 
    "ВЫБРАТЬ *
    |ИЗ РегистрСведений.РеестрКадровыхПриказов КАК Реестр
    |ГДЕ Реестр.Регистратор = &Регистратор И НЕ Реестр.Сотрудник = &Сотрудник";

Запрос.УстановитьПараметр("Регистратор", СсылкаНаДокумент);
Запрос.УстановитьПараметр("Сотрудник", ВыбранныйСотрудник);

Результат = Запрос.Выполнить();
Выборка = Результат.Выбрать();

НЗ = РегистрыСведений.РеестрКадровыхПриказов.СоздатьНаборЗаписей();
НЗ.Отбор.Регистратор.Установить(СсылкаНаДокумент);

Пока Выборка.Следующий() Цикл
    ЗаполнитьЗначенияСвойств(НЗ.Добавить(), Выборка);
КонецЦикла;

НЗ.Записать(Истина); // Полностью замещаем записи регистратора в БД

Этот метод работает быстрее при больших объемах данных, так как фильтрация происходит на уровне СУБД, а не в коде 1С.

Способ 3. Удаление через объект-регистратор (наиболее надежный)

Проанализируем критическую проблему первых двух способов: если мы удалим записи напрямую из регистра, но в самом документе (в его табличных частях) данные останутся — при первом же перепроведении документа записи в регистре восстановятся. Если ваша задача — окончательное удаление, правильнее работать с объектом документа.

Разберем этот процесс подробнее:

  1. Получаем объект документа-регистратора.
  2. Находим строку в соответствующей табличной части документа.
  3. Удаляем строку из документа.
  4. Записываем документ в режиме проведения.

ДокументОбъект = Выборка.Регистратор.ПолучитьОбъект();
ПараметрыПоиска = Новый Структура("Сотрудник", ВыбранныйСотрудник);

// Предположим, данные регистра строятся по табличной части "Состав"
МассивСтрок = ДокументОбъект.Состав.НайтиСтроки(ПараметрыПоиска);

Если МассивСтрок.Количество() > 0 Тогда
    Для Каждого СтрокаТЧ Из МассивСтрок Цикл
        ДокументОбъект.Состав.Удалить(СтрокаТЧ);
    КонецЦикла;
    
    Попытка
        ДокументОбъект.Записать(РежимЗаписиДокумента.Проведение);
    Исключение
        Сообщить("Не удалось провести документ: " + ОписаниеОшибки());
    КонецПопытки;
КонецЕсли;

Преимущество этого метода: Вы соблюдаете ссылочную целостность системы. Записи регистра будут приведены в соответствие с документом автоматически самой платформой. Минус: Перепроведение документа может занять много времени и затронуть другие регистры, на которые влияет этот документ.

Подведение итогов и рекомендации

Выясним, какой метод выбрать в конкретной ситуации:

Перед выполнением подобных операций обязательно делайте резервную копию базы данных или проверяйте работу кода на копии. Прямая манипуляция записями регистров — это мощный, но опасный инструмент, требующий понимания логики работы конкретной конфигурации.

← На главную