В процессе разработки в среде «1С:Предприятие» программисты нередко сталкиваются с ситуациями, когда стандартный, многократно проверенный код внезапно отказывается работать в контексте конкретной формы. Одной из таких коварных проблем является ошибка «Поле объекта не обнаружено» при попытке обратиться к глобальным коллекциям метаданных, например, к менеджеру документов через конструкцию Документы.ИмяДокумента.СоздатьДокумент().
В этой статье мы подробно разберем, почему возникает эта ошибка, как работает механизм разрешения имен в платформе «1С:Предприятие», и рассмотрим несколько эффективных способов решения данной проблемы.
Давайте представим стандартную задачу: у нас есть внешняя или встроенная обработка. Внутри нее создано несколько форм. Из основной формы мы открываем вспомогательную форму (например, «ФормаЭлемента») и передаем в нее некоторые параметры. На этой вспомогательной форме мы создаем кнопку, по нажатию на которую должен программно создаваться новый документ (например, «Платежное поручение»).
Мы пишем стандартный код для создания документа на сервере:
&НаСервере
Процедура СоздатьПротоколНаСервере()
ДокПриход = Документы.ПлатежноеПоручение.СоздатьДокумент();
// Дальнейшее заполнение и запись документа
КонецПроцедуры
&НаКлиенте
Процедура СоздатьПротокол(Команда)
СоздатьПротоколНаСервере();
КонецПроцедуры
Однако при выполнении этого кода платформа выдает ошибку:
{Обработка.МояОбработка.Форма.ФормаЭлемента.Форма(X)}: Поле объекта не обнаружено (ПлатежноеПоручение)
При этом, если мы перенесем точно такой же код в другую форму (например, «Форма3») той же самой обработки, создание документа пройдет абсолютно гладко и без ошибок. Давайте проанализируем ситуацию и выясним истинную причину такого поведения системы.
Причина такого странного поведения кроется в конфликте (перекрытии) локального и глобального контекстов управляемой формы. В платформе 1С существует строгий порядок разрешения имен при обращении к свойствам и методам в модуле формы. Когда система встречает идентификатор (в нашем случае Документы), она ищет его «изнутри наружу» по следующей цепочке приоритетов:
Перем в самом верху модуля формы.УправляемаяФорма).Справочники, Документы, РегистрыСведений).Если на нашей проблемной форме («ФормаЭлемента») разработчик создал собственный реквизит формы (например, таблицу значений, дерево или динамический список) и назвал его «Документы», то приоритет поиска этого имени смещается на шаг 3.
В результате платформа перестает видеть глобальный менеджер метаданных Документы. Вместо этого под идентификатором Документы она начинает понимать конкретный локальный реквизит текущей управляемой формы. Разумеется, у локального реквизита (будь то таблица или список) нет свойства ПлатежноеПоручение, что и приводит к закономерной ошибке: «Поле объекта не обнаружено».
Давайте убедимся в этом предположении на практике с помощью отладочных инструментов. Для этого выполним несколько простых шагов:
ПриСозданииНаСервере (или создадим ее, если она отсутствует).Документы и нажмем клавишу ввода.Если в окне результатов вместо ожидаемого типа ДокументыМенеджер мы увидим, например, тип ДанныеФормыКоллекция, ДинамическийСписок или ТаблицаЗначений, это будет стопроцентным подтверждением нашей гипотезы: имя глобального контекста перекрыто локальным реквизитом.
В зависимости от архитектуры вашей конфигурации и степени интеграции реквизита формы в логику программы, мы можем применить один из трех вариантов решения.
Самым правильным и надежным решением является устранение самого конфликта имен. Использование имен глобального контекста для локальных переменных и реквизитов считается плохим стилем программирования.
Выполним переименование:
Документы.ТаблицаДокументов, СписокДокументов или ДокументыДляОбработки.Документы на новое имя в пределах модуля этой формы.После этого платформа снова начнет успешно разрешать имя Документы как глобальную коллекцию менеджеров документов, и ошибка исчезнет.
Бывают ситуации, когда реквизит формы Документы задействован во множестве сложных связей, используется в динамических списках на нескольких страницах формы, связан с элементами интерфейса, и его переименование может повлечь за собой длительное тестирование и риски что-то сломать. В таком случае мы можем обойти ограничение контекста формы.
Для этого мы можем перенести процедуру создания документа в общий серверный модуль, где локальный контекст формы отсутствует по определению. Выполним следующие действия:
ДокументыСервер).
// В общем модуле "ДокументыСервер"
Функция СоздатьПлатежноеПоручениеНаСервере(ДанныеЗаполнения) Экспорт
НовыйДокумент = Документы.ПлатежноеПоручение.СоздатьДокумент();
// Заполняем реквизиты из переданной структуры ДанныеЗаполнения
НовыйДокумент.Сотрудник = ДанныеЗаполнения.Сотрудник;
НовыйДокумент.Отдел = ДанныеЗаполнения.Отдел;
НовыйДокумент.Записать();
Возврат НовыйДокумент.Ссылка;
КонецФункции
&НаСервере
Процедура СоздатьПротоколНаСервере()
ДанныеЗаполнения = Новый Структура;
ДанныеЗаполнения.Вставить("Сотрудник", Объект.Сотрудник);
ДанныеЗаполнения.Вставить("Отдел", Объект.Отдел);
// Вызываем метод общего модуля, где контекст формы не мешает работе
СсылкаНаНовыйДокумент = ДокументыСервер.СоздатьПлатежноеПоручениеНаСервере(ДанныеЗаполнения);
КонецПроцедуры
Если мы не хотим создавать новые общие модули в конфигурации, мы можем задействовать модуль менеджера нашей собственной обработки. Это также отличная изолированная область выполнения кода, не привязанная к контексту конкретной формы.
Разберем этот вариант по шагам:
МояОбработка.
// В модуле менеджера обработки "МояОбработка"
Процедура СоздатьПлатежку(ПараметрыЗаполнения) Экспорт
НовыйДок = Документы.ПлатежноеПоручение.СоздатьДокумент();
ЗаполнитьЗначенияСвойств(НовыйДок, ПараметрыЗаполнения);
НовыйДок.Записать();
КонецПроцедуры
&НаСервере
Процедура СоздатьПротоколНаСервере()
ПараметрыЗаполнения = Новый Структура("Сотрудник, Отдел", Сотрудник, Отдел);
// Обращаемся напрямую к менеджеру нашей обработки
Обработки.МояОбработка.СоздатьПлатежку(ПараметрыЗаполнения);
КонецПроцедуры
Для того чтобы избежать подобных неочевидных ошибок в будущем, мы рекомендуем придерживаться простых стандартов кодирования на платформе «1С:Предприятие»:
Документы, Справочники, РегистрыСведений, ПланыВидовХарактеристик, Константы, Перечисления.Документы назовите реквизит СписокДокументов. Вместо Сотрудники — ТаблицаСотрудников. Это полностью исключит пересечение контекстов.