Logo
  • Other versions
  • Virage Group
  • Espace Magenta
  • Support ticket
  • Strat Monitor documentation
Project Monitor v7.7 Knowledge Center
Project Monitor v7.7 Knowledge Center
Workflow memo

Workflow memo

‣
Table of contents
icon

Workflow Memo - 7. sample workflow filesWorkflow Memo - 7. sample workflow files

1. Preamble

This document describes how Camunda® validation workflows work in Project MonitorProject Monitorfrom the user's point of view and from the design point of view . It is aimed at consultants who need to implement validation workflows in their applications.

icon
Workflow representation a sequence of tasks or operations assigned to a person or group of people by moving from one step to another WorkflowsWorkflows

Validation workflows can be configured in Project MonitorProject Monitor. To do this, an add-on must be activated.

🚦

The file containing the workflow code, which is loaded into Project MonitorProject Monitor must have a .bpmn extension

‣
Whatever the workflow in the application, the principle is always the same:

Editorial agreement

Term
Description
PM
Project MonitorProject Monitor
WF
Workflow - Process
JPDL file
Text file containing the code for a validation workflow loaded into Project MonitorProject Monitor so that it can be used by the application. This file has the extension *.jpdl.xml
BPMN file
File created from the Camunda® modeler containing the code for a validation workflow, loaded into Project MonitorProject Monitor so that it can be used by the application. This file has the extension *.bpmn
API
Application Programming Interface
REST
Representation State Transfer
HTTP
HyperText Transfer Protocol

Architecture principle

The new Camunda® workflow engine lets you define workflows according to the BPMN (Business Process Model and Notation) standard.

It is based on the use of webservices.

The APIs that can be called up in the Camunda® modeler are listed on page Webservices memoWebservices memo according to the following standards :

  • the principle of REST (Representational State Transfer) architecture,
  • Hyper Text Transfer Protocol ( HTTP ),
  • JSON (JavaScript Object Notation) data format.

Test tools

To ensure that each webservice used in the Camunda® modeler is correct, it is necessary to test the requests beforehand.

Several REST API-type tools are available for this purpose:

  • Rest Client (Mozilla Firefox ®)
  • Postman
🖇️
To find out more about REST APIs, visit the following link:
What is a REST API?

A REST API (also known as a RESTful API) is an application programming interface (API or web API) that respects the constraints of the REST architecture style and enables interaction with RESTful web services. The REST (Representational State Transfer) architecture was created by computer scientist Roy Fielding.

www.redhat.com

What is a REST API?

2. General information

Technical architecture

To migrate a client environment to the new Camunda® workflow engine, it is necessary to deploy Camunda® on the application server.

icon

The deployment procedure is currently being drafted!

Modeler Camunda®

Camunda® modeler is required to generate bpmn files 👇

https://camunda.com/download/modeler/

3. Camunda® workflow

Camunda® vs JPDL

Camunda® allows you to regain almost all existing workflow actions in Project MonitorProject Monitor to date.

What determines whether or not an existing customer can activate the add-on is :

  • SaaS / On premise hosting mode
  • JPDL workflow functional scope
Action
Description
JPDL
Camunda®
Trigger
Automatic on status change
✔
✔
Manual on action user
✔
✔
Update 
status object planning
✔
✔
attribute values
✔
✔
Project status
✔
✔
The status document
✔
✔
Notify
Send tasks by email
✔
✔
Packaging
Check scheduling budget
✔
✔
Multiple validation 
✔
✔
Multi-factor condition "if phase in status AND task refused, then...".
✔
✔
View
Workflow
❌
✔
Advances in proceedings
❌
✔

4. Project MonitorProject Monitor with Camunda®.

Workflow principles in Project MonitorProject Monitor

Workflow principles Project MonitorProject Monitor have not fundamentally changed, the migration to Camunda® has been designed to be ISO to JPDL.

  • Workflow can also be triggered by project status , document status or manually.
  • It is inherited from the project creation template. It is not possible to add a workflow to a project without inserting a template. Conversely, all projects with a template carry that template's workflow.
  • It advances by task validation.

Depending on public webservices, it will even be possible to add actions that are currently non-existent in JPDL, such as creating new objects in the project.

Pre-configuration

Add-on activation

The add-on is activated at the MM > Platforms.

In the list of add-ons, activate the add-on Workflow.

