Skip to content

Discount reminder

One day after an incoming invoice is approved, the script checks whether its early-payment discount still applies. While the period runs, it reminds accounting of the last day to pay and the amount saved, and tags the invoice “Skonto”. The discount period and rate are fields the workflow adds itself. With a card it needs no script: Watch the Skonto deadline reads the Skonto terms and gives the approval the Skonto date as its deadline.

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: skonto-reminder
name: Skonto-Erinnerung
template: custom
trigger:
documentType: incoming-invoice
fields:
- key: discountDays
kind: number
labelDe: Skontofrist (Tage)
labelEn: Discount period (days)
documentTypes:
- incoming-invoice
- key: discountPercent
kind: number
labelDe: Skonto (%)
labelEn: Discount (%)
documentTypes:
- incoming-invoice
start: approve
steps:
- 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: wait
onRejected: rejected
- kind: timer
name: wait
label: Einen Tag warten
days: 1
daysBefore: 0
next: remind
- kind: script
name: remind
label: An Skonto erinnern
script: remind.ts
hook: onTimer
inputs:
- kind: field
key: invoiceNumber
type: text
- kind: field
key: invoiceDate
type: date
- kind: field
key: totalGross
type: money
- kind: field
key: discountDays
type: number
- kind: field
key: discountPercent
type: number
returns: []
exits: []
next: approved
- kind: end
name: approved
label: Freigegeben
outcome: approved
- kind: end
name: rejected
label: Zurückgewiesen
outcome: rejected

Hook: onTimer · Inputs: invoiceNumber, invoiceDate, totalGross, discountDays, discountPercent · Return values: none

import { date, defineScript, money } from '@acriva/workflow-sdk';
export default defineScript({
hook: 'onTimer',
run(ctx) {
const { invoiceNumber, invoiceDate, totalGross, discountDays, discountPercent } =
ctx.document.fields;
// Without the invoice date, the discount period or the discount there is nothing to remind of.
if (invoiceDate == null || discountDays == null || discountPercent == null) return;
// The discount applies up to and including the last day of its period, counted in whole days.
const deadline = date.addDays(invoiceDate, Math.trunc(discountDays));
if (ctx.today() > deadline) {
ctx.log('Discount expired on', date.format(deadline));
return;
}
const percent = String(discountPercent).replace('.', ',');
const saving =
totalGross == null
? ''
: ` Das sind ${money.format(money.round((totalGross * discountPercent) / 100))}.`;
ctx.notify({
to: [{ role: 'Buchhaltung' }],
text:
`Die Rechnung ${invoiceNumber ?? 'ohne Nummer'} ist freigegeben. ` +
`Bei Zahlung bis ${date.format(deadline)} ziehen Sie ${percent} % Skonto ab.${saving}`,
});
ctx.addTag('Skonto');
},
});

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

  • Input: invoiceNumber RE-2026-0815, invoiceDate 2026-10-15, totalGross 1190, discountDays 14, discountPercent 2, last task outcome Approved, today 2026-10-22
  • Exit: the default next step
  • Changes: notification to the role Buchhaltung: “Die Rechnung RE-2026-0815 ist freigegeben. Bei Zahlung bis 29.10.2026 ziehen Sie 2 % Skonto ab. Das sind 23,80 €.”; tag “Skonto”
  • Input: invoiceNumber RE-2026-0702, invoiceDate 2026-10-01, totalGross 499.8, discountDays 10, discountPercent 3, last task outcome Approved, today 2026-10-22
  • Exit: the default next step
  • Changes: none
  • Input: invoiceNumber RE-2026-0911, invoiceDate 2026-10-20, totalGross 250, discountDays null, discountPercent null, last task outcome Approved
  • Exit: the default next step
  • Changes: none