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