Tag by keyword
When an invoice’s vendor or purpose mentions Bahn, Hotel or Lufthansa, it gets the tag Reisekosten; Telekom, Vodafone or Miete (rent) give Fixkosten. The keywords are in a small table in the script, and upper and lower case don’t matter.
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: tag-by-keywordname: Schlagwort nach Stichworttemplate: customtrigger: documentType: incoming-invoicefields: - key: purpose kind: text labelDe: Verwendungszweck labelEn: Purpose documentTypes: - incoming-invoicestart: tagsteps: - kind: script name: tag label: Schlagwort nach Stichwort script: tag.ts hook: onDocumentArrived inputs: - kind: field key: vendorName type: text - kind: field key: purpose 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: rejectedThe script tag.ts
Section titled “The script tag.ts”Hook: onDocumentArrived · Inputs: vendorName, purpose · Return values: none
import { defineScript, type TagName } from '@acriva/workflow-sdk';
/** The tag a document gets when its vendor or its purpose holds one of the keywords as a whole word, in any case. */const KEYWORDS: readonly { tag: TagName; keywords: readonly string[] }[] = [ { tag: 'Reisekosten', keywords: ['bahn', 'hotel', 'lufthansa', 'flug'] }, { tag: 'Fixkosten', keywords: ['telekom', 'vodafone', 'miete', 'versicherung'] },];
export default defineScript({ hook: 'onDocumentArrived', run(ctx) { const { vendorName, purpose } = ctx.document.fields; // Both fields may be empty; the search reads the words of what is there, in lower case, so "Bahn" never matches // "Bahnhofsbäckerei". const words = new Set( [vendorName, purpose] .filter((part) => part != null) .join(' ') .toLowerCase() .split(/[\s,.;:()/&+-]+/), ); // Tag names don't depend on case, so a tag the document has in another spelling counts too. const has = new Set(ctx.document.tags.map((tag) => tag.toLowerCase())); KEYWORDS.filter((entry) => entry.keywords.some((keyword) => words.has(keyword))) .filter((entry) => !has.has(entry.tag.toLowerCase())) .forEach((entry) => ctx.addTag(entry.tag)); },});What it does
Section titled “What it does”A test runs each of these cases in the sandbox and compares the result.
Fixed costs, already tagged
Section titled “Fixed costs, already tagged”- Input:
vendorNameTelekom Deutschland GmbH,purposenull, tags „Fixkosten“ - Exit: the default next step
- Changes: none
No keyword
Section titled “No keyword”- Input:
vendorNameBürobedarf Schmidt KG,purposeDruckerpapier - Exit: the default next step
- Changes: none
A purpose mentioning “Hotel”
Section titled “A purpose mentioning “Hotel””- Input:
vendorNameSeehof GmbH,purposeHOTEL Messe Hannover, 2 Nächte - Exit: the default next step
- Changes: tag “Reisekosten”
A travel vendor
Section titled “A travel vendor”- Input:
vendorNameDeutsche Bahn AG,purposenull - Exit: the default next step
- Changes: tag “Reisekosten”