Назад к блогу

API курсов валют для ERP: автоматизация обменных курсов в NetSuite, SAP и Dynamics 365

V
Vlado Grigirov
September 03, 2026
Currency API Exchange Rates ERP NetSuite SAP Dynamics 365 Integration

Каждая международная финансовая команда рано или поздно сталкивается с одной и той же проблемой: ERP-системе нужен курс обмена валют для каждой валютной операции, каждый день, для каждой пары, с которой работает бизнес — а кто-то до сих пор вбивает их вручную. API курсов валют для ERP решает эту проблему, превращая ежедневную загрузку курсов в задачу по расписанию, которая выполняется до того, как бухгалтерия придёт на работу. В этом руководстве рассказывается, как NetSuite, SAP S/4HANA и Microsoft Dynamics 365 Finance получают курсы валют, как построить единый конвейер курсов, который питает все три системы, и какие пограничные случаи незаметно портят закрытие месяца, если их не учесть.

Почему ERP-системам нужен внешний источник курсов

Каждая ERP-система с включённой мультивалютностью хранит собственную таблицу курсов обмена. В NetSuite это список Currency Exchange Rates, в SAP — таблица TCURR (заполняется через транзакцию OB08), а в Dynamics 365 Finance — страница Currency exchange rates. Ничто в вашей главной книге не обращается напрямую к рыночному фиду — проводки читают именно эту внутреннюю таблицу.

Такой подход осознан, и он правильный. Финансовые проводки должны быть воспроизводимыми: если аудитор заново проведёт мартовскую проводку, результат должен совпасть с тем, что было получено в марте. Таблица датированных, неизменяемых курсов даёт именно это. Обращение к рынку в реальном времени — нет.

Проблема в том, как эта таблица заполняется. На практике используются три распространённых подхода:

  1. Ручной ввод. Кто-то открывает сайт ЕЦБ или банка, копирует курсы и вбивает их вручную. Это медленно, подвержено ошибкам и практически невозможно нормально проверить при аудите — нет записи о том, откуда взялось число.
  2. Встроенный провайдер ERP. NetSuite, SAP и Dynamics поставляются с каким-либо встроенным фидом курсов. Они работают, но вы получаете то покрытие валют, тот источник курсов и то расписание обновлений, которые выбрал вендор, и их сложно сверять с другими системами.
  3. Отдельный API курсов валют, питающий задачу по расписанию. Вы контролируете источник, время, список валют и журнал аудита — и один и тот же фид может одновременно обслуживать вашу биллинговую платформу, хранилище данных и ERP.

Именно вариант 3 разбирается в этом руководстве. Решающее преимущество — согласованность между системами: если ваш биллинг на Stripe, BI-дашборды и ERP получают данные из одного и того же снепшота, сверка выручки перестаёт порождать необъяснимые расхождения по курсовым разницам.

Требования к источнику курсов уровня ERP

Не любой API курсов валют подходит для бухгалтерского учёта. Фиды, ориентированные на трейдинг, оптимизированы под задержку; фиды для ERP — под воспроизводимость. Вот что действительно важно.

Ежедневные снепшоты, а не тиковые данные

Вашей главной книге не нужны курсы с точностью до долей секунды. Ей нужен один авторитетный курс на валютную пару в день, зафиксированный в постоянное время и применяемый последовательно. Фид, который выдаёт немного разное число в зависимости от того, в какую секунду вы его вызвали, — это не преимущество, а обязательство. Вам нужно стабильное значение дневного закрытия, которое можно запросить повторно и получить тот же ответ.

Исторический endpoint с полным бэкфилом

Исторические курсы понадобятся вам постоянно: чтобы восполнить пробел после сбоя, переоценить остатки за прошлые периоды, исправить проводку трёхнедельной давности и удовлетворить запросы аудита. API исторических курсов валют, охватывающий несколько лет, — обязательное условие. Если ваш провайдер отдаёт только «последний курс», вы купили игрушку.

Широкое покрытие валют, включая редкие пары

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

Детерминированное, задокументированное округление

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

Журнал аудита, которым владеете вы

