Skip to content

Check the order number

Purchasing enters the invoice’s order number during the review, a field the workflow declares itself. Then the script asks the order system for that order through the data source bestellungen. When the invoice matches the order (up to 2 % above it), it goes to the approval, with the order’s amount as a hint. When the number is missing, the order doesn’t exist, the order system doesn’t answer, or the invoice lies above it, purchasing clarifies it first. When the order is a document in Acriva, it needs no script and no data source: Order match.

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: order-number-check
name: Bestellnummer prüfen
template: custom
trigger:
documentType: incoming-invoice
fields:
- key: orderNumber
kind: text
labelDe: Bestellnummer
labelEn: Order number
documentTypes:
- incoming-invoice
start: review
steps:
- kind: humanTask
name: review
label: Sachliche Prüfung
taskKind: review
assignees:
- when: null
assignee:
kind: member
member: einkauf@example.com
fallback:
kind: role
role: Buchhaltung
options:
excludeRequester: false
excludeActorsOfSteps: []
pickFile: false
next: check
onRejected: rejected
- kind: script
name: check
label: Bestellung prüfen
script: check.ts
hook: onApproved
inputs:
- kind: field
key: orderNumber
type: text
- kind: field
key: totalGross
type: money
- kind: source
key: bestellungen
returns:
- key: result
type: enumeration
values:
- matches
- clarify
branch: result
exits:
- value: matches
next: approve
- value: clarify
next: clarify
next: approve
- kind: humanTask
name: clarify
label: Bestellung klären
taskKind: fix
assignees:
- when: null
assignee:
kind: member
member: einkauf@example.com
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: rejected

Hook: onApproved · Inputs: orderNumber, totalGross, data source bestellungen · Return values: result (matches, clarify)

import { defineScript, money } from '@acriva/workflow-sdk';
/** How far an invoice may lie above its order, as a share of the order's amount. */
const TOLERANCE = 0.02;
/** What the order system answers to `GET bestellungen/<Bestellnummer>`: the order's gross amount in euros. */
interface Order {
betrag: number;
}
/** A source's answer is JSON of any shape until the script has checked it. */
const isOrder = (data: unknown): data is Order =>
typeof data === 'object' && data !== null && typeof (data as Order).betrag === 'number';
export default defineScript({
hook: 'onApproved',
outputs: { result: ['matches', 'clarify'] },
async run(ctx) {
const { orderNumber, totalGross } = ctx.document.fields;
const number = orderNumber?.trim();
if (!number) {
ctx.hint(
'Die Rechnung hat keine Bestellnummer. Bitte klären Sie, zu welcher Bestellung sie gehört.',
);
return ctx.branch('clarify');
}
const result = await ctx.sources.get('bestellungen', { path: number });
if (!result.ok) {
ctx.hint(
result.status === 404
? `Die Bestellung ${number} gibt es im Bestellsystem nicht.`
: `Das Bestellsystem hat nicht geantwortet (${result.error}). Bitte prüfen Sie die Bestellung ${number} von Hand.`,
);
return ctx.branch('clarify');
}
if (!isOrder(result.data)) {
ctx.hint(`Das Bestellsystem hat zur Bestellung ${number} keinen Betrag geliefert.`);
return ctx.branch('clarify');
}
const ordered = result.data.betrag;
if (totalGross != null && totalGross > ordered * (1 + TOLERANCE)) {
ctx.hint(
`Die Rechnung über ${money.format(totalGross)} liegt über der Bestellung ${number} über ${money.format(ordered)}.`,
);
return ctx.branch('clarify');
}
ctx.hint(`Passt zur Bestellung ${number} über ${money.format(ordered)}.`);
return ctx.branch('matches');
},
});

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

  • Input: orderNumber 4500012345, totalGross 1499.4, last task outcome Approved
  • Data source bestellungen: answers {"betrag":1190}
  • Exit: clarify
  • Changes: hint on the next task: “Die Rechnung über 1.499,40 € liegt über der Bestellung 4500012345 über 1.190,00 €.”
  • Input: orderNumber 4500012345, totalGross 1190, last task outcome Approved
  • Data source bestellungen: answers {"betrag":1190}
  • Exit: matches
  • Changes: hint on the next task: “Passt zur Bestellung 4500012345 über 1.190,00 €.”
  • Input: orderNumber null, totalGross 238, last task outcome Approved
  • Exit: clarify
  • Changes: hint on the next task: “Die Rechnung hat keine Bestellnummer. Bitte klären Sie, zu welcher Bestellung sie gehört.”
  • Input: orderNumber 4500099999, totalGross 595, last task outcome Approved
  • Data source bestellungen: no data: http-error (status 404)
  • Exit: clarify
  • Changes: hint on the next task: “Die Bestellung 4500099999 gibt es im Bestellsystem nicht.”