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