summaryrefslogtreecommitdiff
path: root/FS
diff options
context:
space:
mode:
authorMark Wells <mark@freeside.biz>2012-04-17 15:52:14 -0700
committerMark Wells <mark@freeside.biz>2012-04-17 15:52:14 -0700
commit1c59bba12621e154765a8255534e94a041dfd200 (patch)
tree5f9acae2881b035e9e3b9a21d8bc6bab1f4b2a73 /FS
parent71cbdde5012550846390e9f0ebafdb48e06da5e8 (diff)
link tickets to services, #17067
Diffstat (limited to 'FS')
-rw-r--r--FS/FS/TicketSystem/RT_External.pm51
-rw-r--r--FS/FS/TicketSystem/RT_Internal.pm63
-rw-r--r--FS/FS/cust_svc.pm86
-rw-r--r--FS/FS/svc_phone.pm3
4 files changed, 182 insertions, 21 deletions
diff --git a/FS/FS/TicketSystem/RT_External.pm b/FS/FS/TicketSystem/RT_External.pm
index f976ac0..22d2472 100644
--- a/FS/FS/TicketSystem/RT_External.pm
+++ b/FS/FS/TicketSystem/RT_External.pm
@@ -97,6 +97,11 @@ sub customer_tickets {
}
+sub service_tickets {
+ warn "service_tickets not available with RT_External.\n";
+ return;
+}
+
sub comments_on_tickets {
my ($self, $custnum, $limit, $time ) = @_;
$limit ||= 0;
@@ -206,7 +211,20 @@ sub statuses {
}
sub href_customer_tickets {
- my( $self, $custnum ) = ( shift, shift );
+ my($self, $custnum) = (shift, shift);
+ if ( $custnum =~ /^(\d+)$/ ) {
+ return $self->href_search_tickets("MemberOf = 'freeside://freeside/cust_main/$1'");
+ }
+ warn "bad custnum $custnum"; return '';
+}
+
+sub href_service_tickets {
+ warn "service_tickets not available with RT_External.\n";
+ '';
+}
+
+sub href_search_tickets {
+ my( $self, $where ) = ( shift, shift );
my( $priority, @statuses);
if ( ref($_[0]) ) {
my $opt = shift;
@@ -225,8 +243,8 @@ sub href_customer_tickets {
#$href .=
my $href =
"Search/Results.html?Order=ASC&".
- "Query= MemberOf = 'freeside://freeside/cust_main/$custnum' ".
- #" AND ( Status = 'open' OR Status = 'new' OR Status = 'stalled' )"
+ "Query= $where" .
+ #MemberOf = 'freeside://freeside/cust_main/$custnum' ".
" AND ( ". join(' OR ', map "Status = '$_'", @statuses ). " ) "
;
@@ -274,15 +292,19 @@ sub href_customer_tickets {
}
sub href_params_new_ticket {
- my( $self, $custnum_or_cust_main, $requestors ) = @_;
-
- my( $custnum, $cust_main );
- if ( ref($custnum_or_cust_main) ) {
- $cust_main = $custnum_or_cust_main;
- $custnum = $cust_main->custnum;
- } else {
- $custnum = $custnum_or_cust_main;
- $cust_main = qsearchs('cust_main', { 'custnum' => $custnum } );
+ # my( $self, $custnum_or_cust_main, $requestors ) = @_;
+ # no longer takes $custnum--it must be an object
+ my ( $self, $object, $requestors ) = @_;
+ my $cust_main; # for default requestors
+ if ( $object->isa('FS::cust_main') ) {
+ $cust_main = $object;
+ }
+ elsif ( $object->isa('FS::svc_Common') ) {
+ $object = $object->cust_svc;
+ $cust_main = $object->cust_pkg->cust_main if ( $object->cust_pkg );
+ }
+ elsif ( $object->isa('FS::cust_svc') ) {
+ $cust_main = $object->cust_pkg->cust_main if ( $object->cust_pkg );
}
# explicit $requestors > config option > invoicing_list
@@ -291,9 +313,12 @@ sub href_params_new_ticket {
$requestors = $cust_main->invoicing_list_emailonly_scalar
if (!$requestors) and defined($cust_main);
+ my $subtype = $object->table;
+ my $pkey = $object->get($object->primary_key);
+
my %param = (
'Queue' => ($cust_main->agent->ticketing_queueid || $default_queueid),
- 'new-MemberOf'=> "freeside://freeside/cust_main/$custnum",
+ 'new-MemberOf'=> "freeside://freeside/$subtype/$pkey",
'Requestors' => $requestors,
);
diff --git a/FS/FS/TicketSystem/RT_Internal.pm b/FS/FS/TicketSystem/RT_Internal.pm
index d96e5f0..ffa8c7c 100644
--- a/FS/FS/TicketSystem/RT_Internal.pm
+++ b/FS/FS/TicketSystem/RT_Internal.pm
@@ -107,10 +107,13 @@ properly.
=cut
-sub _customer_tickets_search {
- my ( $self, $custnum, $limit, $priority ) = @_;
+# create an RT::Tickets object for a specified custnum or svcnum
- $custnum =~ /^\d+$/ or die "invalid custnum: $custnum";
+sub _tickets_search {
+ my ( $self, $type, $number, $limit, $priority ) = @_;
+
+ $type =~ /^Customer|Service$/ or die "invalid type: $type";
+ $number =~ /^\d+$/ or die "invalid custnum/svcnum: $number";
$limit =~ /^\d+$/ or die "invalid limit: $limit";
my $session = $self->session();
@@ -119,7 +122,8 @@ sub _customer_tickets_search {
my $Tickets = RT::Tickets->new($CurrentUser);
- my $rtql = "MemberOf = 'freeside://freeside/cust_main/$custnum'";
+ # "Customer.number" searches tickets linked via cust_svc also
+ my $rtql = "$type.number = $number";
if ( defined( $priority ) ) {
my $custom_priority = FS::Conf->new->config('ticket_system-custom_priority_field');
@@ -144,8 +148,25 @@ sub _customer_tickets_search {
return $Tickets;
}
+sub href_customer_tickets {
+ my ($self, $custnum) = (shift, shift);
+ if ($custnum =~ /^(\d+)$/) {
+ return $self->href_search_tickets("Customer.number = $custnum");
+ }
+ warn "bad custnum $custnum"; '';
+}
+
+sub href_service_tickets {
+ my ($self, $svcnum) = (shift, shift);
+ if ($svcnum =~ /^(\d+)$/ ) {
+ return $self->href_search_tickets("Service.number = $svcnum");
+ }
+ warn "bad svcnum $svcnum"; '';
+}
+
sub customer_tickets {
- my $Tickets = _customer_tickets_search(@_);
+ my $self = shift;
+ my $Tickets = $self->_tickets_search('Customer', @_);
my $conf = FS::Conf->new;
my $priority_order =
@@ -168,8 +189,30 @@ sub customer_tickets {
sub num_customer_tickets {
my ( $self, $custnum, $priority ) = @_;
- my $Tickets = $self->_customer_tickets_search($custnum, 0, $priority);
- return $Tickets->CountAll;
+ $self->_tickets_search('Customer', $custnum, 0, $priority)->CountAll;
+}
+
+sub service_tickets {
+ my $self = shift;
+ my $Tickets = $self->_tickets_search('Service', @_);
+
+ my $conf = FS::Conf->new;
+ my $priority_order =
+ $conf->exists('ticket_system-priority_reverse') ? 'ASC' : 'DESC';
+
+ my @order_by = (
+ { FIELD => 'Priority', ORDER => $priority_order },
+ { FIELD => 'Id', ORDER => 'DESC' },
+ );
+
+ $Tickets->OrderByCols(@order_by);
+
+ my @tickets;
+ while ( my $t = $Tickets->Next ) {
+ push @tickets, _ticket_info($t);
+ }
+
+ return \@tickets;
}
sub _ticket_info {
@@ -200,6 +243,12 @@ sub _ticket_info {
if ( $ss_priority ) {
$ticket_info{'_selfservice_priority'} = $ticket_info{"CF.{$ss_priority}"};
}
+ my $svcnums = [
+ map { $_->Target =~ /cust_svc\/(\d+)/; $1 }
+ @{ $t->Services->ItemsArrayRef }
+ ];
+ $ticket_info{'svcnums'} = $svcnums;
+
return \%ticket_info;
}
diff --git a/FS/FS/cust_svc.pm b/FS/FS/cust_svc.pm
index fc6e605..a527913 100644
--- a/FS/FS/cust_svc.pm
+++ b/FS/FS/cust_svc.pm
@@ -756,8 +756,94 @@ sub get_session_history {
}
+=item tickets
+
+Returns an array of hashes representing the tickets linked to this service.
+
+=cut
+
+sub tickets {
+ my $self = shift;
+
+ my $conf = FS::Conf->new;
+ my $num = $conf->config('cust_main-max_tickets') || 10;
+ my @tickets = ();
+
+ if ( $conf->config('ticket_system') ) {
+ unless ( $conf->config('ticket_system-custom_priority_field') ) {
+
+ @tickets = @{ FS::TicketSystem->service_tickets($self->svcnum, $num) };
+
+ } else {
+
+ foreach my $priority (
+ $conf->config('ticket_system-custom_priority_field-values'), ''
+ ) {
+ last if scalar(@tickets) >= $num;
+ push @tickets,
+ @{ FS::TicketSystem->service_tickets( $self->svcnum,
+ $num - scalar(@tickets),
+ $priority,
+ )
+ };
+ }
+ }
+ }
+ (@tickets);
+}
+
+
=back
+=head1 SUBROUTINES
+
+=over 4
+
+=item smart_search OPTION => VALUE ...
+
+Accepts the option I<search>, the string to search for. The string will
+be searched for as a username, email address, IP address, MAC address,
+phone number, and hardware serial number. Unlike the I<smart_search> on
+customers, this always requires an exact match.
+
+=cut
+
+# though perhaps it should be fuzzy in some cases?
+sub smart_search {
+ my %opt = @_;
+ # some false laziness w/ search/cust_svc.html
+ my $string = $opt{'search'};
+ $string =~ s/(^\s+|\s+$)//; #trim leading & trailing whitespace
+
+ my @extra_sql = ' ( '. join(' OR ',
+ map { my $table = $_;
+ my $search_sql = "FS::$table"->search_sql($string);
+ " ( svcdb = '$table'
+ AND 0 < ( SELECT COUNT(*) FROM $table
+ WHERE $table.svcnum = cust_svc.svcnum
+ AND $search_sql
+ )
+ ) ";
+ }
+ FS::part_svc->svc_tables
+ ). ' ) ';
+ push @extra_sql, $FS::CurrentUser::CurrentUser->agentnums_sql(
+ 'null_right' => 'View/link unlinked services'
+ );
+ my $extra_sql = ' WHERE '.join(' AND ', @extra_sql);
+ #for agentnum
+ my $addl_from = ' LEFT JOIN cust_pkg USING ( pkgnum )'.
+ ' LEFT JOIN cust_main USING ( custnum )'.
+ ' LEFT JOIN part_svc USING ( svcpart )';
+
+ qsearch({
+ 'table' => 'cust_svc',
+ 'addl_from' => $addl_from,
+ 'hashref' => {},
+ 'extra_sql' => $extra_sql,
+ });
+}
+
=head1 BUGS
Behaviour of changing the svcpart of cust_svc records is undefined and should
diff --git a/FS/FS/svc_phone.pm b/FS/FS/svc_phone.pm
index 118748e..b395ea6 100644
--- a/FS/FS/svc_phone.pm
+++ b/FS/FS/svc_phone.pm
@@ -218,13 +218,14 @@ Class method which returns an SQL fragment to search for the given string.
sub search_sql {
my( $class, $string ) = @_;
+ my $conf = new FS::Conf;
+
if ( $conf->exists('svc_phone-allow_alpha_phonenum') ) {
$string =~ s/\W//g;
} else {
$string =~ s/\D//g;
}
- my $conf = new FS::Conf;
my $ccode = ( $conf->exists('default_phone_countrycode')
&& $conf->config('default_phone_countrycode')
)