Как исправить ошибку дублирования ключевых значений при добавлении цен в динамический список номенклатуры?

Программист 1С v8.3 (Управляемые формы) 1С:Управление торговлей IT и автоматизация бизнеса
← На главную

При доработке формы списка справочника Номенклатура разработчики часто сталкиваются с необходимостью вывести актуальные цены и остатки товаров прямо в динамический список. Однако простое добавление левого соединения с виртуальной таблицей РегистрСведений.ЦеныНоменклатуры.СрезПоследних нередко приводит к аварийному завершению работы формы с ошибкой: «Обнаружено дублирование ключевых значений в колонке ссылка».

Разберем подробно, почему возникает данная ошибка на платформе 1С:Предприятие 8.3, и рассмотрим правильные варианты решения этой задачи.

Причина возникновения ошибки дублирования ключей

Проанализируем механизм работы динамического списка управляемой формы:

  1. Контроль уникальности поля основной таблицы: Когда в свойствах динамического списка указана Основная таблица (в нашем случае Справочник.Номенклатура), платформа жестко следит за тем, чтобы каждая ссылка на элемент справочника встречалась в результирующей выборке строго один раз. Это необходимо для корректного позиционирования курсора, динамического считывания данных порциями и редактирования элементов.
  2. Множественность измерений регистра: Регистр сведений ЦеныНоменклатуры хранит информацию в разрезе нескольких измерений: Номенклатура, ВидЦены, а также Характеристика и Упаковка (в зависимости от конфигурации). Без явного отбора виртуальная таблица СрезПоследних возвращает последнюю цену для каждой комбинации всех измерений.
  3. Размножение строк при соединении: Если для одного товара заведено несколько видов цен (например, «Оптовая», «Розничная», «Закупочная») или характеристики, то левое соединение по полю Номенклатура вернет сразу несколько строк на одну позицию справочника. В результате поле Ссылка перестает быть уникальным, и платформа выдает ошибку.
  4. Аналогичная проблема с регистром остатков: Виртуальная таблица РегистрНакопления.ТоварыНаСкладах.Остатки также содержит измерения Склад, Характеристика, Серия. Если товар лежит на нескольких складах, без фильтрации или агрегации остатков возникнет такое же дублирование.

Способ 1. Фильтрация измерений в параметрах виртуальных таблиц

Самый простой и эффективный способ избавиться от дублирования — передать конкретные значения измерений в параметры виртуальных таблиц запроса динамического списка. В этом случае виртуальная таблица вернет строго одну строку для каждой позиции номенклатуры.

Посмотрим на пример правильного оформления соединений в тексте запроса динамического списка:


ВЫБРАТЬ
    СпрНоменклатура.Ссылка КАК Ссылка,
    СпрНоменклатура.Код КАК Код,
    СпрНоменклатура.Наименование КАК Наименование,
    СпрНоменклатура.Артикул КАК Артикул,
    ЕСТЬNULL(ТоварыНаСкладахОстатки.ВНаличииОстаток, 0) КАК Остаток,
    ЕСТЬNULL(ТоварыНаСкладахОстатки.КОтгрузкеОстаток, 0) КАК Резерв,
    ЕСТЬNULL(ТоварыНаСкладахОстатки.ВНаличииОстаток, 0) - ЕСТЬNULL(ТоварыНаСкладахОстатки.КОтгрузкеОстаток, 0) КАК Доступно,
    ЦеныНоменклатурыСрезПоследних.Цена КАК Цена
ИЗ
    Справочник.Номенклатура КАК СпрНоменклатура
    ЛЕВОЕ СОЕДИНЕНИЕ РегистрНакопления.ТоварыНаСкладах.Остатки(
        &Период,
        Склад = &Склад
            И Характеристика = ЗНАЧЕНИЕ(Справочник.ХарактеристикиНоменклатуры.ПустаяСсылка)
    ) КАК ТоварыНаСкладахОстатки
    ПО (ТоварыНаСкладахОстатки.Номенклатура = СпрНоменклатура.Ссылка)
    ЛЕВОЕ СОЕДИНЕНИЕ РегистрСведений.ЦеныНоменклатуры.СрезПоследних(
        &Период,
        ВидЦены = &ВидЦены
            И Характеристика = ЗНАЧЕНИЕ(Справочник.ХарактеристикиНоменклатуры.ПустаяСсылка)
    ) КАК ЦеныНоменклатурыСрезПоследних
    ПО (ЦеныНоменклатурыСрезПоследних.Номенклатура = СпрНоменклатура.Ссылка)
ГДЕ
    НЕ СпрНоменклатура.ЭтоГруппа

Параметры &ВидЦены и &Склад необходимо заполнить в модуле формы, например, в обработчике ПриСозданииНаСервере:


&НаСервере
Процедура ПриСозданииНаСервере(Отказ, СтандартнаяОбработка)
    Список.Параметры.УстановитьЗначениеПараметра("ВидЦены", ПредопределенноеЗначение("Справочник.ВидыЦен.Розничная"));
    Список.Параметры.УстановитьЗначениеПараметра("Склад", ПредопределенноеЗначение("Справочник.Склады.ОсновнойСклад"));
    Список.Параметры.УстановитьЗначениеПараметра("Период", ТекущаяДатаСеанса());
КонецПроцедуры

Способ 2. Агрегация данных во вложенном запросе

Если требуется выводить общий остаток сразу по всем складам или конкретную агрегированную цену (например, максимальную), соединять динамический список нужно не напрямую с виртуальной таблицей, а со сгруппированным подзапросом.

Рассмотрим пример группировки остатков по номенклатуре:


ЛЕВОЕ СОЕДИНЕНИЕ (
    ВЫБРАТЬ
        ТоварыОстатки.Номенклатура КАК Номенклатура,
        СУММА(ТоварыОстатки.ВНаличииОстаток) КАК ВНаличииОстаток,
        СУММА(ТоварыОстатки.КОтгрузкеОстаток) КАК КОтгрузкеОстаток
    ИЗ
        РегистрНакопления.ТоварыНаСкладах.Остатки(&Период, ) КАК ТоварыОстатки
    СГРУППИРОВАТЬ ПО
        ТоварыОстатки.Номенклатура
) КАК ТоварыНаСкладахОстатки
ПО (ТоварыНаСкладахОстатки.Номенклатура = СпрНоменклатура.Ссылка)

Способ 3. Вычисление данных в событии «ПриПолученииДанныхНаСервере»

Прямое соединение больших справочников со срезами регистров в динамическом списке может существенно замедлить работу формы и листание списка на СУБД. В типовых конфигурациях для вывода цен и остатков часто применяют расчет данных для видимых строк.

Разберем по шагам реализацию этого подхода:

  1. Оставляем запрос динамического списка без тяжелых соединений с регистрами сведений и накопления.
  2. Добавляем в таблицу формы пользовательские колонки «Цена» и «Остаток» (с типом Число), не связывая их напрямую с полями динамического списка.
  3. В свойствах таблицы формы подключаем обработчик события ПриПолученииДанныхНаСервере.
  4. В обработчике получаем массив ссылок только тех строк, которые сейчас отображаются пользователю на экране, и одним пакетным запросом считываем для них цены и остатки, после чего заполняем значения колонок в коллекции СтрокиОформления.

Этот способ полностью исключает ошибки дублирования ключевых полей и гарантирует высокую отзывчивость интерфейса даже при сотнях тысяч позиций номенклатуры в базе данных.

← На главную