Difference between revisions of "WorkFlow2"

From Request Tracker Wiki
Jump to navigation Jump to search
(Main Scrip Setup)
m
 
(10 intermediate revisions by 2 users not shown)
Line 21: Line 21:
 
== Template Setup ==
 
== Template Setup ==
  
Add a single global template that consists of the following:
+
Add a single global template called "WorkflowTemplate" that consists of the following:
  
<nowiki>===Create-Ticket:  {$Tickets{'TOP'}-&gt;FirstCustomFieldValue('WorkflowName');}::{$Tickets{'TOP'}-&gt;FirstCustomFieldValue('WorkflowStage');}
+
<pre>
Subject: Workflow::{$Tickets{'TOP'}-&gt;Subject;}
+
  ===Create-Ticket:  {$Tickets{'TOP'}-&gt;FirstCustomFieldValue('WorkflowName');}::{$Tickets{'TOP'}-&gt;FirstCustomFieldValue('WorkflowStage');}
Parents: TOP
+
  Subject: Workflow::{$Tickets{'TOP'}-&gt;Subject;}
Queue: {$Tickets{'TOP'}-&gt;FirstCustomFieldValue('WorkflowStageQueue');}
+
  Parents: TOP
Owner: {$Tickets{'TOP'}-&gt;FirstCustomFieldValue('WorkflowStageOwner');}
+
  Queue: {$Tickets{'TOP'}-&gt;FirstCustomFieldValue('WorkflowStageQueue');}
Content: {$Tickets{'TOP'}-&gt;FirstCustomFieldValue('WorkflowStageDesc');}
+
  Owner: {$Tickets{'TOP'}-&gt;FirstCustomFieldValue('WorkflowStageOwner');}
ENDOFCONTENT
+
  Content: {$Tickets{'TOP'}-&gt;FirstCustomFieldValue('WorkflowStageDesc');}
</nowiki>
+
  ENDOFCONTENT
 +
</pre>
  
 
== Main Scrip Setup ==
 
== Main Scrip Setup ==
  
On User Defined Create Tickets with Template (aforementioned) (Global) Beware, this scrip is pretty ugly. Basically, it's a ticket creation scrip that performs a lot more than condition checking in the condition section (i didnt wanna mess around with making a new ticket from a template in the scrip, and this was easy).
+
Create a global scrip;
  
Note: From the veiwpoint of a non RT guru, this step is incomprehensible. When you create a scrip, you need to set Condition, Actiion, Template and Stage parameters (RT3.6). The correct settings for these are not provided. Also, where does one paste the code? I assume that it goes in the Custom Condition box but I really hate having to assume stuff when following instructions.
+
Condition: User Defined
  
  <nowiki>#example workflow.  Whatever you do, do NOT make '|' (a pipe symbol) the first character in a stagename
+
Action: Create Tickets
  my @newcompworkflow = (
+
 
        #  stagename              stagedesc                                                    owner      queue
+
Template: WorkflowTemplate
        ["AskUserForSoftware", "please ask the user what specialized software they would like", "person1",  "mainhelpqueue"],
+
 
        ["OrderNewComputer", "please order the user's new computer", "person1", "purchases"],
+
Stage: TransactionCreate
        ["ImageMachine","please image the user's new machine","person2", "mainhelpqueue"],
+
 
        ["blah","please blah blah blah","person2","someotherqueue"],
+
Beware, this scrip is pretty ugly. Basically, it's a ticket creation scrip that performs a lot more than condition checking in the condition section (i didnt wanna mess around with making a new ticket from a template in the scrip, and this was easy).
  );
+
 
  my %workflows = ();
+
Paste the code below into the "Custom Condition" text box
  #Set workflow name to 'New Computer'
+
 
  #Make as many workflows you want like the example above and define them here.
+
  <pre>
  #The name here ('New Computer') will correspond to the possible values for the customfield WorkflowName, so add them  as values there.
+
        #example workflow.  Whatever you do, do NOT make '|' (a pipe symbol) the first character in a stagename
  $workflows{'New Computer'} = \@newcompworkflow;
