more reporting options for failed billing events, RT#6447
[freeside.git] / FS / FS / cust_main_Mixin.pm
1 package FS::cust_main_Mixin;
2
3 use strict;
4 use vars qw( $DEBUG $me );
5 use FS::UID qw(dbh);
6 use FS::cust_main;
7
8 $DEBUG = 0;
9 $me = '[FS::cust_main_Mixin]';
10
11 =head1 NAME
12
13 FS::cust_main_Mixin - Mixin class for records that contain fields from cust_main
14
15 =head1 SYNOPSIS
16
17 package FS::some_table;
18 use vars qw(@ISA);
19 @ISA = qw( FS::cust_main_Mixin FS::Record );
20
21 =head1 DESCRIPTION
22
23 This is a mixin class for records that contain fields from the cust_main table,
24 for example, from a JOINed search.  See httemplate/search/ for examples.
25
26 =head1 METHODS
27
28 =over 4
29
30 =cut
31
32 sub cust_unlinked_msg { '(unlinked)'; }
33 sub cust_linked { $_[0]->custnum; }
34
35 =item display_custnum
36
37 Given an object that contains fields from cust_main (say, from a JOINed
38 search; see httemplate/search/ for examples), returns the equivalent of the
39 FS::cust_main I<name> method, or "(unlinked)" if this object is not linked to
40 a customer.
41
42 =cut
43
44 sub display_custnum {
45   my $self = shift;
46   $self->cust_linked
47     ? FS::cust_main::display_custnum($self)
48     : $self->cust_unlinked_msg;
49 }
50
51 =item name
52
53 Given an object that contains fields from cust_main (say, from a JOINed
54 search; see httemplate/search/ for examples), returns the equivalent of the
55 FS::cust_main I<name> method, or "(unlinked)" if this object is not linked to
56 a customer.
57
58 =cut
59
60 sub name {
61   my $self = shift;
62   $self->cust_linked
63     ? FS::cust_main::name($self)
64     : $self->cust_unlinked_msg;
65 }
66
67 =item ship_name
68
69 Given an object that contains fields from cust_main (say, from a JOINed
70 search; see httemplate/search/ for examples), returns the equivalent of the
71 FS::cust_main I<ship_name> method, or "(unlinked)" if this object is not
72 linked to a customer.
73
74 =cut
75
76 sub ship_name {
77   my $self = shift;
78   $self->cust_linked
79     ? FS::cust_main::ship_name($self)
80     : $self->cust_unlinked_msg;
81 }
82
83 =item contact
84
85 Given an object that contains fields from cust_main (say, from a JOINed
86 search; see httemplate/search/ for examples), returns the equivalent of the
87 FS::cust_main I<contact> method, or "(unlinked)" if this object is not linked
88 to a customer.
89
90 =cut
91
92 sub contact {
93   my $self = shift;
94   $self->cust_linked
95     ? FS::cust_main::contact($self)
96     : $self->cust_unlinked_msg;
97 }
98
99 =item ship_contact
100
101 Given an object that contains fields from cust_main (say, from a JOINed
102 search; see httemplate/search/ for examples), returns the equivalent of the
103 FS::cust_main I<ship_contact> method, or "(unlinked)" if this object is not
104 linked to a customer.
105
106 =cut
107
108 sub ship_contact {
109   my $self = shift;
110   $self->cust_linked
111     ? FS::cust_main::ship_contact($self)
112     : $self->cust_unlinked_msg;
113 }
114
115 =item country_full
116
117 Given an object that contains fields from cust_main (say, from a JOINed
118 search; see httemplate/search/ for examples), returns the equivalent of the
119 FS::cust_main I<country_full> method, or "(unlinked)" if this object is not
120 linked to a customer.
121
122 =cut
123
124 sub country_full {
125   my $self = shift;
126   $self->cust_linked
127     ? FS::cust_main::country_full($self)
128     : $self->cust_unlinked_msg;
129 }
130
131 =item invoicing_list_emailonly
132
133 Given an object that contains fields from cust_main (say, from a JOINed
134 search; see httemplate/search/ for examples), returns the equivalent of the
135 FS::cust_main I<invoicing_list_emailonly> method, or "(unlinked)" if this
136 object is not linked to a customer.
137
138 =cut
139
140 sub invoicing_list_emailonly {
141   my $self = shift;
142   warn "invoicing_list_email only called on $self, ".
143        "custnum ". $self->custnum. "\n"
144     if $DEBUG;
145   $self->cust_linked
146     ? FS::cust_main::invoicing_list_emailonly($self)
147     : $self->cust_unlinked_msg;
148 }
149
150 =item invoicing_list_emailonly_scalar
151
152 Given an object that contains fields from cust_main (say, from a JOINed
153 search; see httemplate/search/ for examples), returns the equivalent of the
154 FS::cust_main I<invoicing_list_emailonly_scalar> method, or "(unlinked)" if
155 this object is not linked to a customer.
156
157 =cut
158
159 sub invoicing_list_emailonly_scalar {
160   my $self = shift;
161   warn "invoicing_list_emailonly called on $self, ".
162        "custnum ". $self->custnum. "\n"
163     if $DEBUG;
164   $self->cust_linked
165     ? FS::cust_main::invoicing_list_emailonly_scalar($self)
166     : $self->cust_unlinked_msg;
167 }
168
169 =item invoicing_list
170
171 Given an object that contains fields from cust_main (say, from a JOINed
172 search; see httemplate/search/ for examples), returns the equivalent of the
173 FS::cust_main I<invoicing_list> method, or "(unlinked)" if this object is not
174 linked to a customer.
175
176 Note: this method is read-only.
177
178 =cut
179
180 #read-only
181 sub invoicing_list {
182   my $self = shift;
183   $self->cust_linked
184     ? FS::cust_main::invoicing_list($self)
185     : ();
186 }
187
188 =item status
189
190 Given an object that contains fields from cust_main (say, from a JOINed
191 search; see httemplate/search/ for examples), returns the equivalent of the
192 FS::cust_main I<status> method, or "(unlinked)" if this object is not linked to
193 a customer.
194
195 =cut
196
197 sub cust_status {
198   my $self = shift;
199   return $self->cust_unlinked_msg unless $self->cust_linked;
200
201   #FS::cust_main::status($self)
202   #false laziness w/actual cust_main::status
203   # (make sure FS::cust_main methods are called)
204   for my $status (qw( prospect active inactive suspended cancelled )) {
205     my $method = $status.'_sql';
206     my $sql = FS::cust_main->$method();;
207     my $numnum = ( $sql =~ s/cust_main\.custnum/?/g );
208     my $sth = dbh->prepare("SELECT $sql") or die dbh->errstr;
209     $sth->execute( ($self->custnum) x $numnum )
210       or die "Error executing 'SELECT $sql': ". $sth->errstr;
211     return $status if $sth->fetchrow_arrayref->[0];
212   }
213 }
214
215 =item ucfirst_cust_status
216
217 Given an object that contains fields from cust_main (say, from a JOINed
218 search; see httemplate/search/ for examples), returns the equivalent of the
219 FS::cust_main I<ucfirst_status> method, or "(unlinked)" if this object is not
220 linked to a customer.
221
222 =cut
223
224 sub ucfirst_cust_status {
225   my $self = shift;
226   $self->cust_linked
227     ? ucfirst( $self->cust_status(@_) ) 
228     : $self->cust_unlinked_msg;
229 }
230
231 =item cust_statuscolor
232
233 Given an object that contains fields from cust_main (say, from a JOINed
234 search; see httemplate/search/ for examples), returns the equivalent of the
235 FS::cust_main I<statuscol> method, or "000000" if this object is not linked to
236 a customer.
237
238 =cut
239
240 sub cust_statuscolor {
241   my $self = shift;
242
243   $self->cust_linked
244     ? FS::cust_main::cust_statuscolor($self)
245     : '000000';
246 }
247
248 =item prospect_sql
249
250 =item active_sql
251
252 =item inactive_sql
253
254 =item suspended_sql
255
256 =item cancelled_sql
257
258 Given an object that contains fields from cust_main (say, from a JOINed
259 search; see httemplate/search/ for examples), returns the equivalent of the
260 corresponding FS::cust_main method, or "0" if this object is not linked to
261 a customer.
262
263 =cut
264
265 foreach my $sub (qw( prospect active inactive suspended cancelled )) {
266   eval "
267     sub ${sub}_sql {
268       my \$self = shift;
269       \$self->cust_linked
270         ? FS::cust_main::${sub}_sql(\$self)
271         : '0';
272       }
273   ";
274   die $@ if $@;
275 }
276
277 =item cust_search_sql
278
279 Returns a list of SQL WHERE fragments to search for parameters specified
280 in HASHREF.  Valid parameters are:
281
282 =over 4
283
284 =item agentnum
285
286 =item status
287
288 =item payby
289
290 =back
291
292 =cut
293
294 sub cust_search_sql {
295   my($class, $param) = @_;
296
297   if ( $DEBUG ) {
298     warn "$me cust_search_sql called with params: \n".
299          join("\n", map { "  $_: ". $param->{$_} } keys %$param ). "\n";
300   }
301
302   my @search = ();
303
304   if ( $param->{'agentnum'} && $param->{'agentnum'} =~ /^(\d+)$/ ) {
305     push @search, "cust_main.agentnum = $1";
306   }
307
308   #status (prospect active inactive suspended cancelled)
309   if ( grep { $param->{'status'} eq $_ } FS::cust_main->statuses() ) {
310     my $method = $param->{'status'}. '_sql';
311     push @search, $class->$method();
312   }
313
314   #payby
315   my @payby = ref($param->{'payby'})
316                 ? @{ $param->{'payby'} }
317                 : split(',', $param->{'payby'});
318   @payby = grep /^([A-Z]{4})$/, @payby;
319   if ( @payby ) {
320     push @search, 'cust_main.payby IN ('. join(',', map "'$_'", @payby). ')';
321   }
322
323   #here is the agent virtualization
324   push @search,
325     $FS::CurrentUser::CurrentUser->agentnums_sql( 'table' => 'cust_main' );
326   
327   return @search;
328
329 }
330
331 =back
332
333 =head1 BUGS
334
335 =head1 SEE ALSO
336
337 L<FS::cust_main>, L<FS::Record>
338
339 =cut
340
341 1;
342