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

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

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

Разбор основной причины ошибки: почему запрос возвращает «не то»?

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

Важный нюанс: использование секции ГДЕ для фильтрации виртуальных таблиц является грубой ошибкой проектирования. Если вы напишете ИЗ РегистрСведений.КурсыВалют.СрезПоследних КАК Курсы ГДЕ Курсы.Валюта = &Валюта, система сначала получит последние курсы абсолютно по всем валютам в базе, а уже потом отфильтрует их. Это не только замедляет работу, но и может приводить к логическим ошибкам при обращении к результату.

Правильное построение запроса к курсам валют

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


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

Обратите внимание, что мы получаем не только Курс, но и Кратность. Это критически важно для валют с низким номиналом (например, японская иена или некоторые валюты стран СНГ), где курс устанавливается за 10, 100 или 1000 единиц.

Реализация логики пересчета на форме документа

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

Рассмотрим алгоритм действий на клиенте:

  1. При изменении счета организации получаем валюту счета.
  2. Вызываем серверную функцию для получения курса этой валюты на дату документа.
  3. Рассчитываем рублевую сумму по формуле: (Сумма в валюте * Курс) / Кратность.
  4. Если курс не найден, информируем пользователя (или реализуем отказ при проведении, как требует задание).

Пример обработки события изменения суммы на клиенте:


&НаКлиенте
Процедура СуммаОплатыПриИзменении(Элемент)
    ПересчитатьСуммуВРубли();
КонецПроцедуры

&НаКлиенте
Процедура ПересчитатьСуммуВРубли()
    
    Если Объект.Валюта = ПредопределенноеЗначение("Справочник.Валюты.Рубль") Тогда
        Объект.СуммаИтог = Объект.СуммаОплаты;
        Возврат;
    КонецЕсли;
    
    ДанныеКурса = ПолучитьКурсВалютыНаДату(Объект.Дата, Объект.Валюта);
    
    Если ДанныеКурса.Курс = 0 Тогда
        Сообщить("Внимание: курс валюты " + Объект.Валюта + " не задан на дату " + Формат(Объект.Дата, "ДЛФ=D"));
        Объект.СуммаИтог = 0;
    Иначе
        Объект.СуммаИтог = (Объект.СуммаОплаты * ДанныеКурса.Курс) / ДанныеКурса.Кратность;
    КонецЕсли;
    
КонецПроцедуры

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

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

Всегда используйте параметры виртуальной таблицы &НаДату и Валюта = &Валюта. Это гарантирует, что база данных вернет ровно одну (самую актуальную на указанный момент) запись.

Обработка проведения: финальный контроль

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

Проанализируем фрагмент кода для модуля объекта:


Процедура ОбработкаПроведения(Отказ, РежимПроведения)
    
    // Проверка наличия курса
    ДанныеКурса = ПолучитьКурсВалютыНаДату(Дата, ВалютаДокумента);
    
    Если ВалютаДокумента <> ВалютаРеглУчета И ДанныеКурса.Курс = 0 Тогда
        ТекстСообщения = СтрШаблон("Невозможно провести документ %1: не задан курс валюты %2 на дату %3", 
            ЭтотОбъект, ВалютаДокумента, Формат(Дата, "ДЛФ=D"));
        ОбщегоНазначенияКлиентСервер.СообщитьПользователю(ТекстСообщения, , , , Отказ);
        Возврат;
    КонецЕсли;
    
    // Далее следует логика формирования проводок Дт 52 - Кт 62.01
    // с использованием рассчитанной суммы.
    
КонецПроцедуры

Выясним причину, по которой задание требует жесткого отказа: в бухгалтерском учете недопустимы проводки с нулевой рублевой суммой по валютным счетам (52 счет), так как это искажает баланс и делает невозможным последующую переоценку валютных остатков. Поэтому проверка курса в ОбработкаПроведения — это не просто рекомендация, а требование финансовой корректности системы.

Заключение

Мы проанализировали ситуацию и выяснили, что для правильной работы с курсами валют необходимо:

  1. Всегда устанавливать отбор по конкретной валюте в параметрах виртуальной таблицы СрезПоследних.
  2. Обязательно учитывать Кратность валюты при расчетах.
  3. Использовать &НаСервереБезКонтекста для получения данных, чтобы интерфейс документа работал быстро.
  4. Дублировать проверку курса в модуле объекта при проведении для исключения ошибок в данных.

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

← На главную