<?xml version="1.0"?>
<feed xmlns="http://www.w3.org/2005/Atom" xml:lang="en">
	<id>https://rt-wiki.bestpractical.com/api.php?action=feedcontributions&amp;feedformat=atom&amp;user=Kincl</id>
	<title>Request Tracker Wiki - User contributions [en]</title>
	<link rel="self" type="application/atom+xml" href="https://rt-wiki.bestpractical.com/api.php?action=feedcontributions&amp;feedformat=atom&amp;user=Kincl"/>
	<link rel="alternate" type="text/html" href="https://rt-wiki.bestpractical.com/wiki/Special:Contributions/Kincl"/>
	<updated>2026-08-22T20:02:37Z</updated>
	<subtitle>User contributions</subtitle>
	<generator>MediaWiki 1.41.1</generator>
	<entry>
		<id>https://rt-wiki.bestpractical.com/index.php?title=CustomConditionSnippets&amp;diff=26366</id>
		<title>CustomConditionSnippets</title>
		<link rel="alternate" type="text/html" href="https://rt-wiki.bestpractical.com/index.php?title=CustomConditionSnippets&amp;diff=26366"/>
		<updated>2016-08-15T17:50:24Z</updated>

		<summary type="html">&lt;p&gt;Kincl: Fixing greater than and adding pre blocks&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;= Introduction =&lt;br /&gt;
