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

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

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

Причина проблемы: почему обычное соединение не работает

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

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

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

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


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

Шаг 2: Находим даты последних закупок. Теперь для каждой номенклатуры из нашей временной таблицы ВТ_СписокНоменклатуры нужно найти дату самого свежего документа. Для этого используем функцию МАКСИМУМ.


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

Шаг 3: Получаем цену из конкретного документа. Имея на руках пару "Номенклатура — Дата последней закупки", мы можем соединиться с исходной таблицей товаров еще раз, чтобы вытащить цену именно из того документа, дата которого совпала с максимальной.


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

Важное уточнение: использование Момента времени

Проанализируем ситуацию, озвученную опытными разработчиками: что если в одну и ту же секунду (на одну дату) введены два разных заказа с разными ценами? В этом случае метод с датой вернет две строки. Чтобы этого избежать, вместо Ссылка.Дата рекомендуется использовать Ссылка.МоментВремени. Момент времени в 1С гарантирует уникальность и правильную последовательность документов. Однако стоит помнить, что в группировках МАКСИМУМ от момента времени работает корректно, позволяя выбрать именно физически последний документ.

Метод 2: Использование регистров сведений (СрезПоследних)

Если в вашей конфигурации (например, УТ 11, ERP, КА) ведется запись цен в регистр сведений (например, ЦеныНоменклатурыПоставщиков), задача упрощается в разы. Выясним, почему это предпочтительнее. Виртуальная таблица СрезПоследних спроектирована специально для получения последних данных на указанную точку времени. Это работает значительно быстрее, чем сканирование табличных частей документов.

Пример запроса к регистру:


ВЫБРАТЬ
    Список.Номенклатура,
    ЕСТЬNULL(ЦеныСрезПоследних.Цена, 0) КАК Цена
ИЗ
    ВТ_СписокНоменклатуры КАК Список
        ЛЕВОЕ СОЕДИНЕНИЕ РегистрСведений.ЦеныНоменклатурыПоставщиков.СрезПоследних(&Период, ) КАК ЦеныСрезПоследних
        ПО Список.Номенклатура = ЦеныСрезПоследних.Номенклатура

Важный момент: если вы используете характеристики номенклатуры, обязательно добавляйте связь по полю Характеристика, иначе вы получите усредненную или неактуальную цену без учета спецификации товара.

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

Если вы работаете на современной версии платформы, мы можем применить оконные функции. Это самый элегантный способ, позволяющий избежать создания трех временных таблиц. Мы пронумеруем все покупки для каждой номенклатуры в порядке убывания даты и выберем только первую строку.


ВЫБРАТЬ
    Закупки.Номенклатура,
    Закупки.Цена,
    ROW_NUMBER() OVER (PARTITION BY Закупки.Номенклатура УПОРЯДОЧИТЬ ПО Закупки.Ссылка.Дата УБЫВ) КАК НомерСтроки
ПОМЕСТИТЬ ВТ_ПронумерованныеЦены
ИЗ
    Документ.ЗаказПоставщику.Товары КАК Закупки;

////////////////////////////////////////////////////////////////////////////////
ВЫБРАТЬ
    Номенклатура,
    Цена
ИЗ
    ВТ_ПронумерованныеЦены
ГДЕ
    НомерСтроки = 1

Дополнительные факторы успеха

При решении задачи "последней цены" всегда учитывайте следующие нюансы:

  1. Валюта. Цены в заказах могут быть в разных валютах (USD, EUR, RUB). При расчете бюджета или выводе в отчет необходимо приводить их к единой валюте через регистр КурсыВалют на дату документа.
  2. Единицы измерения. Убедитесь, что цена указана за одну и ту же единицу (штуки, коробки, килограммы). Если в документах используются разные упаковки, цену нужно пересчитывать через коэффициенты.
  3. Статус документа. Всегда проверяйте условие Проведен = ИСТИНА. Помеченные на удаление или просто записанные документы не должны влиять на расчет актуальной цены.
  4. Тип цен. В случае работы с регистрами, уточните, по какому типу цен (или по какому поставщику) нужно получить данные. Часто требуется цена "любого последнего поставщика", но иногда — только конкретного.

Таким образом, выбор метода зависит от версии вашей платформы и структуры метаданных конфигурации. Для старых систем оптимален метод трех таблиц, для новых — оконные функции, а для типовых решений — работа с виртуальными таблицами регистров сведений.

← На главную