Почему возникает ошибка «Метод объекта не обнаружен» при ссылке на документ в 1С и как её исправить

Программист 1С v8.3 (Обычные формы) 1С:Управление торговлей IT и автоматизация бизнеса
← На главную

При разработке и кастомизации конфигураций на платформе «1С:Предприятие 8» (особенно на обычных формах, как в «Управление торговлей 10.3») программисты часто сталкиваются с классической архитектурной ошибкой. Она выражается в виде системного сообщения: «Метод объекта не обнаружен» при попытке вызвать экспортную процедуру или функцию, расположенную в модуле документа. В этой статье мы подробно разберем, почему возникает эта проблема, изучим физику процессов платформы 1С, рассмотрим сопутствующие ошибки (такие как «Элемент не выбран!») и предложим несколько надежных вариантов решения задачи с примерами качественного кода.

Физика процесса: в чем разница между Ссылкой и Объектом?

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

  1. Ссылка (тип ДокументСсылка) — это легкий указатель на запись в таблице базы данных. Ссылка содержит уникальный идентификатор (GUID) и минимальный набор реквизитов (например, представление, код или номер/дата), необходимых для быстрого отображения в списках. Ссылка не хранит в оперативной памяти табличные части документа и его тяжелые реквизиты. У ссылочного типа данных нет доступа к методам, процедурам и функциям, написанным в модуле объекта.
  2. Объект (тип ДокументОбъект) — это полноценный экземпляр данных, загруженный в оперативную память для редактирования или проведения. Именно к типу ДокументОбъект компилируется код, написанный в Модуле объекта. Экспортные методы модуля объекта становятся доступны только тогда, когда мы работаем непосредственно с экземпляром объекта, а не с его ссылкой.

Когда мы пытаемся выполнить код вида СсылкаНаОбъект.ПечатьДоверенности(), где СсылкаНаОбъект имеет тип ДокументСсылка.РеализацияТоваровУслуг, платформа справедливо сообщает: «Метод объекта не обнаружен (ПечатьДоверенности)». У типа данных ДокументСсылка физически отсутствует метод с таким именем.

Другая вариация этой ошибки возникает, если тип переменной вообще не определен или равен Неопределено. В таком случае платформа выдает ошибку: «Значение не является значением объектного типа (ПечатьДоверенности)». Это сигнализирует о том, что переменная, через которую вызывается функция, пуста или хранит примитивный тип данных.

Анализируем сопутствующую ошибку: «Элемент не выбран!»

Попытавшись решить проблему "в лоб", разработчики часто используют стандартный метод получения объекта из ссылки — ПолучитьОбъект(). Код приобретает следующий вид:


ДокОбъект = СсылкаНаОбъект.ПолучитьОбъект();
ДокОбъект.ПечатьДоверенности();

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

Это происходит по двум основным причинам:

  1. Реквизит формы СсылкаНаОбъект не был инициализирован или передан из родительской формы при ее открытии.
  2. Пользователь пытается вызвать печать из нового, еще ни разу не записанного в базу данных документа. У нового документа ссылки в физической таблице СУБД еще не существует.

Рассмотрим детально, как правильно реализовать передачу данных и вызов методов для разных жизненных циклов документа.

Вариант 1. Работа с записанным документом через Ссылку

Если мы уверены, что документ уже существует в базе данных и гарантированно записан, нам необходимо правильно передать его ссылку во вспомогательную форму настройки печати и безопасно извлечь объект. Разберем этот процесс по шагам.

В вызывающей форме (например, в основной форме документа РеализацияТоваровУслуг) код открытия нашей вспомогательной формы настройки должен выглядеть так:


// Получаем форму настройки печати по ее пути в метаданных
ФормаНастройки = ПолучитьФорму("Документ.РеализацияТоваровУслуг.Форма.ФормаНастройкиПечати");

// Явно передаем ссылку на текущий документ в реквизит вспомогательной формы
ФормаНастройки.СсылкаНаОбъект = ЭтотОбъект.Ссылка;

