Меньше копипаста!, или как Вася универсальную процедуру писал
Программист Вася разбирает подход создания универсальных методов на примере программного вывода СКД.
Введение
Есть множество кусков кода, которые мы повторяем вновь и вновь. Одно и тоже. Однотипные кусочки, которые уже выучили наизусть. Однако, юный программист Василий считает, что копипасты - это зло. И что можно выделить универсальный метод, который позволит выполнить тоже самое, но с меньшим количеством строк кода и усилий разработчика.
1. Постановка цели
У программиста Васи простая задача - вывести программно СКД. Что делает среднестатистический разработчик? Копипастит в код заготовленный шаблон программного вывода. Когда нужно сделать таких выводов несколько штук с разными условиями? Делает несколько кусков однотипного кода, где меняется буквально пара параметров.
Но Вася не такой. Он ещё юн и свеж и полон решимости улучшать всё вокруг.
“Я делаю это, потому что копипасты - Зло!”
И для начала он копипастит пишет этот самый стандартный кусок кода.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
//Формируем макет, с помощью компоновщика макета
КомпоновщикМакета = Новый КомпоновщикМакетаКомпоновкиДанных;
//Передаем в макет компоновки схему, настройки и данные расшифровки
МакетКомпоновки = КомпоновщикМакета.Выполнить(СхемаКомпоновкиДанных, Настройки, ДанныеРасшифровки);
//Выполним компоновку с помощью процессора компоновки
ПроцессорКомпоновкиДанных = Новый ПроцессорКомпоновкиДанных;
ПроцессорКомпоновкиДанных.Инициализировать(МакетКомпоновки, , ДанныеРасшифровки);
//Выводим результат в табличный документ
ПроцессорВывода = Новый ПроцессорВыводаРезультатаКомпоновкиДанныхВТабличныйДокумент;
ПроцессорВывода.УстановитьДокумент(Результат);
ПроцессорВывода.Вывести(ПроцессорКомпоновкиДанных);
“Ну всё” - думает Вася.
И действительно, если обернуть данный код в процедуру, то её использование уже поможет убрать из конфигурации несколько кучек стандартного вывода СКД.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
Процедура СкомпоноватьРезультатОтчета(Результат, СхемаКомпоновкиДанных, НастройкиКомпоновки, ДанныеРасшифровки = Неопределено) Экспорт
//Формируем макет, с помощью компоновщика макета
КомпоновщикМакета = Новый КомпоновщикМакетаКомпоновкиДанных;
//Передаем в макет компоновки схему, настройки и данные расшифровки
МакетКомпоновки = КомпоновщикМакета.Выполнить(СхемаКомпоновкиДанных, НастройкиКомпоновки, ДанныеРасшифровки);
//Выполним компоновку с помощью процессора компоновки
ПроцессорКомпоновкиДанных = Новый ПроцессорКомпоновкиДанных;
ПроцессорКомпоновкиДанных.Инициализировать(МакетКомпоновки, , ДанныеРасшифровки);
//Выводим результат в табличный документ
ПроцессорВывода = Новый ПроцессорВыводаРезультатаКомпоновкиДанныхВТабличныйДокумент;
ПроцессорВывода.УстановитьДокумент(Результат);
ПроцессорВывода.Вывести(ПроцессорКомпоновкиДанных);
КонецПроцедуры
Этот код достаточно распространён и ещё не раз потребуется в разработке.
Однако, Вася идёт дальше…
2. Варианты применения
“А что, если потребуется вывести результат в таблицу значений?”
Для этого используется немного другой кусок кода.
Вася нашёл его в гугле памяти и выделил для себя те участки, которые отличаются от предыдущего.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
//Формируем макет, с помощью компоновщика макета
КомпоновщикМакета = Новый КомпоновщикМакетаКомпоновкиДанных;
//Передаем в макет компоновки схему, настройки и данные расшифровки
МакетКомпоновки = КомпоновщикМакета.Выполнить(СхемаКомпоновкиДанных, Настройки, ДанныеРасшифровки,,
Тип("ГенераторМакетаКомпоновкиДанныхДляКоллекцииЗначений"));
//Выполним компоновку с помощью процессора компоновки
ПроцессорКомпоновкиДанных = Новый ПроцессорКомпоновкиДанных;
ПроцессорКомпоновкиДанных.Инициализировать(МакетКомпоновки, , ДанныеРасшифровки);
//Выводим результат в табличный документ
ПроцессорВывода = Новый ПроцессорВыводаРезультатаКомпоновкиДанныхВКоллекциюЗначений;
ПроцессорВывода.УстановитьОбъект(Результат);
ПроцессорВывода.Вывести(ПроцессорКомпоновкиДанных);
Отличия минимальные и юный разработчик берётся за доработку своего метода.
“Я делаю это, потому что нужен умный метод!”
Для этого Вася сначала решает, как метод будет определять необходимость вывода в табличный документ или в коллекцию.
Можно, конечно, сделать просто - добавить параметр в процедуру. Однако, Васин метод должен быть максимально “умным”!
Решено. Будем смотреть на тип параметра “Результат”.
1
2
3
4
5
6
7
8
//Определяем тип генератора
Если ТипЗнч(Результат) = Тип("ТаблицаЗначений") ИЛИ ТипЗнч(Результат) = Тип("ДеревоЗначений") Тогда
ВыводВКоллекциюЗначений = Истина;
ТипГенератораВывода = Тип("ГенераторМакетаКомпоновкиДанныхДляКоллекцииЗначений");
Иначе
ВыводВКоллекциюЗначений = Ложь;
ТипГенератораВывода = Тип("ГенераторМакетаКомпоновкиДанных");
КонецЕсли;
А далее, в зависимости от значения переменной ВыводВКоллекциюЗначений, используем тот или иной код. Вася оглядывает получившийся метод:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
Процедура СкомпоноватьРезультатОтчета(Результат, СхемаКомпоновкиДанных, НастройкиКомпоновки, ДанныеРасшифровки) Экспорт
//Определяем тип генератора
Если ТипЗнч(Результат) = Тип("ТаблицаЗначений") ИЛИ ТипЗнч(Результат) = Тип("ДеревоЗначений") Тогда
ВыводВКоллекциюЗначений = Истина;
ТипГенератораВывода = Тип("ГенераторМакетаКомпоновкиДанныхДляКоллекцииЗначений");
Иначе
ВыводВКоллекциюЗначений = Ложь;
ТипГенератораВывода = Тип("ГенераторМакетаКомпоновкиДанных");
КонецЕсли;
//Формируем макет, с помощью компоновщика макета
КомпоновщикМакета = Новый КомпоновщикМакетаКомпоновкиДанных;
//Передаем в макет компоновки схему, настройки и данные расшифровки
МакетКомпоновки = КомпоновщикМакета.Выполнить(СхемаКомпоновкиДанных, НастройкиКомпоновки, ДанныеРасшифровки,,ТипГенератораВывода);
//Выполним компоновку с помощью процессора компоновки
ПроцессорКомпоновкиДанных = Новый ПроцессорКомпоновкиДанных;
ПроцессорКомпоновкиДанных.Инициализировать(МакетКомпоновки, , ДанныеРасшифровки);
//Выводим результат
Если ВыводВКоллекциюЗначений Тогда
ПроцессорВывода = Новый ПроцессорВыводаРезультатаКомпоновкиДанныхВКоллекциюЗначений;
ПроцессорВывода.УстановитьОбъект(Результат);
Иначе
ПроцессорВывода = Новый ПроцессорВыводаРезультатаКомпоновкиДанныхВТабличныйДокумент;
ПроцессорВывода.УстановитьДокумент(Результат);
КонецЕсли;
ПроцессорВывода.Вывести(ПроцессорКомпоновкиДанных);
КонецПроцедуры
Ну вот, теперь наша процедура пригодится ещё в большей части случаев. Василий, откладывает подальше кисть мышку, но вдруг вспоминает, что в его задаче в СКД есть “набор данных объект”…
3. Распространенные параметры
Что мы делаем, когда нужно выполнить СКД с внешним набором данных? Формируем программно точно так же, но с передачей ещё одного параметра:
1
2
ПроцессорКомпоновки = Новый ПроцессорКомпоновкиДанных;
ПроцессорКомпоновки.Инициализировать(МакетКомпоновки, ВнешниеНаборыДанных, ДанныеРасшифровки);
Вот и Василий вспомнил об этом. И решил доработать свою процедуру.
“Я делаю это, потому что никто другой делать не захочет”
Он просто так не сдастся! Тем более, что тут всего лишь пара штрихов.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
Процедура СкомпоноватьРезультатОтчета(Результат, СхемаКомпоновкиДанных,
НастройкиКомпоновки, ДанныеРасшифровки = Неопределено, ВнешниеНаборыДанных = Неопределено) Экспорт
//Определяем тип генератора
Если ТипЗнч(Результат) = Тип("ТаблицаЗначений") ИЛИ ТипЗнч(Результат) = Тип("ДеревоЗначений") Тогда
ВыводВКоллекциюЗначений = Истина;
ТипГенератораВывода = Тип("ГенераторМакетаКомпоновкиДанныхДляКоллекцииЗначений");
Иначе
ВыводВКоллекциюЗначений = Ложь;
ТипГенератораВывода = Тип("ГенераторМакетаКомпоновкиДанных");
КонецЕсли;
//Формируем макет, с помощью компоновщика макета
КомпоновщикМакета = Новый КомпоновщикМакетаКомпоновкиДанных;
//Передаем в макет компоновки схему, настройки и данные расшифровки
МакетКомпоновки = КомпоновщикМакета.Выполнить(СхемаКомпоновкиДанных, НастройкиКомпоновки, ДанныеРасшифровки,,ТипГенератораВывода);
//Выполним компоновку с помощью процессора компоновки
ПроцессорКомпоновкиДанных = Новый ПроцессорКомпоновкиДанных;
ПроцессорКомпоновкиДанных.Инициализировать(МакетКомпоновки, ВнешниеНаборыДанных, ДанныеРасшифровки);
//Выводим результат
Если ВыводВКоллекциюЗначений Тогда
ПроцессорВывода = Новый ПроцессорВыводаРезультатаКомпоновкиДанныхВКоллекциюЗначений;
ПроцессорВывода.УстановитьОбъект(Результат);
Иначе
ПроцессорВывода = Новый ПроцессорВыводаРезультатаКомпоновкиДанныхВТабличныйДокумент;
ПроцессорВывода.УстановитьДокумент(Результат);
КонецЕсли;
ПроцессорВывода.Вывести(ПроцессорКомпоновкиДанных);
КонецПроцедуры
Красота! Всё, этот метод уже покроет большую часть необходимых программных выводов СКД.
Но почему-то Василий недоволен. Ему кажется, что что-то можно улучшить. Но что?
4. Дополнительные параметры
Какая-то неведанная сила толкнула юного разработчика открыть Синтаксис-Помощник…
Взгляните сами
- МакетОформления (необязательный) Тип: МакетОформленияКомпоновкиДанных. Макет оформления, в соответствии с которым необходимо оформлять макет компоновки данных. Если не указан, будет использоваться макет оформления по умолчанию. - ПроверятьДоступностьПолей (необязательный) Тип: Булево. Определяет, выполнять ли проверку прав на просмотр полей и проверку доступности поля во включенных функциональных возможностях. Значение по умолчанию: Истина. - ПараметрыФункциональныхОпций (необязательный) Тип: Структура. Содержит параметры функциональных опций, используемые при исполнении отчета. - ВозможностьИспользованияВнешнихФункций (необязательный) Тип: Булево. Указывает возможность использования функции общих модулей конфигурации в выражениях компоновки данных. Значение по умолчанию: Ложь. - ОграничиватьПолучениеПолейПоСсылкамПоПравуПросмотр (необязательный) Тип: Булево. Если значение параметра равно Истина, то при отображении результата запроса учитывается право пользователя "Просмотр" на таблицы и поля, участвующие в запросе. Если в отчете или динамическом списке присутствует значение поля недоступного по праву "Просмотр", то выводится пустая ячейка. Если значение параметра равно Ложь и параметр ПроверятьДоступностьПолей установлен в Истина, то значение выводится, даже если у пользователя нет права "Просмотр". Значение по умолчанию: Истина.Василий узнал много нового. И теперь он жаждет открыть это для других.
“Я это делаю, чтобы нести знания!”
Для начала он вставил все найденные параметры в методы.
1
2
3
4
5
6
7
//Передаем в макет компоновки схему, настройки и данные расшифровки
МакетКомпоновки = КомпоновщикМакета.Выполнить(СхемаКомпоновкиДанных, НастройкиКомпоновки,
ДанныеРасшифровки, МакетОформления, ТипГенератораВывода, ПроверятьДоступностьПолей, ПараметрыФункциональныхОпций);
//Выполним компоновку с помощью процессора компоновки
ПроцессорКомпоновкиДанных = Новый ПроцессорКомпоновкиДанных;
ПроцессорКомпоновкиДанных.Инициализировать(МакетКомпоновки, ВнешниеНаборыДанных, ДанныеРасшифровки, ВозможностьИспользованияВнешнихФункций);
На входе в процедуру он решил выделить для всех новых параметров одну структуру “ДополнительныеПараметры”.
1
2
Процедура СкомпоноватьРезультатОтчета(Результат, СхемаКомпоновкиДанных, НастройкиКомпоновки,
ДанныеРасшифровки = Неопределено, ВнешниеНаборыДанных = Неопределено, ДополнительныеПараметры = Неопределено) Экспорт
Василий вообще любит структуры. Он сделал отдельную “ПараметрыВывода”, где прописал все используемые дополнительные параметры с их стандартными значениями. А потом просто дозаполняет эту структуру.
1
2
3
4
5
6
7
8
9
10
//Инициализируем дополнительные параметры вывода
ПараметрыВывода = Новый Структура;
ПараметрыВывода.Вставить("МакетОформления" , Неопределено);
ПараметрыВывода.Вставить("ПроверятьДоступностьПолей" , Истина);
ПараметрыВывода.Вставить("ПараметрыФункциональныхОпций" , Новый Структура);
ПараметрыВывода.Вставить("ВозможностьИспользованияВнешнихФункций" , Ложь);
Если НЕ ДополнительныеПараметры = Неопределено Тогда
ЗаполнитьЗначенияСвойств(ПараметрыВывода, ДополнительныеПараметры);
КонецЕсли;
Вася доволен тем, что получилось:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
Процедура СкомпоноватьРезультатОтчета(Результат, СхемаКомпоновкиДанных, НастройкиКомпоновки,
ДанныеРасшифровки = Неопределено, ВнешниеНаборыДанных = Неопределено, ДополнительныеПараметры = Неопределено) Экспорт
//Инициализируем дополнительные параметры вывода
ПараметрыВывода = Новый Структура;
ПараметрыВывода.Вставить("МакетОформления" , Неопределено);
ПараметрыВывода.Вставить("ПроверятьДоступностьПолей" , Истина);
ПараметрыВывода.Вставить("ПараметрыФункциональныхОпций" , Новый Структура);
ПараметрыВывода.Вставить("ВозможностьИспользованияВнешнихФункций" , Ложь);
Если НЕ ДополнительныеПараметры = Неопределено Тогда
ЗаполнитьЗначенияСвойств(ПараметрыВывода, ДополнительныеПараметры);
КонецЕсли;
//Определяем тип генератора
Если ТипЗнч(Результат) = Тип("ТаблицаЗначений") ИЛИ ТипЗнч(Результат) = Тип("ДеревоЗначений") Тогда
ВыводВКоллекциюЗначений = Истина;
ТипГенератораВывода = Тип("ГенераторМакетаКомпоновкиДанныхДляКоллекцииЗначений");
Иначе
ВыводВКоллекциюЗначений = Ложь;
ТипГенератораВывода = Тип("ГенераторМакетаКомпоновкиДанных");
КонецЕсли;
//Формируем макет, с помощью компоновщика макета
КомпоновщикМакета = Новый КомпоновщикМакетаКомпоновкиДанных;
//Передаем в макет компоновки схему, настройки и данные расшифровки
МакетКомпоновки = КомпоновщикМакета.Выполнить(СхемаКомпоновкиДанных, НастройкиКомпоновки,
ДанныеРасшифровки, ПараметрыВывода.МакетОформления, ТипГенератораВывода,
ПараметрыВывода.ПроверятьДоступностьПолей, ПараметрыВывода.ПараметрыФункциональныхОпций);
//Выполним компоновку с помощью процессора компоновки
ПроцессорКомпоновкиДанных = Новый ПроцессорКомпоновкиДанных;
ПроцессорКомпоновкиДанных.Инициализировать(МакетКомпоновки, ВнешниеНаборыДанных,
ДанныеРасшифровки, ПараметрыВывода.ВозможностьИспользованияВнешнихФункций);
//Выводим результат
Если ВыводВКоллекциюЗначений Тогда
ПроцессорВывода = Новый ПроцессорВыводаРезультатаКомпоновкиДанныхВКоллекциюЗначений;
ПроцессорВывода.УстановитьОбъект(Результат);
Иначе
ПроцессорВывода = Новый ПроцессорВыводаРезультатаКомпоновкиДанныхВТабличныйДокумент;
ПроцессорВывода.УстановитьДокумент(Результат);
КонецЕсли;
ПроцессорВывода.Вывести(ПроцессорКомпоновкиДанных);
КонецПроцедуры
Метод, который полностью повторяет возможности платформенных, но при этом удобно обёрнут в универсальную процедуру.
“Ну теперь точно всё!”.
И решил добавить ещё хотелочку…
5. Прихоть в мелочах
Метод готов. Но Васе хочется, чтобы он был ещё чуть по-умнее.
“Я делаю это, потому что хочу!”
И юный разработчик добавляет в начало процедуры обработку параметра с настройками.
1
2
3
4
5
6
7
8
9
10
11
12
//Инициализируем настройки
Если ТипЗнч(НастройкиКомпоновки) = Тип("КомпоновщикНастроекКомпоновкиДанных") Тогда
ВыполняемыеНастройки = НастройкиКомпоновки.ПолучитьНастройки();
ИначеЕсли ТипЗнч(НастройкиКомпоновки) = Тип("Строка") И ЭтоАдресВременногоХранилища(НастройкиКомпоновки) Тогда
ВыполняемыеНастройки = ПолучитьИзВременногоХранилища(НастройкиКомпоновки);
Иначе
ВыполняемыеНастройки = НастройкиКомпоновки;
КонецЕсли;
Если НЕ ТипЗнч(ВыполняемыеНастройки) = Тип("НастройкиКомпоновкиДанных") Тогда
Возврат;
КонецЕсли;
Это позволит в метод передавать не только сами настройки, но и, к примеру, компоновщик настроек. Или же адрес хранилища, где настройки хранятся. Дополнительный штрих, который добавляет небольшую возможность.
Теперь, например, когда Васе потребуется вызывать методы из модуля отчёта, то он просто передаст КомпоновщикНастроек, а процедура сама уже возьмёт из него настройки. В результате код вызова этой процедуры немного, но уменьшится.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
Процедура СкомпоноватьРезультатОтчета(Результат, СхемаКомпоновкиДанных, НастройкиКомпоновки,
ДанныеРасшифровки = Неопределено, ВнешниеНаборыДанных = Неопределено, ДополнительныеПараметры = Неопределено) Экспорт
//Инициализируем настройки
Если ТипЗнч(НастройкиКомпоновки) = Тип("КомпоновщикНастроекКомпоновкиДанных") Тогда
ВыполняемыеНастройки = НастройкиКомпоновки.ПолучитьНастройки();
ИначеЕсли ТипЗнч(НастройкиКомпоновки) = Тип("Строка") И ЭтоАдресВременногоХранилища(НастройкиКомпоновки) Тогда
ВыполняемыеНастройки = ПолучитьИзВременногоХранилища(НастройкиКомпоновки);
Иначе
ВыполняемыеНастройки = НастройкиКомпоновки;
КонецЕсли;
Если НЕ ТипЗнч(ВыполняемыеНастройки) = Тип("НастройкиКомпоновкиДанных") Тогда
Возврат;
КонецЕсли;
//Инициализируем дополнительные параметры вывода
ПараметрыВывода = Новый Структура;
ПараметрыВывода.Вставить("МакетОформления" , Неопределено);
ПараметрыВывода.Вставить("ПроверятьДоступностьПолей" , Истина);
ПараметрыВывода.Вставить("ПараметрыФункциональныхОпций" , Новый Структура);
ПараметрыВывода.Вставить("ВозможностьИспользованияВнешнихФункций" , Ложь);
Если НЕ ДополнительныеПараметры = Неопределено Тогда
ЗаполнитьЗначенияСвойств(ПараметрыВывода, ДополнительныеПараметры);
КонецЕсли;
//Определяем тип генератора
Если ТипЗнч(Результат) = Тип("ТаблицаЗначений") ИЛИ ТипЗнч(Результат) = Тип("ДеревоЗначений") Тогда
ВыводВКоллекциюЗначений = Истина;
ТипГенератораВывода = Тип("ГенераторМакетаКомпоновкиДанныхДляКоллекцииЗначений");
Иначе
ВыводВКоллекциюЗначений = Ложь;
ТипГенератораВывода = Тип("ГенераторМакетаКомпоновкиДанных");
КонецЕсли;
//Формируем макет, с помощью компоновщика макета
КомпоновщикМакета = Новый КомпоновщикМакетаКомпоновкиДанных;
//Передаем в макет компоновки схему, настройки и данные расшифровки
МакетКомпоновки = КомпоновщикМакета.Выполнить(СхемаКомпоновкиДанных, ВыполняемыеНастройки,
ДанныеРасшифровки, ПараметрыВывода.МакетОформления, ТипГенератораВывода,
ПараметрыВывода.ПроверятьДоступностьПолей, ПараметрыВывода.ПараметрыФункциональныхОпций);
//Выполним компоновку с помощью процессора компоновки
ПроцессорКомпоновкиДанных = Новый ПроцессорКомпоновкиДанных;
ПроцессорКомпоновкиДанных.Инициализировать(МакетКомпоновки, ВнешниеНаборыДанных,
ДанныеРасшифровки, ПараметрыВывода.ВозможностьИспользованияВнешнихФункций);
//Выводим результат
Если ВыводВКоллекциюЗначений Тогда
ПроцессорВывода = Новый ПроцессорВыводаРезультатаКомпоновкиДанныхВКоллекциюЗначений;
ПроцессорВывода.УстановитьОбъект(Результат);
Иначе
ПроцессорВывода = Новый ПроцессорВыводаРезультатаКомпоновкиДанныхВТабличныйДокумент;
ПроцессорВывода.УстановитьДокумент(Результат);
КонецЕсли;
ПроцессорВывода.Вывести(ПроцессорКомпоновкиДанных);
КонецПроцедуры
И вот Васина универсальная “обёртка” для вывода СКД. С ней он может не вставлять шаблонный набор строк, а просто вызвать заготовленную процедуру из общего модуля. Осталось теперь добавить описание…
6. Документирование
Василий добавляет описание к своему методу. Старается соблюдать “шаблоны” и рекомендации. Чтобы контекстная подсказка отображалась красиво и подхватывала и типы и описания.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
// Выполняет компоновку СКД по переданным настройкам
//
// Параметры:
// Результат - ТабличныйДокумент,ТаблицаЗначений,ДеревоЗначений - результат выполнения
// СхемаКомпоновкиДанных - СхемаКомпоновкиДанных - выполняемая СКД
// НастройкиКомпоновки - НастройкиКомпоновкиДанных,КомпоновщикНастроекКомпоновкиДанных,Строка - выполняемые настройки или адрес временного хранилища или компоновщик с настройками
// ДанныеРасшифровки - ДанныеРасшифровкиКомпоновкиДанных, Неопределено - данные расшифровки
// ВнешниеНаборыДанных - Структура, Неопределено - коллекция внешних наборов данных
// ДополнительныеПараметры - Структура, Неопределено - коллекция внешних наборов данных
// * МакетОформления - МакетОформленияКомпоновкиДанных - Макет оформления, в соответствии с которым необходимо оформлять макет компоновки данных.
// * ПроверятьДоступностьПолей - Булево - Определяет, выполнять ли проверку прав на просмотр полей и проверку доступности поля во включенных функциональных возможностях.
// * ПараметрыФункциональныхОпций - Булево - Содержит параметры функциональных опций, используемые при исполнении отчета.
// * ВозможностьИспользованияВнешнихФункций - Булево - Указывает возможность использования функции общих модулей конфигурации в выражениях компоновки данных.
//
Приходит время опробовать метод на практике.
7. Примеры использования
“Нужно сделать банальный програмный вывод СКД? Нате!”
1
2
//Стандартный программный вывод
СкомпоноватьРезультатОтчета(ДокументРезультат, СхемаКомпоновкиДанных, КомпоновщикНастроек, ДанныеРасшифровки);
“Или же вывести результат в таблицу значений?”
1
2
3
//Вывод в коллекцию значений
ДанныеРезультата = Новый ТаблицаЗначений;
СкомпоноватьРезультатОтчета(ДанныеРезультата, СхемаКомпоновкиДанных, КомпоновщикНастроек);
“А может наоборот вывести СКД, используя содержимое таблицы значений?”
1
2
3
//Программный вывод с внешними наборами данных
ВнешниеНаборыДанных = Новый Структура("ТаблицаДанных", ТаблицаДанных);
СкомпоноватьРезультатОтчета(ДокументРезультат, СхемаКомпоновкиДанных, КомпоновщикНастроек, ДанныеРасшифровки, ВнешниеНаборыДанных);
“Можно сделать стандартный вывод, но с возможностью использования “внешних функций”“
1
2
3
//Вывод с возможностью использования внешних функций
ПараметрыВывода = Новый Структура("ВозможностьИспользованияВнешнихФункций", Истина);
СкомпоноватьРезультатОтчета(ДокументРезультат, СхемаКомпоновкиДанных, КомпоновщикНастроек, ДанныеРасшифровки,, ПараметрыВывода);
Или, например, со своим макетом оформления
1
2
3
//Вывод с использованием своего макета оформления
ПараметрыВывода = Новый Структура("МакетОформления", ПолучитьМакет("МакетОформления"));
СкомпоноватьРезультатОтчета(ДокументРезультат, СхемаКомпоновкиДанных, КомпоновщикНастроек, ДанныеРасшифровки,, ПараметрыВывода);
Васе нравится метод. Возможно, он не идеален. Возможно, эту процедуру никто и не заметит в закоулках модулей этой покрытой костылями и багами конфигурации. Но Вася всё равно доволен своей работой. Ведь на самом деле:
“Я делаю это, потому что мне просто нравится”
Вася похвалил себя, почесал ЧСВ и пошёл дальше заниматься своей работой. Ещё столько всего нужно скопипастить…
8. Поддержка (UPDATE 29.06.2021)
Прошло время. Вася с удовольствием пользовался своей универсальной процедуркой. Использовал её где только можно - и в отчетах, и в обработках, и в расширениях.
А тем временем платформа развивалась и наделила программный вывод СКД новой возможностью. Передавать при формировании менеджер временных таблиц.
Ранее в запросах СКД нельзя было использовать заранее подготовленные временные таблицы. И приходилось использовать костыли и связи наборов в СКД.
О, ужас! Хорошо, что это время прошло!
Но вот незадача. Текущая Васина процедура не позволяет использовать менеджер временных.
Пришло время доработки!
И так, для начала начала программист оглядел синтаксис-помощник. Что именно появилось нового?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
ПроцессорКомпоновкиДанных.Инициализировать(<Макет>, <ВнешниеНаборыДанных>, <ДанныеРасшифровки>, <ВозможностьИспользованияВнешнихФункций>, <ОграничиватьПолучениеПолейПоСсылкамПоПравуПросмотр>, <МенеджерВременныхТаблиц>)
<ОграничиватьПолучениеПолейПоСсылкамПоПравуПросмотр> (необязательный)
Тип: Булево.
Если значение параметра равно Истина, то при отображении результата запроса учитывается право пользователя "Просмотр" на таблицы и поля, участвующие в запросе. Если в отчете или динамическом списке присутствует значение поля недоступного по праву "Просмотр", то выводится пустая ячейка.
Если значение параметра равно Ложь и параметр ПроверятьДоступностьПолей установлен в Истина, то значение выводится, даже если у пользователя нет права "Просмотр".
Значение по умолчанию: Истина.
<МенеджерВременныхТаблиц> (необязательный)
Тип: МенеджерВременныхТаблиц.
Менеджер временных таблиц, который будет установлен для исполняемых запросов.
В процессе работы процессора компоновки данных таблицы из этого менеджера могут быть получены, но не могут быть удалены или добавлены новые.
Значение по умолчанию: Неопределено.
1С всё расширяет количество параметров метода. А вот у Васи есть для этого структура ДополнительныеПараметры =)
1
2
3
4
5
6
7
8
9
10
11
12
//Инициализируем дополнительные параметры вывода
ПараметрыВывода = Новый Структура;
ПараметрыВывода.Вставить("МакетОформления" , Неопределено);
ПараметрыВывода.Вставить("ПроверятьДоступностьПолей" , Истина);
ПараметрыВывода.Вставить("ПараметрыФункциональныхОпций" , Новый Структура);
ПараметрыВывода.Вставить("ВозможностьИспользованияВнешнихФункций" , Ложь);
ПараметрыВывода.Вставить("МенеджерВременныхТаблиц" , Неопределено);
ПараметрыВывода.Вставить("ОграничиватьПолучениеПолейПоСсылкамПоПравуПросмотр" , Истина);
Если НЕ ДополнительныеПараметры = Неопределено Тогда
ЗаполнитьЗначенияСвойств(ПараметрыВывода, ДополнительныеПараметры);
КонецЕсли;
Вот и всё, теперь в ДополнительныеПараметры можно передать новые параметры компоновки.
Осталось только передать их в сам метод платформы:
1
2
3
4
5
6
7
//Выполним компоновку с помощью процессора компоновки
ПроцессорКомпоновкиДанных = Новый ПроцессорКомпоновкиДанных;
ПроцессорКомпоновкиДанных.Инициализировать(
МакетКомпоновки, ВнешниеНаборыДанных, ДанныеРасшифровки,
ПараметрыВывода.ВозможностьИспользованияВнешнихФункций,
ПараметрыВывода.ОграничиватьПолучениеПолейПоСсылкамПоПравуПросмотр,
ПараметрыВывода.МенеджерВременныхТаблиц);
Этого уже достаточно, чтобы Васин универсальный метод соответствовал новым возможностям платформы.
Но юный программист чувствует, что этого недостаточно…
Так ли удобно передавать в СКД менеджер временных таблиц? А вот и не всегда.
Иногда просто хочется передать таблицу значений, которую сформировали программно \ прочитали из файла \ получили из хранилища \ <ваш вариант="">ваш>
Для этого разработчику придется: создавать запрос с менеджером временных таблиц, описывать текст запроса с выборкой всех полей таблицы, передавать эту таблицу и выполнять запрос.
Это нужно автоматизировать!
И Вася взялся за дело:
1
2
3
4
5
6
7
Если ТипЗнч(ПараметрыВывода.МенеджерВременныхТаблиц) = Тип("Структура") Тогда
МенеджерВременныхТаблиц = Новый МенеджерВременныхТаблиц;
Для Каждого КлючИЗначение Из ПараметрыВывода.МенеджерВременныхТаблиц Цикл
//Здесь мы будем добавлять таблицу в МВТ
КонецЦикла;
ПараметрыВывода.МенеджерВременныхТаблиц = МенеджерВременныхТаблиц;
КонецЕсли;
Теперь, в доп. параметре МенеджерВременныхТаблиц можно передать и структуру. А на основе её мы сделаем МВТ.
Ключем этой структуры будет Имя временной таблицы, которую нужно создать в менеджере. А значением будет сама таблица.
И тут наш 1Сник решил сделать отдельный метод, в котором он будет добавлять таблицу в менеджер.
В этой процедуре запрос генерируется сам. И через него в МВТ попадают нужные данные.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
Процедура ДобавитьТаблицуВМенеджерВременныхТаблиц(МенеджерВременныхТаблиц, ИмяТаблицы, ДанныеТаблицы) Экспорт
ИменаКолонок = Новый Массив;
Для Каждого Колонка Из ДанныеТаблицы.Колонки Цикл
ИменаКолонок.Добавить(Колонка.Имя);
КонецЦикла;
ИменаКолонок = СтрСоединить(ИменаКолонок, ",");
ТекстЗапроса = "ВЫБРАТЬ %1 ПОМЕСТИТЬ %2 ИЗ &ТЗ КАК ТЗ";
ТекстЗапроса = СтрШаблон(ТекстЗапроса, ИменаКолонок, ИмяТаблицы);
Запрос = Новый Запрос(ТекстЗапроса);
Запрос.МенеджерВременныхТаблиц = МенеджерВременныхТаблиц;
Запрос.УстановитьПараметр("ТЗ", ДанныеТаблицы);
Запрос.Выполнить();
КонецПроцедуры
Теперь нужно этот новый метод вызвать:
1
2
3
4
5
6
7
Если ТипЗнч(ПараметрыВывода.МенеджерВременныхТаблиц) = Тип("Структура") Тогда
МенеджерВременныхТаблиц = Новый МенеджерВременныхТаблиц;
Для Каждого КлючИЗначение Из ПараметрыВывода.МенеджерВременныхТаблиц Цикл
ДобавитьТаблицуВМенеджерВременныхТаблиц(МенеджерВременныхТаблиц, КлючИЗначение.Ключ, КлючИЗначение.Значение);
КонецЦикла;
ПараметрыВывода.МенеджерВременныхТаблиц = МенеджерВременныхТаблиц;
КонецЕсли;
Можно код из метода “встроить” внутрь основного. Тогда будет так:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
Если ТипЗнч(ПараметрыВывода.МенеджерВременныхТаблиц) = Тип("Структура") Тогда
МенеджерВременныхТаблиц = Новый МенеджерВременныхТаблиц;
Для Каждого КлючИЗначение Из ПараметрыВывода.МенеджерВременныхТаблиц Цикл
ИмяТаблицы = КлючИЗначение.Ключ;
ДанныеТаблицы = КлючИЗначение.Значение;
ИменаКолонок = Новый Массив;
Для Каждого Колонка Из ДанныеТаблицы.Колонки Цикл
ИменаКолонок.Добавить(Колонка.Имя);
КонецЦикла;
ИменаКолонок = СтрСоединить(ИменаКолонок, ",");
ТекстЗапроса = "ВЫБРАТЬ %1 ПОМЕСТИТЬ %2 ИЗ &ТЗ КАК ТЗ";
ТекстЗапроса = СтрШаблон(ТекстЗапроса, ИменаКолонок, ИмяТаблицы);
Запрос = Новый Запрос(ТекстЗапроса);
Запрос.МенеджерВременныхТаблиц = МенеджерВременныхТаблиц;
Запрос.УстановитьПараметр("ТЗ", ДанныеТаблицы);
Запрос.Выполнить();
КонецЦикла;
ПараметрыВывода.МенеджерВременныхТаблиц = МенеджерВременныхТаблиц;
КонецЕсли;
Дело сделано! Осталось подправить описание.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
// Выполняет компоновку СКД по переданным настройкам
//
// Параметры:
// Результат - ТабличныйДокумент,ТаблицаЗначений,ДеревоЗначений - результат выполнения
// СхемаКомпоновкиДанных - СхемаКомпоновкиДанных - выполняемая СКД
// НастройкиКомпоновки - НастройкиКомпоновкиДанных,КомпоновщикНастроекКомпоновкиДанных,Строка - выполняемые настройки или адрес временного хранилища или компоновщик с настройками
// ДанныеРасшифровки - ДанныеРасшифровкиКомпоновкиДанных, Неопределено - данные расшифровки
// ВнешниеНаборыДанных - Структура, Неопределено - коллекция внешних наборов данных
// ДополнительныеПараметры - Структура, Неопределено - коллекция внешних наборов данных
// * МакетОформления - МакетОформленияКомпоновкиДанных - Макет оформления, в соответствии с которым необходимо оформлять макет компоновки данных.
// * МенеджерВременныхТаблиц - МенеджерВременныхТаблиц, Структура - Временные таблицы, которые будут доступны в запросе СКД. Или МВТ или структура, где Ключ - Имя, а Значение - Таблица
// * ПроверятьДоступностьПолей - Булево - Определяет, выполнять ли проверку прав на просмотр полей и проверку доступности поля во включенных функциональных возможностях.
// * ПараметрыФункциональныхОпций - Булево - Содержит параметры функциональных опций, используемые при исполнении отчета.
// * ВозможностьИспользованияВнешнихФункций - Булево - Указывает возможность использования функции общих модулей конфигурации в выражениях компоновки данных.
// * ОграничиватьПолучениеПолейПоСсылкамПоПравуПросмотр - Булево - Указывает обязательность наличия роли Просмотр для используемых метаданных в запросе СКД
И, конечно же, закрепить примерами использования:
1
2
3
//Вывод с передачей менеджера временных таблиц
ПараметрыВывода = Новый Структура("МенеджерВременныхТаблиц", Запрос.МенеджерВременныхТаблиц);
СкомпоноватьРезультатОтчета(ДокументРезультат, СхемаКомпоновкиДанных, КомпоновщикНастроек, ДанныеРасшифровки,, ПараметрыВывода);
1
2
3
4
//Вывод с передачей таблиц, которые попадут в МВТ
ПараметрыВывода = Новый Структура("МенеджерВременныхТаблиц", Новый Структура);
ПараметрыВывода.МенеджерВременныхТаблиц.Вставить("МояВТ", МояТЗ);
СкомпоноватьРезультатОтчета(ДокументРезультат, СхемаКомпоновкиДанных, КомпоновщикНастроек, ДанныеРасшифровки,, ПараметрыВывода);
Теперь Вася прошел весь цикл. Идея, релиализация и поддержка универсальной процедуры. И доволен окончательно. Или же нет?
Надо сделать общий модуль с гибкими методами по работе с СКД!
Финальный вариант метода
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
// Выполняет компоновку СКД по переданным настройкам
//
// Параметры:
// Результат - ТабличныйДокумент,ТаблицаЗначений,ДеревоЗначений - результат выполнения
// СхемаКомпоновкиДанных - СхемаКомпоновкиДанных - выполняемая СКД
// НастройкиКомпоновки - НастройкиКомпоновкиДанных,КомпоновщикНастроекКомпоновкиДанных,Строка - выполняемые настройки или адрес временного хранилища или компоновщик с настройками
// ДанныеРасшифровки - ДанныеРасшифровкиКомпоновкиДанных, Неопределено - данные расшифровки
// ВнешниеНаборыДанных - Структура, Неопределено - коллекция внешних наборов данных
// ДополнительныеПараметры - Структура, Неопределено - коллекция внешних наборов данных
// * МакетОформления - МакетОформленияКомпоновкиДанных - Макет оформления, в соответствии с которым необходимо оформлять макет компоновки данных.
// * МенеджерВременныхТаблиц - МенеджерВременныхТаблиц,Структура - Временные таблицы, которые будут доступны в запросе СКД. Или МВТ или структура, где Ключ - Имя, а Значение - Таблица
// * ПроверятьДоступностьПолей - Булево - Определяет, выполнять ли проверку прав на просмотр полей и проверку доступности поля во включенных функциональных возможностях.
// * ПараметрыФункциональныхОпций - Булево - Содержит параметры функциональных опций, используемые при исполнении отчета.
// * ВозможностьИспользованияВнешнихФункций - Булево - Указывает возможность использования функции общих модулей конфигурации в выражениях компоновки данных.
// * ОграничиватьПолучениеПолейПоСсылкамПоПравуПросмотр - Булево - Указывает обязательность наличия роли Просмотр для используемых метаданных в запросе СКД
//
//Подробнее об использовании метода: //infostart.ru/public/1082944/?ref=1159
//
Процедура СкомпоноватьРезультатОтчета(Результат, СхемаКомпоновки, НастройкиКомпоновки,
ДанныеРасшифровки = Неопределено, ВнешниеНаборыДанных = Неопределено, ДополнительныеПараметры = Неопределено) Экспорт
//Инициализируем настройки
Если ТипЗнч(НастройкиКомпоновки) = Тип("КомпоновщикНастроекКомпоновкиДанных") Тогда
ВыполняемыеНастройки = НастройкиКомпоновки.ПолучитьНастройки();
ИначеЕсли ТипЗнч(НастройкиКомпоновки) = Тип("Строка") И ЭтоАдресВременногоХранилища(НастройкиКомпоновки) Тогда
ВыполняемыеНастройки = ПолучитьИзВременногоХранилища(НастройкиКомпоновки);
Иначе
ВыполняемыеНастройки = НастройкиКомпоновки;
КонецЕсли;
Если НЕ ТипЗнч(ВыполняемыеНастройки) = Тип("НастройкиКомпоновкиДанных") Тогда
Возврат;
КонецЕсли;
//Инициализируем дополнительные параметры вывода
ПараметрыВывода = Новый Структура;
ПараметрыВывода.Вставить("МакетОформления" , Неопределено);
ПараметрыВывода.Вставить("ПроверятьДоступностьПолей" , Истина);
ПараметрыВывода.Вставить("ПараметрыФункциональныхОпций" , Новый Структура);
ПараметрыВывода.Вставить("ВозможностьИспользованияВнешнихФункций" , Ложь);
ПараметрыВывода.Вставить("МенеджерВременныхТаблиц" , Неопределено);
ПараметрыВывода.Вставить("ОграничиватьПолучениеПолейПоСсылкамПоПравуПросмотр" , Истина);
Если НЕ ДополнительныеПараметры = Неопределено Тогда
ЗаполнитьЗначенияСвойств(ПараметрыВывода, ДополнительныеПараметры);
КонецЕсли;
Если ТипЗнч(ПараметрыВывода.МенеджерВременныхТаблиц) = Тип("Структура") Тогда
МенеджерВременныхТаблиц = Новый МенеджерВременныхТаблиц;
Для Каждого КлючИЗначение Из ПараметрыВывода.МенеджерВременныхТаблиц Цикл
ДобавитьТаблицуВМенеджерВременныхТаблиц(МенеджерВременныхТаблиц, КлючИЗначение.Ключ, КлючИЗначение.Значение);
КонецЦикла;
ПараметрыВывода.МенеджерВременныхТаблиц = МенеджерВременныхТаблиц;
КонецЕсли;
//Определяем тип генератора
Если ТипЗнч(Результат) = Тип("ТаблицаЗначений") ИЛИ ТипЗнч(Результат) = Тип("ДеревоЗначений") Тогда
ВыводВКоллекциюЗначений = Истина;
ТипГенератораВывода = Тип("ГенераторМакетаКомпоновкиДанныхДляКоллекцииЗначений");
Иначе
ВыводВКоллекциюЗначений = Ложь;
ТипГенератораВывода = Тип("ГенераторМакетаКомпоновкиДанных");
КонецЕсли;
//Формируем макет, с помощью компоновщика макета
КомпоновщикМакета = Новый КомпоновщикМакетаКомпоновкиДанных;
//Передаем в макет компоновки схему, настройки и данные расшифровки
МакетКомпоновки = КомпоновщикМакета.Выполнить(СхемаКомпоновки, ВыполняемыеНастройки,
ДанныеРасшифровки, ПараметрыВывода.МакетОформления, ТипГенератораВывода,
ПараметрыВывода.ПроверятьДоступностьПолей, ПараметрыВывода.ПараметрыФункциональныхОпций);
//Выполним компоновку с помощью процессора компоновки
ПроцессорКомпоновкиДанных = Новый ПроцессорКомпоновкиДанных;
ПроцессорКомпоновкиДанных.Инициализировать(
МакетКомпоновки, ВнешниеНаборыДанных, ДанныеРасшифровки,
ПараметрыВывода.ВозможностьИспользованияВнешнихФункций,
ПараметрыВывода.ОграничиватьПолучениеПолейПоСсылкамПоПравуПросмотр,
ПараметрыВывода.МенеджерВременныхТаблиц);
//Выводим результат
Если ВыводВКоллекциюЗначений Тогда
ПроцессорВывода = Новый ПроцессорВыводаРезультатаКомпоновкиДанныхВКоллекциюЗначений;
ПроцессорВывода.УстановитьОбъект(Результат);
Иначе
ПроцессорВывода = Новый ПроцессорВыводаРезультатаКомпоновкиДанныхВТабличныйДокумент;
ПроцессорВывода.УстановитьДокумент(Результат);
КонецЕсли;
ПроцессорВывода.Вывести(ПроцессорКомпоновкиДанных);
КонецПроцедуры
// Процедура добавляет таблицу в менеджер временных таблиц
//
// Параметры:
// МенеджерВременныхТаблиц - МенеджерВременныхТаблиц - Менеджер, в который нужно добавить таблицу
// ИмяТаблицы - Строка - Имя временной таблицы
// ДанныеТаблицы - ТаблицаЗначений - Данные таблицы
//
Процедура ДобавитьТаблицуВМенеджерВременныхТаблиц(МенеджерВременныхТаблиц, ИмяТаблицы, ДанныеТаблицы) Экспорт
ИменаКолонок = Новый Массив;
Для Каждого Колонка Из ДанныеТаблицы.Колонки Цикл
ИменаКолонок.Добавить(Колонка.Имя);
КонецЦикла;
ИменаКолонок = СтрСоединить(ИменаКолонок, ",");
ТекстЗапроса = "ВЫБРАТЬ %1 ПОМЕСТИТЬ %2 ИЗ &ТЗ КАК ТЗ";
ТекстЗапроса = СтрШаблон(ТекстЗапроса, ИменаКолонок, ИмяТаблицы);
Запрос = Новый Запрос(ТекстЗапроса);
Запрос.МенеджерВременныхТаблиц = МенеджерВременныхТаблиц;
Запрос.УстановитьПараметр("ТЗ", ДанныеТаблицы);
Запрос.Выполнить();
КонецПроцедуры