При работе с регистром сведений ЦеныНоменклатуры часто возникает задача сравнить цену из текущего документа с ранее установленной ценой на этот же товар. Однако при обращении к виртуальной таблице СрезПоследних разработчики часто сталкиваются с ситуацией, когда одна и та же номенклатура повторяется в результате несколько раз по количеству зарегистрированных типов цен.
В этой статье разберем причин возникновения такой проблемы, проанализируем особенности работы виртуальных таблиц и рассмотрим рабочие способы построения запроса для получения единственной последней цены.
Проанализируем работу платформы 1С при обращении к виртуальной таблице СрезПоследних. Данная таблица формирует срез сведений строго в разрезе всех измерений регистра сведений.
В большинстве типовых конфигураций измерением регистра ЦеныНоменклатуры является не только Номенклатура, но и ТипЦен (или ВидЦены). Если в параметрах или условиях запроса указать условие ТипЦен <> &ТипЦен, система просто исключит конкретный тип цены (например, закупочную), но для всех остальных типов цен вернет актуальные на момент среза значения. В результате для одной позиции номенклатуры в выборку попадет столько строк, сколько активных типов цен хранится в регистре.
Разберем по шагам, как получить ровно одну последнюю цену на номенклатуру, исключив закупочный тип цены и записи самого проверяемого документа.
Сначала поместим товары из документа во временную таблицу, чтобы в дальнейшем делать выборку только по нужным позициям. Затем определим максимальную дату установки цены для каждого товара и соединим полученные данные с регистром сведений.
Рассмотрим пример пакетного запроса:
// Шаг 1: Получаем товары из документа
ВЫБРАТЬ
УстановкаЦенНоменклатурыТовары.Номенклатура КАК Номенклатура,
УстановкаЦенНоменклатурыТовары.ТипЦен КАК ТипЦен,
УстановкаЦенНоменклатурыТовары.Цена КАК Цена
ПОМЕСТИТЬ ВТ_Док
ИЗ
Документ.УстановкаЦенНоменклатуры.Товары КАК УстановкаЦенНоменклатурыТовары
ГДЕ
УстановкаЦенНоменклатурыТовары.Ссылка = &Ссылка
И УстановкаЦенНоменклатурыТовары.ТипЦен = &ТипЦенДокумента;
////////////////////////////////////////////////////////////////////////////////
// Шаг 2: Находим максимальную дату установки цены по номенклатуре из документа
ВЫБРАТЬ
Цены.Номенклатура КАК Номенклатура,
МАКСИМУМ(Цены.Период) КАК Период
ПОМЕСТИТЬ ВТ_Периоды
ИЗ
РегистрСведений.ЦеныНоменклатуры КАК Цены
ГДЕ
Цены.Номенклатура В (ВЫБРАТЬ РАЗЛИЧНЫЕ ВТ_Док.Номенклатура ИЗ ВТ_Док КАК ВТ_Док)
И Цены.ТипЦен <> &ТипЦенИсключаемый
И Цены.Регистратор <> &Ссылка
СГРУППИРОВАТЬ ПО
Цены.Номенклатура;
////////////////////////////////////////////////////////////////////////////////
// Шаг 3: Получаем максимальную цену на найденную максимальную дату
ВЫБРАТЬ
Цены.Номенклатура КАК Номенклатура,
Цены.Период КАК Период,
МАКСИМУМ(Цены.Цена) КАК Цена
ПОМЕСТИТЬ ВТ_ПоследняяЦена
ИЗ
ВТ_Периоды КАК Периоды
ВНУТРЕННЕЕ СОЕДИНЕНИЕ РегистрСведений.ЦеныНоменклатуры КАК Цены
ПО Периоды.Номенклатура = Цены.Номенклатура
И Периоды.Период = Цены.Период
ГДЕ
Цены.ТипЦен <> &ТипЦенИсключаемый
И Цены.Регистратор <> &Ссылка
СГРУППИРОВАТЬ ПО
Цены.Номенклатура,
Цены.Период;
////////////////////////////////////////////////////////////////////////////////
// Шаг 4: Сравниваем цену из документа с ранее установленной ценой
ВЫБРАТЬ
Док.Номенклатура КАК Номенклатура,
Док.Цена КАК ЦенаДокумента,
ПоследняяЦена.Цена КАК ПредыдущаяЦена
ИЗ
ВТ_Док КАК Док
ВНУТРЕННЕЕ СОЕДИНЕНИЕ ВТ_ПоследняяЦена КАК ПоследняяЦена
ПО Док.Номенклатура = ПоследняяЦена.Номенклатура
И Док.Цена > ПоследняяЦена.Цена
Разберем логику работы этого пакета запросов:
ВТ_Док: выбирает товары текущего документа и фиксирует исходные цены.ВТ_Периоды: обращается к физической таблице регистра сведений РегистрСведений.ЦеныНоменклатуры, отсекает ненужные типы цен и текущий документ-регистратор, после чего находит самую свежую дату установки цены (МАКСИМУМ(Период)) по каждой позицией.ВТ_ПоследняяЦена: соединяет найденную дату с физической таблицей регистра сведений. Если в этот конкретный день по номенклатуре было установлено несколько цен разного типа, агрегатная функция МАКСИМУМ(Цена) сжимает результат до единой строки.В современной платформе 1С (начиная с версии 8.3.20) реализована поддержка оконных функций. Это позволяет существенно упростить выборку последней записи без создания цепочки промежуточных соединений.
Посмотрим на пример с использованием функции ROW_NUMBER():
ВЫБРАТЬ
Цены.Номенклатура КАК Номенклатура,
Цены.Цена КАК Цена,
ROW_NUMBER() OVER(PARTITION BY Цены.Номенклатура ОБРАБОТКА ПО Цены.Период УБЫВ) КАК НомерСтроки
ПОМЕСТИТЬ ВТ_РанжированныеЦены
ИЗ
РегистрСведений.ЦеныНоменклатуры КАК Цены
ГДЕ
Цены.Номенклатура В (ВЫБРАТЬ РАЗЛИЧНЫЕ ВТ_Док.Номенклатура ИЗ ВТ_Док)
И Цены.ТипЦен <> &ТипЦенИсключаемый
И Цены.Регистратор <> &Ссылка;
////////////////////////////////////////////////////////////////////////////////
ВЫБРАТЬ
Ранги.Номенклатура,
Ранги.Цена КАК ПоследняяЦена
ПОМЕСТИТЬ ВТ_Срез
ИЗ
ВТ_РанжированныеЦены КАК Ранги
ГДЕ
Ранги.НомерСтроки = 1
В данном подходе конструкция PARTITION BY Цены.Номенклатура ORDER BY Цены.Период DESC нумерует все записи по каждой номенклатуре в порядке убывания даты. Самая свежая запись получает номер 1. Фильтрация по условию НомерСтроки = 1 гарантирует получение строго одной актуальной цены для каждой позиции номенклатуры.