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