Convert a foreign currency into euros
During the review, accounting enters the currency and the amount in foreign currency. Then the script fetches the reference rate for the invoice date through the data source wechselkurse, for example from a service with the European Central Bank’s rates, and sets the exchange rate and the amount in euros. The workflow declares the four fields itself. An invoice in euros needs no rate; when a currency has none, the approval gets a hint.
The workflow
Section titled “The workflow”The workflow starts for every document of the type incoming-invoice. Its file workflow.yaml:
$schema: 'https://docs.acriva.de/schemas/workflow.schema.json'format: 1schemaVersion: 4sdk: '1.0.0'key: exchange-ratesname: Fremdwährung umrechnentemplate: customtrigger: documentType: incoming-invoicefields: - key: currency kind: text labelDe: Währung labelEn: Currency documentTypes: - incoming-invoice - key: foreignAmount kind: number labelDe: Betrag in Fremdwährung labelEn: Amount in foreign currency documentTypes: - incoming-invoice - key: exchangeRate kind: number labelDe: Wechselkurs labelEn: Exchange rate documentTypes: - incoming-invoice - key: amountEur kind: money labelDe: Betrag in Euro labelEn: Amount in euros documentTypes: - incoming-invoicestart: reviewsteps: - kind: humanTask name: review label: Sachliche Prüfung taskKind: review assignees: - when: null assignee: kind: role role: Buchhaltung fallback: kind: role role: Buchhaltung options: excludeRequester: false excludeActorsOfSteps: [] pickFile: false next: convert onRejected: rejected - kind: script name: convert label: In Euro umrechnen script: convert.ts hook: onApproved inputs: - kind: field key: currency type: text - kind: field key: foreignAmount type: number - kind: field key: invoiceDate type: date - kind: source key: wechselkurse returns: [] exits: [] next: approve - kind: humanTask name: approve label: Freigabe taskKind: approve assignees: - when: null assignee: kind: costCenterResponsible fallback: kind: role role: Buchhaltung options: excludeRequester: true excludeActorsOfSteps: [] pickFile: false next: approved onRejected: rejected - kind: end name: approved label: Freigegeben outcome: approved - kind: end name: rejected label: Zurückgewiesen outcome: rejectedThe script convert.ts
Section titled “The script convert.ts”Hook: onApproved · Inputs: currency, foreignAmount, invoiceDate, data source wechselkurse · Return values: none
import { date, defineScript, money, type PlainDate } from '@acriva/workflow-sdk';
/** What the rate service answers to `GET kurse?waehrung=…&datum=…`: the reference rate (1 EUR = `kurs` units) and its day. */interface Rate { kurs: number; datum: PlainDate;}
/** A source's answer is JSON of any shape until the script has checked it. */const isRate = (data: unknown): data is Rate => typeof data === 'object' && data !== null && typeof (data as Rate).kurs === 'number' && (data as Rate).kurs > 0 && typeof (data as Rate).datum === 'string';
export default defineScript({ hook: 'onApproved', async run(ctx) { const { currency, foreignAmount, invoiceDate } = ctx.document.fields; const code = currency?.trim().toUpperCase(); // A euro invoice, or one without an amount, needs no rate. if (!code || code === 'EUR' || foreignAmount == null) return; const result = await ctx.sources.get('wechselkurse', { path: 'kurse', query: { waehrung: code, datum: invoiceDate ?? ctx.today() }, }); if (!result.ok || !isRate(result.data)) { const reason = result.ok ? '' : ` (${result.error})`; ctx.hint( `Für ${code} gibt es keinen Wechselkurs${reason}. Bitte rechnen Sie den Betrag von Hand in Euro um.`, ); return; } const { kurs, datum } = result.data; const amount = money.round(foreignAmount / kurs); ctx.setField('exchangeRate', kurs); ctx.setField('amountEur', amount); ctx.hint( `Umgerechnet zum Referenzkurs vom ${date.format(datum)} (1 EUR = ${String(kurs).replace('.', ',')} ${code}): ${money.format(amount)}.`, ); },});What it does
Section titled “What it does”A test runs each of these cases in the sandbox and compares the result.
An invoice in US dollars
Section titled “An invoice in US dollars”- Input:
currencyUSD,foreignAmount1250,invoiceDate2026-10-20, last task outcomeApproved - Data source
wechselkurse: answers{"kurs":1.0842,"datum":"2026-10-20"} - Exit: the default next step
- Changes: field
exchangeRate=1.0842; fieldamountEur=1152.92; hint on the next task: “Umgerechnet zum Referenzkurs vom 20.10.2026 (1 EUR = 1,0842 USD): 1.152,92 €.”
An invoice in euros
Section titled “An invoice in euros”- Input:
currencyEUR,foreignAmountnull,invoiceDate2026-10-20, last task outcomeApproved - Exit: the default next step
- Changes: none
A currency without a rate
Section titled “A currency without a rate”- Input:
currencyXAU,foreignAmount2,invoiceDate2026-10-20, last task outcomeApproved - Data source
wechselkurse: no data:http-error(status 404) - Exit: the default next step
- Changes: hint on the next task: “Für XAU gibt es keinen Wechselkurs (http-error). Bitte rechnen Sie den Betrag von Hand in Euro um.”