signup page w/advertising source dropdown
[freeside.git] / FS / FS / ClientAPI / Signup.pm
1 package FS::ClientAPI::Signup;
2
3 use strict;
4 use Tie::RefHash;
5 use FS::Conf;
6 use FS::Record qw(qsearch qsearchs dbdef);
7 use FS::agent;
8 use FS::cust_main_county;
9 use FS::part_pkg;
10 use FS::svc_acct_pop;
11 use FS::cust_main;
12 use FS::cust_pkg;
13 use FS::svc_acct;
14 use FS::acct_snarf;
15 use FS::Msgcat qw(gettext);
16
17 use FS::ClientAPI; #hmm
18 FS::ClientAPI->register_handlers(
19   'Signup/signup_info'  => \&signup_info,
20   'Signup/new_customer' => \&new_customer,
21 );
22
23 sub signup_info {
24   #my $packet = shift;
25
26   my $conf = new FS::Conf;
27
28   use vars qw($signup_info); #cache for performance;
29   $signup_info ||= {
30
31     'cust_main_county' =>
32       [ map { $_->hashref } qsearch('cust_main_county', {}) ],
33
34     'agent' =>
35       [
36         map { $_->hashref }
37           qsearch('agent', dbdef->table('agent')->column('disabled')
38                              ? { 'disabled' => '' }
39                              : {}
40                  )
41       ],
42
43     'part_referral' =>
44       [
45         map { $_->hashref }
46           qsearch('part_referral',
47                     dbdef->table('part_referral')->column('disabled')
48                       ? { 'disabled' => '' }
49                       : {}
50                  )
51       ],
52
53     'agentnum2part_pkg' =>
54       {
55         map {
56           my $href = $_->pkgpart_hashref;
57           $_->agentnum =>
58             [
59               map { { 'payby' => [ $_->payby ], %{$_->hashref} } }
60                 grep { $_->svcpart('svc_acct') && $href->{ $_->pkgpart } }
61                   qsearch( 'part_pkg', { 'disabled' => '' } )
62             ];
63         } qsearch('agent', dbdef->table('agent')->column('disabled')
64                              ? { 'disabled' => '' }
65                              : {}
66                  )
67       },
68
69     'svc_acct_pop' => [ map { $_->hashref } qsearch('svc_acct_pop',{} ) ],
70
71     'security_phrase' => $conf->exists('security_phrase'),
72
73     'payby' => [ $conf->config('signup_server-payby') ],
74
75     'cvv_enabled' => defined dbdef->table('cust_main')->column('paycvv'),
76
77     'msgcat' => { map { $_=>gettext($_) } qw(
78       passwords_dont_match invalid_card unknown_card_type not_a
79     ) },
80
81     'statedefault' => $conf->config('statedefault') || 'CA',
82
83     'countrydefault' => $conf->config('countrydefault') || 'US',
84
85     'refnum' => $conf->config('signup_server-default_refnum'),
86
87   };
88
89   if (
90     $conf->config('signup_server-default_agentnum')
91     && !exists $signup_info->{'part_pkg'} #cache for performance
92   ) {
93     my $agentnum = $conf->config('signup_server-default_agentnum');
94     my $agent = qsearchs( 'agent', { 'agentnum' => $agentnum } )
95       or die "fatal: signup_server-default_agentnum $agentnum not found\n";
96     my $pkgpart_href = $agent->pkgpart_hashref;
97
98     $signup_info->{'part_pkg'} = [
99       #map { $_->hashref }
100       map { { 'payby' => [ $_->payby ], %{$_->hashref} } }
101         grep { $_->svcpart('svc_acct') && $pkgpart_href->{ $_->pkgpart } }
102           qsearch( 'part_pkg', { 'disabled' => '' } )
103     ];
104   }
105
106   $signup_info;
107
108 }
109
110 sub new_customer {
111   my $packet = shift;
112
113   my $conf = new FS::Conf;
114   
115   #things that aren't necessary in base class, but are for signup server
116     #return "Passwords don't match"
117     #  if $hashref->{'_password'} ne $hashref->{'_password2'}
118   return { 'error' => gettext('empty_password') }
119     unless $packet->{'_password'};
120   # a bit inefficient for large numbers of pops
121   return { 'error' => gettext('no_access_number_selected') }
122     unless $packet->{'popnum'} || !scalar(qsearch('svc_acct_pop',{} ));
123
124   #shares some stuff with htdocs/edit/process/cust_main.cgi... take any
125   # common that are still here and library them.
126   my $cust_main = new FS::cust_main ( {
127     #'custnum'          => '',
128     'agentnum'      => $packet->{agentnum}
129                        || $conf->config('signup_server-default_agentnum'),
130     'refnum'        => $packet->{refnum}
131                        || $conf->config('signup_server-default_refnum'),
132
133     map { $_ => $packet->{$_} } qw(
134       last first ss company address1 address2 city county state zip country
135       daytime night fax payby payinfo paycvv paydate payname referral_custnum
136       comments
137     ),
138
139   } );
140
141   return { 'error' => "Illegal payment type" }
142     unless grep { $_ eq $packet->{'payby'} }
143                 $conf->config('signup_server-payby');
144
145   $cust_main->payinfo($cust_main->daytime)
146     if $cust_main->payby eq 'LECB' && ! $cust_main->payinfo;
147
148   my @invoicing_list = split( /\s*\,\s*/, $packet->{'invoicing_list'} );
149
150   $packet->{'pkgpart'} =~ /^(\d+)$/ or '' =~ /^()$/;
151   my $pkgpart = $1;
152   return { 'error' => 'Please select a package' } unless $pkgpart; #msgcat
153
154   my $part_pkg =
155     qsearchs( 'part_pkg', { 'pkgpart' => $pkgpart } )
156       or return { 'error' => "WARNING: unknown pkgpart: $pkgpart" };
157   my $svcpart = $part_pkg->svcpart('svc_acct');
158
159   my $cust_pkg = new FS::cust_pkg ( {
160     #later#'custnum' => $custnum,
161     'pkgpart' => $packet->{'pkgpart'},
162   } );
163   my $error = $cust_pkg->check;
164   return { 'error' => $error } if $error;
165
166   my $svc_acct = new FS::svc_acct ( {
167     'svcpart'   => $svcpart,
168     map { $_ => $packet->{$_} }
169       qw( username _password sec_phrase popnum ),
170   } );
171
172   my @acct_snarf;
173   my $snarfnum = 1;
174   while ( length($packet->{"snarf_machine$snarfnum"}) ) {
175     my $acct_snarf = new FS::acct_snarf ( {
176       'machine'   => $packet->{"snarf_machine$snarfnum"},
177       'protocol'  => $packet->{"snarf_protocol$snarfnum"},
178       'username'  => $packet->{"snarf_username$snarfnum"},
179       '_password' => $packet->{"snarf_password$snarfnum"},
180     } );
181     $snarfnum++;
182     push @acct_snarf, $acct_snarf;
183   }
184   $svc_acct->child_objects( \@acct_snarf );
185
186   my $y = $svc_acct->setdefault; # arguably should be in new method
187   return { 'error' => $y } if $y && !ref($y);
188
189   $error = $svc_acct->check;
190   return { 'error' => $error } if $error;
191
192   use Tie::RefHash;
193   tie my %hash, 'Tie::RefHash';
194   %hash = ( $cust_pkg => [ $svc_acct ] );
195   #msgcat
196   $error = $cust_main->insert( \%hash, \@invoicing_list, 'noexport' => 1 );
197   return { 'error' => $error } if $error;
198
199   if ( $conf->exists('signup_server-realtime') ) {
200
201     #warn "[fs_signup_server] Billing customer...\n" if $Debug;
202
203     my $bill_error = $cust_main->bill;
204     #warn "[fs_signup_server] error billing new customer: $bill_error"
205     #  if $bill_error;
206
207     $cust_main->apply_payments;
208     $cust_main->apply_credits;
209
210     $bill_error = $cust_main->collect;
211     #warn "[fs_signup_server] error collecting from new customer: $bill_error"
212     #  if $bill_error;
213
214     if ( $cust_main->balance > 0 ) {
215
216       #this makes sense.  credit is "un-doing" the invoice
217       $cust_main->credit( $cust_main->balance, 'signup server decline' );
218       $cust_main->apply_credits;
219
220       #should check list for errors...
221       #$cust_main->suspend;
222       local $FS::svc_Common::noexport_hack = 1;
223       $cust_main->cancel('quiet'=>1);
224
225       return { 'error' => '_decline' };
226     }
227
228   }
229   $cust_main->reexport;
230
231   return { error => '' };
232
233 }
234
235 1;