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