В практике разработки на платформе 1С:Предприятие 8.3 часто возникает ситуация, когда внешняя обработка должна взаимодействовать с данными, добавленными через расширения конфигурации. Типичный сценарий: во внешней обработке создан реквизит (или колонка табличной части) с типом Любая ссылка, и мы пытаемся записать туда ссылку на справочник, существующий только в расширении. Однако при выполнении кода НовСтр.Ссылка = Выборка.Ссылка; мы с удивлением обнаруживаем, что значение превращается в Неопределено. Разберем подробно, почему это происходит и как правильно решить данную задачу.
Первым делом нам необходимо понять, как работает тип данных Любая ссылка. Вопреки названию, этот тип не является абсолютно универсальным «контейнером» для любых ссылок в системе. Когда вы создаете внешнюю обработку (файл .epf) в конфигураторе, платформа фиксирует список типов, которые входят в состав Любая ссылка, исходя из метаданных основной конфигурации.
Поскольку расширение подключается к информационной базе динамически, внешняя обработка на этапе проектирования «не знает» о типах данных, определенных в расширении. Для метаданных внешней обработки типов из расширения просто не существует в перечне допустимых. В момент присвоения значения система проверяет, входит ли тип присваиваемого объекта в разрешенный набор типов реквизита. Если типа нет в списке — 1С сбрасывает значение в Неопределено. Это стандартное поведение платформы для обеспечения строгой типизации.
Самый правильный и технологичный способ — это динамическое расширение состава типов реквизита в момент выполнения кода. Мы не можем изменить метаданные обработки «на лету» в конфигураторе, но мы можем переопределить объект ОписаниеТипов для конкретного элемента формы или колонки таблицы.
Рассмотрим, как это реализовать программно. Нам нужно создать новый массив типов, включив в него как стандартные типы, так и типы из расширения, а затем назначить это описание реквизиту. Выясним, как получить тип из расширения, если мы не можем выбрать его визуально:
// Пример добавления типа из расширения в описание типов колонки
МассивТипов = Новый Массив;
// Добавляем стандартный тип (например, все ссылки)
МассивТипов.Добавить(Тип("СправочникСсылка.Номенклатура"));
// Добавляем тип из расширения по его строковому имени
// Важно: укажите префикс, если он есть в расширении
ТипИзРасширения = Тип("СправочникСсылка.ПРФ_МойСправочникИзРасширения");
Если ТипИзРасширения <> Неопределено Тогда
МассивТипов.Добавить(ТипИзРасширения);
КонецЕсли;
НовоеОписание = Новый ОписаниеТипов(МассивТипов);
// Если работаем с колонкой таблицы значений на форме:
Элементы.ТаблицаДанныхКолонкаСсылка.ОграничениеТипа = НовоеОписание;
Важный нюанс: Если реквизит принадлежит табличной части, описанной в метаданных обработки (в дереве объектов .epf), изменить его тип программно крайне сложно из-за жесткой структуры объектов метаданных. В таких случаях рекомендуется использовать Таблицу Значений, размещенную непосредственно на форме, а не в реквизитах объекта.
Если мы используем программное создание колонок для ТаблицыЗначений, мы получаем полную свободу в типизации. Давайте посмотрим на пример создания колонки, которая сможет принять ссылку любого типа, включая те, что появятся в будущем в любых расширениях:
// Программное создание колонки ТЗ с поддержкой типов расширений
МассивТипов = Новый Массив;
МассивТипов.Добавить(Тип("СправочникСсылка.Валюты")); // пример
// Если нужно разрешить вообще все ссылки, используем конструктор ОписаниеТипов
// Однако для расширений лучше указывать типы явно или собирать их перебором
ТЗ = Новый ТаблицаЗначений;
ТЗ.Колонки.Добавить("СсылкаНаОбъект", Новый ОписаниеТипов("СправочникСсылка.ПРФ_МойСправочник"));
// Теперь присвоение будет работать корректно
НоваяСтрока = ТЗ.Добавить();
НоваяСтрока.СсылкаНаОбъект = Справочники.ПРФ_МойСправочник.НайтиПоКоду("001");
В некоторых ситуациях (например, при сложной передаче данных между контекстами или в старых версиях платформы) программисты используют «извращенный», но рабочий метод сохранения ссылки через её уникальный идентификатор. Рассмотрим этот алгоритм:
Строка (длиной 36 символов для GUID) и реквизит ИмяТипа (строка).Строка(Выборка.Ссылка.УникальныйИдентификатор()) и имя метаданных справочника.Проанализируем код восстановления ссылки:
// Восстановление ссылки из строки GUID
ИмяТипа = "Справочник.ПРФ_МойСправочник";
ГУИД = Новый УникальныйИдентификатор(СтрокаИзРеквизита);
Ссылка = Справочники[ИмяТипа].ПолучитьСсылку(ГУИД);
Если Ссылка.ПолучитьОбъект() = Неопределено Тогда
Сообщить("Объект не найден в базе!");
КонецЕсли;
Внимание: Этот способ является трудозатратным и менее производительным, так как требует дополнительных операций поиска. Используйте его только в крайнем случае, если динамическое управление ОписаниеТипов недоступно.
Выясним еще одну важную деталь: часто ошибка Неопределено возникает из-за того, что обработка запущена «вне контекста». Если в свойствах расширения не стоит галочка «Использовать в основных типах», или если режим совместимости конфигурации ниже 8.3.10, работа с типами расширений во внешних файлах может быть нестабильной.
Всегда проверяйте в отладчике значение Выборка.Ссылка непосредственно перед присвоением. Если там уже Неопределено, значит, запрос выполняется в контексте, где расширение неактивно или типы не видны. Если же в Выборка.Ссылка есть валидная ссылка, а после присвоения в НовСтр.Ссылка значение пропадает — значит, мы столкнулись именно с ограничением ОписаниеТипов, описанным выше.
Подведем итоги нашей совместной работы. Чтобы ваша внешняя обработка успешно принимала ссылки из справочников расширения, следуйте этим правилам:
ОписаниеТипов с помощью конструктора от строки: Новый ОписаниеТипов("СправочникСсылка.ИмяИзРасширения").Любая ссылка, перед заполнением данных программно обновите ОграничениеТипа этого реквизита, добавив туда нужные типы из расширения через Массив.Расш1_Склады, то и в коде к нему нужно обращаться именно по этому полному имени.Используя данные подходы, вы обеспечите стабильную работу своих внешних обработок в условиях постоянно меняющихся расширений конфигурации.