Почему задваивается номенклатура при запросе остатков с иерархией в 1С и как правильно получать остатки по группам

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

При разработке отчетов по остаткам товаров с иерархической группировкой разработчики часто сталкиваются с двумя распространенными проблемами: задвоением элементов номенклатуры в табличном документе и отсутствием данных по остаткам для групп (папок) справочника. Разберем причинно-следственную связь этих ошибок и рассмотрим правильные методы их устранения.

Анализ причин задвоения элементов при выгрузке

Рассмотрим стандартный запрос, в котором выбираются остатки товаров, цены из среза последних и рассчитываются итоги по иерархии номенклатуры:


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

Если выполнять обход такого запроса через обычный метод Результат.Выбрать() без указания параметров обхода, платформа совершает линейное считывание всех записей. В этом случае в выборку попадают как иерархические узлы (итоги), так и детальные записи для каждого элемента. В результате один и тот же элемент номенклатуры выводится дважды: сначала как родительский узел иерархии, а затем как конечная детальная строка.

Кроме того, причиной задвоения физических строк в запросе может служить неполная фильтрация в присоединяемых виртуальных таблицах. Если в регистре сведений ЦеныНоменклатуры.СрезПоследних присутствуют цены разных типов, а фильтр по конкретному типу цены не установлен в параметрах виртуальной таблицы, одна позиция номенклатуры вернет несколько записей с разными ценами.

Правильный обход иерархического результата запроса

Для корректного вывода дерева иерархии в табличный документ необходимо использовать обход по группировкам. Для этого методу Выбрать() передается специальный параметр ОбходРезультатаЗапроса.ПоГруппировкамСИерархией.

Разберем пример кода программного вывода иерархического отчета:


Результат = Запрос.Выполнить();

ТабДок.НачатьАвтогруппировкуСтрок();

// Указываем тип обхода по группировкам с иерархией
ВыборкаИерархия = Результат.Выбрать(ОбходРезультатаЗапроса.ПоГруппировкамСИерархией);

Пока ВыборкаИерархия.Следующий() Цикл
	
	// Заполняем параметры области макета
	ОбластьГруппа.Параметры.Заполнить(ВыборкаИерархия);
	
	Если ВыборкаИерархия.ЭтоГруппа Тогда            
		ОбластьГруппа.Область("R1C1:R1C18").ЦветФона = WebЦвета.СветлоКоричневый; 
	Иначе
		ОбластьГруппа.Область("R1C1:R1C18").ЦветФона = WebЦвета.Белый;
	КонецЕсли;    
	
	// Выводим строку с учетом уровня иерархии
	ТабДок.Вывести(ОбластьГруппа, ВыборкаИерархия.Уровень()); 
	
КонецЦикла;

ТабДок.ЗакончитьАвтогруппировкуСтрок();

Использование ОбходРезультатаЗапроса.ПоГруппировкамСИерархией гарантирует, что каждая группа и элемент будут обработаны строго один раз на своем уровне вложенности.

Проблема получения остатков по группам и запросы в цикле

Частой ошибкой начинающих разработчиков является попытка рассчитать остаток для группы номенклатуры отдельным запросом внутри цикла обхода. Проанализируем следующий неоптимальный код:


Функция ОстатокНомНаСкладе(вхНоменклатура, вхСклад)
	Запрос = Новый Запрос;
	Запрос.Текст = 
		"ВЫБРАТЬ
		|	ТоварыНаСкладахОстатки.КоличествоОстаток КАК КоличествоОстаток
		|ИЗ
		|	РегистрНакопления.ТоварыНаСкладах.Остатки(
		|		&ДатаОст,
		|		Склад = &вхСклад И Номенклатура = &Номенклатура) КАК ТоварыНаСкладахОстатки";
	
	Запрос.УстановитьПараметр("ДатаОст", КонецДня(ЭтотОбъект.Дата));    
	Запрос.УстановитьПараметр("вхСклад", вхСклад);
	Запрос.УстановитьПараметр("Номенклатура", вхНоменклатура);        
	
	Выборка = Запрос.Выполнить().Выбрать();
	Если Выборка.Следующий() Тогда
		Возврат Выборка.КоличествоОстаток;
	Иначе
		Возврат 0;
	КонецЕсли;
КонецФункции

Данный подход содержит две критические проблемы:

  1. Отсутствие остатков у групп: В регистрах накопления движения записываются исключительно по конечным элементам (где ЭтоГруппа = Ложь). Условие Номенклатура = &Номенклатура, где в параметр передана группа, всегда вернет пустой результат. Чтобы получить суммарный остаток по всем вложенным элементам папки, необходимо использовать оператор В ИЕРАРХИИ: Номенклатура В ИЕРАРХИИ (&Номенклатура).
  2. Низкая производительность: Выполнение запросов внутри циклов категорически противоречит стандартам разработки 1С. При выгрузке справочника из нескольких тысяч позиций система выполнит тысячи отдельных обращений к СУБД, что приведет к зависанию интерфейса.

Оптимальное решение: получение всех данных в одном запросе

Разберем грамотную архитектонику получения остатков по складам и ценам в одном запросе без использования циклов. Для этого данные предварительно подготавливаются во временных таблицах, а расчет итогов доверяется секции ИТОГИ.


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

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

Преимущества такого подхода:

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

← На главную