Публикация может быть полезна начинающим программистам 1С8. Для опытных - ничего нового.
"Универсальное" - значит совместимое с несколькими платформами, конфигурациями, метаданными. Такие решения обладают преимуществом быстрого внедрения в существующую 1С инфраструктуру организации.
Примеры решений на сайте infostart.ru
Вопрос создания подобных прикладных решений связан с разработкой дополнительных алгоритмов проверок метаданных конфигурации.
Цель публикации - рассмотрение примера использования макета табличный документ в качестве хранилища имён метаданных для решения задачи поиска возможных для работы программы объектов прикладного типа, их реквизитов, измерений, табличных частей и др.
Макет табличный документ можно хранить в объекте внешней обработки.
При использовании внешней обработки в управляемой форме необходимо получить макет, например, в предопределенной процедуре ПриОткрытии:
&НаКлиенте
Перем МакетСписокИменМетаданных;
&НаСервере
Функция ПолучитьМакетНаСервере(ИмяМакета)
Возврат РеквизитФормыВЗначение("Объект").ПолучитьМакет(ИмяМакета);
КонецФункции
&НаКлиенте
Процедура ПриОткрытии(Отказ)
МакетСписокИменМетаданных = ПолучитьМакетНаСервере("СписокИменМетаданных");
КонецПроцедуры
Формат макета табличный документ СписокИменМетаданных:

Каждый столбец - список имён метаданных, используемых в различных конфигурациях для решения одной и той же задачи.
Первая строка табличного документа - имя параметра для алгоритма, а также идентификатор для проверки и поиска прикладного объекта/реквизита: Справочник, Документ, Регистр, Табличная часть, Измерение и др.
Извлечения списка имён из макета табличный документ
Получение списка имён простым перебором колонок и строк табличного документа.
//Функция возвращает список имён параметра из текстового макета
//Входные параметры:
// Макет - табличный документ предложенного выше формата,
// ИмяПараметра - "СпрНоменклатура", "ТчТовары", "РсШтрихкоды" и другое имя (первая строка макета).
&НаСервере
Функция СписокИменПараметра(ИмяПараметра="", Макет=Неопределено)
Если Макет=Неопределено Тогда Возврат Неопределено; КонецЕсли;
Колонка = 0; ЕстьПараметр = Истина; СЗ = Новый СписокЗначений;
Пока ЕстьПараметр Цикл
Колонка = Колонка+1;
Параметр = СокрЛП(Макет.Область(1,Колонка).Текст);
Если НЕ ПустаяСтрока(Параметр) Тогда
Если Параметр=ИмяПараметра Тогда
Строка = 1;
ЕстьЗначение = Истина;
Пока ЕстьЗначение Цикл
Строка = Строка + 1;
Значение = СокрЛП(Макет.Область(Строка,Колонка).Текст);
Если НЕ ПустаяСтрока(Значение) Тогда
СЗ.Добавить(Значение);
Иначе
ЕстьЗначение = Ложь;
КонецЕсли;
КонецЦикла;
Прервать;
КонецЕсли;
Иначе
ЕстьПараметр = Ложь;
КонецЕсли;
КонецЦикла;
Возврат СЗ;
КонецФункции
На основе существующих имён/видов прикладных объектов и списка имён из макета табличный документ можно решить вопрос совместимости алгоритма для различных конфигураций.
Поиск на основе списка имён из макета
Набор функций для различных задач, работающих на основе списка имён из макета СписокИменМетаданных.
&НаСервере
Функция ИмяСправочникаИзПараметра(ИмяПараметра,Макет=Неопределено)
СЗ = СписокИменПараметра(ИмяПараметра,Макет);
Для Каждого ЭлементСписка Из СЗ Цикл
Если НЕ Метаданные.Справочники.Найти(ЭлементСписка.Значение)=Неопределено Тогда
Возврат ЭлементСписка.Значение;
КонецЕсли;
КонецЦикла;
Возврат Неопределено;
КонецФункции
&НаСервере
Функция ИмяДокументаИзПараметра(ИмяПараметра,Макет=Неопределено)
СЗ = СписокИменПараметра(ИмяПараметра,Макет);
Для Каждого ЭлементСписка Из СЗ Цикл
Если НЕ Метаданные.Документы.Найти(ЭлементСписка.Значение)=Неопределено Тогда
Возврат ЭлементСписка.Значение;
КонецЕсли;
КонецЦикла;
Возврат Неопределено;
КонецФункции
&НаСервере
Функция ИмяРегистраСведенийИзПараметра(ИмяПараметра,Макет=Неопределено)
СЗ = СписокИменПараметра(ИмяПараметра,Макет);
Для Каждого ЭлементСписка Из СЗ Цикл
Если НЕ Метаданные.РегистрыСведений.Найти(ЭлементСписка.Значение)=Неопределено Тогда
Возврат ЭлементСписка.Значение;
КонецЕсли;
КонецЦикла;
Возврат Неопределено;
КонецФункции
&НаСервере
Функция ИмяРеквизитаТчИзПараметра(ТЧ,ИмяПараметра,Макет=Неопределено)
СЗ = СписокИменПараметра(ИмяПараметра,Макет);
Для Каждого ЭлементСписка Из СЗ Цикл
Если НЕ ТЧ.Реквизиты.Найти(ЭлементСписка.Значение) = Неопределено Тогда
Возврат ЭлементСписка.Значение;
КонецЕсли;
КонецЦикла;
Возврат "";
КонецФункции
&НаСервере
Функция ИмяИзмеренияИзПараметра(РС,ИмяПараметра,Макет=Неопределено)
СЗ = СписокИменПараметра(ИмяПараметра,Макет);
Для Каждого ЭлементСписка Из СЗ Цикл
Если НЕ Метаданные.РегистрыСведений[РС].Измерения.Найти(ЭлементСписка.Значение)=Неопределено Тогда
Возврат ЭлементСписка.Значение;
КонецЕсли;
КонецЦикла;
Для Каждого ЭлементСписка Из СЗ Цикл
Если НЕ Метаданные.РегистрыСведений[РС].Ресурсы.Найти(ЭлементСписка.Значение)=Неопределено Тогда
Возврат ЭлементСписка.Значение;
КонецЕсли;
КонецЦикла;
Возврат Неопределено;
КонецФункции
Проверки и поиск имён метаданных может выполняться на основе списка существующих в конфигурации идентификаторов.
Поиск на основе списка имён из макета и метаданных
Требуется предварительно сформировать дополнительные списки.
&НаСервере
Функция СписокСправочниковМетаданных()
Список = Новый СписокЗначений;
Для Каждого Справочник Из Метаданные.Справочники Цикл
Список.Добавить(Справочник.Имя,,,БиблиотекаКартинок.Справочник);
КонецЦикла;
Возврат Список;
КонецФункции
&НаКлиенте
Функция ВидСправочникаИзПараметра(ИмяПараметра,СписокСправочников=Неопределено,Макет=Неопределено)
Если СписокСправочников = Неопределено Тогда СписокСправочников = СписокСправочниковМетаданных(); КонецЕсли;
СЗ = СписокИменПараметра(ИмяПараметра,Макет);
Для Каждого ЭлементСписка Из СЗ Цикл
ВидСпр = СписокСправочников.НайтиПоЗначению(ЭлементСписка.Значение);
Если НЕ ВидСпр = Неопределено Тогда Возврат ЭлементСписка.Значение; КонецЕсли;
КонецЦикла;
Возврат Неопределено;
КонецФункции
&НаСервере
Функция СписокДокументовМетаданных()
Список = Новый СписокЗначений;
Для Каждого Документ Из Метаданные.Документы Цикл
Список.Добавить(Документ.Имя,,,БиблиотекаКартинок.Документ);
КонецЦикла;
Возврат Список;
КонецФункции
&НаКлиенте
Функция ВидДокументаИзПараметра(ИмяПараметра,СписокДокументов=Неопределено,Макет=Неопределено)
Если СписокДокументов = Неопределено Тогда СписокДокументов = СписокДокументовМетаданных(); КонецЕсли;
СЗ = СписокИменПараметра(ИмяПараметра,Макет);
Для Каждого ЭлементСписка Из СЗ Цикл
ВидСпр = СписокДокументов.НайтиПоЗначению(ЭлементСписка.Значение);
Если НЕ ВидСпр = Неопределено Тогда Возврат ЭлементСписка.Значение; КонецЕсли;
КонецЦикла;
Возврат Неопределено;
КонецФункции
И так далее... Порядок использования макета может быть любой и зависит от конкретной реализации. В приложенном примере обработки также предлагаются функции: РеквизитИзПараметра, ТчДокументаИзПараметра.
Полезное использование приведенных алгоритмов можно рассмотреть на примере получения цены выбранного на форме товара, с заданным видом и датой цены, в любой конфигурации.
При запуске обработки сформируем параметры типов - имена прикладных объектов, измерений, реквизитов, совместимых с используемой конфигурацией.
На клиенте будет храниться переменная-структура ПараметрыТипов для быстрого доступа к именам, совместимым с используемой конфигурацией.
&НаКлиенте
Перем МакетСписокИменМетаданных,ПараметрыТипов;
&НаКлиенте
Процедура ПриОткрытии(Отказ)
МакетСписокИменМетаданных = ПолучитьМакетНаСервере("СписокИменМетаданных");
ПараметрыТипов = ЗагрузитьСписокТиповНаСервере(МакетСписокИменМетаданных);
Объект.ДатаПечати = ТекущаяДатаНаСервере();
КонецПроцедуры
&НаСервере
Функция ЗагрузитьСписокТиповНаСервере(МакетСписокИменМетаданных)
ПараметрыТипов = Новый Структура;
СписокСпрМД = СписокСправочниковМетаданных();
//Номенклатура
СтрокаТипЗнчНоменклатура = ""; СтрокаВидСпрНоменклатура = "";
ВидСпр = ВидСправочникаИзПараметра("СпрНоменклатура",СписокСпрМД,МакетСписокИменМетаданных);
Если Не ВидСпр = Неопределено Тогда
СтрокаТипЗнчНоменклатура = "СправочникСсылка."+ВидСпр;
СтрокаВидСпрНоменклатура = ВидСпр;
Элементы.НаименованиеПоиск.ОграничениеТипа = Новый ОписаниеТипов(СтрокаТипЗнчНоменклатура);
КонецЕсли;
ПараметрыТипов.Вставить("СтрокаТипЗнчНоменклатура",СтрокаТипЗнчНоменклатура);
ПараметрыТипов.Вставить("СтрокаВидСпрНоменклатура",СтрокаВидСпрНоменклатура);
//Вид цены
ВидСпр = ВидСправочникаИзПараметра("СпрВидыЦен",СписокСпрМД,МакетСписокИменМетаданных);
Если Не ВидСпр = Неопределено Тогда
Элементы.ВидЦены.ОграничениеТипа = Новый ОписаниеТипов("СправочникСсылка."+ВидСпр);
ПараметрыТипов.Вставить("ВидСпрВидыЦен",ВидСпр);
КонецЕсли;
//Характеристики
ВидСпр = ВидСправочникаИзПараметра("СпрХарактеристики",СписокСпрМД,МакетСписокИменМетаданных);
Если Не ВидСпр = Неопределено Тогда
ПараметрыТипов.Вставить("ВидСпрХарактеристики",ВидСпр);
ПараметрыТипов.Вставить("СтрокаТипЗнчХарактеристики","СправочникСсылка."+ВидСпр);
КонецЕсли;
//ЦЕНА
ИмяРсЦены = ИмяРегистраСведенийИзПараметра("РсЦеныНоменклатуры",МакетСписокИменМетаданных);
Если Не ИмяРсЦены=Неопределено Тогда
ИзмНоменклатураЦена=ИмяИзмеренияИзПараметра(ИмяРсЦены,"ИзмНоменклатура",МакетСписокИменМетаданных);
ИзмЦена = ИмяИзмеренияИзПараметра(ИмяРсЦены,"ИзмЦена",МакетСписокИменМетаданных);
ИзмТипЦен = ИмяИзмеренияИзПараметра(ИмяРсЦены,"ИзмВидЦены",МакетСписокИменМетаданных);
ИзмХарактеристикаЦена = ИмяИзмеренияИзПараметра(ИмяРсЦены,"ИзмХарактеристика",МакетСписокИменМетаданных);
ТекстЗапросаЦена = ТекстЗапросаПолучитьЦену(ИмяРсЦены,ИзмНоменклатураЦена,ИзмЦена,ИзмТипЦен);
ПараметрыТипов.Вставить("ИзмНоменклатураЦена",ИзмНоменклатураЦена);
ПараметрыТипов.Вставить("ИзмЦена",ИзмЦена);
ПараметрыТипов.Вставить("ИзмТипЦен",ИзмТипЦен);
ПараметрыТипов.Вставить("ИзмХарактеристикаЦена",ИзмХарактеристикаЦена);
ПараметрыТипов.Вставить("ТекстЗапросаЦена",ТекстЗапросаЦена);
КонецЕсли;
ПараметрыТипов.Вставить("ИмяРсЦены",ИмяРсЦены);
//Валюта Цены
Элементы.Валюта.Заголовок = ИзменениеВидаЦены(Объект.ВидЦены, Объект.Валюта);
Возврат ПараметрыТипов;
КонецФункции
&НаСервере
функция ТекстЗапросаПолучитьЦену(ИмяРсЦены,ИзмНоменклатураЦена,ИзмЦена,ИзмТипЦен) Экспорт
Если ИмяРсЦены=Неопределено ИЛИ ИзмНоменклатураЦена=Неопределено ИЛИ ИзмТипЦен=Неопределено Тогда Возврат ""; КонецЕсли;
Текст = "ВЫБРАТЬ
| ЦеныНоменклатурыСрезПоследних."+ИзмЦена+" КАК Цена
|ИЗ
| РегистрСведений."+ИмяРсЦены+".СрезПоследних(
| КОНЕЦПЕРИОДА(&Дата, ДЕНЬ),
| "+ИзмНоменклатураЦена+" = &"+ИзмНоменклатураЦена;
Если Не ИзмТипЦен=Неопределено Тогда
Текст =Текст+Символы.ПС+"И"+Символы.ПС+ИзмТипЦен+" = &"+ИзмТипЦен;
КонецЕсли;
Текст =Текст+Символы.ПС+"ИзмХарактеристикаЦена";
Текст =Текст+Символы.ПС+") КАК ЦеныНоменклатурыСрезПоследних";
Возврат Текст;
Конецфункции
&НаСервере
Функция ПолучитьМакетНаСервере(ИмяМакета)
Возврат РеквизитФормыВЗначение("Объект").ПолучитьМакет(ИмяМакета);
КонецФункции
&НаСервереБезКонтекста
Функция ТекущаяДатаНаСервере()
Возврат ТекущаяДата();
КонецФункции
Текст запроса для получения цены будет настраиваться по именам из структуры ПараметрыТипов.
В момент вызова функции ПолучитьЦену текст запроса будет состоять только из совместимых идентификаторов.
Реквизиты объекта обработки, доступные через поля формы для выбора товара, вида и даты цены: Объект.НаименованиеПоискУФ, Объект.ВидЦены, Объект.ДатаПечати. Валюта настраивается автоматически по виду цен.
&НаКлиенте
Процедура ПолучитьЦенуВыбранногоТовара()
Если ЗначениеЗаполнено(Объект.НаименованиеПоискУФ)
И ЗначениеЗаполнено(Объект.ВидЦены)
И ЗначениеЗаполнено(Объект.ДатаПечати) Тогда
ВремЦена = ПолучитьЦену(Объект.НаименованиеПоискУФ,Объект.ВидЦены,Объект.ДатаПечати,,ПараметрыТипов);
Элементы.ЗначениеЦены.Заголовок = Формат(ВремЦена,"ЧЦ=15; ЧРД=.; ЧГ=; ЧН=; ЧДЦ=2")+" "+Объект.Валюта;
КонецЕсли;
КонецПроцедуры
&НаСервереБезКонтекста
функция ПолучитьЦену(Товар,ЗначениеВидЦены,ДатаЦены,Характеристика="",ПараметрыТипов)
Если ЗначениеВидЦены = Неопределено Тогда Возврат 0; КонецЕсли;
ЦенаСрезПоследних=0; ЦенаСрезПервых=0;
Запрос = Новый Запрос;
Запрос.УстановитьПараметр("Дата", ДатаЦены);
Запрос.УстановитьПараметр(ПараметрыТипов.ИзмНоменклатураЦена, Товар);
Запрос.УстановитьПараметр(ПараметрыТипов.ИзмТипЦен, ЗначениеВидЦены);
СтрокаЗапросаХарактеристика = "";
Если Не ПараметрыТипов.ИзмХарактеристикаЦена=Неопределено И ЗначениеЗаполнено(Характеристика) Тогда
Запрос.УстановитьПараметр(ПараметрыТипов.ИзмХарактеристикаЦена, Характеристика);
СтрокаЗапросаХарактеристика = "И"+Символы.ПС+ПараметрыТипов.ИзмХарактеристикаЦена+" = &"+ПараметрыТипов.ИзмХарактеристикаЦена;
КонецЕсли;
Запрос.Текст = СтрЗаменить(ПараметрыТипов.ТекстЗапросаЦена,"ИзмХарактеристикаЦена",СтрокаЗапросаХарактеристика);
Попытка
Результат = Запрос.Выполнить();
Исключение
Возврат 0;
КонецПопытки;
ВыборкаДетальныеЗаписи = Результат.Выбрать();
Пока ВыборкаДетальныеЗаписи.Следующий() Цикл
ТекЦена=ВыборкаДетальныеЗаписи.Цена;
ЦенаСрезПоследних=?(ЦенаСрезПоследних=0,ТекЦена,?(ТекЦена>ЦенаСрезПоследних,ТекЦена,ЦенаСрезПоследних));
КонецЦикла;
Если ЦенаСрезПоследних>0 Тогда Возврат ЦенаСрезПоследних; КонецЕсли;
Запрос.Текст = СтрЗаменить(Запрос.Текст,"СрезПоследних","СрезПервых");
Попытка
Результат = Запрос.Выполнить();
Исключение
Возврат 0;
КонецПопытки;
ВыборкаДетальныеЗаписи = Результат.Выбрать();
Пока ВыборкаДетальныеЗаписи.Следующий() Цикл
ТекЦена=ВыборкаДетальныеЗаписи.Цена;
ЦенаСрезПервых=?(ЦенаСрезПервых=0,ТекЦена,?(ТекЦена>ЦенаСрезПервых,ТекЦена,ЦенаСрезПервых));
КонецЦикла;
Если ЦенаСрезПервых>0 Тогда Возврат ЦенаСрезПервых; КонецЕсли;
Если ЗначениеЗаполнено(Характеристика) Тогда Возврат ПолучитьЦену(Товар,ЗначениеВидЦены,ДатаЦены,,ПараметрыТипов); КонецЕсли;
Возврат 0;
Конецфункции
В приложенной к публикации обработке дополнительно демонстрируется простейший способ выбора вида документа и справочника интерактивно через нажатие клавиши, что полезно при создании универсальных обработок, поскольку список видов формируется по именам в метаданных используемой конфигурации.
Все примеры алгоритмов протестированы на многих различных типовых конфигурациях и приведены в обработке из публикации. Тестировалось в конфигурациях: "Управление торговлей" редакция 10.3 (10.3.46.3), "Управление торговлей" редакция 11 (11.4.7.150), "Управление производственным предприятием" редакция 1.3 (1.3.143.1), "Управление нашей фирмой" редакция 1.6 (1.6.22.48), "Розница" редакция 2.3 (2.3.2.33), "1С:Комплексная автоматизация" редакция 2.4 (2.4.7.151), "Бухгалтерия предприятия" редакция 3.0 (3.0.44.140), 1С:ERP Управление предприятием 2 (2.4.6.174).
В обработках 1С8 в обычном режиме реализуется та же идея, с меньшим количеством процедур, благодаря отсутствию клиент-серверной архитектуры.
Спасибо за Ваш интерес к публикации! Критика приветствуется.