Как исправить ошибку «Поле объекта не обнаружено (ТипЗначения)» в 1С:Предприятии 8.3

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

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

{Документ.АктПроизводственногоКонтроля.Форма.пбФормаДокумента.Форма(705)}: Поле объекта не обнаружено (ТипЗначения)

Эта ошибка чаще всего возникает в программном коде, который пытается динамически типизировать или заполнить ячейки табличной части. Проанализируем, почему возникает эта проблема, как устроена работа с типами данных в управляемых формах 1С 8.3 и как правильно переписать проблемный код.

Выясняем причину ошибки

Рассмотрим проблемный фрагмент кода, на который указывает платформа:


Процедура ЗаполнитьСписок(Объект, ИмяРеквизита, ИмяТаблицы, ИмяПодчиненнойТаблицы, ТипЗначения)
    
    Для Каждого Элемент Из Объект[ИмяТаблицы] Цикл
        
        // Ошибка возникает на этой строке:
        Элемент[ИмяРеквизита].ТипЗначения = Новый ОписаниеТипов(ТипЗначения);
        
        СтрокиПоиска = Объект[ИмяПодчиненнойТаблицы].НайтиСтроки(Новый Структура("Идентификатор", Элемент.ИдентификаторНарушения));
        Для Каждого Строка Из СтрокиПоиска Цикл
            Элемент[ИмяРеквизита].Добавить(Строка[ИмяРеквизита]);
        КонецЦикла;
        
    КонецЦикла;
    
КонецПроцедуры

Выясним, почему этот код вызывает аварийное завершение работы программы. В Обычных формах (на платформе 1С 8.2) разработчики часто использовали удобный прием: программно подменяли тип значения в ячейке табличного поля на СписокЗначений. Это позволяло выбирать несколько элементов в рамках одной строки.

В Управляемых формах (на платформе 1С 8.3) архитектура работы с данными кардинально изменилась:

  1. Строка таблицы на форме представлена объектом типа ДанныеФормыЭлементКоллекции.
  2. При обращении к свойству строки через квадратные скобки Элемент[ИмяРеквизита] платформа возвращает непосредственное значение, хранящееся в базе данных (например, ссылку СправочникСсылка.НарушенныеТребования или пустое значение Неопределено).
  3. Поскольку значением является простая ссылка, у нее физически отсутствует свойство ТипЗначения и метод Добавить(). Это и приводит к ошибке «Поле объекта не обнаружено (ТипЗначения)».

Даже если в отладчике на клиенте вам кажется, что реквизит ведет себя как список, реальный тип в структуре данных остается ссылочным. Попытка обойти такую переменную циклом Для Каждого вызовет другую ошибку: «Итератор для значения не определен».

Разберем детально, как решить эту задачу правильно в соответствии со стандартами разработки 1С.

Вариант решения А. Использование виртуального реквизита формы

Если вам необходимо предоставить пользователю возможность выбирать несколько значений прямо в ячейке таблицы на управляемой форме, мы должны использовать специальный реквизит формы типа СписокЗначений, который не связан напрямую с базой данных.

Выполним следующие шаги для настройки формы:

  1. Откроем форму документа в конфигураторе.
  2. В окне «Реквизиты» раскроем нашу основную табличную часть (например, Нарушения) и добавим новый реквизит формы (не реквизит объекта!). Назовем его НарушенныеТребованияСписок.
  3. В свойствах созданного реквизита укажем тип данных: СписокЗначений.
  4. В поле «Тип значения списка» выберем тип справочника, элементы которого будут выбираться (например, СправочникСсылка.НарушенныеТребования).
  5. Перетащим этот реквизит на форму в качестве колонки таблицы вместо старого ссылочного поля.

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


&НаСервере
Процедура ЗаполнитьСписокНаСервере()
    
    // Перебираем строки коллекции на форме
    Для Каждого СтрокаТаблицы Из Объект.Нарушения Цикл
        
        // Теперь здесь доступен полноценный СписокЗначений, созданный на форме
        СписокНаФорме = СтрокаТаблицы.НарушенныеТребованияСписок;
        СписокНаФорме.Очистить();
        
        // Ограничиваем типы значений для списка, если это необходимо
        СписокНаФорме.ТипЗначения = Новый ОписаниеТипов("СправочникСсылка.НарушенныеТребования");
        
        // Находим связанные строки в подчиненной табличной части объекта
        Отбор = Новый Структура("ИдентификаторНарушения", СтрокаТаблицы.ИдентификаторНарушения);
        НайденныеСтроки = Объект.ПодчиненнаяТаблицаТребований.НайтиСтроки(Отбор);
        
        Для Каждого ПодчиненнаяСтрока Из НайденныеСтроки Цикл
            СписокНаФорме.Добавить(ПодчиненнаяСтрока.Требование);
        КонецЦикла;
        
    КонецЦикла;
    
