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

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

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

Рассмотрим подробно, как правильно связать таблицы в запросе, если у них нет общих полей, и как корректно обработать отсутствие курса для рубля.

Метод 1. Использование соединения по условию ИСТИНА

Разберем ситуацию, когда нам нужно получить курс конкретной валюты, выбранной пользователем, и применить его ко всем строкам таблицы товаров. Основная сложность заключается в том, что в таблице ТоварыНаСкладахОстатки нет поля Валюта, по которому можно было бы связаться с регистром курсов. В этом случае мы используем механизм Cross Join (перекрестное соединение), который в языке запросов 1С реализуется через ЛЕВОЕ СОЕДИНЕНИЕ с условием ПО ИСТИНА.

Проанализируем пример запроса, дополненный правильной связью:


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

Выясним причину, почему это работает. Когда мы пишем ПО ИСТИНА, система пытается сопоставить каждую строку из левой таблицы с каждой строкой из правой. Чтобы избежать "раздувания" результата (декартова произведения), крайне важно ограничить правую таблицу в параметрах виртуальной таблицы. В данном случае мы передаем параметр Валюта = &Валюта. Это гарантирует, что в виртуальной таблице КурсыВалют.СрезПоследних всегда будет только одна строка с курсом выбранной валюты. Таким образом, к каждой строке товаров просто "подклеится" это единственное число.

Метод 2. Обработка курса рубля через ЕСТЬNULL

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

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

ЕСТЬNULL(КурсыВалютСрезПоследних.Курс, 1)

Здесь мы говорим системе: "Если курса нет (значение NULL), считай, что он равен 1". Это идеальное решение для рубля, так как деление на 1 не меняет сумму, и расчет стоимости остается верным.

Метод 3. Использование конструкции ВЫБОР для предопределенных валют

Если в вашей конфигурации валюта регламентированного учета (рубль) является предопределенным элементом справочника, можно сделать логику еще более надежной, используя конструкцию ВЫБОР. Это позволит не обращаться к регистру курсов вовсе, если выбрана национальная валюта.

Рассмотрим пример реализации в поле запроса:


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

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

Важные нюансы работы с кратностью

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

Формула расчета цены в валюте должна выглядеть так:

ЦенаВалютная = (ЦенаВРублях * Кратность) / Курс

Если вы планируете развивать систему, убедитесь, что в вашем соединении также участвует поле Кратность, и используйте ЕСТЬNULL(КурсыВалютСрезПоследних.Кратность, 1) для расчетов.

Альтернативный вариант: Передача курса параметром запроса

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

Рассмотрим, как это реализовать в коде обработки:


СтруктураКурса = РегистрыСведений.КурсыВалют.ПолучитьПоследнее(ТекущаяДата(), Новый Структура("Валюта", ВыбраннаяВалюта));
КурсЧислом = ?(СтруктураКурса.Курс = 0, 1, СтруктураКурса.Курс);

Запрос = Новый Запрос;
Запрос.Текст = 
    "ВЫБРАТЬ
    |   Товары.Цена / &ПереданныйКурс КАК ЦенаВВалюте
    |ИЗ
    |   ... (ваша таблица)";
Запрос.УстановитьПараметр("ПереданныйКурс", КурсЧислом);

Этот метод имеет несколько преимуществ:

  1. Производительность: Запрос становится проще, так как в нем на одно соединение меньше.
  2. Читаемость: Текст запроса не перегружен сложными конструкциями ВЫБОР и ЕСТЬNULL для каждой строки.
  3. Отладка: Вы можете легко проверить значение курса в отладчике перед тем, как передать его в запрос.

Подведем итог: если вам необходимо получить курс в запросе, используйте ЛЕВОЕ СОЕДИНЕНИЕ ПО ИСТИНА с обязательным фильтром в параметрах виртуальной таблицы. Если же курс один для всего документа или отчета — передавайте его готовым параметром из встроенного языка.

← На главную