Как исправить ошибку «Поле объекта не обнаружено (Ключ)» при разработке печатной формы М-11 для нескольких документов

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

При разработке внешних печатных форм в современных конфигурациях 1С (УТ 11, ERP, КА 2), работающих на базе Библиотеки стандартных подсистем (БСП), программисты часто сталкиваются с ошибкой Поле объекта не обнаружено (Ключ). Эта проблема особенно актуальна, когда одна печатная форма (например, Требование-накладная М-11) должна формироваться сразу для нескольких видов документов: «Перемещение товаров» и «Внутреннее потребление товаров». В этой статье мы подробно разберем, почему возникает эта ошибка, как работает механизм группировки объектов по типам и как правильно написать код, чтобы печать работала корректно.

Выясним причину возникновения ошибки

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

В типовых решениях для печати используется коллекция Соответствие, где ключом является полное имя типа метаданных (строка), а значением — массив ссылок на документы этого типа. Если вместо Соответствия в функцию передается обычный Массив ссылок, то при итерации мы получаем саму Ссылку на документ. У объекта ДокументСсылка нет и не может быть свойства Ключ, что и приводит к аварийному завершению работы программы.

Проанализируем фрагмент кода, в котором происходит сбой:


Для Каждого СтруктураОбъектов Из СтруктураТипов Цикл
    // Ошибка происходит здесь, если СтруктураТипов - это Массив, а не Соответствие
    Если СтруктураОбъектов.Ключ = "Документ.ПеремещениеТоваров" Тогда 
        // Логика формирования данных
    КонецЕсли;
КонецЦикла;

Разберем механизм группировки объектов по типам

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


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

Эта функция превращает плоский список документов в удобную структуру. Например, если пользователь выделил в списке 3 перемещения и 2 внутренних потребления, функция вернет Соответствие с двумя ключами, внутри которых будут лежать массивы ссылок. Именно наличие этого Соответствия позволяет использовать свойство Ключ в дальнейшем коде.

Шаг 1: Исправляем регистрацию внешней печатной формы

Прежде чем приступать к логике печати, убедимся, что форма правильно регистрируется в системе. В функции СведенияОВнешнейОбработке часто допускают опечатку при создании массива назначений. Рассмотрим правильный вариант заполнения:


Функция СведенияОВнешнейОбработке() Экспорт
    ПараметрыРегистрации = Новый Структура;
    ПараметрыРегистрации.Вставить("Вид", "ПечатнаяФорма");
    
    МассивНазначений = Новый Массив;
    // Важно добавлять именно в МассивНазначений
    МассивНазначений.Добавить("Документ.ПеремещениеТоваров");
    МассивНазначений.Добавить("Документ.ВнутреннееПотреблениеТоваров");
    
    ПараметрыРегистрации.Вставить("Назначение", МассивНазначений);
    ПараметрыРегистрации.Вставить("Наименование", "Требование-накладная (М-11) (внешняя)");
    ПараметрыРегистрации.Вставить("БезопасныйРежим", Ложь); // Для использования Привилегированного режима
    
    // Команды
    ТаблицаКоманд = ПолучитьТаблицуКоманд();
    ДобавитьКоманду(ТаблицаКоманд, "Требование-накладная (М-11)", "M11Внешний", "ВызовСерверногоМетода", Ложь, "ПечатьMXL");
    ПараметрыРегистрации.Вставить("Команды", ТаблицаКоманд);
    
    Возврат ПараметрыРегистрации;
КонецФункции

Обратите внимание: если вы используете УстановитьПривилегированныйРежим(Истина), как в форме ТОРГ-12 со штрихкодом, в коде формирования данных, свойство БезопасныйРежим в параметрах регистрации должно быть установлено в Ложь, иначе привилегии не будут применены.

Шаг 2: Корректируем экспортную процедуру Печать

Проанализируем процедуру Печать, которая является точкой входа при вызове из интерфейса. Здесь крайне важно правильно подготовить переменную СтруктураТипов, используя функцию группировки, которую мы рассмотрели ранее.


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

Шаг 3: Реализуем функцию СформироватьПечатнуюФормуМ11

Теперь рассмотрим основной блок формирования печатного документа. Мы будем обходить Соответствие, получать тип документа через Ключ и массив ссылок через Значение. Это позволит нам вызывать разные функции сбора данных для разных документов.