+
        my @newcompworkflow = (
  my $stagename = 0;
+
              #  stagename              stagedesc                                                    owner      queue
  my $stagedesc = 1;
+
              ["AskUserForSoftware", "please ask the user what specialized software they would like", "person1",  "mainhelpqueue"],
  my $stageowner = 2;
+
              ["OrderNewComputer", "please order the user's new computer", "person1", "purchases"],
  my $stagequeue = 3;
+
              ["ImageMachine","please image the user's new machine","person2", "mainhelpqueue"],
  my $wfn = 'WorkflowName';
+
              ["blah","please blah blah blah","person2","someotherqueue"],
 +
        );
 +
        my %workflows = ();
 +
        #Set workflow name to 'New Computer'
 +
        #Make as many workflows you want like the example above and define them here.
 +
        #The name here ('New Computer') will correspond to the possible values for the customfield WorkflowName, so add them  as values there.
 +
        $workflows{'New Computer'} = \@newcompworkflow;
 +
        my $stagename = 0;
 +
        my $stagedesc = 1;
 +
        my $stageowner = 2;
 +
        my $stagequeue = 3;
 +
        my $wfn = 'WorkflowName';
 +
        my $wfs = 'WorkflowStage';
 +
        my $wfsd = 'WorkflowStageDesc';
 +
        my $wfso = 'WorkflowStageOwner';
 +
        my $wfsq = 'WorkflowStageQueue';
 +
       
 +
          my $QueueObj = $self-&gt;TicketObj-&gt;QueueObj;
 +
        my $wfnobj = RT::CustomField-&gt;new( $QueueObj-&gt;CurrentUser );
 +
          $wfnobj-&gt;LoadByNameAndQueue( Name =&gt; $wfn, Queue =&gt; $QueueObj-&gt;id );
 +
          unless( $wfnobj-&gt;id ) {
 +
            $wfnobj-&gt;LoadByNameAndQueue( Name =&gt; $wfn, Queue =&gt; 0 );
 +
            unless( $wfnobj-&gt;id ) {
 +
              $RT::Logger-&gt;warning("custom field '$wfn' isn't global or defined for queue '". $QueueObj-&gt;Name ."'");
 +
              return undef;
 +
            }
 +
          }
 +
       
 +
          my $wfsobj = RT::CustomField-&gt;new( $QueueObj-&gt;CurrentUser );
 +
          $wfsobj-&gt;LoadByNameAndQueue( Name =&gt; $wfs, Queue =&gt; $QueueObj-&gt;id );
 +
          unless( $wfsobj-&gt;id ) {
 +
            $wfsobj-&gt;LoadByNameAndQueue( Name =&gt; $wfs, Queue =&gt; 0 );
 +
            unless( $wfsobj-&gt;id ) {
 +
              $RT::Logger-&gt;warning("custom field '$wfs' isn't global or defined for queue '". $QueueObj-&gt;Name ."'");
 +
              return undef;
 +
            }
 +
          }
 +
          my $wfsdobj = RT::CustomField-&gt;new( $QueueObj-&gt;CurrentUser );
 +
        $wfsdobj-&gt;LoadByNameAndQueue( Name =&gt; $wfsd, Queue =&gt; $QueueObj-&gt;id );
 +
          unless( $wfsdobj-&gt;id ) {
 +
            $wfsdobj-&gt;LoadByNameAndQueue( Name =&gt; $wfsd, Queue =&gt; 0 );
 +
            unless( $wfsdobj-&gt;id ) {
 +
              $RT::Logger-&gt;warning("custom field '$wfsd' isn't global or defined for queue '". $QueueObj-&gt;Name ."'");
 +
              return undef;
 +
            }
 +
          }
 +
       
 +
          my $wfsoobj = RT::CustomField-&gt;new( $QueueObj-&gt;CurrentUser );
 +
        $wfsoobj-&gt;LoadByNameAndQueue( Name =&gt; $wfso, Queue =&gt; $QueueObj-&gt;id );
 +
          unless( $wfsoobj-&gt;id ) {
 +
            $wfsoobj-&gt;LoadByNameAndQueue( Name =&gt; $wfso, Queue =&gt; 0 );
 +
            unless( $wfsoobj-&gt;id ) {
 +
              $RT::Logger-&gt;warning("custom field '$wfso' isn't global or defined for queue '". $QueueObj-&gt;Name ."'");
 +
              return undef;
 +
            }
 +
          }
 +
          my $wfsqobj = RT::CustomField-&gt;new( $QueueObj-&gt;CurrentUser );
 +
        $wfsqobj-&gt;LoadByNameAndQueue( Name =&gt; $wfsq, Queue =&gt; $QueueObj-&gt;id );
 +
          unless( $wfsqobj-&gt;id ) {
 +
            $wfsqobj-&gt;LoadByNameAndQueue( Name =&gt; $wfsq, Queue =&gt; 0 );
 +
            unless( $wfsqobj-&gt;id ) {
 +
              $RT::Logger-&gt;warning("custom field '$wfsq' isn't global or defined for queue '". $QueueObj-&gt;Name ."'");
 +
              return undef;
 +
            }
 +
          }
 +
       
 +
        #See if this ticket is part of a workflow
 +
        my $wfname = $self-&gt;TicketObj-&gt;FirstCustomFieldValue('WorkflowName');
 +
        my @workflow;
 +
        if ($wfname ne '') {
 +
              @workflow = @{$workflows{$wfname}};
 +
        }
 +
       
 +
        #If the customfield workflowname was just set, then set workflowstage to the first stage of that workflow
 +
        if ( ( $self-&gt;TransactionObj-&gt;Type eq 'CustomField' ) &amp;&amp;
 +
            ($self-&gt;TransactionObj-&gt;Field == $wfnobj-&gt;id) ) {
 +
       
 +
                if ($workflows{$wfname}) {
 +
                    my( $st, $msg ) = $self-&gt;TicketObj-&gt;AddCustomFieldValue(
 +
                                                  Field =&gt; $wfsobj-&gt;id,
 +
                                                  Value =&gt; $workflow[0][$stagename],
 +
                                                  RecordTransaction =&gt; 1 );
 +
                }
 +
                  return undef;
 +
                #even though this did what we wanted it to, return undef because we have no email to send at this point
 +
        }
 +
        #if we dont have a workflow by now, bail
 +
        if (!@workflow) {
 +
            return undef;
 +
        }
 +
        #if the workflowstage is prefixed by a '|', advance to the next workflow stage in the sequence, or 'completed' if  it's done
 +
        #because completion generates no notice, i'd recommend having the last item in your workflow be a notification of the responsible party
 +
       
 +
        if ( ($self-&gt;TransactionObj-&gt;Type eq "CustomField") &amp;&amp;
 +
              ($self-&gt;TransactionObj-&gt;Field == $wfsobj-&gt;id) ) {
 +
            if (substr($self-&gt;TicketObj-&gt;FirstCustomFieldValue('WorkflowStage'), 0, 1) eq '|') {
 +
                my $x = 0;
 +
                for ($x=0;$x&lt;@workflow;$x++) {
 +
                    if ($workflow[$x][$stagename] eq substr($self-&gt;TicketObj-&gt;FirstCustomFieldValue('WorkflowStage'), 1)) {
 +
                            my( $st, $msg ) = $self-&gt;TicketObj-&gt;AddCustomFieldValue(
 +
                                                  Field =&gt; $wfsobj-&gt;id,
 +
                                                  Value =&gt; ($x == $#workflow) ? 'Completed' : $workflow[$x+1][$stagename],
 +
                                                  RecordTransaction =&gt; 1 );
 +
                    }
 +
                }
 +
                return undef;
 +
            }
 +
            #If the workflowstage was just set, update the corresponding description, owner, and queue, and send off the new ticket
 +
            my $x = 0;
 +
            for ($x =0;$x&lt;@workflow;$x++) {
 +
              if ($workflow[$x][$stagename] eq $self-&gt;TicketObj-&gt;FirstCustomFieldValue('WorkflowStage')) {
 +
            my( $st, $msg ) = $self-&gt;TicketObj-&gt;AddCustomFieldValue(
 +
                                                  Field =&gt; $wfsdobj-&gt;id,
 +
                                                  Value =&gt; $workflow[$x][$stagedesc],
 +
                                                  RecordTransaction =&gt; 0 );
 +
       
 +
                my( $st, $msg ) = $self-&gt;TicketObj-&gt;AddCustomFieldValue(
 +
                                                  Field =&gt; $wfsoobj-&gt;id,
 +
                                                  Value =&gt; $workflow[$x][$stageowner],
 +
                                                  RecordTransaction =&gt; 0 );
 +
                my( $st, $msg ) = $self-&gt;TicketObj-&gt;AddCustomFieldValue(
 +
                                                  Field =&gt; $wfsqobj-&gt;id,
 +
                                                  Value =&gt; $workflow[$x][$stagequeue],
 +
                                                  RecordTransaction =&gt; 0 );
 +
       
 +
            return 1;
 +
                }
 +
            }
 +
       
 +
        }
 +
        return undef;
 +
       
 +
</pre>
 +
 
 +
I apologize for the terrible formatting, if someone has a perl formatter handy feel free to have at it ;)
 +
 
 +
