Исполняемые представления - фреймворк для создания кастомных виртуальных таблиц

04.08.25

Разработка - Инструментарий разработчика

Расширяем возможности языка запросов: создаем собственные источники данных, которые работают как встроенные виртуальные таблицы. Теперь сложные алгоритмы, внешние API или многоэтапные расчеты доступны в запросе так же просто, как обычные таблицы. Полноценный фреймворк с поддержкой конструктора запросов.

Бесплатные

ВНИМАНИЕ: Файлы из Базы знаний - это исходный код разработки. Это примеры решения задач, шаблоны, заготовки, "строительные материалы" для учетной системы. Файлы ориентированы на специалистов 1С, которые могут разобраться в коде и оптимизировать программу для запуска в базе данных. Гарантии работоспособности нет. Возврата нет. Технической поддержки нет.

Наименование Скачано Бесплатно
Исполняемые представления - фреймворк для создания кастомных виртуальных таблиц
.cfe 588,48Kb
17 Скачать бесплатно

Если вы хотите узнать подробнее про архитектуру решения, используемые паттерны проектирования, кейсы реального использования, голосуйте за мой доклад на Infostart tech event 2025. Исходный код доступен в репозитории.
 

1. Мотивация: От проблемы к фреймворку

Разработчики 1С часто не могут получить запросом данные, которые рассчитываются программно, а не хранятся в базе — классический пример, это данные из подсистемы ЗУП. Стандартный метод — вызов функции, выгрузка результата во временную таблицу и затем запрос к ней — слишком громоздкий для интерактивного анализа.

Как возникла идея

Идея родилась из вопроса: «Почему нельзя обращаться к методам программного интерфейса (ПИ) ЗУП прямо из текста запроса, как к обычным виртуальным таблицам?».

Изначально хотелось создать консоль, упрощающую отладку методов получения данных ЗУП. Но по итогу получилась не только консоль запросов (подробнее про нее в оригинальной статье), но и полноценный фреймворк для создания кастомных виртуальных таблиц.

2. Что такое исполняемые представления

Исполняемое представление — это кастомный источник данных для запроса, который работает по принципу "выполнить и подставить". Вместо простого преобразования в SQL-подзапрос, как это делают платформенные виртуальные таблицы, обращение к исполняемому представлению запускает заранее определенный алгоритм на языке 1С. Этот алгоритм формирует и возвращает временную таблицу, которая бесшовно интегрируется в дальнейшее выполнение запроса.

Для специалистов, знакомых с T-SQL, ближайшим аналогом является Multi-Statement Table-Valued Function (MSTVF) в MS SQL Server. Оба механизма позволяют инкапсулировать сложную императивную логику внутри объекта, который затем можно использовать в предложении FROM как обычную таблицу, эффективно скрывая всю сложность его внутреннего уcтройства.

3. Обзор архитектуры

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

  • Лексер и Парсер: Сначала текст запроса преобразуется в поток токенов, из которого рекурсивный нисходящий парсер (на базе LL(2)-грамматики) строит Абстрактное синтаксическое дерево (AST).
  • Семантический анализатор: Обходит дерево AST, проверяя его на смысловую корректность: разрешает имена полей, выводит типы данных и валидирует использование конструкций языка. Использует паттерн "Посетитель".
  • Поставщик Представлений (Реестр): Инфраструктурный компонент, отвечающий за обнаружение сервисов (Service Discovery). Он сканирует метаданные конфигурации (специализированные подсистемы) для автоматического поиска и каталогизации всех доступных в системе обработчиков представлений. Именно этот модуль предоставляет остальным компонентам метаописания представлений по их имени, обеспечивая слабую связанность и легкую расширяемость фреймворка.
  • Обработчик Исполняемых Представлений: Специализированный "плагин" для семантического анализатора. Перехватывает узлы AST, соответствующие обращениям к ИсполняемоеПредставление.*, запрашивает их метаописание у Поставщика, разбирает параметры и преобразует узел в специальный объект, готовый к выполнению.
  • Исполнитель: Финальный модуль и "мозг" системы, который, используя паттерн "Стратегия", определяет конечный результат (исполнение запроса или генерация кода). Ключевая особенность Исполнителя — встроенный оптимизатор. Он анализирует запрос к представлению и, если это возможно, делегирует (пробрасывает) операции напрямую обработчику представления:
    • Фильтрацию (Predicate Pushdown): условия из ГДЕ передаются внутрь представления.
    • Выбор полей (Projection Pushdown): запрашиваются только нужные колонки.

Это позволяет в простых случаях избежать создания временных таблиц и выполнить запрос за один проход. Если же оптимизация невозможна (например, из-за JOIN или GROUP BY), Исполнитель прозрачно для пользователя материализует представление во временную таблицу и подменяет им узел в AST. Он также полностью управляет жизненным циклом всех создаваемых временных таблиц.

4. Как добавлять свои исполняемые представления

Фреймворк спроектирован как полностью расширяемая система. Добавление нового представления сводится к созданию специальной обработки-обработчика и ее регистрации в системе через механизм подсистем. Это позволяет разработчикам создавать собственные библиотеки представлений, инкапсулируя в них сложную логику получения данных.

Основное правило: Каждое исполняемое представление — это обработка, которая обязательно должна быть включена в одну из дочерних подсистем основной подсистемы ИсполняемыеПредставления. Вы можете создавать свои подсистемы для логической группировки (например, ИсполняемыеПредставления -> ЗарплатаКадры или -> Бюджетирование).

Фреймворк поддерживает два основных сценария создания представлений.

Способ 1: Создание уникального (Standalone) представления

Этот способ используется для создания представлений, которые возвращают специфичный, уникальный набор данных и имеют фиксированное имя.

Пример: ИсполняемоеПредставление.КадровыеДанныеСотрудников.

Алгоритм создания:

  1. Создайте новую обработку (например, ПредставлениеКадровыеДанныеСотрудников).
  2. В модуле менеджера обработки реализуйте "контракт" — три обязательные экспортные функции:
    • ИмяПредставления(): Возвращает имя представления
    • Описание(): Возвращает структуру с метаописанием полей, параметров и возможностей вашего представления.
    • Исполнить(): Содержит основную логику получения данных и возвращает РезультатЗапроса.
    • ИсполняемыйКод(): Возвращает текстовое представление логики из Исполнить() на языке 1С.
  3. Зарегистрируйте обработку: Включите созданную обработку в состав любой подсистемы внутри ветки ИсполняемыеПредставления.

После этого фреймворк автоматически обнаружит новое представление, и вы сможете обращаться к нему в запросах по имени, которое было указано в его метаописании

Способ 2: Создание шаблонного представления для типа метаданных

Это более мощный механизм, позволяющий создать одно представление, которое будет автоматически "размножено" для всех объектов метаданных определенного типа (например, для всех регистров сведений).

Пример: Создать представление РегистрНакопления.НарастающийИтог, которое будет доступно для любого регистра накопления: РегистрНакопления.ТоварыНаСкладах.НарастающийИтог, РегистрНакопления.РасчетыСПокупателями.НарастающийИтог и т.д.

