Как вывести все штрихкоды номенклатуры через запятую в динамическом списке 1С

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

При разработке интерфейсов в 1С часто возникает задача отображения данных «один ко многим». Используя приемы построения эффективных интерфейсов, можно решить множество проблем юзабилити. Типичный пример — справочник Номенклатура, где у одной позиции может быть несколько штрихкодов. Если мы просто добавим левое соединение с регистром сведений Штрихкоды в запрос динамического списка, мы получим дублирование строк номенклатуры: на каждый штрихкод будет выведена отдельная строка. Рассмотрим, как решить эту задачу правильно, сохранив уникальность строк и обеспечив удобство пользователя.

Анализ проблемы и выбор стратегии решения

Проанализируем ситуацию: в языке запросов 1С отсутствует встроенная функция агрегации строк (аналог string_agg в PostgreSQL или GROUP_CONCAT в MySQL). Мы не можем «склеить» строки прямо в Выбрать. Группировка по Максимуму, которую часто используют новички, вернет только один (самый большой) штрихкод, игнорируя остальные.

Выясним причину, почему это сложно реализовать стандартным запросом. Динамический список работает порционно, и для каждой строки ему нужен уникальный ключ (обычно это Ссылка). Если мы выведем штрихкоды через запятую, нам придется либо делать это «на лету» при выводе, либо хранить подготовленную строку в базе данных.

Разберем три основных способа реализации этой задачи.

Способ 1. Использование события ПриПолученииДанныхНаСервере

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

Для начала добавим в динамический список произвольную колонку Штрихкод (тип — Строка). К слову, по такой же методике часто реализуется флажок в динамическом списке или индикатор прогресса в ячейке, когда нужно вывести нетипичные данные. Затем создадим на форме обработчик события ПриПолученииДанныхНаСервере. Посмотрим на пример программного кода, который реализует сбор штрихкодов:


&НаСервере
Процедура СписокПриПолученииДанныхНаСервере(ИмяЭлемента, Настройки, Строки)
    
    // Подготовим массив ссылок номенклатуры, которые сейчас отображены в списке
    МассивНоменклатуры = Строки.ПолучитьКлючи();
    
    // Выполним один запрос ко всем штрихкодам для этого массива ссылок
    Запрос = Новый Запрос;
    Запрос.Текст = 
    "ВЫБРАТЬ
    |	Штрихкоды.Владелец КАК Владелец,
    |	Штрихкоды.Штрихкод КАК Штрихкод
    |ИЗ
    |	РегистрСведений.Штрихкоды КАК Штрихкоды
    |ГДЕ
    |	Штрихкоды.Владелец В(&МассивНоменклатуры)";
    
    Запрос.УстановитьПараметр("МассивНоменклатуры", МассивНоменклатуры);
    
    РезультатЗапроса = Запрос.Выполнить();
    Выборка = РезультатЗапроса.Выбрать();
    
    // Используем Соответствие для временного хранения склеенных строк
    ТаблицаШтрихкодов = Новый Соответствие;
    
    Пока Выборка.Следующий() Цикл
        ТекущийШтрихкод = ТаблицаШтрихкодов.Получить(Выборка.Владелец);
        Если ТекущийШтрихкод = Неопределено Тогда
            ТаблицаШтрихкодов.Вставить(Выборка.Владелец, Выборка.Штрихкод);
        Иначе
            ТаблицаШтрихкодов.Вставить(Выборка.Владелец, ТекущийШтрихкод + ", " + Выборка.Штрихкод);
        КонецЕсли;
    КонецЦикла;
    
    // Заполняем данные оформления в списке
    Для Каждого КлючИЗначение Из Строки Цикл
        ТекстШтрихкодов = ТаблицаШтрихкодов.Получить(КлючИЗначение.Ключ);
        Если ТекстШтрихкодов <> Неопределено Тогда
            КлючИЗначение.Значение.Данные.Штрихкод = ТекстШтрихкодов;
        КонецЕсли;
    КонецЦикла;
    
КонецПроцедуры

