Skip to main content

Notification Rules

The Notification Rules screen is the write side of the notification system — a library of notification-rule resources that decide:

  • when a notification fires (status code and / or rejection reason);
  • what it says (subject and body, or the dispatcher defaults);
  • to whom (a portal user / role, an email list, or both);
  • on which channels (portal inbox, e-mail, external API call), with the rendered PDF attached to the e-mail by default.

Rules are matched by InvoiceStatusCatalog.StatusTransition.apply() after the database write of every status change, by the manual SetStatusModal, and by the CLI flows (-process, -fetch-import, -fetch-status, -fetch-single, -fetch-all). A failure in dispatch never aborts the underlying status update.

The read side of the system — the inbox where users acknowledge notifications and the navbar bell — is documented on the Notifications page.

The page applies regardless of source system — JD Edwards, SAP, NetSuite or a custom ERP. Triggers reference the standard statuses and rejection-reason-codes catalogues, not source-system-specific codes.


Opening the editor

  • Sidebar → Management → Notification Rules.
  • A fresh installation ships with no rules — the dispatcher emits nothing until the first rule is created. Use Add to create one.

At a glance

Notification Rules+ Add⎘ Copy🗑 Remove💾 SaveSearch…pa-rejectionAlert on PA rejectiononpa-successPA deposit confirmationonvalidation-failureXSD / Schematron errorsoffpa-rejectionAlert on PA rejectionTRIGGERStatus9904 ×9907 ×ReasonREJ_ADR ×CHANNELS☑ portal☑ email☐ actionRECIPIENTTyperole ▾ValueaccountantsCCsupervisor@nomana-it.frEMAIL CONTENTSubjectInvoice {doc} {dct} {kco} — RejectedBodyStatus: {statusLabel}Reason: {reasonLabel}attachPdf · attach the rendered invoiceACTION CALL · disabledConnector / endpoint / params — only when ☑ action aboveTEST12345RI00070▶ Run test✓ Dispatched · 1 portal · 2 emailToolbaradd / copy / remove / saveRule liston / off pill per rowTrigger chipsstatus + reason multi-selectChannelsportal · email · actionRecipientportal target + email listSubject + body{placeholders} resolved liveTest panelfires the rule end-to-end

The page is split between the rule list on the left and the rule editor on the right. Each rule is a notification-rule resource saved in config-notifications.json; the dispatcher reloads them at startup and after every save.


Toolbar actions

ActionEffect
AddOpens a modal asking for a name and a description. Creates a new rule in disabled-when-empty state — no trigger, channels defaulted to portal, recipient type defaulted to user.
CopyDuplicates the selected rule under a new name. Convenient for deriving a B2C variant from a B2B rule, or a per-region rule from a generic one.
RemoveDeletes the selected rule after a confirmation.
SaveWrites the editor state back to config-notifications.json and signals the dispatcher to reload. The next status change picks up the new rule.

The rule list

Every rule in the catalogue appears with two visual cues:

  • Description in italic underneath the rule name — the same field the Add modal asked for, free text.
  • on / off pill at the right of each row — bound to the enabled property. A rule with off stays in the catalogue but is skipped at dispatch time. Useful while iterating on a rule without losing it.

A search box at the top of the sidebar filters the list by substring on the rule name.


The rule editor

The editor is structured as four section groups plus a synchronous test panel.

Trigger

The trigger decides when the rule fires. Two fields combine, both optional:

FieldSourceBehaviour
Statusstatuses catalogueComma-separated list of status codes (e.g. 9904,9907). The rule fires when the status code of the new transition is in this list. Empty = match every status.
Reasonrejection-reason-codes catalogueComma-separated list of rejection-reason codes (e.g. REJ_ADR,REJ_FMT). The rule fires only when the reason code of the new transition is in this list. Empty = match every reason.

Both fields are surfaced as chip multi-selects — picking from a dropdown adds a chip; the × on a chip removes it. The dropdown is populated from the same statuses and rejection-reason-codes resources used by the Set Status modal and the invoice History tab, so a rule cannot reference a code that the application does not recognise.

When both fields are filled, both must match (logical AND) for the rule to fire.

Channels

Three boxes, any combination:

  • portal — writes a row in F564253 for the recipient. The user sees it in the Notifications inbox and the bell.
  • email — sends an SMTP message via the configured e-invoicing mail account.
  • action — fires an outbound HTTP call against an API Connector endpoint.

A rule that emits zero channels is useless and rejected at save time.

Recipient

The recipient model has two independent halves: a portal target and an email list.

FieldDescription
TypePicks the portal target — user (a single F564250 username), role (every user that carries this role on their URROLE value), or empty. When auth is disabled, the empty option reads Broadcast — all portal users and writes a single F564253 row under the * sentinel.
ValueThe username or role name selected by Type. Free text — auto-completion comes from the connected database when available.
CCIndependent list of e-mail addresses, separated by , or ;. Each address goes on the To: header of the dispatched email. The portal target's USEMAIL, when present on its F564250 row, is added automatically.

