Разработка мультивалютного учета — это классическая задача, с которой сталкивается практически каждый программист 1С при создании или кастомизации конфигураций. На первый взгляд задача кажется простой: достаточно умножить цену в валюте на курс. Однако при детальном рассмотрении всплывает множество нюансов: работа с дешевыми валютами, оптимизация отображения курсов в интерфейсе, правильная настройка типов данных в конфигураторе и исключение ошибок округления. Рассмотрим подробно, как правильно спроектировать и реализовать этот механизм с нуля.
Для реализации полноценного валютного учета в системе необходимо создать два взаимосвязанных объекта метаданных:
Валюты. Он будет содержать список всех используемых валют (например, доллары, рубли, евро, юани). В реквизитах справочника полезно хранить символьный код валюты (USD, EUR) и трехзначный цифровой код по классификатору (840, 978).КурсыВалют. Этот регистр должен быть периодическим (с периодичностью "В пределах дня"), чтобы система знала, какой курс действовал на конкретную дату совершения операции. Режим записи для такого регистра устанавливается как "Независимый".Проанализируем структуру измерений и ресурсов регистра сведений КурсыВалют:
Валюта (тип СправочникСсылка.Валюты, свойство "Ведущее" должно быть включено).Курс (тип Число).Кратность (тип Число).Частой ошибкой начинающих разработчиков является непонимание того, как платформа 1С настраивает числовые поля ввода. Выясним причину проблемы, из-за которой пользователи не могут ввести курс с несколькими знаками после запятой (например, 74.1234).
В конфигураторе 1С для числового типа данных существуют два ключевых параметра: Длина и Точность. Посмотрим на правила платформы:
Проанализируем пример ошибки. Если установить свойства числового поля как Длина = 4, а Точность = 4, то под целую часть останется ровно 0 знаков (4 знака длины минус 4 знака точности). В такое поле можно будет ввести только значения меньше единицы (например, 0.9543). Попытка ввести число 1.2345 или 75.5432 приведет к ошибке или автоматическому сбросу значения.
Правильная настройка для хранения курса валюты:
104При таких настройках под целую часть выделяется 6 знаков (10 - 4), что позволяет хранить курсы до 999999.9999. Этого с запасом хватит для абсолютного большинства мировых валют.
Понятие Кратность (или масштаб курса) необходимо для предотвращения потери точности при расчетах с дешевыми валютами. Если одна единица иностранной валюты стоит доли копейки базовой валюты (например, в случае с вьетнамским донгом или казахстанским тенге), то прямой курс будет содержать слишком много нулей после запятой. Округление такого курса приведет к колоссальным погрешностям при расчете больших сумм.
Вместо хранения микроскопического курса для 1 единицы валюты, устанавливают курс для некоторого объема валюты:
20.70100Это означает, что 100 единиц валюты стоят 20.70 рублей (то есть 1 единица стоит 0.2070 рублей). Кратность всегда должна быть целым числом (Точность = 0) и по умолчанию равняться единице.
Рассмотрим формулу пересчета произвольной суммы из одной валюты (Валюта А) в другую валюту (Валюта Б) через базовую валюту (например, рубли):
СуммаВБ = (СуммаВА * КурсА * КратностьБ) / (КурсБ * КратностьА)
Реализуем программный метод пересчета в виде функции на языке 1С. Обратите внимание на обязательную проверку деления на ноль:
Функция ПересчитатьСумму(Сумма, ВалютаИсточник, КурсИсточник, КратностьИсточник, ВалютаПриемник, КурсПриемник, КратностьПриемник) Экспорт
Если Сумма = 0 Тогда
Возврат 0;
КонецЕсли;
// Проверка на корректность исходных курсов во избежание деления на ноль
Если КурсИсточник = 0 Или КратностьИсточник = 0 Или КурсПриемник = 0 Или КратностьПриемник = 0 Тогда
ЗаписатьЛогОшибки("Неверно заданы курсы или кратность валют для пересчета!");
Возврат 0;
КонецЕсли;
// Пересчет в базовую валюту
СуммаВБазовой = (Сумма * КурсИсточник) / КратностьИсточник;
// Пересчет из базовой валюты в целевую
ИтоговаяСумма = (СуммаВБазовой * КратностьПриемник) / КурсПриемник;
Возврат ОкруглитьСумму(ИтоговаяСумма);
КонецФункции
Часто при анализе типовых конфигураций (например, Управление торговлей) разработчики видят в запросе формы списка справочника Валюты странную конструкцию:
ВЫБРАТЬ
Валюты.Ссылка КАК Ссылка,
Валюты.Код КАК Код,
Валюты.Наименование КАК Наименование,
ВЫРАЗИТЬ(NULL КАК ЧИСЛО(10, 2)) КАК Курс,
ВЫРАЗИТЬ(NULL КАК СТРОКА(50)) КАК Кратность
ИЗ
Справочник.Валюты КАК Валюты
Разберем, для чего это сделано. Попытка получить срез последних курсов валют напрямую через левое соединение таблиц в SQL-запросе динамического списка может критически снизить производительность системы. При быстрой прокрутке списка база данных будет выполнять тяжелые операции соединения на лету.
Вместо этого разработчики 1С используют паттерн оптимизации:
ВЫРАЗИТЬ(NULL КАК ЧИСЛО(10, 2)) создает на форме пустую виртуальную колонку (заглушку) нужного типа.ПриПолученииДанныхНаСервере.Если вы ведете разработку в конфигурации, где уже развернута Библиотека стандартных подсистем (БСП), вам не нужно писать алгоритмы расчета и загрузки вручную. Для работы с валютами используйте готовые методы общего модуля РаботаСКурсамиВалют. Для быстрого поиска нужных процедур библиотеки и примеров их вызова подойдёт справочник по методам БСП.
Для быстрого пересчета сумм задействуйте функцию:
СуммаВЦелевойВалюте = РаботаСКурсамиВалютКлиентСервер.ПересчитатьПоКурсу(
СуммаДляПересчета,
ПараметрыТекущегоКурса,
ПараметрыНовогоКурса
);
Параметры курсов можно получить на определенную дату с помощью метода:
ПараметрыКурса = РаботаСКурсамиВалют.ПолучитьКурсВалюты(ВыбраннаяВалюта, ДатаДокумента);
Этот метод возвращает структуру, содержащую актуальный Курс и Кратность выбранной валюты на указанный момент времени, избавляя вас от необходимости писать прямые запросы к базе данных.