Skip to content

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 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: tag-by-keyword
name: Schlagwort nach Stichwort
template: custom
trigger:
documentType: incoming-invoice
fields:
- key: purpose
kind: text
labelDe: Verwendungszweck
labelEn: Purpose
documentTypes:
- incoming-invoice
start: tag
steps:
- 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: rejected

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

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

  • Input: vendorName Telekom Deutschland GmbH, purpose null, tags „Fixkosten“
  • Exit: the default next step
  • Changes: none
  • Input: vendorName Bürobedarf Schmidt KG, purpose Druckerpapier
  • Exit: the default next step
  • Changes: none
  • Input: vendorName Seehof GmbH, purpose HOTEL Messe Hannover, 2 Nächte
  • Exit: the default next step
  • Changes: tag “Reisekosten”
  • Input: vendorName Deutsche Bahn AG, purpose null
  • Exit: the default next step
  • Changes: tag “Reisekosten”