Petriflow process consits of a workflow model, roles, data and actions. As a workflow model, Petriflow uses place/transition Petri nets enriched by reset arcs, inhibitor arcs and read arcs. Transitions of Petri nets represent tasks of workflow models. Roles layer defines who can execute tasks. Data variables represent all attributes of a process instance called case during its life-cycle. Data variables associated to workflow tasks define data fields and create task forms. Workflow model, roles, data variables and data fields defining task forms are stored in XML. Actions are pieces of Groovy code that define reactions to events on tasks (assign event, finish event, cancel event) and events on data fields. In actions, events can be triggered and external fucntions can be called.

For complete documentation and other materials of Petriflow language please visit    

Petriflow Process

Petriflow model

Petriflow model XSD Schema


<xs:element name="document">
    <xs:complexType>
        <xs:sequence>
            <xs:element ref="id" minOccurs="0"/>
            <xs:element ref="version" minOccurs="0"/>
            <xs:element ref="initials" minOccurs="0"/>
            <xs:element name="title" minOccurs="0" type="i18nStringType"/>
            <xs:element ref="icon" minOccurs="0"/>
            <xs:element ref="defaultRole" minOccurs="0"/>
            <xs:element ref="transitionRole" minOccurs="0"/>
            <xs:element ref="caseName" minOccurs="0"/>
            <xs:element ref="transaction" maxOccurs="unbounded" minOccurs="0"/>
            <xs:element ref="role" maxOccurs="unbounded" minOccurs="0"/>
            <xs:element ref="data" maxOccurs="unbounded" minOccurs="0"/>
            <xs:element ref="mapping" maxOccurs="unbounded" minOccurs="0"/>
            <xs:element ref="i18n" maxOccurs="unbounded" minOccurs="0"/>
            <xs:element ref="transition" maxOccurs="unbounded"/>
            <xs:element ref="place" maxOccurs="unbounded" minOccurs="0"/>
            <xs:element ref="arc" maxOccurs="unbounded" minOccurs="0"/>
        </xs:sequence>
    </xs:complexType>
</xs:element>
            

Example of a Petriflow model as an XML document


<document
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance "
    xsi:noNamespaceSchemaLocation="petriflow_schema.xsd">
    <id>1</id>
    <defaultRole>true</defaultRole>
    <caseName>New quote</caseName>
    <transaction>...</transaction>
    ...
    <role>...</role>
    ...
    <data>...</data>
    ...
    <mapping>...</mapping>
    ...
    <i18n>...</i18n>
    ...
    <transition>...</transition>
    ...
    <place>...</place>
    ...
    <arc>...</arc>
    ...
</document>
            

Object of a Petriflow model


PetriNet
    - ObjectId _id
    - String importId
    - String identifier
    - I18nString title
    - I18nString defaultCaseName
    - String initials
    - String icon
    - LocalDateTime creationDate
    - String version
    - Author author
    - Map<String, Place/> places
    - Map<String, Transition/> transitions
    - Map<String, List<Arc/> arcs
    - Map<String, Field/> dataSet
    - Map<String, ProcessRole/> roles
    - Map<String, Transaction/> transactions
    - boolean initialized
    - String importXmlPath
            

Transitions

Transition XSD Schema


<xs:element name="transition">
    <xs:complexType>
        <xs:sequence>
            <xs:element ref="id"/>
            <xs:element ref="x"/>
            <xs:element ref="y"/>
            <xs:element ref="label"/>
            <xs:element ref="icon" minOccurs="0"/>
            <xs:element ref="priority" minOccurs="0"/>
            <xs:element ref="assignPolicy" minOccurs="0"/>
            <xs:element ref="dataFocusPolicy" minOccurs="0"/>
            <xs:element ref="finishPolicy" minOccurs="0"/>
            <xs:element ref="trigger" minOccurs="0" maxOccurs="unbounded"/>
            <xs:element ref="transactionRef" minOccurs="0"/>
            <xs:element ref="roleRef" maxOccurs="unbounded" minOccurs="0"/>
            <xs:element ref="dataRef" maxOccurs="unbounded" minOccurs="0"/>
            <xs:element ref="dataGroup" maxOccurs="unbounded" minOccurs="0"/>
            <xs:element ref="event" maxOccurs="unbounded" minOccurs="0"/>
        </xs:sequence>
    </xs:complexType>
