При работе с платформой 1С:Предприятие разработчики часто сталкиваются с необходимостью доработки функционала с помощью расширений конфигурации и создания внешних обработок. Однако, когда требуется, чтобы реквизиты внешней обработки, особенно реквизиты табличных частей, ссылались на объекты, созданные в расширении, возникает неочевидная проблема: платформа не позволяет напрямую выбрать или даже программно присвоить такой тип.
В этой статье мы подробно разберем, почему возникает такая ситуация, и предложим проверенное рабочее решение, которое поможет вам успешно присвоить реквизиту во внешней обработке тип из расширения.
Прежде чем перейти к решению, выясним причину возникновения данной проблемы. Взаимодействие внешних обработок с расширениями имеет свои особенности, которые обусловлены архитектурой платформы 1С:Предприятие.
Изоляция внешних обработок: Внешние обработки (файлы с расширением .epf или .erf) существуют как самостоятельные программные объекты, которые загружаются и выполняются в отдельном контексте. Они не являются частью основной конфигурации и работают в некотором роде в "песочнице". Такой подход обеспечивает стабильность системы: ошибки во внешней обработке с меньшей вероятностью приведут к фатальным сбоям всей информационной базы. Однако эта изоляция накладывает ограничения на их возможности, в частности, на прямую видимость объектов метаданных, созданных в расширениях, в режиме Конфигуратора.
Расширения как "мини-конфигурации": Расширения конфигурации, появившиеся в платформе 8.3.6 (и получившие развитие в 8.3.10), представляют собой мощный механизм для доработки функционала конфигурации без изменения ее самой. Они позволяют добавлять новые объекты метаданных, изменять существующие формы и модули, а также дополнять функциональность типовых объектов. Расширения "наследуют" объекты основной конфигурации и расширяют их возможности, но при этом они не являются частью самой внешней обработки.
Доступность типов из расширений: Исторически, в ранних версиях платформы 1С:Предприятие объекты, созданные в расширениях, не были напрямую доступны из внешних обработок в режиме Конфигуратора при выборе типов для реквизитов. Это приводило к невозможности явного указания типа ДокументСсылка.МойДокументИзРасширения или СправочникСсылка.__Партии (где "__" указывает на объект расширения) через стандартный механизм выбора типов. Тем не менее, ожидается, что в будущих версиях платформы (например, 8.3.28) эта проблема будет решена, и объекты расширений будут доступны для выбора в режиме Конфигуратора во внешних отчетах и обработках.
Одним из ключевых моментов, который мы выяснили в ходе обсуждения, является принципиальное различие между реквизитом самой внешней обработки (как объекта метаданных) и реквизитом формы, размещенным на этой обработке. Это различие определяет доступность различных типов данных и методов работы с ними.
Реквизиты внешней обработки (объекта): Эти реквизиты создаются на закладке "Реквизиты" самой внешней обработки (в дереве метаданных Конфигуратора, если мы открываем внешнюю обработку как объект). Для таких реквизитов, предназначенных для хранения данных обработки, список доступных типов довольно ограничен. Если мы хотим хранить ссылочные данные, чаще всего нам будет предложен тип "ЛюбаяСсылка", но при этом отсутствует тип "Произвольный". Попытки программно установить ограничение типа для таких реквизитов часто не приводят к желаемому результату, поскольку платформа игнорирует эти ограничения при работе с ссылками на объекты расширений.
Реквизиты формы: Эти реквизиты создаются на закладке "Реквизиты" конкретной формы внешней обработки (в дереве форм, если мы открываем форму обработки). Для реквизитов формы, в отличие от реквизитов объекта, становится доступен тип "Произвольный". Это ключевое отличие. Тип "Произвольный" дает нам максимальную гибкость и возможность управлять типом данных, которые могут храниться в этом реквизите, уже на этапе выполнения кода. Именно через реквизиты формы мы сможем реализовать наше решение.
Для лучшего понимания ситуации рассмотрим подходы, которые мы попробовали и которые оказались неэффективными при попытке присвоить реквизиту табличной части внешней обработки тип из расширения. Эти попытки основаны на стандартных методах, которые, к сожалению, не учитывают специфику взаимодействия с расширениями для реквизитов объекта обработки:
Создание реквизита табличной части как реквизита объекта внешней обработки:
Мы создаем новый реквизит для табличной части на закладке "Реквизиты" самой внешней обработки (не формы). Для такого реквизита мы пытаемся выбрать тип ссылки на документ из расширения. В режиме Конфигуратора, как правило, такой тип недоступен для выбора. Максимум, что мы можем выбрать для ссылочных данных – это тип "ЛюбаяСсылка".
Программное ограничение типа для реквизита объекта обработки с типом "ЛюбаяСсылка":
Даже если мы выбираем тип "ЛюбаяСсылка" для реквизита объекта внешней обработки и затем пытаемся программно ограничить его с помощью объекта ОписаниеТипов в процедуре ПриСозданииНаСервере, этот подход также не срабатывает. Платформа игнорирует это ограничение для реквизитов объекта, особенно когда речь идет о типах из расширений.
&НаСервере
Процедура ПриСозданииНаСервере(Отказ, СтандартнаяОбработка)
// Пример неработающего кода для реквизита объекта обработки
// Элементы.РезультатПартия - это элемент формы, связанный с реквизитом объекта обработки
ОписаниеТипа = Новый ОписаниеТипов("СправочникСсылка.__Партии");
Элементы.РезультатПартия.ОграничениеТипа = ОписаниеТипа;
КонецПроцедуры
В этом случае, несмотря на программную установку ограничения, поле на форме все равно будет позволять выбрать любой ссылочный тип, и автоматическое приведение к требуемому типу из расширения не произойдет должным образом.
Мы видим, что прямые методы, которые хорошо работают для типовых объектов конфигурации, оказываются бессильны в условиях специфического взаимодействия внешних обработок с типами, определенными в расширениях.
Теперь, когда мы понимаем причины проблемы и отличия между реквизитами, давайте перейдем к действенному решению, которое подтверждено практикой и отзывами коллег. Суть его заключается в использовании реквизита формы с типом "Произвольный" и последующим программным ограничением этого типа. Мы рассмотрим это решение по шагам.
Удаляем "неправильный" реквизит (при необходимости):
Если вы уже создали реквизит табличной части как реквизит самого объекта внешней обработки и присвоили ему тип "ЛюбаяСсылка" (или пытались присвоить другой тип из расширения), рекомендуем удалить его. Это позволит избежать путаницы и гарантировать чистый старт.
Добавляем реквизит формы для табличной части:
Вместо реквизита объекта внешней обработки, мы создаем новый реквизит на форме нашей обработки. Для этого:
РезультатПартия.ТабличнаяЧастьФормы, затем добавьте к ней новый реквизит с типом "Произвольный".Элементы.РезультатПартия.Программно прописываем ограничение типа в процедуре ПриСозданииНаСервере:
После того как реквизит формы с типом "Произвольный" создан, мы можем программно указать, какие именно типы данных он должен принимать. Для этого используем процедуру ПриСозданииНаСервере модуля формы. Эта процедура выполняется при создании формы на сервере, до ее отображения пользователю, что идеально подходит для установки начальных параметров.
Для установки ограничения типа нам потребуется два основных объекта:
ОписаниеТипов: Этот объект позволяет сформировать описание нужных типов. Мы можем указать один или несколько типов, включая ссылки на объекты из расширений. Для обращения к объекту расширения используем префикс __ (два нижних подчеркивания) перед именем объекта, например, ДокументСсылка.__МойДокументИзРасширения или СправочникСсылка.__Партии.Элементы.ИмяЭлементаФормы.ОграничениеТипа: Это свойство элемента формы, связанного с нашим реквизитом, позволяет установить ограничение по типу. В качестве значения этому свойству мы присваиваем созданный объект ОписаниеТипов.Рассмотрим пример кода:
&НаСервере
Процедура ПриСозданииНаСервере(Отказ, СтандартнаяОбработка)
// Создаем объект ОписаниеТипов, указывая полный тип объекта из расширения
// В данном примере используется "СправочникСсылка.__Партии",
// но вы можете подставить свой тип, например "ДокументСсылка.__МойДокументИзРасширения"
ОписаниеТипа = Новый ОписаниеТипов("СправочникСсылка.__Партии");
// Получаем элемент формы, связанный с нашим реквизитом табличной части
// Имя элемента формы должно соответствовать имени, которое вы дали элементу управления на форме
// Например, если реквизит табличной части называется 'Партия', а элемент формы 'РезультатПартия',
// то здесь нужно указать 'РезультатПартия'.
Элементы.РезультатПартия.ОграничениеТипа = ОписаниеТипа;
КонецПроцедуры
После выполнения этих шагов, при работе с внешней обработкой, поле в табличной части, связанное с реквизитом РезультатПартия, будет принимать только значения указанного типа из расширения. Это позволит корректно заполнять табличную часть ссылками на объекты расширения, например, путем подтягивания их запросом.
Для более глубокого понимания и эффективного использования описанного решения, а также для учета возможных будущих задач, рассмотрим несколько дополнительных аспектов:
Работа с составными типами: Если ваш реквизит формы должен принимать значения нескольких различных ссылочных типов (например, и документы, и справочники из расширения), вы можете указать их все в объекте ОписаниеТипов, перечисляя через запятую:
ОписаниеТипа = Новый ОписаниеТипов("СправочникСсылка.__Партии, ДокументСсылка.__МойДокументИзРасширения");
При использовании составных типов, особенно в запросах, отборах или сортировке, рекомендуется оставлять в их составе только ссылочные типы (например, СправочникСсылка.[…], ДокументСсылка.[…]) и избегать включения нессылочных типов (Строка, Число, Дата и т.п.) для оптимизации производительности и корректной работы с данными.
Использование ПривестиЗначение(): Если реквизит формы имеет составной тип и требуется назначить ему конкретное значение из набора допустимых типов, вы можете использовать метод ПривестиЗначение() объекта ОписаниеТипов для приведения значения к нужному типу:
ЗначениеИзРасширения = Запрос.Выполнить().Выбрать().Следующий().СсылкаНаОбъектИзРасширения;
ОписаниеТипаДляПриведения = Новый ОписаниеТипов("СправочникСсылка.__Партии");
ПриведенноеЗначение = ОписаниеТипаДляПриведения.ПривестиЗначение(ЗначениеИзРасширения);
// Теперь ПриведенноеЗначение можно присвоить реквизиту формы
Программное создание реквизитов формы: В некоторых сложных сценариях может потребоваться программное создание не только ограничения типа, но и самих реквизитов формы и их элементов. Это дает полный контроль над их свойствами, но значительно усложняет разработку. Для большинства типовых задач достаточно подхода, описанного выше.
Встроенные обработки в расширениях: Если внешняя обработка является частью расширения (т.е., она была заимствована в расширение или создана непосредственно внутри него), то для ее реквизитов можно напрямую выбирать типы из этого же расширения в режиме Конфигуратора. Описанные выше сложности возникают именно тогда, когда мы говорим о "внешней" обработке, которая существует как отдельный файл .epf и не является частью какого-либо расширения.
Перспективы развития платформы: Как мы уже упоминали, в будущих версиях платформы (например, 8.3.28) ожидается улучшение взаимодействия внешних отчетов и обработок с объектами расширений. Это изменение направлено на упрощение процесса назначения типов и повышение удобства разработки, позволяя выбирать типы объектов расширений непосредственно в Конфигураторе.
Мы надеемся, что это подробное руководство поможет вам успешно справиться с задачей присвоения типа из расширения реквизиту табличной части во внешней обработке. Помните, что понимание архитектурных особенностей платформы и гибкое использование ее механизмов — ключ к решению нетривиальных задач.