Как составить запрос для пересчета сумм документов в доллары по курсу на дату документа в УТ 11

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

В процессе разработки отчетов или механизмов анализа в 1С:Управление торговлей часто возникает задача: вывести список документов (например, РеализацияТоваровУслуг) и пересчитать их сумму в доллары (или любую другую валюту) по курсу, актуальному на дату каждого конкретного документа. На первый взгляд задача кажется простой, но при её реализации разработчики часто сталкиваются с неочевидными подводными камнями: потерей документов при отсутствии курсов, неверным учетом кратности валют и снижением производительности запросов.

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

Метод 1. Простой левое соединение по началу дня (для гарантированно заполненных курсов)

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

Рассмотрим структуру такого запроса:


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

Важные детали реализации:

  1. Мы используем ЛЕВОЕ СОЕДИНЕНИЕ. Это критически важно: если использовать внутреннее или полное соединение (как часто ошибочно делают начинающие разработчики), документы, для которых по какой-то причине не окажется курса в регистре, просто исчезнут из итоговой выборки.
  2. Мы применили функцию НАЧАЛОПЕРИОДА(Реализация.Дата, ДЕНЬ). Поскольку документы регистрируются со временем (секундами), а курс валюты обычно записывается на начало дня (00:00:00), прямое соединение по секундам не сработает.
  3. Учет кратности валюты: Формула Сумма / Курс является ошибочной. Для некоторых валют курс устанавливается за 10, 100 или 1000 единиц (например, для казахстанского тенге или японской иены). Поэтому необходимо умножать сумму на кратность и только потом делить на курс: (Сумма * Кратность) / Курс.

Метод 2. Универсальный срез последних на каждую дату (через временные таблицы)

Что делать, если курс доллара заполнен не на каждый день? Например, на праздниках или выходных курс не менялся, и в регистре нет записи за конкретное число. В таком случае простое соединение по равенству дат вернет пустое значение (NULL). Нам необходимо получить курс на ближайшую предыдущую дату (классический «срез последних» на каждую дату документа).

Для реализации этого алгоритма мы воспользуемся пакетом запросов и временными таблицами. Это наиболее производительный и надежный способ в СУБД.

Разберем этот процесс по шагам:

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


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

Шаг 2. Соединение полученной временной таблицы с регистром курсов для получения значений курса и кратности по найденной дате:


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

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

Метод 3. Элегантное решение в отчетах на СКД

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

Проанализируем, как настроить такую схему:

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

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

Особенности работы в современных версиях УТ 11 (11.5 и выше)

Разработчикам, работающим с актуальными релизами УТ 11, КА 2.5 или ERP 2.5, важно учитывать изменения в архитектуре конфигураций. Начиная с версии 11.5.7, фирма «1С» внедрила новый механизм ведения валютных расчетов.

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

Использование готового программного интерфейса (API БСП)

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

Рассмотрим пример программного пересчета:


СуммаВДолларах = РаботаСКурсамиВалют.ПересчитатьИзВалютыВВалюту(
    СуммаДокумента,
    ВалютаДокумента,
    ВалютаДоллара,
    ДатаДокумента
);

Этот метод самостоятельно определит актуальные курсы обеих валют на указанную дату (с учетом их кратности и возможных кросс-курсов), обработает возможные ошибки деления на ноль и вернет точный математический результат. Использование стандартных общих модулей существенно повышает надежность кода и упрощает поддержку конфигурации при будущих обновлениях.

← На главную