При проектировании интерфейсов на управляемых формах 1С разработчики часто сталкиваются с необходимостью отображения сложных, динамически обновляемых списков. Классический сценарий: вывести список товаров из справочника Справочник.Номенклатура и дополнить его актуальными остатками из регистра накопления. Однако на этом пути часто возникают две преграды — динамический список внезапно оказывается абсолютно пустым, а стандартные механизмы не позволяют пользователю просто «проставить галочки» напротив нужных строк для их последующей обработки. В рамках этой статьи мы подробно разберем, почему возникают эти проблемы, и реализуем их пошаговое решение.
---
Рассмотрим ситуацию: вы создали динамический список с произвольным запросом. В запросе вы связали справочник левым соединением с регистром остатков, чтобы выводить товары даже в том случае, если их нет на складе. Но как только вы открываете форму — список пуст. При этом, если в качестве основной таблицы вы указываете регистр остатков, данные начинают отображаться. Почему так происходит?
Существует две основные причины такого поведения механизма динамического считывания данных в платформе «1С:Предприятие 8».
1. Неявное преобразование ЛЕВОГО соединения во ВНУТРЕННЕЕ
Это классическая ошибка при написании произвольных запросов. Разработчик связывает таблицы левым соединением, но в секции ГДЕ накладывает условие на поля правой (присоединяемой) таблицы.
Посмотрим на пример некорректного запроса:
ВЫБРАТЬ
СпрНоменклатура.Ссылка КАК Ссылка,
СпрНоменклатура.Код КАК Код,
ТоварыНаСкладахОстатки.ВНаличииОстаток КАК Остаток
ИЗ
Справочник.Номенклатура КАК СпрНоменклатура
ЛЕВОЕ СОЕДИНЕНИЕ РегистрНакопления.ТоварыНаСкладах.Остатки КАК ТоварыНаСкладахОстатки
ПО СпрНоменклатура.Ссылка = ТоварыНаСкладахОстатки.Номенклатура
ГДЕ
ТоварыНаСкладахОстатки.Склад = &ПараметрСклад
Проанализируем работу СУБД: левое соединение возвращает все элементы левой таблицы (номенклатуру). Для товаров, которых нет на выбранном складе, поля правой таблицы примут значение NULL. Затем СУБД начинает обрабатывать секцию ГДЕ. Проверка NULL = &ПараметрСклад всегда возвращает ложь. В результате все строки с незаполненными остатками отсекаются. Левое соединение превращается во внутреннее. Если на выбранном складе в этот момент нет остатков ни по одной позиции, весь список полностью пустеет.
Решение: перенесите все условия фильтрации правой таблицы внутрь параметров ее виртуальной таблицы. В секции ГДЕ условий по правой таблице быть не должно.
Разберем правильный вариант запроса:
ВЫБРАТЬ
СпрНоменклатура.Ссылка КАК Ссылка,
СпрНоменклатура.Код КАК Код,
ЕСТЬNULL(ТоварыНаСкладахОстатки.ВНаличииОстаток, 0) КАК Остаток
ИЗ
Справочник.Номенклатура КАК СпрНоменклатура
ЛЕВОЕ СОЕДИНЕНИЕ РегистрНакопления.ТоварыНаСкладах.Остатки(, Склад = &ПараметрСклад) КАК ТоварыНаСкладахОстатки
ПО СпрНоменклатура.Ссылка = ТоварыНаСкладахОстатки.Номенклатура
2. Дублирование ключевого поля при установленной основной таблице
Платформа использует свойство динамического списка ОсновнаяТаблица для оптимизации запросов, динамического считывания порций данных (скроллинга) и предоставления стандартных команд (создание, удаление, изменение). Ключевым полем при этом выступает уникальный идентификатор (в нашем случае — Ссылка номенклатуры).
Если в результате левого соединения с регистром остатков по разным характеристикам или складам одна и та же номенклатура дублируется в выборке запроса (на одну ссылку приходится несколько записей), платформа не может сопоставить строки динамического списка с уникальным ключом основной таблицы. Это приводит к программному сбою: список либо отображается пустым, либо выдает ошибку.
Решение:
ОсновнаяТаблица в настройках динамического списка. Учтите, что при этом пропадет возможность интерактивного добавления/удаления строк стандартными кнопками формы.Ссылка.---
Динамический список по своей сути работает в режиме «только чтение» напрямую с СУБД. В него нельзя добавить обычную булеву колонку и дать пользователю возможность просто кликать по ней, как в табличной части, так как эти данные физически не сохранены в базе данных для каждого сеанса работы пользователя.
Рассмотрим три наиболее эффективных способа решения этой задачи.
Этот способ основан на хранении отмеченных ссылок в реквизите формы (коллекции) и динамическом изменении параметра запроса списка.
Разберем реализацию по шагам:
Шаг 1. Модернизация запроса динамического списка
Добавим в текст запроса динамического списка вычисляемое булево поле, которое будет проверять наличие текущей ссылки в массиве отмеченных:
ВЫБРАТЬ
СпрНоменклатура.Ссылка КАК Ссылка,
СпрНоменклатура.Ссылка В (&МассивОтмеченных) КАК Пометка,
СпрНоменклатура.Код КАК Код
ИЗ
Справочник.Номенклатура КАК СпрНоменклатура
Шаг 2. Подготовка реквизитов формы
Создадим на управляемой форме реквизит МассивОтмеченных с типом СписокЗначений или Массив.
При создании формы на сервере инициализируем параметр динамического списка пустым массивом, чтобы избежать ошибок выполнения запроса:
&НаСервере
Процедура ПриСозданииНаСервере(Отказ, СтандартнаяОбработка)
// Инициализируем параметр запроса пустым массивом
ПустойМассив = Новый Массив;
Список.Параметры.УстановитьЗначениеПараметра("МассивОтмеченных", ПустойМассив);
КонецПроцедуры
Шаг 3. Обработка клика по флажку
Чтобы пользователь мог интерактивно управлять галочками, нам нужно перехватить событие выбора строки или нажатия на ячейку. Для этого настроим обработчик события Выбор у элемента формы — таблицы динамического списка:
&НаКлиенте
Процедура СписокВыбор(Элемент, ВыбраннаяСтрока, Поле, СтандартнаяОбработка)
// Если пользователь кликнул по колонке с пометкой
Если Поле.Имя = "Пометка" Тогда
СтандартнаяОбработка = Ложь; // Отменяем стандартное открытие карточки
ПереключитьПометкуНаСервере(ВыбраннаяСтрока);
КонецЕсли;
КонецПроцедуры
Теперь напишем серверную процедуру, которая будет добавлять или удалять ссылку из нашего массива, а затем обновлять параметры списка:
&НаСервере
Процедура ПереключитьПометкуНаСервере(ВыбраннаяСсылка)
// Получаем текущие параметры динамического списка
ПараметрМассив = Список.Параметры.Найти("МассивОтмеченных");
ТекущийМассив = ?(ПараметрМассив = Неопределено, Новый Массив, ПараметрМассив.Значение);
// Проверяем наличие ссылки в массиве отмеченных
Индекс = ТекущийМассив.Найти(ВыбраннаяСсылка);
Если Индекс = Неопределено Тогда
// Ссылка не найдена — добавляем (ставим галочку)
ТекущийМассив.Добавить(ВыбраннаяСсылка);
Иначе
// Ссылка найдена — удаляем (снимаем галочку)
ТекущийМассив.Удалить(Индекс);
КонецЕсли;
// Передаем обновленный массив обратно в параметры динамического списка
Список.Параметры.УстановитьЗначениеПараметра("МассивОтмеченных", ТекущийМассив);
КонецПроцедуры
Для визуальной красоты настройте колонку «Пометка» в элементе формы: установите свойство Вид в значение Поле флажка или используйте поле картинки с изображениями установленного и снятого флага.
---
Этот метод более прогрессивный и производительный, особенно для списков, содержащих сотни тысяч записей, так как он не пересчитывает весь запрос динамического списка целиком при каждом изменении флага.
Рассмотрим этот алгоритм подробнее:
Шаг 1. Запрос списка
В произвольном запросе динамического списка мы просто создаем фиктивную колонку:
ВЫБРАТЬ
СпрНоменклатура.Ссылка КАК Ссылка,
ЛОЖЬ КАК Пометка,
СпрНоменклатура.Код КАК Код
ИЗ
Справочник.Номенклатура КАК СпрНоменклатура
Шаг 2. Создание коллекции хранения на форме
На форме создаем реквизит СоответствиеОтмеченных с типом Соответствие (оно работает быстрее массивов на больших объемах данных).
Шаг 3. Обработка события получения данных
Событие ПриПолученииДанныхНаСервере срабатывает только для тех строк динамического списка, которые в данный момент реально отображаются на экране монитора пользователя (обычно это 30–40 строк).
&НаСервере
Процедура СписокПриПолученииДанныхНаСервере(ИмяЭлемента, НастройкиКомпоновки, Строки)
Для Каждого КлючИЗначение Из Строки Цикл
ТекущиеДанныеСтроки = КлючИЗначение.Значение;
// Проверяем, отмечен ли элемент в нашей коллекции СоответствиеОтмеченных
Если СоответствиеОтмеченных.Получить(ТекущиеДанныеСтроки.Ссылка) <> Неопределено Тогда
ТекущиеДанныеСтроки.Данные.Пометка = Истина;
Иначе
ТекущиеДанныеСтроки.Данные.Пометка = Ложь;
КонецЕсли;
КонецДля;
КонецПроцедуры
При клике на строку или интерактивном изменении флажка вам достаточно добавить/удалить ссылку из коллекции СоответствиеОтмеченных и вызвать стандартный метод обновления таблицы на клиенте: Элементы.Список.Обновить().
---
Если задача заключается в том, чтобы быстро выбрать группу строк (даже разрозненных) и передать их на обработку, то создавать виртуальные галочки вовсе не обязательно. Платформа 1С поддерживает множественное выделение строк штатными средствами с зажатыми клавишами Ctrl или Shift.
Посмотрим, как получить выделенные строки и обработать их:
&НаКлиенте
Процедура ОбработатьВыделенныеСтроки(Команда)
// Получаем массив идентификаторов выделенных строк
ВыделенныеСтроки = Элементы.Список.ВыделенныеСтроки;
Если ВыделенныеСтроки.Количество() = 0 Тогда
ПоказатьПредупреждение(, "Не выбрано ни одной строки для обработки!");
Возврат;
КонецЕсли;
// Передаем массив идентификаторов на сервер для получения реальных ссылок
ОбработатьДанныеНаСервере(ВыделенныеСтроки);
КонецПроцедуры
&НаСервере
Процедура ОбработатьДанныеНаСервере(МассивИдентификаторов)
Для Каждого Идентификатор Из МассивИдентификаторов Цикл
// Извлекаем данные строки по ее идентификатору
ДанныеСтроки = Элементы.Список.ДанныеСтроки(Идентификатор);
Если ДанныеСтроки <> Неопределено Тогда
СсылкаНаНоменклатуру = ДанныеСтроки.Ссылка;
// Выполняем полезное действие, например, изменение реквизитов или проведение документов
КонецЕсли;
КонецДля;
КонецПроцедуры
Кроме того, если вы настроили перетаскивание (Drag-and-Drop) из динамического списка в обычную табличную часть, то в событии окончания перетаскивания вы также оперируете массивом выделенных строк, который можно быстро обойти и скопировать во внутреннюю коллекцию для дальнейшей работы.
---
Проанализируем обе технологии, чтобы сделать правильный выбор:
Таким образом, используя предложенные методики настройки левых соединений и программной работы с параметрами запроса или событием ПриПолученииДанныхНаСервере, мы можем создавать быстрые, удобные и функциональные интерфейсы, сочетающие в себе мощь динамических списков 1С и гибкость интерактивной работы.