:: Version 2.7.0 ::

Flag Tickets waiting for a 3rd party

Prerequisite: You must be familiar with the Syntax used in Tutorials and have already created an extension.

learning:
Add a state to a lifecycle and count time spent in it
level:
Advanced
domains:
XML, Stopwatch, Automation, Lifecycle
min version:
2.3.0

In this tutorial, we want to identify Tickets for which the support team is waiting for a 3rd party provider information/action, and cannot work on the ticket without it. In order to achieve this goal, we will:

  1. Add a new state to the UserRequest, indicating that we are waiting for the 3rd party provider
  2. Add a new stimulus, to move a User Request in that new state
  3. Add two transitions, to allow the UserRequest to be moved to and out of this new state

In a second step we want to track how much time was spent waiting for the provider on each ticket. This requires to:

  1. Add a stopwatch attribute on UserRequest class, to count when the ticket is in “pending_provider” state.
  2. Add a sub-item field to display the time spent while waiting for provider.
  3. Display the total time spent waiting for the 3rd party provider only in some states of the UserRequest

Declare a new state

itop_design / classes / class@UserRequest / fields
      <!-- Add a new state value to the existing status -->
      <field id="status" _delta="must_exist">
        <values>
          <value id="pending_provider" _delta=define">pending_provider</value>
        </values>
      </field>
itop_design / dictionaries / dictionary@EN US
  <!-- Give a label to the new state -->
  <entry id="Class:UserRequest/Attribute:status/Value:pending_provider" _delta="define">
    <![CDATA[Pending provider]]>
  </entry>
  • Note the dictionary code logic for an enum value: Class:<class_name>/Attribute:<attribute_code>/Value:<value_code>

Add a stimulus

The stimulus correspond to the button and “Other actions” menu which will be proposed on the details of the UserRequest, when this action will be possible.

itop_design / classes / class@UserRequest / lifecycle
      <stimuli>
        <!-- Add a new stimulus to switch to pending_provider state -->
        <stimulus id="ev_pending_provider" xsi:type="StimulusUserAction" _delta="define"/>
      </stimuli>
itop_design / dictionaries / dictionary@EN US / entries
  <entry id="Class:UserRequest/Stimulus:ev_pending_provider" _delta="define">
    <![CDATA[Wait for Provider]]>
  </entry>
  • Note the dictionary code logic for a stimulus: Class:UserRequest/Stimulus:stimulus_id
  • The label of a stimulus is applied on all transitions using this stimulus.
  • In general a given stimulus always end-up on the same state.
  • The stimulus declaration does not specify how it will be used, it can be used for any transition between any states.
If you need to have a different label for two transitions, then create two different stimulus

Add state and transitions

A transition is defined by:

  1. a starting state,
  2. a stimulus, which triggers the transition and is proposed as a possible action on the starting state
  3. an ending state, which is the resulting state in which the object will be after the transition

Here we want to propose two possible transitions:

  1. One transition from the “assigned” to “pending_provider” state using the newly created stimulus “ev_pending_provider”. It will be used when the support team is waiting for the 3rd party provider to act and respond.
  2. One transition to go back from “pending_provider” to “assigned” using the classic “ev_assign” stimulus. It will be used when provider has replied/act and that support team can go on with the Ticket resolution.
itop_design / classes / class@UserRequest / lifecycle
  <states>
    <!-- This level define the state we are in -->
    <state id="assigned" _delta="must_exist">
      <transitions>
        <!-- this define an available transition from the above state -->
        <transition id="ev_pending_provider" _delta="define">
          <!-- This define the stimulus which trigger the transition -->
          <!-- on a given state there can only be one transition triggered by a given stimulus --> 
          <stimulus>ev_pending_provider</stimulus>
          <!-- This define on which state the UserRequest will end-up -->
          <target>pending_provider</target>
          <actions/>
        </transition>
      </transitions>
    </state>
    <!-- From the new state -->
    <state id="pending_provider" _delta="define">
      <flags /> <!-- it's a mandatory tag, even if empty -->
      <transitions>
        <!-- we want to assign the ticket back to the support team -->
        <transition id="ev_assign">
          <stimulus>ev_assign</stimulus>
          <target>assigned</target>
          <actions/>
        </transition>
      </transitions>
      <!-- The fields are the same as in "assigned" state -->
      <inherit_flags_from>assigned</inherit_flags_from>
    </state>
  </states>
When you define a new state, you don't need to redefine for each and every field, if that field, should be read_only, hidden or mandatory in this state. You can just say that it inherits from another state and describe the difference under a <flags> tag. See this tutorial for example: Force a field to be mandatory by lifecycle

Counting time waiting for 3rd party

In this complementary usecase, we want to track how much time was spent waiting for the provider on each ticket. For this we will

  1. Add a stopwatch attribute on UserRequest class, to count when the ticket is in “pending_provider” state.
  2. Add a sub-item field to display the time spent while waiting for provider.
  3. Display the total time spent waiting for the 3rd party provider only in some states of the UserRequest

Add a stopwatch

