this should fix the occasional extra ticket showing up on wrong customer record
[freeside.git] / FS / FS / TicketSystem / RT_External.pm
1 package FS::TicketSystem::RT_External;
2
3 use strict;
4 use vars qw( $DEBUG $me $conf $dbh $default_queueid $external_url
5              $priority_reverse
6              $priority_field $priority_field_queue $field
7            );
8 use URI::Escape;
9 use FS::UID qw(dbh);
10 use FS::Record qw(qsearchs);
11 use FS::cust_main;
12
13 $me = '[FS::TicketSystem::RT_External]';
14 $DEBUG = 0;
15
16 FS::UID->install_callback( sub { 
17   $conf = new FS::Conf;
18   $default_queueid = $conf->config('ticket_system-default_queueid');
19   $priority_reverse = $conf->exists('ticket_system-priority_reverse');
20   $priority_field =
21     $conf->config('ticket_system-custom_priority_field');
22   if ( $priority_field ) {
23     $priority_field_queue =
24       $conf->config('ticket_system-custom_priority_field_queue');
25
26     $field = $priority_field_queue
27                   ? $priority_field_queue. '.%7B'. $priority_field. '%7D'
28                   : $priority_field;
29   } else {
30     $priority_field_queue = '';
31     $field = '';
32   }
33
34   $external_url = '';
35   $dbh = dbh;
36   if ($conf->config('ticket_system') eq 'RT_External') {
37     my ($datasrc, $user, $pass) = $conf->config('ticket_system-rt_external_datasrc');
38     $dbh = DBI->connect($datasrc, $user, $pass, { 'ChopBlanks' => 1 })
39       or die "RT_External DBI->connect error: $DBI::errstr\n";
40
41     $external_url = $conf->config('ticket_system-rt_external_url');
42   }
43
44   #kludge... should *use* the id... but good enough for now
45   if ( $priority_field_queue =~ /^(\d+)$/ ) {
46     my $id = $1;
47     my $sql = 'SELECT Name FROM Queues WHERE Id = ?';
48     my $sth = $dbh->prepare($sql) or die $dbh->errstr. " preparing $sql";
49     $sth->execute($id)            or die $sth->errstr. " executing $sql";
50
51     $priority_field_queue = $sth->fetchrow_arrayref->[0];
52
53   }
54
55 } );
56
57 sub num_customer_tickets {
58   my( $self, $custnum, $priority ) = @_;
59
60   my( $from_sql, @param) = $self->_from_customer( $custnum, $priority );
61
62   my $sql = "SELECT COUNT(*) $from_sql";
63   warn "$me $sql (@param)" if $DEBUG;
64   my $sth = $dbh->prepare($sql) or die $dbh->errstr. " preparing $sql";
65   $sth->execute(@param)         or die $sth->errstr. " executing $sql";
66
67   $sth->fetchrow_arrayref->[0];
68
69 }
70
71 sub customer_tickets {
72   my( $self, $custnum, $limit, $priority ) = @_;
73   $limit ||= 0;
74
75   my( $from_sql, @param) = $self->_from_customer( $custnum, $priority );
76   my $sql = "
77     SELECT Tickets.*,
78            Queues.Name AS Queue,
79            Users.Name  AS Owner,
80            position(Tickets.Status in 'newopenstalledresolvedrejecteddeleted')
81              AS svalue
82            ". ( length($priority) ? ", ObjectCustomFieldValues.Content" : '' )."
83       $from_sql
84       ORDER BY svalue,
85                Priority ". ( $priority_reverse ? 'ASC' : 'DESC' ). ",
86                id DESC
87       LIMIT $limit
88   ";
89   warn "$me $sql (@param)" if $DEBUG;
90   my $sth = $dbh->prepare($sql) or die $dbh->errstr. "preparing $sql";
91   $sth->execute(@param)         or die $sth->errstr. "executing $sql";
92
93   #munge column names???  #httemplate/view/cust_main/tickets.html has column
94   #names that might not make sense now...
95   $sth->fetchall_arrayref({});
96
97 }
98
99 sub _from_customer {
100   my( $self, $custnum, $priority ) = @_;
101
102   my @param = ();
103   my $join = '';
104   my $where = '';
105   if ( defined($priority) ) {
106
107     my $queue_sql = " ObjectCustomFields.ObjectId = ( SELECT id FROM Queues
108                                                        WHERE Queues.Name = ? )
109                       OR ( ? = '' AND ObjectCustomFields.ObjectId = 0 )";
110
111     my $customfield_sql =
112       "customfield = ( 
113         SELECT CustomFields.Id FROM CustomFields
114                   JOIN ObjectCustomFields
115                     ON ( CustomFields.id = ObjectCustomFields.CustomField )
116          WHERE LookupType = 'RT::Queue-RT::Ticket'
117            AND Name = ?
118            AND ( $queue_sql )
119        )";
120
121     push @param, $priority_field,
122                  $priority_field_queue,
123                  $priority_field_queue;
124
125     if ( length($priority) ) {
126       #$where = "    
127       #  and ? = ( select content from TicketCustomFieldValues
128       #             where ticket = tickets.id
129       #               and customfield = ( select id from customfields
130       #                                    where name = ?
131       #                                      and ( $queue_sql )
132       #                                 )
133       #          )
134       #";
135       unshift @param, $priority;
136
137       $join = "JOIN ObjectCustomFieldValues
138                  ON ( Tickets.id = ObjectCustomFieldValues.ObjectId )";
139       
140       $where = " AND Content = ?
141                  AND ObjectCustomFieldValues.Disabled != 1
142                  AND ObjectType = 'RT::Ticket'
143                  AND $customfield_sql";
144
145     } else {
146
147       $where =
148                "AND 0 = ( SELECT COUNT(*) FROM ObjectCustomFieldValues
149                            WHERE ObjectId    = Tickets.id
150                              AND ObjectType  = 'RT::Ticket'
151                              AND $customfield_sql
152                         )
153                ";
154     }
155
156   }
157
158   my $sql = "
159     FROM Tickets
160       JOIN Queues ON ( Tickets.Queue = Queues.id )
161       JOIN Users  ON ( Tickets.Owner = Users.id  )
162       JOIN Links  ON ( Tickets.id    = Links.LocalBase
163                        AND Links.Base LIKE '%/ticket/' || Tickets.id )
164       $join 
165
166     WHERE ( ". join(' OR ', map "Status = '$_'", $self->statuses ). " )
167       AND Target = 'freeside://freeside/cust_main/$custnum'
168       $where
169   ";
170
171   ( $sql, @param );
172
173 }
174
175 sub statuses {
176   #my $self = shift;
177   my @statuses = grep { ! /^\s*$/ } $conf->config('cust_main-ticket_statuses');
178   @statuses = (qw( new open stalled )) unless scalar(@statuses);
179   @statuses;
180 }
181
182 sub href_customer_tickets {
183   my( $self, $custnum ) = ( shift, shift );
184   my( $priority, @statuses);
185   if ( ref($_[0]) ) {
186     my $opt = shift;
187     $priority = $opt->{'priority'};
188     @statuses = $opt->{'statuses'} ? @{$opt->{'statuses'}} : $self->statuses;
189   } else {
190     $priority = shift;
191     @statuses = $self->statuses;
192   }
193
194   #my $href = $self->baseurl;
195
196   #i snarfed this from an RT bookmarked search, then unescaped (some of) it with
197   #perl -npe 's/%([0-9A-F]{2})/pack('C', hex($1))/eg;'
198
199   #$href .= 
200   my $href = 
201     "Search/Results.html?Order=ASC&".
202     "Query= MemberOf = 'freeside://freeside/cust_main/$custnum' ".
203     #" AND ( Status = 'open'  OR Status = 'new'  OR Status = 'stalled' )"
204     " AND ( ". join(' OR ', map "Status = '$_'", @statuses ). " ) "
205   ;
206
207   if ( defined($priority) && $field && $priority_field_queue ) {
208     $href .= " AND Queue = '$priority_field_queue' ";
209   }
210   if ( defined($priority) && $field ) {
211     $href .= " AND 'CF.$field' ";
212     if ( $priority ) {
213       $href .= "= '$priority' ";
214     } else {
215       $href .= "IS 'NULL' "; #this is "RTQL", not SQL
216     }
217   }
218
219   #$href = 
220   uri_escape($href);
221   #eventually should unescape all of it...
222
223   $href .= '&Rows=100'.
224            '&OrderBy=id&Page=1'.
225            '&Format=%27%20%20%20%3Cb%3E%3Ca%20href%3D%22'.
226            $self->baseurl.
227            'Ticket%2FDisplay.html%3Fid%3D__id__%22%3E__id__%3C%2Fa%3E%3C%2Fb%3E%2FTITLE%3A%23%27%2C%20%0A%27%3Cb%3E%3Ca%20href%3D%22'.
228            $self->baseurl.
229            'Ticket%2FDisplay.html%3Fid%3D__id__%22%3E__Subject__%3C%2Fa%3E%3C%2Fb%3E%2FTITLE%3ASubject%27%2C%20%0A%27__Status__%27%2C%20';
230
231   if ( defined($priority) && $field ) {
232     $href .= '%0A%27__CustomField.'. $field. '__%2FTITLE%3ASeverity%27%2C%20';
233   }
234
235   $href .= '%0A%27__QueueName__%27%2C%20%0A%27__OwnerName__%27%2C%20%0A%27__Priority__%27%2C%20%0A%27__NEWLINE__%27%2C%20%0A%27%27%2C%20%0A%27%3Csmall%3E__Requestors__%3C%2Fsmall%3E%27%2C%20%0A%27%3Csmall%3E__CreatedRelative__%3C%2Fsmall%3E%27%2C';
236
237   if ( defined($priority) && $field ) {
238     $href .=   '%20%0A%27__-__%27%2C';
239   }
240
241   $href .= '%20%0A%27%3Csmall%3E__ToldRelative__%3C%2Fsmall%3E%27%2C%20%0A%27%3Csmall%3E__LastUpdatedRelative__%3C%2Fsmall%3E%27%2C%20%0A%27%3Csmall%3E__TimeLeft__%3C%2Fsmall%3E%27';
242
243   #$href =
244   #uri_escape($href);
245
246   $self->baseurl. $href;
247
248 }
249
250 sub href_new_ticket {
251   my( $self, $custnum_or_cust_main, $requestors ) = @_;
252
253   my( $custnum, $cust_main );
254   if ( ref($custnum_or_cust_main) ) {
255     $cust_main = $custnum_or_cust_main;
256     $custnum = $cust_main->custnum;
257   } else {
258     $custnum = $custnum_or_cust_main;
259     $cust_main = qsearchs('cust_main', { 'custnum' => $custnum } );
260   }
261   my $queueid = $cust_main->agent->ticketing_queueid || $default_queueid;
262
263   $self->baseurl.
264   'Ticket/Create.html?'.
265     "Queue=$queueid".
266     "&new-MemberOf=freeside://freeside/cust_main/$custnum".
267     ( $requestors ? '&Requestors='. uri_escape($requestors) : '' )
268     ;
269 }
270
271 sub href_ticket {
272   my($self, $ticketnum) = @_;
273   $self->baseurl. 'Ticket/Display.html?id='.$ticketnum;
274 }
275
276 sub queues {
277   my($self) = @_;
278
279   my $sql = "SELECT id, Name FROM Queues WHERE Disabled = 0";
280   my $sth = $dbh->prepare($sql) or die $dbh->errstr. " preparing $sql";
281   $sth->execute()               or die $sth->errstr. " executing $sql";
282
283   map { $_->[0] => $_->[1] } @{ $sth->fetchall_arrayref([]) };
284
285 }
286
287 sub queue {
288   my($self, $queueid) = @_;
289
290   return '' unless $queueid;
291
292   my $sql = "SELECT Name FROM Queues WHERE id = ?";
293   my $sth = $dbh->prepare($sql) or die $dbh->errstr. " preparing $sql";
294   $sth->execute($queueid)       or die $sth->errstr. " executing $sql";
295
296   my $rows = $sth->fetchrow_arrayref;
297   $rows ? $rows->[0] : '';
298
299 }
300
301 sub baseurl {
302   #my $self = shift;
303   $external_url. '/';
304 }
305
306 sub _retrieve_single_value {
307   my( $self, $sql ) = @_;
308
309   warn "$me $sql" if $DEBUG;
310   my $sth = $dbh->prepare($sql) or die $dbh->errstr. "preparing $sql";
311   $sth->execute                 or die $sth->errstr. "executing $sql";
312
313   my $arrayref = $sth->fetchrow_arrayref;
314   $arrayref ? $arrayref->[0] : $arrayref;
315 }
316
317 sub transaction_creator {
318   my( $self, $transaction_id ) = @_;
319
320   my $sql = "SELECT Name FROM Transactions JOIN Users ON ".
321             "Transactions.Creator=Users.id WHERE Transactions.id = ".
322             $transaction_id;
323
324   $self->_retrieve_single_value($sql);
325 }
326
327 sub transaction_ticketid {
328   my( $self, $transaction_id ) = @_;
329
330   my $sql = "SELECT ObjectId FROM Transactions WHERE Transactions.id = ".
331             $transaction_id;
332   
333   $self->_retrieve_single_value($sql);
334 }
335
336 sub transaction_subject {
337   my( $self, $transaction_id ) = @_;
338
339   my $sql = "SELECT Subject FROM Transactions JOIN Tickets ON ObjectId=".
340             "Tickets.id WHERE Transactions.id = ".  $transaction_id;
341   
342   $self->_retrieve_single_value($sql);
343 }
344
345 sub transaction_status {
346   my( $self, $transaction_id ) = @_;
347
348   my $sql = "SELECT Status FROM Transactions JOIN Tickets ON ObjectId=".
349             "Tickets.id WHERE Transactions.id = ".  $transaction_id;
350   
351   $self->_retrieve_single_value($sql);
352 }
353
354 sub access_right {
355   warn "WARNING: no access rights available w/ external RT";
356   0;
357 }
358
359 1;
360