== Stage Advancer scrip ==
 +
 
 +
The second scrip (also global) is responsible for changing the workflowstage from "stagename" to "|stagename" on the resolution of a child ticket. This is all that is necessary for the main scrip to notice that it is time to advance to the next stage.
 +
 
 +
Readers Note: <sigh> Condition? Action? Template? Stage?
 +
 
 +
<pre>
 +
  my $MemberOf = $self-&gt;TicketObj-&gt;MemberOf;
 +
  my $l = $MemberOf-&gt;Next;
 +
  my $parentObj = $l-&gt;TargetObj;
 +
 
 +
  my $QueueObj = $parentObj-&gt;QueueObj;
 
   my $wfs = 'WorkflowStage';
 
   my $wfs = 'WorkflowStage';
  my $wfsd = 'WorkflowStageDesc';
 
  my $wfso = 'WorkflowStageOwner';
 
  my $wfsq = 'WorkflowStageQueue';
 
 
 
  my $QueueObj = $self-&gt;TicketObj-&gt;QueueObj;
 
  my $wfnobj = RT::CustomField-&gt;new( $QueueObj-&gt;CurrentUser );
 
  $wfnobj-&gt;LoadByNameAndQueue( Name =&gt; $wfn, Queue =&gt; $QueueObj-&gt;id );
 
  unless( $wfnobj-&gt;id ) {
 
    $wfnobj-&gt;LoadByNameAndQueue( Name =&gt; $wfn, Queue =&gt; 0 );
 
    unless( $wfnobj-&gt;id ) {
 
      $RT::Logger-&gt;warning("custom field '$wfn' isn't global or defined for queue '". $QueueObj-&gt;Name ."'");
 
      return undef;
 
    }
 
  }
 
 
 
 
   my $wfsobj = RT::CustomField-&gt;new( $QueueObj-&gt;CurrentUser );
 
   my $wfsobj = RT::CustomField-&gt;new( $QueueObj-&gt;CurrentUser );
 
   $wfsobj-&gt;LoadByNameAndQueue( Name =&gt; $wfs, Queue =&gt; $QueueObj-&gt;id );
 
   $wfsobj-&gt;LoadByNameAndQueue( Name =&gt; $wfs, Queue =&gt; $QueueObj-&gt;id );