</xs:element>
            

Object of a Transition


Transition
    - ObjectId _id
    - String importId
    - Position position
    - I18nString title;
    - Map<String, DataGroup> dataGroups
    - LinkedHashMap<String, DataFieldLogic> dataSet
    - Map<String, Set<RolePermission> roles
    - List<Trigger> triggers
    - Integer priority
    - AssignPolicy assignPolicy
    - String icon
    - DataFocusPolicy dataFocusPolicy
    - FinishPolicy finishPolicy
    - Map<EventType, Event> events
    - String defaultRoleId
            

Places

Place XSD Schema


<xs:element name="place">
    <xs:complexType>
        <xs:sequence>
            <xs:element ref="id"/>
            <xs:element ref="x"/>
            <xs:element ref="y"/>
            <xs:element ref="label"/>
            <xs:element ref="tokens"/>
            <xs:choice>
                <xs:element ref="isStatic"/>
                <xs:element ref="static"/>
            </xs:choice>
        </xs:sequence>
    </xs:complexType>
</xs:element>
            

Arcs

Arc XSD Schema


<xs:element name="arc">
    <xs:complexType>
        <xs:sequence>
            <xs:element ref="id"/>
            <xs:element name="type" type="arc_type" default="regular"/>
            <xs:element ref="sourceId"/>
            <xs:element ref="destinationId"/>
            <xs:element ref="multiplicity"/>
            <xs:element ref="breakPoint" minOccurs="0" maxOccurs="unbounded"/>
        </xs:sequence>
    </xs:complexType>
</xs:element>
            

Types of arces:

  • Regular arc
  • Reset arc
  • Inhibitor arc
  • Read arc
  • Variable arc

Example of a varieble arc as an XML document


<data type="number">
    <id>500001</id>
    <title>vararc_byt_true</title>
    <init>0.0</init>
</data>
...
<arc>
    <id>3270</id>
    <type>variable</type>
    <sourceId>414</sourceId>
    <destinationId>2284</destinationId>
    <multiplicity>500001</multiplicity>
</arc>
            

Transactions

Place XSD Schema


<xs:element name="transaction">
    <xs:complexType>
        <xs:sequence>
            <xs:element ref="id"/>
            <xs:element ref="title"/>
        </xs:sequence>
    </xs:complexType>
</xs:element>
            

Roles

XSD Schema for Role


<xs:element name="role">
    <xs:complexType>
        <xs:sequence>
            <xs:element ref="id"/>
            <xs:choice>
                <xs:element ref="title"/>
                <xs:element ref="name"/>
            </xs:choice>
        </xs:sequence>
    </xs:complexType>
</xs:element>
            

Data variables

XSD Schema for a data variable


<xs:element name="data">
    <xs:complexType>
        <xs:sequence>
            <xs:element ref="id"/>
            <xs:element ref="title"/>
            <xs:element ref="placeholder" minOccurs="0"/>
            <xs:element ref="desc" minOccurs="0"/>
            <xs:element ref="values" minOccurs="0" maxOccurs="unbounded"/>
            <xs:element ref="valid" minOccurs="0" maxOccurs="unbounded"/>
            <xs:element ref="init" minOccurs="0"/>
            <xs:element ref="encryption" minOccurs="0"/>
            <xs:element ref="action" minOccurs="0" maxOccurs="unbounded"/>
            <xs:element ref="actionRef" minOccurs="0" maxOccurs="unbounded"/>
            <xs:element ref="documentRef" minOccurs="0"/>
            <xs:element ref="remote" minOccurs="0"/>
        </xs:sequence>
        <xs:attribute type="data_type" name="type" use="required"/>
        <xs:attribute type="xs:boolean" name="immediate"/>
    </xs:complexType>
</xs:element>
            