Для каждого загруженного курса стоит фиксировать: источник, базовую и котируемую валюту, курс, дату действия, метку времени запроса и ID запуска задачи. Аудиторы спрашивают «откуда это взялось?» и «кто мог это изменить?» — и ответ «встроенный провайдер ERP, кажется» звучит слабо. Это особенно важно для курсов валют в налоговой отчётности, где регуляторы часто требуют курсы из определённого источника, применяемые последовательно на протяжении периода.

Как каждая ERP получает курсы

Конвейер общий для всех систем; различается только финальный шаг доставки.

NetSuite

NetSuite предлагает три пути. Встроенная функция Currency Exchange Rate Integration (включается в Setup > Company > Enable Features) автоматически обновляет курсы раз в день от интегрированного провайдера. Import Assistant принимает CSV-файл с курсами валют. А SuiteScript может напрямую записывать записи курсов — это путь, который стоит выбрать, если вам нужен собственный источник и собственное расписание.

Скрипт SuiteScript 2.x по расписанию, который получает данные из вашего API и создаёт записи currencyrate, даёт вам полный контроль:

/**
 * @NApiVersion 2.1
 * @NScriptType ScheduledScript
 */
define(['N/https', 'N/record', 'N/runtime'], (https, record, runtime) => {

  const fetchRates = (base, symbols) => {
    const res = https.get({
      url: `https://api.finexly.com/v1/latest?base=${base}&symbols=${symbols.join(',')}`,
      headers: { Authorization: `Bearer ${runtime.getCurrentScript().getParameter({ name: 'custscript_fx_key' })}` }
    });
    if (res.code !== 200) throw Error(`Finexly returned ${res.code}`);
    return JSON.parse(res.body);
  };

  const execute = () => {
    const base = 'USD';
    const symbols = ['EUR', 'GBP', 'JPY', 'CAD', 'AUD', 'CHF', 'SEK', 'MXN'];
    const payload = fetchRates(base, symbols);

    Object.entries(payload.rates).forEach(([quote, rate]) => {
      const rec = record.create({ type: 'currencyrate' });
      rec.setValue({ fieldId: 'basecurrency', value: currencyIdFor(base) });
      rec.setValue({ fieldId: 'transactioncurrency', value: currencyIdFor(quote) });
      rec.setValue({ fieldId: 'effectivedate', value: new Date(payload.date) });
      rec.setValue({ fieldId: 'exchangerate', value: rate });
      rec.save();
    });
  };

  return { execute };
});

Обратите особое внимание на направление. Поле exchangerate в NetSuite в одних случаях ожидает курс, выраженный как единицы базовой валюты на единицу валюты транзакции, а в других — обратное соотношение, в зависимости от настройки вашего subsidiary. Загрузите одну пару вручную, посмотрите, что получается в главной книге, и подберите нужное направление — это самая частая причина того, что загрузка курсов проходит «чисто», но проводки получаются задом наперёд.

SAP S/4HANA и ECC

SAP хранит курсы в таблице TCURR и предлагает несколько путей загрузки. Транзакция OB08 предназначена для ручного ведения курсов. Транзакция TBD4 — стандартный путь для автоматических обновлений от поставщика рыночных данных. BAPI_EXCHANGERATE_CREATE записывает курсы программно — это то, что используется в большинстве кастомных интеграций. Некоторые команды вместо этого формируют файл рыночных данных в формате, который ожидает стандартный импорт SAP, и кладут его на сервер приложений для задачи по расписанию.

Два специфичных для SAP момента, которые важно учитывать:

  • Типы курсов обмена. SAP различает M (стандартный курс пересчёта, используется в большинстве проводок), B (курс покупки банка), G (курс продажи банка) и часто собственные типы для плановых или бюджетных курсов. Загрузка только типа M — обычно правильная отправная точка; уточните у команды FI, какие типы действительно настроены в вашей системе.
  • Факторы курса. Таблица TCURF хранит коэффициенты пересчёта «из/в» для пары валют. Для валют с большим числовым соотношением (JPY, KRW, IDR, VND к EUR или USD) фактор часто составляет 1:100 или 1:1000. Если загрузить «сырой» курс без настройки соответствующего фактора, суммы разъедутся на два-три порядка — и, что хуже, они будут выглядеть достаточно правдоподобно, чтобы пройти беглую проверку.

Распространённый паттерн — сформировать файл загрузки из вашего API и позволить job на ABAP по расписанию его обработать:

import csv
from datetime import date
import requests

API = "https://api.finexly.com/v1/latest"
HEADERS = {"Authorization": "Bearer YOUR_API_KEY"}

BASE = "EUR"
SYMBOLS = ["USD", "GBP", "JPY", "CHF", "PLN", "CZK", "SEK", "NOK"]
RATE_TYPE = "M"

def build_tcurr_load(target: date, path: str) -> None:
    r = requests.get(API, headers=HEADERS,
                     params={"base": BASE, "symbols": ",".join(SYMBOLS)}, timeout=15)
    r.raise_for_status()
    payload = r.json()

    with open(path, "w", newline="") as fh:
        w = csv.writer(fh, delimiter=";")
        for quote, rate in payload["rates"].items():
            # SAP expects the rate at the precision configured for the pair;
            # 5 decimals is a safe default for majors.
            w.writerow([RATE_TYPE, BASE, quote,
                        target.strftime("%Y%m%d"), f"{rate:.5f}"])

build_tcurr_load(date.today(), "/interface/fx/tcurr_load.csv")

Microsoft Dynamics 365 Finance

Dynamics 365 Finance поставляется с фреймворком exchange rate provider и периодической задачей Import currency exchange rates, которая по расписанию получает данные от настроенного провайдера. «Из коробки» доступно несколько провайдеров центральных банков. Фреймворк расширяемый: вы можете реализовать собственного провайдера на X++, чтобы ваш API стал полноценной опцией в том же интерфейсе, которым уже пользуется финансовая команда.

Если писать на X++ не хочется, прагматичная альтернатива — передавать курсы через Data Management Framework с использованием сущности данных exchange rate, запускаемой Azure Function или Logic App по таймеру. Это позволяет вести интеграцию на языке, который команда уже поддерживает, и избегать деплоя кода при каждом изменении расписания.

Построение конвейера

Независимо от места назначения, структура задачи одна и та же. Получить данные один раз, преобразовать под каждую ERP, загрузить, проверить.

Шаг 1: получить один снепшот

Забирайте один снепшот в день и относитесь к нему как к единственному источнику истины для всех систем ниже по цепочке:

curl "https://api.finexly.com/v1/latest?base=USD&symbols=EUR,GBP,JPY,CAD,AUD,CHF" \
  -H "Authorization: Bearer YOUR_API_KEY"
{
  "base": "USD",
  "date": "2026-09-03",
  "rates": {
    "EUR": 0.8631,
    "GBP": 0.7402,
    "JPY": 151.28,
    "CAD": 1.3574,
    "AUD": 1.4938,
    "CHF": 0.8025
  }
}

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

Шаг 2: вывести пары, которые реально нужны вашей ERP

Ваш API возвращает курсы относительно одной базовой валюты. Вашей ERP может понадобиться GBP→JPY, а у каждой дочерней компании может быть своя функциональная валюта. Выводите кросс-курсы из одного снепшота, а не делайте отдельный запрос на каждую пару — это сохраняет внутреннюю согласованность всех производных курсов и держит число запросов низким:

def cross_rate(rates: dict, base: str, quote: str) -> float:
    """Both legs come from the same snapshot, so the cross is consistent."""
    if base == quote:
        return 1.0
    return rates[quote] / rates[base]

# GBP -> JPY from a USD-based snapshot
gbp_jpy = cross_rate(payload["rates"], "GBP", "JPY")  # 151.28 / 0.7402 = 204.38

Если арифметика кросс-курсов вам незнакома, наш материал про кросс-курсы валют разбирает её как следует.

Шаг 3: загрузить, затем проверить

Никогда не считайте успешный HTTP 200 от ERP доказательством того, что загрузка сработала. После записи считайте обратно выборку пар и сравните со снепшотом. Трёхстрочный шаг проверки ловит перевёрнутые направления, тихо потерянные строки и обрезание точности — до того, как бухгалтерия проведёт документы по плохим данным.