Line 81: Line 214:
 
     }
 
     }
 
   }
 
   }
  my $wfsdobj = RT::CustomField-&gt;new( $QueueObj-&gt;CurrentUser );
+
  my $curval = $parentObj-&gt;FirstCustomFieldValue('WorkflowStage');
  $wfsdobj-&gt;LoadByNameAndQueue( Name =&gt; $wfsd, Queue =&gt; $QueueObj-&gt;id );
 
  unless( $wfsdobj-&gt;id ) {
 
    $wfsdobj-&gt;LoadByNameAndQueue( Name =&gt; $wfsd, Queue =&gt; 0 );
 
    unless( $wfsdobj-&gt;id ) {
 
      $RT::Logger-&gt;warning("custom field '$wfsd' isn't global or defined for queue '". $QueueObj-&gt;Name ."'");
 
      return undef;
 
    }
 
  }
 
 
    
 
    
  my $wfsoobj = RT::CustomField-&gt;new( $QueueObj-&gt;CurrentUser );
+
   if ($curval ne '') {
  $wfsoobj-&gt;LoadByNameAndQueue( Name =&gt; $wfso, Queue =&gt; $QueueObj-&gt;id );
+
    my( $st, $msg ) = $parentObj-&gt;AddCustomFieldValue(
  unless( $wfsoobj-&gt;id ) {
 
    $wfsoobj-&gt;LoadByNameAndQueue( Name =&gt; $wfso, Queue =&gt; 0 );
 
    unless( $wfsoobj-&gt;id ) {
 
      $RT::Logger-&gt;warning("custom field '$wfso' isn't global or defined for queue '". $QueueObj-&gt;Name ."'");
 
      return undef;
 
    }
 
  }
 
  my $wfsqobj = RT::CustomField-&gt;new( $QueueObj-&gt;CurrentUser );
 
  $wfsqobj-&gt;LoadByNameAndQueue( Name =&gt; $wfsq, Queue =&gt; $QueueObj-&gt;id );
 
  unless( $wfsqobj-&gt;id ) {
 
    $wfsqobj-&gt;LoadByNameAndQueue( Name =&gt; $wfsq, Queue =&gt; 0 );
 
    unless( $wfsqobj-&gt;id ) {
 
      $RT::Logger-&gt;warning("custom field '$wfsq' isn't global or defined for queue '". $QueueObj-&gt;Name ."'");
 
      return undef;
 
    }
 
  }
 
 
 
  #See if this ticket is part of a workflow
 
  my $wfname = $self-&gt;TicketObj-&gt;FirstCustomFieldValue('WorkflowName');
 
  my @workflow;
 
   if ($wfname ne '') {
 
      @workflow = @{$workflows{$wfname}};
 
  }
 
 
 
  #If the customfield workflowname was just set, then set workflowstage to the first stage of that workflow
 
  if ( ( $self-&gt;TransactionObj-&gt;Type eq 'CustomField' ) &amp;&amp;
 
      ($self-&gt;TransactionObj-&gt;Field == $wfnobj-&gt;id) ) {
 
 
 
          if ($workflows{$wfname}) {
 
              my( $st, $msg ) = $self-&gt;TicketObj-&gt;AddCustomFieldValue(
 
 
                                           Field =&gt; $wfsobj-&gt;id,
 
                                           Field =&gt; $wfsobj-&gt;id,
                                           Value =&gt; $workflow[0][$stagename],
+
                                           Value =&gt; '|' . $curval,
 
                                           RecordTransaction =&gt; 1 );
 
                                           RecordTransaction =&gt; 1 );
          }
+
    return 1;
          return undef;
 
        #even though this did what we wanted it to, return undef because we have no email to send at this point
 
  }
 
  #if we dont have a workflow by now, bail
 
  if (!@workflow) {
 
    return undef;
 
  }
 
  #if the workflowstage is prefixed by a '|', advance to the next workflow stage in the sequence, or 'completed' if  it's done
 
  #because completion generates no notice, i'd recommend having the last item in your workflow be a notification of the responsible party
 
 
 
  if ( ($self-&gt;TransactionObj-&gt;Type eq "CustomField") &amp;&amp;
 
        ($self-&gt;TransactionObj-&gt;Field == $wfsobj-&gt;id) ) {
 
    if (substr($self-&gt;TicketObj-&gt;FirstCustomFieldValue('WorkflowStage'), 0, 1) eq '|') {
 
        my $x = 0;
 
        for ($x=0;$x&lt;@workflow;$x++) {
 
              if ($workflow[$x][$stagename] eq substr($self-&gt;TicketObj-&gt;FirstCustomFieldValue('WorkflowStage'), 1)) {
 
                    my( $st, $msg ) = $self-&gt;TicketObj-&gt;AddCustomFieldValue(
 
                                          Field =&gt; $wfsobj-&gt;id,
 
                                          Value =&gt; ($x == $#workflow) ? 'Completed' : $workflow[$x+1][$stagename],
 
                                          RecordTransaction =&gt; 1 );
 
              }
 
        }
 
        return undef;
 
    }
 
    #If the workflowstage was just set, update the corresponding description, owner, and queue, and send off the new ticket
 
    my $x = 0;
 
    for ($x =0;$x&lt;@workflow;$x++) {
 
        if ($workflow[$x][$stagename] eq $self-&gt;TicketObj-&gt;FirstCustomFieldValue('WorkflowStage')) {
 
    my( $st, $msg ) = $self-&gt;TicketObj-&gt;AddCustomFieldValue(
 
                                          Field =&gt; $wfsdobj-&gt;id,
 
                                          Value =&gt; $workflow[$x][$stagedesc],
 
                                          RecordTransaction =&gt; 0 );
 
 
 
        my( $st, $msg ) = $self-&gt;TicketObj-&gt;AddCustomFieldValue(
 
                                          Field =&gt; $wfsoobj-&gt;id,
 
                                          Value =&gt; $workflow[$x][$stageowner],
 
                                          RecordTransaction =&gt; 0 );
 
        my( $st, $msg ) = $self-&gt;TicketObj-&gt;AddCustomFieldValue(
 
                                          Field =&gt; $wfsqobj-&gt;id,
 
                                          Value =&gt; $workflow[$x][$stagequeue],
 
                                          RecordTransaction =&gt; 0 );
 
 
 
    return 1;
 
        }
 
    }
 
 
 
 
   }
 
   }
 
   return undef;
 
   return undef;
 
+
</pre>
  </nowiki>
 
 
 
I apologize for the terrible formatting, if someone has a perl formatter handy feel free to have at it ;)
 
 
 
