Skip to content

Cost center and creditor account from the ERP

Your ERP knows each vendor’s cost center and creditor account. The script asks it for the invoice’s vendor through the data source erp and sets both fields, which the workflow declares itself. When the ERP doesn’t know the vendor (status 404) or doesn’t answer, the script attaches a hint to the approval so the invoice is allocated by hand; a failed call doesn’t end the run.

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: erp-cost-center
name: Kostenstelle und Kreditorenkonto aus dem ERP
template: custom
trigger:
documentType: incoming-invoice
fields:
- key: costCenter
kind: text
labelDe: Kostenstelle
labelEn: Cost center
documentTypes:
- incoming-invoice
- key: creditorAccount
kind: text
labelDe: Kreditorenkonto
labelEn: Creditor account
documentTypes:
- incoming-invoice
start: allocate
steps:
- kind: script
name: allocate
label: Kontierung aus dem ERP
script: allocate.ts
hook: onDocumentArrived
inputs:
- kind: field
key: vendorName
type: text
- kind: source
key: erp
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, data source erp · Return values: none

import { defineScript } from '@acriva/workflow-sdk';
/** What the ERP answers to `GET lieferanten?name=…`: the vendor's cost center and its creditor account. */
interface ErpVendor {
kostenstelle: string;
kreditorenkonto: string;
}
/** A source's answer is JSON of any shape until the script has checked it. */
const isErpVendor = (data: unknown): data is ErpVendor =>
typeof data === 'object' &&
data !== null &&
typeof (data as ErpVendor).kostenstelle === 'string' &&
typeof (data as ErpVendor).kreditorenkonto === 'string';
export default defineScript({
hook: 'onDocumentArrived',
async run(ctx) {
const vendor = ctx.document.fields.vendorName;
if (vendor == null) return;
const result = await ctx.sources.get('erp', { path: 'lieferanten', query: { name: vendor } });
// A failed call is a result, not an exception: the invoice is then allocated by hand.
if (!result.ok) {
ctx.hint(
result.status === 404
? `„${vendor}“ ist im ERP nicht angelegt. Bitte kontieren Sie die Rechnung von Hand.`
: `Das ERP hat nicht geantwortet (${result.error}). Bitte kontieren Sie die Rechnung von Hand.`,
);
return;
}
if (!isErpVendor(result.data)) {
ctx.hint(
'Das ERP hat keine Kostenstelle geliefert. Bitte kontieren Sie die Rechnung von Hand.',
);
return;
}
ctx.setField('costCenter', result.data.kostenstelle);
ctx.setField('creditorAccount', result.data.kreditorenkonto);
},
});

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

  • Input: vendorName Bürobedarf Schmidt KG
  • Data source erp: no data: timeout
  • Exit: the default next step
  • Changes: hint on the next task: “Das ERP hat nicht geantwortet (timeout). Bitte kontieren Sie die Rechnung von Hand.”
  • Input: vendorName Telekom Deutschland GmbH
  • Data source erp: answers {"kostenstelle":"4400","kreditorenkonto":"70012"}
  • Exit: the default next step
  • Changes: field costCenter = 4400; field creditorAccount = 70012
  • Input: vendorName Neue Firma AG
  • Data source erp: no data: http-error (status 404)
  • Exit: the default next step
  • Changes: hint on the next task: “„Neue Firma AG“ ist im ERP nicht angelegt. Bitte kontieren Sie die Rechnung von Hand.”