⚠️
All Camunda® WFs must be modeled before activating the add-on, as activating the add-on deactivates the JPDL WF.

Technical user

For Camunda® and Project MonitorProject Monitor to function, you need to create a technical user with sufficient rights to the WF actions to be carried out.

It is therefore necessary to assign the user a general role with extended rights.

Code concordance

This is necessary to ensure that the WF is compatible with Project MonitorProject Monitor that the codes used in the webservices correspond to those of Project MonitorProject Monitor :

  • Code role
  • Code status task list value
  • Code list value task type
  • Code list value status phase
  • ...

5. Camunda® workflow actions

The Camunda® workflow file must be created in the Camunda® modeler.

Each WF must contain at least :

  • an event start,
  • a task,
  • an event end.

Each WF step is linked to the next by a "flow".

Modelling structure

Camunda® modeler is made up of :

  1. WF Action
  2. Diagram
  3. Properties panel

Only the followingaction buttons can be used in v6.5.2:

  • Event start
  • Flow
  • Task
  • Gateway
image

Creating a BPMN file

🚦
This action requires configuration.

There are two ways to create a new workflow file:

  • Create a new file and click on the BPMN diagram (Camunda Platform):
  • Use an existing BPMN file below 👇 by opening it from File > Open file
  • demo1.bpmn13.0KB

    When creating a BPMN file, the properties panel is accessible on the right.

    The following structuring elements must be filled in, which will then be used in the Project MonitorProject Monitor :

    Camunda®
    Project MonitorProject Monitor
    id
    code
    Name
    Wording

The name of the BPMN file to be saved is not an essential entry, as it is not included in Project MonitorProject Monitor.

On the other hand, it allows you to manage versions in your development environment.

When importing a WF, it's the id that identifies whether it's a new WF or a new version of a WF.

🚦
Don't forget to add a name="processname"

in this XML line : <bpmn:process id="test" name="nomprocessus" isExecutable="true">

Otherwise, import into Project MonitorProject Monitor may encounter an error.

image

Create an action

Actions are created by Drag&Drop.

Simply enter an action in the menu and click and drag it into the "schematic" area.

Ideally, we proceed in two stages:

  • Full WF modeling
  • Setting each action

Once dragged into the schematic, each action will have its own settings menu 👉

When you click on the key, the menu appears, allowing you to determine the nature of theaction.

The "properties" panel opens for setting parameters.

Eachaction setting is detailed below.

image
image

Create an event start

✅
This action does not require any settings.

Each WF must start with a event. It is therefore necessary to create a Event start 👉

image

Create a task

🚦
This action requires configuration.

The WF principle of Project MonitorProject Monitor is to work with WF tasks, so it is necessary to materialize each step to be validated with a Task 👉

There are several types of task :

  • WS tasks Service task
  • User tasks User task
image

Service task

🚦
Parameter setting is essential and MUST respect the parameters and their case.

The task service creates the WF task Project MonitorProject Monitor.

It is represented by the cogwheel icon.

It uses webservices and WF variables in the properties panel.

General

Id
task id
Name
task name
Implementation
Connector

Connector

