Skip to main content

Multi-step form / workflow

The problem​

An expense claim goes through Draft → Submitted → Approved / Rejected. Each transition belongs to a given role; some fields only matter in some states; a claim waiting too long must be chased.

The pattern​

NeedMechanism
A transition that only applies from the right stateOne write query per transition, with AND STATUS = '<expected>' in its WHERE.
A button per transitionA row-menu action: a chain with confirmation, prompt and query.
Only managers approveThe transition query's permission, granted to the managers' role.
Fields by statevisible_when, required_when, disabled_when on the columns.
Frozen once decidedThe screen's editable_when.
Warn the right peopleNotification rules on the screen's writes.
Chase stalled claimsA daily Nomaflow job.

The recipe​

1. Table​

CREATE TABLE expense_claims (
id INTEGER PRIMARY KEY,
employee VARCHAR(64) NOT NULL,
amount DECIMAL(10,2) NOT NULL,
description VARCHAR(2000),
status VARCHAR(16) NOT NULL DEFAULT 'DRAFT',
submitted_at TIMESTAMP,
decided_by VARCHAR(64),
decided_at TIMESTAMP,
reject_reason VARCHAR(500),
reminded_at TIMESTAMP
);

2. Status list​

Liberty Next → Build → Dictionary → Enums: an enum CLAIM_STATUS with the values DRAFT (Draft), SUBMITTED (Submitted), APPROVED (Approved), REJECTED (Rejected). The STATUS entry gets Rules ENUM, Rules values CLAIM_STATUS: the grid shows the labels and the dialog a list.

3. One query per transition​

In Liberty Next → Build → Connectors → expenses → Queries, three writable queries:

-- submit_claim
UPDATE expense_claims
SET STATUS = 'SUBMITTED', SUBMITTED_AT = {{SYSTIMESTAMP}}
WHERE ID = :ID AND STATUS = 'DRAFT' AND EMPLOYEE = {{LOGIN}}

-- approve_claim
UPDATE expense_claims
SET STATUS = 'APPROVED', DECIDED_BY = {{LOGIN}}, DECIDED_AT = {{SYSTIMESTAMP}}
WHERE ID = :ID AND STATUS = 'SUBMITTED'

-- reject_claim
UPDATE expense_claims
SET STATUS = 'REJECTED', DECIDED_BY = {{LOGIN}}, DECIDED_AT = {{SYSTIMESTAMP}},
REJECT_REASON = :REJECT_REASON
WHERE ID = :ID AND STATUS = 'SUBMITTED'

The AND STATUS = … guard makes a transition from the wrong state change nothing; EMPLOYEE = {{LOGIN}} lets employees submit only their own claims ({{LOGIN}} is the user name in upper case, as EMPLOYEE must be stored).

4. The transition buttons​

In the screen editor, Row menu tab, one entry per transition:

[[screens.expenses.expense_claims.row_menu]]
type = "chain"
id = "reject"
label = "Reject"
prompt_title = "Reject the claim"
prompt_fields = [{ name = "REJECT_REASON", label = "Reason", required = true }]
steps = [
{ type = "run_query", id = "do", query = "reject_claim",
param_binds = [{ param = "ID", source = "ID" },
{ param = "REJECT_REASON", source = "INPUT.REJECT_REASON" }] },
{ type = "refresh", id = "reload" },
]

Approve follows the same shape with a confirm step instead of the prompt; Submit with submit_claim. To show a clear message when the claim is not in the right state, wrap the steps in an if on STATUS (equals SUBMITTED): then_steps run the query, else_steps a notify. See Actions and lifecycle.

5. Who may do what​

Each query has its own permission: sql:expenses:submit_claim, sql:expenses:approve_claim, sql:expenses:reject_claim. In Liberty Next → Administer → Access → Roles, grant the first one to employees and the other two to managers only: an employee who clicks Approve gets a refusal. See Roles & permissions.

6. Fields by state​

On the columns (Edit and Filter tabs of the inspector):

ColumnSetting
DESCRIPTION, AMOUNTrequired_when STATUS = DRAFT
SUBMITTED_ATvisible_when STATUS = SUBMITTED, APPROVED, REJECTED; disabled
DECIDED_BY, DECIDED_ATvisible_when STATUS = APPROVED, REJECTED; disabled
REJECT_REASONvisible_when STATUS = REJECTED; disabled

And on the General tab, editable_when STATUS = DRAFT: once submitted, the claim opens read-only and only the row-menu transitions change it. See Form conditions.

7. Notifications​

In Liberty Next → Build → Notifications, a rule on the screen's update event, Only when the written row has STATUS = SUBMITTED, channel portal and email, recipients role:managers, subject Claim {ID} of {EMPLOYEE} to approve. A second rule on STATUS = REJECTED can warn the requester. See Notifications.

8. Reminders​

A Nomaflow job, every morning, with a sql_query step:

UPDATE expense_claims SET REMINDED_AT = CURRENT_TIMESTAMP
WHERE STATUS = 'SUBMITTED' AND SUBMITTED_AT < CURRENT_TIMESTAMP - INTERVAL '5' DAY

and a notification rule on that job's succeeded event: {ROWS} claims waiting for more than 5 days, to role:managers.

Variations​

NeedHow
History of every transitionThe screen's audit_table — see Audit trail.
Reopen a rejected claimA reopen_claim query (STATUS = 'DRAFT' … WHERE STATUS = 'REJECTED') and its row-menu entry.
A long form in stepsDialog tabs, with hide_on_add / hide_on_edit for the tabs that only make sense in one mode.

What's next​