Example of a data variable as an XML document


            <document
                    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance "
                    xsi:noNamespaceSchemaLocation="petriflow_schema.xsd">
                <id>1</id>
                <defaultRole>true</defaultRole>
                <caseName>New quote</caseName>
                <transaction>...</transaction>
                    ...
                <role>...</role>
                    ...
                <data>...</data>
                    ...
                <mapping>...</mapping>
                    ...
                <i18n>...</i18n>
                    ...
                <transition>...</transition>
                    ...
                <place>...</place>
                    ...
                <arc>...</arc>
                    ...
            </document>
            

Process Instances

Cases

Object of a case


Case
    - ObjectId _id
    - String visualId
    - PetriNet petriNet
    - String processIdentifier
    - Map<String, Integer> activePlaces
    - String title
    - String color
    - String icon
    - LocalDateTime creationDate
    - LinkedHashMap<String, DataField> dataSet
    - LinkedHashSet<String> immediateDataFields
    - List<Field> immediateData
    - Author author
    - Map<String, Integer> resetArcTokens
    - Set<TaskPair> tasks
            

Tasks

Object of a task


Task
    - ObjectId _id
    - String processId
    - String caseId
    - String transitionId
    - I18nString title
    - String caseColor
    - String caseTitle
    - Integer priority
    - Long userId
    - User user
    - List<Trigger> triggers
    - Map<String, Map<String, Boolean> roles
    - LocalDateTime startDate
    - LocalDateTime finishDate
    - Long finishedBy
    - String transactionId
    - Boolean requiredFilled
    - LinkedHashSet<String> immediateDataFields
    - List<Field> immediateData
    - String icon
    - AssignPolicy assignPolicy
    - DataFocusPolicy dataFocusPolicy
    - FinishPolicy finishPolicy
            

Action API

Actions

Changes behaviour of given data field f on transition t, if condition returns true. Behaviour can be one of:

  • visible
  • editable
  • required
  • optional
  • hidden
  • forbidden

garage_check: f.garage_check,
garage_cost: f.garage_cost,
garage: t.garage;
make garage_cost,visible on garage when {
	return garage_check.value == true;
}
            

Sets new value to data field f returned by calculation closure. If the returned value is null, fields value is set to default value. If the returned value is unchanged, fields value is unchanged and actions with a trigger set on given field are not triggered.


period: f.108001,
sum: f.308011;
change period value {
    def limit = 20.0;
    if (period.value == "polročná")
        limit = 40.0;
    if (period.value == "štvrťročná")
        limit = 80.0;
    if ((sum.value as Double) < (limit as Double))
        return "ročná";
    return unchanged;
}
                            

Sets a new set of choices to data field f.


other: f.410001,
field: f.this;
change field choices {
    if (other.value == "Nehnutelnost")
        return field.choices + ["rozostavaná stavba"];
    return field.choices;
}
                            

Calls method and saves generated value into data field f. The field can be only of type Text or File. If repeat is equal to always new value is generated on each run of action. If repeat is equal to once new value is generated only if fields value is null.


self: f.this;
generate "Insurance.offerPDF",always into self
                            

Changes the property of the current case, the new value is generated by the supplier.


trans: t.this;
changeCaseProperty "icon" about { trans.icon }
                            

Creates a new instance of the newest version of net identified by the identifier. If the title is not specified, nets default case name is used. If the colour is null, the default colour is used (black at the moment).


createCase("create_case_net","Create Case Case","color-fg-amber-500", otherUser);
createCase("create_case_net","Create Case Case","color-fg-amber-500");
createCase("create_case_net","Create Case Case");
createCase("create_case_net");
                            

Creates a new instance of the given net. If the title is not specified, nets default case name is used. If the colour is null, the default colour is used (black at the moment).


def net = petriNetService.getNewestVersionByIdentifier("insurance")
createCase(net)
createCase(net, "My insurance")
createCase(net, "My insurance", "color-fg-amber-500")
createCase(net, "Other insurance", "color-fg-amber-500", otherUser)
                            

Finds all the cases that match the given predicate. The predicate is a groovy closure that accepts QCase object and returns QueryDSL Predicate.


