Как получить цену последнего и предпоследнего поступления в запросе 1С

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

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

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

Часто начинающие разработчики пытаются получить последнюю цену простым использованием функции МАКСИМУМ к дате и группировкой по цене. Проанализируем типичный пример кода, который не будет работать корректно:


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

Выясним причину, почему этот запрос неверен. При включении поля Цена в группировку, система выдаст вам уникальные комбинации номенклатуры и цены. Если товар приходил 10 раз по 5 разным ценам, вы получите 5 строк, для каждой из которых будет указана максимальная дата именно для этой цены. Это не даст нам последнюю закупку. Правильный алгоритм всегда должен состоять из двух этапов: сначала поиск целевой даты, а затем получение данных (цены) именно по этой дате.

Метод 1: Классический способ через временные таблицы (двойной максимум)

Этот метод является самым надежным и работает на любых версиях платформы 1С 8. Разберем его по шагам.

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


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

Шаг 2. Получение даты предпоследнего поступления. Теперь нам нужно найти максимум среди тех документов, которые были раньше найденного последнего момента. Создадим таблицу ВТ_ПредпоследниеДаты.


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

Шаг 3. Финальная сборка данных. Теперь соединим наши временные таблицы с исходными данными документов, чтобы вытащить цены для обеих дат.


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

Метод 2: Использование оконных функций (Платформа 8.3.20+)

Если ваша база работает на платформе версии 8.3.20 или выше, задачу можно решить гораздо элегантнее и быстрее с помощью функции ПОРЯДКОВЫЙ_НОМЕР(). Посмотрим на пример такого подхода:

Разберем логику: мы нумеруем строки для каждой номенклатуры, сортируя их по убыванию даты. Строка с номером 1 будет последним поступлением, с номером 2 — предпоследним.


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

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

Этот способ значительно производительнее, так как СУБД делает меньше проходов по таблицам по сравнению с методом вложенных максимумов.

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

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

  1. Проведенность документов: Никогда не забывайте проверку Ссылка.Проведен = ИСТИНА. В табличных частях хранятся данные и непроведенных, и помеченных на удаление документов.
  2. Момент времени против Даты: Если два поступления созданы в одну и ту же секунду, использование просто Дата приведет к неопределенности. Используйте МоментВремени в запросе (в языке запросов это пара Дата + Ссылка) для однозначной идентификации последовательности.
  3. Дубли в одном документе: Если в одном документе поступления одна и та же номенклатура указана двумя строками с разной ценой, обычное соединение по ссылке выдаст «раздувание» результата. В таких случаях перед поиском цен нужно предварительно сгруппировать данные внутри одного документа (например, взять среднюю или последнюю цену в рамках позиции).
  4. Единицы измерения: Помните, что цена в документе привязана к единице измерения. Если товар в одном случае пришел в штуках, а в другом в коробках, прямое сравнение цен будет некорректным без приведения к базовой единице через коэффициент.

Метод 3: Использование подзапроса ПЕРВЫЕ 2

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


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

Такой запрос вернет две строки: первую с текущей ценой и вторую с предыдущей. Однако помните, что использовать такой подход внутри цикла или как вложенный запрос для большого списка товаров — плохая практика с точки зрения производительности.

Подведем итог: для массовых отчетов на старых платформах используйте метод «двойного максимума» с временными таблицами. Если конфигурация позволяет использовать последние обновления платформы — выбирайте оконные функции, это сделает ваш код более читаемым и быстрым.

← На главную