summaryrefslogtreecommitdiff
path: root/rt/lib
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 /rt/lib
parent71cbdde5012550846390e9f0ebafdb48e06da5e8 (diff)
link tickets to services, #17067
Diffstat (limited to 'rt/lib')
-rw-r--r--rt/lib/RT/Interface/Web_Vendor.pm26
-rwxr-xr-xrt/lib/RT/Record.pm47
-rw-r--r--rt/lib/RT/Tickets_Overlay.pm182
-rw-r--r--rt/lib/RT/URI/freeside/Internal.pm81
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;