List<Case> cases = findCases( { it.title.eq("Case 1") } );
...
List<Case> cases = findCases( { it.dataSet.get("name").value.eq("Jozko") } );
                            

Finds all the cases that match the given predicate. The predicate is a groovy closure that accepts QCase object and returns QueryDSL Predicate. Pageable determines the requested page number, page size, sort fields, and sort direction.


// returns the first page of 5 cases sorted by the title
List<Case> cases = findCases( { it.dataSet.get("name").value.eq("Jozko") }, new PageRequest(0, 10, Sort.by("title").ascending() ) );
...
// returns the second page of 5 cases sorted from the newest to oldest
List<Case> cases = findCases( { it.dataSet.get("name").value.eq("Jozko") }, new PageRequest(1, 5, Sort.by("creationDate").descending() ) );
                            

Finds the first case that matches the given predicate. The predicate is a groovy closure that accepts QCase object and returns QueryDSL Predicate.


Case useCase = findCase( { it.title.eq("Case 1") & it.processIdentifier.eq("insurance") } );
...
Case useCase = findCase( { it.dataSet.get("name").value.eq("Jozko") & it.processIdentifier.eq("insurance") } );
                            

Finds all tasks that match the given predicate. The predicate is a groovy closure that accepts QCase object and returns QueryDSL Predicate.


def useCase = findCase(...)
Task task = findTask( { it.caseId.eq(useCase.stringId) & it.transitionId.eq("edit_limit") } );
                            

Finds all tasks that match the given predicate. The predicate is a groovy closure that accepts QCase object and returns QueryDSL Predicate. Pageable determines the requested page number, page size, sort fields, and sort direction.


// find 10 tasks sorted by priority
def newTasks = findTasks( { it.transitionId.eq("new_task") }, new PageRequest(0, 10, Sort.by("priority").descending() ) )
                            

Finds the first task that matches the given predicate. The predicate is a groovy closure that accepts QCase object and returns QueryDSL Predicate.


List<Task> tasks = findTasks( { it.transitionId.eq("edit_limit") } )
...
def useCase = findCase(...)
List<Task> tasks = findTasks( { it.caseId.eq(useCase.stringId) } );
                            

See cancel.


None.
                            

Executes all fireable transitions identified by the transitionId in all case where the predicate returns true. For each task following actions are called:

  • assign to the system user
  • save new data values
  • finish

The predicate is a list of Querydsl queries. Every case property can be used in a query. For more info see querydsl doc and QCase javadoc.


field: f.field;
execute "synchronized" where ([
    "title eq Case 1"
] as List) with ([
    "field": [
        value: 128.0,
        type: "number"
    ]
] as Map)
                            

Assign the task in current case with given transitionId. Optional parameter user identifies actor who will perform assign.


selectedUser: f.select_controler;
if (selectedUser.value) {
    def user = userService.findById(selectedUser.value.id, false)
    assignTask("control", user);
}
                            

Assign the task in current case with given transitionId. Optional parameter aCase identifies case which the task belongs to. Optional parameter user identifies actor who will perform assign.


selectedUser: f.select_controler;
if (selectedUser.value) {
    def aCase = findCase({ it.author.id.eq(selectedUser.value.id) })
    def user = userService.findById(selectedUser.value.id, false)
    assignTask("control", aCase, user);
}
                            

Assign the task to user. Optional parameter user identifies actor who will perform assign.


selectedUser: f.select_controler;
if (selectedUser.value) {
    def usecase = findCase({ it.title("Some case") }).first()
    def task = findTask({ it.importId.eq("control") & it.caseId.eq(usecase.stringId) })
    def user = userService.findById(selectedUser.value.id, false)
    assignTask(task, user);
}
                            

Assign the tasks to user. Optional parameter user identifies actor who will perform assign.


// find all my cases and assign all their control tasks to me
def cases = findCases( { it.author.id.eq(loggedUser().id)) } )
def caseIds = cases.collect { it.stringId }
def tasks = findTasks({ it.importId.eq("control") & it.caseId.in(cases) })
assignTasks(tasks)
                            

