Как решить проблему пустого динамического списка при соединении таблиц и реализовать выбор строк флажками в 1С

Программист 1С v8.3 (Управляемые формы) IT и автоматизация бизнеса
← На главную

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

---

Часть 1. Выясняем причину: почему пустеет динамический список?

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

Существует две основные причины такого поведения механизма динамического считывания данных в платформе «1С:Предприятие 8».

1. Неявное преобразование ЛЕВОГО соединения во ВНУТРЕННЕЕ

Это классическая ошибка при написании произвольных запросов. Разработчик связывает таблицы левым соединением, но в секции ГДЕ накладывает условие на поля правой (присоединяемой) таблицы.

Посмотрим на пример некорректного запроса:


ВЫБРАТЬ
    СпрНоменклатура.Ссылка КАК Ссылка,
    СпрНоменклатура.Код КАК Код,
    ТоварыНаСкладахОстатки.ВНаличииОстаток КАК Остаток
ИЗ
    Справочник.Номенклатура КАК СпрНоменклатура
    ЛЕВОЕ СОЕДИНЕНИЕ РегистрНакопления.ТоварыНаСкладах.Остатки КАК ТоварыНаСкладахОстатки
        ПО СпрНоменклатура.Ссылка = ТоварыНаСкладахОстатки.Номенклатура
ГДЕ
    ТоварыНаСкладахОстатки.Склад = &ПараметрСклад

Проанализируем работу СУБД: левое соединение возвращает все элементы левой таблицы (номенклатуру). Для товаров, которых нет на выбранном складе, поля правой таблицы примут значение NULL. Затем СУБД начинает обрабатывать секцию ГДЕ. Проверка NULL = &ПараметрСклад всегда возвращает ложь. В результате все строки с незаполненными остатками отсекаются. Левое соединение превращается во внутреннее. Если на выбранном складе в этот момент нет остатков ни по одной позиции, весь список полностью пустеет.

Решение: перенесите все условия фильтрации правой таблицы внутрь параметров ее виртуальной таблицы. В секции ГДЕ условий по правой таблице быть не должно.

Разберем правильный вариант запроса:


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

2. Дублирование ключевого поля при установленной основной таблице

Платформа использует свойство динамического списка ОсновнаяТаблица для оптимизации запросов, динамического считывания порций данных (скроллинга) и предоставления стандартных команд (создание, удаление, изменение). Ключевым полем при этом выступает уникальный идентификатор (в нашем случае — Ссылка номенклатуры).

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

Решение:

  1. Если вам нужно выводить детальные остатки в разрезе складов или характеристик (что неизбежно порождает дубли), очистите свойство ОсновнаяТаблица в настройках динамического списка. Учтите, что при этом пропадет возможность интерактивного добавления/удаления строк стандартными кнопками формы.
  2. Если дубли не требуются, сгруппируйте данные в самом запросе до уникальности по ключевому полю Ссылка.

---

Часть 2. Как добавить интерактивные флажки выбора строк в динамический список?

Динамический список по своей сути работает в режиме «только чтение» напрямую с СУБД. В него нельзя добавить обычную булеву колонку и дать пользователю возможность просто кликать по ней, как в табличной части, так как эти данные физически не сохранены в базе данных для каждого сеанса работы пользователя.

Рассмотрим три наиболее эффективных способа решения этой задачи.

Способ №1. Использование параметра запроса и обновление списка (Классический метод)

Этот способ основан на хранении отмеченных ссылок в реквизите формы (коллекции) и динамическом изменении параметра запроса списка.

Разберем реализацию по шагам:

Шаг 1. Модернизация запроса динамического списка

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


ВЫБРАТЬ
    СпрНоменклатура.Ссылка КАК Ссылка,
    СпрНоменклатура.Ссылка В (&МассивОтмеченных) КАК Пометка,
    СпрНоменклатура.Код КАК Код
ИЗ
    Справочник.Номенклатура КАК СпрНоменклатура

Шаг 2. Подготовка реквизитов формы

Создадим на управляемой форме реквизит МассивОтмеченных с типом СписокЗначений или Массив.

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


&НаСервере
Процедура ПриСозданииНаСервере(Отказ, СтандартнаяОбработка)
    
    // Инициализируем параметр запроса пустым массивом
    ПустойМассив = Новый Массив;
    Список.Параметры.УстановитьЗначениеПараметра("МассивОтмеченных", ПустойМассив);
    
КонецПроцедуры

Шаг 3. Обработка клика по флажку

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


