При переходе с обычных форм на управляемые или при доработке существующих конфигураций (особенно сложных отраслевых решений, таких как «1С:Производственная безопасность») разработчики часто сталкиваются со следующей ошибкой:
{Документ.АктПроизводственногоКонтроля.Форма.пбФормаДокумента.Форма(705)}: Поле объекта не обнаружено (ТипЗначения)
Эта ошибка чаще всего возникает в программном коде, который пытается динамически типизировать или заполнить ячейки табличной части. Проанализируем, почему возникает эта проблема, как устроена работа с типами данных в управляемых формах 1С 8.3 и как правильно переписать проблемный код.
Рассмотрим проблемный фрагмент кода, на который указывает платформа:
Процедура ЗаполнитьСписок(Объект, ИмяРеквизита, ИмяТаблицы, ИмяПодчиненнойТаблицы, ТипЗначения)
Для Каждого Элемент Из Объект[ИмяТаблицы] Цикл
// Ошибка возникает на этой строке:
Элемент[ИмяРеквизита].ТипЗначения = Новый ОписаниеТипов(ТипЗначения);
СтрокиПоиска = Объект[ИмяПодчиненнойТаблицы].НайтиСтроки(Новый Структура("Идентификатор", Элемент.ИдентификаторНарушения));
Для Каждого Строка Из СтрокиПоиска Цикл
Элемент[ИмяРеквизита].Добавить(Строка[ИмяРеквизита]);
КонецЦикла;
КонецЦикла;
КонецПроцедуры
Выясним, почему этот код вызывает аварийное завершение работы программы. В Обычных формах (на платформе 1С 8.2) разработчики часто использовали удобный прием: программно подменяли тип значения в ячейке табличного поля на СписокЗначений. Это позволяло выбирать несколько элементов в рамках одной строки.
В Управляемых формах (на платформе 1С 8.3) архитектура работы с данными кардинально изменилась:
ДанныеФормыЭлементКоллекции.Элемент[ИмяРеквизита] платформа возвращает непосредственное значение, хранящееся в базе данных (например, ссылку СправочникСсылка.НарушенныеТребования или пустое значение Неопределено).ТипЗначения и метод Добавить(). Это и приводит к ошибке «Поле объекта не обнаружено (ТипЗначения)».Даже если в отладчике на клиенте вам кажется, что реквизит ведет себя как список, реальный тип в структуре данных остается ссылочным. Попытка обойти такую переменную циклом Для Каждого вызовет другую ошибку: «Итератор для значения не определен».
Разберем детально, как решить эту задачу правильно в соответствии со стандартами разработки 1С.
Если вам необходимо предоставить пользователю возможность выбирать несколько значений прямо в ячейке таблицы на управляемой форме, мы должны использовать специальный реквизит формы типа СписокЗначений, который не связан напрямую с базой данных.
Выполним следующие шаги для настройки формы:
Нарушения) и добавим новый реквизит формы (не реквизит объекта!). Назовем его НарушенныеТребованияСписок.СписокЗначений.СправочникСсылка.НарушенныеТребования).Теперь наш виртуальный реквизит формы действительно содержит полноценный объект СписокЗначений для каждой строки. Перепишем процедуру заполнения с учетом новых данных:
&НаСервере
Процедура ЗаполнитьСписокНаСервере()
// Перебираем строки коллекции на форме
Для Каждого СтрокаТаблицы Из Объект.Нарушения Цикл
// Теперь здесь доступен полноценный СписокЗначений, созданный на форме
СписокНаФорме = СтрокаТаблицы.НарушенныеТребованияСписок;
СписокНаФорме.Очистить();
// Ограничиваем типы значений для списка, если это необходимо
СписокНаФорме.ТипЗначения = Новый ОписаниеТипов("СправочникСсылка.НарушенныеТребования");
// Находим связанные строки в подчиненной табличной части объекта
Отбор = Новый Структура("ИдентификаторНарушения", СтрокаТаблицы.ИдентификаторНарушения);
НайденныеСтроки = Объект.ПодчиненнаяТаблицаТребований.НайтиСтроки(Отбор);
Для Каждого ПодчиненнаяСтрока Из НайденныеСтроки Цикл
СписокНаФорме.Добавить(ПодчиненнаяСтрока.Требование);
КонецЦикла;
КонецЦикла;
КонецПроцедуры
При записи документа нам потребуется выполнить обратную операцию: перенести данные из временных списков значений на форме в реальную физическую подчиненную табличную часть документа. Для этого пропишем код в обработчике события ПередЗаписьюНаСервере:
&НаСервере
Процедура ПередЗаписьюНаСервере(Отказ, ТекущийОбъект, ПараметрыЗаписи)
// Очищаем подчиненную таблицу перед перезаписью данных
ТекущийОбъект.ПодчиненнаяТаблицаТребований.Очистить();
Для Каждого СтрокаФормы Из Объект.Нарушения Цикл
// Считываем данные из списка значений на форме
Для Каждого ЭлементСписка Из СтрокаФормы.НарушенныеТребованияСписок Цикл
НоваяСтрока = ТекущийОбъект.ПодчиненнаяТаблицаТребований.Добавить();
НоваяСтрока.ИдентификаторНарушения = СтрокаФормы.ИдентификаторНарушения;
НоваяСтрока.Требование = ЭлементСписка.Значение;
КонецЦикла;
КонецЦикла;
КонецПроцедуры
Передача тяжелых коллекций вроде СписокЗначений в каждой строке динамической таблицы на форме существенно увеличивает сетевой трафик между сервером и тонким клиентом. Проанализируем более производительный вариант, соответствующий стандартам разработки фирмы «1С»:
ПредставлениеТребований (тип Строка).НачалоВыбора) открытие модального или вспомогательного окна с подбором элементов.Рассмотрим пример реализации обработчика выбора ячейки:
&НаКлиенте
Процедура ТаблицаНарушенийТребованиеНачалоВыбора(Элемент, ДанныеВыбора, СтандартнаяОбработка)
СтандартнаяОбработка = Ложь;
ТекущаяСтрока = Элементы.ТаблицаНарушений.ТекущиеДанные;
Если ТекущаяСтрока = Неопределено Тогда
Возврат;
КонецЕсли;
// Формируем параметры для открытия формы выбора или подбора
ПараметрыФормы = Новый Структура;
ПараметрыФормы.Вставить("МножественныйВыбор", Истина);
// Открываем форму выбора справочника
ОткрытьФорму("Справочник.НарушенныеТребования.ФормаВыбора", ПараметрыФормы, Элемент);
КонецПроцедуры
Разработчики, работающие с логикой связи табличных частей «один ко многим» по уникальному идентификатору, часто совершают критическую ошибку при удалении строк из ведущей таблицы. Проанализируем типичный ошибочный код:
// ОШИБОЧНЫЙ КОД!
ПодчиненныеСтроки = Объект.ФайлыПоНарушениям.НайтиСтроки(Новый Структура("ИдентификаторНарушения", Идентификатор));
Для Каждого ПодчиненнаяСтрока Из ПодчиненныеСтроки Цикл
Объект.ПриостановленныеРаботы.Удалить(ПодчиненнаяСтрока);
КонецЦикла;
В чем заключается проблема? Код ищет связанные данные в одной табличной части (ФайлыПоНарушениям), а пытается удалить полученные строки из совершенно другой таблицы (ПриостановленныеРаботы). Из-за несовпадения индексов и коллекций платформа незамедлительно выбросит исключение «Индекс находится за границами массива».
Устраним эту ошибку, обращаясь к правильной коллекции:
// ИСПРАВЛЕННЫЙ КОД
ПодчиненныеСтроки = Объект.ФайлыПоНарушениям.НайтиСтроки(Новый Структура("ИдентификаторНарушения", Идентификатор));
Для Каждого ПодчиненнаяСтрока Из ПодчиненныеСтроки Цикл
// Удаляем строго из той коллекции, в которой производился поиск!
Объект.ФайлыПоНарушениям.Удалить(ПодчиненнаяСтрока);
КонецЦикла;
Используя предложенные методы, вы сможете успешно адаптировать старый код под стандарты управляемого приложения 1С, избавитесь от ошибок обращения к свойствам ссылочных типов и оптимизируете скорость работы интерфейса вашей программы.