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

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

При разработке отчетов по остаткам программисты часто сталкиваются с классической проблемой: при попытке добавить в отчет штрихкоды или места хранения (ячейки) итоговые суммы начинают «раздуваться». Это происходит из-за природы ЛЕВОГО СОЕДИНЕНИЯ в языке запросов 1С. Если у одной позиции номенклатуры в справочнике или регистре сведений ШтрихкодыНоменклатуры числится пять разных кодов, то при соединении одна строка остатка превратится в пять строк. Система честно суммирует эти строки в итогах, и вместо реальных 10 штук товара мы видим 50.

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

Причина возникновения дублей: Механика соединения

Проанализируем ситуацию на примере кода автора темы. В запросе выбираются остатки из виртуальной таблицы РегистрНакопления.ТоварыНаСкладах.Остатки, к которым присоединяется регистр сведений ШтрихкодыНоменклатуры. Рассмотрим, что происходит на уровне базы данных:


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

Если у Номенклатуры есть Штрихкод1, Штрихкод2 и Штрихкод3, то результат запроса выдаст три строки, в каждой из которых будет указан полный остаток. При попытке получить общую сумму по складу СКД просто сложит эти значения. Наша задача — заставить систему видеть реальный остаток, не отказываясь от вывода штрихкодов.

Способ 1. Агрегация на уровне запроса (выбор любого ШК)

Если бизнес-логика позволяет вывести в строку только один (любой) штрихкод из имеющихся, проще всего использовать группировку и агрегатные функции МИНИМУМ или МАКСИМУМ. Это «схлопнет» результат до одной строки на каждую пару «Номенклатура + Характеристика».

Разберем, как изменить исходный запрос:


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

Плюсы: Итоги всегда будут верными, запрос работает быстро.
Минусы: Мы теряем информацию о других штрихкодах, если их несколько.

Способ 2. Использование функции СКД СоединитьСтроки()

Этот метод идеален, если нам нужно видеть все штрихкоды, но мы не хотим, чтобы они плодили лишние строки. В этом случае мы выводим штрихкоды не как детальные записи, а как ресурс.

Рассмотрим шаги реализации:

  1. В запросе оставляем левое соединение со штрихкодами.
  2. В схеме компоновки данных (СКД) на вкладке «Ресурсы» добавляем поле Штрихкод.
  3. В колонке «Выражение» пишем: СоединитьСтроки(Различные Штрихкод, ", ").
  4. Поле ВНаличииОстаток также добавляем в ресурсы с обычным выражением Сумма(ВНаличииОстаток).

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

Способ 3. Продвинутый расчет через ВычислитьВыражениеСГруппировкойМассив

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

Проанализируем выражение для ресурса Остаток:


Сумма(ВычислитьВыражениеСГруппировкойМассив("Максимум(ВНаличииОстаток)", "Номенклатура, Характеристика"))

Как это работает? Разберем по шагам:

  1. Функция ВычислитьВыражениеСГруппировкойМассив сначала собирает данные во временный массив.
  2. Для каждой уникальной комбинации «Номенклатура + Характеристика» она берет Максимум(ВНаличииОстаток). Поскольку остаток в пяти строках дублей одинаковый, максимум из них будет равен этому самому остатку (один раз).
  3. Затем внешняя функция Сумма складывает полученные значения из массива.

В итоге, на уровне строк со штрихкодами мы видим остаток, а на уровне общего итога по складу — корректную сумму без повторов.

Способ 4. Связи наборов данных (Набор данных - запрос + Набор данных - запрос)

Этот метод считается наиболее «правильным» с точки зрения архитектуры 1С. Вместо того чтобы мучить один запрос сложными соединениями, мы разделяем получение данных на два независимых потока.

Шаг 1. Создаем НаборДанных1 (Остатки):


ВЫБРАТЬ Номенклатура, Склад, ВНаличииОстаток ИЗ РегистрНакопления.ТоварыНаСкладах.Остатки

Шаг 2. Создаем НаборДанных2 (Штрихкоды):

ВЫБРАТЬ Номенклатура, Штрихкод ИЗ РегистрСведений.ШтрихкодыНоменклатуры

Шаг 3. На вкладке «Связи наборов данных» указываем:

В настройках отчета мы добавляем группировку по Номенклатуре, а внутрь нее — детальные записи со Штрихкодом. СКД сама поймет, что остаток относится к группировке верхнего уровня, и не будет его суммировать пять раз при выводе детальных записей штрихкодов.

Способ 5. Работа с иерархией ресурсов и ролями полей

Иногда помогает тонкая настройка полей в СКД. Выясним, как использовать роли:

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

Также важно помнить про игнорирование значений. В настройках ресурса можно указать «Рассчитывать по...» и перечислить все поля группировок, кроме штрихкода. Это заставит СКД на уровне склада суммировать данные не «по всем строкам», а «по номенклатуре».

Рекомендации по выбору метода

Выясним, какой способ лучше применить в вашей ситуации:

Важное замечание по производительности: Левое соединение в запросе с последующей группировкой (Способ 1) работает быстрее всего на больших объемах данных. Использование ВычислитьВыражениеСГруппировкойМассив (Способ 3) создает дополнительную нагрузку на память сервера приложений, поэтому применяйте его обдуманно, если в отчете тысячи строк.

← На главную