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

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

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

Разберем проблему: почему обычный «СрезПоследних» не подходит

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

Метод 1: Оптимальный алгоритм через временные таблицы

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

Шаг 1. Получаем данные о продажах. Сначала мы выделим все уникальные даты, на которые нам нужно найти курсы. Использование регистра накопления Продажи вместо прямого обращения к документам — это признак хорошего тона и залог производительности системы.


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

Шаг 2. Находим максимальную дату курса для каждой продажи. Теперь нам нужно для каждой даты продажи найти ближайшую к ней дату в регистре курсов, которая меньше или равна дате продажи. Мы используем функцию МАКСИМУМ для поиска этой точки во времени.


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

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


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

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

Если задача выполняется в Системе компоновки данных, мы можем пойти по пути «Low-code» и использовать механизм связей наборов данных. Этот метод проще в написании, хотя при очень большом количестве строк может работать медленнее.

  1. Создадим Набор данных 1 (Запрос) для получения продаж: выбираем дату и сумму.
  2. Создадим Набор данных 2 (Запрос) для получения курса: обращаемся к РегистрСведений.КурсыВалют.СрезПоследних(&ПериодКурса, Валюта = &Валюта).
  3. Перейдем на закладку Связи наборов данных.
  4. Укажем источником связи Набор 1, приемником — Набор 2.
  5. В колонке «Параметры» для связи установим соответствие: параметр ПериодКурса Набора 2 должен получать значение из поля ДатаПродажи Набора 1.

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

Важные нюансы при расчете валют

Проанализируем типичные ошибки, которые могут привести к неверным цифрам в отчете:

1. Кратность валюты. Многие забывают, что курс в 1С всегда привязан к кратности. Например, курс иены может быть указан за 100 единиц. Поэтому формула всегда должна быть: Сумма * (Курс / Кратность) (если мы пересчитываем из валюты в рубли) или Сумма / (Курс / Кратность) (если из рублей в валюту).

2. Обработка рубля. Если пользователь выберет в качестве валюты отчета «рубль», программа может не найти записей в регистре (так как рубль обычно там не хранится). Рассмотрим ситуацию: ЛЕВОЕ СОЕДИНЕНИЕ вернет NULL. Чтобы отчет не сломался, используем конструкцию ЕСТЬNULL(Курс, 1). Еще лучше — в начале запроса проверить, не совпадает ли выбранная валюта с валютой регламентированного учета, и в этом случае вообще не соединяться с регистром, а просто проставить единицы.

3. Время в датах. Посмотрим на формат дат. Документы могут создаваться в течение всего дня (например, 15:30:00), а курсы обычно загружаются на начало дня (00:00:00). При использовании условия КурсыВалют.Период <= Продажи.Дата это не проблема, но если вы используете НАЧАЛОПЕРИОДА, убедитесь, что вы не «отрезали» время так, что курс на текущий день стал недоступен для документа этого же дня.

4. Отсутствие курса. Если для выбранной валюты нет ни одной записи в истории (новая валюта), соединение по МАКСИМУМ(Период) не найдет ни одной строки. Если мы хотим, чтобы продажи все равно выводились, нужно следить за тем, чтобы таблица продаж была основной в ЛЕВОМ СОЕДИНЕНИИ, а значения курса по умолчанию принимались за 1 через ЕСТЬNULL.

Заключение

Мы рассмотрели два эффективных способа получения курсов на каждую дату. Выбор метода зависит от ваших целей: первый метод (через временные таблицы в одном запросе) является максимально быстрым и надежным для сложных отчетов с большим объемом данных. Второй метод (связи в СКД) удобнее при быстрой разработке и позволяет использовать мощь виртуальных таблиц без написания громоздких конструкций с поиском максимума.

← На главную