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