Roles
This screen manages NomaUBL's role-based access control. Each role bundles four kinds of grant:
- A list of pages the role can reach (plus a list of dashboard cards visible to it).
- A per-action whitelist of operations the role is allowed to perform (Edit, Delete, Resend, Push status…).
- A data scope — the companies the role is restricted to and optional row filters that narrow the visible rows by column value.
- The members assigned to the role.
Roles are application-wide and source-agnostic — they apply equally whether NomaUBL is plugged into JD Edwards, SAP, NetSuite or a custom ERP. Default roles (admin, viewer) are seeded by the Initialize Database action in Database Connectors → NomaUBL.
The editor has been reorganised around purpose-built tabs and the grant model now goes much finer than pages and read-only:
- Four tabs — Access (pages, dashboard cards, features), Actions (the new per-button whitelist), Data scope (companies + row filters), Members. The role Name and Description sit above the tab bar so they stay visible from any tab.
- Granular action permissions — the old all-or-nothing
readonlyflag is replaced by an explicit whitelist over Invoices, E-Reporting and Integration ops. A role with no whitelist set continues to allow every action (legacy behaviour); turning the whitelist on pre-populates with everything so the role does not suddenly lose actions. - Role-level row filters — pick a column (e.g. the invoice's customer alpha key
UHALKY) and one or more values; the filter applies to the list views, the dashboard, every per-row endpoint and the rendered PDF stream. Multiple values on the same column are combined as OR; filters on different columns combine as AND, side-by-side with the existing Companies grant. - Per-card dashboard whitelist — each dashboard widget is its own permission. An empty list shows every card (existing behaviour); a populated list is a strict whitelist and hidden cards never run their SQL nor reach the wire.
Opening the editor
- Sidebar → Configuration → Security → Roles.
- The page opens with every existing role as a card. Click any card to expand the edit panel below the list. Use + New Role at the top right to start from scratch — the same edit panel opens with the Name field unlocked.
At a glance
Roles list
The top of the page lists every existing role as a card.
| Element | Description |
|---|---|
| Name | Internal identifier of the role (e.g. admin, operator, customer_acme). Used to bind users to the role from the Users editor. |
| Description | Free-text human-readable summary. |
| Member count | Number of users currently assigned to the role. |
| Badge | Admin when the role has the Settings access feature, User otherwise. Quick read of the role's reach. |
| ⎘ Copy | Duplicates the role: pre-fills the edit panel with all grants of the source role; the Name field is empty for the operator to choose a new one; the description gets (copy) appended. |
| 🗑 Delete | Removes the role after confirmation. Users assigned to it lose every permission until reassigned. |
Click any card to open the Edit panel below the list. Use + New Role at the top right to create a role from scratch.
Identity (always visible)
The role's Name (visible only when creating) and Description sit above the tab bar, so the operator never has to switch tabs to relabel a role.
| Field | Description |
|---|---|
| Name | Internal identifier of the role. Must be unique. Locked once created. |
| Description | Human-readable summary shown in the role list. |
Tab — Access
Defines what the role can reach: features, pages and dashboard cards.
Features
A short list of binary feature flags. Each row carries a checkbox plus a one-line helper that explains what the flag does.
| Feature | Helper text | Effect |
|---|---|---|
| Settings access | Can open the Settings page (template / connector editing). | Opens the entire Configuration menu. The role is then displayed with the Admin badge in the list. |
| Read-only mode | No edit / delete / resend actions, even on permitted pages. | Members can browse the application but every write action is disabled — overrides the Actions tab entirely. |
Allowed Pages
A grouped checklist mirroring the application's left-hand navigation. Each page checkbox shows the friendly label (the same i18n nav.* key the Sidebar uses) plus the page id in muted monospace beside it — so the row stays informative when localised yet still searchable by id.
| Group | Pages |
|---|---|
| Navigation | dashboard, techdashboard, invoices, vatdeclaration, ereporting, edirectory, notifications, integrationerrors, processinglog |
| Processing | fetchinput, import, retrievestatuses |
| Operations | process, extractandprocess, processapi |
| UBL | validate, xsleditor, xmlviewer, ubldefaults |
| Extract | extractbip (JD Edwards-specific), extract, extractftp |
| Documentation | releasenotes, upgradehistory, statusreference, reasoncodes, ublreference, xref, apireference |
| Management | documents, pdftemplates, actions, notificationrules, dailydigest, autoretry, fileversions |
Helpers:
- All / None buttons above the groups — instantly grant or revoke every page.
- Per-group check all / uncheck all toggle — flip an entire group in one click.
- A group's checkbox shows an indeterminate state when only some of its pages are selected.
- Empty list = all pages allowed. Same convention as the other lists: an unfiltered role sees everything.
Dashboard cards
Each dashboard widget is an individual permission keyed by dashboard.<card>. The grouping below mirrors the dashboard layout:
| Group | Cards |
|---|---|
| Hero metrics | dashboard.total, dashboard.inflight, dashboard.errors-tech, dashboard.errors-business |
| Charts & widgets | dashboard.pipeline, dashboard.volume, dashboard.recent, dashboard.stale, dashboard.error-rules, dashboard.per-company, dashboard.ereporting, dashboard.round-trip |
| Sections | dashboard.quick-actions |
- Empty list = every card visible (legacy behaviour).
- A populated list is a strict whitelist — hidden cards are skipped server-side, so their SQL never runs and their data never reaches the wire.
- The same All / None / per-group toggles are available as on the Allowed Pages list.
Tab — Actions
Defines what the role can do on the pages it can reach.
Restrict toggle
| Toggle | Effect |
|---|---|
| Off (default) | Empty whitelist on the server — the role can perform every action allowed by its pages. Equivalent to the legacy behaviour. |
| On | Only the actions checked below are allowed. Switching on pre-fills the list with every action so the role does not suddenly lose access — uncheck from there. |
The helper text under the toggle states Off (default) = role can perform every action allowed by its pages. On = only the checked actions below are allowed.
The section header above the toggle reflects the current state:
(every action is allowed — no whitelist set)when the toggle is off.(N actions explicitly allowed — everything else is blocked)when the toggle is on and N actions are checked.(0 actions allowed — same effect as Read-only)when the whitelist is on but empty.(Read-only mode is on under Access — every action is blocked)when the Read-only feature is on — the whole tab is greyed out.
Check all / Uncheck all buttons appear under the toggle (disabled when the whitelist is off).
Action catalog
Grouped by the page where the button lives — the same key can power buttons on more than one page.
| Group | Action | Key | Effect |
|---|---|---|---|
| Invoices | Create | invoice.create | Dashboard Quick action and the New invoice button on the list. |
| Invoices | Edit | invoice.edit | Update invoice fields from the detail modal / edit panel. |
| Invoices | Delete | invoice.delete | Hard-delete an invoice and all its child records. |
| Invoices | Resend to PA | invoice.resend | Submit / re-submit an invoice (or a bulk batch) to the PA. |
| Invoices | Push status (PA) | invoice.status.pa | Send a status event through the PA — payment received, in dispute, etc. (the Set Status modal's PA tab). |
| Invoices | Push status (DB) | invoice.status.db | Directly update a status in the database, bypassing the PA — admin break-fix path used when the PA round-trip is broken. |
| Invoices | Validate UBL | invoice.validate | Run XSD + Schematron validation against the stored UBL XML (Validate button in the History tab). |
| Invoices | Download UBL | invoice.download | Read the raw UBL XML BLOB — the Download UBL button and the underlying /xml endpoint. |
| Invoices | Preset actions | invoice.preset-action | Use the per-status preset buttons (Resend on 9904, …) in the seller actions row. |
| Invoices | Custom actions | invoice.custom-action | Use the admin-defined custom buttons in the custom actions row. |
| Invoices | Email PDF | invoice.email | Send the rendered PDF via the configured SMTP relay. |
| E-Reporting | Generate batch | ereporting.generate | Build a new e-reporting batch from the Generate dialog. |
| E-Reporting | Resend batch | ereporting.resend | Re-submit an existing e-reporting batch to the PA. |
| Integration ops | Run batch jobs | integration.run | Trigger Import statuses / Fetch received / Retrieve statuses from the toolbar. |
The detail modal renders Parties, Invoice Lines, TVA recap and Notes from the UBL XML endpoint, which is read-open (still subject to row filters and page visibility). Only the explicit Download UBL button is gated by invoice.download — view-only roles can therefore inspect the invoice content without the right to extract the raw XML.
Tab — Data scope
Defines which rows the role can see — companies and optional row filters.
Companies
An add-row table of company codes (KCO) the role is scoped to. Each row carries a free-form input and a × button to remove it; + Add company at the bottom appends a new row.
- Empty list = all companies. This is the typical default — leaving the table empty grants the role access to every company in the database.
- Adding even one row restricts the role to just the listed companies.
- The placeholder hint reads KCO code (e.g. 00001) and the input uses a monospace font to make typos easier to spot.
Row filters
A more granular restriction: for any catalog column flagged as filterable, the role can be restricted to one or more exact values. Typical use case — an external customer who should only see invoices issued to its own alpha key (UHALKY).
Each row filter is a card with:
- A searchable column picker showing every filterable column across the four catalogs:
- Invoices (header columns:
UHALKY,UHAN8, customer name, contract reference, …). - Integration errors (the integration error catalog's row-filterable columns).
- Processing log (processing-log catalog columns).
- E-Reporting (e-reporting catalog columns).
- Invoices (header columns:
- A list of values for the picked column, each on its own row with its own remove button. + Add value (OR) appends another value.
- A row-level remove button to drop the entire filter.
Below the list, + Add row filter lets a single role combine filters on different columns.
Combination rules
| Pattern | Combined as |
|---|---|
| Multiple values on the same column | OR — the role sees rows that match any of the values. |
| Multiple columns in the filter list | AND — the role sees only rows that match every column constraint. |
| Companies grant + row filters | AND — both must be satisfied. |
Where row filters apply
A row filter is not a UI hint — it is enforced everywhere a forbidden row could otherwise leak:
- The list views (Invoices, E-Reporting, Integration Errors, Processing Log) — the filter is appended to the SQL.
- The Dashboard counters, charts and widgets — every card respects the role's row filter.
- Per-row endpoints — lifecycle, lines, XML download, PDF render, status push, delete, resend, email PDF.
- The rendered PDF byte stream — a forbidden row cannot even be turned into a PDF.
Forbidden rows return the same "not found" shape the UI uses for genuinely missing data, so the response cannot be used to probe for invoices a role should not know exist.
Tab — Members
Available only when editing an existing role (hidden during creation).
Lists every user currently bound to the role:
| Column | Description |
|---|---|
| Username | The user's login. |
| Full name | The user's display name (or – when not set). |
| Status | Active (green) or Inactive (red). |
This view is read-only — to add or remove a user from a role, edit the user from Configuration → Security → Users.
Save / Cancel
- Create (when creating) / Save (when editing) persists the role and refreshes the list — available on every tab except Members.
- Cancel discards changes and closes the panel.
- Inline status messages appear below the panel (
Role created,Role updated,Role deleted, error messages).
How grants are stored
Every grant is one row in F564254. The shape:
F564254
PMROLE — role name (FK to F564251.RLNAME)
PMCRAPPID — grant type: 'page' / 'company' / 'feature' / 'action'
/ 'dashboard-card' / 'row-filter'
PMCRAPPVAL — grant value, encoded per type:
page → page id (e.g. 'invoices')
company → KCO code (e.g. '00001')
feature → flag ('settings', 'readonly', 'actions-whitelist')
action → action key (e.g. 'invoice.delete')
dashboard-card → card key (e.g. 'dashboard.volume')
row-filter → 'column=value' (e.g. 'UHALKY=123456')
PMENABL — '1' enabled / '0' disabled (used during dialect-specific bootstrapping)
Row-filter grants are persisted as flat column=value strings — one row per (column, value) pair. The editor groups them by column on load and re-flattens them on save, so the operator never sees the raw shape.
Adding a future permission dimension becomes an INSERT in this same table — no DDL change required.
The bootstrap is idempotent: dropping F564254 and re-running Initialize Database re-seeds the default admin and viewer grants without touching role rows. The init log reports the count of newly-inserted grants for visibility.
Delete a role
Clicking the 🗑 icon on a role card opens a confirmation modal:
Delete role "X"? Users assigned to this role will lose their permissions.
Confirming removes the role and every grant row in F564254 keyed by it. Users previously bound to the role keep their account but lose every permission until they are reassigned to another role.
Tips & best practices
- Create one role per profile, not per individual.
operator,auditor,customer_<name>are simpler to maintain than per-user roles. - Grant Settings access sparingly. It opens the entire Configuration menu — limit it to a small admin group.
- Pair Read-only with Actions off for compliance / audit accounts — both layers reinforce each other.
- Use the Companies table to enforce multi-tenant isolation. Leaving it empty defeats company-level filtering for the role.
- Row filters first, action whitelist second when scoping an external user. The row filter hides everything the user should not see; the action whitelist controls what they can do with what they see.
- The dashboard card whitelist runs server-side — disabled cards never query the database. Use it to hide cost-sensitive widgets from low-trust roles.
- Use Copy when forking a role. Starting from an existing role with one or two grants flipped is faster than rebuilding the checklists from scratch — and the result is closer to the source role's intent.
- Re-run Initialize Database (Database Connectors → NomaUBL) if the default
adminorviewergrants are missing — it re-seeds the rows without touching custom roles. - Delete a role only after re-binding its members. Once deleted, members lose access to everything until reassigned. The Members tab is the fastest way to check who would be affected.