Функция СформироватьПечатнуюФормуМ11(СтруктураТипов, ОбъектыПечати, ПараметрыПечати) Экспорт
    
    УстановитьПривилегированныйРежим(Истина);
    
    ТабличныйДокумент = Новый ТабличныйДокумент;
    ТабличныйДокумент.ОриентацияСтраницы = ОриентацияСтраницы.Ландшафт;
    ТабличныйДокумент.ИмяПараметровПечати = "ПАРАМЕТРЫ_ПЕЧАТИ_М11";
    
    ПервыйДокумент = Истина;
    
    Для Каждого ЭлементТипа Из СтруктураТипов Цикл
        
        // ЭлементТипа имеет тип ЭлементСоответствия, у него ЕСТЬ свойства Ключ и Значение
        ИмяТипаДокумента = ЭлементТипа.Ключ; 
        МассивСсылок = ЭлементТипа.Значение;
        
        Если Не ПервыйДокумент Тогда
            ТабличныйДокумент.ВывестиГоризонтальныйРазделительСтраниц();
        КонецЕсли;
        
        ДанныеДляПечати = Неопределено;
        
        Если ИмяТипаДокумента = "Документ.ПеремещениеТоваров" Тогда
            ДанныеДляПечати = ПолучитьДанныеМ11Перемещение(МассивСсылок, ПараметрыПечати);
        ИначеЕсли ИмяТипаДокумента = "Документ.ВнутреннееПотреблениеТоваров" Тогда
            ДанныеДляПечати = ПолучитьДанныеМ11Потребление(МассивСсылок, ПараметрыПечати);
        КонецЕсли;
        
        Если ДанныеДляПечати <> Неопределено Тогда
            ЗаполнитьТабличныйДокументМ11(ТабличныйДокумент, ДанныеДляПечати, ОбъектыПечати);
        КонецЕсли;
        
        ПервыйДокумент = Ложь;
        
    КонецЦикла;
    
    Возврат ТабличныйДокумент;
    
КонецФункции

Как отладить код, если ошибка сохраняется?

Если вы всё равно получаете ошибку «Поле объекта не обнаружено (Ключ)», проанализируем ситуацию с помощью отладки или временных вставок кода. Нам нужно точно знать, какой тип данных приходит в цикл. Добавьте следующую строку перед началом цикла:

Сообщить("Тип коллекции: " + ТипЗнч(СтруктураТипов));

Разберем возможные результаты:

  1. ТипЗнч = Соответствие: Ошибки быть не должно. Проверьте, что внутри цикла вы используете именно имя переменной-итератора (например, ЭлементТипа.Ключ).
  2. ТипЗнч = Массив: Это и есть причина ошибки. Значит, функция СоответствиеМассивовПоТипамОбъектов либо не была вызвана, либо отработала некорректно. В этом случае нужно либо вызвать её в процедуре Печать, либо изменить цикл под работу с массивом.

Если вам по какой-то причине неудобно использовать Соответствие, мы можем переписать цикл для работы напрямую с массивом ссылок. В этом случае мы будем определять тип каждого документа «на лету»:


Для Каждого Ссылка Из МассивОбъектов Цикл
    // Здесь Ссылка - это конкретный документ, Ключа у него нет!
    // Используем Метаданные для определения типа
    Если Ссылка.Метаданные().ПолноеИмя() = "Документ.ПеремещениеТоваров" Тогда
        // Логика для одного документа
    КонецЕсли;
КонецЦикла;

Однако такой подход менее производителен, так как типовые функции печати УТ 11 (например, ПечатьМ11) обычно оптимизированы для работы с массивом документов одного типа за один раз, чтобы минимизировать количество запросов к базе данных.

Заключение

Мы проанализировали основные причины возникновения ошибки при разработке формы М-11. Главный вывод: всегда следите за соответствием типов коллекций. БСП ожидает Соответствие для групповой печати. Если вы используете готовый код с Инфостарта или других ресурсов, обязательно проверьте модуль ОбщегоНазначения вашей конфигурации — функции в разных версиях УТ 11/ERP могут называться по-разному или иметь другие наборы параметров.

Используя приведенные выше примеры codebig, вы сможете построить надежную структуру внешней печатной формы, которая не только корректно обрабатывает несколько типов документов, но и легко поддается дальнейшему расширению.

← На главную