== Stage Advancer scrip ==
 
 
 
The second scrip (also global) is responsible for changing the workflowstage from "stagename" to "|stagename" on the resolution of a child ticket. This is all that is necessary for the main scrip to notice that it is time to advance to the next stage.
 
 
 
<nowiki>my $MemberOf = $self-&gt;TicketObj-&gt;MemberOf;
 
my $l = $MemberOf-&gt;Next;
 
my $parentObj = $l-&gt;TargetObj;
 
 
my $QueueObj = $parentObj-&gt;QueueObj;
 
my $wfs = 'WorkflowStage';
 
  my $wfsobj = RT::CustomField-&gt;new( $QueueObj-&gt;CurrentUser );
 
  $wfsobj-&gt;LoadByNameAndQueue( Name =&gt; $wfs, Queue =&gt; $QueueObj-&gt;id );
 
  unless( $wfsobj-&gt;id ) {
 
    $wfsobj-&gt;LoadByNameAndQueue( Name =&gt; $wfs, Queue =&gt; 0 );
 
    unless( $wfsobj-&gt;id ) {
 
      $RT::Logger-&gt;warning("custom field '$wfs' isn't global or defined for queue '". $QueueObj-&gt;Name ."'");
 
      return undef;
 
    }
 
  }
 
my $curval = $parentObj-&gt;FirstCustomFieldValue('WorkflowStage');
 
 
if ($curval ne '') {
 
  my( $st, $msg ) = $parentObj-&gt;AddCustomFieldValue(
 
                                          Field =&gt; $wfsobj-&gt;id,
 
                                          Value =&gt; '|' . $curval,
 
                                          RecordTransaction =&gt; 1 );
 
  return 1;
 
}
 
