1 package FS::ClientAPI::Signup;
6 use FS::Record qw(qsearch qsearchs dbdef);
7 use FS::Msgcat qw(gettext);
8 use FS::ClientAPI_SessionCache;
10 use FS::cust_main_county;
23 my $conf = new FS::Conf;
25 use vars qw($signup_info); #cache for performance;
28 [ map { $_->hashref } qsearch('cust_main_county', {}) ],
33 qsearch('agent', dbdef->table('agent')->column('disabled')
34 ? { 'disabled' => '' }
42 qsearch('part_referral',
43 dbdef->table('part_referral')->column('disabled')
44 ? { 'disabled' => '' }
49 'agentnum2part_pkg' =>
52 my $href = $_->pkgpart_hashref;
55 map { { 'payby' => [ $_->payby ], %{$_->hashref} } }
56 grep { $_->svcpart('svc_acct') && $href->{ $_->pkgpart } }
57 qsearch( 'part_pkg', { 'disabled' => '' } )
59 } qsearch('agent', dbdef->table('agent')->column('disabled')
60 ? { 'disabled' => '' }
65 'svc_acct_pop' => [ map { $_->hashref } qsearch('svc_acct_pop',{} ) ],
67 'emailinvoiceonly' => $conf->exists('emailinvoiceonly'),
69 'security_phrase' => $conf->exists('security_phrase'),
71 'payby' => [ $conf->config('signup_server-payby') ],
73 'cvv_enabled' => defined dbdef->table('cust_main')->column('paycvv'),
75 'ship_enabled' => defined dbdef->table('cust_main')->column('ship_last'),
77 'msgcat' => { map { $_=>gettext($_) } qw(
78 passwords_dont_match invalid_card unknown_card_type not_a empty_password illegal_or_empty_text
81 'statedefault' => $conf->config('statedefault') || 'CA',
83 'countrydefault' => $conf->config('countrydefault') || 'US',
85 'refnum' => $conf->config('signup_server-default_refnum'),
90 if ($conf->exists('signup_server-classnum2') || $conf->exists('signup_server-classnum3')) {
91 $signup_info->{optional_packages} = [];
93 if (my $classnum = $conf->config('signup_server-classnum2')) {
94 my @pkgs = map { $_->hashref } FS::Record::qsearch( 'part_pkg', { classnum => $classnum } );
95 push @{$signup_info->{optional_packages}}, \@pkgs;
98 if (my $classnum = $conf->config('signup_server-classnum3')) {
99 my @pkgs = map { $_->hashref } FS::Record::qsearch( 'part_pkg', { classnum => $classnum } );
100 push @{$signup_info->{optional_packages}}, \@pkgs;
105 my $agentnum = $conf->config('signup_server-default_agentnum');
108 if ( exists $packet->{'session_id'} ) {
109 my $cache = new FS::ClientAPI_SessionCache( {
110 'namespace' => 'FS::ClientAPI::Agent',
112 $session = $cache->get($packet->{'session_id'});
114 $agentnum = $session->{'agentnum'};
116 return { 'error' => "Can't resume session" }; #better error message
120 $signup_info->{'part_pkg'} = [];
122 if ( $packet->{'reg_code'} ) {
123 $signup_info->{'part_pkg'} =
124 [ map { { 'payby' => [ $_->payby ], %{$_->hashref} } }
125 grep { $_->svcpart('svc_acct') }
127 qsearchs( 'reg_code', { 'code' => $packet->{'reg_code'},
128 'agentnum' => $agentnum, } )
132 $signup_info->{'error'} = 'Unknown registration code'
133 unless @{ $signup_info->{'part_pkg'} };
135 } elsif ( $packet->{'promo_code'} ) {
137 $signup_info->{'part_pkg'} =
138 [ map { { 'payby' => [ $_->payby ], %{$_->hashref} } }
139 grep { $_->svcpart('svc_acct') }
140 qsearch( 'part_pkg', { 'promo_code' => {
142 value=>$packet->{'promo_code'}
144 'disabled' => '', } )
147 $signup_info->{'error'} = 'Unknown promotional code'
148 unless @{ $signup_info->{'part_pkg'} };
151 if ( $agentnum && ! @{ $signup_info->{'part_pkg'} } ) {
152 $signup_info->{'part_pkg'} = $signup_info->{'agentnum2part_pkg'}{$agentnum};
155 # delete $signup_info->{'part_pkg'};
159 my $agent_signup_info = { %$signup_info };
160 delete $agent_signup_info->{agentnum2part_pkg};
161 $agent_signup_info->{'agent'} = $session->{'agent'};
172 my $conf = new FS::Conf;
174 #things that aren't necessary in base class, but are for signup server
175 #return "Passwords don't match"
176 # if $hashref->{'_password'} ne $hashref->{'_password2'}
177 return { 'error' => gettext('empty_password') }
178 unless length($packet->{'_password'});
179 # a bit inefficient for large numbers of pops
180 return { 'error' => gettext('no_access_number_selected') }
181 unless $packet->{'popnum'} || !scalar(qsearch('svc_acct_pop',{} ));
184 if ( exists $packet->{'session_id'} ) {
185 my $cache = new FS::ClientAPI_SessionCache( {
186 'namespace' => 'FS::ClientAPI::Agent',
188 my $session = $cache->get($packet->{'session_id'});
190 $agentnum = $session->{'agentnum'};
192 return { 'error' => "Can't resume session" }; #better error message
195 $agentnum = $packet->{agentnum}
196 || $conf->config('signup_server-default_agentnum');
199 #shares some stuff with htdocs/edit/process/cust_main.cgi... take any
200 # common that are still here and library them.
201 my $cust_main = new FS::cust_main ( {
203 'agentnum' => $agentnum,
204 'refnum' => $packet->{refnum}
205 || $conf->config('signup_server-default_refnum'),
207 map { $_ => $packet->{$_} } qw(
209 last first ss company address1 address2
210 city county state zip country
213 ship_last ship_first ship_ss ship_company ship_address1 ship_address2
214 ship_city ship_county ship_state ship_zip ship_country
215 ship_daytime ship_night ship_fax
218 payinfo paycvv paydate payname
219 paystart_month paystart_year payissue
222 referral_custnum comments
227 return { 'error' => "Illegal payment type" }
228 unless grep { $_ eq $packet->{'payby'} }
229 $conf->config('signup_server-payby');
231 $cust_main->payinfo($cust_main->daytime)
232 if $cust_main->payby eq 'LECB' && ! $cust_main->payinfo;
234 my @invoicing_list = split( /\s*\,\s*/, $packet->{'invoicing_list'} );
236 $packet->{'pkgpart'} =~ /^(\d+)$/ or '' =~ /^()$/;
238 return { 'error' => 'Please select a package' } unless $pkgpart; #msgcat
241 qsearchs( 'part_pkg', { 'pkgpart' => $pkgpart } )
242 or return { 'error' => "WARNING: unknown pkgpart: $pkgpart" };
243 my $svcpart = $part_pkg->svcpart('svc_acct');
246 if ( $packet->{'reg_code'} ) {
247 $reg_code = qsearchs( 'reg_code', { 'code' => $packet->{'reg_code'},
248 'agentnum' => $agentnum, } )
249 or return { 'error' => 'Unknown registration code' };
252 my $cust_pkg = new FS::cust_pkg ( {
253 #later#'custnum' => $custnum,
254 'pkgpart' => $packet->{'pkgpart'},
255 'promo_code' => $packet->{'promo_code'},
256 'reg_code' => $packet->{'reg_code'},
258 #my $error = $cust_pkg->check;
259 #return { 'error' => $error } if $error;
261 my $svc_acct = new FS::svc_acct ( {
262 'svcpart' => $svcpart,
263 map { $_ => $packet->{$_} }
264 qw( username _password sec_phrase popnum ),
269 while ( exists($packet->{"snarf_machine$snarfnum"})
270 && length($packet->{"snarf_machine$snarfnum"}) ) {
271 my $acct_snarf = new FS::acct_snarf ( {
272 'machine' => $packet->{"snarf_machine$snarfnum"},
273 'protocol' => $packet->{"snarf_protocol$snarfnum"},
274 'username' => $packet->{"snarf_username$snarfnum"},
275 '_password' => $packet->{"snarf_password$snarfnum"},
278 push @acct_snarf, $acct_snarf;
280 $svc_acct->child_objects( \@acct_snarf );
282 my $y = $svc_acct->setdefault; # arguably should be in new method
283 return { 'error' => $y } if $y && !ref($y);
285 #$error = $svc_acct->check;
286 #return { 'error' => $error } if $error;
288 #setup a job dependancy to delay provisioning
289 my $placeholder = new FS::queue ( {
290 'job' => 'FS::ClientAPI::Signup::__placeholder',
291 'status' => 'locked',
293 my $error = $placeholder->insert;
294 return { 'error' => $error } if $error;
297 tie my %hash, 'Tie::RefHash';
298 %hash = ( $cust_pkg => [ $svc_acct ] );
300 $error = $cust_main->insert(
303 'depend_jobnum' => $placeholder->jobnum,
306 my $perror = $placeholder->delete;
307 $error .= " (Additionally, error removing placeholder: $perror)" if $perror;
308 return { 'error' => $error };
311 if ( $conf->exists('signup_server-realtime') ) {
313 #warn "[fs_signup_server] Billing customer...\n" if $Debug;
315 my $bill_error = $cust_main->bill;
316 #warn "[fs_signup_server] error billing new customer: $bill_error"
319 $cust_main->apply_payments;
320 $cust_main->apply_credits;
322 $bill_error = $cust_main->collect('realtime' => 1);
323 #warn "[fs_signup_server] error collecting from new customer: $bill_error"
326 if ( $cust_main->balance > 0 ) {
328 #this makes sense. credit is "un-doing" the invoice
329 $cust_main->credit( $cust_main->balance, 'signup server decline' );
330 $cust_main->apply_credits;
332 #should check list for errors...
333 #$cust_main->suspend;
334 local $FS::svc_Common::noexport_hack = 1;
335 $cust_main->cancel('quiet'=>1);
337 my $perror = $placeholder->depended_delete;
338 warn "error removing provisioning jobs after decline: $perror" if $perror;
340 $perror = $placeholder->delete;
341 warn "error removing placeholder after decline: $perror" if $perror;
344 return { 'error' => '_decline' };
350 $error = $reg_code->delete;
351 return { 'error' => $error } if $error;
354 $error = $placeholder->delete;
355 return { 'error' => $error } if $error;
357 return { error => '' };