itop_design / classes / class@UserRequest / fields
      <!-- Add a stopwatch to count the timespent in this state -->
      <field id="providerstopwatch" xsi:type="AttributeStopWatch" _delta="define">
        <states>
          <!-- here are the states during which the stopwatch is counting time -->
          <state id="pending_provider">pending_provider</state>
        </states>
        <working_time/>
        <thresholds/>
        <always_load_in_tables>true</always_load_in_tables>
      </field>
itop_design / dictionaries / dictionary@EN US / entries
  <entry id="Class:UserRequest/Attribute:providerstopwatch" _delta="define">
    <![CDATA[Provider stopwatch]]>
  </entry>
  • This is the simplest use you can make of a stopwatch, just counting time spent in particular states

Track time spent

This step is mandatory to be able to display this information in a form and query it by OQL.

itop_design / classes / class@UserRequest / fields
      <!-- Declaring this stopwatch sub-item is required to display or query it -->
      <field id="pending_provider_delay" xsi:type="AttributeSubItem" _delta="define">
        <target_attcode>providerstopwatch</target_attcode>
        <!-- Possible item_code: timespent, started, laststart, stopped  -->
        <item_code>timespent</item_code>
      </field>
    </fields>
itop_design / dictionaries / dictionary@EN US / entries
  <entry id="Class:UserRequest/Attribute:pending_provider_delay" _delta="define">
    <![CDATA[Provider time spent]]>
  </entry>

Display Stopwatch

In this example, we will display the stopwatch it-self. This field is rather special as it is a composite one, it contains sub-information, which are all displayed. We have decided to display the stopwatch all the time, regardless if it is running or not. You will see that the displayed data will not be available in some state.

Sub-items can also be displayed independently, assuming you have declared them, as we just did for the sub-item timespent. So we will also display the time spent.

itop_design / classes / class@UserRequest
    <!-- Display the 2 new fields in the details of the UserRequest -->
    <presentation>
      <details _delta="must_exist">
        <items>
          <!-- First column of the UserRequest display -->
          <item id="col:col1">
            <items>
              <item id="fieldset:Ticket:moreinfo">
                <items>
                  <!-- Add the provider timespent delay -->
                  <item id="pending_provider_delay" _delta="define">
                    <rank>80</rank>
                  </item>
                  <!-- This displays a stopwatch (including some sub-items) -->
                  <!-- This will help you to understand how a stopwatch behave -->
                  <item id="providerstopwatch" _delta="define">
                    <rank>90</rank>
                  </item>
                </items>
                <rank>20</rank>
              </item>
            </items>
          </item>
        </items>
      </details>
    </presentation>

Display time spent

Because the time spent is only calculated when the stopwatch is stopping - so here when the UserRequest exits the “pending_provider” state -

  • it should not be displayed in state “pending_provider”, as it does not provide an accurate information
  • we decided not to display it in state “new”, as it's useless and empty
  • we decided to display it when the ticket is resolved or closed, in read only as it should not be modified by the user.

We have defined in the presentation, where that field need to be displayed, now we can modulate when it should be available, by defining for each state, if this field should be hidden or read only.

  • When we define a field to be hidden in state new, as states are chained in standard datamodel, this propagates to all following states.
  • When we define it to be read only in state resolved, it overwrites the hidden flag
  • As closed is changed to resolved state, it is still visible
itop_design / classes / class@UserRequest
    <lifecycle>
      <states>
        <state id="new" _delta="must_exist">
          <flags>
            <!-- Hide the pending provider_delay, it will propagate to all chained states -->
            <attribute id="pending_provider_delay" _delta="define">
              <hidden/>
            </attribute>
          </flags>
        </state>
        <state id="resolved" _delta="must_exist">
          <flags>
            <attribute id="pending_provider_delay" _delta="define">
              <!-- Read_only takes precedence on hidden ??? -->
              <read_only/>
            </attribute>
          </flags>
        </state>
      </states>
    </lifecycle>

Here is the result on a User Request for which was moved three time back and force between “Assigned” and “Pending provider”, as a result, “Elapsed” does not correspond to the difference between “Started” and “Stopped”. But you can see that 7*60s + 49s = 469s, as “Provider time spent” is just a nice display of the “Elapsed” sub-item (=timespent)

Behavior of the Stopwatch

In this part, we will show what the stopwatch contains along the User Request life

1. Stopwatch never started:

2. User Request is moved from “Assigned” to Pending provider for the first time:

  • stopwatch is running, so “Elapsed” (= timespent) is not available
  • “Started” and “LastStart” are equal

3. User Request is moved back from “Pending Provider” to Assigned for the first time:

  • stopwatch is stopped, so “Elapsed” (= timespent) is available
  • “LastStart” is empty when not running
  • “Stopped” is documented

4. User Request is moved again from “Assigned” to Pending provider:

  • stopwatch is running, so “Elapsed” (= timespent) is not available
  • “Started” is unchanged
  • “LastStart” correspond to the last time the stopwatch has started
  • “Stopped” is empty when running

