Workflows
Workflow definition syntax — initiation, steps, approvals, conditional transitions, and automated actions.
Workflow Definition
P9 workflows define multi-step approval processes with conditional routing, dynamic approvals, and automated actions. Workflows are defined in .workflow files.
Structure
@workflow WorkflowName
@application ApplicationName
@module "Module Name"
@company CompanyName
initiate {
@model ObjectName
@trigger Manual
on (submitted) {
start "First Step"
}
}
step "Step Name" {
@model ObjectName
approvals {
from "role_name"
}
on (approved) {
next "Next Step"
}
on (rejected) {
finish
}
}
Workflow Declaration
Every workflow starts with metadata:
@workflow ExpenseApproval
@application Finance
@module "Finance"
@company AlFahim
| Directive | Required | Description |
|---|---|---|
@workflow |
Yes | Unique workflow name (PascalCase) |
@application |
No | Application this workflow belongs to |
@module |
No | Human-readable module name |
@company |
No | Company this workflow applies to (omit for generic) |
Generic Workflows:
For workflows that apply across all companies, use the generic:true attribute:
@workflow GenericApprovalFlow generic:true
initiate {
on (submitted) {
start "Request for Approval"
}
}
Initiate Block
The initiate block defines how the workflow starts:
initiate {
@model Expense
@trigger Manual
on (submitted) {
start "Manager Approval"
}
}
Conditional Start:
Use when conditions to start different steps based on record data:
initiate {
@model PurchaseOrder
on (submitted) {
start "Budget Check"
} when ({Amount} > 5000)
on (submitted) {
start "Manager Approval"
} when ({Amount} <= 5000)
}
Step Blocks
Each step represents a stage in the workflow:
step "Manager Approval" {
@model Expense
approvals {
from "Department_Manager"
}
on (submitted) {
status "InProgress"
}
on (approved) {
status "Approved"
finish
}
on (rejected) {
status "Rejected"
finish
}
}
Workflow Events
| Event | Description |
|---|---|
submitted |
Record submitted to workflow |
submitting |
Before submission |
created |
Record created in database |
creating |
Before creation |
updated |
Record updated |
updating |
Before update |
approved |
Approver approved the record |
rejected |
Approver rejected the record |
validated |
Custom validation passed |
notvalidated |
Custom validation failed |
validating |
Before validation runs |
finalized |
Record marked final |
void |
Record voided |
Approval Configurations
Simple Role Approval:
approvals {
from "Finance_Controller"
}
Dynamic Approval Map:
approvals {
from {ApprovalMap}
}
Approval with Label:
approvals {
from "Department_Manager" label "Manager Approval"
}
Custom Approval Buttons:
approvals {
from {ApprovalMap} buttons:[approved,rejected]
}
Workflow Actions
Navigation Actions:
start "Step Name" // Start a workflow step
next "Step Name" // Move to next step
finish // Complete workflow
Status Actions:
Update record status:
on (approved) {
status "Approved"
finish
}
on (rejected) {
status "Rejected"
finish
}
Assignment Actions:
on (validating) {
assign "procurement_officer"
}
on (creating) {
assign type:User field:"Manager"
}
Edit Actions:
on (updating) {
edit "sales_team" label "Upload Document"
}
Notification Actions:
on (created) {
notifications {
channel:"Email"
pipelines:"Workflow"
to: "{Tenant.Email}"
cc: "manager@company.com"
subject:"{Tenant.Welcome}"
message:"{Tenant.Welcome}"
}
finish
}
Complete Example: Expense Approval
@workflow ExpenseFlow
@application Finance
@module "Finance"
@company AlFahim
initiate {
@model Expense
on (submitted) {
start "Approval from Manager"
}
}
step "Approval from Manager" {
@model Expense
approvals {
from "Payable_Controller"
}
on (submitted) {
status "InProgress"
}
on (approved) {
status "Approved"
finish
}
on (rejected) {
status "Rejected"
finish
}
}
Complete Example: Multi-Step Procurement
@workflow Procure2PayFlow
@application Finance
@module "Procure to Pay"
@company AlFahim
initiate {
@model MaterialRequest
on (submitted) {
start "Material Requisition"
}
}
step "Material Requisition" {
@model MaterialRequest
approvals {
from {ApprovalMap}
}
on (approved) {
status "Approved"
next "Check Stock"
}
on (rejected) {
status "Rejected"
finish
}
}
step "Check Stock" {
@model MaterialRequest
on (validating) {
assign "procurement_warehousing"
}
on (validated) {
next "Stock Transfer"
}
on (notvalidated) {
next "Request for Quote"
}
}
step "Stock Transfer" {
@model StockTransfer
on (creating) {
assign "procurement_warehousing"
}
on (created) {
status "Closed"
finish
}
}
step "Request for Quote" {
@model RequestForQuotation
on (creating) {
assign "procurement_purchasing"
}
on (created) {
next "Purchase Order"
}
}
step "Purchase Order" {
@model PurchaseOrder
approvals {
from {ApprovalMap}
}
on (approved) {
status "POApproved"
next "Goods Receipt"
}
on (rejected) {
status "Rejected"
finish
}
}
step "Goods Receipt" {
@model GoodsReceiptNote
on (created) {
status "Received"
next "Invoice"
}
}
step "Invoice" {
@model PurchaseInvoice
on (finalized) {
status "Closed"
finish
}
}
Complete Example: Leasing Flow with Notifications
@workflow LeasingFlow
@application Leasing
@module "Leasing"
@company AlFahim
initiate {
@model Contract
on (submitted) {
start "Create Tenancy Contract"
} when ({ContractInitiationType} = 'New')
}
step "Create Tenancy Contract" {
@model Contract
on (submitted) {
status "Open"
next "Upload Sign Copy"
}
}
step "Upload Sign Copy" {
@model Contract
on (updating) {
edit sales_team label "Upload"
}
on (updated) {
next "Supervisor Approve"
}
}
step "Supervisor Approve" {
@model Contract
approvals {
from "leasing_executive" label "Supervisor Approve"
}
on (approved) {
status "ContractApproved"
next "Payments"
}
on (rejected) {
status "Open"
finish
}
}
step "Payments" {
@model PaymentReceipt
on (creating) {
assign "cash_officer"
}
on (created) {
status "Payment"
notifications {
channel:"Email"
pipelines:"Workflow"
to: "{Tenant.Email}"
cc: "manager@company.com"
subject:"Welcome to {Building.Name}"
message:"{Tenant.WelcomeMessage}"
}
finish
}
}
Best Practices
- Use descriptive step names — "Approval from Manager" instead of "Step 1"
- Always handle approval outcomes — Include both
approvedandrejectedevents - Execute status updates — Keep workflow status in sync with business logic
- Use conditional starts — Apply
whenconditions for complex initiation logic - Leverage approval maps — Use
{ApprovalMap}for flexible, configurable approvals - Send notifications — Keep users informed at critical steps
- Assign appropriately — Use role-based or field-based assignment
- Handle validation — Include
validatedandnotvalidatedevents for conditional logic - Document with labels — Use
labelattribute in approvals for clarity - Finish workflows — Always include
finishat terminal steps