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

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

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

Проблема использования левого соединения в запросе

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

Когда мы выполняем ЛЕВОЕ СОЕДИНЕНИЕ таблицы остатков (где одна строка на товар) с таблицей приходов (где много строк по разным партиям), СУБД копирует значение остатка для каждой найденной строки партии. Если СКД затем начинает суммировать эти данные без специальных настроек, итоговая сумма превращается в «кашу». Более того, использование виртуальных таблиц внутри временных таблиц с последующим соединением иногда приводит к побочным эффектам из-за механизмов оптимизации запросов 1С, особенно если параметры периодов передаются неявно.

В исходном коде автора использовался следующий подход:


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

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

Решение №1: Переход к объединению наборов данных (Union) в СКД

Вместо того чтобы пытаться «склеить» данные в одном огромном запросе, разделим их на несколько независимых наборов данных в схеме компоновки. Это позволит СКД самостоятельно сопоставить строки по общим полям (измерениям).

  1. Создадим Набор данных - запрос для приходов. В нем выберем Номенклатуру, Партию и показатели прихода.
  2. Создадим второй Набор данных - запрос для начальных остатков. В нем выберем только Номенклатуру и поле КоличествоНачальныйОстаток. Поле Партия в этом запросе будет отсутствовать.
  3. В настройках СКД убедимся, что имена полей Номенклатура в обоих наборах совпадают.

Теперь СКД выполнит эти запросы отдельно и объединит результат. Там, где есть партия, данные по остаткам будут сопоставлены по номенклатуре. Однако здесь мы сталкиваемся со второй проблемой: дублированием начального остатка в каждой строке с партией.

Решение №2: Настройка ресурсов «Рассчитывать по...»

Чтобы начальный остаток по номенклатуре не суммировался в детальных записях по партиям, необходимо правильно настроить вкладку Ресурсы в СКД. Рассмотрим подробнее:

Если мы просто добавим поле КоличествоНачальныйОстаток в ресурсы с выражением СУММА(КоличествоНачальныйОстаток), то в строке каждой партии мы увидим общий остаток товара, а в итоге по номенклатуре — сумму этих остатков, умноженную на количество партий. Это в корне неверно.

Разобьем настройку по шагам:

  1. Перейдем на закладку «Ресурсы».
  2. В колонке «Рассчитывать по...» для поля остатка снимем флажок со всех группировок, кроме Номенклатура и ОбщийИтог (или Иерархия Номенклатуры).
  3. Для группировки Партия расчет ресурса остатка должен быть отключен.

В этом случае в детальных записях (строках с партиями) поле остатка будет пустым, а в группировке по товару отобразится корректное значение. Это именно то, что требовалось автору: «показывать только по Номенклатуре (этих партий-то еще не было)».

Решение №3: Использование ролей полей для остатков

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

Для поля КоличествоНачальныйОстаток в наборе данных следует указать роль Остаток. В параметрах роли укажите:

Это позволит СКД автоматически применять правила агрегации остатков и корректно выводить итоги в иерархических отчетах.

Оптимизация работы с виртуальными таблицами

Проанализируем еще один важный момент из кода автора — фильтрацию в секции ГДЕ по реквизиту партии:


ГДЕ
    ПартииНаСкладахОстаткиИОборотыПриход.Партия.Дата МЕЖДУ &НачалоПериодаПриход И &КонецПериодаПриход

Важный совет: Старайтесь никогда не использовать фильтрацию через точку от измерения в секции ГДЕ для виртуальных таблиц. Это заставляет систему сначала получить все записи регистра, а затем фильтровать их. Правильнее передавать условия в параметры самой виртуальной таблицы. Если же нужно фильтровать именно по дате документа-партии, лучше всего сделать так:

  1. Сначала во временную таблицу выбрать только нужные Партии, подходящие под период.
  2. Затем использовать эту таблицу в качестве фильтра (условие В (ВЫБРАТЬ ... ИЗ втНужныеПартии)) в параметре «Условие» виртуальной таблицы ОстаткиИОбороты.

Работа с моментом времени (Граница)

В Сообщении 1 автор упоминает вычитание одной секунды из даты. В 1С 8.3 это не всегда надежный метод. Если документ проведен ровно в ту же секунду, на которую мы берем остаток, он может либо попасть, либо не попасть в выборку в зависимости от вида таблицы.

Вместо манипуляций с секундами (ДОБАВИТЬКДАТЕ(..., "СЕКУНДА", -1)) в коде программной инициализации отчета лучше использовать объект Граница:


// Для получения остатков на начало, не включая движения самой секунды начала
ГраницаПериода = Новый Граница(НачалоПериода, ВидГраницы.Исключая);

В СКД при использовании стандартных периодов система сама корректно обрабатывает границы, если полям назначены соответствующие роли периодов.

Резюмируем: Для создания отчета по остаткам номенклатуры и оборотам партий мы используем несколько наборов данных, объединяем их в СКД по ключевым измерениям и тонко настраиваем расчет ресурсов по группировкам, чтобы избежать дублирования сумм. Такой подход делает отчет гибким, быстрым и корректным с точки зрения итогов.

← На главную