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