При разработке собственных печатных форм в среде «1С:Предприятие 8.3» программисты часто сталкиваются с ситуацией, когда код, успешно работающий в модуле формы или модуле объекта, внезапно выдает ошибку при переносе в Модуль команды. Одной из самых коварных ошибок является сообщение: Поле объекта не обнаружено (Номер). Давайте детально разберем, почему это происходит и как правильно организовать передачу данных для печати.
Проанализируем контекст возникновения ошибки. В типовой процедуре ОбработкаКоманды на стороне клиента первым аргументом выступает ПараметрКоманды. Новички часто предполагают, что в нем всегда содержится одиночная ссылка на документ. Однако в современных конфигурациях (особенно на базе УП 2.1, ERP или УТ 11) это не так.
Рассмотрим свойства команды в дереве конфигурации. Обратим внимание на свойство «Режим использования параметра». Если там установлено значение Множественный, платформа 1С автоматически упаковывает ссылки в объект типа Массив. Это происходит даже в том случае, если вы нажали кнопку печати, выделив всего один документ в списке. Массив — это коллекция, у которой есть методы Количество() или Добавить(), но у него нет реквизитов Номер или Дата. Именно поэтому при попытке обратиться к ПараметрКоманды.Номер система выдает закономерную ошибку.
Если мы точно знаем, что команда будет вызываться только для одного выбранного объекта, мы можем обратиться к первому элементу массива. Давайте посмотрим на пример корректировки клиентской части кода:
&НаКлиенте
Процедура ОбработкаКоманды(ПараметрКоманды, ПараметрыВыполненияКоманды)
// Проверяем, не пустой ли массив пришел в параметрах
Если ПараметрКоманды.Количество() = 0 Тогда
Возврат;
КонецЕсли;
ТабДок = Новый ТабличныйДокумент;
// Передаем первый элемент массива (индекс 0), который и является нашей ссылкой
Печать(ТабДок, ПараметрКоманды[0]);
ТабДок.Показать("Счет на оплату");
КонецПроцедуры
Этот подход решает проблему «в лоб», однако он не является универсальным. Если пользователь выделит в списке три документа и нажмет печать, распечатается только первый. Поэтому правильнее использовать цикл.
Рассмотрим более профессиональный подход, который позволит обрабатывать как один документ, так и целую пачку. Для этого нам нужно изменить логику взаимодействия клиента и сервера. Вместо того чтобы передавать по одной ссылке, мы передадим на сервер весь массив целиком, а там организуем цикл формирования табличных документов.
Проанализируем ситуацию на сервере. Нам необходимо изменить запрос так, чтобы он эффективно получал данные по списку ссылок. Использование условия ГДЕ Ссылка В (&МассивСсылок) в запросе — это стандарт де-факто для высокопроизводительных систем.
&НаСервере
Процедура Печать(ТабДок, МассивСсылок)
Макет = Документы.КоммерческоеПредложениеКлиенту.ПолучитьМакет("МакетСчетНаОплату");
// Формируем запрос сразу по всем переданным ссылкам
Запрос = Новый Запрос;
Запрос.Текст =
"ВЫБРАТЬ
| КП.Ссылка КАК Ссылка,
| КП.Номер КАК Номер,
| КП.Дата КАК Дата,
| КП.Товары.(
| Номенклатура,
| Количество,
| Цена,
| Сумма
| )
|ИЗ
| Документ.КоммерческоеПредложениеКлиенту КАК КП
|ГДЕ
| КП.Ссылка В(&МассивСсылок)";
Запрос.УстановитьПараметр("МассивСсылок", МассивСсылок);
РезультатЗапроса = Запрос.Выполнить();
ВыборкаДокументов = РезультатЗапроса.Выбрать();
Пока ВыборкаДокументов.Следующий() Цикл
// Здесь выполняется вывод областей макета для каждого документа
// Например: ОбластьШапка = Макет.ПолучитьОбласть("Шапка");
// ОбластьШапка.Параметры.НомерДокумента = ВыборкаДокументов.Номер;
// ТабДок.Вывести(ОбластьШапка);
// Если документов несколько, можно добавлять разрыв страницы
Если МассивСсылок.Количество() > 1 Тогда
ТабДок.ВывестиГоризонтальныйРазделительСтраниц();
КонецЕсли;
КонецЦикла;
КонецПроцедуры
Выясним еще одну причину ошибок, упомянутых в обсуждении. При работе в Модуле команды у нас нет доступа к переменным Объект или ЭтотОбъект. Эти переменные специфичны для Модуля формы и Модуля объекта соответственно.
Объект.Ссылка, так как форма «знает», с каким объектом она работает через основной реквизит.ЭтотОбъект.Ссылка (или просто Ссылка), так как код исполняется «внутри» данных самого объекта.ПараметрКоманды, который нам заботливо передала платформа при нажатии кнопки.В исходном сообщении автора была замечена еще одна методологическая ошибка: обращение к реквизитам через точку в условии запроса (кп.ссылка.номер = &Номер). Рассмотрим, почему это плохо. С точки зрения SQL-сервера, каждое обращение «через точку» от ссылки — это дополнительная операция соединения таблиц (LEFT JOIN). В больших базах это существенно замедляет работу.
Правило хорошего тона 1С: Всегда передавайте в запрос саму ссылку целиком. Если вам нужен номер документа для вывода в заголовке окна или сообщения, используйте функцию ОбщегоНазначения.ЗначениеРеквизитаОбъекта(Ссылка, "Номер"). Это работает значительно быстрее, чем получение тяжелого объекта документа целиком в память сервера.
Подводя итог, при возникновении ошибки Поле объекта не обнаружено в команде печати первым делом проверьте тип данных входящего параметра с помощью функции ТипЗнч(). Скорее всего, вы обнаружите там Массив, и простая индексация [0] или обход циклом Для Каждого решит вашу проблему.