Как вывести цены для всей номенклатуры в «1С:Розница 2.3»: исправляем ошибки запроса и учитываем специфику базы данных

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

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

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

Где хранятся цены номенклатуры в «1С:Розница»?

Для начала определимся с источником данных. В отличие от некоторых других систем, где цена может быть простым реквизитом карточки товара, в 1С используется периодический регистр сведений. Это позволяет хранить историю изменения цен. В «1С:Розница 2.3» для этих целей предназначен регистр сведений ЦеныНоменклатуры. Он имеет несколько ключевых измерений:

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

Анализ классической ошибки при соединении таблиц

Рассмотрим реальную ситуацию. Начинающий разработчик пытается получить данные о номенклатуре и ценах с помощью временных таблиц (ВТ) и пишет следующий запрос:


ВЫБРАТЬ
    Номенклатура.Ссылка КАК Наименование,
    Номенклатура.Артикул КАК Артикул,
    Номенклатура.Родитель.Ссылка КАК ГруппаНоменклатуры
ПОМЕСТИТЬ ДанныеНоменклатурыВТ
ИЗ
    Справочник.Номенклатура КАК Номенклатура
ГДЕ
    Номенклатура.ЭтоГруппа = ЛОЖЬ
;

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

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

При выполнении этого запроса колонка с ценами оказывается абсолютно пустой (содержит значения NULL). Выясним причину этой проблемы. Здесь допущены сразу две грубые ошибки:

  1. Отсутствие поля связи во временной таблице цен. Во второй временной таблице (ЦенаНоменклатурыВТ) выбирается только одно поле — Цена. В этой таблице нет поля Номенклатура. Без него платформа физически не сможет сопоставить, какому именно товару принадлежит конкретная цена.
  2. Некорректное условие связи. В финальном запросе левое соединение происходит по условию:
    ПО ДанныеНоменклатурыВТ.Наименование.Ссылка = ЦенаНоменклатурыВТ.Цена.
    Здесь сравнивается ссылка на справочник Номенклатура с числовым значением цены (например, Ссылка на товар "Ботинки" сравнивается с числом 1500). Различные типы данных никогда не совпадут, поэтому связь не устанавливается.

Исправленный и оптимизированный вариант запроса

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

Проанализируем исправленный вариант запроса:


ВЫБРАТЬ
    Номенклатура.Ссылка КАК Номенклатура,
    Номенклатура.Артикул КАК Артикул,
    Номенклатура.Родитель.Ссылка КАК ГруппаНоменклатуры
ПОМЕСТИТЬ ДанныеНоменклатурыВТ
ИЗ
    Справочник.Номенклатура КАК Номенклатура
ГДЕ
    Номенклатура.ЭтоГруппа = ЛОЖЬ
ИНДЕКСИРОВАТЬ ПО
    Номенклатура
;

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

////////////////////////////////////////////////////////////////////////////////
ВЫБРАТЬ
    ДанныеНоменклатурыВТ.Номенклатура КАК Товар,
    ДанныеНоменклатурыВТ.Артикул КАК Артикул,
    ДанныеНоменклатурыВТ.ГруппаНоменклатуры КАК ГруппаНоменклатуры,
    ЕСТЬNULL(ЦенаНоменклатурыВТ.Цена, 0) КАК Цена
ИЗ
    ДанныеНоменклатурыВТ КАК ДанныеНоменклатурыВТ
        ЛЕВОЕ СОЕДИНЕНИЕ ЦенаНоменклатурыВТ КАК ЦенаНоменклатурыВТ
        ПО ДанныеНоменклатурыВТ.Номенклатура = ЦенаНоменклатурыВТ.Номенклатура

Обратите внимание на инструкцию ИНДЕКСИРОВАТЬ ПО. Индексирование временных таблиц по полям связи существенно ускоряет выполнение запроса СУБД, особенно если в вашей базе данных хранятся десятки тысяч наименований товаров.

Важные нюансы «1С:Розница 2.3», о которых нельзя забывать

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

1. Учет характеристик номенклатуры

В розничной торговле один и тот же товар (например, "Футболка мужская") может иметь разные характеристики (размер S, M, L или разные цвета). Очень часто цены устанавливаются не просто на товар, а на конкретное сочетание Товар + Характеристика.

Если мы не учтем характеристики в запросе, то:

Разберем по шагам, как правильно составить запрос с учетом характеристик. Нам нужно добавить поле Характеристика как в таблицу номенклатуры, так и в таблицу цен, а соединение выполнить по обоим полям:


ВЫБРАТЬ
    Номенклатура.Ссылка КАК Номенклатура,
    Номенклатура.Артикул КАК Артикул,
    Характеристики.Ссылка КАК Характеристика
ПОМЕСТИТЬ ДанныеНоменклатурыВТ
ИЗ
    Справочник.Номенклатура КАК Номенклатура
        ЛЕВОЕ СОЕДИНЕНИЕ Справочник.ХарактеристикиНоменклатуры КАК Характеристики
        ПО Характеристики.Владелец = Номенклатура.Ссылка
ГДЕ
    Номенклатура.ЭтоГруппа = ЛОЖЬ
ИНДЕКСИРОВАТЬ ПО
    Номенклатура,
    Характеристика
;

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

////////////////////////////////////////////////////////////////////////////////
ВЫБРАТЬ
    ДанныеНоменклатурыВТ.Номенклатура КАК Товар,
    ДанныеНоменклатурыВТ.Артикул КАК Артикул,
    ДанныеНоменклатурыВТ.Характеристика КАК Характеристика,
    ЕСТЬNULL(ЦенаНоменклатурыВТ.Цена, 0) КАК Цена
ИЗ
    ДанныеНоменклатурыВТ КАК ДанныеНоменклатурыВТ
        ЛЕВОЕ СОЕДИНЕНИЕ ЦенаНоменклатурыВТ КАК ЦенаНоменклатурыВТ
        ПО ДанныеНоменклатурыВТ.Номенклатура = ЦенаНоменклатурыВТ.Номенклатура
            И ДанныеНоменклатурыВТ.Характеристика = ЦенаНоменклатурыВТ.Характеристика

2. Как программно определить правильный «Вид цены»?

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

В «1С:Розница 2.3» связь между магазином и ценами реализована следующим образом:

  1. У каждого магазина (справочник Магазины) задано Правило ценообразования (справочник ПравилаЦенообразования).
  2. В правиле ценообразования указан конкретный Вид цен (например, "Розничная цена").

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


// Получаем розничный вид цены для выбранного магазина
ВидЦенМагазина = МагазинПараметр.ПравилоЦенообразования.ВидЦен;

Запрос = Новый Запрос;
Запрос.Текст = "ВЫБРАТЬ ... ИЗ РегистрСведений.ЦеныНоменклатуры.СрезПоследних(&Период, ВидЦены = &ВидЦены) ...";
Запрос.УстановитьПараметр("Период", ТекущаяДата());
Запрос.УстановитьПараметр("ВидЦены", ВидЦенМагазина);
РезультатЗапроса = Запрос.Выполнить();

Используя этот подход, вы всегда будете получать актуальные розничные цены, которые ваши кассиры видят на рабочих местах при продаже товаров.

← На главную