КонецПроцедуры

При записи документа нам потребуется выполнить обратную операцию: перенести данные из временных списков значений на форме в реальную физическую подчиненную табличную часть документа. Для этого пропишем код в обработчике события ПередЗаписьюНаСервере:


&НаСервере
Процедура ПередЗаписьюНаСервере(Отказ, ТекущийОбъект, ПараметрыЗаписи)
    
    // Очищаем подчиненную таблицу перед перезаписью данных
    ТекущийОбъект.ПодчиненнаяТаблицаТребований.Очистить();
    
    Для Каждого СтрокаФормы Из Объект.Нарушения Цикл
        
        // Считываем данные из списка значений на форме
        Для Каждого ЭлементСписка Из СтрокаФормы.НарушенныеТребованияСписок Цикл
            
            НоваяСтрока = ТекущийОбъект.ПодчиненнаяТаблицаТребований.Добавить();
            НоваяСтрока.ИдентификаторНарушения = СтрокаФормы.ИдентификаторНарушения;
            НоваяСтрока.Требование = ЭлементСписка.Значение;
            
        КонецЦикла;
        
    КонецЦикла;
    
КонецПроцедуры

Вариант решения Б. Строковое представление и выбор во всплывающем окне

Передача тяжелых коллекций вроде СписокЗначений в каждой строке динамической таблицы на форме существенно увеличивает сетевой трафик между сервером и тонким клиентом. Проанализируем более производительный вариант, соответствующий стандартам разработки фирмы «1С»:

  1. В физической табличной части оставим только текстовое поле ПредставлениеТребований (тип Строка).
  2. Для редактирования списка требований назначим кнопке выбора ячейки (событие НачалоВыбора) открытие модального или вспомогательного окна с подбором элементов.
  3. После закрытия окна выбора сохраняем данные напрямую в подчиненную табличную часть, а в основной строке формируем красивую текстовую строку через запятую.

Рассмотрим пример реализации обработчика выбора ячейки:


&НаКлиенте
Процедура ТаблицаНарушенийТребованиеНачалоВыбора(Элемент, ДанныеВыбора, СтандартнаяОбработка)
    
    СтандартнаяОбработка = Ложь;
    ТекущаяСтрока = Элементы.ТаблицаНарушений.ТекущиеДанные;
    
    Если ТекущаяСтрока = Неопределено Тогда
        Возврат;
    КонецЕсли;
    
    // Формируем параметры для открытия формы выбора или подбора
    ПараметрыФормы = Новый Структура;
    ПараметрыФормы.Вставить("МножественныйВыбор", Истина);
    
    // Открываем форму выбора справочника
    ОткрытьФорму("Справочник.НарушенныеТребования.ФормаВыбора", ПараметрыФормы, Элемент);
    
КонецПроцедуры

Бонус: Исправление критической ошибки удаления строк

Разработчики, работающие с логикой связи табличных частей «один ко многим» по уникальному идентификатору, часто совершают критическую ошибку при удалении строк из ведущей таблицы. Проанализируем типичный ошибочный код:


// ОШИБОЧНЫЙ КОД!
ПодчиненныеСтроки = Объект.ФайлыПоНарушениям.НайтиСтроки(Новый Структура("ИдентификаторНарушения", Идентификатор));
Для Каждого ПодчиненнаяСтрока Из ПодчиненныеСтроки Цикл
    Объект.ПриостановленныеРаботы.Удалить(ПодчиненнаяСтрока); 
КонецЦикла;

В чем заключается проблема? Код ищет связанные данные в одной табличной части (ФайлыПоНарушениям), а пытается удалить полученные строки из совершенно другой таблицы (ПриостановленныеРаботы). Из-за несовпадения индексов и коллекций платформа незамедлительно выбросит исключение «Индекс находится за границами массива».

Устраним эту ошибку, обращаясь к правильной коллекции:


// ИСПРАВЛЕННЫЙ КОД
ПодчиненныеСтроки = Объект.ФайлыПоНарушениям.НайтиСтроки(Новый Структура("ИдентификаторНарушения", Идентификатор));
Для Каждого ПодчиненнаяСтрока Из ПодчиненныеСтроки Цикл
    // Удаляем строго из той коллекции, в которой производился поиск!
    Объект.ФайлыПоНарушениям.Удалить(ПодчиненнаяСтрока); 
КонецЦикла;

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

← На главную