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