When the portal target carries a USEMAIL, the email channel sends to both that address and every entry in CC in a single SMTP transaction. When the F564250 lookup fails, the portal channel still emits (the row is keyed by the literal username), so the inbox stays populated even during a transient database outage.

Email content

FieldDefaultDescription
SubjectInvoice {doc} {dct} {kco} — {statusLabel}Subject line. Placeholders are resolved at dispatch time.
BodyStatus: {statusLabel} \n Reason: {reasonLabel} \n Action: {actionLabel}Plain-text body. Multi-line input.
Attach PDFYRender the invoice PDF (via the resolved pdf-template) and attach it to the e-mail. The PDF is rendered once per dispatch and reused across every recipient; a render failure is logged but never fails the e-mail.

Available placeholders in subject / body: {doc}, {dct}, {kco}, {statusCode}, {statusLabel}, {statusMessage}, {reasonCode}, {reasonLabel}, {actionCode}, {actionLabel}, {ruleName}, {message}.

The portal NTSUBJ uses the same subject; the portal NTMSGE defaults to just {statusLabel} because the inbox UI already shows the doc reference inline — duplicating it in the body would be noise.

Action call

When the action channel is enabled, three additional rows appear:

FieldDescription
ConnectorDropdown listing every api-connector template. Same set as on Process API.
EndpointDropdown populated by api.connectors.listEndpoints(connector) once a connector is picked.
ParametersPre-populated from the endpoint's defined parameter list. Each row carries a key (locked) and a value (editable). Values may contain the same {placeholders} as the e-mail subject / body.

The action call is fired in the same dispatch transaction as the portal write and the e-mail send. Failures are logged and do not abort the underlying status update or the other channels.

Test panel

A synchronous Test runner sits at the bottom of the form. It accepts a (doc, dct, kco) triplet, optionally a status code and a custom message, and actually fires the rule through every enabled channel — the portal write lands in the inbox, the e-mail goes out via SMTP, the action call is made. The resulting banner reports the dispatch counts (✓ Dispatched · 1 portal · 2 email) or the first error.

The test panel does not save the rule — only fires whatever is currently in the form. Use it to validate edits before clicking Save.


How a notification gets dispatched

When a status transition is applied (every database write that touches F564231.UHK74RSCD), the dispatcher walks the catalogue in three steps.

StatusTransition.apply()

NotificationDispatcher.fire(doc, dct, kco, status, reason, action, message)
↓ — for each rule where enabled = Y
↓ match trigger.status (CSV) ∋ status
↓ AND match trigger.reason (CSV) ∋ reason (or trigger.reason = '')
↓ → resolve recipient (portal target + email list)
↓ → render the invoice PDF once if attachPdf = Y
↓ — for each enabled channel:
↓ • portal → INSERT into F564253
↓ • email → SMTP one message with everyone on To:
↓ • action → HTTP call to connector.endpoint with resolved params
↓ all exceptions caught — failures never abort the status update

The dispatcher uses a singleton with a 2-thread asynchronous worker pool, so the calling code returns immediately. A JVM shutdown hook drains the pool with a 2-second grace before the process dies, so CLI flows that exit right after a status update still deliver their notifications.


REST API

The page reads and writes via the standard template endpoints; the dispatcher exposes one extra route for the test panel.

Method + pathPurpose
GET /api/templatesLists all templates; the page filters by type = notification-rule.
GET /api/templates/{name}Loads a single rule.
POST /api/templatesCreates a new rule (Add).
POST /api/templates/{from}/copy/{to}Duplicates (Copy).
PUT /api/templates/{name}Saves edits.
DELETE /api/templates/{name}Removes a rule.
POST /api/notifications/testFires the rule's payload synchronously against every enabled channel — used by the Test panel.

Tips & best practices

  • Start narrow, widen later. A trigger of 9904 + REJ_ADR is easier to validate than a catch-all '', and you keep the noise low while the recipient list and the body are still being tuned.
  • Use the Test panel before saving. Especially for the action channel — the dispatcher swallows failures, so a misconfigured connector silently no-ops at production time. The test runner surfaces the same error inline.
  • Keep one rule per purpose, not per status code. Group several status codes behind a single rule when the body is identical (9904, 9907 → Rejected); split into separate rules only when the recipient list or the body differs.
  • PDFs are heavy. attachPdf renders the invoice once per dispatch — fine for low-volume rules, expensive for fleet-wide alerts. Disable it on rules that fire on 9900 (just-created) or 9901 (validated), where the PDF rarely adds value.
  • Use role over user whenever possible. A role-based recipient survives staff changes; a user-based one needs an edit each time the assignee leaves. The role list on F564251 is the source of truth.
  • Disable, don't delete. While iterating, flip the rule off instead of removing it — the catalogue keeps the history, the dispatcher skips it, and the test runner remains available.
  • Read the inbox after a release. Rules sometimes drift from the codes they reference (a status renamed in the catalogue, a reason retired) — the Notifications page is the fastest cross-check that the production catalogue is still consistent with the rules in this page.