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