WorkFlow2: Difference between revisions

From Request Tracker Wiki
Jump to navigation Jump to search
mNo edit summary
 
(6 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 ==


Create a Global Scrip with the following parameters;
Create a global scrip;


Condition: User Defined
Condition: User Defined
Line 41: Line 42:


Template: WorkflowTemplate
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).
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).
Line 46: Line 49:
Paste the code below into the "Custom Condition" text box
Paste the code below into the "Custom Condition" text box


  <nowiki>#example workflow.  Whatever you do, do NOT make '|' (a pipe symbol) the first character in a stagename
  <pre>
      my @newcompworkflow = (
        #example workflow.  Whatever you do, do NOT make '|' (a pipe symbol) the first character in a stagename
            #  stagename              stagedesc                                                    owner      queue
        my @newcompworkflow = (
            ["AskUserForSoftware", "please ask the user what specialized software they would like", "person1",  "mainhelpqueue"],
              #  stagename              stagedesc                                                    owner      queue
            ["OrderNewComputer", "please order the user's new computer", "person1", "purchases"],
              ["AskUserForSoftware", "please ask the user what specialized software they would like", "person1",  "mainhelpqueue"],
            ["ImageMachine","please image the user's new machine","person2", "mainhelpqueue"],
              ["OrderNewComputer", "please order the user's new computer", "person1", "purchases"],
            ["blah","please blah blah blah","person2","someotherqueue"],
              ["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'
        my %workflows = ();
      #Make as many workflows you want like the example above and define them here.
        #Set workflow name to 'New Computer'
      #The name here ('New Computer') will correspond to the possible values for the customfield WorkflowName, so add them  as values there.
        #Make as many workflows you want like the example above and define them here.
      $workflows{'New Computer'} = \@newcompworkflow;
        #The name here ('New Computer') will correspond to the possible values for the customfield WorkflowName, so add them  as values there.
      my $stagename = 0;
        $workflows{'New Computer'} = \@newcompworkflow;
      my $stagedesc = 1;
        my $stagename = 0;
      my $stageowner = 2;
        my $stagedesc = 1;
      my $stagequeue = 3;
        my $stageowner = 2;
      my $wfn = 'WorkflowName';
        my $stagequeue = 3;
      my $wfs = 'WorkflowStage';
        my $wfn = 'WorkflowName';
      my $wfsd = 'WorkflowStageDesc';
        my $wfs = 'WorkflowStage';
      my $wfso = 'WorkflowStageOwner';
        my $wfsd = 'WorkflowStageDesc';
      my $wfsq = 'WorkflowStageQueue';
        my $wfso = 'WorkflowStageOwner';
     
        my $wfsq = 'WorkflowStageQueue';
      my $QueueObj = $self-&gt;TicketObj-&gt;QueueObj;
       
      my $wfnobj = RT::CustomField-&gt;new( $QueueObj-&gt;CurrentUser );
          my $QueueObj = $self-&gt;TicketObj-&gt;QueueObj;
      $wfnobj-&gt;LoadByNameAndQueue( Name =&gt; $wfn, Queue =&gt; $QueueObj-&gt;id );
        my $wfnobj = RT::CustomField-&gt;new( $QueueObj-&gt;CurrentUser );
      unless( $wfnobj-&gt;id ) {
          $wfnobj-&gt;LoadByNameAndQueue( Name =&gt; $wfn, Queue =&gt; $QueueObj-&gt;id );
        $wfnobj-&gt;LoadByNameAndQueue( Name =&gt; $wfn, Queue =&gt; 0 );
          unless( $wfnobj-&gt;id ) {
        unless( $wfnobj-&gt;id ) {
            $wfnobj-&gt;LoadByNameAndQueue( Name =&gt; $wfn, Queue =&gt; 0 );
          $RT::Logger-&gt;warning("custom field '$wfn' isn't global or defined for queue '". $QueueObj-&gt;Name ."'");
            unless( $wfnobj-&gt;id ) {
          return undef;
              $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
      my $wfsobj = RT::CustomField-&gt;new( $QueueObj-&gt;CurrentUser );
        if ( ( $self-&gt;TransactionObj-&gt;Type eq 'CustomField' ) &amp;&amp;
      $wfsobj-&gt;LoadByNameAndQueue( Name =&gt; $wfs, Queue =&gt; $QueueObj-&gt;id );
            ($self-&gt;TransactionObj-&gt;Field == $wfnobj-&gt;id) ) {
      unless( $wfsobj-&gt;id ) {
       
        $wfsobj-&gt;LoadByNameAndQueue( Name =&gt; $wfs, Queue =&gt; 0 );
                if ($workflows{$wfname}) {
        unless( $wfsobj-&gt;id ) {
                    my( $st, $msg ) = $self-&gt;TicketObj-&gt;AddCustomFieldValue(
          $RT::Logger-&gt;warning("custom field '$wfs' isn't global or defined for queue '". $QueueObj-&gt;Name ."'");
                                                  Field =&gt; $wfsobj-&gt;id,
          return undef;
                                                  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
      my $wfsdobj = RT::CustomField-&gt;new( $QueueObj-&gt;CurrentUser );
         if (!@workflow) {
      $wfsdobj-&gt;LoadByNameAndQueue( Name =&gt; $wfsd, Queue =&gt; $QueueObj-&gt;id );
            return undef;
      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;
         }
         }
      }
        #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
      my $wfsoobj = RT::CustomField-&gt;new( $QueueObj-&gt;CurrentUser );
       
      $wfsoobj-&gt;LoadByNameAndQueue( Name =&gt; $wfso, Queue =&gt; $QueueObj-&gt;id );
        if ( ($self-&gt;TransactionObj-&gt;Type eq "CustomField") &amp;&amp;
      unless( $wfsoobj-&gt;id ) {
              ($self-&gt;TransactionObj-&gt;Field == $wfsobj-&gt;id) ) {
        $wfsoobj-&gt;LoadByNameAndQueue( Name =&gt; $wfso, Queue =&gt; 0 );
            if (substr($self-&gt;TicketObj-&gt;FirstCustomFieldValue('WorkflowStage'), 0, 1) eq '|') {
         unless( $wfsoobj-&gt;id ) {
                my $x = 0;
          $RT::Logger-&gt;warning("custom field '$wfso' isn't global or defined for queue '". $QueueObj-&gt;Name ."'");
                for ($x=0;$x&lt;@workflow;$x++) {
          return undef;
                    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;
                }
            }
       
         }
         }
      }
      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;
         return undef;
      }
          
      #if the workflowstage is prefixed by a '|', advance to the next workflow stage in the sequence, or 'completed' if  it's done
</pre>
      #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;
     
      </nowiki>


I apologize for the terrible formatting, if someone has a perl formatter handy feel free to have at it ;)
I apologize for the terrible formatting, if someone has a perl formatter handy feel free to have at it ;)
Line 192: Line 196:
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.
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;
Readers Note: <sigh> Condition? Action? Template? Stage?
my $l = $MemberOf-&gt;Next;
 
my $parentObj = $l-&gt;TargetObj;
  <pre>
  my $MemberOf = $self-&gt;TicketObj-&gt;MemberOf;
my $QueueObj = $parentObj-&gt;QueueObj;
  my $l = $MemberOf-&gt;Next;
my $wfs = 'WorkflowStage';
  my $parentObj = $l-&gt;TargetObj;
  my $wfsobj = RT::CustomField-&gt;new( $QueueObj-&gt;CurrentUser );
 
  $wfsobj-&gt;LoadByNameAndQueue( Name =&gt; $wfs, Queue =&gt; $QueueObj-&gt;id );
  my $QueueObj = $parentObj-&gt;QueueObj;
  unless( $wfsobj-&gt;id ) {
  my $wfs = 'WorkflowStage';
    $wfsobj-&gt;LoadByNameAndQueue( Name =&gt; $wfs, Queue =&gt; 0 );
  my $wfsobj = RT::CustomField-&gt;new( $QueueObj-&gt;CurrentUser );
    unless( $wfsobj-&gt;id ) {
  $wfsobj-&gt;LoadByNameAndQueue( Name =&gt; $wfs, Queue =&gt; $QueueObj-&gt;id );
      $RT::Logger-&gt;warning("custom field '$wfs' isn't global or defined for queue '". $QueueObj-&gt;Name ."'");
  unless( $wfsobj-&gt;id ) {
      return undef;
    $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;
   }
   }
my $curval = $parentObj-&gt;FirstCustomFieldValue('WorkflowStage');
  return undef;
</pre>
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: <sigh> 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