Skip to content

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 starts for every document of the type incoming-invoice. Its file workflow.yaml:

$schema: 'https://docs.acriva.de/schemas/workflow.schema.json'
format: 1
schemaVersion: 4
sdk: '1.0.0'
key: exchange-rates
name: Fremdwährung umrechnen
template: custom
trigger:
documentType: incoming-invoice
fields:
- 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-invoice
start: review
steps:
- 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: rejected

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)}.`,
);
},
});

A test runs each of these cases in the sandbox and compares the result.

  • Input: currency USD, foreignAmount 1250, invoiceDate 2026-10-20, last task outcome Approved
  • Data source wechselkurse: answers {"kurs":1.0842,"datum":"2026-10-20"}
  • Exit: the default next step
  • Changes: field exchangeRate = 1.0842; field amountEur = 1152.92; hint on the next task: “Umgerechnet zum Referenzkurs vom 20.10.2026 (1 EUR = 1,0842 USD): 1.152,92 €.”
  • Input: currency EUR, foreignAmount null, invoiceDate 2026-10-20, last task outcome Approved
  • Exit: the default next step
  • Changes: none
  • Input: currency XAU, foreignAmount 2, invoiceDate 2026-10-20, last task outcome Approved
  • 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.”