Работа с данными 1С: Предприятие 7.7 в контексте метаданных


В сети интернет достаточно материалов с описанием справочника функций по работе с метаданными 1С: Предприятие 7.7. Например, здесь. А, также, статей с описанием формата таблиц данных и правилам построения прямых запросов к ним. Например, здесь и здесь. Зачем же нужна эта статья?

Дело в том, что есть целый класс задач по работе с данными 1С: Предприятие 7.7, которые методы, перечисленные в данных [и других подобных] статьях не решают. В качестве примера, можно рассмотреть ситуацию, когда известно некоторое значение информационной базы и требуется определить, каким образом оно хранится в таблицах dbf-файлов, если мы имеем файловый формат информационной базы. Или, обратная к ней задача, когда есть символьное значение некоторого поля dbf-таблицы и нам требуется определить соответствующее ему значение информационной базы.

С подобными задачами, в той или иной постановке, сталкивается каждый, кто начинает, например, применять механизм «прямых запросов» в работе с данными информационной базы. При этом, стандартного набора функций и процедур 1С: Предприятие 7.7 недостаточно для решения этих задач и он, как будто, не подразумевает такой вариант использования механизмов платформы. В справочной литературе на эту тему, практически, ничего нет, а примеров само-документированного кода, в сети интернет, не так уж много.

Заполнить этот пробел и призвана эта статья. В которой, со ссылкой на рабочий пример конфигурации, с реализованным 1С++ классом «Метаданные», будет представлен пример решения вышеуказанных задач.

Начнем с первой, из этих задач, а именно, поиска представления значения информационной базы в таблицах dbf-файлов. Для решения этой задачи, вполне достаточно механизмов самой платформы, правда, некоторой частью, недокументированных.

В ходе решения этой задачи, мы будем пользоваться стандартной функцией платформы, «ЗначениеВСтрокуВнутр(<Значение>)» [см. справку по языку 1С: Предприятие 7.7]. Откуда, в частности, можно узнать, что эта функция возвращает символьную строку, содержащую некоторое внутреннее представление заданного значения информационной базы. Однако, в справке нет никакой информации о том, каким образом сформирована эта строка, например, для значения ссылочного типа.

На самом деле, все очень просто. Символьная строка представляет собой результат выполнения функции «ВСтрокуСРазделителями()» списка значений, первое, четвертое и седьмое значение, которого, представляют, в совокупности, полный путь к строке записи и определяют, соответственно, префикс имени файла [«SC» — для справочников или «DH» — для документов], номер файла и порядковый номер строки, задающий первичный ключ в таблице данных.

На этом, можно было бы считать нашу задачу решенной, если бы не один очень важный момент. Значения идентификаторов в таблицах информационной базы задаются в формате Base36 -строки. При этом, в ходе трансформации, 13-ти символьная строка идентификатора объекта в числовом формате, преобразуется в 9-ти символьную строку. Причем, последние 3 символа строки, представляющие код узла распределенной информационной базы, переносятся в результирующую строку без изменений. Для трансформации идентификатора во внутреннее представление используется недокументированная функция «_IdToStr(<Значение>)» платформы 1С: Предприятие 7.7.

// получение внутреннего представления значения информационной базы
Ид = глмМетаданные.Ссылка2Ид(ЗначениеИБ);
 
// определение номера в имени dbf-файла
КодТаблицы = глмМетаданные.Ид2КодТаблицыИБ(Ид);
 
// определение типа значения	
Тип = глмМетаданные.Ид2Тип(глмМетаданные.ИдКаталога(Ид));
 
Если Тип = "Справочник" Тогда
 
// имя dbf-файла таблицы		
    ИмяФайла = "SC" + КодТаблицы + ".dbf";
 
// первичный ключ (PK) таблицы		
    ИмяПоля = "ID";
 
// искомое значение ключа строки 		
    ЗначениеПоля = глмМетаданные.Ид2СтрокаИБ(Ид);
 
ИначеЕсли Тип = "Документ" Тогда
 
// имя dbf-файла таблицы		
    ИмяФайла = "DH" + КодТаблицы + ".dbf";
 
// первичный ключ (PK) таблицы		
    ИмяПоля = "IDDOC";
 
// искомое значение ключа строки 		
    ЗначениеПоля = глмМетаданные.Ид2СтрокаИБ(Ид);
 
КонецЕсли;

Решение обратной задачи, для случая, когда значение является значением первичного ключа одной из таблиц информационной базы, можно получить, основываясь на тех же принципах, применяя для этих целей композицию функций «_StrToId(<Значение>)» и «ЗначениеИзСтрокиВнутр(<Значение>)» — обратных к указанным выше.
// получение внутреннего представления значения информационной базы
Ид = глмМетаданные.КодИБ2Ид(КодТаблицы, ЗначениеПоля);
 
