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

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

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

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

Причина возникновения ошибки отбора

Проанализируем ситуацию: когда регистр сведений является подчиненным, его записи жестко привязаны к физическим таблицам движений документов. Платформа 1С оптимизирована таким образом, что работа с набором записей возможна только в контексте одного документа. Поэтому объект Отбор у набора записей в этом режиме содержит только одно свойство — Регистратор. Попытка обращения к НаборЗаписей.Отбор.Сотрудник вызывает исключение, так как такого свойства у программного объекта просто не существует.

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

Алгоритм решения задачи: пошаговое руководство

Для корректного удаления записей по конкретному измерению (в нашем примере — по сотруднику) нам необходимо выполнить следующую последовательность действий:

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

Способ 1. Использование таблицы значений для фильтрации

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


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

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

Пока Выборка.Следующий() Цикл
    
    // 2. Создаем набор записей с отбором по регистратору
    НаборЗаписей = РегистрыСведений.НачальныеОстаткиОтпусков.СоздатьНаборЗаписей();
    НаборЗаписей.Отбор.Регистратор.Установить(Выборка.Регистратор);
    
    // 3. Читаем данные из базы в объект
    НаборЗаписей.Прочитать();
    
    // 4. Выгружаем записи в таблицу значений для удобной манипуляции
    ТаблицаДанных = НаборЗаписей.Выгрузить();
    
    // 5. Ищем строки, соответствующие нашему условию (сотруднику)
    ПараметрыОтбора = Новый Структура("Сотрудник", Объект.НужныйСотрудник);
    НайденныеСтроки = ТаблицаДанных.НайтиСтроки(ПараметрыОтбора);
    
    // 6. Удаляем лишние строки из таблицы
    Для Каждого СтрокаДляУдаления Из НайденныеСтроки Цикл
        ТаблицаДанных.Удалить(СтрокаДляУдаления);
    КонецЦикла;
    
    // 7. Загружаем очищенную таблицу обратно в набор и записываем в БД
    НаборЗаписей.Загрузить(ТаблицаДанных);
    НаборЗаписей.Записать();
    
КонецЦикла;

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

Способ 2. Обход коллекции набора записей с конца (Оптимальный)

Работа с промежуточной таблицей значений (как в первом способе) на больших объемах данных может потреблять лишнюю оперативную память. Мы можем модифицировать записи непосредственно внутри коллекции НаборЗаписей. Однако здесь есть тонкость: при удалении элемента из коллекции во время прямого обхода (Для Каждого...) индексы последующих элементов смещаются, что приводит к ошибкам или пропуску строк.

Рассмотрим правильный метод — обход коллекции с конца с использованием индекса:


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

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

// Устанавливаем флаг загрузки данных, чтобы пропустить лишние проверки
НаборЗаписей.ОбменДанными.Загрузка = Истина;
НаборЗаписей.Записать();

Использование свойства ОбменДанными.Загрузка

Проанализируем ситуацию с производительностью и бизнес-логикой. В модулях набора записей или в подписках на события часто прописаны процедуры, выполняющие дополнительные проверки при записи или обновляющие связанные данные. При массовом удалении или технической очистке эти проверки могут существенно замедлить процесс или вовсе заблокировать его. Чтобы избежать этого, рекомендуется устанавливать свойство НаборЗаписей.ОбменДанными.Загрузка = Истина;. Это сообщает платформе, что запись производится в режиме «обмена», и большинство стандартных проверок будут проигнорированы.

Риски перепроведения документов

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

Если ваша задача — удалить данные навсегда, вам необходимо:

Работа с большими объемами и блокировки

Рассмотрим технические нюансы при работе в многопользовательской среде. Запись набора записей накладывает исключительную блокировку на записи этого регистратора. Если вы обрабатываете тысячи регистраторов в одной транзакции, вы можете парализовать работу пользователей, пытающихся провести документы в это время.

Для минимизации конфликтов рекомендуется:

  1. Выполнять запись каждого набора в отдельной транзакции (не оборачивать весь цикл по регистраторам в одну транзакцию).
  2. Если объем правок колоссален, делать небольшие паузы между транзакциями.
  3. Использовать разделение по времени: проводить такие операции в часы минимальной активности пользователей.

Проблема «битых» записей и пустых регистраторов

Иногда в базе данных возникают аномалии: записи в подчиненном регистре существуют, а документ-регистратор был удален или помечен на удаление без очистки движений (например, из-за сбоев или некорректных обменов данными). В таком случае Отбор.Регистратор.Установить() может не сработать, если регистратор равен Неопределено или ссылается на объект, которого нет в базе.

Для исправления таких ситуаций следует использовать встроенные средства платформы: «Администрирование» -> «Обслуживание» -> «Тестирование и исправление» с включенной опцией «Очистка движений при удалении регистратора». Программное удаление «битых» записей напрямую через объектную модель 1С крайне затруднено, так как платформа требует валидный объект ссылки для отбора.

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

← На главную