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