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