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