В типовых конфигурациях 1С (например, «Управление торговлей 10.3», «Управление производственным предприятием» или «Комплексная автоматизация 1.1») отчеты по складским остаткам выводят информацию в единицах хранения остатков. Однако для управленческого анализа часто требуется видеть складские запасы исключительно в базовых единицах измерения (в которых ведется глобальный учет) и оценивать их стоимость по определенному типу цен (например, по базовой или трансфертной цене), установленной в карточке номенклатуры.
В рамках этой статьи мы подробно разберем, как кастомизировать стандартный отчет с помощью Системы компоновки данных (СКД), исправим критические ошибки проектирования запросов, которые часто допускают разработчики на форумах, и научимся правильно связывать таблицы остатков с периодическими регистрами цен.
Нам необходимо получить отчет, аналогичный «Ведомости по товарам на складах», который будет выполнять следующие функции:
---
В платформе 1С количество в регистрах накопления (таких как ТоварыНаСкладах или ПартииТоваровНаСкладах) хранится в единицах хранения остатков. Каждая единица измерения номенклатуры имеет свой коэффициент относительно базовой единицы (у самой базовой единицы коэффициент всегда равен 1).
Чтобы перевести количество из единиц хранения в базовые единицы, мы должны использовать следующую математическую формулу:
Количество (базовое) = Количество (в единицах хранения) * Коэффициент единицы хранения
В запросе это выражается обращением к реквизитам номенклатуры через точку:
ТоварыНаСкладахОстаткиИОбороты.КоличествоНачальныйОстаток * ТоварыНаСкладахОстаткиИОбороты.Номенклатура.ЕдиницаХраненияОстатков.Коэффициент
---
Часто начинающие программисты пытаются получить цены простым соединением с физической таблицей регистра сведений ЦеныНоменклатуры, как показано в запросе ниже (типичная ошибка с форума):
// ПРИМЕР НЕПРАВИЛЬНОГО ЗАПРОСА!
ВЫБРАТЬ
Таб.Номенклатура,
Таб.КоличествоКонечныйОстаток,
ЦеныНоменклатуры.Цена
ИЗ
Таб КАК Таб
ВНУТРЕННЕЕ СОЕДИНЕНИЕ РегистрСведений.ЦеныНоменклатуры КАК ЦеныНоменклатуры
ПО Таб.Номенклатура = ЦеныНоменклатуры.Номенклатура
ГДЕ
ЦеныНоменклатуры.ТипЦен = &ТипЦен
Почему этот запрос категорически нельзя использовать в реальной базе?
ЦеныНоменклатуры является периодическим. Это значит, что для одной номенклатуры в нем может храниться история изменения цен за несколько лет (например, 10 записей). При соединении с физической таблицей СУБД вернет 10 строк для одного товара. В итоге СКД просуммирует остатки, и ваш отчет покажет завышенные в 10 раз данные!ВНУТРЕННЕГО СОЕДИНЕНИЯ приведет к тому, что если для какого-то товара на складе временно не установлена базовая цена, этот товар вообще пропадет из отчета. Пользователь не увидит даже его количественный остаток.Как сделать правильно:
Для получения цен необходимо использовать виртуальную таблицу СрезПоследних, которая гарантированно возвращает ровно одну (самую актуальную на указанную дату) запись для каждой номенклатуры. Соединение обязательно должно быть ЛЕВЫМ.
---
Спроектируем оптимальный запрос для схемы компоновки данных. Мы выберем остатки и обороты из регистра ТоварыНаСкладах, пересчитаем их с учетом коэффициента единицы хранения остатков и левым соединением подтянем цены из среза последних на конец периода отчета.
ВЫБРАТЬ
ТоварыОбороты.Номенклатура КАК Номенклатура,
ТоварыОбороты.Склад КАК Склад,
// Пересчет количества в базовые единицы измерения
ТоварыОбороты.КоличествоНачальныйОстаток * ЕСТЬNULL(ТоварыОбороты.Номенклатура.ЕдиницаХраненияОстатков.Коэффициент, 1) КАК КоличествоНачальныйОстатокБазовое,
ТоварыОбороты.КоличествоПриход * ЕСТЬNULL(ТоварыОбороты.Номенклатура.ЕдиницаХраненияОстатков.Коэффициент, 1) КАК КоличествоПриходБазовое,
ТоварыОбороты.КоличествоРасход * ЕСТЬNULL(ТоварыОбороты.Номенклатура.ЕдиницаХраненияОстатков.Коэффициент, 1) КАК КоличествоРасходБазовое,
ТоварыОбороты.КоличествоКонечныйОстаток * ЕСТЬNULL(ТоварыОбороты.Номенклатура.ЕдиницаХраненияОстатков.Коэффициент, 1) КАК КоличествоКонечныйОстатокБазовое,
// Получение цены из виртуальной таблицы среза последних
ЕСТЬNULL(ЦеныСрезПоследних.Цена, 0) КАК БазоваяЦена
ПОМЕСТИТЬ ВТ_ДанныеТоваров
ИЗ
РегистрНакопления.ТоварыНаСкладах.ОстаткиИОбороты(&НачалоПериода, &КонецПериода, Авто, , ) КАК ТоварыОбороты
ЛЕВОЕ СОЕДИНЕНИЕ РегистрСведений.ЦеныНоменклатуры.СрезПоследних(&КонецПериода, ТипЦен = &ТипЦен) КАК ЦеныСрезПоследних
ПО ТоварыОбороты.Номенклатура = ЦеныСрезПоследних.Номенклатура
;
////////////////////////////////////////////////////////////////////////////////
ВЫБРАТЬ
ВТ_ДанныеТоваров.Номенклатура,
ВТ_ДанныеТоваров.Склад,
ВТ_ДанныеТоваров.КоличествоНачальныйОстатокБазовое,
ВТ_ДанныеТоваров.КоличествоПриходБазовое,
ВТ_ДанныеТоваров.КоличествоРасходБазовое,
ВТ_ДанныеТоваров.КоличествоКонечныйОстатокБазовое,
ВТ_ДанныеТоваров.БазоваяЦена,
// Расчет стоимостных показателей по базовой цене
ВТ_ДанныеТоваров.КоличествоНачальныйОстатокБазовое * ВТ_ДанныеТоваров.БазоваяЦена КАК СтоимостьНачальныйОстаток,
ВТ_ДанныеТоваров.КоличествоПриходБазовое * ВТ_ДанныеТоваров.БазоваяЦена КАК СтоимостьПриход,
ВТ_ДанныеТоваров.КоличествоРасходБазовое * ВТ_ДанныеТоваров.БазоваяЦена КАК СтоимостьРасход,
ВТ_ДанныеТоваров.КоличествоКонечныйОстатокБазовое * ВТ_ДанныеТоваров.БазоваяЦена КАК СтоимостьКонечныйОстаток
ИЗ
ВТ_ДанныеТоваров КАК ВТ_ДанныеТоваров
Обратите внимание, что условие отбора по типу цен ТипЦен = &ТипЦен мы поместили непосредственно в параметры виртуальной таблицы СрезПоследних. Это крайне важно для производительности СУБД: база данных сначала отфильтрует цены по нужному типу и только потом выполнит соединение, что значительно ускорит формирование отчета на больших базах данных.
---
После того как вы вставили запрос в схему компоновки данных, перейдите к настройке полей и ресурсов:
СУММА(). Полю БазоваяЦена назначать ресурс не нужно, так как суммировать цены по разным товарам не имеет математического смысла.&ТипЦен укажите тип данных СправочникСсылка.ТипыЦенНоменклатуры. Чтобы пользователю не приходилось выбирать тип цены вручную при каждом запуске, в колонке «Значение» можно сразу жестко указать предопределенный или конкретный элемент (например, найти и выбрать элемент «Базовая»).&НачалоПериода и &КонецПериода настройте понятное представление и при необходимости задайте стандартное выражение (например, начало текущего месяца и конец текущего месяца).---
Проанализируем более сложную управленческую ситуацию, упомянутую в ходе обсуждения. Что делать, если цена в течение месяца менялась несколько раз, а пользователю нужно видеть корректную стоимость приходов и расходов именно на момент совершения конкретных операций (а не по финальной цене на конец месяца)?
Для решения этой задачи в СКД применяется механизм связи наборов данных:
НаборДанных_Товары, в котором выберем детальные записи движения товаров с периодичностью «Регистратор» или «День» из регистра накопления.НаборДанных_Цены, который будет выбирать срез последних цен. В параметрах виртуальной таблицы укажем параметр &ПериодДействия.Номенклатура.&ПериодДействия укажем поле Период (или Дата документа-регистратора) из первого набора.При таком подходе СКД сама для каждой строки движения товара найдет цену, которая действовала именно на дату этого движения, и рассчитает точную историческую стоимость поступлений и списаний.
---
Если перед вами стоит задача оценить исключительно текущие остатки товаров на складе по определенному типу цен (без вывода приходов, расходов и исторических оборотов за период), нет необходимости писать новый отчет.
В типовых конфигурациях 1С для этого предназначен стандартный отчет «Стоимостная оценка склада в ценах номенклатуры». В его настройках пользователь может выбрать любой тип цен из справочника, после чего система автоматически рассчитает текущую стоимость остатков по актуальным тарифам.