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

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

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

Выбор правильного типа регистра

Часто разработчики пытаются использовать РегистрНакопления с видом "Обороты" для хранения курсов валют. Это в корне неверный подход. Регистры накопления предназначены для суммирования количественных или суммовых показателей (остатков или оборотов товаров, денег). Курс валюты — это состояние (показатель), которое действует в определенный момент времени и не подлежит суммированию.

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

Настройка регистра сведений для курсов валют

Создадим правильный регистр для хранения курсов. Разберем по шагам его структуру:

  1. Тип регистра: Независимый. Нам не нужно привязывать записи курсов к конкретному документу-регистратору, курсы обычно загружаются обработками или регламентными заданиями.
  2. Периодичность: В пределах дня (или секунды, в зависимости от требований бизнеса, но обычно дня достаточно).
  3. Измерения: Создаем измерение Валюта (Тип: СправочникСсылка.Валюты). В свойствах измерения устанавливаем галочки "Ведущее", "Основной отбор" и "Запрет незаполненных значений".
  4. Ресурсы: Добавляем ресурс Курс (Тип: Число 10, 4) и ресурс Кратность (Тип: Число 10, 0). Кратность нужна для валют, курс которых слишком мал по отношению к базовой валюте (например, 100 иен).

Написание запроса для получения актуального курса

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


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

Запрос.УстановитьПараметр("Дата", НачалоДня(ДатаДокумента)); 
Запрос.УстановитьПараметр("Валюта", ТекущаяВалюта); 

РезультатЗапроса = Запрос.Выполнить().Выбрать(); 
Если РезультатЗапроса.Следующий() Тогда
    КурсВалюты = РезультатЗапроса.Курс; 
    КратностьКурса = РезультатЗапроса.Кратность;
Иначе
    КурсВалюты = 1;
    КратностьКурса = 1;
КонецЕсли;

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

Интеграция в форму документа

Теперь разберем, как правильно вызвать этот код в управляемой форме документа при изменении валюты в табличной части. Событие ПриИзменении срабатывает на стороне клиента, а данные базы данных (регистры) доступны только на сервере. Оптимальный вариант — использовать директиву &НаСервереБезКонтекста.


&НаКлиенте
Процедура ТоварыВалютаПриИзменении(Элемент)
    
    ТекущиеДанные = Элементы.Товары.ТекущиеДанные;
    Если ТекущиеДанные = Неопределено Тогда
        Возврат;
    КонецЕсли;
    
    // Проверка на пустую дату документа
    ДатаДляКурса = Объект.Дата;
    Если ДатаДляКурса = '00010101' Тогда
        ДатаДляКурса = ТекущаяДата(); // или ТекущаяДатаСеанса()
    КонецЕсли;
    
    СтруктураКурса = ПолучитьКурсВалютыНаСервере(ТекущиеДанные.Валюта, ДатаДляКурса);
    
    ТекущиеДанные.Курс = СтруктураКурса.Курс;
    ТекущиеДанные.Кратность = СтруктураКурса.Кратность;
    
    // Здесь же можно вызвать пересчет суммы строки
    
КонецПроцедуры

&НаСервереБезКонтекста
Функция ПолучитьКурсВалютыНаСервере(Валюта, ДатаКурса)
    
    Результат = Новый Структура("Курс, Кратность", 1, 1);
    
    // Текст запроса из предыдущего примера
    Запрос = Новый Запрос;
    Запрос.Текст = 
    "ВЫБРАТЬ
    |   КурсыВалютСрезПоследних.Курс КАК Курс,
    |   КурсыВалютСрезПоследних.Кратность КАК Кратность
    |ИЗ
    |   РегистрСведений.КурсыВалют.СрезПоследних(&Дата, Валюта = &Валюта) КАК КурсыВалютСрезПоследних";
    
    Запрос.УстановитьПараметр("Дата", ДатаКурса);
    Запрос.УстановитьПараметр("Валюта", Валюта);
    
    Выборка = Запрос.Выполнить().Выбрать();
    Если Выборка.Следующий() Тогда
        Результат.Курс = ?(Выборка.Курс = 0, 1, Выборка.Курс);
        Результат.Кратность = ?(Выборка.Кратность = 0, 1, Выборка.Кратность);
    КонецЕсли;
    
    Возврат Результат;
    
КонецФункции

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

Современные подходы: Библиотека стандартных подсистем (БСП)

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

В БСП есть общий модуль, который берет на себя всю логику извлечения курсов. Достаточно вызвать готовую функцию:


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

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

Важные нюансы: пустые даты и актуальность данных

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

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

← На главную