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