return undef;
 
</nowiki>
 
  
 
== Kicking it off ==
 
== Kicking it off ==

Latest revision as of 18:06, 13 August 2016

Another kind of workflow for RT

This method draws heavily on the other workflow plugin and from many contributions here in general, so I definitely don't claim ownership of all the code/methods (thx guys :)) In the original workflow plugin, customfields were defined for each task and initiated manually, with (potentially) all the tasks running concurrently (as subtickets). In this model, one master ticket initiates a child ticket for the first task, when the first child is resolved the second child ticket is initiated etc. In addition, this method allows a name, description, queue, and owner for each subticket (stage) of the process. Also, this reduces the number of scrips+templates+customfields to a constant number, regardless of how many workflows you have.

Custom Field Setup

Add 5 (ticket) custom fields and activate them globally using the following names:

WorkflowName

WorkflowStage

WorkflowStageDesc

WorkflowStageOwner

WorkflowstageQueue

All of these should be "enter a value" fields with the exception of WorkflowName, which should be a "select one value" field.

Template Setup

Add a single global template called "WorkflowTemplate" that consists of the following:

  ===Create-Ticket:  {$Tickets{'TOP'}->FirstCustomFieldValue('WorkflowName');}::{$Tickets{'TOP'}->FirstCustomFieldValue('WorkflowStage');}
  Subject: Workflow::{$Tickets{'TOP'}->Subject;}
  Parents: TOP
  Queue: {$Tickets{'TOP'}->FirstCustomFieldValue('WorkflowStageQueue');}
  Owner: {$Tickets{'TOP'}->FirstCustomFieldValue('WorkflowStageOwner');}
  Content: {$Tickets{'TOP'}->FirstCustomFieldValue('WorkflowStageDesc');}
  ENDOFCONTENT

Main Scrip Setup

Create a global scrip;

Condition: User Defined

Action: Create Tickets

Template: WorkflowTemplate

Stage: TransactionCreate

Beware, this scrip is pretty ugly. Basically, it's a ticket creation scrip that performs a lot more than condition checking in the condition section (i didnt wanna mess around with making a new ticket from a template in the scrip, and this was easy).

