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