Алгоритм создания:

  1. Создайте новую обработку (например, ПредставлениеОстаткиОстаткиНарастающий.
  2. Реализуйте "контракт", но с одним отличием: функция Описание() должна принимать параметр:
    • Описание(ИмяОбъектаМетаданных): Внутри этой функции вы получаете имя конкретного регистра (например, "ТоварыНаСкладах") и можете динамически построить метаописание, обращаясь к его метаданным (Метаданные.РегистрыНакопления[ИмяОбъектаМетаданных]) для получения списка измерений и ресурсов.
    • Если для конкретного регистра это исполняемое представление не поддерживается, нужно вернуть Неопределено.
    • Так же ИмяПредставления() должно возвращать строку в формате [ТипРегистра].[ИмяПредставления],например РегистрНакопления.НарастающийИтог
  3. Зарегистрируйте обработку в специальной подсистеме: Поместите обработку в одну из предопределенных подсистем, имя которой соответствует типу метаданных:
    • ИсполняемыеПредставления -> РегистрыСведений
    • ИсполняемыеПредставления -> РегистрыНакопления
    • ИсполняемыеПредставления -> РегистрыРасчета
    • ИсполняемыеПредставления -> РегистрыБухгалтерии

После этого фреймворк применит вашу обработку-шаблон ко всем регистрам соответствующего типа, позволяя обращаться к ним в запросах по унифицированному имени.

4. Практический пример: создаем представление "Нарастающие итоги"

Рассмотрим создание универсального представления, которое будет работать для любого регистра накопления. Это представление позволит получать обороты и нарастающие итоги одним запросом, без написания сложного кода с СКД.

Шаг 1: Создание обработки и регистрация в подсистеме

Создаем новую обработку с именем ИсполняемоеПредставлениеНарастающиеИтоги

Включаем обработку в подсистему: ИсполняемыеПредставления/РегистрыНакопления
Важно: Размещение в подсистеме РегистрыНакопления говорит фреймворку, что это шаблонное представление для всех регистров накопления.

Шаг 2: Реализация "контракта" в модуле менеджера

Теперь, когда обработка создана и правильно размещена в дереве подсистем, необходимо реализовать ее "контракт" — набор экспортных функций в модуле менеджера, которые фреймворк будет использовать для получения информации о представлении и его выполнения.

Начнем с функций ИмяПредставления() и Описание().

ИмяПредставления()

Эта функция предельно проста. Она должна вернуть строку-шаблон, по которому фреймворк поймет, к какому типу метаданных относится представление и как оно будет называться.
 

Функция ИмяПредставления() Экспорт
	Возврат "РегистрНакопления.НарастающиеИтоги";
КонецФункции

Описание(ИмяРегистра)

Это функция отвечает за динамическое построение метаданных нашего представления для конкретного регистра накопления, имя которого передается в параметре ИмяРегистра.

 

 

Функция Описание(ИмяРегистра) Экспорт
	МетаданныеРегистра = Метаданные.РегистрыНакопления.Найти(ИмяРегистра);
	
	// Описание возможностей
	Описание = ЭлементыМоделиОписанияПредставлений.НовыйОписаниеПредставления();
	
	Описание.ПоддерживаетсяПсевдонимыПолей = Истина; 
	Описание.ПоддерживаютсяИндексы = Истина; 
	Описание.ПоддерживаетсяПолучениеРезультатаЗапроса = Ложь;      
	Описание.ПоддерживаетсяУказаниеИмяВТРезультат = Истина;
	
	// Описание полей
	Поле = ЭлементыМоделиОписанияПредставлений.НовыйПолеПредставления();    
	Описание.Поля.Добавить(Поле);
	Поле.Имя = "Период";
	Поле.ТипЗначения = Новый ОписаниеТипов("Дата"); 
	
	Для Каждого Измерение Из МетаданныеРегистра.Измерения Цикл
		Поле = ЭлементыМоделиОписанияПредставлений.НовыйПолеПредставления();
		Поле.Имя = Измерение.Имя;
		Поле.ТипЗначения = Измерение.Тип; 
		Описание.Поля.Добавить(Поле);	
	КонецЦикла;	
	
	Для Каждого Ресурс Из МетаданныеРегистра.Ресурсы Цикл
		Поле = ЭлементыМоделиОписанияПредставлений.НовыйПолеПредставления();
		Поле.Имя = Ресурс.Имя + "Оборот";
		Поле.ТипЗначения =  Ресурс.Тип; 
		Описание.Поля.Добавить(Поле); 
		
		Поле = ЭлементыМоделиОписанияПредставлений.НовыйПолеПредставления();
		Поле.Имя = Ресурс.Имя + "НарастающийИтог";
		Поле.ТипЗначения =  Ресурс.Тип; 
		Описание.Поля.Добавить(Поле); 
	КонецЦикла;		
	
	// Описание параметров
	ПараметрНачалоПериода = ЭлементыМоделиОписанияПредставлений.НовыйОписаниеПараметраКонстанта();
	Описание.ОписаниеПараметров.Добавить(ПараметрНачалоПериода);

	ПараметрНачалоПериода.Обязательный = Истина;
	ПараметрНачалоПериода.Имя = "НачалоПериода"; 
	ПараметрНачалоПериода.ДопустимПараметрЗапроса = Истина; 
	ПараметрНачалоПериода.ТипКонстанты = Новый ОписаниеТипов("Дата");  
	
	ПараметрОкончаниеПериода = ЭлементыМоделиОписанияПредставлений.НовыйОписаниеПараметраКонстанта();
	Описание.ОписаниеПараметров.Добавить(ПараметрОкончаниеПериода);
	
	ПараметрОкончаниеПериода.Обязательный = Истина;
	ПараметрОкончаниеПериода.Имя = "КонецПериода"; 
	ПараметрОкончаниеПериода.ДопустимПараметрЗапроса = Истина; 
	ПараметрОкончаниеПериода.ТипКонстанты = Новый ОписаниеТипов("Дата"); 
	
	ПараметрПериодичность = ЭлементыМоделиОписанияПредставлений.НовыйОписаниеПараметраКонстанта();
	Описание.ОписаниеПараметров.Добавить(ПараметрПериодичность);
	ПараметрПериодичность.Имя = "Периодичность";
	ПараметрПериодичность.ТипКонстанты = Новый ОписаниеТипов("Строка");
	ПараметрПериодичность.Обязательный = Ложь;
	ПараметрПериодичность.ДопустимПараметрЗапроса = Ложь;
	ПараметрПериодичность.ЗначениеПоУмолчанию = "МЕСЯЦ";
	
	ПараметрПериодичность.ДоступныеЗначения.Добавить("СЕКУНДА");
	ПараметрПериодичность.ДоступныеЗначения.Добавить("МИНУТА");
	ПараметрПериодичность.ДоступныеЗначения.Добавить("ЧАС");
	ПараметрПериодичность.ДоступныеЗначения.Добавить("ДЕНЬ");
	ПараметрПериодичность.ДоступныеЗначения.Добавить("НЕДЕЛЯ");
	ПараметрПериодичность.ДоступныеЗначения.Добавить("ДЕКАДА");
	ПараметрПериодичность.ДоступныеЗначения.Добавить("МЕСЯЦ");
	ПараметрПериодичность.ДоступныеЗначения.Добавить("КВАРТАЛ");
	ПараметрПериодичность.ДоступныеЗначения.Добавить("ПОЛУГОДИЕ");
	ПараметрПериодичность.ДоступныеЗначения.Добавить("ГОД");
	
	Описание.ОписаниеОтбора = ОписаниеИсполняемыхПредставленийШаблоны.ОписаниеОтбораСоВсемиВозможностями();
	Для Каждого Измерение Из МетаданныеРегистра.Измерения Цикл
		Описание.ОписаниеОтбора.ДоступныеПоля.Добавить(Измерение.Имя);
	КонецЦикла;	
	
	Возврат Описание;
КонецФункции

 

Сразу после создания метода Описание мы сообщаем фреймворку о "способностях" нашего представления. Эти флаги — не просто формальность, а прямое указание для встроенного оптимизатора, какие операции он может делегировать (пробросить) внутрь нашей логики, а какие потребуют материализации представления во временную таблицу.
Разберем каждую строку в новой конфигурации:

  • ПоддерживаетсяУказаниеИмяВТРезультат = Истина: Этот флаг разрешает фреймворку передавать нам имя, которое он хочет присвоить итоговой временной таблице. Это базовая и необходимая возможность для интеграции с общим потоком выполнения запроса.
  • ПоддерживаетсяПсевдонимыПолей = Истина: Установив Истина, мы сообщаем оптимизатору, что наша реализация достаточно "умна", чтобы самостоятельно присваивать псевдонимы полям. Если пользователь напишет ВЫБРАТЬ СуммаНарастающийИтог КАК Итог, фреймворк передаст нам эту информацию. Наша функция Исполнить должна будет создать итоговую временную таблицу сразу с колонкой Итог. Это более эффективно, так как избавляет фреймворк от необходимости выполнять дополнительный запрос ВЫБРАТЬ ... КАК ... ИЗ нашей_ВТ.
  • ПоддерживаютсяИндексы = Истина: Этот флаг говорит о том, что наш код умеет самостоятельно создавать индексы для итоговой временной таблицы. Если пользователь в запросе укажет ИНДЕКСИРОВАТЬ ПО Поле1, оптимизатор передаст нам список этих полей. Наша функция Исполнить должна будет не только создать ВТ, но и добавить к ней нужные индексы. Это также повышает производительность, так как индексация происходит в рамках одной операции.
  • ПоддерживаетсяПолучениеРезультатаЗапроса = Ложь: Этот флаг остается в значении Ложь и это ключевой момент для понимания нашей реализации. Он означает, что наш обработчик всегда возвращает данные в виде временной таблицы. Он не может вернуть РезультатЗапроса напрямую. В нашем случае это абсолютно верно: основная работа (Исполнить) заключается в формировании таблицы значений с помощью СКД и помещении ее в менеджер временных таблиц. А уже финальный запрос ВЫБРАТЬ ... ИЗ этой_ВТ выполнит сам Исполнитель фреймворка.

Описание полей

Следующим шагом нужно определить поля исполняемого представления. Для этого мы заполняем коллекцию Описание.Поля, программно создавая для каждого поля структуру с полями Имя (имя поля) и ТипЗначения.
В нашем случае всегда есть поле Период, остальные поля добавляются на основе метаданных регистра (измерения, и по полю Оборот и НарастающийИтог для каждого ресурса регистра).

Описание параметров

В этом блоке мы декларируем параметры, которые будет принимать наше представление. В нашем случае это период (НачалоПериода и КонецПериода), за который нужно сформировать нарастающий итог, и Периодичность расчета.

Рассмотрим подробнее возможности описания параметра:

  • ОписаниеПараметра.Имя: Имя параметра. Обязательный.
  • ОписаниеПараметра.ТипКонстанты: Тип значения параметра. Обязательный.
  • ОписаниеПараметра.Обязательный: Признак обязательности.
  • ОписаниеПараметра.ЗначениеПоУмолчанию: Для необязательных параметров можно задать значение по умолчанию. Опциональный.
  • ОписаниеПараметра.ДопустимПараметрЗапроса: Определяет, можно ли передавать не только константу (строку, число, булево значение), но и параметр запроса (&МойПараметр). Опциональный.
  • ОписаниеПараметра.ДоступныеЗначения: Массив доступных значений. Если задан, то при валидации запроса будет выполняться проверка, что заданное значение есть в списке. Также этот список будет доступен для выбора в конструкторе запросов. Опциональный.

Описание отбора

Последний шаг в функции Описание — это декларация возможностей фильтрации. Здесь мы сообщаем фреймворку, какие условия из секции ГДЕ основного запроса он может безопасно "пробросить" (делегировать) внутрь нашей логики. Это позволяет выполнять фильтрацию на самом раннем этапе, что критически важно для производительности. Также есть возможность указать, можно ли задавать отбор внутри параметров исполняемого представления.

Структура, описывающая возможности отбора, имеет следующие свойства:

  • ПробрасыватьОтборИзСекцииГДЕ (Булево) - Основной флаг, включающий или отключающий автоматический проброс отбора из секции "ГДЕ".
  • ДоступенВПараметрах (Булево) - Разрешает передавать выражение отбора напрямую через параметры виртуальной таблицы в формате ИмяПредставления(Отбор = (Выражение)).
  • ДоступныеПоля (Массив из Строка) - Список имен полей, которые могут быть использованы в условиях отбора.
  • ДополнительныеОперации (Структура) - Контейнер флагов, разрешающих использование продвинутых операций в отборах:
    • ИЛИ (Булево) - Разрешает использование логического оператора ИЛИ.
    • СравнениеПолейИсточника (Булево) - Разрешает сравнение полей не только с константами, но и с другими полями того же источника (например, ГДЕ Источник.Поле1 > Источник.Поле2).
    • МЕЖДУ (Булево) - Разрешает использование оператора МЕЖДУ.
    • ПОДОБНО (Булево) - Разрешает использование оператора ПОДОБНО.
    • ВложенныйЗапрос (Булево) - Разрешает использовать в правой части операторов сравнения вложенный запрос, возвращающий одно значение.
    • СписокВыражений (Булево) - Разрешает использование кортежей (списков полей) в условиях (например, ГДЕ (Поле1, Поле2) В ((1, "А"))).
    • РазыменованиеПолей (Булево) - Разрешает "проваливаться" через точку в полях-ссылках (например, ГДЕ ПолеКонтрагент.ИНН = "12345").
    • ФункцииЯзыкаЗапросов (Булево) - Разрешает использование встроенных функций языка запросов (НАЧАЛОПЕРИОДА, ПОДСТРОКА и др.).
    • ЯзыкВыражений (Булево) - Разрешает использование сложных выражений, включая арифметические операции, конструкцию ВЫБОР, и все, что разрешают другие флаги.

Для удобства фреймворк предоставляет две готовые функции для получения стандартных описаний отбора:

  1. ОписаниеБазовогоОтбора(): Разрешает только самые простые условия вида Поле = &Параметр или Поле В (&СписокЗначений), соединенные оператором И.
  2. ОписаниеОтбораСоВсемиВозможностями(): Включает поддержку практически всех конструкций языка запросов, включая ИЛИ, сравнение полей между собой, ПОДОБНО, вложенные запросы и использование функций.

В нашем случае, так как логика будет реализована на СКД, которая сама по себе является мощным обработчиком запросов, мы можем смело заявить о поддержке всех возможностей.

Когда исполняется запрос, в котором используется исполняемое представление, и флаг ПробрасыватьОтборИзСекцииГДЕ установлен в Истина, Исполнитель фреймворка анализирует выражение из секции ГДЕ. Те элементы отбора, которые соответствуют описанным возможностям представления, будут удалены из секции ГДЕ и "проброшены" в обработчик представления для выполнения. В основной секции ГДЕ останутся только те условия, которые невозможно было делегировать.

Пример работы Predicate Pushdown

Давайте рассмотрим сложный запрос и посмотрим, как Исполнитель разделит условия фильтрации.

1. Исходный запрос

Предположим, пользователь написал следующий запрос, чтобы получить нарастающие итоги по товарам на складах, соединяя их с какой-то своей второй временной таблицей:
 

ВЫБРАТЬ
	...
ИЗ
	ВТВторойИсточник КАК ВТВторойИсточник
		ЛЕВОЕ СОЕДИНЕНИЕ ИсполняемоеПредставление.РегистрНакопления.ТоварыНаСкладах.НарастающиеИтоги(
			НачалоПериода = &Начало,
			КонецПериода = &Окончание,
			Периодичность = "ДЕНЬ") КАК ТоварыНаСкладахНарастающиеИтоги
		ПО ВТВторойИсточник.Товар = ТоварыНаСкладахНарастающиеИтоги.Номенклатура
ГДЕ
	ТоварыНаСкладахНарастающиеИтоги.Номенклатура В (&Номенклатура)
	И (ТоварыНаСкладахНарастающиеИтоги.Склад = &Склад
		ИЛИ ТоварыНаСкладахНарастающиеИтоги.Склад = ЗНАЧЕНИЕ(Справочник.Склады.ПустаяСсылка)
		ИЛИ ТоварыНаСкладахНарастающиеИтоги.Помещение = &Помещение)
	И (ТоварыНаСкладахНарастающиеИтоги.Характеристика = &Характеристика 
		ИЛИ ВТВторойИсточник.НеУчитыватьХарактеристики)
	И НЕ ТоварыНаСкладахНарастающиеИтоги.Номенклатура ЕСТЬ NULL

Анализ секции ГДЕ

Исполнитель анализирует каждое условие в секции ГДЕ, сверяясь с возможностями, которые мы задали в ОписаниеОтбора:

  • Номенклатура В (&Номенклатура)Можно пробросить. Это простое условие по доступному полю (Номенклатура — это измерение).
  • (Склад = &Склад ИЛИ ТоварыНаСкладахНарастающиеИтоги.Склад = ЗНАЧЕНИЕ(Справочник.Склады.ПустаяСсылка)
    ИЛИ Помещение = &Помещение)Можно пробросить. Оба поля (Склад, Помещение) являются измерениями регистра. И так как мы использовали ОписаниеОтбораСоВсемиВозможностями(), наш обработчик поддерживает оператор ИЛИ и опция ДополнительныеОперации.ФункцииЯзыкаЗапросов установлена в Истина.
  • (Характеристика = &Характеристика ИЛИ ВТВторойИсточник.НеУчитыватьХарактеристики)Нельзя пробросить. Хотя Характеристика — это поле нашего представления, вторая часть условия ВТВторойИсточник.НеУчитыватьХарактеристики обращается к другому источнику. Такое "смешанное" условие обработчик представления выполнить не может.
  • НЕ Номенклатура ЕСТЬ NULLНельзя пробросить. Проверка на NULL в общем случае зависит от результата соединения (ЛЕВОЕ СОЕДИНЕНИЕ может создавать NULL там, где их не было в источнике). Поэтому такие проверки всегда остаются в основном запросе и выполняются после соединения.

3. Результат работы оптимизатора

После анализа Исполнитель разделяет логику:

  • Будет передано в обработчик представления НарастающиеИтоги:
    // Массив делегированных условий
    [
      "Номенклатура В (&Номенклатура)",
      "(Склад = &Склад ИЛИ ТоварыНаСкладахНарастающиеИтоги.Склад = ЗНАЧЕНИЕ(Справочник.Склады.ПустаяСсылка) ИЛИ Помещение = &Помещение)"
    ]
    • Наша функция Исполнить получит эти условия и учтет их при формировании запроса к СКД, что позволит получить из базы данных только нужные записи.
  • Финальный запрос, который будет выполнен движком 1С:
    ВЫБРАТЬ
    	...
    ИЗ
    	ВТВторойИсточник КАК ВТВторойИсточник
    		ЛЕВОЕ СОЕДИНЕНИЕ ВТТоварыНаСкладахНарастающиеИтоги КАК ТоварыНаСкладахНарастающиеИтоги
    		ПО ВТВторойИсточник.Товар = ТоварыНаСкладахНарастающиеИтоги.Номенклатура
    ГДЕ
    	(ТоварыНаСкладахНарастающиеИтоги.Характеристика = &Характеристика 
    		ИЛИ ВТВторойИсточник.НеУчитыватьХарактеристики)
    	И НЕ ТоварыНаСкладахНарастающиеИтоги.Номенклатура ЕСТЬ NULL

    Обратите внимание: ИсполняемоеПредставление уже заменено на временную таблицу ВТТоварыНаСкладахНарастающиеИтоги, которая содержит предварительно отфильтрованные данные. А в секции ГДЕ остались только те "сложные" условия, которые невозможно было делегировать.

Описание временной таблицы фильтра

Помимо простых параметров-констант, фреймворк поддерживает и более мощный механизм фильтрации — передачу временной таблицы-фильтра через специальный параметр ОписаниеВТФильтр.

  • Назначение: Этот механизм позволяет передать внутрь представления уже подготовленный набор данных (например, список сотрудников или документов), по которому будет работать основная логика.
  • Синтаксис в запросе: В тексте запроса это выглядит как (Поле1, Поле2) В (ВЫБРАТЬ ...) и обычно является первым параметром представления.
  • Описание в контракте: В функции Описание() для этого создается и настраивается специальный объект Описание.ОписаниеВТФильтр.

Хотя в нашей текущей реализации НарастающиеИтоги он не используется, мы могли бы расширить ее. Например, если бы мы хотели рассчитывать итоги за разные, непересекающиеся периоды и по разным складам в одном вызове, мы могли бы воспользоваться этим механизмом. Тогда стала бы возможной запись запросов вида:
 

ВЫБРАТЬ
	...	
ИЗ
	ИсполняемоеПредставление.РегистрНакопления.ТоварыНаСкладах.НарастающиеИтоги(
			(НачалоПериода, КонецПериода, Склад) В
			(ВЫБРАТЬ 
				Фильтр.НачалоПериода КАК НачалоПериода,
				Фильтр.КонецПериода КАК КонецПериода,
				Фильтр.Склад КАК Склад
			ИЗ
				ВТФильтр КАК Фильтр),
			Периодичность = "ДЕНЬ") КАК ТоварыНаСкладахНарастающиеИтоги

В этом случае описание ВТФильтр (или материализованный во временную таблицу результат вложенного запроса) было бы передано в метод Исполнить, позволяя реализовать самую сложную логику отбора.
Пример использования ВТФильтр можно посмотреть здесь. Это исполняемое представление - обертка над методом программного интерфейса ЗУП, позволяющего получать срез последних на разные периоды

Теперь запросы с использованием исполняемого представления РегистрНакопления.НарастающиеИтоги будет успешно открываться конструктором запросов, входящим в состав расширения

Исполнить(ПараметрыВыполнения)

Остался последний шаг до того, чтобы запросы с использованием исполняемого представления РегистрНакопления.НарастающиеИтоги смогли исполняться. Нам нужно реализовать императивную логику — написать код, который будет формировать и возвращать данные.

Сам расчёт нарастающего итога мы будем выполнять с помощью СКД. Сначала сгенерируем запрос получения оборотов из соответствующей виртуальной таблицы, а затем добавим настройки СКД для расчета нарастающего итога.

 

 

Функция Исполнить(ПараметрыВыполнения, Запрос) Экспорт
	ЧастиИмени = СтрРазделить(ПараметрыВыполнения.ИмяИсполняемогоПредставления, ".");
	ИмяРегистра = ЧастиИмени[ЧастиИмени.ВГраница() - 1];
	
	МетаданныеРегистра = Метаданные.РегистрыНакопления.Найти(ИмяРегистра);    
	
	СКД = Новый СхемаКомпоновкиДанных();
	Источник = СКД.ИсточникиДанных.Добавить();
	Источник.Имя = "Источник";
	Источник.ТипИсточникаДанных = "local";
	НаборСКД = СКД.НаборыДанных.Добавить(Тип("НаборДанныхЗапросСхемыКомпоновкиДанных"));
	НаборСКД.ИсточникДанных = "Источник";
	НаборСКД.Имя = "Данные";
	НаборСКД.АвтоЗаполнениеДоступныхПолей = Истина;
	
	Для Каждого КлючЗначение Из Запрос.Параметры Цикл
		ПараметрСКД = СКД.Параметры.Добавить();
		ПараметрСКД.Использование = ИспользованиеПараметраКомпоновкиДанных.Всегда;
		ПараметрСКД.Имя = КлючЗначение.Ключ;
		ПараметрСКД.ДоступенСписокЗначений = Истина;
		ПараметрСКД.Значение = КлючЗначение.Значение;
	КонецЦикла;
	
	СоответсвиеРесурсов = СоответсвиеРесурсов(МетаданныеРегистра);
	
	НаборСКД.Запрос = ТекстЗапросаОборотов(МетаданныеРегистра, ПараметрыВыполнения);	
	
	НастройкиСКД = СКД.ВариантыНастроек.Добавить().Настройки;
	Группа = НастройкиСКД.Структура.Добавить(Тип("ГруппировкаКомпоновкиДанных"));
	ДетальныеЗаписи = Группа.Структура.Добавить(Тип("ГруппировкаКомпоновкиДанных"));
	
	ВыбранныеРесурсы = Новый Соответствие; 
	Для Каждого Поле Из ПараметрыВыполнения.ИспользуемыеПоля Цикл
		Если ВРег(Поле.Ключ) = "ПЕРИОД" Тогда
			Продолжить;
		КонецЕсли;	
		
		Ресурс = СоответсвиеРесурсов.Получить(ВРег(Поле.Ключ));
		Если СтрЗаканчиваетсяНа(Поле.Значение, "НАРАСТАЮЩИЙИТОГ") Тогда
			ВычисляемоеПоле = СКД.ВычисляемыеПоля.Добавить();
			ВычисляемоеПоле.Заголовок = Ресурс + "НарастающийИтог";
			ВычисляемоеПоле.ПутьКДанным = Ресурс + "НарастающийИтог";
			ВычисляемоеПоле.Выражение = "ВычислитьВыражение(""Сумма(" + Ресурс + "Оборот)"", , ""Группировка"", ""Первая"", ""Текущая"", ""Период"")";
			ВычисляемоеПоле.ТипЗначения = Новый ОписаниеТипов("Число");
			
			ПолеСКД = ДетальныеЗаписи.Выбор.Элементы.Добавить(Тип("ВыбранноеПолеКомпоновкиДанных"));
			ПолеСКД.Поле = Новый ПолеКомпоновкиДанных(Ресурс + "НарастающийИтог");
			ПолеСКД.Использование = Истина;
		КонецЕсли;	
		Если Ресурс <> Неопределено Тогда
			Если ВыбранныеРесурсы.Получить(ВРег(Поле.Ключ)) <> Неопределено Тогда
				Продолжить;  
			КонецЕсли;   
			
			ПолеСКД = ДетальныеЗаписи.Выбор.Элементы.Добавить(Тип("ВыбранноеПолеКомпоновкиДанных"));
			ПолеСКД.Поле = Новый ПолеКомпоновкиДанных(Ресурс + "Оборот");
			ПолеСКД.Использование = Истина;
		Иначе 
			ПолеСКД = Группа.ПоляГруппировки.Элементы.Добавить(Тип("ПолеГруппировкиКомпоновкиДанных"));
			ПолеСКД.Поле = Новый ПолеКомпоновкиДанных(Поле.Ключ);
			ПолеСКД.Использование = Истина;
			
			ПолеСКД = ДетальныеЗаписи.Выбор.Элементы.Добавить(Тип("ВыбранноеПолеКомпоновкиДанных"));
			ПолеСКД.Поле = Новый ПолеКомпоновкиДанных(Поле.Ключ);
			ПолеСКД.Использование = Истина;
		КонецЕсли;	
	КонецЦикла;	
	
	Для Каждого КлючЗначение Из Запрос.Параметры Цикл
		ПараметрСКД = НастройкиСКД.ПараметрыДанных.НайтиЗначениеПараметра(Новый ПараметрКомпоновкиДанных(КлючЗначение.Ключ));
		Если ПараметрСКД <> Неопределено Тогда
			ПараметрСКД.Использование = Истина;
			ПараметрСКД.Значение = КлючЗначение.Значение;
		КонецЕсли;
	КонецЦикла;
	
	Поле = ДетальныеЗаписи.Выбор.Элементы.Добавить(Тип("ВыбранноеПолеКомпоновкиДанных"));
	Поле.Поле = Новый ПолеКомпоновкиДанных("СистемныеПоля.Уровень");
	Поле.Использование = Истина;
	
	Поле = ДетальныеЗаписи.Выбор.Элементы.Добавить(Тип("ВыбранноеПолеКомпоновкиДанных"));
	Поле.Поле = Новый ПолеКомпоновкиДанных("Период");
	Поле.Использование = Истина;
	
	ЭлементПорядка = ДетальныеЗаписи.Порядок.Элементы.Добавить(Тип("ЭлементПорядкаКомпоновкиДанных"));
	ЭлементПорядка.Использование = Истина;
	ЭлементПорядка.Поле = Новый ПолеКомпоновкиДанных("Период");
	
	КомпоновщикМакета = Новый КомпоновщикМакетаКомпоновкиДанных;
	МакетКомпоновкиДанных = КомпоновщикМакета.Выполнить(СКД, НастройкиСКД, , ,
		Тип("ГенераторМакетаКомпоновкиДанныхДляКоллекцииЗначений"));
		
	ПроцессорКомпоновки = Новый ПроцессорКомпоновкиДанных;
	ПроцессорКомпоновки.Инициализировать(МакетКомпоновкиДанных, , , , , Запрос.МенеджерВременныхТаблиц);
	НарастающийИтог = Новый ТаблицаЗначений;
		
	ПроцессорВывода = Новый ПроцессорВыводаРезультатаКомпоновкиДанныхВКоллекциюЗначений;
	ПроцессорВывода.УстановитьОбъект(НарастающийИтог);
		
	ПроцессорВывода.Вывести(ПроцессорКомпоновки);	
	
	ЗапросПомещенияВВТ = Новый Запрос();
	ЗапросПомещенияВВТ.МенеджерВременныхТаблиц = Запрос.МенеджерВременныхТаблиц;
	ЗапросПомещенияВВТ.УстановитьПараметр("Таблица", НарастающийИтог.Скопировать(Новый Структура("СистемныеПоляУровень", 2)));
	
	ЗапросПомещенияВВТ.Текст = ТекстЗапросаСозданияВТ(ПараметрыВыполнения);
	ЗапросПомещенияВВТ.Выполнить();
КонецФункции                                                 

Функция ТекстЗапросаОборотов(МетаданныеРегистра, ПараметрыВыполнения)
	ПараметНачалоПериода = ПараметрыВыполнения.ЗначенияПараметровКонстант.Получить("НачалоПериода").Значение;  
	ПараметКонецПериода = ПараметрыВыполнения.ЗначенияПараметровКонстант.Получить("КонецПериода").Значение;
	
	Если ПараметрыВыполнения.ЗначенияПараметровКонстант.Получить("Периодичность") = Неопределено Тогда
		Периодичность = "МЕСЯЦ";
	Иначе
		Периодичность = ПараметрыВыполнения.ЗначенияПараметровКонстант.Получить("Периодичность").Значение;
	КонецЕсли;		
	
	ШаблонЗапросаОборотов = 
	"ВЫБРАТЬ
	|	Обороты.Период КАК Период
	|ИЗ
	|	РегистрНакопления.#ИмяРегистра.Обороты(#НачалоПериода, #КонецПериода, #Периодичность, #Условие) КАК Обороты";
	
	ТекстЗапросаОбороты = СтрЗаменить(ШаблонЗапросаОборотов, "#ИмяРегистра", МетаданныеРегистра.Имя); 
	ТекстЗапросаОбороты = СтрЗаменить(ТекстЗапросаОбороты, "#Периодичность", Периодичность);      
	ТекстЗапросаОбороты = СтрЗаменить(ТекстЗапросаОбороты, "#НачалоПериода", "&" + ПараметНачалоПериода);
	ТекстЗапросаОбороты = СтрЗаменить(ТекстЗапросаОбороты, "#КонецПериода", "&" + ПараметКонецПериода);
	
	ТекстОтбора = ИсполнительПредставленийУтилиты.ТекстВыраженияОтбора(ПараметрыВыполнения, "Обороты");
	ТекстЗапросаОбороты = СтрЗаменить(ТекстЗапросаОбороты, "#Условие", ТекстОтбора);
	
	СхемаЗапроса = Новый СхемаЗапроса;
	СхемаЗапроса.УстановитьТекстЗапроса(ТекстЗапросаОбороты);
	ЗапросСхемы = СхемаЗапроса.ПакетЗапросов[0];
	Оператор = ЗапросСхемы.Операторы[0];
	
	ЗапросСхемы.ВыбиратьРазрешенные = ПараметрыВыполнения.ТолькоРазрешенные;
	
	СоответсвиеРесурсов = СоответсвиеРесурсов(МетаданныеРегистра);
	
	ВыбранныеРесурсы = Новый Соответствие();
	Для Каждого Поле Из ПараметрыВыполнения.ИспользуемыеПоля Цикл
		Если ВРег(Поле.Ключ) = "ПЕРИОД" Тогда
			Продолжить;
		КонецЕсли;	
		
		Ресурс = СоответсвиеРесурсов.Получить(ВРег(Поле.Ключ));
		Если Ресурс <> Неопределено Тогда
			Если ВыбранныеРесурсы.Получить(ВРег(Поле.Ключ)) <> Неопределено Тогда
				Продолжить;  
			КонецЕсли;   
			Оператор.ВыбираемыеПоля.Добавить(Ресурс + "Оборот");   
			ВыбранныеРесурсы.Вставить(Ресурс, Истина);
		Иначе
			Оператор.ВыбираемыеПоля.Добавить(Поле.Ключ);   
		КонецЕсли;	
	КонецЦикла;	
	
	Возврат СхемаЗапроса.ПолучитьТекстЗапроса();
КонецФункции

Функция ТекстЗапросаСозданияВТ(ПараметрыВыполнения)
	СхемаЗапроса = Новый СхемаЗапроса();
	Запрос = СхемаЗапроса.ПакетЗапросов.Добавить(Тип("ЗапросВыбораСхемыЗапроса"));
	Запрос.ТаблицаДляПомещения = ПараметрыВыполнения.ИмяВТРезультат;
	Оператор = Запрос.Операторы[0];
	Источник = Оператор.Источники.Добавить(Тип("ОписаниеВременнойТаблицыСхемыЗапроса"), "&Таблица");
	
	ПсевдонимыПолейВРег  = Новый Соответствие();
	Для Каждого КлючЗначение Из ПараметрыВыполнения.ПсевдонимыПолей Цикл
		ПсевдонимыПолейВРег.Вставить(ВРег(КлючЗначение.Ключ), КлючЗначение.Значение);	
	КонецЦикла;
	
	Для Каждого Поле Из ПараметрыВыполнения.ИспользуемыеПоля Цикл
		Источник.Источник.ДоступныеПоля.Добавить(Поле.Ключ);
		Оператор.ВыбираемыеПоля.Добавить("Таблица." + Поле.Ключ);
		Псевдоним = ПсевдонимыПолейВРег.Получить(ВРег(Поле.Ключ));
		Если Псевдоним <> Неопределено Тогда 
			Колонка = Запрос.Колонки.Найти(Поле.Ключ);
			Колонка.Псевдоним = Псевдоним;
		КонецЕсли;
	КонецЦикла;
		
	
	Для Каждого Индекс Из ПараметрыВыполнения.Индексы Цикл
		Колонка = Запрос.Колонки.Найти(Индекс);
		Запрос.Индекс.Добавить(Колонка);
	КонецЦикла;
	
	Возврат СхемаЗапроса.ПолучитьТекстЗапроса();
КонецФункции

Функция СоответсвиеРесурсов(МетаданныеРегистра)
	СоответсвиеРесурсов = Новый Соответствие;
	Для Каждого Ресурс Из МетаданныеРегистра.Ресурсы Цикл
		СоответсвиеРесурсов.Вставить(ВРег(Ресурс.Имя + "НарастающийИтог"), Ресурс.Имя); 
		СоответсвиеРесурсов.Вставить(ВРег(Ресурс.Имя + "Оборот"), Ресурс.Имя);  
	КонецЦикла;	
	Возврат СоответсвиеРесурсов;
КонецФункции

Эта функция — сердце нашего представления. Она получает от Исполнителя фреймворка объект ПараметрыВыполнения, который содержит всю необходимую информацию: имя регистра, переданные параметры (НачалоПериода, КонецПериода), "проброшенные" условия отбора (Отборы), а также список полей, которые запросил пользователь (ИспользуемыеПоля).

Структура ПараметрыВыполнения содержит следующие ключевые свойства, которуе будут использоваться для генерации текста запроса и настроек СКД:

  • ИмяВТРезультат (Строка) — Имя, которое фреймворк предлагает использовать для итоговой временной таблицы.
  • ЗначенияПараметровКонстант (Соответствие) — Значения именованных параметров, переданных в скобках при вызове представления.
    Получить значение нужного параметра можно с помощью метода
    ИсполнительПредставленийУтилиты.ЗначениеПараметраКонстанты(ИмяПараметра, ПараметрыВыполнения, ПараметрыЗапроса, УчитыватьЗначениеПоУмолчанию = Ложь)
  • Отборы (Массив) — Массив выражений (представленных в виде элементов AST) из секции ГДЕ, которые были "проброшены" оптимизатором для выполнения внутри нашего представления. Также сюда включено выражение заданное в параметре Отбор исполняемого представления.
    Сгенерировать текст выражения отбора можно с помощью метода
    ИсполнительПредставленийУтилиты.ТекстВыраженияОтбора(ПараметрыВыполнения, ПсевдонимИсточника, ВыводитьПсевдонимИсточника = Ложь);
  • Индексы (Массив из Строка) — Список полей, по которым нужно создать индексы в итоговой временной таблице, если была использована секция ИНДЕКСИРОВАТЬ ПО.
  • ПсевдонимыПолей (Соответствие) — Соответствие "оригинальное имя поля" -> "псевдоним", если пользователь использовал конструкцию КАК.
  • ИспользуемыеПоля (Соответствие) — Соответствие с именами полей, которые реально используются в запросе (результат работы оптимизации Projection Pushdown).
  • ТолькоРазрешенные (Булево) — Признак того, что в запросе использовалось ключевое слово РАЗРЕШЕННЫЕ.

ИсполняемыйКод(ПараметрыВыполнения)

Чтобы наши запросы к исполняемым представлениям не выглядели как "черный ящик", фреймворк обязывает реализовать метод ИсполняемыйКод. Эта функция должна вернуть код на языке 1С, который получает ровно тот же результат, что и функция Исполнить, но не зависит от методов самого фреймворка. Это невероятно мощный инструмент для отладки и понимания того, что происходит "под капотом".

 

 

Функция ИсполняемыйКод(ПараметрыВыполнения, Знач ТекущиеТабуляции) Экспорт
	ЧастиИмени = СтрРазделить(ПараметрыВыполнения.ИмяИсполняемогоПредставления, ".");
	ИмяРегистра = ЧастиИмени[ЧастиИмени.ВГраница() - 1];
	
	МетаданныеРегистра = Метаданные.РегистрыНакопления.Найти(ИмяРегистра);  
	
	ТекстовыйДокумент = Новый ТекстовыйДокумент();
	Утилиты = ГенерацияИсполняемогоКодаПредставленийУтилиты;
	
	ТекстовыйДокумент.ДобавитьСтроку("");
	
	Код = "СКД = Новый СхемаКомпоновкиДанных();
	|Источник = СКД.ИсточникиДанных.Добавить();
	|Источник.Имя = ""Источник"";
	|Источник.ТипИсточникаДанных = ""local"";
	|НаборСКД = СКД.НаборыДанных.Добавить(Тип(""НаборДанныхЗапросСхемыКомпоновкиДанных""));
	|НаборСКД.ИсточникДанных = ""Источник"";
	|НаборСКД.Имя = ""Данные"";
	|НаборСКД.АвтоЗаполнениеДоступныхПолей = Истина;";
	
	Утилиты.ВывестиМультиСтрокуВТекстовыйДокумент(ТекстовыйДокумент, Код, ТекущиеТабуляции);
	
	Строка = "НаборСКД.Запрос = ";
	ТекстовыйДокумент.ДобавитьСтроку(ТекущиеТабуляции + Строка);
	
	ЗапросПолученияДанныхРегистра = ТекстЗапросаОборотов(МетаданныеРегистра, ПараметрыВыполнения);
	ТекстЗапроса = Утилиты.ПримитивноеЗначениеВСтроку(ЗапросПолученияДанныхРегистра) + ";";
	Утилиты.ВывестиМультиСтрокуВТекстовыйДокумент(ТекстовыйДокумент, ТекстЗапроса, ТекущиеТабуляции);
	
	ТекстовыйДокумент.ДобавитьСтроку("");
	
	ВспомогательныйЗапрос = Новый СхемаЗапроса();
	ВспомогательныйЗапрос.УстановитьТекстЗапроса(ЗапросПолученияДанныхРегистра);
	
	Для Каждого ОписаниеПараметра Из ВспомогательныйЗапрос.НайтиПараметры() Цикл
		Код = "ПараметрСКД = СКД.Параметры.Добавить();
		|ПараметрСКД.Использование = ИспользованиеПараметраКомпоновкиДанных.Всегда;
		|ПараметрСКД.Имя = """ + ОписаниеПараметра.Имя + """;
		|ПараметрСКД.ДоступенСписокЗначений = Истина;
		|ПараметрСКД.Значение = " + ОписаниеПараметра.Имя + ";";
		
		Утилиты.ВывестиМультиСтрокуВТекстовыйДокумент(ТекстовыйДокумент, Код, ТекущиеТабуляции);
		ТекстовыйДокумент.ДобавитьСтроку("");
	КонецЦикла;
	
	Код = "НастройкиСКД = СКД.ВариантыНастроек.Добавить().Настройки;
	|Группа = НастройкиСКД.Структура.Добавить(Тип(""ГруппировкаКомпоновкиДанных""));
	|ДетальныеЗаписи = Группа.Структура.Добавить(Тип(""ГруппировкаКомпоновкиДанных""));";
	
	Утилиты.ВывестиМультиСтрокуВТекстовыйДокумент(ТекстовыйДокумент, Код, ТекущиеТабуляции);
	ТекстовыйДокумент.ДобавитьСтроку("");
	
	СоответсвиеРесурсов = СоответсвиеРесурсов(МетаданныеРегистра);
	
	ВыбранныеРесурсы = Новый Соответствие; 
	Для Каждого Поле Из ПараметрыВыполнения.ИспользуемыеПоля Цикл
		Если ВРег(Поле.Ключ) = "ПЕРИОД" Тогда
			Продолжить;
		КонецЕсли;	
		
		Ресурс = СоответсвиеРесурсов.Получить(ВРег(Поле.Ключ));
		Если СтрЗаканчиваетсяНа(Поле.Значение, "НАРАСТАЮЩИЙИТОГ") Тогда
			Код = "ВычисляемоеПоле = СКД.ВычисляемыеПоля.Добавить();
			|ВычисляемоеПоле.Заголовок = """ + Ресурс + "НарастающийИтог"";
			|ВычисляемоеПоле.ПутьКДанным = """ + Ресурс + "НарастающийИтог"";
			|ВычисляемоеПоле.Выражение = ""ВычислитьВыражение(""""Сумма(" + Ресурс + "Оборот)"""", , """"Группировка"""", """"Первая"""", """"Текущая"""", """"Период"""")"";
			|ВычисляемоеПоле.ТипЗначения = Новый ОписаниеТипов(""Число"");
			
			|ПолеСКД = ДетальныеЗаписи.Выбор.Элементы.Добавить(Тип(""ВыбранноеПолеКомпоновкиДанных""));
			|ПолеСКД.Поле = Новый ПолеКомпоновкиДанных(""" + Ресурс  + "НарастающийИтог"");
			|ПолеСКД.Использование = Истина;";
			
			Утилиты.ВывестиМультиСтрокуВТекстовыйДокумент(ТекстовыйДокумент, Код, ТекущиеТабуляции);
		КонецЕсли;	
		Если Ресурс <> Неопределено Тогда
			Если ВыбранныеРесурсы.Получить(ВРег(Поле.Ключ)) <> Неопределено Тогда
				Продолжить;  
			КонецЕсли;   
			
			Код = "ПолеСКД = ДетальныеЗаписи.Выбор.Элементы.Добавить(Тип(""ВыбранноеПолеКомпоновкиДанных""));
			|ПолеСКД.Поле = Новый ПолеКомпоновкиДанных(""" + Ресурс  + "Оборот"");
			|ПолеСКД.Использование = Истина;";
			Утилиты.ВывестиМультиСтрокуВТекстовыйДокумент(ТекстовыйДокумент, Код, ТекущиеТабуляции);
			
			ВыбранныеРесурсы.Вставить(ВРег(Поле.Ключ), Истина);
		Иначе 
			Код = "ПолеСКД = Группа.ПоляГруппировки.Элементы.Добавить(Тип(""ПолеГруппировкиКомпоновкиДанных""));
			|ПолеСКД.Поле = Новый ПолеКомпоновкиДанных(""" + Поле.Ключ + """);
			|ПолеСКД.Использование = Истина;
			|
			|ПолеСКД = ДетальныеЗаписи.Выбор.Элементы.Добавить(Тип(""ВыбранноеПолеКомпоновкиДанных""));
			|ПолеСКД.Поле = Новый ПолеКомпоновкиДанных(""" + Поле.Ключ + """);
			|ПолеСКД.Использование = Истина;";
			Утилиты.ВывестиМультиСтрокуВТекстовыйДокумент(ТекстовыйДокумент, Код, ТекущиеТабуляции);
		КонецЕсли;	
	КонецЦикла;	
	
	ТекстовыйДокумент.ДобавитьСтроку("");
	
	Для Каждого ОписаниеПараметра Из ВспомогательныйЗапрос.НайтиПараметры() Цикл
		Код = "ПараметрСКД = НастройкиСКД.ПараметрыДанных.НайтиЗначениеПараметра(Новый ПараметрКомпоновкиДанных(""" + ОписаниеПараметра.Имя + """));
		|Если ПараметрСКД <> Неопределено Тогда
		|	ПараметрСКД.Использование = Истина;
		|	ПараметрСКД.Значение = " + ОписаниеПараметра.Имя + ";
		|КонецЕсли;";
		Утилиты.ВывестиМультиСтрокуВТекстовыйДокумент(ТекстовыйДокумент, Код, ТекущиеТабуляции);
	КонецЦикла;
	
	ТекстовыйДокумент.ДобавитьСтроку("");
	
	Код = "Поле = ДетальныеЗаписи.Выбор.Элементы.Добавить(Тип(""ВыбранноеПолеКомпоновкиДанных""));
	|Поле.Поле = Новый ПолеКомпоновкиДанных(""СистемныеПоля.Уровень"");
	|Поле.Использование = Истина;
	|
	|Поле = ДетальныеЗаписи.Выбор.Элементы.Добавить(Тип(""ВыбранноеПолеКомпоновкиДанных""));
	|Поле.Поле = Новый ПолеКомпоновкиДанных(""Период"");
	|Поле.Использование = Истина;
	|
	|ЭлементПорядка = ДетальныеЗаписи.Порядок.Элементы.Добавить(Тип(""ЭлементПорядкаКомпоновкиДанных""));
	|ЭлементПорядка.Использование = Истина;
	|ЭлементПорядка.Поле = Новый ПолеКомпоновкиДанных(""Период"");
	|
	|КомпоновщикМакета = Новый КомпоновщикМакетаКомпоновкиДанных;
	|МакетКомпоновкиДанных = КомпоновщикМакета.Выполнить(СКД, НастройкиСКД, , ,
	|	Тип(""ГенераторМакетаКомпоновкиДанныхДляКоллекцииЗначений""));
	|	
	|ПроцессорКомпоновки = Новый ПроцессорКомпоновкиДанных;
	|ПроцессорКомпоновки.Инициализировать(МакетКомпоновкиДанных, , , , , Запрос.МенеджерВременныхТаблиц);
	|НарастающийИтог = Новый ТаблицаЗначений;
	|	
	|ПроцессорВывода = Новый ПроцессорВыводаРезультатаКомпоновкиДанныхВКоллекциюЗначений;
	|ПроцессорВывода.УстановитьОбъект(НарастающийИтог);
	|	
	|ПроцессорВывода.Вывести(ПроцессорКомпоновки);";
	
	Утилиты.ВывестиМультиСтрокуВТекстовыйДокумент(ТекстовыйДокумент, Код, ТекущиеТабуляции);
	ТекстовыйДокумент.ДобавитьСтроку("");	
	
	Код = "ЗапросПомещенияВВТ = Новый Запрос();
	|ЗапросПомещенияВВТ.МенеджерВременныхТаблиц = Запрос.МенеджерВременныхТаблиц;
	|ЗапросПомещенияВВТ.УстановитьПараметр(""Таблица"", НарастающийИтог.Скопировать(Новый Структура(""СистемныеПоляУровень"", 2)));
	|ЗапросПомещенияВВТ.Текст = ";
	
	Утилиты.ВывестиМультиСтрокуВТекстовыйДокумент(ТекстовыйДокумент, Код, ТекущиеТабуляции);
	
	ТекстЗапросаПомещенияВТ = ТекстЗапросаСозданияВТ(ПараметрыВыполнения);
	
	ТекстЗапроса = Утилиты.ПримитивноеЗначениеВСтроку(ТекстЗапросаПомещенияВТ) + ";";
	Утилиты.ВывестиМультиСтрокуВТекстовыйДокумент(ТекстовыйДокумент, ТекстЗапроса, ТекущиеТабуляции);
	
	
	Строка = "ЗапросПомещенияВВТ.Выполнить();";
	ТекстовыйДокумент.ДобавитьСтроку(ТекущиеТабуляции + Строка);
	
	Возврат ТекстовыйДокумент.ПолучитьТекст();
КонецФункции

 

 

Что мы получили в результате

Теперь, реализовав все необходимые методы "контракта", мы получили полностью рабочее шаблонное представление.

Это значит, что мы можем исполнять запросы с использованием исполняемого представления РегистрНакопления.НарастающиеИтоги и генерировать код для получения данных как в интерактивном режиме в консоли запросов, входящей в состав расширения, так и программно, с помощью методов общего модуля ИсполнительПредставлений:

  • ИсполнительПредставлений.ВыполнитьПакетЗапросов(ТекстЗапроса, Параметры, МенеджерВременныхТаблиц)
  • ИсполнительПредставлений.ПолучитьИсполняемыйКод(ТекстЗапроса, Параметры)

Пример запроса для УТ и ERP

ВЫБРАТЬ
	ТоварыНаСкладахНарастающиеИтоги.Номенклатура КАК Номенклатура,
	ТоварыНаСкладахНарастающиеИтоги.Период КАК Период,
	ТоварыНаСкладахНарастающиеИтоги.ВНаличииОборот КАК ВНаличииОборот,
	ТоварыНаСкладахНарастающиеИтоги.ВНаличииНарастающийИтог КАК ВНаличииНарастающийИтог
ИЗ
	ИсполняемоеПредставление.РегистрНакопления.ТоварыНаСкладах.НарастающиеИтоги(
		НачалоПериода = &Началао,
		КонецПериода = &Конец,
		Периодичность = "ДЕНЬ",
		Отбор = (Склад = &Склад)) КАК ТоварыНаСкладахНарастающиеИтоги
УПОРЯДОЧИТЬ ПО
	Номенклатура,
	Период

Выполнив такой запрос в консоли запросов, входящей в состав расширения, на демо базе УТ 11, мы получим такой результат.

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

 

 

Функция ПолучитьДанные(Склад, Конец, Началао)
	Запрос = Новый Запрос;
	Запрос.МенеджерВременныхТаблиц = Новый МенеджерВременныхТаблиц();
	Запрос.УстановитьПараметр("Склад", Склад);
	Запрос.УстановитьПараметр("Конец", Конец);
	Запрос.УстановитьПараметр("Началао", Началао);

	СКД = Новый СхемаКомпоновкиДанных();
	Источник = СКД.ИсточникиДанных.Добавить();
	Источник.Имя = "Источник";
	Источник.ТипИсточникаДанных = "local";
	НаборСКД = СКД.НаборыДанных.Добавить(Тип("НаборДанныхЗапросСхемыКомпоновкиДанных"));
	НаборСКД.ИсточникДанных = "Источник";
	НаборСКД.Имя = "Данные";
	НаборСКД.АвтоЗаполнениеДоступныхПолей = Истина;
	НаборСКД.Запрос = 
	"ВЫБРАТЬ
	|	Обороты.Период КАК Период,
	|	Обороты.ВНаличииОборот КАК ВНаличииОборот,
	|	Обороты.ВНаличииОборот КАК ВНаличииОборот1,
	|	Обороты.Номенклатура КАК Номенклатура
	|ИЗ
	|	РегистрНакопления.ТоварыНаСкладах.Обороты(&Началао, &Конец, ДЕНЬ, Склад = &Склад) КАК Обороты";

	ПараметрСКД = СКД.Параметры.Добавить();
	ПараметрСКД.Использование = ИспользованиеПараметраКомпоновкиДанных.Всегда;
	ПараметрСКД.Имя = "Конец";
	ПараметрСКД.ДоступенСписокЗначений = Истина;
	ПараметрСКД.Значение = Конец;

	ПараметрСКД = СКД.Параметры.Добавить();
	ПараметрСКД.Использование = ИспользованиеПараметраКомпоновкиДанных.Всегда;
	ПараметрСКД.Имя = "Началао";
	ПараметрСКД.ДоступенСписокЗначений = Истина;
	ПараметрСКД.Значение = Началао;

	ПараметрСКД = СКД.Параметры.Добавить();
	ПараметрСКД.Использование = ИспользованиеПараметраКомпоновкиДанных.Всегда;
	ПараметрСКД.Имя = "Склад";
	ПараметрСКД.ДоступенСписокЗначений = Истина;
	ПараметрСКД.Значение = Склад;

	НастройкиСКД = СКД.ВариантыНастроек.Добавить().Настройки;
	Группа = НастройкиСКД.Структура.Добавить(Тип("ГруппировкаКомпоновкиДанных"));
	ДетальныеЗаписи = Группа.Структура.Добавить(Тип("ГруппировкаКомпоновкиДанных"));

	ВычисляемоеПоле = СКД.ВычисляемыеПоля.Добавить();
	ВычисляемоеПоле.Заголовок = "ВНаличииНарастающийИтог";
	ВычисляемоеПоле.ПутьКДанным = "ВНаличииНарастающийИтог";
	ВычисляемоеПоле.Выражение = "ВычислитьВыражение(""Сумма(ВНаличииОборот)"", , ""Группировка"", ""Первая"", ""Текущая"", ""Период"")";
	ВычисляемоеПоле.ТипЗначения = Новый ОписаниеТипов("Число");
	ПолеСКД = ДетальныеЗаписи.Выбор.Элементы.Добавить(Тип("ВыбранноеПолеКомпоновкиДанных"));
	ПолеСКД.Поле = Новый ПолеКомпоновкиДанных("ВНаличииНарастающийИтог");
	ПолеСКД.Использование = Истина;
	ПолеСКД = ДетальныеЗаписи.Выбор.Элементы.Добавить(Тип("ВыбранноеПолеКомпоновкиДанных"));
	ПолеСКД.Поле = Новый ПолеКомпоновкиДанных("ВНаличииОборот");
	ПолеСКД.Использование = Истина;
	ПолеСКД = ДетальныеЗаписи.Выбор.Элементы.Добавить(Тип("ВыбранноеПолеКомпоновкиДанных"));
	ПолеСКД.Поле = Новый ПолеКомпоновкиДанных("ВНаличииОборот");
	ПолеСКД.Использование = Истина;
	ПолеСКД = Группа.ПоляГруппировки.Элементы.Добавить(Тип("ПолеГруппировкиКомпоновкиДанных"));
	ПолеСКД.Поле = Новый ПолеКомпоновкиДанных("НОМЕНКЛАТУРА");
	ПолеСКД.Использование = Истина;
	
	ПолеСКД = ДетальныеЗаписи.Выбор.Элементы.Добавить(Тип("ВыбранноеПолеКомпоновкиДанных"));
	ПолеСКД.Поле = Новый ПолеКомпоновкиДанных("НОМЕНКЛАТУРА");
	ПолеСКД.Использование = Истина;

	ПараметрСКД = НастройкиСКД.ПараметрыДанных.НайтиЗначениеПараметра(Новый ПараметрКомпоновкиДанных("Конец"));
	Если ПараметрСКД <> Неопределено Тогда
		ПараметрСКД.Использование = Истина;
		ПараметрСКД.Значение = Конец;
	КонецЕсли;
	ПараметрСКД = НастройкиСКД.ПараметрыДанных.НайтиЗначениеПараметра(Новый ПараметрКомпоновкиДанных("Началао"));
	Если ПараметрСКД <> Неопределено Тогда
		ПараметрСКД.Использование = Истина;
		ПараметрСКД.Значение = Началао;
	КонецЕсли;
	ПараметрСКД = НастройкиСКД.ПараметрыДанных.НайтиЗначениеПараметра(Новый ПараметрКомпоновкиДанных("Склад"));
	Если ПараметрСКД <> Неопределено Тогда
		ПараметрСКД.Использование = Истина;
		ПараметрСКД.Значение = Склад;
	КонецЕсли;

	Поле = ДетальныеЗаписи.Выбор.Элементы.Добавить(Тип("ВыбранноеПолеКомпоновкиДанных"));
	Поле.Поле = Новый ПолеКомпоновкиДанных("СистемныеПоля.Уровень");
	Поле.Использование = Истина;
	
	Поле = ДетальныеЗаписи.Выбор.Элементы.Добавить(Тип("ВыбранноеПолеКомпоновкиДанных"));
	Поле.Поле = Новый ПолеКомпоновкиДанных("Период");
	Поле.Использование = Истина;
	
	ЭлементПорядка = ДетальныеЗаписи.Порядок.Элементы.Добавить(Тип("ЭлементПорядкаКомпоновкиДанных"));
	ЭлементПорядка.Использование = Истина;
	ЭлементПорядка.Поле = Новый ПолеКомпоновкиДанных("Период");
	
	КомпоновщикМакета = Новый КомпоновщикМакетаКомпоновкиДанных;
	МакетКомпоновкиДанных = КомпоновщикМакета.Выполнить(СКД, НастройкиСКД, , ,
		Тип("ГенераторМакетаКомпоновкиДанныхДляКоллекцииЗначений"));
		
	ПроцессорКомпоновки = Новый ПроцессорКомпоновкиДанных;
	ПроцессорКомпоновки.Инициализировать(МакетКомпоновкиДанных, , , , , Запрос.МенеджерВременныхТаблиц);
	НарастающийИтог = Новый ТаблицаЗначений;
		
	ПроцессорВывода = Новый ПроцессорВыводаРезультатаКомпоновкиДанныхВКоллекциюЗначений;
	ПроцессорВывода.УстановитьОбъект(НарастающийИтог);
		
	ПроцессорВывода.Вывести(ПроцессорКомпоновки);

	ЗапросПомещенияВВТ = Новый Запрос();
	ЗапросПомещенияВВТ.МенеджерВременныхТаблиц = Запрос.МенеджерВременныхТаблиц;
	ЗапросПомещенияВВТ.УстановитьПараметр("Таблица", НарастающийИтог.Скопировать(Новый Структура("СистемныеПоляУровень", 2)));
	ЗапросПомещенияВВТ.Текст = 
	"ВЫБРАТЬ
	|	Таблица.ВНАЛИЧИИНАРАСТАЮЩИЙИТОГ КАК ВНАЛИЧИИНАРАСТАЮЩИЙИТОГ,
	|	Таблица.ВНАЛИЧИИОБОРОТ КАК ВНАЛИЧИИОБОРОТ,
	|	Таблица.ПЕРИОД КАК ПЕРИОД,
	|	Таблица.НОМЕНКЛАТУРА КАК НОМЕНКЛАТУРА
	|ПОМЕСТИТЬ ВТТоварыНаСкладахНарастающиеИтоги
	|ИЗ
	|	&Таблица КАК Таблица";
	ЗапросПомещенияВВТ.Выполнить();

	Запрос.Текст = 
	"ВЫБРАТЬ
	|	ТоварыНаСкладахНарастающиеИтоги.Номенклатура КАК Номенклатура,
	|	ТоварыНаСкладахНарастающиеИтоги.Период КАК Период,
	|	ТоварыНаСкладахНарастающиеИтоги.ВНаличииОборот КАК ВНаличииОборот,
	|	ТоварыНаСкладахНарастающиеИтоги.ВНаличииНарастающийИтог КАК ВНаличииНарастающийИтог
	|ИЗ
	|	ВТТоварыНаСкладахНарастающиеИтоги КАК ТоварыНаСкладахНарастающиеИтоги
	|
	|УПОРЯДОЧИТЬ ПО
	|	Номенклатура,
	|	Период";
	Возврат Запрос.Выполнить();
КонецФункции

 

 

Ограничения

На текущий момент проект находится в стадии беты, и ряд возможностей еще не реализован. Существуют следующие ограничения:

  • Поддерживается только русскоязычный синтаксис языка запросов.

  • Не поддерживается группировка по наборам.

  • Не поддерживается создание нескольких индексов для одной временной таблицы.

  • Не поддерживается создание уникальных индексов для временных таблиц.

  • Расширения языка запросов, используемые в СКД, поддерживаются только для чтения. При редактировании запроса в конструкторе эти инструкции будут потеряны.

Проверено на следующих конфигурациях и релизах:

  • Управление торговлей, редакция 11, релизы 11.5.23.59
  • 1С:ERP Управление предприятием 2, релизы 2.5.23.59
  • 1С:Библиотека стандартных подсистем, редакция 3.1, релизы 3.1.11.255
  • Зарплата и кадры государственного учреждения КОРП, редакция 3, релизы 3.1.34.40
  • Бухгалтерия предприятия, редакция 3.0, релизы 3.0.180.14
  • Зарплата и управление персоналом, редакция 3.1, релизы 3.1.34.40

Вступайте в нашу телеграмм-группу Инфостарт

См. также

Инструментарий разработчика Роли и права Запросы СКД Программист Руководитель проекта 1С v8.3 Управляемые формы Запросы Система компоновки данных Платные (руб)

Инструменты для разработчиков 1С 8.3: Infostart Toolkit. Автоматизация и ускорение разработки на управляемых формах. Легкость работы с 1С.

15500 руб.

02.09.2020    202832    1116    410    

1022

Инструментарий разработчика Чистка данных Свертка базы Инструменты администратора БД Системный администратор Программист Руководитель проекта 1С v8.3 1С:ERP Управление предприятием 2 1С:Бухгалтерия 3.0 1С:Управление торговлей 11 1С:Комплексная автоматизация 2.х 1С:Управление нашей фирмой 3.0 Россия Платные (руб)

Инструмент представляет собой обработку для проведения свёртки или обрезки баз данных. Работает на ЛЮБЫХ конфигурациях (УТ, БП, ERP, УНФ, КА и т.д.). Поддерживаются серверные и файловые базы, управляемые и обычные формы. Может выполнять свертку одновременно в несколько потоков. А так же автоматически, без непосредственного участия пользователя. Решение в Реестре отечественного ПО

8400 руб.

20.08.2024    35770    206    104    

196

Пакетная печать Печатные формы Инструментарий разработчика Программист 1С v8.3 Запросы 1С:Зарплата и кадры бюджетного учреждения 1С:ERP Управление предприятием 2 1С:Управление торговлей 11 Платные (руб)

Расширение для создания и редактирования печатных форм в системе 1С:Предприятие 8.3. Благодаря конструктору можно значительно снизить затраты времени на разработку печатных форм, повысить качество и прозрачность разработки, а также навести порядок в многообразии корпоративных печатных форм.

22200 руб.

06.10.2023    24020    62    26    

92

Инструменты администратора БД Инструментарий разработчика Роли и права Программист 1С v8.3 1C:Бухгалтерия Россия Платные (руб)

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

16000 руб.

10.11.2023    16492    69    39    

88

Инструментарий разработчика Программист 1С v8.3 Платные (руб)

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

9360 руб.

17.05.2024    34719    123    53    

165

SALE! 30%

Инструментарий разработчика WEB-интеграция 1С v8.3 1C v8.2 1C:Бухгалтерия 1С:ERP Управление предприятием 2 1С:Бухгалтерия 3.0 1С:Управление торговлей 11 1С:Зарплата и Управление Персоналом 3.x Платные (руб)

Инструмент для генерации OpenApi (Swagger) спецификаций на основании файлов конфигураций 1С. Это консольное и десктопное приложение на языке Rust с полноценным редактором кода, содержащим автозамену и подсвечивание ошибок для быстрого и безошибочного написания документирующего комментария.

18000 12600 руб.

22.11.2024    1795    1    0    

8

Инструментарий разработчика Программист 1С v8.3 1C:Бухгалтерия Россия Платные (руб)

Восстановление партий или взаиморасчетов, расчет зарплаты, пакетное формирование документов или отчетов - теперь все это стало доступнее. * Есть желание повысить скорость работы медленных алгоритмов! Но... * Нет времени думать о реализации многопоточности? * о запуске и остановке потоков? * о поддержании потоков в рабочем состоянии? * о передаче данных в потоки и как получить ответ из потока? * об организации последовательности? Тогда ЭТО - то что надо!!!

6000 руб.

07.02.2018    107180    249    100    

313

Инструментарий разработчика 1С v8.3 1C:Бухгалтерия 1С:ERP Управление предприятием 2 Платные (руб)

Разработка Конструктор автоматизированных рабочих мест "Конструктор АРМ" реализована в виде расширения и является универсальным инструментом для создания АРМ любой сложности в пользовательском режиме.

3600 руб.

27.12.2024    2882    6    0    

11
Комментарии
Подписаться на ответы Инфостарт бот Сортировка: Древо развёрнутое
Свернуть все
1. amiralnar 9 04.08.25 10:09 Сейчас в теме
Поразительно! Когда кажется, что уже нельзя придумать ничего сложнее чем представления ЗУП - на сцену выходит ваш проект.
ubnkfl; Matveev_VS; ardn; tsmult; +4 Ответить
2. zup_dev 61 04.08.25 10:16 Сейчас в теме
(1) Тут есть разница с представлениями ЗУП. Тот механизм не так сложен "под капотом", но более сложен для потребителя.

Тут все наоборот "под капотом" все сложно, зато потребитель работает с вполне наглядными запросами, например

ВЫБРАТЬ
	ТоварыНаСкладахНарастающиеИтоги.Номенклатура КАК Номенклатура,
	ТоварыНаСкладахНарастающиеИтоги.Период КАК Период,
	ТоварыНаСкладахНарастающиеИтоги.ВНаличииОборот КАК ВНаличииОборот,
	ТоварыНаСкладахНарастающиеИтоги.ВНаличииНарастающийИтог КАК ВНаличииНарастающийИтог
ИЗ
	ИсполняемоеПредставление.РегистрНакопления.ТоварыНаСкладах.НарастающиеИтоги(
		НачалоПериода = &Началао,
		КонецПериода = &Конец,
		Периодичность = "ДЕНЬ",
		Отбор = (Склад = &Склад)) КАК ТоварыНаСкладахНарастающиеИтоги
УПОРЯДОЧИТЬ ПО
	Номенклатура,
	Период  
Показать


которые в целом укладываются в стиль языка запросов 1С. Плюс есть конструктор запросов, поддерживающий такие запросы, с возможностями сопоставимыми с стандартным конструктором запрсов для управляемых форм.
3. ixijixi 2034 04.08.25 12:32 Сейчас в теме
Впечатляет!

ОписаниеПараметра.ДоступныеЗначения: Массив доступных значений
Не логичнее использовать список значений? Эти значения ведь потом используются в схеме КД через УстановитьДоступныеЗначения?
4. zup_dev 61 04.08.25 13:38 Сейчас в теме
(3)
Не логичнее использовать список значений?

Нужно будет подумать.

Эти значения ведь потом используются в схеме КД через УстановитьДоступныеЗначения?


Сам массив доступных значений используется только на этапе валидации запроса и в UI конструктора запросов, что бы можно было значение параметра выбрать из списка.
5. ixijixi 2034 04.08.25 14:20 Сейчас в теме
(4) Ну, было бы логично следующим шагом использовать исполняемые представления непосредственно в запросе схемы компоновки отчетов, по аналогии с ЗУП. Тогда компоновщик настроек будет нагляднее отображать доступные значения. Это мысли вслух)
Оставьте свое сообщение