Cancels the task in current case with given transitionId. Optional parameter user identifies actor who will perform cancel.


// cancel the task "work_task", currently assigned to me, in the current case
def taskId = "work_task";
cancelTask(taskId);
                            

Cancels the task in current case with given transitionId. Optional parameter aCase identifies case which the task belongs to. Optional parameter user identifies actor who will perform cancel.


def taskId = "work_task";
def aCase = findCase({ it.author.id.eq(loggedUser().id) })
cancelTask(taskId, aCase);
                            

Cancels the provided task. Optional parameter user identifies actor who will perform cancel.


// cancel the task "work_task", currently assigned to me, in the current case
def task = findTask( { it.transitionId.eq("work_task") } );
cancelTask(task);
                            

Cancels all the provided tasks. Optional parameter user identifies actor who will perform cancel.


// cancel the task "work_task", currently assigned to me, in the current case
def tasks = findTasks( { it.transitionId.eq("work_task") } );
cancelTasks(tasks);
                            

Finish the task in current case with given transitionId. Optional parameter user identifies actor who will perform cancel.


// cancel the task "work_task", currently assigned to me, in the current case
def taskId = "work_task";
finishTask(taskId);
                            

Finish the task in current case with given transitionId. Optional parameter aCase identifies case which the task belongs to. Optional parameter user identifies actor who will perform cancel.


// finish the task "work_task", currently assigned to me, in the current case
def taskId = "work_task";
def aCase = findCase({ it.author.id.eq(loggedUser().id) })
finishTask(taskId, aCase);
                            

Finish the provided task. Optional parameter user identifies actor who will perform cancel.


// finish the task "work_task", currently assigned to me in current case
def task = findTask( { it.transitionId.eq("work_task") & it.caseId.eq(useCase.stringId) & it.userId.eq(loggedUser().id) } );
finishTask(task);
                            

Finish all the provided tasks. Optional parameter user identifies actor who will perform cancel.


// finish all the tasks "work_task", currently assigned to me
def tasks = findTasks( { it.transitionId.eq("work_task") & it.userId.eq(loggedUser().id) } );
finishTasks(tasks);
                            

Sets values of data fields on given task. Values are mapped to data fields in dataSet using data fields import Id as key.


def usecase = findCase({ it.title.eq("Limits") }).first()
def task = findTask({ it.caseId.eq(usecase.stringId & it.transitionId.eq("edit_limit")) })
setData(task, [
    "new_limit": [
        "value": "10000",
        "type" : "number"
    ],
])
                            

Sets values of data fields on task of transition in current case. Values are mapped to data fields in dataSet using data fields import Id as key.


transition: t.edit_limit;
setData(transition, [
    "new_limit": [
        "value": "10000",
        "type" : "number"
    ],
])
                            

Sets values of data fields on task identified by transitionId of given case. Values are mapped to data fields in dataSet using data fields import Id as key.


def usecase = findCase({ it.title.eq("Limits") }).first()
setData("edit_limit", usecase, [
    "new_limit": [
        "value": "10000",
        "type" : "number"
    ],
])
                            

Gets all data fields on given task, mapped by its import Id.


actual_limit: f.actual_limit;
def usecase = findCase({ it.title.eq("Limits") }).first()
def task = findTask({ it.transitionId.eq("view_limit") & it.caseId.eq(usecase.stringId) })
def data = getData(task)
change actual_limit value {
    data["remote_limit"].value
}
                            

Gets all data fields on the task of transition in the current case, mapped by its import Id.


view_limit: t.view_limit;
actual_limit: f.actual_limit;
def data = getData(view_limit)
change actual_limit value {
    data["remote_limit"].value
}
                            

Gets all data fields on the task defined by its transitionId in given case, mapped by its import Id.


view_limit: t.view_limit;
def usecase = findCase({ it.title.eq("Limits") }).first()
def data = getData("view_limit", usecase)
change actual_limit value {
    data["remote_limit"].value
}
                            

