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