Пост

Меньше копипаста!, или как Вася универсальную процедуру писал

Программист Вася разбирает подход создания универсальных методов на примере программного вывода СКД.


Введение

Есть множество кусков кода, которые мы повторяем вновь и вновь. Одно и тоже. Однотипные кусочки, которые уже выучили наизусть. Однако, юный программист Василий считает, что копипасты - это зло. И что можно выделить универсальный метод, который позволит выполнить тоже самое, но с меньшим количеством строк кода и усилий разработчика.

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 ИЗ &ТЗ КАК ТЗ";
    ТекстЗапроса = СтрШаблон(ТекстЗапроса, ИменаКолонок, ИмяТаблицы);
        
    Запрос = Новый Запрос(ТекстЗапроса);
    Запрос.МенеджерВременныхТаблиц = МенеджерВременныхТаблиц;
    Запрос.УстановитьПараметр("ТЗ", ДанныеТаблицы);
    Запрос.Выполнить();
        
КонецПроцедуры
Авторский пост защищен лицензией CC BY 4.0 .