Как выбрать последнюю цену из разных типов документов в одном запросе

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

В практике разработки на платформе 1С:Предприятие 8 часто возникает задача получения актуальных (последних по дате) цен из различных видов документов. Например, закупочная цена может фиксироваться как в документе ПоступлениеТоваровУслуг, так и в АвансовыйОтчет. Если пытаться решить эту задачу простым соединением табличных частей с таблицей продаж, мы неизбежно столкнемся с «задвоением» строк и некорректными результатами.

В данной статье мы разберем, как правильно спроектировать запрос для СКД или обычного отчета, чтобы отобрать ровно одну — самую свежую — цену для каждой позиции номенклатуры, учитывая при этом несколько типов документов-регистраторов.

Проблема использования вложенных запросов и объединений

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

Для корректного решения нам необходимо применить механизм временных таблиц и поэтапную группировку данных. Проанализируем этот подход подробнее.

Шаг 1: Сбор всех данных во временную таблицу

Первым делом нам необходимо собрать все возможные цены из всех интересующих нас документов в одну структуру. На этом этапе мы также приведем цены к единому стандарту (например, включим НДС, если он не включен в цену документа).

Рассмотрим пример формирования временной таблицы ВТ_ВсеЦены:


ВЫБРАТЬ
    ТЧ.Номенклатура КАК Номенклатура,
    ТЧ.Ссылка.Дата КАК Дата,
    ТЧ.Ссылка КАК Ссылка,
    ВЫБОР
        КОГДА ТЧ.Ссылка.СуммаВключаетНДС ТОГДА ТЧ.Цена
        ИНАЧЕ ТЧ.Цена + ТЧ.Цена * 20 / 100
    КОНЕЦ КАК Цена
ПОМЕСТИТЬ ВТ_ВсеЦены
ИЗ
    Документ.ПоступлениеТоваровУслуг.Товары КАК ТЧ
ГДЕ
    ТЧ.Ссылка.Проведен

ОБЪЕДИНИТЬ ВСЕ

ВЫБРАТЬ
    ТЧ.Номенклатура,
    ТЧ.Ссылка.Дата,
    ТЧ.Ссылка,
    ВЫБОР
        КОГДА ТЧ.Ссылка.СуммаВключаетНДС ТОГДА ТЧ.Цена
        ИНАЧЕ ТЧ.Цена + ТЧ.Цена * 20 / 100
    КОНЕЦ
ИЗ
    Документ.АвансовыйОтчет.Товары КАК ТЧ
ГДЕ
    ТЧ.Ссылка.Проведен
;

Важный момент: Мы используем ОБЪЕДИНИТЬ ВСЕ, чтобы не тратить ресурсы на поиск дублей на этом этапе, так как мы всё равно будем группировать данные далее. Не забудьте добавить условие Проведен, чтобы не учитывать черновики и помеченные на удаление документы.

Шаг 2: Определение самого свежего документа

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

Создадим вторую временную таблицу ВТ_МаксимальныеДаты:


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

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

Шаг 3: Получение финальной цены

Теперь мы соединяем наши временные таблицы, чтобы оставить только те записи из ВТ_ВсеЦены, которые соответствуют максимальным датам из ВТ_МаксимальныеДаты.


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

Обратите внимание на использование ИНДЕКСИРОВАТЬ ПО Номенклатура. Это критически важно для производительности, так как далее эта таблица будет соединяться с основными данными отчета (например, продажами).

Шаг 4: Соединение с основной таблицей отчета

На финальном этапе мы берем данные о продажах и левым соединением подтягиваем нашу актуальную цену. Посмотрим на итоговую структуру запроса:


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

Почему этот метод лучше?

Разберем основные преимущества использования временных таблиц по сравнению с подходом автора темы:

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

Рекомендации для СКД

Если вы работаете в Системе компоновки данных, вы можете использовать этот запрос как основной набор данных. Однако, если расчет цен требуется делать динамически в зависимости от периода строки, рассмотрите возможность выноса получения цены в отдельный Набор данных - запрос и свяжите их по номенклатуре. Это позволит СКД передавать параметр номенклатуры в подчиненный запрос, хотя метод с временными таблицами внутри одного запроса обычно работает быстрее.

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

← На главную