Как получить последнюю цену в 1С без дублей по типам цен: подробное руководство

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

В практике разработки на платформе 1С:Предприятие часто возникает задача получения актуальной цены товара. Однако стандартная виртуальная таблица СрезПоследних регистра сведений ЦеныКонтрагентов (или аналогичных) часто выдает «лишние» строки. Это происходит потому, что система возвращает последние записи в разрезе всех измерений регистра. Если у одной номенклатурной позиции заданы цены с разными типами (например, «Оптовая», «Закупочная», «Спеццена»), запрос выдаст по одной строке для каждого типа, даже если нам нужна просто самая последняя цена, зафиксированная в системе.

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

Почему «СрезПоследних» выдает несколько строк?

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

  1. Измерение: Номенклатура
  2. Измерение: ТипЦены (или ВидЦены)
  3. Измерение: Контрагент
  4. Ресурс: Цена

Если для «Номенклатуры 1» вчера была установлена «Закупочная цена», а сегодня — «Оптовая», то срез последних на текущую дату вернет обе записи, так как комбинации {Номенклатура 1, Закупочная} и {Номенклатура 1, Оптовая} являются уникальными ключами в регистре. Чтобы получить только одну, самую последнюю запись, нам придется использовать механизмы группировки и временных таблиц.

Решение 1: Использование временной таблицы и группировки по периоду

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

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


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

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

Шаг 2. Получение данных о цене. Теперь соединим нашу временную таблицу с реальными данными регистра, чтобы вытащить значение цены.


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

Решение 2: Проблема коллизий и использование Регистратора

Проанализируем ситуацию, когда в одну и ту же секунду (один и тот же период) по одной номенклатуре были введены две разные цены (например, разными документами или в рамках одного документа по разным типам цен). В этом случае даже первый метод вернет две строки, так как условие ВТ_МаксимальныеДаты.Период = ЦеныКонтрагентов.Период сработает для обоих записей.

Для однозначного определения последней записи в 1С рекомендуется использовать МоментВремени или ссылку на Регистратор. Поскольку в запросах к обычным таблицам регистров сведений мы можем агрегировать ссылки, используем МАКСИМУМ(Регистратор) (система сравнивает ссылки по их внутреннему идентификатору Guid, что в большинстве случаев позволяет выбрать «самый свежий» документ среди созданных одновременно).


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

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

Решение 3: Бизнес-логика выбора цены (Приоритеты)

Иногда технически «последняя» цена не является корректной с точки зрения бизнеса. Например, если в один день установлена «Закупочная» и «Розничная» цена, возможно, нам всегда нужно отдавать приоритет «Розничной», независимо от времени записи. В таких случаях мы можем использовать конструкцию ВЫБОР КОГДА внутри агрегатных функций или дополнительные фильтры.

Посмотрим на пример, как можно выбрать максимальную цену, если периоды совпадают:


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

Здесь мы применяем функцию МАКСИМУМ(Цена). Это простейший способ избавиться от дублей, если нам не принципиально, из какого именно типа цен взято значение, лишь бы оно было самым большим на актуальную дату.

Важные нюансы при работе с ценами

Разберем еще несколько профессиональных советов, которые помогут сделать ваш запрос стабильным и быстрым:

  1. Фильтрация нулевых цен: Часто в 1С цена «обнуляется» для того, чтобы прекратить действие прайс-листа. Если вы хотите получить последнюю действующую цену, обязательно добавьте в запрос условие ГДЕ Цена > 0. В противном случае срез последних может вернуть 0, хотя исторически цена была задана.
  2. Учет характеристик: Если в вашей программе ведется учет в разрезе характеристик номенклатуры, их обязательно нужно включать во все группировки и условия соединений. Иначе вы получите «среднюю температуру по больнице», смешав цены разных размеров или цветов одного товара.
  3. Производительность: Старайтесь всегда ограничивать выборку по списку номенклатуры в параметрах виртуальной таблицы или в секции ГДЕ. Запрос «всех цен на все товары» в крупной базе может выполняться крайне долго и вызывать блокировки таблиц.

Подведем итог: Для получения единственной цены без дублей по типам цен, нам необходимо отказаться от стандартного использования СрезПоследних в чистом виде. Вместо этого мы используем двухэтапный запрос с выделением максимального периода (и, при необходимости, регистратора) во временную таблицу. Это решение является универсальным для любых конфигураций на платформе 1С v8.3, будь то «Управление торговлей», «ERP» или «Бухгалтерия».

← На главную