Почему при вызове команды печати возникает ошибка «Поле объекта не обнаружено (Номер)» и как её исправить?

Программист 1С v8.3 (Управляемые формы) 1С:ERP Управление предприятием
← На главную

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

Выясняем причину: почему «ПараметрКоманды» — это не ссылка?

Проанализируем контекст возникновения ошибки. В типовой процедуре ОбработкаКоманды на стороне клиента первым аргументом выступает ПараметрКоманды. Новички часто предполагают, что в нем всегда содержится одиночная ссылка на документ. Однако в современных конфигурациях (особенно на базе УП 2.1, ERP или УТ 11) это не так.

Рассмотрим свойства команды в дереве конфигурации. Обратим внимание на свойство «Режим использования параметра». Если там установлено значение Множественный, платформа 1С автоматически упаковывает ссылки в объект типа Массив. Это происходит даже в том случае, если вы нажали кнопку печати, выделив всего один документ в списке. Массив — это коллекция, у которой есть методы Количество() или Добавить(), но у него нет реквизитов Номер или Дата. Именно поэтому при попытке обратиться к ПараметрКоманды.Номер система выдает закономерную ошибку.

Разбираем решение через обращение к элементам массива

Если мы точно знаем, что команда будет вызываться только для одного выбранного объекта, мы можем обратиться к первому элементу массива. Давайте посмотрим на пример корректировки клиентской части кода:


&НаКлиенте
Процедура ОбработкаКоманды(ПараметрКоманды, ПараметрыВыполненияКоманды)
    
    // Проверяем, не пустой ли массив пришел в параметрах
    Если ПараметрКоманды.Количество() = 0 Тогда
        Возврат;
    КонецЕсли;

    ТабДок = Новый ТабличныйДокумент;
    
    // Передаем первый элемент массива (индекс 0), который и является нашей ссылкой
    Печать(ТабДок, ПараметрКоманды[0]); 
    
    ТабДок.Показать("Счет на оплату");
    
КонецПроцедуры

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

Проектируем универсальный механизм печати

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

Проанализируем ситуацию на сервере. Нам необходимо изменить запрос так, чтобы он эффективно получал данные по списку ссылок. Использование условия ГДЕ Ссылка В (&МассивСсылок) в запросе — это стандарт де-факто для высокопроизводительных систем.

Пример оптимизированного кода серверной части


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

КонецПроцедуры

Важные замечания по контексту «ЭтотОбъект» и «Объект»

Выясним еще одну причину ошибок, упомянутых в обсуждении. При работе в Модуле команды у нас нет доступа к переменным Объект или ЭтотОбъект. Эти переменные специфичны для Модуля формы и Модуля объекта соответственно.

  1. Модуль формы: Используется Объект.Ссылка, так как форма «знает», с каким объектом она работает через основной реквизит.
  2. Модуль объекта: Используется ЭтотОбъект.Ссылка (или просто Ссылка), так как код исполняется «внутри» данных самого объекта.
  3. Модуль команды: Это независимый обработчик. Единственный способ получить данные документа здесь — это извлечь их из ПараметрКоманды, который нам заботливо передала платформа при нажатии кнопки.

Рекомендации по исправлению запросов

В исходном сообщении автора была замечена еще одна методологическая ошибка: обращение к реквизитам через точку в условии запроса (кп.ссылка.номер = &Номер). Рассмотрим, почему это плохо. С точки зрения SQL-сервера, каждое обращение «через точку» от ссылки — это дополнительная операция соединения таблиц (LEFT JOIN). В больших базах это существенно замедляет работу.

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

Подводя итог, при возникновении ошибки Поле объекта не обнаружено в команде печати первым делом проверьте тип данных входящего параметра с помощью функции ТипЗнч(). Скорее всего, вы обнаружите там Массив, и простая индексация [0] или обход циклом Для Каждого решит вашу проблему.

← На главную