Build a workflow: Expense Report approval
A state machine turns a plain status field into a guided workflow: an instance can only move between states along the transitions you define, and each move can run checks or follow-up steps before it's confirmed. This walkthrough builds a complete example — an Expense Report that flows Draft → Submitted → Approved or Rejected → Paid — so you can see how every piece fits together.
You'll combine building blocks Yellow already provides:
- an enumeration whose items are the states,
- a State Machine constraint that says "this model's status lives in this property and uses this enumeration",
- transition records that define the allowed moves, and
- a reference constraint so the status field shows as a dropdown.
What you'll build
| Piece | Purpose |
|---|---|
ExpenseReport model |
The thing that has a workflow — Title, Amount, Submitter, and a State property. |
ExpenseReportStatus enumeration |
The set of states: Draft, Submitted, Approved, Rejected, Paid. |
| State Machine constraint | Binds the State property of ExpenseReport to the ExpenseReportStatus enumeration. |
| Transitions | The allowed moves between states. |
RejectionInfo model |
Extra data captured when a report is rejected. |
Step 1 — Create the status enumeration
Create an enumeration named ExpenseReportStatus and add one item per state. Give each item a Name (shown to users) and an Id (a number you choose, used for ordering):
| Item | Id |
|---|---|
| Draft | 1 |
| Submitted | 2 |
| Approved | 3 |
| Rejected | 4 |
| Paid | 5 |
Result: an enumeration with five items, listed in Id order.
Step 2 — Create the ExpenseReport model
Create a model named ExpenseReport with these properties:
| Property | Type | What it does |
|---|---|---|
| Title | Text | A short title for the report. |
| Amount | Number | The total amount. |
| Submitter | Text | Who submitted it. |
| State | Reference | Holds the current state — it points at an item of ExpenseReportStatus. |
The State property is the one the workflow moves; the other three are ordinary fields.
Step 3 — Make State a dropdown
Add a reference constraint on the State property so it only accepts the statuses:
- Target Model:
EnumerationItem - Enumeration:
ExpenseReportStatus - Required: yes
Result: when you edit an expense report, State shows as a dropdown of the five statuses instead of a free reference picker.
Step 4 — Declare the state machine
Add a State Machine constraint with:
- Model:
ExpenseReport - Property:
State - State Machine:
ExpenseReportStatus
This tells Yellow that ExpenseReport has a workflow and which property holds its status.
Step 5 — Define the transitions
Create one transition per allowed move. Each transition names the State Machine, an Initial State, a Final State, and a Direct setting. Some also point at an Additional Info model for extra data.
| From | To | Direct | Additional Info |
|---|---|---|---|
| Draft | Submitted | Yes | — |
| Submitted | Approved | No | — |
| Submitted | Rejected | No | RejectionInfo |
| Approved | Paid | No | — |
For the Reject transition, first create a small RejectionInfo model with Reason (Text) and Rejected By (Text). Whoever rejects a report fills these in.
Direct vs. non-direct moves
The Direct setting decides how a move is applied:
- Direct = Yes — the move happens immediately when you save. Draft → Submitted is direct: submitting is always allowed, so the state changes right away.
- Direct = No — the move is recorded as a request and applied afterwards, once Yellow has run the model's validations and any follow-up steps. Submitted → Approved is non-direct: approval can depend on rules, so it's checked before the state changes.
A non-direct move that can't be completed comes back with the reason, and the state stays where it was.
Using it
Open an expense report in Draft and look for the Save & transition menu next to Save. It lists only the states you can reach from the current one — from Draft you'll see Submitted.
- Pick Submitted. Because that move is direct, the report changes to Submitted as soon as it saves.
- From Submitted, pick Approved or Rejected. These are non-direct: Yellow records the request and resolves it, then updates the state if the rules pass.
- Choosing Rejected first opens a small form — the RejectionInfo fields — so you can capture the reason. Fill it in and confirm.
The State field is read-only in the editor on purpose: the only way to change it is through a transition, which keeps the workflow's rules in force.
Where to find it
The status enumeration and its items live in the Enumerations area. The model, its State property, and both constraints are edited in the model editor. Transitions are ordinary instances of the StateMachineTransition model, so you create and edit them just like any other data.
Related
- Enumerations — the states are an enumeration's items.
- Constraints — the reference and State Machine constraints used here.
- Property types — references and the other property types.