Skip to content

Prepare booking fields

The workflow adds the fields booking text and G/L account and fills them as soon as an invoice arrives: the booking text from the vendor and the invoice number in at most 60 characters, as DATEV takes it, and the G/L account from a table in the script. A vendor name that is too long is cut at a word boundary, so the invoice number stays complete. The fields are on the document for accounting; the DATEV export forms its own booking text from the vendor and the invoice number and doesn’t read these fields.

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: datev-fields
name: Buchungsfelder vorbereiten
template: custom
trigger:
documentType: incoming-invoice
fields:
- key: bookingText
kind: text
labelDe: Buchungstext
labelEn: Booking text
documentTypes:
- incoming-invoice
- key: glAccount
kind: text
labelDe: Sachkonto
labelEn: G/L account
documentTypes:
- incoming-invoice
start: prepare
steps:
- kind: script
name: prepare
label: DATEV-Felder vorbereiten
script: prepare.ts
hook: onDocumentArrived
inputs:
- kind: field
key: vendorName
type: text
- kind: field
key: invoiceNumber
type: text
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: onDocumentArrived · Inputs: vendorName, invoiceNumber · Return values: none

import { defineScript } from '@acriva/workflow-sdk';
/** DATEV takes at most 60 characters as the booking text. */
const MAX_BOOKING_TEXT = 60;
/** The G/L account (SKR03) per vendor; a vendor matches when its name starts with the entry's name. */
const GL_ACCOUNTS: readonly { vendor: string; glAccount: string }[] = [
{ vendor: 'Telekom', glAccount: '4920' },
{ vendor: 'Deutsche Bahn', glAccount: '4663' },
{ vendor: 'Bürobedarf', glAccount: '4930' },
];
/**
* The vendor and the invoice number in at most 60 characters. A vendor that is too long is cut at a word boundary, so the
* invoice number always stays complete.
*/
function bookingText(vendor: string, invoiceNumber: string | null): string {
const suffix = invoiceNumber == null ? '' : ` ${invoiceNumber}`;
const full = `${vendor}${suffix}`;
if (full.length <= MAX_BOOKING_TEXT) return full;
const room = MAX_BOOKING_TEXT - suffix.length;
if (room <= 0) return suffix.trim().slice(0, MAX_BOOKING_TEXT);
// Keep one character more, so a word that ends exactly at the limit stays whole.
const head = vendor.slice(0, room + 1);
const space = head.lastIndexOf(' ');
const shortVendor = (space > 0 ? head.slice(0, space) : vendor.slice(0, room)).trimEnd();
return `${shortVendor}${suffix}`;
}
export default defineScript({
hook: 'onDocumentArrived',
run(ctx) {
const { vendorName, invoiceNumber } = ctx.document.fields;
if (vendorName == null) return;
ctx.setField('bookingText', bookingText(vendorName.trim(), invoiceNumber?.trim() || null));
const match = GL_ACCOUNTS.find((entry) =>
vendorName.toLowerCase().startsWith(entry.vendor.toLowerCase()),
);
if (match) {
ctx.setField('glAccount', match.glAccount);
} else {
ctx.hint(
'Für diesen Lieferanten ist kein Sachkonto hinterlegt. Bitte ergänzen Sie es von Hand.',
);
}
},
});

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

  • Input: vendorName Telekom Deutschland GmbH, invoiceNumber 4711-2026-10
  • Exit: the default next step
  • Changes: field bookingText = Telekom Deutschland GmbH 4711-2026-10; field glAccount = 4920
  • Input: vendorName Gebäudereinigung Sauber & Glanz Facility Services Deutschland GmbH, invoiceNumber RE-2026-0815
  • Exit: the default next step
  • Changes: field bookingText = Gebäudereinigung Sauber & Glanz Facility RE-2026-0815; hint on the next task: “Für diesen Lieferanten ist kein Sachkonto hinterlegt. Bitte ergänzen Sie es von Hand.”
  • Input: vendorName null, invoiceNumber RE-2026-0815
  • Exit: the default next step
  • Changes: none