Code Examples
Practical examples for common P9 patterns, API integrations, and real-world configurations.
P9 Examples
Complete Object: Service Request
A facilities service request with cascading lookups, conditional fields, and table details:
@object ServiceRequest
@application FacilityManagement
@icon "clipboard-icon"
@llmspec "Tenant service request for maintenance. Links to Building, Unit, Category. Creates WorkOrders."
field Number type:Autonumber format:"SR-{yy}-#" label:"Number" search-filter:true filter-order:"1"
field Name type:Text label:"Title" required:true search-field:true
field Description type:Text label:"Description" multi-line:true maximum:"2000" optional:true
field Building type:Lookup label:"Building" required:true advance-search:true linked-view:true
field Unit type:Lookup label:"Unit" optional:true parent:Building advance-search:true
field Tenant type:Lookup label:"Tenant" optional:true advance-search:true linked-view:true
field Category type:Lookup label:"Category" optional:true advance-search:true
field SubCategory type:Lookup label:"Sub-Category" optional:true parent:Category
field Priority type:Select label:"Priority"
field Photos type:Attachment label:"Photos" optional:true
field Status type:Select label:"Status"
field Supervisor type:Lookup label:"Assigned Supervisor" optional:true advance-search:true
select Priority {
label:"Low" value:"Low"
label:"Medium" value:"Medium" default:true
label:"High" value:"High"
label:"Critical" value:"Critical"
}
select Status {
label:"Open" value:"Open" default:true
label:"Pending" value:"Pending"
label:"InProgress" value:"InProgress"
label:"Scheduled" value:"Scheduled"
label:"Completed" value:"Completed"
label:"Closed" value:"Closed"
label:"Cancelled" value:"Cancelled"
}
lookup Building {
lookupobjectname:"Building"
displayfield {
"Name"
"Number"
"Emirate"
}
filter {
Status="Active"
}
}
lookup Unit {
displayfield {
"Name"
"Floor"
"Building"
"UnitProperty"
}
parents {
Building {
parentobjectfield: "Building"
}
}
filter {
Status in "Occupied,Available,Active"
}
}
lookup Tenant {
lookupobjectname:"Tenant"
displayfield {
"Name"
"Number"
"Email"
"Phone"
}
filter {
Status="Active"
}
}
lookup Category {
lookupobjectname:"Category"
}
lookup SubCategory {
lookupobjectname:"SubCategory"
displayfield {
"Name"
"Category"
}
parents {
Category {
parentobjectfield: "Category"
}
}
}
lookup Supervisor {
lookupobjectname:"Employee"
displayfield {
"Name"
"Designation"
}
filter {
Designation = "Supervisor"
Status = "Active"
}
}
@layout
form {
create {
title:"Create Service Request"
savebuttontext:"Save"
cancelbuttontext:"Cancel"
section {
title:"Request Details"
subtitle:"Basic information"
row {
layout:6
fields Name
}
row {
layout:12
fields Description
}
}
section {
title:"Location"
subtitle:"Building and unit"
row {
layout:6,6
fields Building,Unit
}
row {
layout:6
fields Tenant
}
}
section {
title:"Classification"
subtitle:"Category and priority"
row {
layout:6,6
fields Category,SubCategory
}
row {
layout:6
fields Priority
}
}
section {
title:"Attachments"
subtitle:"Upload photos"
row {
layout:12
fields Photos
}
}
}
list {
mobile {
fields Number,Name,Building,Priority,Status
}
desktop {
fields Number,Name,Building,Unit,Category,Priority,Status,Supervisor
}
}
}
Object with Widgets: Work Order
An object with embedded dashboard widgets for quick insights:
@object WorkOrder
@application FacilityManagement
@icon "tool"
field Number type:Autonumber format:"WO-{yy}-#" filter-order:"1" search-filter:true
field Name type:Text label:"Title"
field Type type:Select label:"Type"
field Description type:Text label:"Description" multi-line:true maximum:"2000" optional:true
field Building type:Lookup optional:true
field Priority type:Select label:"Priority" optional:true
field Status type:Select label:"Status"
field Supervisor type:Lookup label:"Supervisor" optional:true advance-search:true
select Type {
label:"Reactive" value:"Reactive" default:true
label:"Scheduled" value:"Scheduled"
}
select Priority {
label:"Low" value:"Low"
label:"Medium" value:"Medium" default:true
label:"High" value:"High"
label:"Critical" value:"Critical"
}
select Status {
label:"Pending" value:"Pending" default:true
label:"Active" value:"Active"
label:"On Hold" value:"OnHold"
label:"Completed" value:"Completed"
label:"Closed" value:"Closed"
}
widgets {
default {
scorecard TotalWorkOrders {
label: "Total Work Orders"
order: 1
query: "SELECT COUNT(*) AS value FROM wh_FacilityManagement_WorkOrder WHERE IsDeleted = false"
}
scorecard ActiveWorkOrders {
label: "Active"
order: 2
query: "SELECT COUNT(*) AS value FROM wh_FacilityManagement_WorkOrder WHERE Status IN ('Pending', 'Active', 'OnHold') AND IsDeleted = false"
}
scorecard CompletedWorkOrders {
label: "Completed"
order: 3
query: "SELECT COUNT(*) AS value FROM wh_FacilityManagement_WorkOrder WHERE Status = 'Completed' AND IsDeleted = false"
}
pie WorkOrdersByStatus {
label: "By Status"
order: 4
query: "SELECT Status AS label, COUNT(*) AS value FROM wh_FacilityManagement_WorkOrder WHERE IsDeleted = false GROUP BY Status"
}
pie WorkOrdersByPriority {
label: "By Priority"
order: 5
query: "SELECT COALESCE(Priority, 'Not Set') AS label, COUNT(*) AS value FROM wh_FacilityManagement_WorkOrder WHERE IsDeleted = false GROUP BY Priority"
}
}
}
API Integration: JavaScript
Fetch and display contracts using the Tuli REST API:
const TULI_API = 'https://instance.tuli.io/api/v1';
const API_KEY = 'tuli_sk_live_...';
// List active contracts with pagination
async function getActiveContracts(page = 1) {
const response = await fetch(
`${TULI_API}/contracts?filter=Status eq 'Active'&sort=-MonthlyRent&page=${page}&pageSize=50&expand=Tenant,Building`,
{
headers: {
'Authorization': `Bearer ${API_KEY}`,
'Content-Type': 'application/json'
}
}
);
return response.json();
}
// Response format uses string Keys (not numeric IDs)
// {
// "data": [{
// "Id": "contract-1CAjiqwbu2g3LkcoByCL4Y", // Key, not integer
// "ContractNumber": "CON-2025-00042",
// "Tenant": {
// "Id": "tenant-2DBkjrxcv3h4MldpCzDM5Z",
// "Name": "Acme Corp"
// },
// "MonthlyRent": { "Amount": 5000, "Currency": "AED" },
// "Status": "Active"
// }],
// "pagination": { "page": 1, "pageSize": 50, "totalRecords": 142 }
// }
// Create a new contract
async function createContract(data) {
const response = await fetch(`${TULI_API}/contracts`, {
method: 'POST',
headers: {
'Authorization': `Bearer ${API_KEY}`,
'Content-Type': 'application/json'
},
body: JSON.stringify({
Tenant: data.tenantKey, // Use Key, not numeric ID
Building: data.buildingKey,
Unit: data.unitKey,
MonthlyRent: { Amount: data.rent, Currency: 'AED' },
StartDate: data.startDate,
Duration: data.months,
LeaseType: 'New'
})
});
return response.json();
}
// Get single record by Key
async function getContract(key) {
const response = await fetch(`${TULI_API}/contracts/${key}`, {
headers: { 'Authorization': `Bearer ${API_KEY}` }
});
return response.json();
}
Standalone Dashboard
A standalone dashboard with time period filtering and multiple widget types:
@dashboard FacilityDashboard
@application FacilityManagement
@description "Facility management overview"
@time-period-filter true
scorecard TotalWorkOrders {
label: "Total Work Orders"
value-key: "Total"
query: "SELECT COUNT(*) AS Total FROM wh_FacilityManagement_WorkOrder WHERE IsDeleted = false"
widget-filter {
"toYear(CreatedAt)" = @year
}
}
scorecard OpenWorkOrders {
label: "Open"
value-key: "Total"
query: "SELECT COUNT(*) AS Total FROM wh_FacilityManagement_WorkOrder WHERE Status IN ('Pending', 'Active') AND IsDeleted = false"
widget-filter {
"toYear(CreatedAt)" = @year
}
}
scorecard CompletedThisMonth {
label: "Completed (MTD)"
value-key: "Total"
query: "SELECT COUNT(*) AS Total FROM wh_FacilityManagement_WorkOrder WHERE Status = 'Completed' AND IsDeleted = false AND toStartOfMonth(CreatedAt) = toStartOfMonth(today())"
}
kpicard AvgResolutionTime {
label: "Avg Resolution"
primary-value-key: "AvgDays"
primary-value-format: "number"
secondary-value-key: "TotalCompleted"
query: "SELECT AVG(dateDiff('day', CreatedAt, ClosingTime)) as AvgDays, COUNT(*) as TotalCompleted FROM wh_FacilityManagement_WorkOrder WHERE Status = 'Completed' AND ClosingTime IS NOT NULL AND IsDeleted = false"
widget-filter {
"toYear(CreatedAt)" = @year
}
}
pie WorkOrdersByStatus {
label: "By Status"
label-key: "Status"
value-key: "Count"
query: "SELECT Status AS Status, COUNT(*) AS Count FROM wh_FacilityManagement_WorkOrder WHERE IsDeleted = false GROUP BY Status"
widget-filter {
"toYear(CreatedAt)" = @year
}
group-by {
key: "Status"
}
}
bar MonthlyTrend {
label: "Monthly Trend"
x-axis-key: "Month"
y-axis-key: "Count"
query: "SELECT formatDateTime(CreatedAt, '%b') as Month, COUNT(*) as Count FROM wh_FacilityManagement_WorkOrder WHERE IsDeleted = false GROUP BY Month ORDER BY toMonth(CreatedAt)"
widget-filter {
"toYear(CreatedAt)" = @year
}
group-by {
key: "formatDateTime(CreatedAt, '%b')"
}
}
aging WorkOrderAging {
label: "Open Request Aging"
tint: "negative"
query: "SELECT CASE WHEN dateDiff('day', CreatedAt, today()) <= 7 THEN '0-7 days' WHEN dateDiff('day', CreatedAt, today()) <= 30 THEN '8-30 days' WHEN dateDiff('day', CreatedAt, today()) <= 60 THEN '31-60 days' ELSE '60+ days' END AS label, COUNT(*) AS value FROM wh_FacilityManagement_WorkOrder WHERE Status NOT IN ('Completed', 'Closed', 'Cancelled') AND IsDeleted = false GROUP BY label"
}
Workflow Example
A multi-step approval workflow:
@workflow ServiceRequestFlow
@application FacilityManagement
@module "Facility Management"
initiate {
@model ServiceRequest
on (submitted) {
start "Supervisor Review"
}
}
step "Supervisor Review" {
@model ServiceRequest
approvals {
from "FM_Supervisor"
}
on (approved) {
status "Scheduled"
next "Create Work Order"
}
on (rejected) {
status "Cancelled"
finish
}
}
step "Create Work Order" {
@model WorkOrder
on (creating) {
assign "FM_Supervisor"
}
on (created) {
status "InProgress"
finish
}
}
RBAC Example
Role definitions with row-level filtering:
@company "{company}"
role FM_Technician {
allow ServiceRequest {
view
}
allow WorkOrder {
view {
filter: " Supervisor = '{User.RecordId}' OR Team = '{User.RecordId}' "
}
update
patch
}
allow WorkOrderJob {
view {
filter: " Team = '{User.RecordId}' "
}
update
patch
}
allow Employee {
view
}
allow Building {
view
}
allow Unit {
view
}
}
role FM_Supervisor {
allow FacilityManagement
allow Employee {
view
}
allow Building {
view
}
allow Unit {
view
}
report *
}
Field Trigger Example
Automated expiry notifications:
@object Supplier
@application Procurement
field TRNExpiryDate type:Date label:"TRN Expiry Date" optional:true
trigger TRNExpiryDate {
intervals: [90d, 60d, 30d, 7d]
condition: [Status == "Active"]
task {
assign-to: "role:ComplianceOfficer"
subject: "TRN Expiry Review: {Name} ({Number})"
}
notification {
channel: email
template: "trn-expiry-reminder"
to: "{ContactEmail}"
cc: "{Supervisor.Email}"
}
}