Assigns role identified by roleId to user. User is optional parameter, default value is currently logged user. Returns updated object of user.


transition: t.task;
assignRole(transition.defaultRoleId);
                            

Transition actions

    isEnabled(): boolean NOT IMPLEMENTED
    assign(): Transition DONE
    isAssignable(): boolean NOT IMPLEMENTED
    display() NOT IMPLEMENTED
    isDisplayable() NOT IMPLEMENTED
    hide() NOT IMPLEMENTED
    isHideable() NOT IMPLEMENTED
    expand() NOT IMPLEMENTED
    isExpandable() NOT IMPLEMENTED
    collapse() NOT IMPLEMENTED
    isCollapsible() NOT IMPLEMENTED
    getData() NOT IMPLEMENTED

Task actions

    reassign() ALIAS
    isAssigned() NOT IMPLEMENTED
    finish() DONE
    isFinishable() NOT IMPLEMENTED
    cancel() DONE
    display() NOT IMPLEMENTED
    isDisplayable() NOT IMPLEMENTED
    hide() NOT IMPLEMENTED
    isHideable() NOT IMPLEMENTED
    expand() NOT IMPLEMENTED
    isExpandable() NOT IMPLEMENTED
    isCollapsible() NOT IMPLEMENTED
    getData() DONE
    setData() DONE

Role actions

    assign(User): Role DONE

User actions

    assign(Role): User DONE

Case actions

    Case.findOne(Predicate): Case DONE
    Case.findAll(Predicate): List DONE
    Case.create(PetriNet): Case DONE

Tasks actions

    Task.findOne(): Task DONE
    Task.findAll(Predicate): List DONE

Examples

Example of an order approval model


