<%doc> Accessible Freeside svc_x fields go in here. RT::URI::freeside::Internal pulls all fields from cust_svc and the svc_x tables into ServiceInfo(). RT::Tickets_Overlay resolves "Service.foo" as "cust_svc.foo", and "Service.svc_acct.bar" as "JOIN svc_acct USING (svcnum) ... svc_acct.bar". See /Elements/CustomerFields for notes on this data structure. <%once> my @service_fields = ( # ordered { # svcnum Name => 'Service', Label => 'Service', Display => sub { my $Ticket = shift; my @return = (); foreach my $s (ticket_svc_resolvers($Ticket)) { push @return, \'', $s->AsString, \'', \'
'; } pop @return; @return; }, OrderBy => 'Service.Number', }, { #Column name (format string) Name => 'ServiceType', # Column heading/query builder name Label => 'Service Type', # Column value (coderef, cust_svc/svc_x field, or ServiceInfo key) Display => 'ServiceType', # Query builder options # RT-SQL field, defaults to Name QueryName => 'Service.svcpart', Op => equals_notequals, Value => select_table('part_svc', 'svcpart', 'svc'), # RT-SQL sort key (if any) OrderBy => 'Service.svcpart', }, { Name => 'ServiceLocation', Label => 'Service Location', Display => svc_location_attribute('location'), }, { Name => 'ServiceKey', # loosely corresponds to smartsearch/label field Label => '', # not displayable QueryLabel => { Type => 'select', Options => [ 'Service.svc_acct.username' => loc('Username'), 'Service.svc_phone.phonenum' => loc('Phone Number'), 'Service.svc_broadband.ip_addr' => loc('IP Address'), 'Service.svc_broadband.mac_addr' => loc('MAC Address'), ], }, Op => matches_notmatches, Value => { Type => 'text', Size => 20 }, }, { Name => 'Router', Label => 'Router', QueryName => 'Service.svc_broadband.routernum', # not displayable Op => equals_notequals, Value => select_table('router', 'routernum', 'routername'), OrderBy => 'Service.svc_broadband.routernum', }, ); #helper subs #Op sub equals_notequals { { Type => 'component', Path => '/Elements/SelectBoolean', Arguments => { TrueVal=> '=', FalseVal=> '!=' }, } } sub matches_notmatches { { Type => 'component', Path => '/Elements/SelectMatch', }, } #Value sub select_table { my ($table, $value_col, $name_col, $hashref) = @_; $hashref ||= { disabled => '' }; # common case return { Type => 'select', Options => [ '' => '-', map { $_->$value_col, $_->$name_col } qsearch($table, $hashref) ], } } sub ticket_svc_resolvers { my $Ticket = shift; my @Services = @{ $Ticket->Services->ItemsArrayRef }; return map $_->TargetURI->Resolver, @Services; } sub svc_info_attribute { my $attribute = shift; sub { my $Ticket = shift; my @return; foreach my $s (ticket_svc_resolvers($Ticket)) { push @return, $s->ServiceInfo->{$attribute}, '
'; } pop @return; #trailing
@return; }; } sub svc_location_attribute { # Tricky: if the ticket is linked to a service, we want to return the # service's location, but if it's not, we want to return the customer's # default service location. # If it's linked to Customer A and also to Service A, it should return # Service A's location (and not Customer A's default service location). # But if it's linked to Service A and also to Customer B, then what? We # can't satisfy all the constraints here. my $attribute = shift; sub { my @return; my $Ticket = shift; my @svc_resolvers = ticket_svc_resolvers($Ticket); if (@svc_resolvers) { foreach my $s (@svc_resolvers) { push @return, $s->ServiceInfo->{$attribute}, '
'; } } else { my @cust_resolvers = map $_->TargetURI->Resolver, @{ $Ticket->Customers->ItemsArrayRef }; foreach my $c (@cust_resolvers) { push @return, $c->CustomerInfo->{"ship_$attribute"}, '
'; } } pop @return; #trailing
@return; }; } <%init> my $arg = shift; if ( $arg eq 'Names' ) { return map { $_->{Name} } grep { exists $_->{Display} } @service_fields; } elsif ( $arg eq 'ColumnMap' ) { return map { my $f = $_; $f->{Name} => { title => $f->{Label}, attribute => $f->{OrderBy} || '', value => ref($f->{Display}) eq 'CODE' ? $f->{Display} : svc_info_attribute($f->{Display}) } } #map grep { exists $_->{Display} } @service_fields; } elsif ( $arg eq 'Criteria' ) { return map { my $f = $_; # argument to Search/Elements/ConditionRow { Name => ($f->{QueryName} || $f->{Name}), Field => ($f->{QueryLabel} || $f->{Label}), Op => $f->{Op}, Value => $f->{Value}, } } #map grep { exists($_->{Value}) } @service_fields; } else { die "unknown ServiceFields mode '$arg'\n"; }