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

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

В типовых конфигурациях 1С (например, «Управление торговлей 10.3», «Управление производственным предприятием» или «Комплексная автоматизация 1.1») отчеты по складским остаткам выводят информацию в единицах хранения остатков. Однако для управленческого анализа часто требуется видеть складские запасы исключительно в базовых единицах измерения (в которых ведется глобальный учет) и оценивать их стоимость по определенному типу цен (например, по базовой или трансфертной цене), установленной в карточке номенклатуры.

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

Постановка задачи

Нам необходимо получить отчет, аналогичный «Ведомости по товарам на складах», который будет выполнять следующие функции:

  1. Выводить начальный остаток, приход, расход и конечный остаток товаров.
  2. Пересчитывать все количественные показатели из единиц хранения остатков в базовые единицы измерения номенклатуры.
  3. Получать актуальную на дату отчета цену номенклатуры по определенному типу цен (например, «Базовая»).
  4. Рассчитывать стоимость остатков и оборотов как произведение полученного базового количества на эту цену.

---

Шаг 1. Разбор формулы пересчета в базовые единицы измерения

В платформе 1С количество в регистрах накопления (таких как ТоварыНаСкладах или ПартииТоваровНаСкладах) хранится в единицах хранения остатков. Каждая единица измерения номенклатуры имеет свой коэффициент относительно базовой единицы (у самой базовой единицы коэффициент всегда равен 1).

Чтобы перевести количество из единиц хранения в базовые единицы, мы должны использовать следующую математическую формулу:

Количество (базовое) = Количество (в единицах хранения) * Коэффициент единицы хранения

В запросе это выражается обращением к реквизитам номенклатуры через точку:

ТоварыНаСкладахОстаткиИОбороты.КоличествоНачальныйОстаток * ТоварыНаСкладахОстаткиИОбороты.Номенклатура.ЕдиницаХраненияОстатков.Коэффициент ---

Шаг 2. Анализ и исправление критической ошибки соединения с регистром цен

Часто начинающие программисты пытаются получить цены простым соединением с физической таблицей регистра сведений ЦеныНоменклатуры, как показано в запросе ниже (типичная ошибка с форума):


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

Почему этот запрос категорически нельзя использовать в реальной базе?

  1. Проблема дублирования данных (Умножение остатков): Регистр ЦеныНоменклатуры является периодическим. Это значит, что для одной номенклатуры в нем может храниться история изменения цен за несколько лет (например, 10 записей). При соединении с физической таблицей СУБД вернет 10 строк для одного товара. В итоге СКД просуммирует остатки, и ваш отчет покажет завышенные в 10 раз данные!
  2. Проблема исчезновения товаров (Внутреннее соединение): Использование ВНУТРЕННЕГО СОЕДИНЕНИЯ приведет к тому, что если для какого-то товара на складе временно не установлена базовая цена, этот товар вообще пропадет из отчета. Пользователь не увидит даже его количественный остаток.

Как сделать правильно:
Для получения цен необходимо использовать виртуальную таблицу СрезПоследних, которая гарантированно возвращает ровно одну (самую актуальную на указанную дату) запись для каждой номенклатуры. Соединение обязательно должно быть ЛЕВЫМ.

---

Шаг 3. Создание правильного запроса для СКД

Спроектируем оптимальный запрос для схемы компоновки данных. Мы выберем остатки и обороты из регистра ТоварыНаСкладах, пересчитаем их с учетом коэффициента единицы хранения остатков и левым соединением подтянем цены из среза последних на конец периода отчета.


ВЫБРАТЬ
    ТоварыОбороты.Номенклатура КАК Номенклатура,
    ТоварыОбороты.Склад КАК Склад,
    
    // Пересчет количества в базовые единицы измерения
    ТоварыОбороты.КоличествоНачальныйОстаток * ЕСТЬNULL(ТоварыОбороты.Номенклатура.ЕдиницаХраненияОстатков.Коэффициент, 1) КАК КоличествоНачальныйОстатокБазовое,
    ТоварыОбороты.КоличествоПриход * ЕСТЬNULL(ТоварыОбороты.Номенклатура.ЕдиницаХраненияОстатков.Коэффициент, 1) КАК КоличествоПриходБазовое,
    ТоварыОбороты.КоличествоРасход * ЕСТЬNULL(ТоварыОбороты.Номенклатура.ЕдиницаХраненияОстатков.Коэффициент, 1) КАК КоличествоРасходБазовое,
    ТоварыОбороты.КоличествоКонечныйОстаток * ЕСТЬNULL(ТоварыОбороты.Номенклатура.ЕдиницаХраненияОстатков.Коэффициент, 1) КАК КоличествоКонечныйОстатокБазовое,
    
    // Получение цены из виртуальной таблицы среза последних
    ЕСТЬNULL(ЦеныСрезПоследних.Цена, 0) КАК БазоваяЦена
