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