Почему возникает ошибка «Поле объекта не обнаружено» при программной записи в регистр сведений?

Программист 1С v8.3 (Управляемые формы) IT и автоматизация бизнеса
← На главную

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

Причина ошибки: разница между Набором записей и Записью регистра

Выясним главную причину ошибки. В платформе 1С объект РегистрСведенийНаборЗаписей представляет собой коллекцию строк, объединенных общим отбором. У самого объекта НаборЗаписей есть стандартные свойства и методы: коллекция Отбор, методы Прочитать(), Записать(), Добавить(), Очистить() и другие.

Однако сам объект НаборЗаписей не содержит свойств с именами измерений, ресурсов или реквизитов регистра. Эти свойства принадлежат исключительно объекту строки — РегистрСведенийЗапись, который получается при обходе набора в цикле или при вызове метода Добавить().

Посмотрим на типичный фрагмент кода, в котором допущена опечатка:


НовыйНаборЗаписей = РегистрыСведений.АнтиворСостояниеДокументаДиадок.СоздатьНаборЗаписей();
НоваяЗапись = НовыйНаборЗаписей.Добавить();
НоваяЗапись.Период = ТаблицаДокументов[Индекс].ДатаУчета;

// Ошибка: обращение к набору вместо созданной строки НоваяЗапись
НаборЗаписей.ДатаДокумента = ТаблицаДокументов[Индекс].Дата; 

НоваяЗапись.Номер = НомерДокумента;

В приведенном примере платформа пытается найти свойство ДатаДокумента у переменной НаборЗаписей, а не у НоваяЗапись, в результате чего генерируется исключение Поле объекта не обнаружено.

Корректная работа через объект НаборЗаписей

Разберем по шагам правильный алгоритм обновления существующей или создания новой записи с использованием одного набора записей. Создавать второй экземпляр набора в ветке Иначе не требуется — достаточно использовать исходный объект, в котором уже настроены отборы.


НаборЗаписей = РегистрыСведений.АнтиворСостояниеДокументаДиадок.СоздатьНаборЗаписей();

// 1. Устанавливаем отбор по всем ключевым полям (период и измерения)
НаборЗаписей.Отбор.Период.Установить(ТаблицаДокументов[Индекс].ДатаУчета);
НаборЗаписей.Отбор.ДатаДокумента.Установить(ТаблицаДокументов[Индекс].Дата);
НаборЗаписей.Отбор.Номер.Установить(НомерДокумента);
НаборЗаписей.Отбор.Тип.Установить(ТаблицаДокументов[Индекс].ТипДокумента);

// 2. Читаем существующие данные из базы
НаборЗаписей.Прочитать();

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

// 3. Записываем изменения с замещением по установленному отбору
НаборЗаписей.Записать(Истина);

Использование Менеджера записи для одиночных строк

Рассмотрим более простой и надежный способ для добавления или изменения одной конкретной записи регистра сведений — объект МенеджерЗаписи. В отличие от набора записей, менеджер записи предоставляет прямой доступ ко всем измерениям, ресурсам и реквизитам как к собственным свойствам.

Разберем по шагам реализацию той же логики через менеджер записи:

  1. Создаем менеджер записи с помощью метода СоздатьМенеджерЗаписи().
  2. Заполняем ключевые поля: период (для периодических регистров) и все измерения.
  3. Вызываем метод Прочитать(). Если запись существует в базе, менеджер считает ее ресурсы и реквизиты, а метод Выбран() вернет Истина.
  4. Заполняем или изменяем ресурсы.
  5. Вызываем метод Записать().

Посмотрим на пример кода с менеджером записи:


МенеджерЗаписи = РегистрыСведений.АнтиворСостояниеДокументаДиадок.СоздатьМенеджерЗаписи();

// Заполняем ключевые поля (период и измерения)
МенеджерЗаписи.Период = ТаблицаДокументов[Индекс].ДатаУчета;
МенеджерЗаписи.ДатаДокумента = ТаблицаДокументов[Индекс].Дата;
МенеджерЗаписи.Номер = НомерДокумента;
МенеджерЗаписи.Тип = ТаблицаДокументов[Индекс].ТипДокумента;

// Считываем текущее состояние
МенеджерЗаписи.Прочитать();

// Заполняем ресурс независимо от того, существовала запись ранее или создается заново
МенеджерЗаписи.СостояниеДокумента = ТаблицаДокументов[Индекс].СостояниеДокументооборота;

// Фиксируем изменения в базе данных
МенеджерЗаписи.Записать(Истина);

Использование МенеджерЗаписи существенно сокращает объем кода и исключает ошибки, связанные с неправильным обращением к коллекции или созданием лишних наборов записей.

Особенности метода Записать и параметра Замещать

Обратим внимание на поведение метода Записать() у объекта НаборЗаписей:

  1. Режим замещения (Замещать = Истина): по умолчанию метод удаляет из базы данных все строки, соответствующие установленным в Отбор значениям, и записывает строки из текущего набора. Важно: если отбор не был установлен вовсе, вызов Записать(Истина) полностью очистит таблицу регистра сведений.
  2. Режим вставки без замещения (Замещать = Ложь): выполняет прямую вставку строк. Если в базе уже существует запись с аналогичным набором ключевых полей (период + измерения), платформа вызовет критическую ошибку нарушения уникальности («Запись с такими ключевыми полями существует!»).

Оптимизация при массовой записи данных

Проанализируем ситуацию, когда необходимо записать множество строк из таблицы. Выполнение операций Прочитать() и Записать() в цикле для каждой отдельной строки создает избыточную нагрузку на СУБД из-за множества одиночных транзакций.

Для пакетной записи рекомендуется подготовить таблицу значений, имена колонок которой совпадают с именами измерений и ресурсов регистра, и применить метод Загрузить():


НаборЗаписей = РегистрыСведений.АнтиворСостояниеДокументаДиадок.СоздатьНаборЗаписей();

// Устанавливаем общий отбор, если перезаписывается конкретный разрез данных
// НаборЗаписей.Отбор.Тип.Установить(НужныйТип);

// Загружаем подготовленную ТаблицуЗначений со структурой полей регистра
НаборЗаписей.Загрузить(ТаблицаДокументов);

// Выполняем однократную запись всей пачки данных
НаборЗаписей.Записать(Истина);

Такой подход минимизирует количество обращений к базе данных и значительно ускоряет выполнение программного кода при больших объемах информации.

← На главную