Перейти к содержимому

Валюты и курсы - несколько валют в магазине и пересчёт цен

Заводим вторую валюту, настраиваем курс и разбираемся, в какой валюте в итоге считается заказ.

Решение

Смотрим заведённые валюты и базовую:

use Bitrix\Main\Loader;
Loader::includeModule('currency');
$res = CCurrency::GetList(); // список валют магазина с их настройками
while ($currency = $res->Fetch()) {
printf("%-5s база=%s формат=%s\n",
$currency['CURRENCY'], $currency['BASE'], $currency['FORMAT_STRING']);
}

Базовая валюта в магазине одна, и через неё идут все пересчёты. Цена в долларах превращается в рубли не напрямую, а через базовую, и точность результата зависит от обоих курсов.

Задаём курс на дату:

CCurrencyRates::Add([
'CURRENCY' => 'USD',
'DATE_RATE' => '01.09.2026', // курс привязан к дате
'RATE_CNT' => 1,
'RATE' => 92.5,
]);
// пересчёт всегда идёт через базовую валюту, а не напрямую между двумя

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

Смотрим, в какой валюте посчитан заказ:

$order = \Bitrix\Sale\Order::load($orderId);
printf("валюта=%s сумма=%.2f\n", $order->getCurrency(), $order->getPrice());
// валюта фиксируется при оформлении и потом не меняется

Заказ запоминает свою валюту в момент создания документа. Смена базовой валюты магазина потом не пересчитывает старые заказы: они остаются в той валюте, в которой были оформлены.

Настраиваем формат вывода:

CCurrency::Update('RUB', [
'FORMAT_STRING' => '# руб.', // где стоит символ валюты
'DEC_POINT' => ',',
'THOUSANDS_SEP' => ' ',
'DECIMALS' => 0, // копейки в рознице обычно не показывают
]);

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

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

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

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

Типичные проблемы

Цена на витрине отличается от заданной в админке.

Цена задана в другой валюте и пересчитывается по текущему курсу. Пересчёт при этом идёт через базовую валюту магазина.

Суммы старых заказов изменились.

Так быть не должно: заказ хранит собственную валюту и сумму. Изменение сумм означает, что их переписал чей-то свой код.

Цены разъехались на копейки.

Округление возникает после двойного пересчёта через базовую валюту. Число знаков после запятой задают у валюты явно.

Курс устарел на месяц.

Курсы валют не обновляются автоматически сами. Их заводят руками либо забирают агентом из внешнего источника.

В письме цена в другом формате.

Цена собрана своим кодом мимо штатного форматирования. Формат живёт у валюты и применяется штатной функцией вывода.

Частые вопросы

Нужна ли вторая валюта интернет-магазину?

Только если покупатели действительно платят в разных валютах. Показ цен в валюте при оплате в рублях чаще делают простым пересчётом на витрине.

Откуда брать курсы автоматически?

Своим агентом, забирающим их у центрального банка или у поставщика данных. Штатного источника курсов в поставке нет.

Можно ли менять базовую валюту?

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

Как показать цену в валюте, не меняя валюту заказа?

Пересчётом на витрине при выводе: заказ при этом остаётся в валюте магазина. Так делают справочный показ цен для иностранных покупателей.

Смежное

Первоисточник