<?xml version="1.0" encoding="UTF-8"?>
<document xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="https://modeler.netgrif.com/petriflow_schema.xsd">
	<id>new_model</id>
	<version>1.0.0</version>
	<initials>ORD</initials>
	<title>Order approval</title>
	<icon>store</icon>
	<defaultRole>true</defaultRole>
	<transitionRole>false</transitionRole>
	<!-- TRANSACTIONS -->
	<!-- ROLES -->
	<role>
		<id>customer</id>
		<title>Customer</title>
	</role>
	<role>
		<id>order_admin</id>
		<title>Order admin</title>
	</role>
	<!-- DATA -->
	<data type="enumeration" immediate="true">
		<id>product</id>
		<title>Product</title>
		<desc>Pick one product.</desc>
		<values>Apple</values>
		<values>Banana</values>
		<values>Chery</values>
	</data>
	<data type="number" immediate="true">
		<id>quantity</id>
		<title>Quantity</title>
		<desc>Set number of products.</desc>
	</data>
	<data type="enumeration">
		<id>status</id>
		<title>Status</title>
		<values>Order is finished.</values>
		<values>Order is denied.</values>
	</data>
	<!-- I18NS -->
	<!-- TRANSITIONS -->
	<transition>
		<id>4</id>
		<x>300</x>
		<y>140</y>
		<layout>
			<offset>0</offset>
		</layout>
		<label>Create an order</label>
		<roleRef>
			<id>customer</id>
			<logic>
				<perform>true</perform>
			</logic>
		</roleRef>
		<dataRef>
			<id>product</id>
			<logic>
				<behavior>editable</behavior>
				<behavior>required</behavior>
			</logic>
			<layout>
				<x>0</x>
				<y>0</y>
				<rows>1</rows>
				<cols>2</cols>
				<offset>0</offset>
				<template>material</template>
				<appearance>outline</appearance>
			</layout>
		</dataRef>
		<dataRef>
			<id>quantity</id>
			<logic>
				<behavior>editable</behavior>
				<behavior>required</behavior>
			</logic>
			<layout>
				<x>2</x>
				<y>0</y>
				<rows>1</rows>
				<cols>2</cols>
				<offset>0</offset>
				<template>material</template>
				<appearance>outline</appearance>
			</layout>
		</dataRef>
	</transition>
	<transition>
		<id>5</id>
		<x>460</x>
		<y>140</y>
		<layout>
			<offset>0</offset>
		</layout>
		<label>Order approval</label>
		<roleRef>
			<id>order_admin</id>
			<logic>
				<perform>true</perform>
			</logic>
		</roleRef>
		<dataRef>
			<id>product</id>
			<logic>
				<behavior>visible</behavior>
			</logic>
			<layout>
				<x>0</x>
				<y>0</y>
				<rows>1</rows>
				<cols>2</cols>
				<offset>0</offset>
				<template>material</template>
				<appearance>outline</appearance>
			</layout>
		</dataRef>
		<dataRef>
			<id>quantity</id>
			<logic>
				<behavior>visible</behavior>
			</logic>
			<layout>
				<x>2</x>
				<y>0</y>
				<rows>1</rows>
				<cols>2</cols>
				<offset>0</offset>
				<template>material</template>
				<appearance>outline</appearance>
			</layout>
		</dataRef>
		<dataRef>
			<id>status</id>
			<logic>
				<behavior>editable</behavior>
				<behavior>required</behavior>
			</logic>
			<layout>
				<x>0</x>
				<y>1</y>
				<rows>1</rows>
				<cols>4</cols>
				<offset>0</offset>
				<template>material</template>
				<appearance>outline</appearance>
			</layout>
		</dataRef>
	</transition>
	<transition>
		<id>6</id>
		<x>620</x>
		<y>140</y>
		<layout>
			<offset>0</offset>
		</layout>
		<label>Show order detail</label>
		<roleRef>
			<id>customer</id>
			<logic>
				<view>true</view>
			</logic>
		</roleRef>
		<roleRef>
			<id>order_admin</id>
			<logic>
				<view>true</view>
			</logic>
		</roleRef>
		<dataRef>
			<id>product</id>
			<logic>
				<behavior>visible</behavior>
			</logic>
			<layout>
				<x>0</x>
				<y>0</y>
				<rows>1</rows>
				<cols>2</cols>
				<offset>0</offset>
				<template>material</template>
				<appearance>outline</appearance>
			</layout>
		</dataRef>
		<dataRef>
			<id>quantity</id>
			<logic>
				<behavior>visible</behavior>
			</logic>
			<layout>
				<x>2</x>
				<y>0</y>
				<rows>1</rows>
				<cols>2</cols>
				<offset>0</offset>
				<template>material</template>
				<appearance>outline</appearance>
			</layout>
		</dataRef>
		<dataRef>
			<id>status</id>
			<logic>
				<behavior>visible</behavior>
			</logic>
			<layout>
				<x>0</x>
				<y>1</y>
				<rows>1</rows>
				<cols>4</cols>
				<offset>0</offset>
				<template>material</template>
				<appearance>outline</appearance>
			</layout>
		</dataRef>
	</transition>
	<!-- PLACES -->
	<place>
		<id>1</id>
		<x>220</x>
		<y>140</y>
		<label></label>
		<tokens>1</tokens>
		<static>false</static>
	</place>
	<place>
		<id>2</id>
		<x>380</x>
		<y>140</y>
		<label></label>
		<tokens>0</tokens>
		<static>false</static>
	</place>
	<place>
		<id>3</id>
		<x>540</x>
		<y>140</y>
		<label></label>
		<tokens>0</tokens>
		<static>false</static>
	</place>
	<!-- ARCS -->
	<arc>
		<id>7</id>
		<type>regular</type>
		<sourceId>1</sourceId>
		<destinationId>4</destinationId>
		<multiplicity>1</multiplicity>
	</arc>
	<arc>
		<id>8</id>
		<type>regular</type>
		<sourceId>4</sourceId>
		<destinationId>2</destinationId>
		<multiplicity>1</multiplicity>
	</arc>
	<arc>
		<id>9</id>
		<type>regular</type>
		<sourceId>2</sourceId>
		<destinationId>5</destinationId>
		<multiplicity>1</multiplicity>
	</arc>
	<arc>
		<id>10</id>
		<type>regular</type>
		<sourceId>5</sourceId>
		<destinationId>3</destinationId>
		<multiplicity>1</multiplicity>
	</arc>
	<arc>
		<id>11</id>
		<type>read</type>
		<sourceId>3</sourceId>
		<destinationId>6</destinationId>
		<multiplicity>1</multiplicity>
	</arc>
