При программном создании или обновлении элементов справочников (как внутри базы, так и через внешнее COM-соединение) разработчики часто сталкиваются с ошибкой «Поле объекта недоступно для записи» при попытке изменить реквизит. Разберем по шагам, почему возникает эта ошибка, как работает объектная модель платформы 1С и как правильно переписать код для стабильной работы.
Проанализируем исходный фрагмент кода, вызвавший ошибку:
НовыйКлиент = ComConnection.Справочники.ФизическиеЛица.НайтиПоРеквизиту("КодФизЛицаВОтель", Guest.Code);
Если ЗначениеЗаполнено(НовыйКлиент.КодФизЛицаВОтель) Тогда
НовыйКлиент.ПолучитьОбъект();
Иначе
НовыйКлиент = ComConnection.Справочники.ФизическиеЛица.СоздатьЭлемент();
КонецЕсли;
НовыйКлиент.Наименование = Guest.LastName + " " + Guest.FirstName + " " + Guest.SecondName;
Выясним причину падения программы. Метод НайтиПоРеквизиту() возвращает значение типа СправочникСсылка (или пустую ссылку, если ничего не найдено). В платформе 1С ссылка предназначена только для чтения. Попытка напрямую присвоить значение реквизиту ссылки (например, НовыйКлиент.Наименование = ...) всегда приводит к ошибке «Поле объекта недоступно для записи».
Для модификации данных в базе необходимо получить экземпляр типа СправочникОбъект с помощью метода ПолучитьОбъект(). Однако метод ПолучитьОбъект() не преобразует саму переменную, у которой он вызван, а возвращает новый объект. Если результат вызова метода ничему не присвоить, переменная так и останется неизменяемой ссылкой.
Рассмотрим, как правильно организовать вызов метода ПолучитьОбъект(). Результат функции необходимо обязательно сохранить в рабочую переменную:
НовыйКлиент = НовыйКлиент.ПолучитьОбъект();
После такого присвоения переменная НовыйКлиент начинает содержать тип СправочникОбъект, чьи реквизиты открыты для записи.
Посмотрим на сопутствующие ошибки в логике исходного фрагмента и разберем, как сделать алгоритм надежным:
ЗначениеЗаполнено(НовыйКлиент.КодФизЛицаВОтель) выполняет лишнее неявное обращение к базе данных через точку. Если реквизит у найденного элемента окажется пустым, программа посчитает, что элемент не найден, и создаст дубль. Проверять найденную ссылку следует с помощью ЗначениеЗаполнено(НовыйКлиент) или метода Не НовыйКлиент.Пустая().ПолучитьОбъект(). Если элемент был помечен на удаление и физически удален между операциями или заблокирован, метод ПолучитьОбъект() вернет Неопределено. Рекомендуется проверять полученный объект перед модификацией.НовыйКлиент.Наименование = ... изменяет реквизит исключительно в оперативной памяти. Для фиксации данных в базе данных обязательно требуется вызвать метод Записать().НайтиПоРеквизиту() выполняется быстро только в том случае, если для реквизита КодФизЛицаВОтель в метаданных включено свойство «Индексировать». В противном случае поиск повлечет полное сканирование таблицы справочника.Объединим все исправления в готовый и безопасный блок кода:
СсылкаНаКлиента = ComConnection.Справочники.ФизическиеЛица.НайтиПоРеквизиту("КодФизЛицаВОтель", Guest.Code);
Если ЗначениеЗаполнено(СсылкаНаКлиента) Тогда
ОбъектКлиента = СсылкаНаКлиента.ПолучитьОбъект();
Иначе
ОбъектКлиента = ComConnection.Справочники.ФизическиеЛица.СоздатьЭлемент();
ОбъектКлиента.КодФизЛицаВОтель = Guest.Code;
КонецЕсли;
Если ОбъектКлиента <> Неопределено Тогда
ОбъектКлиента.Наименование = Guest.LastName + " " + Guest.FirstName + " " + Guest.SecondName;
ОбъектКлиента.Записать();
КонецЕсли;
Если после исправления работы с объектом ошибка сохраняется, рассмотрим другие возможные причины:
ФизическиеЛица либо конкретных реквизитов.