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

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

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

Анализ проблемы: почему стандартные методы не всегда работают

Рассмотрим ситуацию: у нас есть список документов РеализацияТоваровУслуг за разные даты. Курс валюты (например, USD) может меняться ежедневно, а может не меняться несколько дней. Если документ создан 03.01.2009, а последняя запись в регистре сведений КурсыВалют датирована 01.01.2009, система должна «подтянуть» именно этот курс. Мы не можем просто соединить таблицы по дате, так как прямого соответствия (равенства дат) может не существовать.

Проанализируем первую попытку решения, которая часто встречается у новичков. Использование таблицы РегламентированныйПроизводственныйКалендарь для «заполнения дырок» в датах является избыточным. Регистр сведений КурсыВалют сам по себе является периодическим, и механизмы запросов 1С позволяют найти последнюю запись перед указанной датой без привлечения календаря. Лишние соединения только замедляют выполнение кода, особенно в крупных базах.

Шаг 1: Подготовка данных и выбор уникальных дат

Первым делом нам необходимо оптимизировать будущий запрос. Если в выборке 1000 документов, но все они созданы в рамках трех дней, нет смысла 1000 раз искать курс в регистре. Правильнее будет сначала выделить уникальные пары «Дата» и «Валюта». Рассмотрим, как это сделать во временной таблице.


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

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

Шаг 2: Получение дат актуальных курсов

Теперь нам нужно выяснить, на какой именно момент времени (период) в регистре КурсыВалют зафиксирован последний курс для каждой нашей даты. Для этого мы используем соединение по условию «меньше или равно» и группировку по максимуму.


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

Разберем этот механизм подробнее. Мы берем дату документа и «просим» систему показать все записи из регистра КурсыВалют, которые были сделаны до этой даты включительно. Из всего этого набора дат мы выбираем самую позднюю (МАКСИМУМ), которая и будет являться точкой актуального курса для нашего документа.

Шаг 3: Получение значений курса и кратности

Имея на руках таблицу, где каждой дате документа соответствует конкретная дата записи в регистре, мы можем получить сами значения. Выясним одну важную деталь: в 1С курс валюты всегда должен рассматриваться вместе с кратностью. Если курс иены равен 80, а кратность 100, то реальная цена единицы валюты — 0.8. Никогда не забывайте про поле Кратность.


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

Использование функции ЕСТЬNULL позволяет нам избежать проблем, если курс на какую-то дату не был задан вовсе. В таком случае мы принимаем курс и кратность за единицу, чтобы не вызвать ошибку деления на ноль при расчетах.

Использование коррелированных подзапросов

В современных версиях платформы (8.3.18+) и при использовании MS SQL Server часто обсуждается применение коррелированных подзапросов (так называемые «запросы в цикле»). На больших объемах данных и при правильных индексах СУБД может оптимизировать такой запрос через OUTER APPLY. Однако классический метод с временными таблицами и МАКСИМУМ(Период), который мы разобрали выше, остается «золотым стандартом» — он одинаково стабильно работает и на файловых базах, и на PostgreSQL, и на старых версиях платформы.

Практические советы при работе с курсами

  1. Всегда проверяйте кратность. Расчет суммы в валюте всегда должен выглядеть так: Сумма * (Курс / Кратность).
  2. Ограничивайте выборку. Не пытайтесь получить курсы сразу за все года. Всегда используйте фильтры по конкретным документам или периодам.
  3. Типовые механизмы. Если вы работаете в конфигурациях БП 3.0, УТ 11 или ERP, обратите внимание на общие модули, такие как РаботаСКурсамиВалют. В них часто реализованы функции, которые позволяют получить таблицу курсов одной строкой кода, используя уже оптимизированные запросы от разработчиков фирмы 1С.
  4. Кросс-курсы. Если вам нужно пересчитать одну иностранную валюту в другую, процедура усложняется: сначала нужно получить курс обеих валют к рублю, а затем вычислить их соотношение.

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

← На главную