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