&lt;br /&gt;
This page introduce you with some code that can be used in [[Condition]]s and is part of [[CodeSnippets]] series of articles.&lt;br /&gt;
&lt;br /&gt;
= Custom conditions specifics =&lt;br /&gt;
&lt;br /&gt;
Read [[WriteCustomCondition]] to understand basics of writing conditions.&lt;br /&gt;
&lt;br /&gt;
= Code Snippets =&lt;br /&gt;
&lt;br /&gt;
Here is list of various custom conditions you can use.&lt;br /&gt;
&lt;br /&gt;
== On Create and variants ==&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
return 0 unless $self-&amp;gt;TransactionObj-&amp;gt;Type eq &amp;quot;Create&amp;quot;;&lt;br /&gt;
return 1;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== On Create, but queue is not &#039;xxx&#039; ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
return 0 unless $self-&amp;gt;TransactionObj-&amp;gt;Type eq &amp;quot;Create&amp;quot;;&lt;br /&gt;
return 0 if $self-&amp;gt;TicketObj-&amp;gt;QueueObj-&amp;gt;Name eq &amp;quot;xxx&amp;quot;;&lt;br /&gt;
return 1;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== On Create with status resolved ===&lt;br /&gt;
&lt;br /&gt;
If you create [[Ticket]] with status resolved then standard RT condition OnResolve wouldn&#039;t trigger.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
return 0 unless $self-&amp;gt;TransactionObj-&amp;gt;Type eq &amp;quot;Create&amp;quot;;&lt;br /&gt;
return 0 unless $self-&amp;gt;TicketObj-&amp;gt;Status eq &#039;resolved&#039;;&lt;br /&gt;
return 1;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== On Correspond/Comment and variants ==&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
return 0 unless $self-&amp;gt;TransactionObj-&amp;gt;Type eq &amp;quot;Correspond&amp;quot;;&lt;br /&gt;
return 1;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== On Correspond to Unowned Ticket ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
return 0 unless $self-&amp;gt;TransactionObj-&amp;gt;Type eq &amp;quot;Correspond&amp;quot;;&lt;br /&gt;
return 0 unless $self-&amp;gt;TicketObj-&amp;gt;Owner == $RT::Nobody-&amp;gt;id;&lt;br /&gt;
return 1;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Can be used in &amp;quot;On Correspond to Unowned Notify Admin Ccs With Admin Correspondence&amp;quot;.&lt;br /&gt;
&lt;br /&gt;
== On Status Change and variants ==&lt;br /&gt;
&lt;br /&gt;
For historical reasons it&#039;s more correct to write this condition like this:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
my $txn = $self-&amp;gt;TransactionObj;&lt;br /&gt;
my $type = $txn-&amp;gt;Type;&lt;br /&gt;
return 0 unless $type eq &amp;quot;Status&amp;quot;&lt;br /&gt;
    || ( $type eq &#039;Set&#039; &amp;amp;&amp;amp; $txn-&amp;gt;Field eq &#039;Status&#039;);&lt;br /&gt;
return 1;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== On Status Change to &amp;quot;resolved&amp;quot; ===&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
my $txn = $self-&amp;gt;TransactionObj;&lt;br /&gt;
my $type = $txn-&amp;gt;Type;&lt;br /&gt;
return 0 unless $type eq &amp;quot;Status&amp;quot;&lt;br /&gt;
    || ( $type eq &#039;Set&#039; &amp;amp;&amp;amp; $txn-&amp;gt;Field eq &#039;Status&#039;);&lt;br /&gt;
&lt;br /&gt;
return 0 unless $txn-&amp;gt;NewValue eq &amp;quot;resolved&amp;quot;;&lt;br /&gt;
return 1;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== on Status Change from &amp;quot;new&amp;quot; to &amp;quot;open&amp;quot; ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
my $txn = $self-&amp;gt;TransactionObj;&lt;br /&gt;
my $type = $txn-&amp;gt;Type;&lt;br /&gt;
return 0 unless $type eq &amp;quot;Status&amp;quot;&lt;br /&gt;
    || ( $type eq &#039;Set&#039; &amp;amp;&amp;amp; $txn-&amp;gt;Field eq &#039;Status&#039;);&lt;br /&gt;
&lt;br /&gt;
return 0 unless $txn-&amp;gt;OldValue eq &amp;quot;new&amp;quot;;&lt;br /&gt;
return 0 unless $txn-&amp;gt;NewValue eq &amp;quot;open&amp;quot;;&lt;br /&gt;
return 1;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== On Queue Change ==&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
return 0 unless $self-&amp;gt;TransactionObj-&amp;gt;Type eq &amp;quot;Set&amp;quot;;&lt;br /&gt;
return 0 unless $self-&amp;gt;TransactionObj-&amp;gt;Field eq &amp;quot;Queue&amp;quot;;&lt;br /&gt;
return 1;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
== On Owner Change and variants ==&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
return 0 unless $self-&amp;gt;TransactionObj-&amp;gt;Type eq &amp;quot;Set&amp;quot;;&lt;br /&gt;
return 0 unless $self-&amp;gt;TransactionObj-&amp;gt;Field eq &amp;quot;Owner&amp;quot;;&lt;br /&gt;
return 1;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== On Owner Change from Nobody to Any ===&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
my $txn = $self-&amp;gt;TransactionObj;&lt;br /&gt;
return 0 unless $txn-&amp;gt;Type eq &amp;quot;Set&amp;quot;;&lt;br /&gt;
return 0 unless $txn-&amp;gt;Field eq &amp;quot;Owner&amp;quot;;&lt;br /&gt;
return 0 unless $txn-&amp;gt;OldValue == $RT::Nobody-&amp;gt;id;&lt;br /&gt;
return 1;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== On Take ===&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
my $txn = $self-&amp;gt;TransactionObj;&lt;br /&gt;
return 0 unless $txn-&amp;gt;Type eq &amp;quot;Set&amp;quot;;&lt;br /&gt;
return 0 unless $txn-&amp;gt;Field eq &amp;quot;Owner&amp;quot;;&lt;br /&gt;
return 0 unless $txn-&amp;gt;OldValue == $RT::Nobody-&amp;gt;id;&lt;br /&gt;
return 0 unless $txn-&amp;gt;NewValue == $txn-&amp;gt;Creator;&lt;br /&gt;
return 1;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== On Steal ===&lt;br /&gt;
&lt;br /&gt;
See also [[OnStealEnhanced]]&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
my $txn = $self-&amp;gt;TransactionObj;&lt;br /&gt;
return 0 unless $txn-&amp;gt;Type eq &amp;quot;Set&amp;quot;;&lt;br /&gt;
return 0 unless $txn-&amp;gt;Field eq &amp;quot;Owner&amp;quot;;&lt;br /&gt;
return 0 if $txn-&amp;gt;OldValue == $RT::Nobody-&amp;gt;id;&lt;br /&gt;
return 0 unless $txn-&amp;gt;NewValue == $txn-&amp;gt;Creator;&lt;br /&gt;
return 1;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== On Give ===&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
my $txn = $self-&amp;gt;TransactionObj;&lt;br /&gt;
return 0 unless $txn-&amp;gt;Type eq &amp;quot;Set&amp;quot;;&lt;br /&gt;
return 0 unless $txn-&amp;gt;Field eq &amp;quot;Owner&amp;quot;;&lt;br /&gt;
return 0 unless $txn-&amp;gt;OldValue == $RT::Nobody-&amp;gt;id;&lt;br /&gt;
return 0 if $txn-&amp;gt;NewValue == $txn-&amp;gt;Creator;&lt;br /&gt;
return 1;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== On Give Up ===&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
my $txn = $self-&amp;gt;TransactionObj;&lt;br /&gt;
return 0 unless $txn-&amp;gt;Type eq &amp;quot;Set&amp;quot;;&lt;br /&gt;
return 0 unless $txn-&amp;gt;Field eq &amp;quot;Owner&amp;quot;;&lt;br /&gt;
return 0 unless $txn-&amp;gt;OldValue == $txn-&amp;gt;Creator;&lt;br /&gt;
return 0 unless $txn-&amp;gt;NewValue == $RT::Nobody-&amp;gt;id;&lt;br /&gt;
return 1;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== On Assign ===&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
my $txn = $self-&amp;gt;TransactionObj;&lt;br /&gt;
return 0 unless $txn-&amp;gt;Type eq &amp;quot;Set&amp;quot;;&lt;br /&gt;
return 0 unless $txn-&amp;gt;Field eq &amp;quot;Owner&amp;quot;;&lt;br /&gt;
return 0 unless $txn-&amp;gt;OldValue == $RT::Nobody-&amp;gt;id;&lt;br /&gt;
return 0 if $txn-&amp;gt;NewValue == $txn-&amp;gt;Creator;&lt;br /&gt;
return 1;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== On Re-Assign ===&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
my $txn = $self-&amp;gt;TransactionObj;&lt;br /&gt;
return 0 unless $txn-&amp;gt;Type eq &amp;quot;Set&amp;quot;;&lt;br /&gt;
return 0 unless $txn-&amp;gt;Field eq &amp;quot;Owner&amp;quot;;&lt;br /&gt;
return 0 if $txn-&amp;gt;OldValue == $txn-&amp;gt;Creator;&lt;br /&gt;
return 0 if $txn-&amp;gt;NewValue == $txn-&amp;gt;Creator;&lt;br /&gt;
return 1;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Checks with acting user (actor) ===&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
my $actor = $self-&amp;gt;TransactionObj-&amp;gt;CreatorObj;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== On action by privileged ===&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
my $actor = $self-&amp;gt;TransactionObj-&amp;gt;CreatorObj;&lt;br /&gt;
return 1 if $actor-&amp;gt;Privileged;&lt;br /&gt;
return 0;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== On owner&#039;s action ===&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
my $actor = $self-&amp;gt;TransactionObj-&amp;gt;CreatorObj;&lt;br /&gt;
my $owner = $self-&amp;gt;TicketObj-&amp;gt;OwnerObj;&lt;br /&gt;
return 1 unless $actor-&amp;gt;id == $owner-&amp;gt;id;&lt;br /&gt;
return 0;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Checks with watchers and groups ==&lt;br /&gt;
&lt;br /&gt;
=== When particular email address is in requestors ===&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
return 1 if $self-&amp;gt;TicketObj-&amp;gt;IsWatcher(&lt;br /&gt;
    Type =&amp;gt; &#039;Requestor&#039;, Email =&amp;gt; &#039;foo@bar.com&#039;&lt;br /&gt;
);&lt;br /&gt;
return 0;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== When actor is member of one or more particular groups ===&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
my @enforced_groups = (&amp;quot;Group 1&amp;quot;,&amp;quot;Group 2&amp;quot;,&amp;quot;etc&amp;quot;);&lt;br /&gt;
&lt;br /&gt;
my $actor = $self-&amp;gt;TransactionObj-&amp;gt;CreatorObj;&lt;br /&gt;
&lt;br /&gt;
foreach my $gname (@enforced_groups) {&lt;br /&gt;
  my $group = RT::Group-&amp;gt;new( $self-&amp;gt;CurrentUser );&lt;br /&gt;
  $group-&amp;gt;LoadUserDefinedGroup( $gname );&lt;br /&gt;
  next unless $group-&amp;gt;id;&lt;br /&gt;
&lt;br /&gt;
  return 1 if $group-&amp;gt;HasMemberRecursively( $actor-&amp;gt;id );&lt;br /&gt;
}&lt;br /&gt;
return 0;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Owner is not in the group ===&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
my $in_group = &#039;SupportManagers&#039;;&lt;br /&gt;
&lt;br /&gt;
my $owner = $self-&amp;gt;TicketObj-&amp;gt;OwnerObj;&lt;br /&gt;
my $group = RT::Group-&amp;gt;new( $self-&amp;gt;CurrentUser );&lt;br /&gt;
$group-&amp;gt;LoadUserDefinedGroup( $in_group );&lt;br /&gt;
return 0 if $group-&amp;gt;HasMemberRecursively( $owner-&amp;gt;id );&lt;br /&gt;
&lt;br /&gt;
return 1;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Checks with Custom Fields ==&lt;br /&gt;
&lt;br /&gt;
=== When current custom field value is X ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
my $Ticket = $self-&amp;gt;TicketObj;&lt;br /&gt;
&lt;br /&gt;
return 0 unless $self-&amp;gt;TicketObj-&amp;gt;FirstCustomFieldValue(&#039;CustomFieldName&#039;) eq &#039;X&#039;;&lt;br /&gt;
return 1;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== On CustomField change ===&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
return 0 unless $self-&amp;gt;TransactionObj-&amp;gt;Type eq &#039;CustomField&#039;;&lt;br /&gt;
return 1;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== On CustomField &#039;X&#039; change ===&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
return 0 unless $self-&amp;gt;TransactionObj-&amp;gt;Type eq &#039;CustomField&#039;;&lt;br /&gt;
&lt;br /&gt;
my $cf = RT::CustomField-&amp;gt;new( $self-&amp;gt;CurrentUser );&lt;br /&gt;
$cf-&amp;gt;Load( $self-&amp;gt;TransactionObj-&amp;gt;Field );&lt;br /&gt;
return 0 unless $cf-&amp;gt;Name eq &#039;X&#039;;&lt;br /&gt;
&lt;br /&gt;
return 1;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== On CustomField &#039;X&#039; change from &#039;A&#039; to &#039;B&#039; ===&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
my $txn = $self-&amp;gt;TransactionObj;&lt;br /&gt;
return 0 unless $txn-&amp;gt;Type eq &#039;CustomField&#039;;&lt;br /&gt;
&lt;br /&gt;
my $cf = RT::CustomField-&amp;gt;new( $self-&amp;gt;CurrentUser );&lt;br /&gt;
$cf-&amp;gt;Load( $txn-&amp;gt;Field );&lt;br /&gt;
return 0 unless $cf-&amp;gt;Name eq &#039;X&#039;;&lt;br /&gt;
&lt;br /&gt;
return 0 unless $txn-&amp;gt;OldValue eq &#039;A&#039;;&lt;br /&gt;
return 0 unless $txn-&amp;gt;NewValue eq &#039;B&#039;;&lt;br /&gt;
&lt;br /&gt;
return 1;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== When Custom Field &#039;X&#039; is set to &#039;A&#039; ===&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
my $txn = $self-&amp;gt;TransactionObj;&lt;br /&gt;
my $type = $txn-&amp;gt;Type;&lt;br /&gt;
if ( $type eq &#039;Create&#039; ) {&lt;br /&gt;
    # CF can be set on create&lt;br /&gt;
    return 0 unless $self-&amp;gt;TicketObj-&amp;gt;FirstCustomFieldValue(&#039;X&#039;) eq &#039;A&#039;;&lt;br /&gt;
} elsif ( $type eq &#039;CustomField&#039; ) {&lt;br /&gt;
    # CF can be changed later&lt;br /&gt;
    my $cf = RT::CustomField-&amp;gt;new( $self-&amp;gt;CurrentUser );&lt;br /&gt;
    $cf-&amp;gt;Load( $txn-&amp;gt;Field );&lt;br /&gt;
    return 0 unless $cf-&amp;gt;Name eq &#039;X&#039;;&lt;br /&gt;
&lt;br /&gt;
    return 0 unless $txn-&amp;gt;NewValue eq &#039;A&#039;;&lt;br /&gt;
} else {&lt;br /&gt;
     return 0;&lt;br /&gt;
}&lt;br /&gt;
return 1;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== On Correspond, set Custom Field &amp;quot;X&amp;quot; to &amp;quot;0&amp;quot; ===&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
return 1;&lt;br /&gt;
&lt;br /&gt;
my $ticket = $self-&amp;gt;TicketObj;&lt;br /&gt;
my $cf_obj = RT::CustomField-&amp;gt;new($RT::SystemUser);&lt;br /&gt;
my $cf_name = &amp;quot;X&amp;quot;;&lt;br /&gt;
my $cf_value = &amp;quot;0&amp;quot;;&lt;br /&gt;
&lt;br /&gt;
$cf_obj-&amp;gt;LoadByName( Name =&amp;gt; $cf_name );&lt;br /&gt;
$RT::Logger-&amp;gt;debug( &amp;quot;Loaded \$cf_obj-&amp;gt;Name = &amp;quot;. $cf_obj-&amp;gt;Name() .&amp;quot;\n&amp;quot; );&lt;br /&gt;
$ticket-&amp;gt;AddCustomFieldValue( Field=&amp;gt;$cf_obj, Value=&amp;gt;$cf_value,&lt;br /&gt;
RecordTransaction=&amp;gt;0 );&lt;br /&gt;
&lt;br /&gt;
return 1;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== UserDefined2 (use with rt-crontool) ==&lt;br /&gt;
create a file UserDefined2.pm in lib/RT/Condition&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
package RT::Condition::UserDefined2;&lt;br /&gt;
use base &#039;RT::Condition&#039;;&lt;br /&gt;
use strict;&lt;br /&gt;
=head2 IsApplicable&lt;br /&gt;
&lt;br /&gt;
=cut&lt;br /&gt;
&lt;br /&gt;
sub IsApplicable {&lt;br /&gt;
    my $self = shift;&lt;br /&gt;
    local $@;&lt;br /&gt;
    my $retval = eval { $self-&amp;gt;Argument; }; warn $@ if $@;&lt;br /&gt;
    return ($retval);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
RT::Base-&amp;gt;_ImportOverlays();&lt;br /&gt;
1;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
With this you have a powerful way to check a customized condition. You can use it like that:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
bin/rt-crontool --verbose --log debug --search RT::Search::ActiveTicketsInQueue --search-arg Support --condition RT::Condition::UserDefined2 --condition-arg &amp;quot;if($self-&amp;gt;TicketObj-&amp;gt;Priority == 4){return(1);}else {return(undef);}&amp;quot; --action RT::Action::SetPriority --action-arg 7&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This example searches all tickets in queue Support, checks if the priority is 4 and sets the priority to 7. You may use arbitrary perl-code for the condition.&lt;br /&gt;
== add your own conditions ==&lt;br /&gt;
&lt;br /&gt;
...&lt;/div&gt;</summary>
		<author><name>Kincl</name></author>
	</entry>
	<entry>
		<id>https://rt-wiki.bestpractical.com/index.php?title=WriteCustomAction&amp;diff=26365</id>
		<title>WriteCustomAction</title>
		<link rel="alternate" type="text/html" href="https://rt-wiki.bestpractical.com/index.php?title=WriteCustomAction&amp;diff=26365"/>
		<updated>2016-08-15T17:38:42Z</updated>

		<summary type="html">&lt;p&gt;Kincl: Fixing greater than symbols post import from old wiki and adding pre wrappers&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;This text in Portuguese in [[WriteCustomActionBR]]&lt;br /&gt;
&lt;br /&gt;
How to write custom action code&lt;br /&gt;
&lt;br /&gt;
== Introduction ==&lt;br /&gt;
&lt;br /&gt;
Included in RT3 is the ability to create your own custom ticket actions via the Web UI. The RT3 Custom Scrip capability lets you access the RT API and is a very powerful tool for customising RT.&lt;br /&gt;
&lt;br /&gt;
== Basics ==&lt;br /&gt;
&lt;br /&gt;
[[File:Example1ScripUI.png|thumb|left|325px|Modify a scrip UI]]Let&#039;s start with Web UI. Goto Configuration -&amp;gt; Globals -&amp;gt; Scrips -&amp;gt; New Scrip.&lt;br /&gt;
&lt;br /&gt;
The Scrip UI will display the following selectors: Description, Condition, Action, Template and Stage. In the action selector you can pick actions based on files (modules). An outstanding action from this list is &amp;quot;User Defined&amp;quot;. The rest of this document describes this action. So in the Action field, select &amp;quot;User Defined&amp;quot; from the menu.&lt;br /&gt;
&lt;br /&gt;
Once you have selected &amp;quot;User Defined&amp;quot; from the Action Field, the following two Action code areas are enabled and used by RT:&lt;br /&gt;
* Custom action preparation code&lt;br /&gt;
* Custom action cleanup code&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
When a [[Transaction]] is created, RT does the following steps to enable all this magic and give you a chance to be a small God:&lt;br /&gt;
&lt;br /&gt;
* Select [[Scrip|Scrips]]&lt;br /&gt;
* Check applicability of scrips by executing [[Condition]]s&lt;br /&gt;
* Execute preparation code scrip by scrip&lt;br /&gt;
* Scrips that fail will be thrown away&lt;br /&gt;
* Execute commit code of survived scrips proceeding scrip by scrip&lt;br /&gt;
&lt;br /&gt;
When RT executes your perl Action code within this scrip, your code can become an actor upon the ticket. RT has already defined the variable $self. This variable represents an instance of class [=RT::Action::[[UserDefined]]], a subclass of &amp;lt;code&amp;gt;RT::Action::Generic&amp;lt;/code&amp;gt;. You can get more info from perldoc and from the source code.&lt;br /&gt;
&lt;br /&gt;
Instance &amp;lt;code&amp;gt;$self&amp;lt;/code&amp;gt; provides your Action code with very useful methods:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
$self-&amp;gt;TransactionObj&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Returns &amp;lt;code&amp;gt;RT::Transaction&amp;lt;/code&amp;gt; instance, the transaction that has been created and caused all this.&lt;br /&gt;
 &lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
$self-&amp;gt;TicketObj&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Returns &amp;lt;code&amp;gt;RT::Ticket&amp;lt;/code&amp;gt; instance which represents the ticket. Our transaction was applied to it.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
$self-&amp;gt;TemplateObj&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Returns the template (&amp;lt;code&amp;gt;RT::Template&amp;lt;/code&amp;gt;) which was selected for this scrip.&lt;br /&gt;
&lt;br /&gt;
=== Getting more info about these objects ===&lt;br /&gt;
&lt;br /&gt;
You can get complete information about these objects from their POD (embedded documentation).&lt;br /&gt;
&lt;br /&gt;
 perldoc /opt/rt3/lib/RT/Ticket.pm&lt;br /&gt;
 perldoc /opt/rt3/lib/RT/Ticket_Overlay.pm&lt;br /&gt;
 perldoc /opt/rt3/lib/RT/Tickets.pm&lt;br /&gt;
 perldoc /opt/rt3/lib/RT/Tickets_Overlay.pm&lt;br /&gt;
 perldoc /opt/rt3/lib/RT/Transaction.pm&lt;br /&gt;
 perldoc /opt/rt3/lib/RT/Transaction_Overlay.pm&lt;br /&gt;
 perldoc /opt/rt3/lib/RT/Transactions.pm&lt;br /&gt;
 perldoc /opt/rt3/lib/RT/Transactions_Overlay.pm&lt;br /&gt;
 ...&lt;br /&gt;
&lt;br /&gt;
== Simple example ==&lt;br /&gt;
&lt;br /&gt;
Ok, let&#039;s try to change something.&lt;br /&gt;
&lt;br /&gt;
Requirement: There is a support queue for special customers where each request must have high priority on ticket creation.&lt;br /&gt;
&lt;br /&gt;
[[File:Example1ScripBehavior.png|thumb|275px]]Preparation code:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
# we don&#039;t need any preparation yet.&lt;br /&gt;
return 1;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Commit code:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
$self-&amp;gt;TicketObj-&amp;gt;SetPriority( 100 );&lt;br /&gt;
return 1;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
I hope that this example is understandable enough, but it has at least one weakness. I&#039;ve hardcoded the priority value. Since RT lets the administrator define default final priority per queue, our code should reflect that.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
my $qfp = $self-&amp;gt;TicketObj-&amp;gt;QueueObj-&amp;gt;FinalPriority || 100;&lt;br /&gt;
$self-&amp;gt;TicketObj-&amp;gt;SetPriority( int( $qfp * 0.9 ) );&lt;br /&gt;
return 1;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This change to the logic first retrieves the [[FinalPriority]] of the current Queue or 100 if no [[FinalPriority]] is set for the Queue. Then it sets the Priority of this ticket to 90% of the retrieved Priority. The final 10% of the priority is reserved for very exclusive, super high priority requests.&lt;br /&gt;
&lt;br /&gt;
== Errors handling ==&lt;br /&gt;
&lt;br /&gt;
It&#039;s important to check errors to protect you from headache. Most methods in RT that change something return a tuple ($status, $msg). If $status is not true value then something failed and $msg is error text describing the reason. Let&#039;s extend our code:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
my $qfp = $self-&amp;gt;TicketObj-&amp;gt;QueueObj-&amp;gt;FinalPriority || 100;&lt;br /&gt;
my ($status, $msg) = $self-&amp;gt;TicketObj-&amp;gt;SetPriority( int( $qfp * 0.9 ) );&lt;br /&gt;
unless ( $status ) {&lt;br /&gt;
    $RT::Logger-&amp;gt;error(&amp;quot;Couldn&#039;t change priority: $msg&amp;quot;);&lt;br /&gt;
    return 0;&lt;br /&gt;
}&lt;br /&gt;
return 1;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== What you can (not) do with scrips ==&lt;br /&gt;
&lt;br /&gt;
You can manipulate almost any object in RT within a scrip.&lt;br /&gt;
&lt;br /&gt;
* update properties of tickets, for example set properties of tickets with commands in email&lt;br /&gt;
* change linked tickets, for example [[OpenTicketOnAllMemberResolve]] and [[OpenDependantsOnResolve]]&lt;br /&gt;
* extract info from messages, implement own workflow, create approvals and many-many more actions&lt;br /&gt;
&lt;br /&gt;
Let&#039;s talk about impossible things you don&#039;t even want to try to do with a scrip:&lt;br /&gt;
&lt;br /&gt;
* you couldn&#039;t deny action scrip&#039;s triggered, for example you don&#039;t want to allow users to open ticket unless it has owner, you may think that you can create scrip &amp;quot;on ticket open block action if ticket has no owner&amp;quot;, but it&#039;s impossible. I said impossible? no... sure you can create such scrip, but instead of preventing action you can revert it by setting status back to old value. yeah, this works but don&#039;t forget that it would be two transactions &#039;set open status&#039; and &#039;set previouse status&#039;. Each action could be applied to other scrips, so really it&#039;s not preventing action.&lt;br /&gt;
* you couldn&#039;t run scrip at some time, remember RT runs scrips after creating transactions, but of course we have solution for this situation - rt-crontool.&lt;br /&gt;
&lt;br /&gt;
== How to be silent ==&lt;br /&gt;
&lt;br /&gt;
Now you put SetXxxx calls all over the places in RT and suddenly note that strange transactions appear in tickets. They have creator [[RT System|RT_System]] and describe what you&#039;ve done with your scrips. Sometimes it&#039;s better to be silent and not mislead users. These transactions also go through the steps described earlier and could trigger some conditions too. Just use the long form of SetXxx functions:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
$TicketObj-&amp;gt;_Set(Field =&amp;gt; &#039;Priority&#039;, Value =&amp;gt; 90, RecordTransaction =&amp;gt; 0);&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The zero in the [[RecordTransaction]] argument informs RT not to record the change as a new transaction.&lt;br /&gt;
&lt;br /&gt;
== How to change ticket custom field values ==&lt;br /&gt;
&lt;br /&gt;
Step 1, get the custom field (CF) object or ID. Don&#039;t use the hardcoded CF ID from the database. [why?] Step 2, get the CF object by ticket object by ticket object (I hope you remember how to get a ticket object) and CF name.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
...&lt;br /&gt;
my $CFName = &#039;MyCustomField&#039;;&lt;br /&gt;
my $CF = RT::CustomField-&amp;gt;new( $RT::SystemUser );&lt;br /&gt;
$CF-&amp;gt;LoadByNameAndQueue( Name =&amp;gt; $CFName, Queue =&amp;gt; $Ticket-&amp;gt;Queue );&lt;br /&gt;
# RT has bug/feature until 3.0.10, you should load global CF yourself&lt;br /&gt;
unless( $CF-&amp;gt;id ) {&lt;br /&gt;
  # queue 0 is special case and is a synonym for global queue&lt;br /&gt;
  $CF-&amp;gt;LoadByNameAndQueue( Name =&amp;gt; $CFName, Queue =&amp;gt; &#039;0&#039; );&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
unless( $CF-&amp;gt;id ) {&lt;br /&gt;
  $RT::Logger-&amp;gt;error( &amp;quot;No field $CFName in queue &amp;quot;. $Ticket-&amp;gt;QueueObj-&amp;gt;Name );&lt;br /&gt;
  return undef;&lt;br /&gt;
}&lt;br /&gt;
...&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Now we could add value to ticket:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
my $Value = &#039;MyValue&#039;;&lt;br /&gt;
$Ticket-&amp;gt;AddCustomFieldValue( Field =&amp;gt; $CF, Value =&amp;gt; $Value );&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
or&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
$Ticket-&amp;gt;AddCustomFieldValue( Field =&amp;gt; $CF, Value =&amp;gt; $Value, RecordTransaction =&amp;gt; 0 );&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
you also could use custom field id instead of object.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
$Ticket-&amp;gt;AddCustomFieldValue( Field =&amp;gt; NN , Value =&amp;gt; $Value );&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Step by step or &amp;quot;Tickets, transactions and attachments&amp;quot; ==&lt;br /&gt;
&lt;br /&gt;
Lets look what happens with RT from the beginning when a user clicks on create button in their browser. Web server gets request with queue id, ticket subject and body, owner and etc. RT fetches info from the request that is needed for Ticket record: its Queue id, Status, Subject, [[CurrentUser]](Creator), and then runs the next code:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
# init empty instance of RT::Ticket class&lt;br /&gt;
my $TicketObj = RT::Ticket-&amp;gt;new( $session{&#039;CurrentUser&#039;} );&lt;br /&gt;
&lt;br /&gt;
# create new record&lt;br /&gt;
# if you more familiar with SQL then it&#039;s INSERT&lt;br /&gt;
# this call is inherited from SearchBuilder API&lt;br /&gt;
my $id = $TicketObj-&amp;gt;Create( %ARGS );&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Ticket is created and it&#039;s time to record transaction into table. RT has all info for this: ticket&#039;s id that was recently created and transaction type - &#039;Create&#039;.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
# this call creates new transaction record&lt;br /&gt;
# this is very similar to situation with new ticket record&lt;br /&gt;
my $TransactionObj = $Ticket-&amp;gt;_RecordTransaction( %ARGS );&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Transactions in RT has an many-to-one mapping with ticket. One ticket =&amp;gt; one or more transactions. You can get collection of transactions by calling Transactions method on a ticket object:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
my $transactions = $TicketObj-&amp;gt;Transactions;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
Each transaction belongs to only one ticket and you get ticket object with the following code:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
my $ticket = $TransactionObj-&amp;gt;TicketObj;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
As you can see RT still didn&#039;t use content that you wrote in the body. Content is an RT::Attachment object. You know how RT creates new record and know about Ticket-Transaction relation, the same relationship applies to Attachments and Transactions.&lt;br /&gt;
&lt;br /&gt;
== From &amp;quot;User Defined&amp;quot; to a module ==&lt;br /&gt;
&lt;br /&gt;
User defined actions you write in the web UI are handy and quick way to write an action. However, at some point you want to re-use your action, make it configurable, edit it in more suitable editor rather than text area, make it more complex and consist of additional methods. So it&#039;s time to move from &amp;quot;User Defined&amp;quot; action to your first module file.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
use strict;&lt;br /&gt;
use warnings;&lt;br /&gt;
&lt;br /&gt;
package RT::Action::MyAction;&lt;br /&gt;
use base qw(RT::Action::Generic);&lt;br /&gt;
&lt;br /&gt;
sub Prepare {&lt;br /&gt;
  my $self = shift;&lt;br /&gt;
&lt;br /&gt;
  ... here goes preparation code ...&lt;br /&gt;
&lt;br /&gt;
  return 1;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
sub Commit {&lt;br /&gt;
  my $self = shift;&lt;br /&gt;
&lt;br /&gt;
  ... here goes commit code ...&lt;br /&gt;
&lt;br /&gt;
  return 1;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
1;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;Note&#039;&#039;: for RT4, replace &#039;&#039;&#039;RT::Action::Generic&#039;&#039;&#039; by &#039;&#039;&#039;RT::Action&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
That&#039;s it. Save it as &#039;&#039;&#039;lib/RT/Action/MyAction.pm&#039;&#039;&#039; (it is important that the file have the same name as the package (&amp;quot;MyAction&amp;quot; in the example)), fill in preparation and commit code then you can register your action in the DB. Use the following data file and [[AddDatabaseRecords]] instructions.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
@ScripActions = (&lt;br /&gt;
  {&lt;br /&gt;
    Name        =&amp;gt; &#039;My super duper action&#039;,&lt;br /&gt;
    Description =&amp;gt; &#039;Super-puper action that does hell of a job&#039; ,&lt;br /&gt;
    ExecModule =&amp;gt; &#039;MyAction&#039;,&lt;br /&gt;
    Argument   =&amp;gt; &#039;some argument if needed&#039;&lt;br /&gt;
  },&lt;br /&gt;
);&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Now your action in the DB and you can pick it for a scrip through the Web UI instead of picking &amp;quot;User Defined&amp;quot;.&lt;br /&gt;
&lt;br /&gt;
== See also ==&lt;br /&gt;
&lt;br /&gt;
Other documentation on this wiki that may help&lt;br /&gt;
&lt;br /&gt;
[[GlobalObjects]], [[ObjectModel]]&lt;br /&gt;
&lt;br /&gt;
== Special thanks ==&lt;br /&gt;
&lt;br /&gt;
* [[TimWilson]], who was the main editor and reviewer&lt;/div&gt;</summary>
		<author><name>Kincl</name></author>
	</entry>
	<entry>
		<id>https://rt-wiki.bestpractical.com/index.php?title=WorkFlow&amp;diff=26364</id>
		<title>WorkFlow</title>
		<link rel="alternate" type="text/html" href="https://rt-wiki.bestpractical.com/index.php?title=WorkFlow&amp;diff=26364"/>
		<updated>2016-08-15T17:32:40Z</updated>

		<summary type="html">&lt;p&gt;Kincl: Fixing subtask documentation, added single quotes because wiki formatting was getting messed up&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;= Modeling Workflow in RT =&lt;br /&gt;
&lt;br /&gt;
We had the requirement to make sure that when equipment was ordered for installation all necessary tasks were completed by the involved parties. Creating a single ticket and assigning it to each responsible party in turn had the problem of serializing the tasks, not giving advance warning, and not having a predetermined checklist of things to do. This is a fairly simple attempt to model a planned workflow in RT, using a master ticket for the installation, and child tickets for the subtask.&lt;br /&gt;
&lt;br /&gt;
== Queue Setup ==&lt;br /&gt;
&lt;br /&gt;
First, set up a new queue, which we&#039;ll call Installation. Nothing special here. Make sure that the correct people have permission to update the queue including the permission to set custom fields.&lt;br /&gt;
&lt;br /&gt;
== Custom Field Setup ==&lt;br /&gt;
&lt;br /&gt;
Next, create a custom field for each subtask. The name of the field should suggest the subtask to the reader, because the field description doesn&#039;t show up anyplace useful. We created custom fields &#039;Order Status&#039;, &#039;Power&#039;, &#039;DNS&#039;, &#039;Rack&#039;, &#039;Array&#039;, and &#039;[[OSInstall]]&#039;. Make each of type &#039;Select one value&#039;, which will make them into drop-down lists. For &#039;Order Status&#039;, we&#039;re using values such as &#039;Requirements&#039;, &#039;In Purchasing&#039;, &#039;Vendor&#039;, and &#039;Delivered&#039; to indicate the status of the order. For the other fields we use the simple values &#039;Required&#039; and &#039;Completed&#039;. (We are still exploring whether the complexity of additional status values for these are useful.) The &#039;Required&#039; status will become magic in the next steps. Go back to the Installation queue and enable these custom fields.&lt;br /&gt;
&lt;br /&gt;
== Template Setup ==&lt;br /&gt;
&lt;br /&gt;
Now we need some templates for the child tickets. These determine what will be in the child tickets when they are first created. Create them in the Installation queue; they don&#039;t need to be global. Name them something related to the subtask name for your sanity, but the name itself doesn&#039;t link them. Here is the [[PowerTicket]] template:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
===Create-Ticket: power&lt;br /&gt;
Subject: Power::{$Tickets{&#039;TOP&#039;}-&amp;gt;Subject}&lt;br /&gt;
Parents: TOP&lt;br /&gt;
Queue: Installation&lt;br /&gt;
Owner: smith&lt;br /&gt;
AdminCc: jones&lt;br /&gt;
Content: Power requirements are needed for this equipment.&lt;br /&gt;
Please see the parent ticket for details.&lt;br /&gt;
ENDOFCONTENT&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The Owner of the ticket should be someone already defined in your RT database who will be responsible for the subtask, in this example smith. The [[AdminCc]] can be used for that person&#039;s backup or other interested parties. As of RT 3.4.2, both of these have to be users, not groups. It would be very nice if groups worked, to allow more dynamic updating of responsibilities if someone is away. Notification of the new owners is taken care of by the normal Create Ticket actions in the queue.&lt;br /&gt;
&lt;br /&gt;
The Queue is the queue in which the new ticket is created. At the moment, we&#039;ve elected to create them in the same queue as the parent ticket, but it may make more sense to create them in other queues. The Parent link will keep everything tied together if you do.&lt;br /&gt;
&lt;br /&gt;
== Action Setup ==&lt;br /&gt;
&lt;br /&gt;
Now we can glue the templates to the custom fields. To make this clean, I created a custom Condition, installed in $RTHOME/local/lib/RT/Condition/[[FieldRequired]].pm:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
# Local condition to check that a custom field&lt;br /&gt;
# has been set to &amp;quot;Required&amp;quot;.&lt;br /&gt;
# -Chuck Boeheim 3/13/06&lt;br /&gt;
&lt;br /&gt;
package RT::Condition::FieldRequired;&lt;br /&gt;
require RT::Condition::Generic;&lt;br /&gt;
&lt;br /&gt;
use strict;&lt;br /&gt;
use vars qw/@ISA/;&lt;br /&gt;
@ISA = qw(RT::Condition::Generic);&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=head2 IsApplicable&lt;br /&gt;
&lt;br /&gt;
If the field named as an argument becomes &#039;Required&#039;.&lt;br /&gt;
Only triggers on transitions, not if it already had&lt;br /&gt;
that value.&lt;br /&gt;
&lt;br /&gt;
=cut&lt;br /&gt;
&lt;br /&gt;
sub IsApplicable {&lt;br /&gt;
    my $self = shift;&lt;br /&gt;
    my $field = $self-&amp;gt;Argument;&lt;br /&gt;
    my $trans = $self-&amp;gt;TransactionObj;&lt;br /&gt;
    if ($trans-&amp;gt;Type eq &#039;Create&#039;) {&lt;br /&gt;
       return 1 if $trans-&amp;gt;TicketObj-&amp;gt;FirstCustomFieldValue($field) =~ /^Required/;&lt;br /&gt;
    }&lt;br /&gt;
    if ($trans-&amp;gt;Type eq &#039;CustomField&#039;) {&lt;br /&gt;
       my $cf = RT::CustomField-&amp;gt;new($self-&amp;gt;CurrentUser);&lt;br /&gt;
       $cf-&amp;gt;Load($field);&lt;br /&gt;
       return 1 if $trans-&amp;gt;Field == $cf-&amp;gt;Id &amp;amp;amp;&amp;amp;amp; $trans-&amp;gt;NewValue =~ /^Required/;&lt;br /&gt;
    }&lt;br /&gt;
    return undef;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
1;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The new action can be installed with the following script, which needs to have your custom field names put in the appropriate place:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
# To install, install FieldRequired.pm in local/lib/RT/Conditions, and&lt;br /&gt;
# this script in local/etc/FieldRequired.install&lt;br /&gt;
#     /path/to/rt3/sbin/rt-setup-database --action insert \&lt;br /&gt;
#         --datafile /path/to/rt3/local/etc/FieldRequired.install&lt;br /&gt;
#&lt;br /&gt;
@ScripConditions = (&lt;br /&gt;
    { Name        =&amp;gt; &#039;On Power Required&#039;,&lt;br /&gt;
      Description =&amp;gt; &#039;Trigger when custom field Power becomes Required&#039;,&lt;br /&gt;
      ExecModule  =&amp;gt; &#039;FieldRequired&#039;,&lt;br /&gt;
      Argument    =&amp;gt; &#039;Power&#039;,                # Your custom field name&lt;br /&gt;
      ApplicableTransTypes =&amp;gt; &#039;Any&#039; },&lt;br /&gt;
    { Name        =&amp;gt; &#039;On DNS Required&#039;,&lt;br /&gt;
      Description =&amp;gt; &#039;Trigger when custom field DNS becomes Required&#039;,&lt;br /&gt;
      ExecModule  =&amp;gt; &#039;FieldRequired&#039;,&lt;br /&gt;
      Argument    =&amp;gt; &#039;DNS&#039;,                   # Next custom field name&lt;br /&gt;
      ApplicableTransTypes =&amp;gt; &#039;Any&#039; },&lt;br /&gt;
# Repeat for additional fields&lt;br /&gt;
);&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Then add scrips to the Installation queue for each subtask that use your new conditions:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
Description: Create Power Ticket&lt;br /&gt;
Condition: On Power Required        &amp;lt;== The ScripCondition Name in the previous step&lt;br /&gt;
Action: Create Tickets&lt;br /&gt;
Template: PowerTicket                   &amp;lt;== The template name in the template step above&lt;br /&gt;
Stage: TransactionCreate&lt;br /&gt;
&amp;lt;/pre&amp;gt; &lt;br /&gt;
&lt;br /&gt;
At this point, if you create a ticket in the Installation queue and set a custom field to &#039;Required&#039; it will create the child ticket. It will also do this if you update an existing ticket to set a &#039;Required&#039; status. This allows you to create a ticket for planning, and only initiate the subtasks at the time they are necessary.&lt;br /&gt;
&lt;br /&gt;
== Completing Subtasks ==&lt;br /&gt;
&lt;br /&gt;
Now one final piece of glue to update the parent status when the child completes. Create another scrip in the queue(s) that have the child tickets. (This is one reason to keep the child tickets in the installation queue.)&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
Description: On Resolve, Update Parent&lt;br /&gt;
Condition: On Resolve&lt;br /&gt;
Action: User Defined&lt;br /&gt;
Template: Global Template Blank&lt;br /&gt;
Stage: TransactionCreate&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
CustomCondition:&lt;br /&gt;
None&lt;br /&gt;
&lt;br /&gt;
CustomActionPreparationCode:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
return 1;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
CustomActionCleanupCode:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
return 1 if ($self-&amp;gt;TransactionObj-&amp;gt;NewValue !~ /^(?:resolved|deleted|rejected)$/);&lt;br /&gt;
# Figure out which kind of child this is&lt;br /&gt;
my $cf_value = &#039;Completed&#039;;&lt;br /&gt;
my $cf_name;&lt;br /&gt;
&lt;br /&gt;
my $subject = $self-&amp;gt;TicketObj-&amp;gt;Subject;&lt;br /&gt;
$cf_name = &#039;Power&#039;      if $subject =~ /^Power::/;&lt;br /&gt;
$cf_name = &#039;Network&#039;    if $subject =~ /^Network::/;&lt;br /&gt;
# Repeat for your custom field names.&lt;br /&gt;
# There may be a better way to do this.&lt;br /&gt;
&lt;br /&gt;
# Alternative:&lt;br /&gt;
# Setup the templates to use the same name as the custom fields, then you can&lt;br /&gt;
# extract the name of each field from the subject, using Perl regex grouping.&lt;br /&gt;
# my $subject = $self-&amp;gt;TicketObj-&amp;gt;Subject;&lt;br /&gt;
# if($subject =~ /^(.*)::/)&lt;br /&gt;
# {$cf_name = $1;}&lt;br /&gt;
&lt;br /&gt;
return undef unless $cf_name;&lt;br /&gt;
&lt;br /&gt;
my $actor = $self-&amp;gt;TransactionObj-&amp;gt;CreatorObj;&lt;br /&gt;
my $actorname = $actor-&amp;gt;RealName . &#039; (&#039; . $actor-&amp;gt;EmailAddress . &#039;)&#039;;&lt;br /&gt;
my $CF_Obj = RT::CustomField-&amp;gt;new($self-&amp;gt;CurrentUser);&lt;br /&gt;
# current ticket is member of(child of some parents)&lt;br /&gt;
my $MemberOf = $self-&amp;gt;TicketObj-&amp;gt;MemberOf;&lt;br /&gt;
while (my $l = $MemberOf-&amp;gt;Next ) {&lt;br /&gt;
  # we can&#039;t check non local objects&lt;br /&gt;
  next unless( $l-&amp;gt;TargetURI-&amp;gt;IsLocal );&lt;br /&gt;
&lt;br /&gt;
  # Update the custom field in the parent ticket to show completed.&lt;br /&gt;
  $CF_Obj-&amp;gt;LoadByName( Name  =&amp;gt; $cf_name,&lt;br /&gt;
                   Queue =&amp;gt; $l-&amp;gt;TargetObj-&amp;gt;QueueObj-&amp;gt;Id);&lt;br /&gt;
  $CF_Obj-&amp;gt;AddValueForObject( Object  =&amp;gt; $l-&amp;gt;TargetObj,&lt;br /&gt;
                          Content =&amp;gt; $cf_value );&lt;br /&gt;
  my $id = $self-&amp;gt;TicketObj-&amp;gt;id;&lt;br /&gt;
  my $status = $self-&amp;gt;TicketObj-&amp;gt;Status;&lt;br /&gt;
  $l-&amp;gt;TargetObj-&amp;gt;Correspond(Content =&amp;gt; &amp;lt;&amp;lt;&#039;END&#039;);&lt;br /&gt;
Child ticket completed:&lt;br /&gt;
Ticket:  $id&lt;br /&gt;
Status:  $status&lt;br /&gt;
Subject: $subject&lt;br /&gt;
By:      $actorname&lt;br /&gt;
END&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
return 1;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;/div&gt;</summary>
		<author><name>Kincl</name></author>
	</entry>
	<entry>
		<id>https://rt-wiki.bestpractical.com/index.php?title=WorkFlow&amp;diff=26363</id>
		<title>WorkFlow</title>
		<link rel="alternate" type="text/html" href="https://rt-wiki.bestpractical.com/index.php?title=WorkFlow&amp;diff=26363"/>
		<updated>2016-08-15T17:23:31Z</updated>

		<summary type="html">&lt;p&gt;Kincl: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;= Modeling Workflow in RT =&lt;br /&gt;
&lt;br /&gt;
We had the requirement to make sure that when equipment was ordered for installation all necessary tasks were completed by the involved parties. Creating a single ticket and assigning it to each responsible party in turn had the problem of serializing the tasks, not giving advance warning, and not having a predetermined checklist of things to do. This is a fairly simple attempt to model a planned workflow in RT, using a master ticket for the installation, and child tickets for the subtask.&lt;br /&gt;
&lt;br /&gt;
== Queue Setup ==&lt;br /&gt;
&lt;br /&gt;
First, set up a new queue, which we&#039;ll call Installation. Nothing special here. Make sure that the correct people have permission to update the queue including the permission to set custom fields.&lt;br /&gt;
&lt;br /&gt;
== Custom Field Setup ==&lt;br /&gt;
&lt;br /&gt;
Next, create a custom field for each subtask. The name of the field should suggest the subtask to the reader, because the field description doesn&#039;t show up anyplace useful. We created custom fields &#039;Order Status&#039;, &#039;Power&#039;, &#039;DNS&#039;, &#039;Rack&#039;, &#039;Array&#039;, and &#039;[[OSInstall]]&#039;. Make each of type &#039;Select one value&#039;, which will make them into drop-down lists. For &#039;Order Status&#039;, we&#039;re using values such as &#039;Requirements&#039;, &#039;In Purchasing&#039;, &#039;Vendor&#039;, and &#039;Delivered&#039; to indicate the status of the order. For the other fields we use the simple values &#039;Required&#039; and &#039;Completed&#039;. (We are still exploring whether the complexity of additional status values for these are useful.) The &#039;Required&#039; status will become magic in the next steps. Go back to the Installation queue and enable these custom fields.&lt;br /&gt;
&lt;br /&gt;
== Template Setup ==&lt;br /&gt;
&lt;br /&gt;
Now we need some templates for the child tickets. These determine what will be in the child tickets when they are first created. Create them in the Installation queue; they don&#039;t need to be global. Name them something related to the subtask name for your sanity, but the name itself doesn&#039;t link them. Here is the [[PowerTicket]] template:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
===Create-Ticket: power&lt;br /&gt;
Subject: Power::{$Tickets{&#039;TOP&#039;}-&amp;gt;Subject}&lt;br /&gt;
Parents: TOP&lt;br /&gt;
Queue: Installation&lt;br /&gt;
Owner: smith&lt;br /&gt;
AdminCc: jones&lt;br /&gt;
Content: Power requirements are needed for this equipment.&lt;br /&gt;
Please see the parent ticket for details.&lt;br /&gt;
ENDOFCONTENT&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The Owner of the ticket should be someone already defined in your RT database who will be responsible for the subtask, in this example smith. The [[AdminCc]] can be used for that person&#039;s backup or other interested parties. As of RT 3.4.2, both of these have to be users, not groups. It would be very nice if groups worked, to allow more dynamic updating of responsibilities if someone is away. Notification of the new owners is taken care of by the normal Create Ticket actions in the queue.&lt;br /&gt;
&lt;br /&gt;
The Queue is the queue in which the new ticket is created. At the moment, we&#039;ve elected to create them in the same queue as the parent ticket, but it may make more sense to create them in other queues. The Parent link will keep everything tied together if you do.&lt;br /&gt;
&lt;br /&gt;
== Action Setup ==&lt;br /&gt;
&lt;br /&gt;
Now we can glue the templates to the custom fields. To make this clean, I created a custom Condition, installed in $RTHOME/local/lib/RT/Condition/[[FieldRequired]].pm:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
# Local condition to check that a custom field&lt;br /&gt;
# has been set to &amp;quot;Required&amp;quot;.&lt;br /&gt;
# -Chuck Boeheim 3/13/06&lt;br /&gt;
&lt;br /&gt;
package RT::Condition::FieldRequired;&lt;br /&gt;
require RT::Condition::Generic;&lt;br /&gt;
&lt;br /&gt;
use strict;&lt;br /&gt;
use vars qw/@ISA/;&lt;br /&gt;
@ISA = qw(RT::Condition::Generic);&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=head2 IsApplicable&lt;br /&gt;
&lt;br /&gt;
If the field named as an argument becomes &#039;Required&#039;.&lt;br /&gt;
Only triggers on transitions, not if it already had&lt;br /&gt;
that value.&lt;br /&gt;
&lt;br /&gt;
=cut&lt;br /&gt;
&lt;br /&gt;
sub IsApplicable {&lt;br /&gt;
    my $self = shift;&lt;br /&gt;
    my $field = $self-&amp;gt;Argument;&lt;br /&gt;
    my $trans = $self-&amp;gt;TransactionObj;&lt;br /&gt;
    if ($trans-&amp;gt;Type eq &#039;Create&#039;) {&lt;br /&gt;
       return 1 if $trans-&amp;gt;TicketObj-&amp;gt;FirstCustomFieldValue($field) =~ /^Required/;&lt;br /&gt;
    }&lt;br /&gt;
    if ($trans-&amp;gt;Type eq &#039;CustomField&#039;) {&lt;br /&gt;
       my $cf = RT::CustomField-&amp;gt;new($self-&amp;gt;CurrentUser);&lt;br /&gt;
       $cf-&amp;gt;Load($field);&lt;br /&gt;
       return 1 if $trans-&amp;gt;Field == $cf-&amp;gt;Id &amp;amp;amp;&amp;amp;amp; $trans-&amp;gt;NewValue =~ /^Required/;&lt;br /&gt;
    }&lt;br /&gt;
    return undef;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
1;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The new action can be installed with the following script, which needs to have your custom field names put in the appropriate place:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
# To install, install FieldRequired.pm in local/lib/RT/Conditions, and&lt;br /&gt;
# this script in local/etc/FieldRequired.install&lt;br /&gt;
#     /path/to/rt3/sbin/rt-setup-database --action insert \&lt;br /&gt;
#         --datafile /path/to/rt3/local/etc/FieldRequired.install&lt;br /&gt;
#&lt;br /&gt;
@ScripConditions = (&lt;br /&gt;
    { Name        =&amp;gt; &#039;On Power Required&#039;,&lt;br /&gt;
      Description =&amp;gt; &#039;Trigger when custom field Power becomes Required&#039;,&lt;br /&gt;
      ExecModule  =&amp;gt; &#039;FieldRequired&#039;,&lt;br /&gt;
      Argument    =&amp;gt; &#039;Power&#039;,                # Your custom field name&lt;br /&gt;
      ApplicableTransTypes =&amp;gt; &#039;Any&#039; },&lt;br /&gt;
    { Name        =&amp;gt; &#039;On DNS Required&#039;,&lt;br /&gt;
      Description =&amp;gt; &#039;Trigger when custom field DNS becomes Required&#039;,&lt;br /&gt;
      ExecModule  =&amp;gt; &#039;FieldRequired&#039;,&lt;br /&gt;
      Argument    =&amp;gt; &#039;DNS&#039;,                   # Next custom field name&lt;br /&gt;
      ApplicableTransTypes =&amp;gt; &#039;Any&#039; },&lt;br /&gt;
# Repeat for additional fields&lt;br /&gt;
);&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Then add scrips to the Installation queue for each subtask that use your new conditions:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
Description: Create Power Ticket&lt;br /&gt;
Condition: On Power Required        &amp;lt;== The ScripCondition Name in the previous step&lt;br /&gt;
Action: Create Tickets&lt;br /&gt;
Template: PowerTicket                   &amp;lt;== The template name in the template step above&lt;br /&gt;
Stage: TransactionCreate&lt;br /&gt;
&amp;lt;/pre&amp;gt; &lt;br /&gt;
&lt;br /&gt;
At this point, if you create a ticket in the Installation queue and set a custom field to &#039;Required&#039; it will create the child ticket. It will also do this if you update an existing ticket to set a &#039;Required&#039; status. This allows you to create a ticket for planning, and only initiate the subtasks at the time they are necessary.&lt;br /&gt;
&lt;br /&gt;
== Completing Subtasks ==&lt;br /&gt;
&lt;br /&gt;
Now one final piece of glue to update the parent status when the child completes. Create another scrip in the queue(s) that have the child tickets. (This is one reason to keep the child tickets in the installation queue.)&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
Description: On Resolve, Update Parent&lt;br /&gt;
Condition: On Resolve&lt;br /&gt;
Action: User Defined&lt;br /&gt;
Template: Global Template Blank&lt;br /&gt;
Stage: TransactionCreate&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
CustomCondition:&lt;br /&gt;
None&lt;br /&gt;
&lt;br /&gt;
CustomActionPreparationCode:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
return 1;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
CustomActionCleanupCode:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
return 1 if ($self-&amp;gt;TransactionObj-&amp;gt;NewValue !~ /^(?:resolved|deleted|rejected)$/);&lt;br /&gt;
# Figure out which kind of child this is&lt;br /&gt;
my $cf_value = &#039;Completed&#039;;&lt;br /&gt;
my $cf_name;&lt;br /&gt;
&lt;br /&gt;
my $subject = $self-&amp;gt;TicketObj-&amp;gt;Subject;&lt;br /&gt;
$cf_name = &#039;Power&#039;      if $subject =~ /^Power::/;&lt;br /&gt;
$cf_name = &#039;Network&#039;    if $subject =~ /^Network::/;&lt;br /&gt;
# Repeat for your custom field names.&lt;br /&gt;
# There may be a better way to do this.&lt;br /&gt;
&lt;br /&gt;
# Alternative:&lt;br /&gt;
# Setup the templates to use the same name as the custom fields, then you can&lt;br /&gt;
# extract the name of each field from the subject, using Perl regex grouping.&lt;br /&gt;
# my $subject = $self-&amp;gt;TicketObj-&amp;gt;Subject;&lt;br /&gt;
# if($subject =~ /^(.*)::/)&lt;br /&gt;
# {$cf_name = $1;}&lt;br /&gt;
&lt;br /&gt;
return undef unless $cf_name;&lt;br /&gt;
&lt;br /&gt;
my $actor = $self-&amp;gt;TransactionObj-&amp;gt;CreatorObj;&lt;br /&gt;
my $actorname = $actor-&amp;gt;RealName . &#039; (&#039; . $actor-&amp;gt;EmailAddress . &#039;)&#039;;&lt;br /&gt;
my $CF_Obj = RT::CustomField-&amp;gt;new($self-&amp;gt;CurrentUser);&lt;br /&gt;
# current ticket is member of(child of some parents)&lt;br /&gt;
my $MemberOf = $self-&amp;gt;TicketObj-&amp;gt;MemberOf;&lt;br /&gt;
while (my $l = $MemberOf-&amp;gt;Next ) {&lt;br /&gt;
  # we can&#039;t check non local objects&lt;br /&gt;
  next unless( $l-&amp;gt;TargetURI-&amp;gt;IsLocal );&lt;br /&gt;
  &lt;br /&gt;
  # Update the custom field in the parent ticket to show completed.&lt;br /&gt;
  $CF_Obj-&amp;gt;LoadByName( Name  =&amp;gt; $cf_name,&lt;br /&gt;
                   Queue =&amp;gt; $l-&amp;gt;TargetObj-&amp;gt;QueueObj-&amp;gt;Id);&lt;br /&gt;
  $CF_Obj-&amp;gt;AddValueForObject( Object  =&amp;gt; $l-&amp;gt;TargetObj,&lt;br /&gt;
                          Content =&amp;gt; $cf_value );&lt;br /&gt;
  my $id = $self-&amp;gt;TicketObj-&amp;gt;id;&lt;br /&gt;
  my $status = $self-&amp;gt;TicketObj-&amp;gt;Status;&lt;br /&gt;
  $l-&amp;gt;TargetObj-&amp;gt;Correspond(Content =&amp;gt; &amp;lt;&amp;lt;END);&lt;br /&gt;
Child ticket completed:&lt;br /&gt;
Ticket:  $id&lt;br /&gt;
Status:  $status&lt;br /&gt;
Subject: $subject&lt;br /&gt;
By:      $actorname&lt;br /&gt;
END&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
return 1;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;/div&gt;</summary>
		<author><name>Kincl</name></author>
	</entry>
</feed>