Paste the code below into the "Custom Condition" text box

        #example workflow.  Whatever you do, do NOT make '|' (a pipe symbol) the first character in a stagename
         my @newcompworkflow = (
               #   stagename               stagedesc                                                    owner       queue
               ["AskUserForSoftware", "please ask the user what specialized software they would like", "person1",  "mainhelpqueue"],
               ["OrderNewComputer", "please order the user's new computer", "person1", "purchases"],
               ["ImageMachine","please image the user's new machine","person2", "mainhelpqueue"],
               ["blah","please blah blah blah","person2","someotherqueue"],
         );
         my %workflows = ();
         #Set workflow name to 'New Computer'
         #Make as many workflows you want like the example above and define them here.
         #The name here ('New Computer') will correspond to the possible values for the customfield WorkflowName, so add them  as values there.
         $workflows{'New Computer'} = \@newcompworkflow;
         my $stagename = 0;
         my $stagedesc = 1;
         my $stageowner = 2;
         my $stagequeue = 3;
         my $wfn = 'WorkflowName';
         my $wfs = 'WorkflowStage';
         my $wfsd = 'WorkflowStageDesc';
         my $wfso = 'WorkflowStageOwner';
         my $wfsq = 'WorkflowStageQueue';
         
          my $QueueObj = $self->TicketObj->QueueObj;
         my $wfnobj = RT::CustomField->new( $QueueObj->CurrentUser );
          $wfnobj->LoadByNameAndQueue( Name => $wfn, Queue => $QueueObj->id );
          unless( $wfnobj->id ) {
            $wfnobj->LoadByNameAndQueue( Name => $wfn, Queue => 0 );
            unless( $wfnobj->id ) {
              $RT::Logger->warning("custom field '$wfn' isn't global or defined for queue '". $QueueObj->Name ."'");
              return undef;
            }
          }
         
          my $wfsobj = RT::CustomField->new( $QueueObj->CurrentUser );
          $wfsobj->LoadByNameAndQueue( Name => $wfs, Queue => $QueueObj->id );
          unless( $wfsobj->id ) {
            $wfsobj->LoadByNameAndQueue( Name => $wfs, Queue => 0 );
            unless( $wfsobj->id ) {
              $RT::Logger->warning("custom field '$wfs' isn't global or defined for queue '". $QueueObj->Name ."'");
              return undef;
            }
          }
          my $wfsdobj = RT::CustomField->new( $QueueObj->CurrentUser );
         $wfsdobj->LoadByNameAndQueue( Name => $wfsd, Queue => $QueueObj->id );
          unless( $wfsdobj->id ) {
            $wfsdobj->LoadByNameAndQueue( Name => $wfsd, Queue => 0 );
            unless( $wfsdobj->id ) {
              $RT::Logger->warning("custom field '$wfsd' isn't global or defined for queue '". $QueueObj->Name ."'");
              return undef;
            }
          }
         
          my $wfsoobj = RT::CustomField->new( $QueueObj->CurrentUser );
         $wfsoobj->LoadByNameAndQueue( Name => $wfso, Queue => $QueueObj->id );
          unless( $wfsoobj->id ) {
            $wfsoobj->LoadByNameAndQueue( Name => $wfso, Queue => 0 );
            unless( $wfsoobj->id ) {
              $RT::Logger->warning("custom field '$wfso' isn't global or defined for queue '". $QueueObj->Name ."'");
              return undef;
            }
          }
          my $wfsqobj = RT::CustomField->new( $QueueObj->CurrentUser );
         $wfsqobj->LoadByNameAndQueue( Name => $wfsq, Queue => $QueueObj->id );
          unless( $wfsqobj->id ) {
            $wfsqobj->LoadByNameAndQueue( Name => $wfsq, Queue => 0 );
            unless( $wfsqobj->id ) {
              $RT::Logger->warning("custom field '$wfsq' isn't global or defined for queue '". $QueueObj->Name ."'");
              return undef;
            }
          }
         
         #See if this ticket is part of a workflow
         my $wfname = $self->TicketObj->FirstCustomFieldValue('WorkflowName');
         my @workflow;
         if ($wfname ne '') {
              @workflow = @{$workflows{$wfname}};
         }
         
         #If the customfield workflowname was just set, then set workflowstage to the first stage of that workflow
         if ( ( $self->TransactionObj->Type eq 'CustomField' ) &&
             ($self->TransactionObj->Field == $wfnobj->id) ) {
         
                 if ($workflows{$wfname}) {
                     my( $st, $msg ) = $self->TicketObj->AddCustomFieldValue(
                                                  Field => $wfsobj->id,
                                                  Value => $workflow[0][$stagename],
                                                  RecordTransaction => 1 );
                 }
                  return undef;
                #even though this did what we wanted it to, return undef because we have no email to send at this point
         }
         #if we dont have a workflow by now, bail
         if (!@workflow) {
            return undef;
         }
         #if the workflowstage is prefixed by a '|', advance to the next workflow stage in the sequence, or 'completed' if  it's done
         #because completion generates no notice, i'd recommend having the last item in your workflow be a notification of the responsible party
         
         if ( ($self->TransactionObj->Type eq "CustomField") &&
               ($self->TransactionObj->Field == $wfsobj->id) ) {
            if (substr($self->TicketObj->FirstCustomFieldValue('WorkflowStage'), 0, 1) eq '|') {
                my $x = 0;
                for ($x=0;$x<@workflow;$x++) {
                     if ($workflow[$x][$stagename] eq substr($self->TicketObj->FirstCustomFieldValue('WorkflowStage'), 1)) {
                            my( $st, $msg ) = $self->TicketObj->AddCustomFieldValue(
                                                  Field => $wfsobj->id,
                                                  Value => ($x == $#workflow) ? 'Completed' : $workflow[$x+1][$stagename],
                                                  RecordTransaction => 1 );
                     }
                }
                return undef;
            }
            #If the workflowstage was just set, update the corresponding description, owner, and queue, and send off the new ticket
            my $x = 0;
            for ($x =0;$x<@workflow;$x++) {
               if ($workflow[$x][$stagename] eq $self->TicketObj->FirstCustomFieldValue('WorkflowStage')) {
            my( $st, $msg ) = $self->TicketObj->AddCustomFieldValue(
                                                  Field => $wfsdobj->id,
                                                  Value => $workflow[$x][$stagedesc],
                                                  RecordTransaction => 0 );
         
                my( $st, $msg ) = $self->TicketObj->AddCustomFieldValue(
                                                  Field => $wfsoobj->id,
                                                  Value => $workflow[$x][$stageowner],
                                                  RecordTransaction => 0 );
                my( $st, $msg ) = $self->TicketObj->AddCustomFieldValue(
                                                  Field => $wfsqobj->id,
                                                  Value => $workflow[$x][$stagequeue],
                                                  RecordTransaction => 0 );
         
            return 1;
                }
            }
         
         }
         return undef;
         

I apologize for the terrible formatting, if someone has a perl formatter handy feel free to have at it ;)

