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