Каждый разработчик, работающий в среде 1С:Предприятие 8, рано или поздно сталкивается с ошибкой «Значение не является значением объектного типа». Эта системная ошибка означает, что программа пытается вызвать метод или обратиться к свойству переменной, которая не содержит ожидаемого объекта (например, содержит значение Неопределено, Null или простую строку/число).
В этой статье мы подробно разберем практическую ситуацию, когда данная ошибка возникает при попытке вывести печатную форму (Универсальный передаточный документ) на экран с помощью метода Показать(). Мы выясним точную причину сбоя, разберем типичные заблуждения начинающих разработчиков и напишем надежный, соответствующий стандартам БСП (Библиотеки Стандартных Подсистем) код.
Рассмотрим ситуацию. У нас есть внешняя обработка или документ, в котором по кнопке выполняется печать пакета документов. Разработчик написал следующий код на клиенте:
&НаКлиенте
Процедура ПечатьПакетаДокументов(СразуНаПринтер = Ложь)
Для Каждого Стр Из Объект.Отгрузка Цикл
Если ЗначениеЗаполнено(Стр.Реализация) Тогда
ТабДок = ПечататьУПД(Стр.Реализация);
Если СразуНаПринтер Тогда
ТабДок.Напечатать(РежимИспользованияДиалогаПечати.НеИспользовать);
Иначе
ТабДок.Показать("Универсальный Передаточный Документ");
КонецЕсли;
Иначе
Сообщить("Нельзя печатать УПД, если не создана реализация");
КонецЕсли;
КонецЦикла;
КонецПроцедуры
При выполнении этого кода в режиме «1С:Предприятие» программа аварийно завершает работу на строке ТабДок.Показать(...) с ошибкой: «Значение не является значением объектного типа (показать)».
На стороне сервера вызывается функция ПечататьУПД, которая должна сформировать табличный документ:
&НаСервере
Функция ПечататьУПД(РеализацияСсылка)
МассивОбъектов = Новый Массив;
ПараметрыПечати = Новый Структура;
ПараметрыПечати.Вставить("ПечатьВВалюте", Ложь);
КоллекцияПечатныхФорм = Новый ТаблицаЗначений;
КоллекцияПечатныхФорм.Колонки.Добавить("ИмяМакета");
КоллекцияПечатныхФорм.Колонки.Добавить("ИмяВРЕГ");
КоллекцияПечатныхФорм.Колонки.Добавить("Экземпляров");
КоллекцияПечатныхФорм.Колонки.Добавить("ТабличныйДокумент");
КоллекцияПечатныхФорм.Колонки.Добавить("СинонимМакета");
КоллекцияПечатныхФорм.Колонки.Добавить("Картинка");
КоллекцияПечатныхФорм.Колонки.Добавить("ПолныйПутьКМакету");
НоваяСтрока = КоллекцияПечатныхФорм.Добавить();
НоваяСтрока.ИмяМакета = "ПФ_MXL_УниверсальныйПередаточныйДокумент981";
НоваяСтрока.ИмяВРЕГ = "УПД";
НоваяСтрока.Экземпляров = 2;
МассивОбъектов.Добавить(РеализацияСсылка);
ОбъектыПечати = Новый СписокЗначений;
ОбъектыПечати.Добавить(Неопределено, "ДоступныеЗначения");
ПараметрыВывода = Новый Структура("ДоступнаПечатьПоКомплектно, ПараметрыОтправки");
ПараметрыВывода.ДоступнаПечатьПоКомплектно = Ложь;
ПараметрыВывода.ПараметрыОтправки = Новый Структура("Получатель, Текст, Тема");
ПараметрыВывода.ПараметрыОтправки.Текст = "";
ПараметрыВывода.ПараметрыОтправки.Тема = "";
Печать(МассивОбъектов, ПараметрыПечати, КоллекцияПечатныхФорм, ОбъектыПечати, ПараметрыВывода);
Возврат КоллекцияпечатныхФорм.Получить(0).ТабличныйДокумент;
КонецФункции
Для генерации макета используется процедура Печать:
Процедура Печать(МассивОбъектов, ПараметрыПечати, КоллекцияПечатныхФорм, ОбъектыПечати, ПараметрыВывода) Экспорт
Если УправлениеПечатью.НужноПечататьМакет(КоллекцияПечатныхФорм, "ПФ_MXL_УниверсальныйПередаточныйДокумент982") Тогда
УправлениеПечатью.ВывестиТабличныйДокументВКоллекцию(КоллекцияПечатныхФорм, "ПФ_MXL_УниверсальныйПередаточныйДокумент982", "Универсальный передаточный документ", ПечатнаяФорма(МассивОбъектов, ОбъектыПечати, "УниверсальныйПередаточныйДокумент", ПараметрыПечати));
КонецЕсли;
КонецПроцедуры
Проанализируем детально, как взаимодействуют эти процедуры. Главная причина сбоя кроется в банальной, но труднозаметной опечатке в именах макетов печатных форм:
ПечататьУПД мы регистрируем макет в коллекции печатных форм со следующим именем:НоваяСтрока.ИмяМакета = "ПФ_MXL_УниверсальныйПередаточныйДокумент981"; (обратите внимание на цифру 981 на конце).Печать. В ней система БСП проверяет, требуется ли формировать табличный документ для конкретного макета:Если УправлениеПечатью.НужноПечататьМакет(КоллекцияПечатныхФорм, "ПФ_MXL_УниверсальныйПередаточныйДокумент982") Тогда (здесь на конце указано число 982).Поскольку имена макетов не совпадают (981 не равно 982), стандартная функция БСП НужноПечататьМакет возвращает значение Ложь. Тело условия «Если» полностью игнорируется, и заполнение табличного документа через процедуру ВывестиТабличныйДокументВКоллекцию не выполняется.
В результате колонка ТабличныйДокумент в нашей коллекции печатных форм остается пустой (содержит дефолтное значение Неопределено). Функция ПечататьУПД выполняет возврат:
Возврат КоллекцияпечатныхФорм.Получить(0).ТабличныйДокумент; // Возвращает Неопределено!
На стороне клиента в переменную ТабДок записывается значение Неопределено. Когда программа доходит до строки ТабДок.Показать(), она пытается вызвать объектный метод у примитивного типа данных. В этот момент платформа аварийно останавливает сеанс и выдает пользователю ошибку.
В процессе обсуждения проблемы автор темы предположил, что в переменной ТабДок возвращается ссылка на документ (ДокументСсылка.РасходнаяНакладная), и безуспешно пытался преобразовать её к объекту с помощью метода ПолучитьОбъект().
Давайте разберем, почему эта гипотеза ошибочна и почему метод ПолучитьОбъект() в данном случае работать не будет:
ПолучитьОбъект() предназначен для обращения к базе данных с целью изменения данных или получения объектной структуры документа. Этот метод доступен только на сервере. Его вызов на клиенте (в процедуре с директивой компиляции &НаКлиенте) гарантированно вызовет синтаксическую ошибку «Метод не обнаружен».Показать() принадлежит исключительно объекту типа ТабличныйДокумент. У ссылок (ДокументСсылка) или объектов документов (ДокументОбъект) такого метода нет. Если бы вашей целью действительно было показать форму самого документа «Расходная накладная» на клиенте, следовало использовать стандартные механизмы открытия форм:
ПоказатьЗначение(, Стр.Реализация);
// Или через ОткрытьФорму:
ПараметрыОткрытия = Новый Структура("Ключ", Стр.Реализация);
ОткрытьФорму("Документ.РасходнаяНакладная.ФормаОбъекта", ПараметрыОткрытия);
Однако, так как нам нужно вывести именно печатную форму УПД, никаких манипуляций со ссылками и получением объектов базы данных делать не требуется. Нам нужно лишь гарантировать, что сервер вернет корректный ТабличныйДокумент.
Давайте исправим ошибки и сделаем код надежным. Нам нужно выполнить два важных шага.
Приведем имена макетов к единому значению. Убедитесь, какое именно имя используется в вашей конфигурации (в оригинальном коде конфигурации УНФ или УТ обычно используется ПФ_MXL_УниверсальныйПередаточныйДокумент982). Исправим строку добавления макета в функции ПечататьУПД:
// Было: НоваяСтрока.ИмяМакета = "ПФ_MXL_УниверсальныйПередаточныйДокумент981";
// Стало:
НоваяСтрока.ИмяМакета = "ПФ_MXL_УниверсальныйПередаточныйДокумент982";
Хорошей практикой программирования в 1С является превентивная проверка типов данных при передаче между сервером и клиентом. Давайте защитим клиентский код от возможных падений в будущем, если вдруг макет не сможет сформироваться по другим причинам (например, из-за нехватки данных или прав доступа):
&НаКлиенте
Процедура ПечатьПакетаДокументов(СразуНаПринтер = Ложь)
Для Каждого Стр Из Объект.Отгрузка Цикл
Если ЗначениеЗаполнено(Стр.Реализация) Тогда
ТабДок = ПечататьУПД(Стр.Реализация);
// Защита: проверяем, что сервер действительно вернул ТабличныйДокумент
Если ТипЗнч(ТабДок) = Тип("ТабличныйДокумент") Тогда
Если СразуНаПринтер Тогда
ТабДок.Напечатать(РежимИспользованияДиалогаПечати.НеИспользовать);
Иначе
ТабДок.Показать("Универсальный Передаточный Документ");
КонецЕсли;
Иначе
Сообщить("Не удалось сформировать печатную форму для документа: " + Строка(Стр.Реализация));
КонецЕсли;
Иначе
Сообщить("Нельзя печатать УПД, если не создана реализация");
КонецЕсли;
КонецЦикла;
КонецПроцедуры
Ручное конструирование таблиц коллекций печатных форм на сервере, их последующий разбор и вызов метода Показать() на клиенте — это устаревший подход, который часто приводит к ошибкам. Современный стандарт разработки подсистем 1С (в таких конфигурациях, как УНФ, УТ 11, КА, ERP) предполагает использование готовых клиентских методов БСП.
Давайте посмотрим, как можно переписать всю процедуру печати пакета документов гораздо лаконичнее и надежнее. Мы можем использовать общий модуль УправлениеПечатьюКлиент. При таком подходе нам не нужно вручную писать серверные функции для формирования коллекций — БСП все сделает автоматически.
Перепишем клиентский метод:
&НаКлиенте
Процедура ПечатьПакетаДокументовБСП(СразуНаПринтер = Ложь)
МассивДокументов = Новый Массив;
// Собираем все заполненные ссылки на документы в один массив
Для Каждого Стр Из Объект.Отгрузка Цикл
Если ЗначениеЗаполнено(Стр.Реализация) Тогда
МассивДокументов.Добавить(Стр.Реализация);
КонецЕсли;
КонецЦикла;
Если МассивДокументов.Количество() > 0 Тогда
ПараметрыПечати = Новый Структура;
ПараметрыПечати.Вставить("СразуНаПринтер", СразуНаПринтер);
// Вызываем стандартную команду печати БСП
УправлениеПечатьюКлиент.ВыполнитьКомандуПечати(
"Документ.РасходнаяНакладная", // Имя менеджера документа в метаданных
"УниверсальныйПередаточныйДокумент", // Идентификатор печатной формы
МассивДокументов,
ЭтаФорма,
ПараметрыПечати
);
Иначе
ПоказатьПредупреждение(, "Список документов для печати пуст!");
КонецЕсли;
КонецПроцедуры
Преимущества использования стандартного механизма БСП:
ПечататьУПД и вручную описывать структуру коллекции.ВыполнитьКомандуПечати автоматически откроет стандартное и привычное пользователям окно «Печать документов». В этом окне они смогут сразу отредактировать документ, изменить количество копий, сохранить форму в форматы PDF/Excel или отправить документ по электронной почте.Резюмируя наш разбор: при возникновении ошибки Значение не является значением объектного типа (показать) всегда в первую очередь проверяйте отладчиком тип переменной, у которой вызывается метод. Чаще всего там оказывается Неопределено из-за скрытых опечаток в условиях или нестыковок в именах параметров конфигурации.