</document>
            

Example of petriflow fundamentals model


<?xml version="1.0" encoding="UTF-8"?>
<document xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="https://modeler.netgrif.com/petriflow_schema.xsd">
	<id>new_model</id>
	<initials>NEW</initials>
	<title>New Model</title>
	<defaultRole>true</defaultRole>
	<transitionRole>false</transitionRole>
	<!-- TRANSACTIONS -->
	<!-- ROLES -->
	<!-- DATA -->
	<!-- I18NS -->
	<!-- TRANSITIONS -->
	<transition>
		<id>t4</id>
		<x>620</x>
		<y>300</y>
		<layout>
			<offset>0</offset>
		</layout>
		<label>Take</label>
	</transition>
	<transition>
		<id>t6</id>
		<x>620</x>
		<y>420</y>
		<layout>
			<offset>0</offset>
		</layout>
		<label>Finish</label>
	</transition>
	<transition>
		<id>t7</id>
		<x>380</x>
		<y>300</y>
		<layout>
			<offset>0</offset>
		</layout>
		<label></label>
	</transition>
	<!-- PLACES -->
	<place>
		<id>p7</id>
		<x>500</x>
		<y>300</y>
		<label>amount</label>
		<tokens>0</tokens>
		<static>false</static>
	</place>
	<place>
		<id>p8</id>
		<x>500</x>
		<y>420</y>
		<label>choice</label>
		<tokens>0</tokens>
		<static>false</static>
	</place>
	<place>
		<id>p10</id>
		<x>260</x>
		<y>300</y>
		<label>init</label>
		<tokens>1</tokens>
		<static>false</static>
	</place>
	<place>
		<id>p11</id>
		<x>740</x>
		<y>420</y>
		<label></label>
		<tokens>0</tokens>
		<static>false</static>
	</place>
	<place>
		<id>p12</id>
		<x>740</x>
		<y>300</y>
		<label>count</label>
		<tokens>0</tokens>
		<static>false</static>
	</place>
	<!-- ARCS -->
	<arc>
		<id>a8</id>
		<type>regular</type>
		<sourceId>p7</sourceId>
		<destinationId>t4</destinationId>
		<multiplicity>1</multiplicity>
	</arc>
	<arc>
		<id>a9</id>
		<type>regular</type>
		<sourceId>p8</sourceId>
		<destinationId>t4</destinationId>
		<multiplicity>1</multiplicity>
	</arc>
	<arc>
		<id>a14</id>
		<type>regular</type>
		<sourceId>t7</sourceId>
		<destinationId>p7</destinationId>
		<multiplicity>5</multiplicity>
	</arc>
	<arc>
		<id>a15</id>
		<type>regular</type>
		<sourceId>t7</sourceId>
		<destinationId>p8</destinationId>
		<multiplicity>1</multiplicity>
	</arc>
	<arc>
		<id>a16</id>
		<type>regular</type>
		<sourceId>p10</sourceId>
		<destinationId>t7</destinationId>
		<multiplicity>1</multiplicity>
	</arc>
	<arc>
		<id>a17</id>
		<type>regular</type>
		<sourceId>t6</sourceId>
		<destinationId>p11</destinationId>
		<multiplicity>1</multiplicity>
	</arc>
	<arc>
		<id>a18</id>
		<type>regular</type>
		<sourceId>t4</sourceId>
		<destinationId>p12</destinationId>
		<multiplicity>1</multiplicity>
	</arc>
	<arc>
		<id>a19</id>
		<type>regular</type>
		<sourceId>p8</sourceId>
		<destinationId>t6</destinationId>
		<multiplicity>1</multiplicity>
	</arc>
	<arc>
		<id>a20</id>
		<type>regular</type>
		<sourceId>t4</sourceId>
		<destinationId>p8</destinationId>
		<multiplicity>1</multiplicity>
	</arc>
</document>