diff options
author | Mark Wells <mark@freeside.biz> | 2012-04-17 15:52:14 -0700 |
---|---|---|
committer | Mark Wells <mark@freeside.biz> | 2012-04-17 15:52:14 -0700 |
commit | 1c59bba12621e154765a8255534e94a041dfd200 (patch) | |
tree | 5f9acae2881b035e9e3b9a21d8bc6bab1f4b2a73 /rt/lib/RT | |
parent | 71cbdde5012550846390e9f0ebafdb48e06da5e8 (diff) |
link tickets to services, #17067
Diffstat (limited to 'rt/lib/RT')
-rw-r--r-- | rt/lib/RT/Interface/Web_Vendor.pm | 26 | ||||
-rwxr-xr-x | rt/lib/RT/Record.pm | 47 | ||||
-rw-r--r-- | rt/lib/RT/Tickets_Overlay.pm | 182 | ||||
-rw-r--r-- | rt/lib/RT/URI/freeside/Internal.pm | 81 |
4 files changed, 282 insertions, 54 deletions
diff --git a/rt/lib/RT/Interface/Web_Vendor.pm b/rt/lib/RT/Interface/Web_Vendor.pm index ee8c34b55..ae7f0899a 100644 --- a/rt/lib/RT/Interface/Web_Vendor.pm +++ b/rt/lib/RT/Interface/Web_Vendor.pm @@ -76,12 +76,32 @@ sub ProcessTicketCustomers { ### ### + #find new services + ### + + my @svcnums = map { /^Ticket-AddService-(\d+)$/; $1 } + grep { /^Ticket-AddService-(\d+)$/ && $ARGSRef->{$_} } + keys %$ARGSRef; + + my @custnums; + foreach my $svcnum (@svcnums) { + my @link = ( 'Type' => 'MemberOf', + 'Target' => "freeside://freeside/cust_svc/$svcnum", + ); + + my( $val, $msg ) = $Ticket->AddLink(@link); + push @results, $msg; + next if !$val; + + } + + ### #find new customers ### - my @custnums = map { /^Ticket-AddCustomer-(\d+)$/; $1 } - grep { /^Ticket-AddCustomer-(\d+)$/ && $ARGSRef->{$_} } - keys %$ARGSRef; + push @custnums, map { /^Ticket-AddCustomer-(\d+)$/; $1 } + grep { /^Ticket-AddCustomer-(\d+)$/ && $ARGSRef->{$_} } + keys %$ARGSRef; #my @delete_custnums = # map { /^Ticket-AddCustomer-(\d+)$/; $1 } diff --git a/rt/lib/RT/Record.pm b/rt/lib/RT/Record.pm index 121c08686..41e60bd84 100755 --- a/rt/lib/RT/Record.pm +++ b/rt/lib/RT/Record.pm @@ -1177,7 +1177,9 @@ sub DependsOn { =head2 Customers - This returns an RT::Links object which references all the customers that this object is a member of. + This returns an RT::Links object which references all the customers that + this object is a member of. This includes both explicitly linked customers + and links implied by services. =cut @@ -1189,11 +1191,16 @@ sub Customers { $self->{'Customers'} = $self->MemberOf->Clone; - $self->{'Customers'}->Limit( - FIELD => 'Target', - OPERATOR => 'STARTSWITH', - VALUE => 'freeside://freeside/cust_main/', - ); + for my $fstable (qw(cust_main cust_svc)) { + + $self->{'Customers'}->Limit( + FIELD => 'Target', + OPERATOR => 'STARTSWITH', + VALUE => "freeside://freeside/$fstable", + ENTRYAGGREGATOR => 'OR', + SUBCLAUSE => 'customers', + ); + } } warn "->Customers method called on $self; returning ". @@ -1205,6 +1212,34 @@ sub Customers { # }}} +# {{{ Services + +=head2 Services + + This returns an RT::Links object which references all the services this + object is a member of. + +=cut + +sub Services { + my( $self, %opt ) = @_; + + unless ( $self->{'Services'} ) { + + $self->{'Services'} = $self->MemberOf->Clone; + + $self->{'Services'}->Limit( + FIELD => 'Target', + OPERATOR => 'STARTSWITH', + VALUE => "freeside://freeside/cust_svc", + ); + } + + return $self->{'Services'}; +} + +# }}} + # {{{ sub _Links =head2 Links DIRECTION [TYPE] diff --git a/rt/lib/RT/Tickets_Overlay.pm b/rt/lib/RT/Tickets_Overlay.pm index a5d37a378..0d482cd04 100644 --- a/rt/lib/RT/Tickets_Overlay.pm +++ b/rt/lib/RT/Tickets_Overlay.pm @@ -146,11 +146,8 @@ our %FIELD_METADATA = ( HasAttribute => [ 'HASATTRIBUTE', 1 ], HasNoAttribute => [ 'HASATTRIBUTE', 0 ], #freeside - Customer => [ 'FREESIDEFIELD', ], -# Agentnum => [ 'FREESIDEFIELD', ], -# Classnum => [ 'FREESIDEFIELD', ], -# Refnum => [ 'FREESIDEFIELD', ], -# Tagnum => [ 'FREESIDEFIELD', 'cust_tag' ], + Customer => [ 'FREESIDEFIELD' => 'Customer' ], + Service => [ 'FREESIDEFIELD' => 'Service' ], WillResolve => [ 'DATE' => 'WillResolve', ], #loc_left_pair ); @@ -1823,6 +1820,15 @@ sub OrderByCols { } push @res, { %$row, ALIAS => $custalias, FIELD => $cust_field }; + } elsif ( $field eq 'Service' ) { + + my $svcalias = $self->JoinToService; + my $svc_field = lc($subkey); + if ( !$svc_field or $svc_field eq 'number' ) { + $svc_field = 'svcnum'; + } + push @res, { %$row, ALIAS => $svcalias, FIELD => $svc_field }; + } #Freeside else { @@ -1842,7 +1848,7 @@ sub JoinToCustLinks { # and an sql expression to retrieve the custnum. my $self = shift; # only join once for each RT::Tickets object - my $linkalias = $self->{cust_linkalias}; + my $linkalias = $self->{cust_main_linkalias}; if (!$linkalias) { $linkalias = $self->Join( TYPE => 'LEFT', @@ -1864,7 +1870,7 @@ sub JoinToCustLinks { OPERATOR => 'STARTSWITH', VALUE => 'freeside://freeside/cust_main/', ); - $self->{cust_linkalias} = $linkalias; + $self->{cust_main_linkalias} = $linkalias; } my $custnum_sql = "CAST(SUBSTR($linkalias.Target,31) AS "; if ( RT->Config->Get('DatabaseType') eq 'mysql' ) { @@ -1890,9 +1896,79 @@ sub JoinToCustomer { return $custalias; } +sub JoinToSvcLinks { + my $self = shift; + my $linkalias = $self->{cust_svc_linkalias}; + if (!$linkalias) { + $linkalias = $self->Join( + TYPE => 'LEFT', + ALIAS1 => 'main', + FIELD1 => 'id', + TABLE2 => 'Links', + FIELD2 => 'LocalBase', + ); + + $self->SUPER::Limit( + LEFTJOIN => $linkalias, + FIELD => 'Type', + OPERATOR => '=', + VALUE => 'MemberOf', + ); + $self->SUPER::Limit( + LEFTJOIN => $linkalias, + FIELD => 'Target', + OPERATOR => 'STARTSWITH', + VALUE => 'freeside://freeside/cust_svc/', + ); + $self->{cust_svc_linkalias} = $linkalias; + } + my $svcnum_sql = "CAST(SUBSTR($linkalias.Target,30) AS "; + if ( RT->Config->Get('DatabaseType') eq 'mysql' ) { + $svcnum_sql .= 'SIGNED INTEGER)'; + } + else { + $svcnum_sql .= 'INTEGER)'; + } + return ($linkalias, $svcnum_sql); +} + +sub JoinToService { + my $self = shift; + my ($linkalias, $svcnum_sql) = $self->JoinToSvcLinks; + $self->Join( + TYPE => 'LEFT', + EXPRESSION => $svcnum_sql, + TABLE2 => 'cust_svc', + FIELD2 => 'svcnum', + ); +} + +# This creates an alternate left join path to cust_main via cust_svc. +# _FreesideFieldLimit needs to add this as a separate, independent join +# and include all tickets that have a matching cust_main record via +# either path. +sub JoinToCustomerViaService { + my $self = shift; + my $svcalias = $self->JoinToService; + my $cust_pkg = $self->Join( + TYPE => 'LEFT', + ALIAS1 => $svcalias, + FIELD1 => 'pkgnum', + TABLE2 => 'cust_pkg', + FIELD2 => 'pkgnum', + ); + my $cust_main = $self->Join( + TYPE => 'LEFT', + ALIAS1 => $cust_pkg, + FIELD1 => 'custnum', + TABLE2 => 'cust_main', + FIELD2 => 'custnum', + ); + $cust_main; +} + sub _FreesideFieldLimit { my ( $self, $field, $op, $value, %rest ) = @_; - my $alias = $self->JoinToCustomer; my $is_negative = 0; if ( $op eq '!=' || $op =~ /\bNOT\b/i ) { # if the op is negative, do the join as though @@ -1903,40 +1979,70 @@ sub _FreesideFieldLimit { $op =~ s/\bNOT\b//; } - my $cust_field = $rest{SUBKEY} || 'custnum'; + my (@alias, $table2, $subfield, $pkey); + if ( $field eq 'Customer' ) { + push @alias, $self->JoinToCustomer; + push @alias, $self->JoinToCustomerViaService; + $pkey = 'custnum'; + } + elsif ( $field eq 'Service' ) { + push @alias, $self->JoinToService; + $pkey = 'svcnum'; + } + else { + die "malformed Freeside query: $field"; + } + + $subfield = $rest{SUBKEY} || $pkey; my $table2; # compound subkey: separate into table name and field in that table # (must be linked by custnum) - ($table2, $cust_field) = ($1, $2) if $cust_field =~ /^(\w+)?\.(\w+)$/; - - $cust_field = lc($cust_field); - $cust_field = 'custnum' if !$cust_field or $cust_field eq 'number'; - - if ( $table2 ) { - $alias = $self->Join( - TYPE => 'LEFT', - ALIAS1 => $alias, - FIELD1 => 'custnum', - TABLE2 => $table2, - FIELD2 => 'custnum', - ); + $subfield = lc($subfield); + ($table2, $subfield) = ($1, $2) if $subfield =~ /^(\w+)?\.(\w+)$/; + $subfield = $pkey if $subfield eq 'number'; + + # if it's compound, create a join from cust_main or cust_svc to that + # table, using custnum or svcnum, and Limit on that table instead. + foreach my $a (@alias) { + if ( $table2 ) { + $a = $self->Join( + TYPE => 'LEFT', + ALIAS1 => $a, + FIELD1 => $pkey, + TABLE2 => $table2, + FIELD2 => $pkey, + ); + } + + # do the actual Limit + $self->SUPER::Limit( + LEFTJOIN => $a, + FIELD => $subfield, + OPERATOR => $op, + VALUE => $value, + ENTRYAGGREGATOR => 'AND', + # no SUBCLAUSE needed, limits on different aliases across left joins + # are inherently independent + ); + + # then, since it's a left join, exclude tickets for which there is now + # no matching record in the table we just limited on. (Or where there + # is a matching record, if $is_negative.) + # For a cust_main query (where there are two different aliases), this + # will produce a subclause: "cust_main_1.custnum IS NOT NULL OR + # cust_main_2.custnum IS NOT NULL" (or "IS NULL AND..." for a negative + # query). + $self->_SQLLimit( + %rest, + ALIAS => $a, + FIELD => $pkey, + OPERATOR => $is_negative ? 'IS' : 'IS NOT', + VALUE => 'NULL', + QUOTEVALUE => 0, + ENTRYAGGREGATOR => $is_negative ? 'AND' : 'OR', + SUBCLAUSE => 'fs_limit', + ); } - - $self->SUPER::Limit( - LEFTJOIN => $alias, - FIELD => $cust_field, - OPERATOR => $op, - VALUE => $value, - ENTRYAGGREGATOR => 'AND', - ); - $self->_SQLLimit( - %rest, - ALIAS => $alias, - FIELD => 'custnum', - OPERATOR => $is_negative ? 'IS' : 'IS NOT', - VALUE => 'NULL', - QUOTEVALUE => 0, - ); } #Freeside diff --git a/rt/lib/RT/URI/freeside/Internal.pm b/rt/lib/RT/URI/freeside/Internal.pm index 5656a51d8..b5e56ee1f 100644 --- a/rt/lib/RT/URI/freeside/Internal.pm +++ b/rt/lib/RT/URI/freeside/Internal.pm @@ -38,8 +38,14 @@ use FS::Conf; use FS::Record qw(qsearchs qsearch dbdef); use FS::cust_main; use FS::cust_svc; +use FS::part_svc; use FS::payby; +#can I do this? +FS::UID->install_callback( + sub { @RT::URI::freeside::svc_tables = FS::part_svc->svc_tables() } +); + =head1 NAME RT::URI::freeside::Internal @@ -105,7 +111,23 @@ sub FreesideGetConfig { sub smart_search { #Subroutine - return map { { $_->hash } } &FS::cust_main::Search::smart_search(@_); + return map { { $_->hash } } &FS::cust_main::Search::smart_search(@_); + +} + +sub service_search { + + return map { + my $cust_pkg = $_->cust_pkg; + my $custnum = $cust_pkg->custnum if $cust_pkg; + my $label = join(': ',($_->label)[0, 1]); + my %hash = ( + $_->hash, + 'label' => $label, + 'custnum' => $custnum, # so that it's smart_searchable... + ); + \%hash + } &FS::cust_svc::smart_search(@_); } @@ -130,10 +152,22 @@ sub _FreesideURILabelLong { if ( $table eq 'cust_main' ) { my $rec = $self->_FreesideGetRecord(); - return small_custview( $rec->{'_object'}, + return '<A HREF="' . $self->HREF . '">' . + small_custview( $rec->{'_object'}, scalar(FS::Conf->new->config('countrydefault')), - 1 #nobalance - ); + 1, #nobalance + ) . '</A>'; + + } elsif ( $table eq 'cust_svc' ) { + + my $string = ''; + my $cust = $self->CustomerResolver; + if ( $cust ) { + $string = $cust->AsStringLong; + } + $string .= '<B><A HREF="' . $self->HREF . '">' . + $self->AsString . '</A></B>'; + return $string; } else { @@ -143,18 +177,36 @@ sub _FreesideURILabelLong { } -# no need to have a separate wrapper method for every one of these things +sub CustomerResolver { + my $self = shift; + if ( $self->{fstable} eq 'cust_main' ) { + return $self; + } + elsif ( $self->{fstable} eq 'cust_svc' ) { + my $rec = $self->_FreesideGetRecord(); + return if !$rec; + my $cust_pkg = $rec->{'_object'}->cust_pkg; + if ( $cust_pkg ) { + my $URI = RT::URI->new($self->CurrentUser); + $URI->FromURI('freeside://freeside/cust_main/'.$cust_pkg->custnum); + return $URI->Resolver; + } + } + return; +} + sub CustomerInfo { my $self = shift; + $self = $self->CustomerResolver or return; my $rec = $self->_FreesideGetRecord() or return; - my $cust_main = $rec->{'_object'}; + my $cust_main = delete $rec->{_object}; my $agent = $cust_main->agent; my $class = $cust_main->cust_class; my $referral = qsearchs('part_referral', { refnum => $cust_main->refnum }); my @part_tags = $cust_main->part_tag; return $self->{CustomerInfo} ||= { - $cust_main->hash, + %$rec, AgentName => ($agent ? ($agent->agentnum.': '.$agent->agent) : ''), CustomerClass => ($class ? $class->classname : ''), @@ -170,4 +222,19 @@ sub CustomerInfo { } } +sub ServiceInfo { + my $self = shift; + $self->{fstable} eq 'cust_svc' or return; + my $rec = $self->_FreesideGetRecord() or return; + my $cust_svc = $rec->{'_object'}; + my $svc_x = $cust_svc->svc_x; + my $part_svc = $cust_svc->part_svc; + return $self->{ServiceInfo} ||= { + $cust_svc->hash, + $svc_x->hash, + ServiceType => $part_svc->svc, + Label => $self->AsString, + } +} + 1; |