rt 4.2.16
[freeside.git] / rt / lib / RT / Action / EscalatePriority.pm
1 # BEGIN BPS TAGGED BLOCK {{{
2 #
3 # COPYRIGHT:
4 #
5 # This software is Copyright (c) 1996-2019 Best Practical Solutions, LLC
6 #                                          <sales@bestpractical.com>
7 #
8 # (Except where explicitly superseded by other copyright notices)
9 #
10 #
11 # LICENSE:
12 #
13 # This work is made available to you under the terms of Version 2 of
14 # the GNU General Public License. A copy of that license should have
15 # been provided with this software, but in any event can be snarfed
16 # from www.gnu.org.
17 #
18 # This work is distributed in the hope that it will be useful, but
19 # WITHOUT ANY WARRANTY; without even the implied warranty of
20 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
21 # General Public License for more details.
22 #
23 # You should have received a copy of the GNU General Public License
24 # along with this program; if not, write to the Free Software
25 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
26 # 02110-1301 or visit their web page on the internet at
27 # http://www.gnu.org/licenses/old-licenses/gpl-2.0.html.
28 #
29 #
30 # CONTRIBUTION SUBMISSION POLICY:
31 #
32 # (The following paragraph is not intended to limit the rights granted
33 # to you to modify and distribute this software under the terms of
34 # the GNU General Public License and is only of importance to you if
35 # you choose to contribute your changes and enhancements to the
36 # community by submitting them to Best Practical Solutions, LLC.)
37 #
38 # By intentionally submitting any modifications, corrections or
39 # derivatives to this work, or any other work intended for use with
40 # Request Tracker, to Best Practical Solutions, LLC, you confirm that
41 # you are the copyright holder for those contributions and you grant
42 # Best Practical Solutions,  LLC a nonexclusive, worldwide, irrevocable,
43 # royalty-free, perpetual, license to use, copy, create derivative
44 # works based on those contributions, and sublicense and distribute
45 # those contributions and any derivatives thereof.
46 #
47 # END BPS TAGGED BLOCK }}}
48
49 =head1 NAME
50
51   RT::Action::EscalatePriority
52
53 =head1 DESCRIPTION
54
55 EscalatePriority is a ScripAction which is NOT intended to be called
56 per transaction. It's intended to be called by an RT escalation tool.
57 One such tool is called rt-crontool and is located in $RTHOME/bin (see
58 C<rt-crontool -h> for more details)
59
60 EsclatePriority uses the following formula to change a ticket's priority:
61
62     Priority = Priority +  (( FinalPriority - Priority ) / ( DueDate-Today))
63
64 Unless the duedate is past, in which case priority gets bumped straight
65 to final priority.
66
67 In this way, priority is either increased or decreased toward the final priority
68 as the ticket heads toward its due date.
69
70 Alternately, if you don't set a due date, the Priority will be incremented by 1
71 until it reaches the Final Priority.  If a ticket without a due date has a Priority
72 greater than Final Priority, it will be decremented by 1.
73
74 =head2 CONFIGURATION
75
76 EsclatePriority's behavior can be controlled by two options:
77
78 =over 4
79
80 =item RecordTransaction
81
82 If true (the default), the action casuses a transaction on the ticket
83 when it is escalated.  If false, the action updates the priority without
84 running scrips or recording a transaction.
85
86 =item UpdateLastUpdated
87
88 If true (the default), the action updates the LastUpdated field when the
89 ticket is escalated.  You cannot set C<UpdateLastUpdated> to false unless
90 C<RecordTransaction> is also false.
91
92 =back
93
94 To use these with C<rt-crontool>, specify them with C<--action-arg>:
95
96     --action-arg "RecordTransaction: 0, UpdateLastUpdated: 0"
97
98 =cut
99
100
101 package RT::Action::EscalatePriority;
102 use base 'RT::Action';
103
104 use strict;
105 use warnings;
106
107 #Do what we need to do and send it out.
108
109 #What does this type of Action does
110
111 sub Describe  {
112   my $self = shift;
113   return (ref $self . " will move a ticket's priority toward its final priority.");
114 }
115
116
117 sub Prepare  {
118     my $self = shift;
119
120     if ($self->TicketObj->Priority() == $self->TicketObj->FinalPriority()) {
121         # no update necessary.
122         return 0;
123     }
124
125     #compute the number of days until the ticket is due
126     my $due = $self->TicketObj->DueObj();
127
128
129     # If we don't have a due date, adjust the priority by one
130     # until we hit the final priority
131     if (not $due->IsSet) {
132         if ( $self->TicketObj->Priority > $self->TicketObj->FinalPriority ){
133             $self->{'prio'} = ($self->TicketObj->Priority - 1);
134             return 1;
135         }
136         elsif ( $self->TicketObj->Priority < $self->TicketObj->FinalPriority ){
137             $self->{'prio'} = ($self->TicketObj->Priority + 1);
138             return 1;
139         }
140         # otherwise the priority is at the final priority. we don't need to
141         # Continue
142         else {
143             return 0;
144         }
145     }
146
147     # we've got a due date. now there are other things we should do
148     else {
149         my $arg = $self->Argument || '';
150         my $now = time();
151         if ( $arg =~ /CurrentTime:\s*(\d+)/i ) {
152             $now = $1;
153         } 
154         my $diff_in_seconds = $due->Diff($now);  
155         my $diff_in_days = int( $diff_in_seconds / 86400);
156
157         #if we haven't hit the due date yet
158         if ($diff_in_days > 0 ) {
159
160             # compute the difference between the current priority and the
161             # final priority
162
163             my $prio_delta =
164               $self->TicketObj->FinalPriority() - $self->TicketObj->Priority;
165
166             my $inc_priority_by = int( $prio_delta / $diff_in_days );
167
168             #set the ticket's priority to that amount
169             $self->{'prio'} = $self->TicketObj->Priority + $inc_priority_by;
170
171         }
172         #if $days is less than 1, set priority to final_priority
173         else {
174             $self->{'prio'} = $self->TicketObj->FinalPriority();
175         }
176
177     }
178     return 1;
179 }
180
181 sub Commit {
182     my $self = shift;
183     my $new_value = $self->{'prio'};
184     return 1 unless defined $new_value;
185
186     my $ticket = $self->TicketObj;
187     return 1 if $ticket->Priority == $new_value;
188
189     # Overide defaults from argument
190     my($record, $update) = (1, 1);
191     {
192         my $arg = $self->Argument || '';
193         if ( $arg =~ /RecordTransaction:\s*(\d+)/i ) {
194             $record = $1;
195             $RT::Logger->debug("Overrode RecordTransaction: $record");
196         }
197         if ( $arg =~ /UpdateLastUpdated:\s*(\d+)/i ) {
198             $update = $1;
199             $RT::Logger->debug("Overrode UpdateLastUpdated: $update");
200         }
201         # If creating a transaction, we have to update lastupdated
202         $update = 1 if $record;
203     }
204
205     $RT::Logger->debug(
206        'Escalating priority of ticket #'. $ticket->Id
207        .' from '. $ticket->Priority .' to '. $new_value
208        .' and'. ($record? '': ' do not') .' record a transaction'
209        .' and'. ($update? '': ' do not') .' touch last updated field'
210     );
211
212     my ($val, $msg);
213     unless ( $record ) {
214         unless ( $update ) {
215             ( $val, $msg ) = $ticket->__Set(
216                 Field => 'Priority',
217                 Value => $new_value,
218             );
219         } else {
220             ( $val, $msg ) = $ticket->_Set(
221                 Field => 'Priority',
222                 Value => $new_value,
223                 RecordTransaction => 0,
224             );
225         }
226     } else {
227         ($val, $msg) = $ticket->SetPriority($new_value);
228     }
229
230     unless ($val) {
231         $RT::Logger->error( "Couldn't set new priority value: $msg");
232         return (0, $msg);
233     }
234     return 1;
235 }
236
237 RT::Base->_ImportOverlays();
238
239 1;