&НаКлиенте
Процедура СписокВыбор(Элемент, ВыбраннаяСтрока, Поле, СтандартнаяОбработка)
    
    // Если пользователь кликнул по колонке с пометкой
    Если Поле.Имя = "Пометка" Тогда
        СтандартнаяОбработка = Ложь; // Отменяем стандартное открытие карточки
        ПереключитьПометкуНаСервере(ВыбраннаяСтрока);
    КонецЕсли;
    
КонецПроцедуры

Теперь напишем серверную процедуру, которая будет добавлять или удалять ссылку из нашего массива, а затем обновлять параметры списка:


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

Для визуальной красоты настройте колонку «Пометка» в элементе формы: установите свойство Вид в значение Поле флажка или используйте поле картинки с изображениями установленного и снятого флага.

---

Способ №2. Использование события «ПриПолученииДанныхНаСервере»

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

Рассмотрим этот алгоритм подробнее:

Шаг 1. Запрос списка

В произвольном запросе динамического списка мы просто создаем фиктивную колонку:


ВЫБРАТЬ
    СпрНоменклатура.Ссылка КАК Ссылка,
    ЛОЖЬ КАК Пометка,
    СпрНоменклатура.Код КАК Код
ИЗ
    Справочник.Номенклатура КАК СпрНоменклатура

Шаг 2. Создание коллекции хранения на форме

На форме создаем реквизит СоответствиеОтмеченных с типом Соответствие (оно работает быстрее массивов на больших объемах данных).

Шаг 3. Обработка события получения данных

Событие ПриПолученииДанныхНаСервере срабатывает только для тех строк динамического списка, которые в данный момент реально отображаются на экране монитора пользователя (обычно это 30–40 строк).


&НаСервере
Процедура СписокПриПолученииДанныхНаСервере(ИмяЭлемента, НастройкиКомпоновки, Строки)
    
    Для Каждого КлючИЗначение Из Строки Цикл
        ТекущиеДанныеСтроки = КлючИЗначение.Значение;
        // Проверяем, отмечен ли элемент в нашей коллекции СоответствиеОтмеченных
        Если СоответствиеОтмеченных.Получить(ТекущиеДанныеСтроки.Ссылка) <> Неопределено Тогда
            ТекущиеДанныеСтроки.Данные.Пометка = Истина;
        Иначе
            ТекущиеДанныеСтроки.Данные.Пометка = Ложь;
        КонецЕсли;
    КонецДля;
    
КонецПроцедуры

При клике на строку или интерактивном изменении флажка вам достаточно добавить/удалить ссылку из коллекции СоответствиеОтмеченных и вызвать стандартный метод обновления таблицы на клиенте: Элементы.Список.Обновить().

---

Способ №3. Множественное выделение строк средствами платформы (Без галочек)

Если задача заключается в том, чтобы быстро выбрать группу строк (даже разрозненных) и передать их на обработку, то создавать виртуальные галочки вовсе не обязательно. Платформа 1С поддерживает множественное выделение строк штатными средствами с зажатыми клавишами Ctrl или Shift.

Посмотрим, как получить выделенные строки и обработать их:


&НаКлиенте
Процедура ОбработатьВыделенныеСтроки(Команда)
    
    // Получаем массив идентификаторов выделенных строк
    ВыделенныеСтроки = Элементы.Список.ВыделенныеСтроки;
    
    Если ВыделенныеСтроки.Количество() = 0 Тогда
        ПоказатьПредупреждение(, "Не выбрано ни одной строки для обработки!");
        Возврат;
    КонецЕсли;
    
    // Передаем массив идентификаторов на сервер для получения реальных ссылок
    ОбработатьДанныеНаСервере(ВыделенныеСтроки);
    
КонецПроцедуры

&НаСервере
Процедура ОбработатьДанныеНаСервере(МассивИдентификаторов)
    
    Для Каждого Идентификатор Из МассивИдентификаторов Цикл
        // Извлекаем данные строки по ее идентификатору
        ДанныеСтроки = Элементы.Список.ДанныеСтроки(Идентификатор);
        Если ДанныеСтроки <> Неопределено Тогда
            СсылкаНаНоменклатуру = ДанныеСтроки.Ссылка;
            // Выполняем полезное действие, например, изменение реквизитов или проведение документов
        КонецЕсли;
    КонецДля;
    
КонецПроцедуры

Кроме того, если вы настроили перетаскивание (Drag-and-Drop) из динамического списка в обычную табличную часть, то в событии окончания перетаскивания вы также оперируете массивом выделенных строк, который можно быстро обойти и скопировать во внутреннюю коллекцию для дальнейшей работы.

---

Что выбрать: Динамический список или Таблицу значений?

Проанализируем обе технологии, чтобы сделать правильный выбор:

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

← На главную