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

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

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

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

Почему нельзя просто передать дату документа в срез последних

Параметры виртуальных таблиц в языке запросов 1С вычисляются СУБД до выполнения основного соединения. В параметры виртуальной таблицы СрезПоследних(&Период, ...) можно передавать только константы или внешние параметры запроса, но нельзя передать поле другой таблицы (например, ПродажиОбороты.Период или ПродажиОбороты.Регистратор.Дата).

Кроме того, проанализируем важную ошибку в исходном запросе автора:


ЛЕВОЕ СОЕДИНЕНИЕ РегистрСведений.ЦеныНоменклатуры.СрезПоследних(&ДатаКон, ) КАК ЦеныНоменклатурыСрезПоследних
ПО ПродажиОбороты.Номенклатура = ЦеныНоменклатурыСрезПоследних.Номенклатура
...
ГДЕ
    ЦеныНоменклатурыСрезПоследних.ТипЦен = &ТипЦен

Важный момент: условие по присоединенной таблице в секции ГДЕ неявно превращает ЛЕВОЕ СОЕДИНЕНИЕ во внутреннее (INNER JOIN). Если для какой-либо номенклатуры цена заданного типа не найдена, вся строка продажи пропадет из отчета. Условие по типу цен следует переносить либо в блок ПО, либо в параметры виртуальной таблицы: СрезПоследних(&Дата, ТипЦен = &ТипЦен).

Способ 1. Получение среза на каждую дату через временные таблицы

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

  1. Формирование уникальных пар «Номенклатура – Период». Сохраняем во временную таблицу только уникальные сочетания товара и даты совершения операции (даты регистратора), обязательно индексируя результат.
  2. Определение максимальной даты установки цены. Соединяем временную таблицу с физической таблицей регистра сведений РегистрСведений.ЦеныНоменклатуры по условию, что дата записи регистра меньше либо равна дате документа, и вычисляем МАКСИМУМ(Период).
  3. Получение результирующих цен. Соединяем полученные максимальные даты с физической таблицей регистра по точному совпадению номенклатуры и периода для извлечения значения цены.

Посмотрим на практическую реализацию этого механизма в коде запроса:


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

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

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

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

Учет периодичности регистра сведений

Обратим внимание на периодичность регистра ЦеныНоменклатуры. В большинстве типовых конфигураций (например, «Управление торговлей» редакции 10.3 или «Управление производственным предприятием») регистр цен имеет периодичность День. Это означает, что время всех записей регистра приведено к значению 00:00:00.

Если дата регистратора содержит время (например, 15.05.2023 14:35:10), простое сравнение Цены.Период <= ВТ.Период корректно найдет цену, установленную в этот день. Однако для надежности и ускорения сопоставления рекомендуется нормализовать даты документов с помощью функции НАЧАЛОПЕРИОДА(ПродажиОбороты.Период, ДЕНЬ).

Способ 2. Использование связей наборов данных в СКД

Если отчет строится на базе Системы компоновки данных (СКД), строить тяжелые пакеты временных таблиц не требуется. Платформа предоставляет штатную возможность передавать параметры в виртуальную таблицу для каждой строки другого набора данных.

Разберем настройку по шагам:

  1. Создадим НаборДанных1 (Запрос), в котором выберем продажи, номенклатуру и дату документа:
    
    ВЫБРАТЬ
        ПродажиОбороты.Номенклатура КАК Номенклатура,
        ПродажиОбороты.Регистратор КАК Регистратор,
        ПродажиОбороты.Период КАК Период,
        ПродажиОбороты.КоличествоОборот КАК Количество,
        ПродажиОбороты.СтоимостьОборот КАК Стоимость
    ИЗ
        РегистрНакопления.Продажи.Обороты(&ДатаНач, &ДатаКон, Регистратор, ) КАК ПродажиОбороты
    
  2. Создадим НаборДанных2 (Запрос) к срезу последних с обязательным параметром даты:
    
    ВЫБРАТЬ
        Цены.Номенклатура КАК Номенклатура,
        Цены.Цена КАК Цена
    ИЗ
        РегистрСведений.ЦеныНоменклатуры.СрезПоследних(&ПериодСреза, ТипЦен = &ТипЦен) КАК Цены
    
  3. Перейдем на вкладку Связи наборов данных и добавим новую строку:
    • Источник связи: НаборДанных1
    • Приемник связи: НаборДанных2
    • Условие связи: Номенклатура = Номенклатура
    • Параметры: в колонке параметров укажем имя ПериодСреза, а в выражении источника укажем Период (или Регистратор.Дата).

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

← На главную