Важный нюанс: Обратите внимание, что мы делаем запрос один раз для всей пачки строк (параметр Строки), а не в цикле по каждой строке. Это критически важно для производительности. Если делать запрос внутри цикла Для Каждого, динамический список будет «тормозить» при прокрутке.

Способ 2. Денормализация (Хранение строки штрихкодов в реквизите)

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

Чтобы поиск работал корректно, рекомендуем использовать метод денормализации данных:

  1. Добавим в справочник Номенклатура новый реквизит ВсеШтрихкоды (длинная строка, неограниченная длина не рекомендуется, лучше 100-200 символов с индексацией).
  2. Создадим подписку на событие ПриЗаписи для регистра сведений Штрихкоды (или внесем изменения в модуль набора записей).
  3. При изменении штрихкодов программно сформируем строку через запятую и запишем ее в реквизит ВсеШтрихкоды соответствующего объекта Номенклатура.

Рассмотрим пример функции для обновления этого реквизита:


Процедура ОбновитьСтрокуШтрихкодов(СсылкаНаНоменклатуру) Экспорт
    
    Запрос = Новый Запрос;
    Запрос.Текст = 
    "ВЫБРАТЬ
    |	Штрихкоды.Штрихкод
    |ИЗ
    |	РегистрСведений.Штрихкоды КАК Штрихкоды
    |ГДЕ
    |	Штрихкоды.Владелец = &Владелец";
    
    Запрос.УстановитьПараметр("Владелец", СсылкаНаНоменклатуру);
    Выборка = Запрос.Выполнить().Выбрать();
    
    МассивШтрихкодов = Новый Массив;
    Пока Выборка.Следующий() Цикл
        МассивШтрихкодов.Добавить(Строка(Выборка.Штрихкод));
    КонецЦикла;
    
    ОбъектНоменклатура = СсылкаНаНоменклатуру.ПолучитьОбъект();
    ОбъектНоменклатура.ВсеШтрихкоды = СтрСоединить(МассивШтрихкодов, ", ");
    ОбъектНоменклатура.Записать();
    
КонецПроцедуры

Преимущество этого метода в том, что в динамическом списке нам вообще не нужно писать код. Мы просто выводим поле ВсеШтрихкоды, и по нему мгновенно работает поиск, сортировка и отбор.

Способ 3. Вычисляемые поля СКД и функции общего модуля

Если ваш динамический список построен на произвольном запросе, можно задействовать механизмы системы компоновки данных (СКД). Этот прием отдаленно напоминает задачу, когда требуется вывести несколько картинок в одну ячейку на СКД, так как в обоих случаях мы программно готовим составное значение для одной ячейки. Выясним, как это сделать:

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

Посмотрим на пример такой функции:


Функция СформироватьСписокШтрихкодов(НоменклатураСсылка) Экспорт
    // Логика получения штрихкодов через СтрСоединить()
    // Внимание: метод может замедлять список, если данных много
КонецФункции

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

Оптимизация и юзабилити

Рассмотрим ситуацию, когда у товара может быть очень много штрихкодов (например, 100 и более). В некоторых случаях может потребоваться даже чтение штрих-кодов из PDF или картинок, если они хранятся в виде присоединенных файлов. Но выводить их все через запятую в одной ячейке — плохая практика.

Проанализируем профессиональные интерфейсные решения:

  1. Ограничение длины: В коде формирования строки добавьте условие: если штрихкодов больше трех, выводите первые три и приписку «... и еще N».
  2. Всплывающие подсказки: В обработчике ПриПолученииДанныхНаСервере можно заполнять не только само значение ячейки, но и ее ТекстПодсказки. В ячейке пользователь видит основной код, а при наведении мыши — полный список через запятую.
  3. Панель превью: Часто лучше вывести штрихкоды в отдельную табличную часть на боковой панели формы списка, которая обновляется при активизации строки номенклатуры.

Подводя итог, можно сказать: если вам нужно только отображение — используйте ПриПолученииДанныхНаСервере с пакетным запросом. Если же критически важен поиск по этим штрихкодам прямо в списке — выбирайте путь с созданием дополнительного реквизита в справочнике Номенклатура.

← На главную