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