// получение значения информационной базы
ЗначениеИБ = глмМетаданные.Ид2Ссылка(Ид);

Для случая, когда определяемое значение символьного типа, в заданной таблице, является реквизитом, стандартных методов платформа 1С: Предприятие 7.7 не предоставляет и мы будем вынуждены обратиться к методам внешних компонент. Таким, например, как функция «ИДОбъекта(<Метаданные>)» объекта «MetaDataWork» компоненты «1С++».
// получение номера поля (идентификатора реквизита)
КодПоля = Сред(ИмяПоля, 3);
 
// получение данных соответствующего реквизита объекта метаданных
сзРеквизит = глмМетаданные.КодИб2Реквизит(_IdToStr(КодПоля));
 
// получение набора свойств реквизита
тзСвойстваРеквизита = глмМетаданные.ВыбратьСвойстваРеквизита(сзРеквизит.Получить("Реквизит"), 
    сзРеквизит.Получить("Объект"));
 
// получение внутреннего представления значения информационной базы		
Ид = глмМетаданные.КодИБ2Ид(тзСвойстваРеквизита.ПолучитьЗначение(5, 2) 
    + "." + тзСвойстваРеквизита.ПолучитьЗначение(6, 2), ЗначениеПоля);	
 
// получение значения информационной базы		
ЗначениеИБ = глмМетаданные.Ид2Ссылка(Ид);

И, раз уже зашла речь об использовании стандартных средств платформы 1С: Предприятие 7.7 для работы с метаданными конфигурации, хочется отметить определенный дискомфорт, который приходится испытывать программисту, в процессе работы, связанный, прежде всего, с отсутствием универсальности в синтаксических конструкциях методов, выполняющих одинаковые функции для разных объектов метаданных. При этом, такие простые задачи, как выборка и анализ свойств стандартных реквизитов объектов метаданных решаются «нагромождением» программного кода в несколько строк, причем, для каждого типа метаданных, отдельно. Поэтому, желание написать некоторую программную оболочку для стандартных процедур работы с метаданными 1С: Предприятие 7.7, вполне естественно. Ниже, представлены примеры, как в 1С++ классе «Метаданные» решаются подобные задачи.
Объекты = глмМетаданные.ВыбратьОбъекты(глмМетаданные.ПолучитьПустойИд());
 
Объекты.ВыбратьСтроки();
Пока Объекты.ПолучитьСтроку() = 1 Цикл
    Если глмМетаданные.ЭтоКаталог(Объекты.Ид) = 0 Тогда
 
        сзМетаданные.ДобавитьЗначение(Объекты.Ид, 
            глмМетаданные.Ид2Тип(Объекты.Ид));
 
    КонецЕсли;	
КонецЦикла;

тзРеквизиты = глмМетаданные.ВыбратьРеквизиты(Ид, "", "", "");

Свойства = глмМетаданные.ВыбратьСвойстваРеквизита(тзРеквизиты, Ид);
 
Свойства.ВыбратьСтроки();
Пока Свойства.ПолучитьСтроку() = 1 Цикл
    Сообщить("""" + Свойства.Имя + """ -> """ + Свойства.Значение + """");
КонецЦикла;

//*******************************************
Процедура ПечатьСведенийТаблицыСправочника(Ид)
    Перем КодПоля;
 
    КодТаблицы = глмМетаданные.Ид2КодТаблицыИБ(Ид);
 
    Сообщить("//*******************************************");
    Сообщить("Таблица данных ""SC" + КодТаблицы + """");
    Сообщить("Поля:");
 
    Реквизиты = глмМетаданные.ВыбратьРеквизиты(Ид, 
        "", "РеквизитСтандартный", "");
 
    Реквизиты.ВыбратьСтроки();                                               
    Пока Реквизиты.ПолучитьСтроку() = 1 Цикл
 
        Поз = глмМетаданные.мСтандартныеРеквизитыСправочников
            .НайтиЗначение(Реквизиты.Ид);
 
        глмМетаданные.мСтандартныеРеквизитыСправочников
            .ПолучитьЗначение(Поз, КодПоля);	
 
        Сообщить("""" + Реквизиты.Ид + """ -> """ + КодПоля + """");
    КонецЦикла;	
 
    Реквизиты = глмМетаданные.ВыбратьРеквизиты(Ид, 
        "", "Реквизит", "");
 
    Реквизиты.ВыбратьСтроки();                                               
    Пока Реквизиты.ПолучитьСтроку() = 1 Цикл
 
        КодПоля = "SP" + глмМетаданные
            .Реквизит2КодПоляИБ(Реквизиты, Ид);
 
        Сообщить("""" + Реквизиты.Ид + """ -> """ + КодПоля + """");
    КонецЦикла;	
КонецПроцедуры