improve customer field access in RT queries, #16490
[freeside.git] / rt / share / html / Elements / CustomerFields
1 <%doc>
2 All accessible Freeside customer fields fields go in here.  Those of 
3 them outside cust_main also need to go in RT::URI::freeside::Internal
4 (where they should be pulled into CustomerInfo).  Nothing should need 
5 to go in RT::Tickets_Overlay; it already resolves "Customer.foo" as 
6 "cust_main.foo", and "Customer.cust_bar.foo" as "JOIN cust_bar using 
7 (custnum) ... cust_bar.foo".
8
9 About the keys:
10 - 'Value' makes the field a search criterion.  This also requires 'Op'.
11   See Search/Elements/PickBasics.
12 - 'Display' makes it an output column, and is either the cust_main 
13   field, the CustomerInfo key, or a coderef that takes the RT::Ticket
14   as an argument.
15 - 'OrderBy' makes it a sort key, and must be set to an RT-SQL field
16   name to sort by.
17 </%doc>
18 <%once>
19 return unless $RT::URI::freeside::IntegrationType eq 'Internal';
20
21 my @customer_fields = ( # ordered
22   {
23     # custnum
24     Name    => 'Customer',
25     Label   => 'Customer',
26     Display => sub {
27                 my $Ticket = shift;
28                 my @return = ();
29                 foreach my $c (ticket_cust_resolvers($Ticket)) {
30                     push @return, \'<A HREF="', $c->HREF, \'">',
31                                   $c->AsString,
32                                   \'</A>',
33                                   \'<BR>';
34                 }
35                 pop @return;
36                 @return;
37               },
38     OrderBy => 'Customer.Number',
39   },
40   {
41     #Column name (format string)
42     Name    => 'Agent',
43     # Column heading/query builder name
44     Label   => 'Agent',
45     # Column value (coderef, cust_main field, or CustomerInfo key)
46     Display => 'AgentName',
47     # Query builder options
48     # RT-SQL field, defaults to Name
49     QueryName => 'Customer.agentnum',
50     #QueryLabel => 'Agent' #defaults to Label
51     Op      => equals_notequals,
52     Value   => select_table('agent', 'agentnum', 'agent'),
53     # RT-SQL sort key (if any)
54     OrderBy => 'Customer.agentnum',
55   },
56   {
57     Name    => 'CustomerClass',
58     Label   => 'Customer Class',
59     Display => 'CustomerClass',
60     QueryName => 'Customer.classnum',
61     Op      => equals_notequals,
62     Value   => select_table('cust_class', 'classnum', 'classname'),
63     OrderBy => 'Customer.classnum',
64   },
65   {
66     Name    => 'AdvertisingSource',
67     Label   => 'Advertising Source',
68     Display => 'Referral',
69     QueryName => 'Customer.refnum',
70     Op      => equals_notequals,
71     Value   => select_table('part_referral', 'refnum', 'referral'),
72     OrderBy => 'Customer.refnum',
73   },
74   {
75     Name    => 'BillingType',
76     Label   => 'Billing Type',
77     Display => 'BillingType',
78     QueryName => 'Customer.payby',
79     Op      => equals_notequals,
80     Value   => {
81       Type => 'select',
82       Options => [ '' => '-',
83         map { $_, FS::payby->longname($_) } FS::payby->cust_payby 
84       ],
85     },
86   },
87   {
88     Name    => 'InvoiceEmail',
89     Label   => 'Invoice Email',
90     Display => 'InvoiceEmail',
91     # query/sort needed?
92   },
93   {
94     Name    => 'City',
95     Label   => 'City',
96     Display => 'city',
97     OrderBy => 'Customer.city',
98   },
99   {
100     Name    => 'State',
101     Label   => 'State',
102     Display => 'state',
103     OrderBy => 'Customer.state',
104   },
105   {
106     Name    => 'CustomerTags',
107     Label   => '',
108     Display => sub {
109                 my $Ticket = shift;
110                 my @return = ();
111                 foreach my $c (ticket_cust_resolvers($Ticket)) {
112                   foreach my $t (@{ $c->CustomerInfo->{CustomerTags} }) {
113                     push @return, \'<SPAN style="background-color:#',
114                     $t->{'color'},
115                     \';">&nbsp;',
116                     $t->{'name'},
117                     \'&nbsp;</SPAN>',
118                     \'&nbsp;'
119                     ;
120                   }
121                   pop @return;
122                   push @return, \'<BR>';
123                 }
124                 pop @return;
125                 @return;
126               },
127     QueryName => 'Customer.cust_tag.tagnum',
128     QueryLabel => 'Tag',
129     Op      => equals_notequals,
130     Value   => select_table('part_tag', 'tagnum', 'tagname'),
131     OrderBy => '',
132   },
133 );
134
135 #helper subs
136 #Op      
137 sub equals_notequals {
138   return {
139       Type => 'component',
140       Path => '/Elements/SelectBoolean',
141       Arguments => { TrueVal=> '=', FalseVal=> '!=' },
142   }
143 }
144
145 #Value
146 sub select_table {
147   my ($table, $value_col, $name_col, $hashref) = @_;
148   $hashref ||= { disabled => '' }; # common case
149   return {
150     Type => 'select',
151     Options => [ 
152       '' => '-',
153       map { $_->$value_col, $_->$name_col }
154       qsearch($table, $hashref)
155     ],
156   }
157 }
158
159 sub ticket_cust_resolvers {
160     my $Ticket = shift;
161     my @Customers = @{ $Ticket->Customers->ItemsArrayRef };
162     return map $_->TargetURI->Resolver, @Customers;
163 }
164
165 sub cust_info_attribute { # the simple case of $resolver->CustomerInfo->{foo}
166     my $attribute = shift;
167     sub {
168         my $Ticket = shift;
169         my @return;
170         foreach my $c (ticket_cust_resolvers($Ticket)) {
171             push @return, $c->CustomerInfo->{$attribute}, '<BR>';
172         }
173         pop @return; #trailing <BR>
174         @return;
175     };
176 }
177
178 </%once>
179 <%init>
180 return unless $RT::URI::freeside::IntegrationType eq 'Internal';
181
182 my $arg = shift;
183 if ( $arg eq 'Names' ) {
184   return map { $_->{Name} } @customer_fields;
185 }
186 elsif ( $arg eq 'ColumnMap' ) {
187   return map {
188     my $f = $_;
189
190     $f->{Name} => {
191         title     => $f->{Label},
192         attribute => $f->{OrderBy} || '',
193         value     => ref($f->{Display}) eq 'CODE' ? 
194                       $f->{Display} : 
195                       cust_info_attribute($f->{Display})
196       }
197   } #map
198   grep { exists $_->{Display} }
199   @customer_fields;
200 }
201 elsif ( $arg eq 'PickBasics' ) {
202   return map {
203     my $f = $_;
204     {
205       Name  => ($f->{QueryName} || $f->{Name}),
206       Field => ($f->{QueryLabel} || $f->{Label}),
207       Op    => $f->{Op},
208       Value => $f->{Value},
209     }
210   } #map
211   grep { exists $_->{Value} }
212   @customer_fields;
213 }
214 else { die "unknown CustomerFields mode '$arg'\n"; }
215 </%init>