// Открываем форму в модальном или обычном режиме
ФормаНастройки.Открыть();

Во вспомогательной форме (где расположена кнопка печати) обработчик нажатия кнопки должен обязательно содержать проверку на заполненность ссылки, чтобы избежать системного сбоя. Реализуем этот код:


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

Вариант 2. Универсальный метод передачи Объектного контекста (для новых документов)

Что делать, если пользователь заполнил документ РеализацияТоваровУслуг, еще не записал его, но уже хочет распечатать доверенность или предварительно просмотреть печатную форму? Метод ПолучитьОбъект() в этом случае работать не будет. Нам нужно передать во вспомогательную форму сам объект, находящийся в оперативной памяти компьютера.

Для реализации этого сценария выполним следующие действия:

  1. Во вспомогательной форме создадим реквизит с именем ДокОбъект (тип данных: ДокументОбъект.РеализацияТоваровУслуг).
  2. В основной форме документа при вызове окна настроек передадим текущий контекст объекта целиком:

ФормаНастройки = ПолучитьФорму("Документ.РеализацияТоваровУслуг.Форма.ФормаНастройкиПечати");

// Передаем ссылку на сам объект (весь контекст оперативной памяти), а не ссылку из БД
ФормаНастройки.ДокОбъект = ЭтотОбъект; 

ФормаНастройки.Открыть();
  1. В обработчике кнопки вспомогательной формы теперь можно напрямую вызывать экспортный метод. Нам больше не требуется обращаться к базе данных через метод ПолучитьОбъект(), что полностью исключает ошибку пустого элемента:

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

Архитектурные рекомендации по оптимизации вызовов в УТ 10.3

Хотя вызовы через ПолучитьОбъект() полностью решают проблему работоспособности кода, с точки зрения стандартов разработки фирмы «1С» и требований высокой производительности СУБД, этот подход считается неоптимальным. Метод ПолучитьОбъект() осуществляет тяжелое чтение из базы данных: он считывает не только шапку документа, но и все его табличные части (товары, услуги, возвратная тара, серийные номера и т.д.), тратя ресурсы сервера и сетевой трафик.

Проанализируем две альтернативные и более производительные архитектуры вызова печати:

А. Использование общего неглобального модуля. Мы можем вынести логику формирования печатных форм в отдельный общий модуль (например, УправлениеПечатьюВызовСервера или использовать встроенный в УТ 10.3 модуль УниверсальныеМеханизмы). В таком случае функция печати принимает в качестве параметра исключительно легкую ссылку, а внутри общего модуля данные выбираются тонким и быстрым запросом:


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

Б. Перенос логики в Модуль менеджера документа. Если ваша база данных работает на платформе 8.2 или 8.3 (даже в режиме совместимости с более старыми версиями), вам доступен функционал Модуля менеджера. Методы модуля менеджера принадлежат типу ссылки и не требуют инициализации тяжелого объекта в памяти. Мы можем перенести функцию ПечатьДоверенности в модуль менеджера документа РеализацияТоваровУслуг, сделав ее экспортной:


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

В этом случае вызов метода из любой формы (в том числе из внешней обработки) становится максимально лаконичным, быстрым и безопасным:


Процедура КнопкаПечатьНажатие(Элемент)
    
    Если ЗначениеЗаполнено(СсылкаНаОбъект) Тогда
        // Вызов через менеджер документа напрямую по ссылке
        ТабДок = Документы.РеализацияТоваровУслуг.ПечатьДоверенности(СсылкаНаОбъект);
        УниверсальныеМеханизмы.НапечататьДокумент(ТабДок);
    КонецЕсли;
    
КонецПроцедуры

Применяя данные подходы, мы не только избавляемся от ошибок «Метод объекта не обнаружен» и «Элемент не выбран!», но и значительно увеличиваем скорость работы системы при одновременной работе большого количества пользователей.

← На главную