При разработке в среде 1С:Предприятие одной из самых распространенных ошибок начинающих программистов является попытка прямого обращения к реквизиту табличной части через имя самой таблицы, например: Объект.Товары.Наименование. В этой статье мы подробно разберем, почему такой подход приводит к ошибкам, какие системные механизмы при этом задействованы и как правильно писать код, чтобы избежать аварийного завершения программы.
Проанализируем структуру данных объекта 1С. Реквизит Товары является объектом типа ТабличнаяЧасть (на сервере) или ДанныеФормыКоллекция (на клиенте). Это коллекция, то есть контейнер, который содержит множество строк. У самого контейнера нет свойства Наименование, это свойство принадлежит каждой отдельной строке внутри него.
Когда мы пишем Объект.Товары.Наименование, система пытается найти поле «Наименование» в описании типа всей таблицы и, не находя его, выдает критическую ошибку «Поле объекта не обнаружено». Разберем, как исправить эту ситуацию. Чтобы получить данные, нам нужно обратиться к конкретному элементу коллекции по его индексу.
Рассмотрим правильный пример обращения к первой строке таблицы:
// Проверяем, есть ли в таблице хотя бы одна строка
Если Объект.Товары.Количество() > 0 Тогда
ЗначениеНаименования = Объект.Товары[0].Наименование;
КонецЕсли;
Выясним еще один важный нюанс. Часто программисты ошибочно полагают, что если в справочнике «Номенклатура» есть реквизит «Наименование», то и в табличной части документа он доступен напрямую. Однако в табличных частях обычно хранится ссылка на объект (например, реквизит Номенклатура), а не само наименование.
Если мы попытаемся вызвать Объект.Товары[0].Наименование, а колонки с таким именем в табличной части нет (есть только колонка Номенклатура), мы снова получим ошибку отсутствия поля. В этом случае путь к данным должен пролегать через ссылочный реквизит.
Посмотрим на пример правильного обращения через ссылку:
// Сначала получаем строку, затем ссылку на номенклатуру, затем её реквизит
ТекСтрока = Объект.Товары[0];
НаименованиеТовара = ТекСтрока.Номенклатура.Наименование;
Рассмотрим ситуацию, когда синтаксис написан верно — Объект.Товары[0].Наименование, но программа всё равно «падает». Это происходит, если табличная часть пуста. Попытка обратиться к индексу [0] в пустой коллекции вызывает исключение «Индекс находится за границами массива».
Проанализируем, как сделать код безопасным. Мы всегда должны использовать проверку количества строк или обходить таблицу в цикле, который просто не выполнится, если данных нет:
// Безопасный обход всех строк таблицы
Для Каждого СтрокаТовара Из Объект.Товары Цикл
Сообщить(СтрокаТовара.Номенклатура.Наименование);
КонецЦикла;
В управляемом приложении 1С важно учитывать, где выполняется код. Если Объект — это данные формы, то на клиенте обращение к Объект.Товары работает через объект ДанныеФормыКоллекция. Однако, если вы попытаетесь использовать методы, доступные только для серверного типа ТабличнаяЧасть (например, некоторые методы метаданных), возникнет ошибка контекста.
Также стоит помнить, что при обращении через точку от ссылки на клиенте (например, Строка.Номенклатура.Наименование) происходит неявный серверный вызов. Если таких строк сотни, это катастрофически замедлит работу программы. Проанализируем, как оптимизировать этот процесс: лучше получить все необходимые данные одним запросом на сервере, чем обращаться к каждому наименованию в цикле на клиенте.
Рассмотрим ситуацию с «битыми» ссылками. Если в колонке Номенклатура хранится ссылка на объект, который был физически удален из базы данных, то при обращении Строка.Номенклатура.Наименование система не выдаст ошибку, но вернет текст «<Объект не обнаружен>...».
Однако, если поле Номенклатура имеет составной тип и в данной строке значение не заполнено (равно Неопределено), то любая попытка обратиться к нему через точку вызовет ошибку, так как у типа Неопределено нет и не может быть никаких реквизитов.
Для предотвращения таких ситуаций разберем следующий алгоритм проверки:
Для Каждого Строка Из Объект.Товары Цикл
// Проверяем, заполнена ли ссылка и не является ли она битой
Если ЗначениеЗаполнено(Строка.Номенклатура) Тогда
Попытка
Текст = Строка.Номенклатура.Наименование;
Исключение
Текст = "Ошибка доступа к реквизиту";
КонецПопытки;
КонецЕсли;
КонецЦикла;
Иногда код идеально работает под полными правами администратора, но выдает ошибку «Поле объекта не обнаружено» у обычного пользователя. Это верный признак работы механизма RLS (Record Level Security). Если у пользователя нет прав на чтение определенной колонки табличной части или прав на просмотр справочника, на который ссылается таблица, система «спрячет» это поле. Программное обращение к нему будет эквивалентно обращению к несуществующему свойству.
Чтобы ваше обращение к данным табличной части всегда было успешным, придерживайтесь следующих правил:
[0] или цикл Для каждого.Соблюдение этих простых рекомендаций сделает ваш код более надежным и защитит пользователей от неожиданных сбоев программы.