При разработке в среде 1С:Предприятие 8.3 программисты часто сталкиваются с необходимостью кастомизации типовых форм подбора товаров. Одной из наиболее востребованных задач является вывод остатков товаров в разрезе организаций. Сложность заключается в том, что количество организаций в базе данных может меняться, и мы не можем заранее «нарисовать» фиксированные колонки в конфигураторе. Нам необходимо реализовать динамическое добавление колонок в динамический список (ДС).
В этой статье мы подробно разберем, как модифицировать текст запроса динамического списка, как избежать «размножения» строк при соединении с таблицами остатков и как программно вывести новые данные на форму.
Рассмотрим стандартную ситуацию. У нас есть основной запрос динамического списка, который выбирает данные из справочника Номенклатура. Нам нужно добавить данные об остатках по каждой организации. Если мы просто добавим ЛЕВОЕ СОЕДИНЕНИЕ с регистром накопления по полю Номенклатура, то в результате выполнения запроса для одной позиции номенклатуры будет получено столько строк, сколько организаций имеют остатки по этому товару.
Динамический список крайне чувствителен к дублированию ключевых полей (обычно это ссылка на объект). Как только платформа обнаруживает две строки с одинаковой ссылкой, она выдает ошибку: «Обнаружено дублирование ключевых значений». Чтобы решить эту проблему, нам нужно выполнить операцию транспонирования: превратить строки с организациями в отдельные колонки для каждой номенклатуры.
Первым делом проанализируем, как должен выглядеть итоговый запрос. Чтобы данные по организациям выстроились в одну строку, мы будем использовать группировку и конструкцию ВЫБОР КОГДА... внутри агрегатной функции СУММА. Рассмотрим этот подход на примере.
Предположим, нам нужно вывести остатки по трем организациям. Нам необходимо динамически сформировать следующий текст запроса в процедуре ПриСозданииНаСервере:
// Пример формирования текста запроса для трех организаций
ТекстЗапроса =
"ВЫБРАТЬ
| Номенклатура.Ссылка КАК Ссылка,
| Номенклатура.Код КАК Код,
| Номенклатура.Наименование КАК Наименование,
| СУММА(ВЫБОР КОГДА Остатки.Организация = &Организация1 ТОГДА Остатки.В наличии ИНАЧЕ 0 КОНЕЦ) КАК Остаток_Организация1,
| СУММА(ВЫБОР КОГДА Остатки.Организация = &Организация2 ТОГДА Остатки.В наличии ИНАЧЕ 0 КОНЕЦ) КАК Остаток_Организация2
|ИЗ
| Справочник.Номенклатура КАК Номенклатура
| ЛЕВОЕ СОЕДИНЕНИЕ РегистрНакопления.ТоварыОрганизаций.Остатки КАК Остатки
| ПО Номенклатура.Ссылка = Остатки.Номенклатура
|ГРУППИРОВАТЬ ПО
| Номенклатура.Ссылка,
| Номенклатура.Код,
| Номенклатура.Наименование";
Обратите внимание на важный момент: использование псевдонимов полей (Остаток_Организация1). Они должны быть уникальными и предсказуемыми, чтобы мы могли позже обратиться к ним при создании элементов формы.
Поскольку количество организаций заранее неизвестно, мы должны сформировать текст запроса в цикле. Проанализируем, как это реализовать в коде:
// Получаем массив или таблицу нужных организаций
СписокОрганизаций = ПолучитьАктуальныеОрганизации();
ТекстПолейОрганизаций = "";
Для Каждого ТекущаяОрганизация Из СписокОрганизаций Цикл
// Формируем уникальный идентификатор для псевдонима колонки
ИмяПоля = "Остаток_" + СтрЗаменить(ТекущаяОрганизация.УникальныйИдентификатор(), "-", "_");
// Добавляем кусок кода в запрос
ТекстПолейОрганизаций = ТекстПолейОрганизаций +
", СУММА(ВЫБОР КОГДА Остатки.Организация = &Орг_" + ИмяПоля + " ТОГДА Остатки.ВНаличииОстаток ИНАЧЕ 0 КОНЕЦ) КАК " + ИмяПоля;
КонецЦикла;
// Вставляем сформированные поля в основной запрос ДС
Список.ТекстЗапроса = СтрЗаменить(Список.ТекстЗапроса, "Номенклатура.Наименование", "Номенклатура.Наименование" + ТекстПолейОрганизаций);
// Не забываем установить параметры для каждой организации
Для Каждого ТекущаяОрганизация Из СписокОрганизаций Цикл
ИмяПоля = "Остаток_" + СтрЗаменить(ТекущаяОрганизация.УникальныйИдентификатор(), "-", "_");
Список.Параметры.УстановитьЗначениеПараметра("Орг_" + ИмяПоля, ТекущаяОрганизация);
КонецЦикла;
После того как мы изменили ТекстЗапроса, поля появились в схеме компоновки данных динамического списка, но они еще не видны пользователю. Выясним, как вывести их на форму в виде колонок таблицы. Нам потребуется метод Элементы.Добавить().
Рассмотрим алгоритм добавления визуальных элементов:
ПолеФормы.ПутьКДанным, связывая элемент с полем из запроса ДС (например, "Список.Остаток_Организация1").
Для Каждого ТекущаяОрганизация Из СписокОрганизаций Цикл
ИмяПоля = "Остаток_" + СтрЗаменить(ТекущаяОрганизация.УникальныйИдентификатор(), "-", "_");
// Добавляем элемент на форму
НоваяКолонка = Элементы.Добавить(ИмяПоля, Тип("ПолеФормы"), Элементы.Список);
НоваяКолонка.Заголовок = Строка(ТекущаяОрганизация);
НоваяКолонка.Вид = ВидПоляФормы.ПолеНадписи; // Или ПолеВвода, если нужно редактирование
НоваяКолонка.ПутьКДанным = "Список." + ИмяПоля; // "Список" - это имя реквизита ДС на форме
КонецЦикла;
Если вы работаете в конфигурации Управление торговлей 11, важно помнить, что механизм подбора товаров довольно сложен. Модификация текста запроса напрямую может конфликтовать с типовыми механизмами фильтрации. Рассмотрим рекомендации по внедрению в такие системы:
ПриСозданииНаСервере после вызова всех типовых инициализаций. Если подбор реализован через общую форму, возможно, придется модифицировать программный интерфейс общего модуля (например, ПодборТоваровСервер).ОсновнаяТаблица заполнено (например, Справочник.Номенклатура), платформа сама следит за уникальностью ключей. Если же вы используете сложный произвольный запрос без основной таблицы, обязательно проверьте настройки ключевых полей в конструкторе запроса ДС.ВЫБОР КОГДА и группировкой может выполняться медленно. В этом случае стоит проанализировать возможность использования Таблицы Значений вместо динамического списка, хотя это и лишит вас преимуществ динамического считывания данных порциями.Реализация динамических колонок — это двухэтапный процесс. Сначала мы подготавливаем данные в запросе, используя агрегатные функции для «схлопывания» дублей в одну строку. Затем мы программно создаем интерфейсные элементы, которые отображают эти данные пользователю.
Использование конструкции СУММА(ВЫБОР КОГДА...) — это классический и наиболее надежный способ транспонирования данных в 1С. Он позволяет сохранить функциональность динамического списка (поиск, отборы, сортировка) и при этом предоставить пользователю актуальную информацию в удобном табличном виде.