ПОМЕСТИТЬ ВТ_ДанныеТоваров
ИЗ
    РегистрНакопления.ТоварыНаСкладах.ОстаткиИОбороты(&НачалоПериода, &КонецПериода, Авто, , ) КАК ТоварыОбороты
    
    ЛЕВОЕ СОЕДИНЕНИЕ РегистрСведений.ЦеныНоменклатуры.СрезПоследних(&КонецПериода, ТипЦен = &ТипЦен) КАК ЦеныСрезПоследних
        ПО ТоварыОбороты.Номенклатура = ЦеныСрезПоследних.Номенклатура
;

////////////////////////////////////////////////////////////////////////////////
ВЫБРАТЬ
    ВТ_ДанныеТоваров.Номенклатура,
    ВТ_ДанныеТоваров.Склад,
    ВТ_ДанныеТоваров.КоличествоНачальныйОстатокБазовое,
    ВТ_ДанныеТоваров.КоличествоПриходБазовое,
    ВТ_ДанныеТоваров.КоличествоРасходБазовое,
    ВТ_ДанныеТоваров.КоличествоКонечныйОстатокБазовое,
    ВТ_ДанныеТоваров.БазоваяЦена,
    
    // Расчет стоимостных показателей по базовой цене
    ВТ_ДанныеТоваров.КоличествоНачальныйОстатокБазовое * ВТ_ДанныеТоваров.БазоваяЦена КАК СтоимостьНачальныйОстаток,
    ВТ_ДанныеТоваров.КоличествоПриходБазовое * ВТ_ДанныеТоваров.БазоваяЦена КАК СтоимостьПриход,
    ВТ_ДанныеТоваров.КоличествоРасходБазовое * ВТ_ДанныеТоваров.БазоваяЦена КАК СтоимостьРасход,
    ВТ_ДанныеТоваров.КоличествоКонечныйОстатокБазовое * ВТ_ДанныеТоваров.БазоваяЦена КАК СтоимостьКонечныйОстаток
ИЗ
    ВТ_ДанныеТоваров КАК ВТ_ДанныеТоваров

Обратите внимание, что условие отбора по типу цен ТипЦен = &ТипЦен мы поместили непосредственно в параметры виртуальной таблицы СрезПоследних. Это крайне важно для производительности СУБД: база данных сначала отфильтрует цены по нужному типу и только потом выполнит соединение, что значительно ускорит формирование отчета на больших базах данных.

---

Шаг 4. Настройка параметров и ресурсов в СКД

После того как вы вставили запрос в схему компоновки данных, перейдите к настройке полей и ресурсов:

  1. Вкладка «Ресурсы»: Для всех числовых полей (количества и стоимости) укажите функцию СУММА(). Полю БазоваяЦена назначать ресурс не нужно, так как суммировать цены по разным товарам не имеет математического смысла.
  2. Вкладка «Параметры»:
    • Для параметра &ТипЦен укажите тип данных СправочникСсылка.ТипыЦенНоменклатуры. Чтобы пользователю не приходилось выбирать тип цены вручную при каждом запуске, в колонке «Значение» можно сразу жестко указать предопределенный или конкретный элемент (например, найти и выбрать элемент «Базовая»).
    • Для параметров &НачалоПериода и &КонецПериода настройте понятное представление и при необходимости задайте стандартное выражение (например, начало текущего месяца и конец текущего месяца).

---

Шаг 5. Продвинутый вариант: Расчет стоимости на дату каждого движения

Проанализируем более сложную управленческую ситуацию, упомянутую в ходе обсуждения. Что делать, если цена в течение месяца менялась несколько раз, а пользователю нужно видеть корректную стоимость приходов и расходов именно на момент совершения конкретных операций (а не по финальной цене на конец месяца)?

Для решения этой задачи в СКД применяется механизм связи наборов данных:

  1. Создадим первый набор данных (запрос) НаборДанных_Товары, в котором выберем детальные записи движения товаров с периодичностью «Регистратор» или «День» из регистра накопления.
  2. Создадим второй набор данных (запрос) НаборДанных_Цены, который будет выбирать срез последних цен. В параметрах виртуальной таблицы укажем параметр &ПериодДействия.
  3. Перейдем на вкладку «Связи наборов данных» и свяжем их по полю Номенклатура.
  4. В качестве параметра связи для &ПериодДействия укажем поле Период (или Дата документа-регистратора) из первого набора.

При таком подходе СКД сама для каждой строки движения товара найдет цену, которая действовала именно на дату этого движения, и рассчитает точную историческую стоимость поступлений и списаний.

---

Альтернативное типовое решение

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

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

← На главную