Zum Inhalt springen

IBAN und USt-IdNr. prüfen

Das Skript rechnet die Prüfziffer der IBAN einer Eingangsrechnung nach und prüft, ob die USt-IdNr. die richtige Form hat. Ob die USt-IdNr. tatsächlich vergeben ist, prüft es nicht. Bei einem Fehler geht die Rechnung mit einem Hinweis zur Korrektur an die Person, die sie hochgeladen hat, sonst direkt zur Freigabe. Ohne Skript geht es mit einer Karte: Pflichtangaben prüfen prüft unter anderem die Prüfziffer der IBAN und die Form der USt-IdNr.

Der Workflow startet für jedes Dokument vom Typ incoming-invoice. Seine Datei workflow.yaml:

$schema: 'https://docs.acriva.de/schemas/workflow.schema.json'
format: 1
schemaVersion: 4
sdk: '1.0.0'
key: iban-vat-id-check
name: IBAN und USt-IdNr. prüfen
template: custom
trigger:
documentType: incoming-invoice
fields:
- key: iban
kind: text
labelDe: IBAN
labelEn: IBAN
documentTypes:
- incoming-invoice
- key: vatId
kind: text
labelDe: USt-IdNr.
labelEn: VAT ID
documentTypes:
- incoming-invoice
start: check
steps:
- kind: script
name: check
label: IBAN und USt-IdNr. prüfen
script: check.ts
hook: onDocumentArrived
inputs:
- kind: field
key: iban
type: text
- kind: field
key: vatId
type: text
returns:
- key: result
type: enumeration
values:
- valid
- invalid
branch: result
exits:
- value: valid
next: approve
- value: invalid
next: fix
next: approve
- kind: humanTask
name: fix
label: Bankdaten korrigieren
taskKind: fix
assignees:
- when: null
assignee:
kind: role
role: Buchhaltung
fallback:
kind: role
role: Buchhaltung
options:
excludeRequester: false
excludeActorsOfSteps: []
pickFile: false
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: onDocumentArrived · Eingaben: iban, vatId · Rückgaben: result (valid, invalid)

import { defineScript } from '@acriva/workflow-sdk';
/** Removes spaces and writes letters in capitals, as IBANs and VAT IDs are compared. */
const compact = (value: string) => value.replace(/\s+/g, '').toUpperCase();
/** Whether a text has the form of an IBAN: a country code, two check digits and 11 to 30 letters or digits; 22 characters in Germany. */
function ibanFormFits(iban: string): boolean {
if (!/^[A-Z]{2}\d{2}[A-Z0-9]{11,30}$/.test(iban)) return false;
return !iban.startsWith('DE') || iban.length === 22;
}
/**
* Whether an IBAN's two check digits fit (ISO 13616, mod 97): the first four characters move to the end, every letter
* becomes a number (A = 10 … Z = 35), and the whole number leaves the remainder 1 when divided by 97.
*/
function ibanCheckDigitsFit(iban: string): boolean {
const rearranged = iban.slice(4) + iban.slice(0, 4);
let remainder = 0;
// A character at a time, so the number never grows too large; an IBAN has at most 34 characters.
for (const char of rearranged) {
const value = parseInt(char, 36);
remainder = (remainder * (value > 9 ? 100 : 10) + value) % 97;
}
return remainder === 1;
}
/**
* Whether a VAT ID has the form of one: a German one is DE and 9 digits, any other a country code and 2 to 12
* characters. It checks the form only, not whether the number is assigned: that only the Federal Central Tax Office
* (BZSt) can confirm.
*/
function vatIdFormFits(vatId: string): boolean {
if (vatId.startsWith('DE')) return /^DE\d{9}$/.test(vatId);
return /^[A-Z]{2}[A-Z0-9]{2,12}$/.test(vatId);
}
export default defineScript({
hook: 'onDocumentArrived',
outputs: { result: ['valid', 'invalid'] },
run(ctx) {
const { iban, vatId } = ctx.document.fields;
const problems: string[] = [];
// An empty field has nothing to check: not every invoice names an IBAN or a VAT ID.
if (iban != null && !ibanFormFits(compact(iban))) {
problems.push(`Die IBAN ${iban} hat nicht die Form einer IBAN (in Deutschland 22 Zeichen).`);
} else if (iban != null && !ibanCheckDigitsFit(compact(iban))) {
problems.push(`Die IBAN ${iban} hat keine gültige Prüfziffer.`);
}
if (vatId != null && !vatIdFormFits(compact(vatId))) {
problems.push(
`Die USt-IdNr. ${vatId} hat nicht die Form einer USt-IdNr. (in Deutschland DE und 9 Ziffern).`,
);
}
if (problems.length === 0) return ctx.branch('valid');
ctx.hint(`${problems.join(' ')} Bitte korrigieren Sie die Angabe anhand der Rechnung.`);
return ctx.branch('invalid');
},
});

Ein Test führt jeden dieser Fälle in der Sandbox aus und vergleicht das Ergebnis.

  • Eingabe: iban DE89 3704 0044 0532 0130 00, vatId DE123456789
  • Ausgang: valid
  • Änderungen: keine
  • Eingabe: iban DE89 3704 0044 0532 0130 01, vatId DE123456789
  • Ausgang: invalid
  • Änderungen: Hinweis an der nächsten Aufgabe: „Die IBAN DE89 3704 0044 0532 0130 01 hat keine gültige Prüfziffer. Bitte korrigieren Sie die Angabe anhand der Rechnung.“
  • Eingabe: iban null, vatId DE12345678
  • Ausgang: invalid
  • Änderungen: Hinweis an der nächsten Aufgabe: „Die USt-IdNr. DE12345678 hat nicht die Form einer USt-IdNr. (in Deutschland DE und 9 Ziffern). Bitte korrigieren Sie die Angabe anhand der Rechnung.“