Stage Advancer scrip

The second scrip (also global) is responsible for changing the workflowstage from "stagename" to "|stagename" on the resolution of a child ticket. This is all that is necessary for the main scrip to notice that it is time to advance to the next stage.

Readers Note: Condition? Action? Template? Stage?

  my $MemberOf = $self->TicketObj->MemberOf;
  my $l = $MemberOf->Next;
  my $parentObj = $l->TargetObj;
  
  my $QueueObj = $parentObj->QueueObj;
  my $wfs = 'WorkflowStage';
   my $wfsobj = RT::CustomField->new( $QueueObj->CurrentUser );
   $wfsobj->LoadByNameAndQueue( Name => $wfs, Queue => $QueueObj->id );
   unless( $wfsobj->id ) {
     $wfsobj->LoadByNameAndQueue( Name => $wfs, Queue => 0 );
     unless( $wfsobj->id ) {
       $RT::Logger->warning("custom field '$wfs' isn't global or defined for queue '". $QueueObj->Name ."'");
       return undef;
     }
   }
  my $curval = $parentObj->FirstCustomFieldValue('WorkflowStage');
  
  if ($curval ne '') {
    my( $st, $msg ) = $parentObj->AddCustomFieldValue(
                                           Field => $wfsobj->id,
                                           Value => '|' . $curval,
                                           RecordTransaction => 1 );
    return 1;
  }
  return undef;

Kicking it off

I don't know how to make it work on create because the object doesnt exist yet, but if you create a ticket and then update the workflowname, the scrip will automatically initiate the first stage and create the first child ticket. As explained in the code comments, I'd recommend having the last phase of a workflow be creating a ticket to notify the responsible party that the sequence is complete, because currently nothing happens after the last step.

Hope this helps someone