iTop Extensions

Catalog of procedures

m( m( m( Combodo's customers only 8-) 8-) 8-)

Create automatically assigned Work-orders based on requested Service
PHP 8.1

This catalog is designed to enrich Service Management, reducing agent's workload and errors.
With this extension, Work Orders are created automatically for the various teams which need to contribute to deliver each service.


Some services requires always the same set of actions to be performed by the same teams. No action must be forgotten. SLA on the main ticket is sufficient. No OLA per team is needed. ⇒ This extension will improve your efficiency.

If you have a similar case, but with one of those additional needs / constrains:

  • You want to track SLA/OLA for each action
  • You need a lot of specific details for each sub-action which cannot be requested in the main ticket
  • Some of those actions must be handled by partners using Portal for Business Partner

Check Global requests management


Config Manager defines templates

  • Allow to define for each Service Subcategory a set of Procedures which are required to deliver the Service.
  • A Procedure describes the task to perform and the team which will do it
  • Procedure can be ordered, as some task can be performed only when another one is completed.

Work Orders automatically created

  • When a User Request or an Incident has a Service Subcategory which has associated Procedures, then
    • Work Orders are created automatically at the first Assignment of the User Request/Incident.
    • For each Procedure, a Work order is created using the Procedure as a template (copying name, description, team and hierarchy).

Agents execute Work Orders in order

  • A Work Order which cannot be performed until another one is finished will appear as pending parent.
  • When a Work Order is closed, then all the Work orders which were waiting for it, are automatically open and their responsible team can be notified.

Agents must close Work Orders before resolving Tickets

  • By default, Resolve transition is no more proposed on Ticket with work-orders created from procedures, as long as there is a non-closed Work order.

Revision History

Version Release Date Comments
1.1.0 2022-09-28 Enabled on Incident class
Prevent Ticket resolution with non-closed work orders
XML nodes removed (Check migration notes)
1.0.2 2022-02-09 Add German translations (many thanks to Lars Kaltefleiter / ITOMIG)
1.0.1 2019-01-22 Alignement with the version published in the designer
1.0.0 2017-02-27 First version

Migration notes


The following ExternalFields were removed from the extension default Datamodel:

Class Removed fields
WorkOrder parent_workorder_name
Procedure servicesubcategory_name


There were 2 versions 1.0.0 of this extension, which are slightly different:

  • The one on the ITSM Designer use an ExternalKey on Procedure (it's the choice kept going forward)
  • The one on GitHub and in Legacy package use a HierarchicalKey on Procedure


Defined for User Request and Incident Ticket only.
Customization required to use it on Changes.



Use the Standard installation process for this extension.


All those actions are optional, as it works out of the box, but they will improve the user experience

Simplify Procedure creation

Set this Configuration parameter, to enable easy creation of procedures from a Service subcategory

'allow_menu_on_linkset' => true,

Simplify Procedure copy

Copy this piece of code as an entry in $MyModuleSettings = array( of the Configuration file, to simplify duplication of a procedure that you want to reuse on a different service sub-category for eg.

    'itop-object-copier' => array (
        'rules' => array (
            'clone-procedure' => array (
                  'source_scope' => 'SELECT Procedure',
                  'allowed_profiles' => 'Administrator,Configuration Manager',
                  'menu_label' => 'Clone...',
                  'menu_label/FR FR' => 'Cloner...',
                  'form_label' => 'Cloning %1$s',
                  'form_label/FR FR' => 'Clonage de %1$s',
                  'report_label' => 'Cloned from %1$s',
                  'report_label/FR FR' => 'Cloné depuis %1$s',
                  'dest_class' => 'Procedure',
                  'preset' => array (
                      0 => 'clone_scalars(*)',
                      1 => 'reset(name)',
                  'retrofit' => array (

Remove control

The extension from version 1.1.0 prevent resolution of a Ticket which is using a catalog of procedure, as long as there are associated Work Orders still not closed. It corresponds to the below configuration which comes automatically at first installation, but even if not present, it will behave this way.

'itop-procedure-catalog' => array (
   'allow_open_workorder_on_resolution' => false,
If you want to disable this control, set explicitly the value allow_open_workorder_on_resolution to true.

Procedure to Workorder

The below entry to copy in $MyModuleSettings = array( of the configuration file , is not needed unless you have already an entry defined for itop-stencils, in which case you must append it.

'itop-stencils' => array (
  'rules' => array (
    'UserRequestProcedureCatalog' => array (
      'name' => 'Workorders',
      'trigger_class' => 'UserRequest',
      'trigger_scope' => 'SELECT UserRequest WHERE task_created="no"',
      'trigger_state' => 'assigned', // triggered when reaching this state
      'report_label' => 'Ticket:Info:WorkOrderCreatedFromProcedure',
      'templates' => 'SELECT Procedure WHERE servicesubcategory_id = :trigger->servicesubcategory_id AND parent_procedure_id =0',
      'copy_class' => 'WorkOrder', // Class of objects which will be created using the templates
      'copy_actions' => array ( // Series of actions to preset the object in the creation form
          0 => 'clone(name)',
          1 => 'clone(description)',
          2 => 'set(ticket_id,$trigger->id$)',
          3 => 'set(team_id,$this->team_id$)',
      'copy_from_trigger' => array (
      // Applies recursively the same process on children of the template 
      'copy_hierarchy' => array ( 
         'template_parent_attcode' => 'parent_procedure_id',
         'copy_parent_attcode' => 'parent_workorder_id',
      'retrofit' => array (
           0 => 'set(private_log,Work orders have been created on this User Request using Procedures defined on [[ServiceSubcategory:$this->servicesubcategory_id$]])',
           1 => 'set(task_created,yes)',
    'IncidentProcedureCatalog' => array (
      'name' => 'Workorders',
      'trigger_class' => 'Incident',
      'trigger_scope' => 'SELECT Incident WHERE task_created="no"',
      'trigger_state' => 'assigned', // triggered when reaching this state
      'report_label' => 'Ticket:Info:WorkOrderCreatedFromProcedure',
      'templates' => 'SELECT Procedure WHERE servicesubcategory_id = :trigger->servicesubcategory_id AND parent_procedure_id =0',
      'copy_class' => 'WorkOrder', // Class of objects which will be created using the templates
      'copy_actions' => array ( // Series of actions to preset the object in the creation form
          0 => 'clone(name)',
          1 => 'clone(description)',
          2 => 'set(ticket_id,$trigger->id$)',
          3 => 'set(team_id,$this->team_id$)',
      'copy_from_trigger' => array (
      // Applies recursively the same process on children of the template 
      'copy_hierarchy' => array ( 
         'template_parent_attcode' => 'parent_procedure_id',
         'copy_parent_attcode' => 'parent_workorder_id',
      'retrofit' => array (
           0 => 'set(private_log,Work orders have been created on this Incident using Procedures defined on [[ServiceSubcategory:$this->servicesubcategory_id$]])',
           1 => 'set(task_created,yes)',


Define Procedures

If for a particular Service subcategory, multiple tasks need to be performed in a particular order by different teams, then you need to create Procedure objects using the new menu: Procedures catalog  Procedure menu

Create Procedure

 Create a procedure

Field Purpose
Name Name of the created WorkOrders
Description Description copied in the created WorkOrders
Service Pickup a Service to restrict the Service Subcategory
Service Subcategory Mandatory, a WorkOrder based on this Procedure will be created on UserRequest using this Service Subcategory
Team Team to which created WorkOrders will be dispatched
Parent procedure Set the parent, if that task cannot be performed until the parent one is done

Avoid creating a loop with the parent procedure, as only loops with 1 or 2 workorders are prevented.

Results on Service Subcategory

Once you have created a few procedures, it will look this way:  Service Subcategory with procedures

Impact on Ticket

  • Create a User Request (or an Incident) on a Service Subcategory with Procedures defined. Nothing special happened to this Ticket at this stage.
  • Assign the Ticket: Workorders are automatically created

 Procedures list in an assigned ticket

  • For each Procedure defined on the Service Subcategory a WorkOrder is created, with the same name, description and assigned team.
  • If the Procedure has a parent, then the created WorkOrder will have a corresponding parent work order.
  • When the WorkOrder as a Parent WorkOrder, its status is automatically set to pending parent until the parent WorkOrder is completed

Impact on resolution

From version 1.1.0 resolution of a Ticket which is using a catalog of procedure, is not proposed as long as there are associated Work Orders not closed.
The transition is not proposed and a message is displayed each time the ticket is open to remind the rule.

Impact on Work Order

This extension modifies the life cycle of WorkOrder objects, by

  • adding a state pendind_parent,
  • two automatic transitions (never proposed to users)
  • a method, to handle the fact that WorkOrder are pending the execution of their parent WorkOrder, and they can start only when their parent is closed.

 Workorder lifecycle

Questions & Answers

Procedure on Changes?

Answer: In order to be able to use it on another Ticket class than UserRequest and Incident, that class must have at least

  • an externalKey to the Service class or an externalKey to the ServiceSubcategory class (better but one or the other works).
  • add a boolean flag field task_created on the Ticket class to identify Tickets on which WorkOrders have been created (if you call it differently adapt the configuration)
  • then modify the configuration entry. Here I am assuming that the class Change has a service_id, but no servicesubcategory_id, so those are just the modified lines:
      23 => array (
            'name' => 'Tasks',
            'trigger_class' => 'Change',
            'trigger_scope' => 'SELECT Change WHERE task_created="no"',
            'templates' => 'SELECT Procedure WHERE service_id = :trigger->service_id',
            'retrofit' => array (
// the following won't work without a private_log field, use another field or remove that line
                // 0 => 'set(private_log,A task list has been created for the ticket)', 
                1 => 'set(task_created,yes)',


Question: My UserRequests are moved to assigned state with an auto-dispatch rule, but the WorkOrders aren't created, why?
Answer: It's a known limitation. We have currently no workaround.

Question: What happen if I bulk resolve 2 Tickets, one with open workorders issued from procedure, and some not?
Answer: At first stage, iTop doesn't check if transition is applicable to each ticket, so the transition is proposed based on default datamodel

Then in the second screen of the bulk transition, iTop controls for each Ticket if the transition is allowed. If not the Ticket is displayed and automatically unselected and greyed.

Question: I cannot install the latest version, it says that filter node cannot be redefine
Answer: If you have defined with a previous version of the Procedure catalog extension a filter on the field parent_workorder_id, with the ITSM Designer or an extension, then you need to remove that filter or change its _delta value to force or redefine.


Question: How can I force the UserRequest to be assigned to the team in charge of the last activated WorkOrder
Answer: If your WorkOrders are all sequential, assigned to different teams all parts of the Customer Delivery Model, and you want your User Request to be automatically reassigned to the next team to work on the UserRequest, then you can do this with an extension containing those 2 changes:

1. Create a PHP function updating the User Request:

public function DispatchParentTicket()
    $oTicket = MetaModel::GetObject('Ticket', $this->Get('ticket_id'), false);
    $oNewTeam = $this->Get('team_id');
    if ($oTicket && $oTicket->Get('team_id') != $oNewTeam)
        $oTicket->Set('team_id', $oNewTeam);
        // This will trigger notification and empty the agent_id
        // This can fails if this stimulus is not applicable in the current state
    return true;

2. Call that function when a WorkOrder is reactivated:

itop-design / classes
    <class id="WorkOrder" _delta="must_exist">
          <state id="pending_parent" _delta="must_exist">
              <transition id="ev_start" _delta="must_exist">
                <actions _delta="redefine">
                      <param xsi:type="attcode">start_date</param>


  • If multiple WorkOrders were waiting for the same parent, the UserRequest will be re-dispatched successively to each of the teams
  • If the team associated with the WorkOrder is not part of the Customer Delivery Model, it will still be affected to the User request
  • If the User Request is not in a state which supports the ev_dispatch stimulus, the team will be changed but neither the state nor the agent.

Question: Can I prevent resolution of Ticket as soon as there is a non-closed Work Order?
Answer: Yes, out of the box, this prevention is limited to Ticket with Work Order created by Procedure, but you can change this by removing in the below method, the tests on task_created:

public function CountBlockingWorkorder()
     $iUnclosed = 0;
     if (!MetaModel::GetModuleSetting('itop-procedure-catalog', 'allow_open_workorder_on_resolution', false)
          && MetaModel::IsValidAttCode(get_class($this),'task_created')
          && ($this->Get('task_created') == 'yes'))
          $sOQL = "SELECT WorkOrder WHERE ticket_id=:ticket_id AND status!='closed'";
          $oWorkOrderSet = new DBObjectSet(DBObjectSearch::FromOQL($sOQL),[],['ticket_id' => $this->GetKey()]);
          $iUnclosed = $oWorkOrderSet->Count();
     return $iUnclosed;

The above method is called

  • Each time the valid transitions of a UserRequest or an Incident are computed.
    If that method returns a number strictly above 0, the ev_resolve transition is removed, and a message is displayed to the User.
  • It is also called, if a user requests a transition, ending on resolve state.
    Again if the above method returns that there are blocking Work orders, then the transition is blocked (The error message displayed isn't userfriendly, but this is an iTop core issue)

extensions/itop-procedure-catalog.txt · Last modified: 2023/05/30 10:04 (external edit)
Back to top
Contact us