Шаг 4: расписание с разумной обработкой сбоев

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

  • Повтор с задержкой (backoff) при временных сетевых сбоях — трёх попыток за десять минут хватает почти всегда.
  • Откат к последнему известному хорошему курсу вместо отсутствия загрузки вообще, с явной пометкой. ERP с устаревшим, но помеченным курсом гораздо лучше, чем ERP с пробелом.
  • Оповещение человека при второй подряд неудаче. Тихие сбои FX-задачи обнаруживаются в момент закрытия месяца — в худшее возможное время.
  • Бэкфил при восстановлении. Когда задача снова заработает, загрузите все пропущенные даты, а не только сегодняшнюю. Наше руководство по кешированию и обработке ошибок описывает общие паттерны для этого.

Подводные камни, которые ломают закрытие месяца

Выходные и праздники. Валютные рынки закрываются. Большинству ERP нужен курс на каждую дату проводки, включая субботы. Определите свою политику явно — переносить пятничный курс вперёд или использовать встроенное заполнение пробелов в ERP — и задокументируйте это, потому что аудиторы обязательно спросят.

Перевёрнутое направление курса. Об этом уже говорилось применительно к NetSuite, но риск универсален. У каждой ERP есть своё мнение о том, хранится ли число как единицы базовой валюты на единицу котируемой или наоборот. Проверяйте на известной паре, где направление очевидно: если USD→JPY выходит как 0,0066, а не 151, значит направление перепутано.

Расхождение по времени между системами. Если ваша биллинговая платформа фиксирует курсы в 00:00 UTC, а задача ERP запускается в 06:00 по местному времени, счета и проводки в главной книге будут использовать разные числа, и кто-то потратит неделю на сверку расхождения. Снимайте снепшот один раз и раздавайте его всем системам.

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

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

Строить или покупать — честный разбор

Конвейер курсов — это действительно небольшой объём кода: несколько сотен строк, включая тесты. То, что вы покупаете у провайдера API курсов валют, — это данные, доступность (uptime) и исторический архив, а не логику интеграции.

Ручной вводВстроенный провайдер ERPОтдельный API + задача по расписанию
Усилия на настройкуНетНизкие1–3 дня
Постоянные усилия15–30 мин/деньМинимальныеПочти нулевые
Покрытие валютТо, что вы найдёте вручнуюСписок провайдера170+
Согласованность между системамиНетНетДа
Собственный журнал аудитаСлабыйОграниченныйПолный
Исторический бэкфилВручнуюОграниченныйПолный
Если вас интересует более широкий вопрос, у нас есть развёрнутый материал о выборе между разработкой и покупкой API курсов валют. Для большинства команд честный ответ таков: интеграцию стоит построить самостоятельно, а данные самостоятельно добывать не стоит.

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

Часто задаваемые вопросы

Можно ли использовать бесплатный API курсов валют для загрузки курсов в ERP?

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

Как часто должна запускаться загрузка курсов в ERP?

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

Какой курс использовать — спот, закрытие или средний?

Стандартная практика по IFRS и US GAAP — использовать курс на дату транзакции для отдельных операций и часто средний курс за период для статей отчёта о прибылях и убытках. Это решение принимает финансовая команда, а не инженерная; ваша задача — сделать выбранный ими курс доступным воспроизводимо. Различие между спотовыми и форвардными курсами тоже имеет значение, если бизнес хеджируется.

Стоит ли заменять встроенного провайдера ERP или запускать оба варианта параллельно?

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

Как поступить с валютой, которую не покрывает мой провайдер?

Выведите её через кросс-курс, если существует ликвидная промежуточная пара. Если нет — а такое действительно редко встречается за пределами длинного хвоста — задокументируйте ручной процесс с назначенным ответственным и определённым источником. Не подменяйте валюту суррогатом молча — именно такие вещи всплывают в аудите через два года.

Начните прямо сейчас

Готовы перестать вбивать курсы валют в ERP вручную? Получите бесплатный API-ключ Finexly — банковская карта не требуется. Вы получаете более 170 валют, исторические данные для бэкфила и аудита, а также REST API, интеграцию которого с NetSuite, SAP или Dynamics можно выполнить за один день. Начните с бесплатного тарифа, изучите документацию по API и переходите на платный тариф только тогда, когда объём запросов действительно этого потребует.

Vlado Grigirov

Senior Currency Markets Analyst & Financial Strategist

Vlado Grigirov is a senior currency markets analyst and financial strategist with over 14 years of experience in foreign exchange markets, cross-border finance, and currency risk management. He has wo...

View full profile →