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

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

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

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

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

Алгоритм "Срез последних на каждую дату"

Для решения этой задачи мы воспользуемся классическим трехэтапным алгоритмом. Проанализируем его шаги:

  1. Получение уникальных пар "Объект + Дата". Сначала мы выбираем все сочетания контрагентов (или номенклатуры) и дат, на которые нам нужно получить цены.
  2. Поиск даты последней установки цены. Для каждой полученной пары мы находим максимальную дату в регистре сведений, которая меньше или равна дате нашего документа.
  3. Получение самих значений. Соединяем найденные даты с реальной таблицей регистра сведений, чтобы вытащить значения ресурсов (цену, ставку и т.д.).

Рассмотрим реализацию через временные таблицы

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


// ШАГ 1 и 2: Находим максимальные даты цен для каждой записи документа
ВЫБРАТЬ
    ЗаданияНаРаботу.Контрагент КАК Контрагент,
    ЗаданияНаРаботу.Период КАК Период,
    МАКСИМУМ(ТабЦен.Период) КАК ДатаУстановкиЦены
ПОМЕСТИТЬ ДатаУстановкиЦенПоКонтрагенту
ИЗ
    РегистрНакопления.ЗаданияНаРаботу КАК ЗаданияНаРаботу
        ЛЕВОЕ СОЕДИНЕНИЕ РегистрСведений.Расш1_Прайс КАК ТабЦен
        ПО (ТабЦен.Период <= ЗаданияНаРаботу.Период)
            И ЗаданияНаРаботу.Контрагент = ТабЦен.Контрагент
ГДЕ
    ЗаданияНаРаботу.Период МЕЖДУ &ДатаНачала И &ДатаОкончания
СГРУППИРОВАТЬ ПО
    ЗаданияНаРаботу.Контрагент,
    ЗаданияНаРаботу.Период 
;

// ШАГ 3: Получаем финальные данные с подставленными ценами
ВЫБРАТЬ
    ЗаданияНаРаботу.Контрагент КАК Контрагент,
    ЗаданияНаРаботу.Номенклатура КАК Номенклатура,
    ЗаданияНаРаботу.Сотрудник КАК Сотрудник,
    ЗаданияНаРаботу.ЗагрузкаФакт КАК ЗагрузкаФакт,
    ТабЦен.БесплатноЧасов КАК ЗначениеЧасы,
    ТабЦен.Сопр КАК ЗначениеСопр,
    ТабЦен.Разраб КАК ЗначениеРазраб,
    ЗаданияНаРаботу.Период КАК Период
ИЗ
    РегистрНакопления.ЗаданияНаРаботу КАК ЗаданияНаРаботу
        ЛЕВОЕ СОЕДИНЕНИЕ ДатаУстановкиЦенПоКонтрагенту КАК ВТ_Даты
        ПО ЗаданияНаРаботу.Контрагент = ВТ_Даты.Контрагент
            И ЗаданияНаРаботу.Период = ВТ_Даты.Период
        ЛЕВОЕ СОЕДИНЕНИЕ РегистрСведений.Расш1_Прайс КАК ТабЦен
        ПО (ВТ_Даты.Контрагент = ТабЦен.Контрагент)
            И (ВТ_Даты.ДатаУстановкиЦены = ТабЦен.Период)

Проанализируем этот код подробнее. В первой части запроса мы соединяем таблицу накопления с физической (не виртуальной!) таблицей регистра сведений. Ключевое условие здесь — ТабЦен.Период <= ЗаданияНаРаботу.Период. Это позволяет для каждой записи документа найти все записи цен, которые были внесены до него. С помощью функции МАКСИМУМ(ТабЦен.Период) мы отсекаем все лишние старые записи, оставляя только одну — самую "свежую" на момент документа.

Важные нюансы и оптимизация производительности

При использовании данного метода на больших объемах данных (десятки тысяч строк) можно столкнуться с замедлением работы. Выясним причину: соединение по условию "меньше или равно" заставляет сервер базы данных выполнять большой объем сравнений. Чтобы оптимизировать процесс, рекомендуем придерживаться следующих правил:

Альтернативный способ: Система компоновки данных (СКД)

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

  1. Создайте первый набор данных (Запрос), который выбирает записи из регистра накопления.
  2. Создайте второй набор данных (Запрос), который выбирает данные из виртуальной таблицы РегистрСведений.Прайс.СрезПоследних(&Период, ).
  3. На закладке "Связи наборов данных" свяжите их по измерению (например, Контрагент).
  4. В колонке "Параметры связи" для параметра Период второго набора укажите поле ДатаДокумента из первого набора.

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

Работа с измерениями регистра

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

Подведем итог. Получение цены на дату документа — это классическая задача, требующая понимания работы временных таблиц и группировок. Использование метода с поиском МАКСИМУМ(Период) является универсальным и работает в любой версии 1С 8.x, обеспечивая корректность данных вне зависимости от того, как часто менялись цены в периоде отчета.

← На главную