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

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

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

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

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

Рассмотрим ситуацию: у нас есть реализация от 1-го числа и реализация от 15-го числа. Курс доллара менялся 5-го числа. Если мы передадим в виртуальную таблицу РегистрСведений.КурсыВалют.СрезПоследних(&Дата, ...) параметр &Дата, равный 15-му числу, то для обоих документов придет курс от 5-го числа. Это будет ошибкой для первого документа. Нам же нужно, чтобы запрос для каждой строки нашел ближайшую предыдущую или совпадающую запись в регистре курсов валют.

Алгоритм решения задачи через временные таблицы

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

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

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


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

Шаг 2. Определение актуальных дат курсов валют

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


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

Важный момент: использование условия ВТ_Документы.ДатаДокумента >= КурсыВалют.Период вместе с функцией МАКСИМУМ позволяет нам найти именно ту запись курса, которая была актуальна на момент создания документа, даже если курс вводится не каждый день (как в ситуации автора темы — раз в две недели).

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

Когда у нас есть пары «Документ — Дата актуального курса», мы можем финальным соединением вытащить сами значения курса и кратности из регистра.


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

Разберем важные детали реализации

Проанализируем несколько критических аспектов, которые часто упускают начинающие разработчики:

  1. Кратность валюты: В системе 1С курс всегда неразрывно связан с кратностью. Если вы просто возьмете поле Курс, вы можете получить неверные данные для валют вроде японской иены или казахского тенге, где курс устанавливается за 10, 100 или 1000 единиц. Настоящий курс для расчетов вычисляется как Курс / Кратность.
  2. Обработка пустых значений (NULL): Если на дату документа в базе еще не было введено ни одного курса выбранной валюты, левое соединение вернет NULL. Использование функции ЕСТЬNULL(Курс, 1) страхует вас от ошибок деления на ноль и пустых ячеек в отчете.
  3. Производительность: Если документов тысячи, соединение «все со всеми» по условию >= может работать медленно. В таких случаях рекомендуется сначала отобрать уникальные даты документов во временную таблицу, найти для каждой уникальной даты её период курса, и только потом присоединять это к основному списку товаров.

Как это применить в коде 1С?

Чтобы реализовать это в вашем модуле, не забудьте правильно установить параметры запроса. Особенно это касается ссылки на валюту USD. Рассмотрим пример кода:


// Получаем ссылку на валюту USD (лучше по коду 840 или из констант)
ВалютаСсылка = Справочники.Валюты.НайтиПоКоду("840");

Запрос = Новый Запрос;
Запрос.Текст = "ВЫБРАТЬ ... (текст запроса из шагов выше) ...";

Запрос.УстановитьПараметр("Контрагент", КонтрагентСсылка);
Запрос.УстановитьПараметр("Дата", ДатаОтсечения);
Запрос.УстановитьПараметр("ВалютаUSD", ВалютаСсылка);

Результат = Запрос.Выполнить();

Альтернативный способ (программный обход)

Если запрос кажется слишком сложным или объем данных невелик (например, всего 10-20 строк в табличной части), можно воспользоваться программным методом в цикле обхода результата запроса. В типовой конфигурации УТ 10.3 существует общий модуль РаботаСКурсамиВалют. Вы можете вызывать функцию РаботаСКурсамиВалют.ПолучитьКурсВалюты(ВалютаСсылка, ДатаДокумента). Однако помните, что вызов функции в цикле — это всегда дополнительные обращения к базе данных, что значительно замедляет работу системы при больших объемах данных.

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

← На главную