В процессе разработки и анализа типовых конфигураций, таких как ЗУП 3.1, программисты часто сталкиваются с необычным поведением платформы 1С:Предприятие 8.3. Одной из таких "загадок" является работа с подписками на события, в которых источником выступают не конкретные объекты (например, СправочникОбъект.Сотрудники), а целые группы менеджеров. Рассмотрим ситуацию: в конфигураторе мы видим четко заданный список типов в источнике подписки, но при попытке программно получить этот список через Метаданные, платформа возвращает пустой массив. Разберем подробно, почему это происходит и как с этим работать.
Проанализируем конкретный пример. В конфигурации существует подписка на событие ТарификацияОбработкаПолученияФормы. В качестве источника в ней указаны обобщенные типы: СправочникМенеджер, ДокументМенеджер, РегистрСведенийМенеджер и другие. На уровне работы приложения всё функционирует корректно — событие перехватывается для всех справочников и документов. Однако, если мы попытаемся программно проинспектировать эту подписку, мы столкнемся с неожиданным результатом.
Посмотрим на пример кода, который должен вывести количество типов в источнике:
ПодпискаМетаданные = Метаданные.ПодпискиНаСобытия.ТарификацияОбработкаПолученияФормы;
КоличествоТипов = ПодпискаМетаданные.Источник.Типы().Количество();
Сообщить("Количество типов: " + КоличествоТипов); // Результат будет: 0
Ситуация кажется парадоксальной: типы в конфигураторе выбраны, платформа их "понимает" при выполнении кода, но коллекция Типы() объекта ОписаниеТипов пуста. Более того, метод СодержитТип() также будет возвращать Ложь, даже если мы передадим туда правильный, казалось бы, тип.
Выясним причину такого поведения. В системе 1С существует разделение на конкретные типы метаданных и базовые (абстрактные) типы платформы. Когда мы выбираем в подписке конкретный справочник, например, СправочникМенеджер.Валюты, это ссылка на конкретный объект метаданных вашей конфигурации. Платформа легко включает его в массив Типы().
Однако типы вроде СправочникМенеджер (во множественном числе или как общая категория) — это внутренние системные классы платформы. Они не принадлежат к конкретным метаданным конфигурации, а являются "родительскими" типами для всех менеджеров данного вида. Платформа 1С использует их для оптимизации: вместо хранения тысяч ссылок на каждый справочник в базе, она хранит один идентификатор, обозначающий "любой менеджер справочника". Проблема в том, что стандартный объект ОписаниеТипов в языке 1С не предназначен для работы с этими системными абстракциями, поэтому он просто "игнорирует" их при вызове метода Типы().
Чтобы понять, что на самом деле хранится внутри источника подписки, мы можем воспользоваться методом ЗначениеВСтрокуВнутр(). Это позволит нам заглянуть во внутреннее представление объекта ОписаниеТипов. Проанализируем результат сериализации такого "пустого" источника:
// Получаем внутреннюю строку описания типов источника
ВнутренняяСтрока = ЗначениеВСтрокуВнутр(ПодпискаМетаданные.Источник);
Сообщить(ВнутренняяСтрока);
В результате мы увидим структуру, содержащую странные идентификаторы (GUID). Например:
{"#",f5c65050-3bbb-11d5-b988-0050bae0a95d, {"Pattern", {"#",82faabf3-7f9b-4b2e-b499-98876415f270}, {"#",26dd1dee-252a-4942-b4b5-62ea44ed8030} } }
Здесь f5c65050-3bbb-11d5-b988-0050bae0a95d — это идентификатор самого класса ОписаниеТипов, а GUID внутри секции Pattern — это и есть те самые "невидимые" типы. Важно понимать, что эти идентификаторы являются фиксированными для платформы 1С. Они не меняются от конфигурации к конфигурации.
В результате коллективного исследования были определены основные идентификаторы, которые соответствуют обобщенным менеджерам в платформе 8.3. Эти данные можно использовать для создания собственных механизмов анализа метаданных, когда стандартные средства бессильны.
Рассмотрим основные соответствия:
СправочникМенеджер (все справочники)ДокументМенеджер (все документы)РегистрСведенийМенеджерРегистрНакопленияМенеджерРегистрРасчетаМенеджерРегистрБухгалтерииМенеджерПланВидовХарактеристикМенеджерПланВидовРасчетаМенеджерПланСчетовМенеджерЖурналДокументовМенеджерЗадачаМенеджерБизнесПроцессМенеджерОбратите внимание, что идентификаторы для "Объектов" (например, СправочникОбъект) будут другими, но они обычно корректно отображаются в коллекции Типы(), если выбраны в метаданных.
Поскольку Источник.СодержитТип(Тип("СправочникМенеджер.Валюты")) вернет Ложь (так как в источнике указан абстрактный "любой менеджер", а не конкретный), нам нужны обходные пути. Разберем два варианта решения этой задачи.
Это наиболее простой, хоть и "грязный" способ. Мы можем проверить наличие системного GUID в сериализованной строке описания типов. Если нам нужно понять, включены ли в подписку "все справочники", мы ищем соответствующий идентификатор.
Функция ПодпискаСодержитВсеСправочники(ПодпискаОбъект)
СтрокаТипов = ЗначениеВСтрокуВнутр(ПодпискаОбъект.Источник);
Возврат Найти(СтрокаТипов, "82faabf3-7f9b-4b2e-b499-98876415f270") > 0;
КонецФункции
Более надежный способ — программно создать ОписаниеТипов, содержащее нужный нам обобщенный менеджер, и сравнить его с источником. Но здесь есть тонкость: в языке 1С нельзя напрямую написать Тип("СправочникМенеджер"). Однако мы можем получить описание типов из метаданных другой подписки или программно сконструировать его, зная структуру внутреннего представления.
Если нам нужно проверить, подходит ли конкретный объект (например, менеджер справочника "Склады") под эту подписку, лучше всего использовать блок Попытка...Исключение или анализировать метаданные самого объекта, сопоставляя их с имеющимися в подписке конкретными типами, если они там есть.
Стоит отдельно отметить, почему эта проблема наиболее остро проявляется именно в событии ОбработкаПолученияФормы. В традиционных событиях типа ПриЗаписи источником всегда является Объект (ссылочный тип данных). Эти типы платформа умеет отображать в коллекции Типы().
В событии ОбработкаПолученияФормы источником выступает Менеджер. Это фундаментальное отличие. Менеджер — это не данные в таблице БД, это программная сущность для управления этими данными. Именно при работе с менеджерами платформа чаще всего использует свои внутренние оптимизированные идентификаторы классов, что и приводит к "пустоте" коллекции типов в метаданных.
Мы выяснили, что отсутствие типов в программном доступе к метаданным подписки — это не ошибка конфигурации, а особенность реализации платформы 1С для обобщенных категорий менеджеров. Основные выводы нашего анализа:
Типы() возвращает пустой массив для системных групп (СправочникиМенеджер, ДокументыМенеджер и т.д.).ЗначениеВСтрокуВнутр().Знание этих внутренних механизмов позволяет разработчику создавать более гибкие инструменты для анализа конфигурации и понимания того, как на самом деле работают подписки на события "под капотом" платформы.