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