Каждый разработчик 1С рано или поздно сталкивается с загадочной ситуацией: в режиме «1С:Предприятие» на управляемой форме отображается нужное поле ввода, но при попытке найти его в конфигураторе на той же управляемой форме мы терпим неудачу. Или еще более запутанный сценарий — поле в свойствах в режиме Предприятия называется, например, ДополнительнаяИнформацияПоДоставке1 (с единицей на конце), а в конфигураторе такого реквизита нет вообще.
В этой статье мы подробно разберем, почему возникает такое расхождение, как устроены механизмы платформы 1С и Библиотеки Стандартных Подсистем (БСП), и научимся правильно обращаться к подобным «невидимым» полям в программном коде, особенно при работе с расширениями конфигурации.
В современных типовых конфигурациях (таких как «Управление торговлей 11», «ERP Управление предприятием» или «Комплексная автоматизация») разработчики фирмы «1С» активно применяют программное изменение интерфейса. Яркий пример — вкладка «Доставка» в документе ЗаказКлиента. В зависимости от выбранного способа доставки (самовывоз, доставка силами перевозчика или нашей службой доставки) состав полей на форме должен динамически меняться.
Для реализации этой логики при создании формы на сервере срабатывает программный код подсистем БСП. Он обращается к метаданным, определяет текущие настройки и генерирует элементы формы «на лету». Рассмотрим, как это работает:
ПриСозданииНаСервере вызываются специализированные общие модули интеграции подсистем доставки.Элементы.Добавить(), который динамически создает новые поля ввода и связывает их с реквизитами объекта.Проанализируем ситуацию, когда элемент формы в режиме Предприятия отображает имя ДополнительнаяИнформацияПоДоставке1. Почему платформа или программный код добавили эту единицу?
В платформе «1С:Предприятие» имена всех элементов на форме в рамках одного окна должны быть абсолютно уникальными. Если при генерации формы программный код пытается добавить элемент с именем ДополнительнаяИнформацияПоДоставке, но элемент с таким именем уже существует (например, он скрыт, находится в другой группе или зарезервирован системой), то платформа автоматически добавляет цифровой суффикс (индекс) для предотвращения конфликта имен. Так получается имя ДополнительнаяИнформацияПоДоставке1.
Здесь кроется ключевое различие, которое часто путает начинающих разработчиков: Имя элемента формы не равно имени реквизита данных!
Элемент формы (например, ПолеФормы) — это лишь визуальное представление на экране. У него есть свойство Путь к данным (ПутьКДанным). Именно в этом свойстве указывается реальный реквизит, в котором хранится значение. Для элемента с именем ДополнительнаяИнформацияПоДоставке1 путем к данным будет являться исходный реквизит объекта: Объект.ДополнительнаяИнформацияПоДоставке.
Если мы не можем понять, какой реквизит отвечает за отображение конкретного поля на форме, выполним следующие шаги в режиме Предприятия:
Объект.ДополнительнаяИнформацияПоДоставке, то сам реквизит в метаданных называется ДополнительнаяИнформацияПоДоставке.Очень часто подобные задачи решаются в рамках расширений конфигурации, чтобы не снимать основную конфигурацию с поддержки. Проанализируем типичные ошибки, которые возникают при работе с кодом в расширении.
Для того чтобы успешно обратиться к реквизиту объекта в модуле расширения, этот реквизит обязательно должен быть добавлен в расширение (активирован/захвачен в дерево метаданных расширения). Если мы попытаемся написать код, использующий реквизит ДополнительнаяИнформацияПоДоставке, но сам реквизит не перенесен в расширение, конфигуратор выдаст ошибку компиляции или контекстная подсказка просто откажется работать.
Поэтому правильный алгоритм работы выглядит так:
ЗаказКлиента) в дереве метаданных основной конфигурации.ДополнительнаяИнформацияПоДоставке.Выясним, как правильно писать обращения к реквизиту в зависимости от того, в каком модуле мы работаем. Это критически важно, так как неправильный выбор контекста ведет к падению программы с ошибкой «Переменная не определена».
Вариант А. Работаем в модуле управляемой формы (МодульФормы)
В контексте формы данные самого документа находятся в основном реквизите формы, который по умолчанию называется Объект (тип ДанныеФормыСтруктура). Поэтому обращение к данным происходит через точку от этого слова:
// Правильное обращение к значению реквизита в форме:
ЗначениеПоля = Объект.ДополнительнаяИнформацияПоДоставке;
// Если нам нужно управлять визуальными свойствами самого поля (например, сделать его доступным или скрыть):
Элементы.ДополнительнаяИнформацияПоДоставке1.Доступность = Истина;
Обратите внимание: для изменения свойств отображения мы используем имя элемента из коллекции Элементы (то, которое с единицей), а для работы со значением — имя реквизита из Объект (без единицы).
Вариант Б. Работаем в модуле объекта (МодульОбъекта) или расширении модуля объекта
В модуле объекта переменной Объект не существует! Код выполняется непосредственно внутри контекста самого документа на сервере. Обращение вида Объект.ДополнительнаяИнформацияПоДоставке вызовет фатальную ошибку. Здесь мы должны обращаться к реквизитам напрямую или через ключевое слово ЭтотОбъект:
// Правильное обращение внутри модуля объекта:
ЗначениеПоля = ЭтотОбъект.ДополнительнаяИнформацияПоДоставке;
// Либо еще проще, напрямую по имени реквизита метаданных:
ЗначениеПоля = ДополнительнаяИнформацияПоДоставке;
Рассмотрим комплексное и наиболее изящное решение задачи: нам необходимо проверять, заполнено ли поле дополнительной информации по доставке при проведении документа «Заказ клиента», если доставка осуществляется не самовывозом. Мы сделаем это через расширение модуля объекта, чтобы не привязываться к конкретным динамическим именам элементов на форме.
Для этого мы воспользуемся стандартным событием ОбработкаПроверкиЗаполнения. Напишем код, который будет выполняться на сервере при попытке проведения или записи документа:
&После("ОбработкаПроверкиЗаполнения")
Процедура Расш1_ОбработкаПроверкиЗаполненияПосле(Отказ, ПроверяемыеРеквизиты)
// Выясним способ доставки в текущем документе.
// Если способ доставки пустой или это самовывоз, проверку делать не нужно.
Если ЗначениеЗаполнено(ЭтотОбъект.СпособДоставки)
И ЭтотОбъект.СпособДоставки <> ПредопределенноеЗначение("Перечисление.СпособыДоставки.Самовывоз") Тогда
// Проверяем, заполнено ли наше целевое поле данных
Если Не ЗначениеЗаполнено(ЭтотОбъект.ДополнительнаяИнформацияПоДоставке) Тогда
// Устанавливаем признак отказа от проведения/записи
Отказ = Истина;
// Формируем понятный текст ошибки для пользователя
ТекстСообщения = "Не заполнено обязательное поле 'Дополнительная информация по доставке'!";
// Используем стандартный механизм БСП для вывода сообщения с привязкой к полю на форме.
// Благодаря указанию пути к данным, платформа автоматически найдет нужный элемент формы
// (даже если он называется ДополнительнаяИнформацияПоДоставке1) и подчеркнет его красным цветом!
ОбщегоНазначенияКлиентСервер.СообщитьПользователю(
ТекстСообщения,
ЭтотОбъект,
"ДополнительнаяИнформацияПоДоставке", // Имя реквизита в объекте базы данных
"Объект.ДополнительнаяИнформацияПоДоставке" // Путь к данным на управляемой форме
);
КонецЕсли;
КонецЕсли;
КонецПроцедуры
Разберем, почему данный подход является наиболее надежным и отказоустойчивым:
ДополнительнаяИнформацияПоДоставке1 на ДополнительнаяИнформацияПоДоставке2, наш код продолжит работать без сбоев.СообщитьПользователю библиотеки БСП принимает параметром путь к данным формы "Объект.ДополнительнаяИнформацияПоДоставке". Платформа самостоятельно сопоставит этот путь со свойством ПутьКДанным любого динамически созданного элемента и корректно сфокусирует пользователя на проблемном поле ввода.