Check the IBAN and the VAT ID
The script recomputes the check digits of an incoming invoice’s IBAN and checks that the VAT ID has the right form. It doesn’t check whether the VAT ID is actually assigned. On an error the invoice goes back to the person who uploaded it to be corrected, with a hint, otherwise straight to the approval. With a card it needs no script: Check the mandatory details checks the IBAN’s check digits and the VAT ID’s form, among others.
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: iban-vat-id-checkname: IBAN und USt-IdNr. prüfentemplate: customtrigger: documentType: incoming-invoicefields: - key: iban kind: text labelDe: IBAN labelEn: IBAN documentTypes: - incoming-invoice - key: vatId kind: text labelDe: USt-IdNr. labelEn: VAT ID documentTypes: - incoming-invoicestart: checksteps: - 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: rejectedThe script check.ts
Section titled “The script check.ts”Hook: onDocumentArrived · Inputs: iban, vatId · Return values: 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'); },});What it does
Section titled “What it does”A test runs each of these cases in the sandbox and compares the result.
The IBAN and the VAT ID are valid
Section titled “The IBAN and the VAT ID are valid”- Input:
ibanDE89 3704 0044 0532 0130 00,vatIdDE123456789 - Exit:
valid - Changes: none
An IBAN with wrong check digits
Section titled “An IBAN with wrong check digits”- Input:
ibanDE89 3704 0044 0532 0130 01,vatIdDE123456789 - Exit:
invalid - Changes: hint on the next task: “Die IBAN DE89 3704 0044 0532 0130 01 hat keine gültige Prüfziffer. Bitte korrigieren Sie die Angabe anhand der Rechnung.”
A VAT ID one digit short
Section titled “A VAT ID one digit short”- Input:
ibannull,vatIdDE12345678 - Exit:
invalid - Changes: hint on the next task: “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.”