5. User Request is moved back again from “Pending Provider” to Assigned:

  • stopwatch is stopped, so “Elapsed” (= timespent) is available
  • “Started” is unchanged
  • “LastStart” is empty again, as the stopwatch is stopped
  • “Stopped” is documented again and correspond to the last stop
  • Note that “Elapsed” is not equal to the difference between “Started” and “Stopped”

A stopwatch is automatically computed and cannot be edited by a user:

Full XML

Here is the consolidated XML for this tutorial

itop_design
<classes>
  <class id="UserRequest">
    <fields>
      <!-- Add a new state value to the existing status -->
      <field id="status" _delta="must_exist">
        <values>
          <value id="pending_provider" _delta="define">pending_provider</value>
        </values>
      </field>
      <!-- Add a stopwatch to count the timespent in this state -->
      <field id="providerstopwatch" xsi:type="AttributeStopWatch" _delta="define">
        <states>
          <!-- this are the states, during which the stopwatch is counting time -->
          <state id="pending_provider">pending_provider</state>
        </states>
        <working_time/>
        <thresholds/>
        <always_load_in_tables>true</always_load_in_tables>
      </field>
      <!-- Declaring this stopwatch sub-item is required to display or query it -->
      <field id="pending_provider_delay" xsi:type="AttributeSubItem" _delta="define">
        <target_attcode>providerstopwatch</target_attcode>
        <!-- Possible item_code: timespent, started, laststart, stopped  -->
        <item_code>timespent</item_code>
      </field>
    </fields>
    <lifecycle>
      <stimuli>
        <!-- Add a new stimulus to switch to pending_provider state -->
        <stimulus _delta="define" id="ev_pending_provider" xsi:type="StimulusUserAction"/>
      </stimuli>
      <states>
        <!-- This level define the state we are in -->
        <state id="assigned" _delta="must_exist">
          <transitions>
            <!-- this define an available transition from the above state -->
            <transition id="ev_pending_provider" _delta="define">
              <!-- This define the stimulus which trigger the transition -->
              <!-- on a given state there can only be one transition triggered by a given stimulus --> 
              <stimulus>ev_pending_provider</stimulus>
              <!-- This define on which state the UserRequest will end-up -->
              <target>pending_provider</target>
              <actions/>
            </transition>
          </transitions>
        </state>
        <!-- From the new state -->
        <state id="pending_provider" _delta="define">
          <flags /> <!-- Also empty it's a mandatory tag -->
          <transitions>
            <!-- we want to assign the ticket back to the support team -->
            <transition id="ev_assign">
              <stimulus>ev_assign</stimulus>
              <target>assigned</target>
              <actions/>
            </transition>
          </transitions>
          <!-- The fields are the same as in "assigned" state -->
          <inherit_flags_from>assigned</inherit_flags_from>
        </state>
        <state _delta="must_exist" id="new">
          <flags>
            <!-- Hide the pending provider_delay, it will propagate to all chained states -->
            <attribute id="pending_provider_delay" _delta="define">
              <hidden/>
            </attribute>
          </flags>
        </state>
        <state id="resolved" _delta="must_exist">
          <flags>
            <attribute id="pending_provider_delay" _delta="define">
              <!-- Read_only takes precedence on hidden ??? -->
              <read_only/>
            </attribute>
          </flags>
        </state>
      </states>
    </lifecycle>
    <!-- Display the 2 new fields in the details of the UserRequest -->
    <presentation>
      <details _delta="must_exist">
        <items>
          <!-- First column of the UserRequest display -->
          <item id="col:col0">
            <items>
              <item id="fieldset:Ticket:moreinfo">
                <items>
                  <!-- Add the provider timespent delay -->
                  <item id="pending_provider_delay" _delta="define">
                    <rank>80</rank>
                  </item>
                  <!-- This displays a stopwatch (including some sub-items) -->
                  <!-- This will help you to understand how a stopwatch behave -->
                  <item id="providerstopwatch" _delta="define">
                    <rank>90</rank>
                  </item>
                </items>
                <rank>20</rank>
              </item>
            </items>
          </item>
        </items>
      </details>
    </presentation>
  </class>
</classes>
<dictionaries>
  <dictionary id="EN US">
    <entries>
      <!-- Give a label to the new state -->
      <entry id="Class:UserRequest/Attribute:status/Value:pending_provider" _delta="define">
        <![CDATA[Pending provider]]>
      </entry>
      <entry id="Class:UserRequest/Attribute:pending_provider_delay" _delta="define">
        <![CDATA[Provider time spent]]>
      </entry>
      <entry id="Class:UserRequest/Stimulus:ev_pending_provider" _delta="define">
        <![CDATA[Wait for Provider]]>
      </entry>
      <entry id="Class:UserRequest/Attribute:providerstopwatch" _delta="define">
        <![CDATA[Provider stopwatch]]>
      </entry>
    </entries>
  </dictionary>
</dictionaries>
2_7_0/customization/add-state.txt · Last modified: 2020/04/15 15:23 (external edit)
Back to top
Contact us