Variable name
Variable assignment type
Variable assignment value
Value
Connector id
http-connector
String or Expression
#NA
Input parameters
headers
Map
Accept
application/json
Content-type
application/json
X-PM-API-ID (replaces X-PM-API-LOGIN
user id
X-PM-API-PASSWORD
user pw
method
String or Expression
POST
url
String or Expression
url
http://localhost:8080/monitorupgrade/api
payload
String or Expression
BeanTacheEntree
See list of variables
image
image
image

List of Payload variables

The following variables are called in the payload (webservice) to execute WF tasks on the project and the project's WF instance:

  • "projet": { "id": #{execution.getVariable('projectId')}} -> retrieves the project id of the current instance
  • "idInstance":"#{execution.getProcessInstanceId()}” -> retrieves the id of the WF instance
  • "id": #{execution.getVariable('documentId')} -> retrieves the document id of the current instance
  • "idWorkflow": "validation_statut_tache1" -> defines the user task waiting to be executed
  • #{statusCode=='CODE_STATUT_TACHE'}
  • #{execution.getVariable('projectLabel')} ->retrieves project label
  • "code" : "CODE-DONNE-POUR-LA-TACHE" -> Set a code for the task
  • "parent" : {"code":"CODE-DONNE-POUR-LA-TACHE-PARENTE"} ->Allows you to attach a task to a parent task
  • "listeRolesParticipants" : ["ARBITRE_DEMANDE", "ROLE_CONTRIBUTOR"] -> Allows you to define a list of role codes (e.g. to notify several roles and participants of a task)
  • #{execution.getVariable('userCamundaId')} -> Retrieves the id of the technical user who will run the process (to be entered in the X-PM-API-ID header)
  • #{execution.getVariable('loginUser')} -> Retrieves the id of the logged-in user who will run the process
  • #{execution.getVariable('codeVar1')} -> Retrieves a variable via Project MonitorProject Monitor (e.g. template code to be applied)

List of URL variables :

  • #{execution.getVariable('projectId')}/attributes/CODEATTRIBUT -> Retrieves the value of an attribute from Camunda
  • http://localhost:#{execution.getVariable('port')}/monitorupgrade/tasks -> Allows workflow to be run from any port (other than 8080)
  • http://localhost:#{execution.getVariable('port')}/monitorupgrade/tasks/parent/#{execution.getVariable('objectId')}/status/#{execution.getVariable('codeVar1')} -> Retrieves the ID of a parent task (#{execution.getVariable('objectId')}) and applies the status set in the automation (#{execution.getVariable('codeVar1')})

Connector outputs from a service task

Connector outputs can be configured within a service task to receive information or error messages when the web service present in a process in Project MonitorProject Monitor. Here are the settings to be applied to each task service :

Process variable name
Assignment type
Format
Type
Script
errorLabel
Script
JavaScript
Inline script
var statusCode = connector.getVariable('statusCode'); if (statusCode >= 400) { var response = JSON.parse(connector.getVariable('response')); if (response.message) { response.message; } else if (response.error) { response.error; } else if (response[0] && response[0].beanWsErreur && response[0].beanWsErreur.message) { response[0].beanWsErreur.message; } }
infosLabel
Script
JavaScript
Inline script
var headers = connector.getVariable('headers'); if (headers.get('X-Context-Info')) { headers.get('X-Context-Info'); }
warningsLabel
Script
JavaScript
Inline script
var headers = connector.getVariable('headers'); if (headers.get('X-Context-Warning')) { headers.get('X-Context-Warning'); }
taskId (only in a task service of the task creation type)
Script
JavaScript
Inline script
var response = JSON.parse(connector.getVariable('response')); response[0].beanWsItem.id;
image

User task

✅
This action does not require any settings

The user task defines the task's listening parameter, usually the status task , and enables the WF to progress in Project MonitorProject Monitor.

It is represented by the icon User.

A user task is always preceded by a service task.

image

Creating a flow

🚦
This action can be parameterized where necessary.
image

The event flow is used to model the process path from one event to another 👉

When the flow carries a WF decision, it is necessary to set the status listened to.

General

id
id
Name
Name
Standard condition
Expression
Expression
#{statusCode==status'}
image

Create a gateway

✅
This action does not require any settings.

A gateway can be used to create several WF paths.

Two types of gateway work:

  • Exclusive gateway

An exclusive gateway makes it possible to determine several independent paths, each leading to a different end of the WF.

image
  • Parallel gateway

A parallel gateway allows you to create several events or tasks in parallel, all converging towards a common event end.

image

Create an event end

✅
This action does not require any settings.

Each flow in the process described must end with an event end.

image

Save WF file

The file name is used to identify WF versions, but is not otherwise used in Project MonitorProject Monitor.

Configure the properties of a workflow or automation file

When creating a BPMN file, you must specify whether it is to be used as a workflow or an automation in Project MonitorProject Monitor. You need to modify the properties of the BPMN file in a text editor (Notepad, for example) or via the display in XML format function in Camunda®.

On line 2, find the value targetNameSpace and specify the following variables according to the BPMN file set :

  • targetNamespace="auto" → the file is an automation file
  • targetNamespace="bpmn" → the file is a workflow file
image

If the process is to run on a particular type of trigger, it is possible to add extension properties within the global BPMN file. You will be asked to enter a name and a value:

Here are all the possible trigger options:

Name
Value
Description
forManual
true
Manual release only
forProject
true
Project status only
forDocument
true
Trigger ondocument status only
forTask
true
Trigger on status task only
forTask-metastatus
terminated
Trigger on status task at completed status only
forPhase
true
Tripping onphase status only
forMilestone
true
Triggered onmilestone status only
forAttribute
true
Trigger onattribute value only
image
💡
If none of these extension properties is not specified, then the BPMN file will run on all available trigger types.

So that the variable code is no longer entered manually in Project MonitorProject Monitoryou can tell Camunda® which element is concerned by the variable and which list of values to display.

Simply add new extension properties within the BPMN :

Name
Value
Description
codeVar1-objectDefinedType
theme code to be filled in
Indicates which type ofattribute will be used as a variable in process parameterization (TASK, PHASE, PROJECT).
codeVar1-attributeListCode
attribute code to be filled in
Allows you to indicate which attribute will be used in the process parameterization variable. The list associated with theattribute will then be displayed in the "unique object" property.
codeVar1-objectType
element code to be filled in
Used to indicate which object will be used as a variable in the process setup, and to enter the object label directly in the setuptemplate to display the list of templates and search in the list of project templates).
codeVar1-permission
right code to be filled in
Checks whether the user setting up the process has the right to consult the list displayed as a variable (the list of project templates, for example), taking into account the platform's partitioning rights.

Example 1:

Automation Change task status when milestone status changes is set as follows:

  • targetNamespace="auto" (property present in the XML version of the BPMN file)
  • Extension properties :
    • forMilestone = true
    • forPhase = true
    • codeVar1-objectDefinedType = TASK
    • codeVar1-attributeListCode = STATUS
image

This means that the file is an automation (targetNamespace="auto"), that it can only be triggered onphase or milestone status (forMilestone and forPhase) and that the variable used in the "unique object"will be based on a task attribute (codeVar1-objectDefinedType = TACHE), which carries the code STATUSthus displaying the list associated with the status task (codeVar1-attributeListCode = STATUS) :

image

Example 2:

The "Apply template" automation is set up as follows:

  • targetNamespace="auto" (property present in the XML version of the BPMN file)
  • Extension properties :
    • codeVar1-objectType = template
    • codeVar1-permission = ACT_UTILISER_GABARIT_PROJET
image

This means that the file is an automation (targetNamespace="auto"), that it can run on all available triggers, that the variable used in the "unique object" property will be based on the list of project templates (codeVar1-objectType = template) and check that the user who will be setting up the automation has the right to Using templates in projectsUsing templates in projects while respecting the compartmentalization settings on the platform (codeVar1-permission = ACT_UTILISER_GABARIT_PROJET) :

image

Creating a conditioned workflow: the Apply a template case

It is possible to condition the execution of certain web services parameterized in a BPMN file via a decision table, configurable in a DMN (Decision Model and Notation) file. A DMN file presents a table that can be used to model decisions and business rules using FEEL (Friendly Enough Expression Language), based on the creation of expressions instead of specific code.

To create a conditioned workflow, you'll need at least one business rule task, conditioned flows and a DMN file in addition to the BPMN file representing the workflow.

In the example below, a template will be applied based on information retrieved from the project ( budget and resource attribute information):

image

Create a service task to retrieve project information

🚦
Parameter setting is essential and MUST respect the parameters.

Create a service task to retrieve information from a project attribute . If the decision-making instance needs to retrieve several pieces of information, create as many service tasks as there are pieces of information to be retrieved. These service tasks must be positioned either successively, or between two parallel gateways.

image

General

Id
Service task id
Name
Service task name

Implementation

Type
Connector
Connector ID
http-connector

Connector inputs

Variable name
Variable assignment type
Variable assignment value
Value
headers
Map
Accept
application/json
Content-type
application/json
X-PM-API-ID
user id
method
String or Expression
GET
url
String or Expression
url
http://localhost:8080/MonitorMakerWeb/api/projects/#{execution.getVariable('projectId')}/attributes/CODEATTRIBUT

Connector outputs (will be used in the DMN file)

Process variable name
Variable name (in the example budget and resource_value)
Assignment type
Script
Format
JavaScript
Type
Inline script
Script
var response = JSON.parse(connector.getVariable('response')); response;

Create a business rule task

🚦
Parameter setting is essential and MUST respect the parameters.

The business rule task retrieves the decision information and business rules present in the DMN file to execute the right decision in the process.

It is represented by the table icon.

image

General

Id
Id of the business rule task
Name
Name of business rule task

Output (to return an error if no decision is found)

Process variable name
errorCode
Assignment type
String or expression
Expression
#{execution.getVariable('result')==null?'ERR_WORKFLOW_NO_DECISION':null}

Implementation

Type
DMN
Decision reference
Decision ID
Binding
Latest
Tenant ID
Result variable
result
Map decision result
Select the result method to be applied
image

Create a decision-making flow

🚦
Parameter setting is essential and MUST respect the parameters.

The decision flow event is used to model the process path from one event to another based on the result found in a decision table. In the event of a zero or non-zero result, this indicates the path to take to continue the workflow.

image
id
id
Name
Name
Standard condition
Expression
Expression for non-zero result
#{execution.getVariable('result')!=null}
Expression for zero result
#{execution.getVariable('result')==null}

In the example opposite, the workflow will apply the template if all the retrieved parameters are sufficient to execute a decision. If the retrieved parameters are not sufficient to execute a decision, the workflow ends.

image

Create a service task to execute the decision

🚦
Parameter setting is essential and MUST respect the parameters.

General

Id
Service task id
Name
Service task name

Implementation

Type
Connector
Connector ID
http-connector
image

Connector inputs

Variable name
Variable assignment type
Variable assignment value
Value
headers
Map
Accept
application/json
Content-type
application/json
X-PM-API-ID
user id
method
String or Expression
POST
payload
String or Expression
BeanTacheEntree
{ "projet": { "id": #{execution.getVariable('projectId')}}, "template": {"code": "#{execution.getVariable('result').get('apply_template')}"}, "insert": false }
url
String or Expression
url
http://localhost:8080/monitorupgrade/projects/template/update

Connector outputs

Process variable name
Assignment type
Format
Type
Script
errorLabel
Script
JavaScript
Inline script
var statusCode = connector.getVariable('statusCode');if (statusCode >= 400) {var response = JSON.parse(connector.getVariable('response'));if (response.message) {response.message;} else if (response.error) {response.error;} else if (response[0] && response[0].beanWsErreur && response[0].beanWsErreur.message) {response[0].beanWsErreur.message;}}
var statusCode = connector.getVariable('statusCode'); if (statusCode >= 400) { var response = JSON.parse(connector.getVariable('response')); if (response.message) { response.message; } else if (response.error) { response.error; } else if (response[0] && response[0].beanWsErreur && response[0].beanWsErreur.message) { response[0].beanWsErreur.message; } }
infosLabel
Script
JavaScript
Inline script
var headers = connector.getVariable('headers');if (headers.get('X-Context-Info')) {headers.get('X-Context-Info');}
warningsLabel
Script
JavaScript
Inline script
var headers = connector.getVariable('headers');if (headers.get('X-Context-Warning')) {headers.get('X-Context-Warning');}

Create a DMN file

Creating a DMN file allows you to configure a table of decisions to be executed according to the rules you've set.

A DMN file necessarily has a name and an ID. In our example, the DMN file will analyze the information retrieved upstream of the budget and resource attributes to check whether there is a rule that corresponds to a decision to be executed:

  • The "Standard project" template will be applied to the pending workflow project if the budget is greater than €50,000 and the resources greater than 150 days/hour.
  • The "V Cycle" template will be applied to the pending workflow project if the budget is less than or equal to 50000€ and the resources less than or equal to 150d/hour.
  • Workflow will terminate if no decision is found (flow conditioned on BPMN file)
image

When retrieving numericattribute values, the table columns must be configured as follows:

Expression
Adopt the process label variable name from the connector output of theattribute valuebudget or resource_value) retrieval service task.
Expression language
feel
Input variable
N/A
Type
double

For the definition of the decision to be executed, here are the parameters:

Output name
Adopt the code indicated in the payload of the service task executing the decision (apply_template).
Type
string

6. Operation in Project MonitorProject Monitor

Once the process has been modelled in the Camunda® modeler, it can be loaded into the Camunda® software. Project MonitorProject Monitor.

However, it is necessary to ensure that the add-on has been activated.

Add-on activation

Add-on activation takes place at the MMConnected in soap.

As soon as the add-on is activated, several consequences take effect in Project MonitorProject Monitor :

JPDL
Camunda®
The card is marked "depreciated".
The card appears and is labelled "Workflow".
JPDL files can no longer be uploaded to admin
It is possible to load BPMN files in admin
WFs in progress can be completed
Camunda WFs will need to be combined with existing templates
It will no longer be possible to associate JPDL WFs with templates.
jpdl WFs will remain associated with existing templates; they must be deleted as soon as existing BPMN files are uploaded and associated.

Suggested checklist before activating the add-on:

SaaS customer
Customer without WF or with "simple" WF (without condition and control)
JPDL file retrieval and list of associated templates
WF modeling with the Camunda® modeler
Add-on activation
Technical user creation
Camunda® WF upload
Linking Camunda® WFs to jigs
Delete existing JPDL WFs if necessary

Upload WF

WF administration is performed in the same way as for JPDL files, from AdministrationAdministration > Model settings >Automations and workflows > WorkflowsWorkflows.

Several WFs can be loaded into Project MonitorProject Monitor. Several versions of WF can be loaded parameter processes evolve.

Versioning enables existing projects to complete all the steps in a process, even if the process has changed.

Camunda® provides the ability to view WF diagrams, instances in progress at each stage, and navigate to project instances.

DMN files are loaded in the same way as BPMN files in Project MonitorProject Monitor but will not be visible in the Project MonitorProject Monitor. You need to connect to Camunda Cockpit to view the DMN file loaded in Project MonitorProject Monitor.

Linking the WF to a template

The WF has been imported into the administration system, and now needs to be associated with a template.

This is the only way a project can inherit from an underlying WF.

If the project has not been created from the template , it is possible to apply the template to projects individually or en masse. This makes it possible to trigger WF on existing projects.

Define a trigger status

When associating a WF with a template, it is possible to define the WF triggering mode on projects:

  • manual release
  • triggered by project status
  • triggered by status document
  • triggered by task status
  • phase status triggering
  • milestone status triggering
  • triggering byattribute value

All you have to do is select the list value from the list selector:

image

If the trigger is based on the status task, phase or milestone, then it is possible to define the type of task, phase or milestone on which the process will be triggered:

image

If the trigger is adocument, task, phase or milestone status , then it is possible to run the process on the last trigger (for example, the template will be applied when all project documents have been validated):

image

WF visualization

There are two ways of viewing workflows:

  • Visualization in the project
  • Visualization in admin

Project visualization

image

Admin view

image

Each project has a diagram showing the progress of the process in the settings screen.

If several WFs are launched on the same project, an unfolded chevron is available to consult the appropriate diagram.

In the admin screen, each process version has its own schema. Each schema contains a number of items of information:

  • The number of projects in each stage in the pink bubble
  • Click on the bubble to filter on the step, and a potato chip is displayed to remind you of the criterion.
  • A "Project list" view is applied to the project list.

When several workflow tasks are sent at the same time at the start of the workflow (each task rejected or approved): the workflow process ends when the last task been processed.

history workflows

Each workflow definition has its own history. This makes it possible to identify which creations/modifications/deletions have been carried out.

7. Examples of workflow files

Workflow.bpmn demo19.3KB

Example of conditioned workflow + decision table

Appliquer_Gabarit_conditionne.response.script.bpmn12.2KB
Appliquer_Gabarit_conditionne-modifie-en-serie.bpmn9.1KB
Apply_Gabarit_conditionne_DMN.dmn2.1KB

Example with project name in task name

recipe-libelle-tachev3.bpmn14.3KB

Example with link between 2 tasks

recipe-filiation-tachev4.bpmn14.3KB

Example with notification of several participants

recipe-notification-multiplev2.bpmn14.4KB

Automations

modifyTasks.v3.bpmn5.0KB
applyTemplate.v3.bpmn5.0KB
insertTemplate.v3.bpmn5.0KB
modifyChildTasks.v1.bpmn5.0KB
modifyParentTask.v1.bpmn5.0KB
modifyPhases.v2.bpmn5.0KB
modifyProjectStatus.v2.bpmn5.4KB

Frequently asked questions

‣

What happens if I forget to create a technical user for the processes?

‣

What do I do if I make a mistake?

‣

I've created a technical user for the processes, I've tested my payload in a RestClient yet my process falls into error in Project MonitorProject Monitor. What could be the reasons?

Logo

Produced by Virage Group