Как исправить ошибку «Поле объекта не обнаружено (РеквизитыШапки)» при создании документов на основании в 1С:ERP

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

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

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

В каких условиях возникает ошибка?

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

«Поле объекта не обнаружено (РеквизитыШапки)» в процедуре СТ_ИнициализироватьДокумент(ДанныеЗаполнения).

Проанализируем исходный код проблемной процедуры, который привел к этой ошибке:


Процедура СТ_ИнициализироватьДокумент(ДанныеЗаполнения)
    Если НЕ ЗначениеЗаполнено(ВидЦены) Тогда
        ВидЦены = СТ_ОбщегоНазначенияСерверПовтИсп.ПолучитьЗначениеКонстанты("УчетнаяЦена");
        
        Если ДанныеЗаполнения <> Неопределено И ДанныеЗаполнения.РеквизитыШапки.ВидЦеныДоп <> ВидЦены Тогда
            ВидЦены = ДанныеЗаполнения.РеквизитыШапки.ВидЦеныДоп;
            Возврат;
        КонецЕсли;
        
        // Дальнейший расчет цен по табличной части...
    КонецЕсли;
КонецПроцедуры

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

Выясняем причину: почему тип «ДанныеЗаполнения» может меняться?

Для того чтобы понять природу ошибки, нам необходимо детально изучить, как именно платформа 1С и БСП передают информацию в обработчик заполнения документа-приемника. Существует два принципиально разных сценария создания документов на основании:

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

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

Специфика метаданных документов-источников

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

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

Откуда же тогда брать вид цены для документа Передача сырья переработчику при его создании на основании пересчета? Рассмотрим наиболее логичные варианты:

Разработаем универсальный и безопасный алгоритм заполнения

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

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

Рассмотрим по шагам реализацию этого подхода в процедуре инициализации документа:


Процедура СТ_ИнициализироватьДокумент(ДанныеЗаполнения)
    
    // Шаг 1. Первоначальная установка цены по умолчанию (если реквизит еще не заполнен)
    Если НЕ ЗначениеЗаполнено(ВидЦены) Тогда
        ВидЦены = СТ_ОбщегоНазначенияСерверПовтИсп.ПолучитьЗначениеКонстанты("УчетнаяЦена");
    КонецЕсли;
    
    Если ДанныеЗаполнения = Неопределено Тогда
        Возврат;
    КонецЕсли;
    
    // Шаг 2. Анализируем тип входящего параметра ДанныеЗаполнения
    Если ТипЗнч(ДанныеЗаполнения) = Тип("Структура") Тогда
        
        // Сценарий А: Данные переданы в виде структуры (программное заполнение БСП)
        РеквизитыШапки = Неопределено;
        Если ДанныеЗаполнения.Свойство("РеквизитыШапки", РеквизитыШапки) И РеквизитыШапки <> Неопределено Тогда
            ВидЦеныДоп = Неопределено;
            Если РеквизитыШапки.Свойство("ВидЦеныДоп", ВидЦеныДоп) И ЗначениеЗаполнено(ВидЦеныДоп) Тогда
                ВидЦены = ВидЦеныДоп;
            КонецЕсли;
        КонецЕсли;
        
    ИначеЕсли Документы.ТипВсеСсылки().СодержитТип(ТипЗнч(ДанныеЗаполнения)) Тогда
        
        // Сценарий Б: Передана прямая ссылка на документ-источник (интерактивный ввод)
        МетаданныеИсточника = ДанныеЗаполнения.Метаданные();
        
        // Проверяем, существует ли физически реквизит "ВидЦеныДоп" в документе-источнике
        Если МетаданныеИсточника.Реквизиты.Найти("ВидЦеныДоп") <> Неопределено Тогда
            Если ЗначениеЗаполнено(ДанныеЗаполнения.ВидЦеныДоп) Тогда
                ВидЦены = ДанныеЗаполнения.ВидЦеныДоп;
            КонецЕсли;
        // Если "ВидЦеныДоп" нет, ищем стандартный реквизит "ВидЦены"
        ИначеЕсли МетаданныеИсточника.Реквизиты.Найти("ВидЦены") <> Неопределено Тогда
            Если ЗначениеЗаполнено(ДанныеЗаполнения.ВидЦены) Тогда
                ВидЦены = ДанныеЗаполнения.ВидЦены;
            КонецЕсли;
        Иначе
            // Сценарий В: У источника вообще нет реквизитов цен (наш случай с Пересчетом товаров)
            // Попробуем получить вид цены из Договора, который указан в документе-источнике
            Если МетаданныеИсточника.Реквизиты.Найти("Договор") <> Неопределено 
                И ЗначениеЗаполнено(ДанныеЗаполнения.Договор) Тогда
                
                // Используем безопасный метод БСП для проверки наличия реквизита у Договора
                Если ОбщегоНазначения.ЕстьРеквизитОбъекта("ВидЦены", ДанныеЗаполнения.Договор) Тогда
                    ДоговорВидЦены = ОбщегоНазначения.ЗначениеРеквизитаОбъекта(ДанныеЗаполнения.Договор, "ВидЦены");
                    Если ЗначениеЗаполнено(ДоговорВидЦены) Тогда
                        ВидЦены = ДоговорВидЦены;
                    КонецЕсли;
                КонецЕсли;
            КонецЕсли;
        КонецЕсли;
        
    КонецЕсли;
    
    // Шаг 3. Финальный расчет цен по табличной части "Товары"
    ПараметрыЗаполнения = Новый Структура;
    ПараметрыЗаполнения.Вставить("Дата", ТекущаяДатаСеанса);
    ПараметрыЗаполнения.Вставить("Валюта", Валюта);
    ПараметрыЗаполнения.Вставить("ВидЦены", ВидЦены);
    ПараметрыЗаполнения.Вставить("ПоляЗаполнения", "Цена");
    
    СтруктураДействий = Новый Структура;
    СтруктураДействий.Вставить("ПересчитатьСумму", "КоличествоУпаковок");
    
    // Используем типовой программный интерфейс ERP для автоматического заполнения цен
    ЦеныРассчитаны = ПродажиСервер.ЗаполнитьЦены(Товары, Неопределено, ПараметрыЗаполнения, СтруктураДействий);
    
КонецПроцедуры

Разбор ключевых функций и подходов, использованных в решении

Давайте проанализируем, почему предложенное решение является максимально надежным и соответствующим стандартам разработки фирмы «1С»:

Резюме и рекомендации

Главное правило при написании обработчиков заполнения в 1С:ERP и других современных конфигурациях — никогда не доверять типу входящего параметра ДанныеЗаполнения без предварительной проверки. Проектируйте свои функции инициализации документов таким образом, чтобы они могли одинаково успешно обрабатывать как прямые ссылки (при ручном вводе пользователем), так и сложные структуры параметров (при автоматическом создании документов подсистемами ERP).

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

← На главную