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