add payment_info_renew_info method to ClientAPI/MyAccount and SG-equivalent previous_...
[freeside.git] / FS / FS / ClientAPI / SGNG.pm
1 #this stuff is SG-specific (i.e. multi-customer company username hack)
2
3 package FS::ClientAPI::SGNG;
4
5 use strict;
6 use vars qw( $cache $DEBUG );
7 use Time::Local qw(timelocal timelocal_nocheck);
8 use Business::CreditCard;
9 use FS::Record qw( qsearch qsearchs );
10 use FS::Conf;
11 use FS::cust_main;
12 use FS::cust_pkg;
13 use FS::ClientAPI::MyAccount; #qw( payment_info process_payment )
14
15 $DEBUG = 0;
16
17 sub _cache {
18   $cache ||= new FS::ClientAPI_SessionCache( {
19                'namespace' => 'FS::ClientAPI::MyAccount', #yes, share session_ids
20              } );
21 }
22
23 #this might almost be general-purpose
24 sub decompify_pkgs {
25   my $p = shift;
26
27   my $session = _cache->get($p->{'session_id'})
28     or return { 'error' => "Can't resume session" }; #better error message
29
30   my $custnum = $session->{'custnum'};
31
32   my $cust_main = qsearchs('cust_main', { 'custnum' => $custnum } )
33     or return { 'error' => "unknown custnum $custnum" };
34
35   return { 'error' => 'Not a complimentary customer' }
36     unless $cust_main->payby eq 'COMP';
37
38   my $paydate =
39     $cust_main->paydate =~ /^\S+$/ ? $cust_main->paydate : '2037-12-31';
40
41   my ($payyear,$paymonth,$payday) = split (/-/,$paydate);
42
43   my $date = timelocal(0,0,0,$payday,--$paymonth,$payyear);
44
45   foreach my $cust_pkg (
46     qsearch({ 'table'     => 'cust_pkg',
47               'hashref'   => { 'custnum' => $custnum,
48                                'bill'    => '',
49                              },
50               'extra_sql' => ' AND '. FS::cust_pkg->active_sql,
51            })
52   ) {
53     $cust_pkg->set('bill', $date);
54     my $error = $cust_pkg->replace;
55     return { 'error' => $error } if $error;
56   }
57
58   return { 'error' => '' };
59
60 }
61
62 #find old payment info
63 # (should work just like MyAccount::payment_info, except returns previous info
64 #  too)
65 # definitly sg-specific, no one else stores past customer records like this
66 sub previous_payment_info {
67   my $p = shift;
68
69   my $session = _cache->get($p->{'session_id'})
70     or return { 'error' => "Can't resume session" }; #better error message
71
72   my $payment_info = FS::ClientAPI::MyAccount::payment_info($p);
73
74   my $custnum = $session->{'custnum'};
75
76   my $cust_main = qsearchs('cust_main', { 'custnum' => $custnum } )
77     or return { 'error' => "unknown custnum $custnum" };
78
79   #?
80   return $payment_info if $cust_main->payby =~ /^(CARD|DCRD|CHEK|DCHK)$/;
81
82   foreach my $prev_cust_main (
83     reverse _previous_cust_main( 'custnum'       => $custnum, 
84                                  'username'      => $cust_main->company,
85                                  'with_payments' => 1,
86                                )
87   ) {
88
89     next unless $prev_cust_main->payby =~ /^(CARD|DCRD|CHEK|DCHK)$/;
90
91     if ( $prev_cust_main->payby =~ /^(CARD|DCRD)$/ ) {
92
93       #card expired?
94       my ($payyear,$paymonth,$payday) = split (/-/, $cust_main->paydate);
95
96       my $expdate = timelocal_nocheck(0,0,0,1,$paymonth,$payyear);
97
98       next if $expdate < time;
99
100     } elsif ( $prev_cust_main->payby =~ /^(CHEK|DCHK)$/ ) {
101
102       #any check?  or just skip these in favor of cards?
103
104     }
105
106     return { %$payment_info,
107              #$prev_cust_main->payment_info
108              _cust_main_payment_info( $prev_cust_main ),
109              'previous_custnum' => $prev_cust_main->custnum,
110            };
111
112   }
113
114   #still nothing?  return an error?
115   return $payment_info;
116
117 }
118
119 #this is really FS::cust_main::payment_info, but here for now
120 sub _cust_main_payment_info {
121   my $self = shift;
122
123   my %return = ();
124
125   $return{balance} = $self->balance;
126
127   $return{payname} = $self->payname
128                      || ( $self->first. ' '. $self->get('last') );
129
130   $return{$_} = $self->get($_) for qw(address1 address2 city state zip);
131
132   $return{payby} = $self->payby;
133   $return{stateid_state} = $self->stateid_state;
134
135   if ( $self->payby =~ /^(CARD|DCRD)$/ ) {
136     $return{card_type} = cardtype($self->payinfo);
137     $return{payinfo} = $self->paymask;
138
139     @return{'month', 'year'} = $self->paydate_monthyear;
140
141   }
142
143   if ( $self->payby =~ /^(CHEK|DCHK)$/ ) {
144     my ($payinfo1, $payinfo2) = split '@', $self->paymask;
145     $return{payinfo1} = $payinfo1;
146     $return{payinfo2} = $payinfo2;
147     $return{paytype}  = $self->paytype;
148     $return{paystate} = $self->paystate;
149
150   }
151
152   #doubleclick protection
153   my $_date = time;
154   $return{paybatch} = "webui-MyAccount-$_date-$$-". rand() * 2**32;
155
156   %return;
157
158 }
159
160 #find old cust_main records (with payments)
161 sub _previous_cust_main {
162
163   #safety check!  return nothing unless we're enabled explicitly
164   return () unless FS::Conf->new->exists('sg-multicustomer_hack');
165
166   my %opt = @_;
167   my $custnum  = $opt{'custnum'};
168   my $username = $opt{'username'};
169   
170   my %search = ();
171   if ( $opt{'with_payments'} ) {
172     $search{'extra_sql'} =
173       ' AND 0 < ( SELECT COUNT(*) FROM cust_pay
174                     WHERE cust_pay.custnum = cust_main.custnum
175                 )
176       ';
177   }
178
179   qsearch( {
180     'table'    => 'cust_main', 
181     'hashref'  => { 'company' => { op => 'ILIKE', value => $opt{'username'} },
182                     'custnum' => { op => '!=',    value => $opt{'custnum'}  },
183                   },
184     'order_by' => 'ORDER BY custnum',
185     %search,
186   } );
187
188 }
189
190 #since we could be passing masked old CC data, need to look that up and
191 #replace it (like regular process_payment does) w/info from old customer record
192 sub previous_process_payment {
193   my $p = shift;
194
195   return FS::ClientAPI::MyAccount::process_payment($p)
196     unless $p->{'previous_custnum'}
197         && (    ( $p->{'payby'} =~ /^(CARD|DCRD)$/ && $p->{'payinfo'}  =~ /x/i )
198              || ( $p->{'payby'} =~ /^(CHEK|DCHK)$/ && $p->{'payinfo1'} =~ /x/i )
199            );
200
201   my $session = _cache->get($p->{'session_id'})
202     or return { 'error' => "Can't resume session" }; #better error message
203
204   my $custnum = $session->{'custnum'};
205
206   my $cust_main = qsearchs('cust_main', { 'custnum' => $custnum } )
207     or return { 'error' => "unknown custnum $custnum" };
208
209   #make sure this is really a previous custnum of this customer
210   my @previous_cust_main =
211     grep { $_->custnum == $p->{'previous_custnum'} }
212          _previous_cust_main( 'custnum'       => $custnum, 
213                               'username'      => $cust_main->company,
214                               'with_payments' => 1,
215                             );
216
217   my $previous_cust_main = $previous_cust_main[0];
218
219   #causes problems with old data w/old masking method
220   #if $previous_cust_main->paymask eq $payinfo;
221   
222   if ( $p->{'payby'} =~ /^(CHEK|DCHK)$/ && $p->{'payinfo1'} =~ /x/i ) {
223     ( $p->{'payinfo1'}, $p->{'payinfo2'} ) =
224       split('@', $previous_cust_main->payinfo);
225   } elsif ( $p->{'payby'} =~ /^(CARD|DCRD)$/ && $p->{'payinfo'} =~ /x/i ) {
226     $p->{'payinfo'} = $previous_cust_main->payinfo;
227   }
228
229   FS::ClientAPI::MyAccount::process_payment($p);
230
231 }
232
233 sub previous_payment_info_renew_info {
234   my $p = shift;
235   my $renew_info   = renew_info($p);
236   my $payment_info = previous_payment_info($p);
237   return { %$renew_info,
238            %$payment_info,
239          };
240 }
241
242 sub previous_process_payment_order_pkg {
243   my $p = shift;
244
245   my $hr = previous_process_payment($p);
246   return $hr if $hr->{'error'};
247
248   order_pkg($p);
249 }
250
251 sub previous_process_payment_change_pkg {
252   my $p = shift;
253
254   my $hr = previous_process_payment($p);
255   return $hr if $hr->{'error'};
256
257   change_pkg($p);
258 }
259
260 sub previous_process_payment_order_renew {
261   my $p = shift;
262
263   my $hr = previous_process_payment($p);
264   return $hr if $hr->{'error'};
265
266   order_renew($p);
267 }
268
269 1;
270