create prospects through signup API, #39776
[freeside.git] / fs_selfservice / FS-SelfService / SelfService.pm
1 package FS::SelfService;
2
3 use strict;
4 use vars qw( $VERSION @ISA @EXPORT_OK $DEBUG
5              $skip_uid_check $dir $socket %autoload $tag );
6 use Exporter;
7 use Socket;
8 use FileHandle;
9 #use IO::Handle;
10 use IO::Select;
11 use Storable 2.09 qw(nstore_fd fd_retrieve);
12
13 $VERSION = '0.03';
14
15 @ISA = qw( Exporter );
16
17 $DEBUG = 0;
18
19 $dir = "/usr/local/freeside";
20 $socket =  "$dir/selfservice_socket";
21 $socket .= '.'.$tag if defined $tag && length($tag);
22
23 #maybe should ask ClientAPI for this list
24 %autoload = (
25   'passwd'                    => 'passwd/passwd',
26   'chfn'                      => 'passwd/passwd',
27   'chsh'                      => 'passwd/passwd',
28   'login_info'                => 'MyAccount/login_info',
29   'login_banner_image'        => 'MyAccount/login_banner_image',
30   'login'                     => 'MyAccount/login',
31   'logout'                    => 'MyAccount/logout',
32   'switch_acct'               => 'MyAccount/switch_acct',
33   'customer_info'             => 'MyAccount/customer_info',
34   'customer_info_short'       => 'MyAccount/customer_info_short',
35   'customer_recurring'        => 'MyAccount/customer_recurring',
36
37   'contact_passwd'            => 'MyAccount/contact/contact_passwd',
38   'list_contacts'             => 'MyAccount/contact/list_contacts',
39   'edit_contact'              => 'MyAccount/contact/edit_contact',
40   'delete_contact'            => 'MyAccount/contact/delete_contact',
41   'new_contact'               => 'MyAccount/contact/new_contact',
42
43   'billing_history'           => 'MyAccount/billing_history',
44   'edit_info'                 => 'MyAccount/edit_info',     #add to ss cgi!
45   'invoice'                   => 'MyAccount/invoice',
46   'invoice_pdf'               => 'MyAccount/invoice_pdf',
47   'legacy_invoice'            => 'MyAccount/legacy_invoice',
48   'legacy_invoice_pdf'        => 'MyAccount/legacy_invoice_pdf',
49   'invoice_logo'              => 'MyAccount/invoice_logo',
50   'list_invoices'             => 'MyAccount/list_invoices', #?
51   'cancel'                    => 'MyAccount/cancel',        #add to ss cgi!
52   'payment_info'              => 'MyAccount/payment_info',
53   'payment_info_renew_info'   => 'MyAccount/payment_info_renew_info',
54   'process_payment'           => 'MyAccount/process_payment',
55   'store_payment'             => 'MyAccount/store_payment',
56   'process_stored_payment'    => 'MyAccount/process_stored_payment',
57   'process_payment_order_pkg' => 'MyAccount/process_payment_order_pkg',
58   'process_payment_change_pkg' => 'MyAccount/process_payment_change_pkg',
59   'process_payment_order_renew' => 'MyAccount/process_payment_order_renew',
60   'process_prepay'            => 'MyAccount/process_prepay',
61   'realtime_collect'          => 'MyAccount/realtime_collect',
62   'list_pkgs'                 => 'MyAccount/list_pkgs',     #add to ss (added?)
63   'list_svcs'                 => 'MyAccount/list_svcs',     #add to ss (added?)
64   'list_svc_usage'            => 'MyAccount/list_svc_usage',   
65   'svc_status_html'           => 'MyAccount/svc_status_html',
66   'svc_status_hash'           => 'MyAccount/svc_status_hash',
67   'set_svc_status_hash'       => 'MyAccount/set_svc_status_hash',
68   'set_svc_status_listadd'    => 'MyAccount/set_svc_status_listadd',
69   'set_svc_status_listdel'    => 'MyAccount/set_svc_status_listdel',
70   'set_svc_status_vacationadd'=> 'MyAccount/set_svc_status_vacationadd',
71   'set_svc_status_vacationdel'=> 'MyAccount/set_svc_status_vacationdel',
72   'acct_forward_info'         => 'MyAccount/acct_forward_info',
73   'process_acct_forward'      => 'MyAccount/process_acct_forward',
74   'list_dsl_devices'          => 'MyAccount/list_dsl_devices',   
75   'add_dsl_device'            => 'MyAccount/add_dsl_device',   
76   'delete_dsl_device'         => 'MyAccount/delete_dsl_device',   
77   'port_graph'                => 'MyAccount/port_graph',   
78   'list_cdr_usage'            => 'MyAccount/list_cdr_usage',   
79   'list_support_usage'        => 'MyAccount/list_support_usage',   
80   'order_pkg'                 => 'MyAccount/order_pkg',     #add to ss cgi!
81   'change_pkg'                => 'MyAccount/change_pkg', 
82   'order_recharge'            => 'MyAccount/order_recharge',
83   'renew_info'                => 'MyAccount/renew_info',
84   'order_renew'               => 'MyAccount/order_renew',
85   'cancel_pkg'                => 'MyAccount/cancel_pkg',    #add to ss cgi!
86   'suspend_pkg'               => 'MyAccount/suspend_pkg',   #add to ss cgi!
87   'charge'                    => 'MyAccount/charge',        #?
88   'part_svc_info'             => 'MyAccount/part_svc_info',
89   'provision_acct'            => 'MyAccount/provision_acct',
90   'provision_phone'           => 'MyAccount/provision_phone',
91   'provision_pbx'             => 'MyAccount/provision_pbx',
92   'provision_external'        => 'MyAccount/provision_external',
93   'provision_forward'         => 'MyAccount/provision_forward',
94   'unprovision_svc'           => 'MyAccount/unprovision_svc',
95   'myaccount_passwd'          => 'MyAccount/myaccount_passwd',
96   'reset_passwd'              => 'MyAccount/reset_passwd',
97   'check_reset_passwd'        => 'MyAccount/check_reset_passwd',
98   'process_reset_passwd'      => 'MyAccount/process_reset_passwd',
99   'validate_passwd'           => 'MyAccount/validate_passwd',
100   'list_tickets'              => 'MyAccount/list_tickets',
101   'create_ticket'             => 'MyAccount/create_ticket',
102   'get_ticket'                => 'MyAccount/get_ticket',
103   'adjust_ticket_priority'    => 'MyAccount/adjust_ticket_priority',
104   'did_report'                => 'MyAccount/did_report',
105   'signup_info'               => 'Signup/signup_info',
106   'skin_info'                 => 'MyAccount/skin_info',
107   'access_info'               => 'MyAccount/access_info',
108   'domain_select_hash'        => 'Signup/domain_select_hash',  # expose?
109   'new_customer'              => 'Signup/new_customer',
110   'new_customer_minimal'      => 'Signup/new_customer_minimal',
111   'capture_payment'           => 'Signup/capture_payment',
112   'new_prospect'              => 'Signup/new_prospect',
113   #N/A 'clear_signup_cache'        => 'Signup/clear_cache',
114   'new_agent'                 => 'Agent/new_agent',
115   'agent_login'               => 'Agent/agent_login',
116   'agent_logout'              => 'Agent/agent_logout',
117   'agent_info'                => 'Agent/agent_info',
118   'agent_list_customers'      => 'Agent/agent_list_customers',
119   'check_username'            => 'Agent/check_username',
120   'suspend_username'          => 'Agent/suspend_username',
121   'unsuspend_username'        => 'Agent/unsuspend_username',
122   'mason_comp'                => 'MasonComponent/mason_comp',
123   'call_time'                 => 'PrepaidPhone/call_time',
124   'call_time_nanpa'           => 'PrepaidPhone/call_time_nanpa',
125   'phonenum_balance'          => 'PrepaidPhone/phonenum_balance',
126
127   'start_thirdparty'          => 'MyAccount/start_thirdparty',
128   'finish_thirdparty'         => 'MyAccount/finish_thirdparty',
129
130   'list_quotations'           => 'MyAccount/quotation/list_quotations',
131   'quotation_new'             => 'MyAccount/quotation/quotation_new',
132   'quotation_delete'          => 'MyAccount/quotation/quotation_delete',
133   'quotation_info'            => 'MyAccount/quotation/quotation_info',
134   'quotation_print'           => 'MyAccount/quotation/quotation_print',
135   'quotation_add_pkg'         => 'MyAccount/quotation/quotation_add_pkg',
136   'quotation_remove_pkg'      => 'MyAccount/quotation/quotation_remove_pkg',
137   'quotation_order'           => 'MyAccount/quotation/quotation_order',
138
139   'freesideinc_service'       => 'Freeside/freesideinc_service',
140
141 );
142 @EXPORT_OK = (
143   keys(%autoload),
144   qw( regionselector regionselector_hashref location_form
145       expselect popselector domainselector didselector
146     )
147 );
148
149 $ENV{'PATH'} ='/usr/bin:/usr/ucb:/bin';
150 $ENV{'SHELL'} = '/bin/sh';
151 $ENV{'IFS'} = " \t\n";
152 $ENV{'CDPATH'} = '';
153 $ENV{'ENV'} = '';
154 $ENV{'BASH_ENV'} = '';
155
156 #you can add BEGIN { $FS::SelfService::skip_uid_check = 1; } 
157 #if you grant appropriate permissions to whatever user
158 my $freeside_uid = scalar(getpwnam('freeside'));
159 die "not running as the freeside user\n"
160   if $> != $freeside_uid && ! $skip_uid_check;
161
162 -e $dir or die "FATAL: $dir doesn't exist!";
163 -d $dir or die "FATAL: $dir isn't a directory!";
164 -r $dir or die "FATAL: Can't read $dir as freeside user!";
165 -x $dir or die "FATAL: $dir not searchable (executable) as freeside user!";
166
167 foreach my $autoload ( keys %autoload ) {
168
169   my $eval =
170   "sub $autoload { ". '
171                    my $param;
172                    if ( ref($_[0]) ) {
173                      $param = shift;
174                    } else {
175                      #warn scalar(@_). ": ". join(" / ", @_);
176                      $param = { @_ };
177                    }
178
179                    $param->{_packet} = \''. $autoload{$autoload}. '\';
180
181                    simple_packet($param);
182                  }';
183
184   eval $eval;
185   die $@ if $@;
186
187 }
188
189 sub simple_packet {
190   my $packet = shift;
191   warn "sending ". $packet->{_packet}. " to server"
192     if $DEBUG;
193   socket(SOCK, PF_UNIX, SOCK_STREAM, 0) or die "socket: $!";
194   connect(SOCK, sockaddr_un($socket)) or die "connect to $socket: $!";
195   nstore_fd($packet, \*SOCK) or die "can't send packet: $!";
196   SOCK->flush;
197
198   #shoudl trap: Magic number checking on storable file failed at blib/lib/Storable.pm (autosplit into blib/lib/auto/Storable/fd_retrieve.al) line 337, at /usr/local/share/perl/5.6.1/FS/SelfService.pm line 71
199
200   #block until there is a message on socket
201 #  my $w = new IO::Select;
202 #  $w->add(\*SOCK);
203 #  my @wait = $w->can_read;
204
205   warn "reading message from server"
206     if $DEBUG;
207
208   my $return = fd_retrieve(\*SOCK) or die "error reading result: $!";
209   die $return->{'_error'} if defined $return->{_error} && $return->{_error};
210
211   warn "returning message to client"
212     if $DEBUG;
213
214   $return;
215 }
216
217 =head1 NAME
218
219 FS::SelfService - Freeside self-service API
220
221 =head1 SYNOPSIS
222
223   # password and shell account changes
224   use FS::SelfService qw(passwd chfn chsh);
225
226   # "my account" functionality
227   use FS::SelfService qw( login customer_info invoice cancel payment_info process_payment );
228
229   #new-style login with an email address and password
230   # can also be used for svc_acct login, set $emailaddress to username@domain
231   my $rv = login ( { 'email'    => $emailaddress,
232                      'password' => $password,
233                    },
234                  );
235   if ( $rv->{'error'} ) {
236     #handle login error...
237   } else {
238     #successful login
239     $session_id = $rv->{'session_id'};
240   }
241
242   #classic svc_acct-based login with separate username and password
243   my $rv = login( { 'username' => $username,
244                     'domain'   => $domain,
245                     'password' => $password,
246                   }
247                 );
248   if ( $rv->{'error'} ) {
249     #handle login error...
250   } else {
251     #successful login
252     $session_id = $rv->{'session_id'};
253   }
254
255   #svc_phone login with phone number and PIN
256   my $rv = login( { 'username' => $phone_number,
257                     'domain'   => 'svc_phone',
258                     'password' => $pin,
259                   }
260                 );
261   if ( $rv->{'error'} ) {
262     #handle login error...
263   } else {
264     #successful login
265     $session_id = $rv->{'session_id'};
266   }
267
268   my $customer_info = customer_info( { 'session_id' => $session_id } );
269
270   #payment_info and process_payment are available in 1.5+ only
271   my $payment_info = payment_info( { 'session_id' => $session_id } );
272
273   #!!! process_payment example
274
275   #!!! list_pkgs example
276
277   #!!! order_pkg example
278
279   #quoting a package, then ordering after confirmation
280
281   my $rv = quotation_new({ 'session_id' => $session_id });
282   my $qnum = $rv->{quotationnum};
283   #  add packages to the quotation
284   $rv = quotation_add_pkg({ 'session_id'   => $session_id,
285                             'quotationnum' => $qnum,
286                             'pkgpart'      => $pkgpart,
287                             'quantity'     => $quantity, # defaults to 1
288                           });
289   #  repeat until all packages are added
290   #  view the pricing information
291   $rv = quotation_info({ 'session_id'   => $session_id,
292                          'quotationnum' => $qnum,
293                       });
294   print "Total setup charges: ".$rv->{total_setup}."\n".
295         "Total recurring charges: ".$rv->{total_recur}."\n";
296   #  quotation_info also provides a detailed breakdown of charges, in
297   #  $rv->{sections}.
298
299   #  ask customer for confirmation, then:
300   $rv = quotation_order({ 'session_id'   => $session_id,
301                           'quotationnum' => $qnum,
302                         });
303
304   #!!! cancel_pkg example
305
306   # signup functionality
307   use FS::SelfService qw( signup_info new_customer new_customer_minimal );
308
309   my $signup_info = signup_info;
310
311   $rv = new_customer( {
312                         'first'            => $first,
313                         'last'             => $last,
314                         'company'          => $company,
315                         'address1'         => $address1,
316                         'address2'         => $address2,
317                         'city'             => $city,
318                         'state'            => $state,
319                         'zip'              => $zip,
320                         'country'          => $country,
321                         'daytime'          => $daytime,
322                         'night'            => $night,
323                         'fax'              => $fax,
324                         'payby'            => $payby,
325                         'payinfo'          => $payinfo,
326                         'paycvv'           => $paycvv,
327                         'paystart_month'   => $paystart_month
328                         'paystart_year'    => $paystart_year,
329                         'payissue'         => $payissue,
330                         'payip'            => $payip
331                         'paydate'          => $paydate,
332                         'payname'          => $payname,
333                         'invoicing_list'   => $invoicing_list,
334                         'referral_custnum' => $referral_custnum,
335                         'agentnum'         => $agentnum,
336                         'pkgpart'          => $pkgpart,
337
338                         'username'         => $username,
339                         '_password'        => $password,
340                         'popnum'           => $popnum,
341                         #OR
342                         'countrycode'      => 1,
343                         'phonenum'         => $phonenum,
344                         'pin'              => $pin,
345                       }
346                     );
347   
348   my $error = $rv->{'error'};
349   if ( $error eq '_decline' ) {
350     print_decline();
351   } elsif ( $error ) {
352     reprint_signup();
353   } else {
354     print_success();
355   }
356
357 =head1 DESCRIPTION
358
359 Use this API to implement your own client "self-service" module.
360
361 If you just want to customize the look of the existing "self-service" module,
362 see XXXX instead.
363
364 =head1 PASSWORD, GECOS, SHELL CHANGING FUNCTIONS
365
366 =over 4
367
368 =item passwd
369
370 Changes the password for an existing user in svc_acct.  Takes a hash
371 reference with the following keys:
372
373 =over 4
374
375 =item username
376
377 Username of the account (required)
378
379 =item domain
380
381 Domain of the account (required)
382
383 =item old_password
384
385 Old password (required)
386
387 =item new_password
388  
389 New password (required)
390
391 =item new_gecos
392
393 New gecos
394
395 =item new_shell
396
397 New Shell
398
399 =back 
400
401 =item chfn
402
403 =item chsh
404
405 =back
406
407 =head1 "MY ACCOUNT" FUNCTIONS
408
409 =over 4
410
411 =item login HASHREF
412
413 Creates a user session.  Takes a hash reference as parameter with the
414 following keys:
415
416 =over 4
417
418 =item email
419
420 Email address (username@domain), instead of username and domain.  Required for
421 contact-based self-service login, can also be used for svc_acct-based login.
422
423 =item username
424
425 Username
426
427 =item domain
428
429 Domain
430
431 =item password
432
433 Password
434
435 =back
436
437 Returns a hash reference with the following keys:
438
439 =over 4
440
441 =item error
442
443 Empty on success, or an error message on errors.
444
445 =item session_id
446
447 Session identifier for successful logins
448
449 =back
450
451 =item customer_info HASHREF
452
453 Returns general customer information.
454
455 Takes a hash reference as parameter with a single key: B<session_id>
456
457 Returns a hash reference with the following keys:
458
459 =over 4
460
461 =item name
462
463 Customer name
464
465 =item balance
466
467 Balance owed
468
469 =item open
470
471 Array reference of hash references of open inoices.  Each hash reference has
472 the following keys: invnum, date, owed
473
474 =item small_custview
475
476 An HTML fragment containing shipping and billing addresses.
477
478 =item The following fields are also returned
479
480 first last company address1 address2 city county state zip country daytime night fax ship_first ship_last ship_company ship_address1 ship_address2 ship_city ship_state ship_zip ship_country ship_daytime ship_night ship_fax payby payinfo payname month year invoicing_list postal_invoicing
481
482 =back
483
484 =item customer_recurring HASHREF
485
486 Takes a hash reference as parameter with a single key B<session_id>
487 or keys B<agent_session_id> and B<custnum>.
488
489 Returns a hash reference with the keys error, custnum and display_recurring.
490
491 display_recurring is an arrayref of hashrefs with the following keys:
492
493 =over 4
494
495 =item freq
496
497 frequency of charge, in months unless units are specified
498
499 =item freq_pretty
500
501 frequency of charge, suitable for display
502
503 =item amount
504
505 amount charged at this frequency
506
507 =back
508
509 =item edit_info HASHREF
510
511 Takes a hash reference as parameter with any of the following keys:
512
513 first last company address1 address2 city county state zip country daytime night fax ship_first ship_last ship_company ship_address1 ship_address2 ship_city ship_state ship_zip ship_country ship_daytime ship_night ship_fax payby payinfo paycvv payname month year invoicing_list postal_invoicing
514
515 If a field exists, the customer record is updated with the new value of that
516 field.  If a field does not exist, that field is not changed on the customer
517 record.
518
519 Returns a hash reference with a single key, B<error>, empty on success, or an
520 error message on errors
521
522 =item invoice HASHREF
523
524 Returns an invoice.  Takes a hash reference as parameter with two keys:
525 session_id and invnum
526
527 Returns a hash reference with the following keys:
528
529 =over 4
530
531 =item error
532
533 Empty on success, or an error message on errors
534
535 =item invnum
536
537 Invoice number
538
539 =item invoice_text
540
541 Invoice text
542
543 =back
544
545 =item list_invoices HASHREF
546
547 Returns a list of all customer invoices.  Takes a hash reference with a single
548 key, session_id.
549
550 Returns a hash reference with the following keys:
551
552 =over 4
553
554 =item error
555
556 Empty on success, or an error message on errors
557
558 =item invoices
559
560 Reference to array of hash references with the following keys:
561
562 =over 4
563
564 =item invnum
565
566 Invoice ID
567
568 =item _date
569
570 Invoice date, in UNIX epoch time
571
572 =back
573
574 =back
575
576 =item cancel HASHREF
577
578 Cancels this customer.
579
580 Takes a hash reference as parameter with a single key: B<session_id>
581
582 Returns a hash reference with a single key, B<error>, which is empty on
583 success or an error message on errors.
584
585 =item payment_info HASHREF
586
587 Returns information that may be useful in displaying a payment page.
588
589 Takes a hash reference as parameter with a single key: B<session_id>.
590
591 Returns a hash reference with the following keys:
592
593 =over 4
594
595 =item error
596
597 Empty on success, or an error message on errors
598
599 =item balance
600
601 Balance owed
602
603 =item payname
604
605 Exact name on credit card (CARD/DCRD)
606
607 =item address1
608
609 Address line one
610
611 =item address2
612
613 Address line two
614
615 =item city
616
617 City
618
619 =item state
620
621 State
622
623 =item zip
624
625 Zip or postal code
626
627 =item payby
628
629 Customer's current default payment type.
630
631 =item card_type
632
633 For CARD/DCRD payment types, the card type (Visa card, MasterCard, Discover card, American Express card, etc.)
634
635 =item payinfo
636
637 For CARD/DCRD payment types, the card number
638
639 =item month
640
641 For CARD/DCRD payment types, expiration month
642
643 =item year
644
645 For CARD/DCRD payment types, expiration year
646
647 =item cust_main_county
648
649 County/state/country data - array reference of hash references, each of which has the fields of a cust_main_county record (see L<FS::cust_main_county>).  Note these are not FS::cust_main_county objects, but hash references of columns and values.
650
651 =item states
652
653 Array reference of all states in the current default country.
654
655 =item card_types
656
657 Hash reference of card types; keys are card types, values are the exact strings
658 passed to the process_payment function
659
660 =cut
661
662 #this doesn't actually work yet
663 #
664 #=item paybatch
665 #
666 #Unique transaction identifier (prevents multiple charges), passed to the
667 #process_payment function
668
669 =back
670
671 =item process_payment HASHREF
672
673 Processes a payment and possible change of address or payment type.  Takes a
674 hash reference as parameter with the following keys:
675
676 =over 4
677
678 =item session_id
679
680 Session identifier
681
682 =item amount
683
684 Amount
685
686 =item save
687
688 If true, address and card information entered will be saved for subsequent
689 transactions.
690
691 =item auto
692
693 If true, future credit card payments will be done automatically (sets payby to
694 CARD).  If false, future credit card payments will be done on-demand (sets
695 payby to DCRD).  This option only has meaning if B<save> is set true.  
696
697 =item payname
698
699 Name on card
700
701 =item address1
702
703 Address line one
704
705 =item address2
706
707 Address line two
708
709 =item city
710
711 City
712
713 =item state
714
715 State
716
717 =item zip
718
719 Zip or postal code
720
721 =item country
722
723 Two-letter country code
724
725 =item payinfo
726
727 Card number
728
729 =item month
730
731 Card expiration month
732
733 =item year
734
735 Card expiration year
736
737 =cut
738
739 #this doesn't actually work yet
740 #
741 #=item paybatch
742 #
743 #Unique transaction identifier, returned from the payment_info function.
744 #Prevents multiple charges.
745
746 =back
747
748 Returns a hash reference with a single key, B<error>, empty on success, or an
749 error message on errors.
750
751 =item process_payment_order_pkg
752
753 Combines the B<process_payment> and B<order_pkg> functions in one step.  If the
754 payment processes sucessfully, the package is ordered.  Takes a hash reference
755 as parameter with the keys of both methods.
756
757 Returns a hash reference with a single key, B<error>, empty on success, or an
758 error message on errors.
759
760 =item process_payment_change_pkg
761
762 Combines the B<process_payment> and B<change_pkg> functions in one step.  If the
763 payment processes sucessfully, the package is ordered.  Takes a hash reference
764 as parameter with the keys of both methods.
765
766 Returns a hash reference with a single key, B<error>, empty on success, or an
767 error message on errors.
768
769
770 =item process_payment_order_renew
771
772 Combines the B<process_payment> and B<order_renew> functions in one step.  If
773 the payment processes sucessfully, the renewal is processed.  Takes a hash
774 reference as parameter with the keys of both methods.
775
776 Returns a hash reference with a single key, B<error>, empty on success, or an
777 error message on errors.
778
779 =item list_pkgs
780
781 Returns package information for this customer.  For more detail on services,
782 see L</list_svcs>.
783
784 Takes a hash reference as parameter with a single key: B<session_id>
785
786 Returns a hash reference containing customer package information.  The hash reference contains the following keys:
787
788 =over 4
789
790 =item custnum
791
792 Customer number
793
794 =item error
795
796 Empty on success, or an error message on errors.
797
798 =item cust_pkg HASHREF
799
800 Array reference of hash references, each of which has the fields of a cust_pkg
801 record (see L<FS::cust_pkg>) as well as the fields below.  Note these are not
802 the internal FS:: objects, but hash references of columns and values.
803
804 =over 4
805
806 =item part_pkg fields
807
808 All fields of part_pkg for this specific cust_pkg (be careful with this
809 information - it may reveal more about your available packages than you would
810 like users to know in aggregate) 
811
812 =cut
813
814 #XXX pare part_pkg fields down to a more secure subset
815
816 =item part_svc
817
818 An array of hash references indicating information on unprovisioned services
819 available for provisioning for this specific cust_pkg.  Each has the following
820 keys:
821
822 =over 4
823
824 =item part_svc fields
825
826 All fields of part_svc (be careful with this information - it may reveal more
827 about your available packages than you would like users to know in aggregate) 
828
829 =cut
830
831 #XXX pare part_svc fields down to a more secure subset
832
833 =back
834
835 =item cust_svc
836
837 An array of hash references indicating information on the customer services
838 already provisioned for this specific cust_pkg.  Each has the following keys:
839
840 =over 4
841
842 =item label
843
844 Array reference with three elements: The first element is the name of this service.  The second element is a meaningful user-specific identifier for the service (i.e. username, domain or mail alias).  The last element is the table name of this service.
845
846 =back
847
848 =item svcnum
849
850 Primary key for this service
851
852 =item svcpart
853
854 Service definition (see L<FS::part_svc>)
855
856 =item pkgnum
857
858 Customer package (see L<FS::cust_pkg>)
859
860 =item overlimit
861
862 Blank if the service is not over limit, or the date the service exceeded its usage limit (as a UNIX timestamp).
863
864 =back
865
866 =back
867
868 =item list_svcs
869
870 Returns service information for this customer.
871
872 Takes a hash reference as parameter with a single key: B<session_id>
873
874 Returns a hash reference containing customer package information.  The hash reference contains the following keys:
875
876 =over 4
877
878 =item custnum
879
880 Customer number
881
882 =item svcs
883
884 An array of hash references indicating information on all of this customer's
885 services.  Each has the following keys:
886
887 =over 4
888
889 =item svcnum
890
891 Primary key for this service
892
893 =item label
894
895 Name of this service
896
897 =item value
898
899 Meaningful user-specific identifier for the service (i.e. username, domain, or
900 mail alias).
901
902 =back
903
904 Account (svc_acct) services also have the following keys:
905
906 =over 4
907
908 =item username
909
910 Username
911
912 =item email
913
914 username@domain
915
916 =item seconds
917
918 Seconds remaining
919
920 =item upbytes
921
922 Upload bytes remaining
923
924 =item downbytes
925
926 Download bytes remaining
927
928 =item totalbytes
929
930 Total bytes remaining
931
932 =item recharge_amount
933
934 Cost of a recharge
935
936 =item recharge_seconds
937
938 Number of seconds gained by recharge
939
940 =item recharge_upbytes
941
942 Number of upload bytes gained by recharge
943
944 =item recharge_downbytes
945
946 Number of download bytes gained by recharge
947
948 =item recharge_totalbytes
949
950 Number of total bytes gained by recharge
951
952 =back
953
954 =back
955
956 =item order_pkg
957
958 Orders a package for this customer.
959
960 If signup_server-realtime is set, bills the new package, attemps to collect
961 payment and (for auto-payment customers) cancels the package if the payment is
962 declined.
963
964 Takes a hash reference as parameter with the following keys:
965
966 =over 4
967
968 =item session_id
969
970 Session identifier
971
972 =item pkgpart
973
974 Package to order (see L<FS::part_pkg>).
975
976 =item quantity
977
978 Quantity for this package order (default 1).
979
980 =item run_bill_events
981
982 If true, runs billing events for the customer after ordering and billing the
983 package (signup_server-realtime must be set).
984
985 =item locationnum
986
987 Optional locationnum for this package order, for existing locations.
988
989 Or, for new locations, pass the following fields: address1*, address2, city*,
990 county, state*, zip*, country.  (* = required in this case)
991
992 (None of this is required at all if you are just ordering a package
993 at the customer's existing default service location.)
994
995 =item address1
996
997 =item address2
998
999 =item city
1000
1001 =item county
1002
1003 =item state
1004
1005 =item zip
1006
1007 =item country
1008
1009 =item svcpart
1010
1011 Service to order (see L<FS::part_svc>).
1012
1013 Normally optional; required only to provision a non-svc_acct service, or if the
1014 package definition does not contain one svc_acct service definition with
1015 quantity 1 (it may contain others with quantity >1).  A svcpart of "none" can
1016 also be specified to indicate that no initial service should be provisioned.
1017
1018 =back
1019
1020 Fields used when provisioning an svc_acct service:
1021
1022 =over 4
1023
1024 =item username
1025
1026 Username
1027
1028 =item _password
1029
1030 Password
1031
1032 =item sec_phrase
1033
1034 Optional security phrase
1035
1036 =item popnum
1037
1038 Optional Access number number
1039
1040 =back
1041
1042 Fields used when provisioning an svc_domain service:
1043
1044 =over 4
1045
1046 =item domain
1047
1048 Domain
1049
1050 =back
1051
1052 Fields used when provisioning an svc_phone service:
1053
1054 =over 4
1055
1056 =item phonenum
1057
1058 Phone number
1059
1060 =item pin
1061
1062 Voicemail PIN
1063
1064 =item sip_password
1065
1066 SIP password
1067
1068 =back
1069
1070 Fields used when provisioning an svc_external service:
1071
1072 =over 4
1073
1074 =item id
1075
1076 External numeric ID.
1077
1078 =item title
1079
1080 External text title.
1081
1082 =back
1083
1084 Fields used when provisioning an svc_pbx service:
1085
1086 =over 4
1087
1088 =item id
1089
1090 Numeric ID.
1091
1092 =item name
1093
1094 Text name.
1095
1096 =back
1097
1098 Returns a hash reference with a single key, B<error>, empty on success, or an
1099 error message on errors.  The special error '_decline' is returned for
1100 declined transactions.
1101
1102 =item change_pkg
1103
1104 Changes a package for this customer.
1105
1106 Takes a hash reference as parameter with the following keys:
1107
1108 =over 4
1109
1110 =item session_id
1111
1112 Session identifier
1113
1114 =item pkgnum
1115
1116 Existing customer package.
1117
1118 =item pkgpart
1119
1120 New package to order (see L<FS::part_pkg>).
1121
1122 =item quantity
1123
1124 Quantity for this package order (default 1).
1125
1126 =back
1127
1128 Returns a hash reference with the following keys:
1129
1130 =over 4
1131
1132 =item error
1133
1134 Empty on success, or an error message on errors.  
1135
1136 =item pkgnum
1137
1138 On success, the new pkgnum
1139
1140 =back
1141
1142
1143 =item renew_info
1144
1145 Provides useful info for early renewals.
1146
1147 Takes a hash reference as parameter with the following keys:
1148
1149 =over 4
1150
1151 =item session_id
1152
1153 Session identifier
1154
1155 =back
1156
1157 Returns a hash reference.  On errors, it contains a single key, B<error>, with
1158 the error message.  Otherwise, contains a single key, B<dates>, pointing to
1159 an array refernce of hash references.  Each hash reference contains the
1160 following keys:
1161
1162 =over 4
1163
1164 =item bill_date
1165
1166 (Future) Bill date.  Indicates a future date for which billing could be run.
1167 Specified as an integer UNIX timestamp.  Pass this value to the B<order_renew>
1168 function.
1169
1170 =item bill_date_pretty
1171
1172 (Future) Bill date as a human-readable string.  (Convenience for display;
1173 subject to change, so best not to parse for the date.)
1174
1175 =item amount
1176
1177 Base amount which will be charged if renewed early as of this date.
1178
1179 =item renew_date
1180
1181 Renewal date; i.e. even-futher future date at which the customer will be paid
1182 through if the early renewal is completed with the given B<bill-date>.
1183 Specified as an integer UNIX timestamp.
1184
1185 =item renew_date_pretty
1186
1187 Renewal date as a human-readable string.  (Convenience for display;
1188 subject to change, so best not to parse for the date.)
1189
1190 =item pkgnum
1191
1192 Package that will be renewed.
1193
1194 =item expire_date
1195
1196 Expiration date of the package that will be renewed.
1197
1198 =item expire_date_pretty
1199
1200 Expiration date of the package that will be renewed, as a human-readable
1201 string.  (Convenience for display; subject to change, so best not to parse for
1202 the date.)
1203
1204 =back
1205
1206 =item order_renew
1207
1208 Renews this customer early; i.e. runs billing for this customer in advance.
1209
1210 Takes a hash reference as parameter with the following keys:
1211
1212 =over 4
1213
1214 =item session_id
1215
1216 Session identifier
1217
1218 =item date
1219
1220 Integer date as returned by the B<renew_info> function, indicating the advance
1221 date for which to run billing.
1222
1223 =back
1224
1225 Returns a hash reference with a single key, B<error>, empty on success, or an
1226 error message on errors.
1227
1228 =item cancel_pkg
1229
1230 Cancels a package for this customer.
1231
1232 Takes a hash reference as parameter with the following keys:
1233
1234 =over 4
1235
1236 =item session_id
1237
1238 Session identifier
1239
1240 =item pkgpart
1241
1242 pkgpart of package to cancel
1243
1244 =item date
1245
1246 Optional date, for future cancellation (expiration) instead of immediate
1247 cancellation.  Specified as an integer UNIX timestamp ("epoch time").
1248
1249 =back
1250
1251 Returns a hash reference with a single key, B<error>, empty on success, or an
1252 error message on errors.
1253
1254 =item provision_acct 
1255
1256 Provisions an account (svc_acct).
1257
1258 Takes a hash reference as parameter with the following keys:
1259
1260 =over 4
1261
1262 =item session_id
1263
1264 Session identifier
1265
1266 =item pkgnum
1267
1268 pkgnum of package into which this service is provisioned
1269
1270 =item svcpart
1271
1272 svcpart or service definition to provision
1273
1274 =item username
1275
1276 =item domsvc
1277
1278 =item _password
1279
1280 =back
1281
1282 =item provision_phone
1283
1284 Provisions a phone number (svc_phone).
1285
1286 Takes a hash reference as parameter with the following keys:
1287
1288 =over 4
1289
1290 =item session_id
1291
1292 Session identifier
1293
1294 =item pkgnum
1295
1296 pkgnum of package into which this service is provisioned
1297
1298 =item svcpart
1299
1300 svcpart or service definition to provision
1301
1302 =item countrycode
1303
1304 =item phonenum
1305
1306 =item address1
1307
1308 =item address2
1309
1310 =item city
1311
1312 =item county
1313
1314 =item state
1315
1316 =item zip
1317
1318 =item country
1319
1320 E911 Address (optional)
1321
1322 =back
1323
1324 =item provision_pbx
1325
1326 Provisions a customer PBX (svc_pbx).
1327
1328 Takes a hash reference as parameter with the following keys:
1329
1330 =over 4
1331
1332 =item session_id
1333
1334 Session identifier
1335
1336 =item pkgnum
1337
1338 pkgnum of package into which this service is provisioned
1339
1340 =item svcpart
1341
1342 svcpart or service definition to provision
1343
1344 =item id
1345
1346 =item title
1347
1348 =item max_extensions
1349
1350 =item max_simultaneous
1351
1352 =item ip_addr
1353
1354 =back
1355
1356 =item provision_external
1357
1358 Provisions an external service (svc_external).
1359
1360 Takes a hash reference as parameter with the following keys:
1361
1362 =over 4
1363
1364 =item session_id
1365
1366 Session identifier
1367
1368 =item pkgnum
1369
1370 pkgnum of package into which this service is provisioned
1371
1372 =item svcpart
1373
1374 svcpart or service definition to provision
1375
1376 =item id
1377
1378 =item title
1379
1380 =back
1381
1382 =back
1383
1384 =head2 "MY ACCOUNT" CONTACT FUNCTIONS
1385
1386 =over 4
1387
1388 =item contact_passwd
1389
1390 Changes the password for the currently-logged in contact.
1391
1392 Takes a hash reference as parameter with the following keys:
1393
1394 =over 4
1395
1396 =item session_id
1397
1398 =item new_password
1399
1400 =back
1401
1402 Returns a hash reference with a single parameter, B<error>, which contains an
1403 error message, or empty on success.
1404
1405 =item list_contacts
1406
1407 Takes a hash reference as parameter with a single key, B<session_id>.
1408
1409 Returns a hash reference with two parameters: B<error>, which contains an error
1410 message, or empty on success, and B<contacts>, a list of contacts.
1411
1412 B<contacts> is an array reference of hash references (i.e. an array of structs,
1413  in XML-RPC).  Each hash reference (struct) has the following keys:
1414
1415 =over 4
1416
1417 =item contactnum
1418
1419 =item class
1420
1421 Contact class name (contact type).
1422
1423 =item first
1424
1425 First name
1426
1427 =item last
1428
1429 Last name
1430
1431 =item title
1432
1433 Position ("Director of Silly Walks"), NOT honorific ("Mr." or "Mrs.")
1434
1435 =item emailaddress
1436
1437 Comma-separated list of email addresses
1438
1439 =item comment
1440
1441 =item selfservice_access
1442
1443 Y when enabled
1444
1445 =back
1446
1447 =item edit_contact
1448
1449 Updates information for the currently-logged in contact, or (optionally) the
1450 specified contact.
1451
1452 Takes a hash reference as parameter with the following keys:
1453
1454 =over 4
1455
1456 =item session_id
1457
1458 =item contactnum
1459
1460 If already logged in as a contact, this is optional.
1461
1462 =item first
1463
1464 =item last
1465
1466 =item emailaddress
1467
1468 =back
1469
1470 Returns a hash reference with a single parameter, B<error>, which contains an
1471 error message, or empty on success.
1472
1473 =item new_contact
1474
1475 Creates a new contact.
1476
1477 Takes a hash reference as parameter with the following keys:
1478
1479 =over 4
1480
1481 =item session_id
1482
1483 =item first
1484
1485 =item last
1486
1487 =item emailaddress
1488
1489 =item classnum
1490
1491 Optional contact classnum (TODO: or name)
1492
1493 =item comment
1494
1495 =item selfservice_access
1496
1497 Y to enable self-service access
1498
1499 =item _password
1500
1501 =back
1502
1503 Returns a hash reference with a single parameter, B<error>, which contains an
1504 error message, or empty on success.
1505
1506 =item delete_contact
1507
1508 Deletes a contact.  (Note: Cannot at this time delete the currently-logged in
1509 contact.)
1510
1511 Takes a hash reference as parameter with the following keys:
1512
1513 =over 4
1514
1515 =item session_id
1516
1517 =item contactnum
1518
1519 =back
1520
1521 Returns a hash reference with a single parameter, B<error>, which contains an
1522 error message, or empty on success.
1523
1524 =back
1525
1526 =head2 "MY ACCOUNT" QUOTATION FUNCTIONS
1527
1528 All of these functions require the user to be logged in, and the 'session_id'
1529 key to be included in the argument hashref.`
1530
1531 =over 4
1532
1533 =item list_quotations HASHREF
1534
1535 Returns a hashref listing this customer's active self-service quotations.
1536 Contents are:
1537
1538 =over 4
1539
1540 =item quotations
1541
1542 an arrayref containing an element for each quotation.
1543
1544 =item quotationnum
1545
1546 the primary key
1547
1548 =item _date
1549
1550 the date it was started
1551
1552 =item num_pkgs
1553
1554 the number of packages
1555
1556 =item total_setup
1557
1558 the sum of setup fees
1559
1560 =item total_recur
1561
1562 the sum of recurring charges
1563
1564 =back
1565
1566 =item quotation_new HASHREF
1567
1568 Creates an empty quotation and returns a hashref containing 'quotationnum',
1569 the primary key of the new quotation.
1570
1571 =item quotation_delete HASHREF
1572
1573 Disables (does not really delete) a quotation. Takes the following arguments:
1574
1575 =over 4
1576
1577 =item session_id
1578
1579 =item quotationnum - the quotation to delete
1580
1581 =back
1582
1583 Returns 'error' => a string, which will be empty on success.
1584
1585 =item quotation_info HASHREF
1586
1587 Returns total and detailed pricing information on a quotation.
1588
1589 Takes the following arguments:
1590
1591 =over 4
1592
1593 =item session_id
1594
1595 =item quotationnum - the quotation to return
1596
1597 =back
1598
1599 Returns a hashref containing:
1600
1601 - total_setup, the total of setup fees (and their taxes)
1602 - total_recur, the total of all recurring charges (and their taxes)
1603 - sections, an arrayref containing an element for each quotation section.
1604   - description, a line of text describing the group of charges
1605   - subtotal, the total of charges in this group (if appropriate)
1606   - detail_items, an arrayref of line items
1607     - pkgnum, the reference number of the package
1608     - description, the package name (or tax name)
1609     - quantity
1610     - amount, the amount charged
1611     If the detail item represents a subtotal, it will instead contain:
1612     - total_item: description of the subtotal
1613     - total_amount: the subtotal amount
1614
1615
1616 =item quotation_print HASHREF
1617
1618 Renders the quotation as HTML or PDF. Takes the following arguments:
1619
1620 =over 4
1621
1622 =item session_id
1623
1624 =item quotationnum - the quotation to return
1625
1626 =item format - 'html' or 'pdf'
1627
1628 =back
1629
1630 Returns a hashref containing 'document', the contents of the file.
1631
1632 =item quotation_add_pkg HASHREF
1633
1634 Adds a package to a quotation. Takes the following arguments:
1635
1636 =over 4
1637
1638 =item session_id
1639
1640 =item pkgpart - the package to add
1641
1642 =item quotationnum - the quotation to add it to
1643
1644 =item quantity - the package quantity (defaults to 1)
1645
1646 =item address1, address2, city, state, zip, country - address fields to set
1647 the service location
1648
1649 =back
1650
1651 Returns 'error' => a string, which will be empty on success.
1652
1653 =item quotation_remove_pkg HASHREF
1654
1655 Removes a package from a quotation. Takes the following arguments:
1656
1657 =over 4
1658
1659 =item session_id
1660
1661 =item pkgnum - the primary key (quotationpkgnum) of the package to remove
1662
1663 =item quotationnum - the quotation to remove it from
1664
1665 =back
1666
1667 Returns 'error' => a string, which will be empty on success.
1668
1669 =item quotation_order HASHREF
1670
1671 Converts the packages in a quotation into real packages. Takes the following
1672 arguments:
1673
1674 Takes the following arguments:
1675
1676 =over 4
1677
1678 =item session_id
1679
1680 =item quotationnum - the quotation to order
1681
1682 =back
1683
1684 =back
1685
1686 =head1 SIGNUP FUNCTIONS
1687
1688 =over 4
1689
1690 =item signup_info HASHREF
1691
1692 Takes a hash reference as parameter with the following keys:
1693
1694 =over 4
1695
1696 =item session_id - Optional agent/reseller interface session
1697
1698 =back
1699
1700 Returns a hash reference containing information that may be useful in
1701 displaying a signup page.  The hash reference contains the following keys:
1702
1703 =over 4
1704
1705 =item cust_main_county
1706
1707 County/state/country data - array reference of hash references, each of which has the fields of a cust_main_county record (see L<FS::cust_main_county>).  Note these are not FS::cust_main_county objects, but hash references of columns and values.
1708
1709 =item part_pkg
1710
1711 Available packages - array reference of hash references, each of which has the fields of a part_pkg record (see L<FS::part_pkg>).  Each hash reference also has an additional 'payby' field containing an array reference of acceptable payment types specific to this package (see below and L<FS::part_pkg/payby>).  Note these are not FS::part_pkg objects, but hash references of columns and values.  Requires the 'signup_server-default_agentnum' configuration value to be set, or
1712 an agentnum specified explicitly via reseller interface session_id in the
1713 options.
1714
1715 =item agent
1716
1717 Array reference of hash references, each of which has the fields of an agent record (see L<FS::agent>).  Note these are not FS::agent objects, but hash references of columns and values.
1718
1719 =item agentnum2part_pkg
1720
1721 Hash reference; keys are agentnums, values are array references of available packages for that agent, in the same format as the part_pkg arrayref above.
1722
1723 =item svc_acct_pop
1724
1725 Access numbers - array reference of hash references, each of which has the fields of an svc_acct_pop record (see L<FS::svc_acct_pop>).  Note these are not FS::svc_acct_pop objects, but hash references of columns and values.
1726
1727 =item security_phrase
1728
1729 True if the "security_phrase" feature is enabled
1730
1731 =item payby
1732
1733 Array reference of acceptable payment types for signup
1734
1735 =over 4
1736
1737 =item CARD
1738
1739 credit card - automatic
1740
1741 =item DCRD
1742
1743 credit card - on-demand - version 1.5+ only
1744
1745 =item CHEK
1746
1747 electronic check - automatic
1748
1749 =item DCHK
1750
1751 electronic check - on-demand - version 1.5+ only
1752
1753 =item LECB
1754
1755 Phone bill billing
1756
1757 =item BILL
1758
1759 billing, not recommended for signups
1760
1761 =item COMP
1762
1763 free, definitely not recommended for signups
1764
1765 =item PREPAY
1766
1767 special billing type: applies a credit (see FS::prepay_credit) and sets billing type to BILL
1768
1769 =back
1770
1771 =item cvv_enabled
1772
1773 True if CVV features are available (1.5+ or 1.4.2 with CVV schema patch)
1774
1775 =item msgcat
1776
1777 Hash reference of message catalog values, to support error message customization.  Currently available keys are: passwords_dont_match, invalid_card, unknown_card_type, and not_a (as in "Not a Discover card").  Values are configured in the web interface under "View/Edit message catalog".
1778
1779 =item statedefault
1780
1781 Default state
1782
1783 =item countrydefault
1784
1785 Default country
1786
1787 =back
1788
1789 =item new_customer_minimal HASHREF
1790
1791 Creates a new customer.
1792
1793 Current differences from new_customer: An address is not required.  promo_code
1794 and reg_code are not supported.  If invoicing_list and _password is passed, a
1795 contact will be created with self-service access (no pkgpart or username is
1796 necessary).  No initial billing is run (this may change in a future version).
1797
1798 Takes a hash reference as parameter with the following keys:
1799
1800 =over 4
1801
1802 =item first
1803
1804 first name (required)
1805
1806 =item last
1807
1808 last name (required)
1809
1810 =item ss
1811
1812 (not typically collected; mostly used for ACH transactions)
1813
1814 =item company
1815
1816 Company name
1817
1818 =item address1
1819
1820 Address line one
1821
1822 =item address2
1823
1824 Address line two
1825
1826 =item city
1827
1828 City
1829
1830 =item county
1831
1832 County
1833
1834 =item state
1835
1836 State
1837
1838 =item zip
1839
1840 Zip or postal code
1841
1842 =item daytime
1843
1844 Daytime phone number
1845
1846 =item night
1847
1848 Evening phone number
1849
1850 =item fax
1851
1852 Fax number
1853
1854 =item payby
1855
1856 CARD, DCRD, CHEK, DCHK, LECB, BILL, COMP or PREPAY (see L</signup_info> (required)
1857
1858 =item payinfo
1859
1860 Card number for CARD/DCRD, account_number@aba_number for CHEK/DCHK, prepaid "pin" for PREPAY, purchase order number for BILL
1861
1862 =item paycvv
1863
1864 Credit card CVV2 number (1.5+ or 1.4.2 with CVV schema patch)
1865
1866 =item paydate
1867
1868 Expiration date for CARD/DCRD
1869
1870 =item payname
1871
1872 Exact name on credit card for CARD/DCRD, bank name for CHEK/DCHK
1873
1874 =item invoicing_list
1875
1876 comma-separated list of email addresses for email invoices.  The special value 'POST' is used to designate postal invoicing (it may be specified alone or in addition to email addresses),
1877
1878 =item referral_custnum
1879
1880 referring customer number
1881
1882 =item agentnum
1883
1884 Agent number
1885
1886 =item pkgpart
1887
1888 pkgpart of initial package
1889
1890 =item username
1891
1892 Username
1893
1894 =item _password
1895
1896 Password
1897
1898 =item sec_phrase
1899
1900 Security phrase
1901
1902 =item popnum
1903
1904 Access number (index, not the literal number)
1905
1906 =item countrycode
1907
1908 Country code (to be provisioned as a service)
1909
1910 =item phonenum
1911
1912 Phone number (to be provisioned as a service)
1913
1914 =item pin
1915
1916 Voicemail PIN
1917
1918 =back
1919
1920 Returns a hash reference with the following keys:
1921
1922 =over 4
1923
1924 =item error
1925
1926 Empty on success, or an error message on errors.  The special error '_decline' is returned for declined transactions; other error messages should be suitable for display to the user (and are customizable in under Configuration | View/Edit message catalog)
1927
1928 =back
1929
1930 =item new_customer HASHREF
1931
1932 Creates a new customer.  Takes a hash reference as parameter with the
1933 following keys:
1934
1935 =over 4
1936
1937 =item first
1938
1939 first name (required)
1940
1941 =item last
1942
1943 last name (required)
1944
1945 =item ss
1946
1947 (not typically collected; mostly used for ACH transactions)
1948
1949 =item company
1950
1951 Company name
1952
1953 =item address1 (required)
1954
1955 Address line one
1956
1957 =item address2
1958
1959 Address line two
1960
1961 =item city (required)
1962
1963 City
1964
1965 =item county
1966
1967 County
1968
1969 =item state (required)
1970
1971 State
1972
1973 =item zip (required)
1974
1975 Zip or postal code
1976
1977 =item daytime
1978
1979 Daytime phone number
1980
1981 =item night
1982
1983 Evening phone number
1984
1985 =item fax
1986
1987 Fax number
1988
1989 =item payby
1990
1991 CARD, DCRD, CHEK, DCHK, LECB, BILL, COMP or PREPAY (see L</signup_info> (required)
1992
1993 =item payinfo
1994
1995 Card number for CARD/DCRD, account_number@aba_number for CHEK/DCHK, prepaid "pin" for PREPAY, purchase order number for BILL
1996
1997 =item paycvv
1998
1999 Credit card CVV2 number (1.5+ or 1.4.2 with CVV schema patch)
2000
2001 =item paydate
2002
2003 Expiration date for CARD/DCRD
2004
2005 =item payname
2006
2007 Exact name on credit card for CARD/DCRD, bank name for CHEK/DCHK
2008
2009 =item invoicing_list
2010
2011 comma-separated list of email addresses for email invoices.  The special value 'POST' is used to designate postal invoicing (it may be specified alone or in addition to email addresses),
2012
2013 =item referral_custnum
2014
2015 referring customer number
2016
2017 =item agentnum
2018
2019 Agent number
2020
2021 =item pkgpart
2022
2023 pkgpart of initial package
2024
2025 =item username
2026
2027 Username
2028
2029 =item _password
2030
2031 Password
2032
2033 =item sec_phrase
2034
2035 Security phrase
2036
2037 =item popnum
2038
2039 Access number (index, not the literal number)
2040
2041 =item countrycode
2042
2043 Country code (to be provisioned as a service)
2044
2045 =item phonenum
2046
2047 Phone number (to be provisioned as a service)
2048
2049 =item pin
2050
2051 Voicemail PIN
2052
2053 =back
2054
2055 Returns a hash reference with the following keys:
2056
2057 =over 4
2058
2059 =item error
2060
2061 Empty on success, or an error message on errors.  The special error '_decline' is returned for declined transactions; other error messages should be suitable for display to the user (and are customizable in under Configuration | View/Edit message catalog)
2062
2063 =back
2064
2065 =item regionselector HASHREF | LIST
2066
2067 Takes as input a hashref or list of key/value pairs with the following keys:
2068
2069 =over 4
2070
2071 =item selected_county
2072
2073 Currently selected county
2074
2075 =item selected_state
2076
2077 Currently selected state
2078
2079 =item selected_country
2080
2081 Currently selected country
2082
2083 =item prefix
2084
2085 Specify a unique prefix string  if you intend to use the HTML output multiple time son one page.
2086
2087 =item onchange
2088
2089 Specify a javascript subroutine to call on changes
2090
2091 =item default_state
2092
2093 Default state
2094
2095 =item default_country
2096
2097 Default country
2098
2099 =item locales
2100
2101 An arrayref of hash references specifying regions.  Normally you can just pass the value of the I<cust_main_county> field returned by B<signup_info>.
2102
2103 =back
2104
2105 Returns a list consisting of three HTML fragments for county selection,
2106 state selection and country selection, respectively.
2107
2108 =cut
2109
2110 #false laziness w/FS::cust_main_county (this is currently the "newest" version)
2111 sub regionselector {
2112   my $param;
2113   if ( ref($_[0]) ) {
2114     $param = shift;
2115   } else {
2116     $param = { @_ };
2117   }
2118   $param->{'selected_country'} ||= $param->{'default_country'};
2119   $param->{'selected_state'} ||= $param->{'default_state'};
2120
2121   my $prefix = exists($param->{'prefix'}) ? $param->{'prefix'} : '';
2122
2123   my $countyflag = 0;
2124
2125   my %cust_main_county;
2126
2127 #  unless ( @cust_main_county ) { #cache 
2128     #@cust_main_county = qsearch('cust_main_county', {} );
2129     #foreach my $c ( @cust_main_county ) {
2130     foreach my $c ( @{ $param->{'locales'} } ) {
2131       #$countyflag=1 if $c->county;
2132       $countyflag=1 if $c->{county};
2133       #push @{$cust_main_county{$c->country}{$c->state}}, $c->county;
2134       #$cust_main_county{$c->country}{$c->state}{$c->county} = 1;
2135       $cust_main_county{$c->{country}}{$c->{state}}{$c->{county}} = 1;
2136     }
2137 #  }
2138   $countyflag=1 if $param->{selected_county};
2139
2140   my $script_html = <<END;
2141     <SCRIPT>
2142     function opt(what,value,text) {
2143       var optionName = new Option(text, value, false, false);
2144       var length = what.length;
2145       what.options[length] = optionName;
2146     }
2147     function ${prefix}country_changed(what) {
2148       country = what.options[what.selectedIndex].text;
2149       for ( var i = what.form.${prefix}state.length; i >= 0; i-- )
2150           what.form.${prefix}state.options[i] = null;
2151 END
2152       #what.form.${prefix}state.options[0] = new Option('', '', false, true);
2153
2154   foreach my $country ( sort keys %cust_main_county ) {
2155     $script_html .= "\nif ( country == \"$country\" ) {\n";
2156     foreach my $state ( sort keys %{$cust_main_county{$country}} ) {
2157       my $text = $state || '(n/a)';
2158       $script_html .= qq!opt(what.form.${prefix}state, "$state", "$text");\n!;
2159     }
2160     $script_html .= "}\n";
2161   }
2162
2163   $script_html .= <<END;
2164     }
2165     function ${prefix}state_changed(what) {
2166 END
2167
2168   if ( $countyflag ) {
2169     $script_html .= <<END;
2170       state = what.options[what.selectedIndex].text;
2171       country = what.form.${prefix}country.options[what.form.${prefix}country.selectedIndex].text;
2172       for ( var i = what.form.${prefix}county.length; i >= 0; i-- )
2173           what.form.${prefix}county.options[i] = null;
2174 END
2175
2176     foreach my $country ( sort keys %cust_main_county ) {
2177       $script_html .= "\nif ( country == \"$country\" ) {\n";
2178       foreach my $state ( sort keys %{$cust_main_county{$country}} ) {
2179         $script_html .= "\nif ( state == \"$state\" ) {\n";
2180           #foreach my $county ( sort @{$cust_main_county{$country}{$state}} ) {
2181           foreach my $county ( sort keys %{$cust_main_county{$country}{$state}} ) {
2182             my $text = $county || '(n/a)';
2183             $script_html .=
2184               qq!opt(what.form.${prefix}county, "$county", "$text");\n!;
2185           }
2186         $script_html .= "}\n";
2187       }
2188       $script_html .= "}\n";
2189     }
2190   }
2191
2192   $script_html .= <<END;
2193     }
2194     </SCRIPT>
2195 END
2196
2197   my $county_html = $script_html;
2198   if ( $countyflag ) {
2199     $county_html .= qq!<SELECT NAME="${prefix}county" onChange="$param->{'onchange'}">!;
2200     foreach my $county ( 
2201       sort keys %{ $cust_main_county{$param->{'selected_country'}}{$param->{'selected_state'}} }
2202     ) {
2203       my $text = $county || '(n/a)';
2204       $county_html .= qq!<OPTION VALUE="$county"!.
2205                       ($county eq $param->{'selected_county'} ? 
2206                         ' SELECTED>' : 
2207                         '>'
2208                       ).
2209                       $text.
2210                       '</OPTION>';
2211     }
2212     $county_html .= '</SELECT>';
2213   } else {
2214     $county_html .=
2215       qq!<INPUT TYPE="hidden" NAME="${prefix}county" VALUE="$param->{'selected_county'}">!;
2216   }
2217
2218   my $state_html = qq!<SELECT NAME="${prefix}state" !.
2219                    qq!onChange="${prefix}state_changed(this); $param->{'onchange'}">!;
2220   foreach my $state ( sort keys %{ $cust_main_county{$param->{'selected_country'}} } ) {
2221     my $text = $state || '(n/a)';
2222     my $selected = $state eq $param->{'selected_state'} ? 'SELECTED' : '';
2223     $state_html .= "\n<OPTION $selected VALUE=$state>$text</OPTION>"
2224   }
2225   $state_html .= '</SELECT>';
2226
2227   my $country_html = '';
2228   if ( scalar( keys %cust_main_county ) > 1 )  {
2229
2230     $country_html = qq(<SELECT NAME="${prefix}country" ).
2231                     qq(onChange="${prefix}country_changed(this); ).
2232                                  $param->{'onchange'}.
2233                                '"'.
2234                       '>';
2235     my $countrydefault = $param->{default_country} || 'US';
2236     foreach my $country (
2237       sort { ($b eq $countrydefault) <=> ($a eq $countrydefault) or $a cmp $b }
2238         keys %cust_main_county
2239     ) {
2240       my $selected = $country eq $param->{'selected_country'}
2241                        ? ' SELECTED'
2242                        : '';
2243       $country_html .= "\n<OPTION$selected>$country</OPTION>"
2244     }
2245     $country_html .= '</SELECT>';
2246   } else {
2247
2248     $country_html = qq(<INPUT TYPE="hidden" NAME="${prefix}country" ).
2249                             ' VALUE="'. (keys %cust_main_county )[0]. '">';
2250
2251   }
2252
2253   ($county_html, $state_html, $country_html);
2254
2255 }
2256
2257 sub regionselector_hashref {
2258   my ($county_html, $state_html, $country_html) = regionselector(@_);
2259   {
2260     'county_html'  => $county_html,
2261     'state_html'   => $state_html,
2262     'country_html' => $country_html,
2263   };
2264 }
2265
2266 =item location_form HASHREF | LIST
2267
2268 Takes as input a hashref or list of key/value pairs with the following keys:
2269
2270 =over 4
2271
2272 =item session_id
2273
2274 Current customer session_id
2275
2276 =item no_asterisks
2277
2278 Omit red asterisks from required fields.
2279
2280 =item address1_label
2281
2282 Label for first address line.
2283
2284 =back
2285
2286 Returns an HTML fragment for a location form (address, city, state, zip,
2287 country)
2288
2289 =cut
2290
2291 sub location_form {
2292   my $param;
2293   if ( ref($_[0]) ) {
2294     $param = shift;
2295   } else {
2296     $param = { @_ };
2297   }
2298
2299   my $session_id = delete $param->{'session_id'};
2300
2301   my $rv = mason_comp( 'session_id' => $session_id,
2302                        'comp'       => '/elements/location.html',
2303                        'args'       => [ %$param ],
2304                      );
2305
2306   #hmm.
2307   $rv->{'error'} || $rv->{'output'};
2308
2309 }
2310
2311
2312 #=item expselect HASHREF | LIST
2313 #
2314 #Takes as input a hashref or list of key/value pairs with the following keys:
2315 #
2316 #=over 4
2317 #
2318 #=item prefix - Specify a unique prefix string  if you intend to use the HTML output multiple time son one page.
2319 #
2320 #=item date - current date, in yyyy-mm-dd or m-d-yyyy format
2321 #
2322 #=back
2323
2324 =item expselect PREFIX [ DATE ]
2325
2326 Takes as input a unique prefix string and the current expiration date, in
2327 yyyy-mm-dd or m-d-yyyy format
2328
2329 Returns an HTML fragments for expiration date selection.
2330
2331 =cut
2332
2333 sub expselect {
2334   #my $param;
2335   #if ( ref($_[0]) ) {
2336   #  $param = shift;
2337   #} else {
2338   #  $param = { @_ };
2339   #my $prefix = $param->{'prefix'};
2340   #my $prefix = exists($param->{'prefix'}) ? $param->{'prefix'} : '';
2341   #my $date =   exists($param->{'date'})   ? $param->{'date'}   : '';
2342   my $prefix = shift;
2343   my $date = scalar(@_) ? shift : '';
2344
2345   my( $m, $y ) = ( 0, 0 );
2346   if ( $date  =~ /^(\d{4})-(\d{2})-\d{2}$/ ) { #PostgreSQL date format
2347     ( $m, $y ) = ( $2, $1 );
2348   } elsif ( $date =~ /^(\d{1,2})-(\d{1,2}-)?(\d{4}$)/ ) {
2349     ( $m, $y ) = ( $1, $3 );
2350   }
2351   my $return = qq!<SELECT NAME="$prefix!. qq!_month" SIZE="1">!;
2352   for ( 1 .. 12 ) {
2353     $return .= qq!<OPTION VALUE="$_"!;
2354     $return .= " SELECTED" if $_ == $m;
2355     $return .= ">$_";
2356   }
2357   $return .= qq!</SELECT>/<SELECT NAME="$prefix!. qq!_year" SIZE="1">!;
2358   my @t = localtime;
2359   my $thisYear = $t[5] + 1900;
2360   for ( ($thisYear > $y && $y > 0 ? $y : $thisYear) .. ($thisYear+10) ) {
2361     $return .= qq!<OPTION VALUE="$_"!;
2362     $return .= " SELECTED" if $_ == $y;
2363     $return .= ">$_";
2364   }
2365   $return .= "</SELECT>";
2366
2367   $return;
2368 }
2369
2370 =item popselector HASHREF | LIST
2371
2372 Takes as input a hashref or list of key/value pairs with the following keys:
2373
2374 =over 4
2375
2376 =item popnum
2377
2378 Access number number
2379
2380 =item pops
2381
2382 An arrayref of hash references specifying access numbers.  Normally you can just pass the value of the I<svc_acct_pop> field returned by B<signup_info>.
2383
2384 =back
2385
2386 Returns an HTML fragment for access number selection.
2387
2388 =cut
2389
2390 #horrible false laziness with FS/FS/svc_acct_pop.pm::popselector
2391 sub popselector {
2392   my $param;
2393   if ( ref($_[0]) ) {
2394     $param = shift;
2395   } else {
2396     $param = { @_ };
2397   }
2398   my $popnum = $param->{'popnum'};
2399   my $pops = $param->{'pops'};
2400
2401   return '<INPUT TYPE="hidden" NAME="popnum" VALUE="">' unless @$pops;
2402   return $pops->[0]{city}. ', '. $pops->[0]{state}.
2403          ' ('. $pops->[0]{ac}. ')/'. $pops->[0]{exch}. '-'. $pops->[0]{loc}.
2404          '<INPUT TYPE="hidden" NAME="popnum" VALUE="'. $pops->[0]{popnum}. '">'
2405     if scalar(@$pops) == 1;
2406
2407   my %pop = ();
2408   my %popnum2pop = ();
2409   foreach (@$pops) {
2410     push @{ $pop{ $_->{state} }->{ $_->{ac} } }, $_;
2411     $popnum2pop{$_->{popnum}} = $_;
2412   }
2413
2414   my $text = <<END;
2415     <SCRIPT>
2416     function opt(what,href,text) {
2417       var optionName = new Option(text, href, false, false)
2418       var length = what.length;
2419       what.options[length] = optionName;
2420     }
2421 END
2422
2423   my $init_popstate = $param->{'init_popstate'};
2424   if ( $init_popstate ) {
2425     $text .= '<INPUT TYPE="hidden" NAME="init_popstate" VALUE="'.
2426              $init_popstate. '">';
2427   } else {
2428     $text .= <<END;
2429       function acstate_changed(what) {
2430         state = what.options[what.selectedIndex].text;
2431         what.form.popac.options.length = 0
2432         what.form.popac.options[0] = new Option("Area code", "-1", false, true);
2433 END
2434   } 
2435
2436   my @states = $init_popstate ? ( $init_popstate ) : keys %pop;
2437   foreach my $state ( sort { $a cmp $b } @states ) {
2438     $text .= "\nif ( state == \"$state\" ) {\n" unless $init_popstate;
2439
2440     foreach my $ac ( sort { $a cmp $b } keys %{ $pop{$state} }) {
2441       $text .= "opt(what.form.popac, \"$ac\", \"$ac\");\n";
2442       if ($ac eq $param->{'popac'}) {
2443         $text .= "what.form.popac.options[what.form.popac.length-1].selected = true;\n";
2444       }
2445     }
2446     $text .= "}\n" unless $init_popstate;
2447   }
2448   $text .= "popac_changed(what.form.popac)}\n";
2449
2450   $text .= <<END;
2451   function popac_changed(what) {
2452     ac = what.options[what.selectedIndex].text;
2453     what.form.popnum.options.length = 0;
2454     what.form.popnum.options[0] = new Option("City", "-1", false, true);
2455
2456 END
2457
2458   foreach my $state ( @states ) {
2459     foreach my $popac ( keys %{ $pop{$state} } ) {
2460       $text .= "\nif ( ac == \"$popac\" ) {\n";
2461
2462       foreach my $pop ( @{$pop{$state}->{$popac}}) {
2463         my $o_popnum = $pop->{popnum};
2464         my $poptext =  $pop->{city}. ', '. $pop->{state}.
2465                        ' ('. $pop->{ac}. ')/'. $pop->{exch}. '-'. $pop->{loc};
2466
2467         $text .= "opt(what.form.popnum, \"$o_popnum\", \"$poptext\");\n";
2468         if ($popnum == $o_popnum) {
2469           $text .= "what.form.popnum.options[what.form.popnum.length-1].selected = true;\n";
2470         }
2471       }
2472       $text .= "}\n";
2473     }
2474   }
2475
2476
2477   $text .= "}\n</SCRIPT>\n";
2478
2479   $param->{'acstate'} = '' unless defined($param->{'acstate'});
2480
2481   $text .=
2482     qq!<TABLE CELLPADDING="0"><TR><TD><SELECT NAME="acstate"! .
2483     qq!SIZE=1 onChange="acstate_changed(this)"><OPTION VALUE=-1>State!;
2484   $text .= "<OPTION" . ($_ eq $param->{'acstate'} ? " SELECTED" : "") .
2485            ">$_" foreach sort { $a cmp $b } @states;
2486   $text .= '</SELECT>'; #callback? return 3 html pieces?  #'</TD>';
2487
2488   $text .=
2489     qq!<SELECT NAME="popac" SIZE=1 onChange="popac_changed(this)">!.
2490     qq!<OPTION>Area code</SELECT></TR><TR VALIGN="top">!;
2491
2492   $text .= qq!<TR><TD><SELECT NAME="popnum" SIZE=1 STYLE="width: 20em"><OPTION>City!;
2493
2494
2495   #comment this block to disable initial list polulation
2496   my @initial_select = ();
2497   if ( scalar( @$pops ) > 100 ) {
2498     push @initial_select, $popnum2pop{$popnum} if $popnum2pop{$popnum};
2499   } else {
2500     @initial_select = @$pops;
2501   }
2502   foreach my $pop ( sort { $a->{state} cmp $b->{state} } @initial_select ) {
2503     $text .= qq!<OPTION VALUE="!. $pop->{popnum}. '"'.
2504              ( ( $popnum && $pop->{popnum} == $popnum ) ? ' SELECTED' : '' ). ">".
2505              $pop->{city}. ', '. $pop->{state}.
2506                ' ('. $pop->{ac}. ')/'. $pop->{exch}. '-'. $pop->{loc};
2507   }
2508
2509   $text .= qq!</SELECT></TD></TR></TABLE>!;
2510
2511   $text;
2512
2513 }
2514
2515 =item domainselector HASHREF | LIST
2516
2517 Takes as input a hashref or list of key/value pairs with the following keys:
2518
2519 =over 4
2520
2521 =item pkgnum
2522
2523 Package number
2524
2525 =item domsvc
2526
2527 Service number of the selected item.
2528
2529 =back
2530
2531 Returns an HTML fragment for domain selection.
2532
2533 =cut
2534
2535 sub domainselector {
2536   my $param;
2537   if ( ref($_[0]) ) {
2538     $param = shift;
2539   } else {
2540     $param = { @_ };
2541   }
2542   my $domsvc= $param->{'domsvc'};
2543   my $rv = 
2544       domain_select_hash(map {$_ => $param->{$_}} qw(pkgnum svcpart pkgpart) );
2545   my $domains = $rv->{'domains'};
2546   $domsvc = $rv->{'domsvc'} unless $domsvc;
2547
2548   return '<INPUT TYPE="hidden" NAME="domsvc" VALUE="">'
2549     unless scalar(keys %$domains);
2550
2551   if (scalar(keys %$domains) == 1) {
2552     my $key;
2553     foreach(keys %$domains) {
2554       $key = $_;
2555     }
2556     return '<TR><TD ALIGN="right">Domain</TD><TD>'. $domains->{$key}.
2557            '<INPUT TYPE="hidden" NAME="domsvc" VALUE="'. $key. '"></TD></TR>'
2558   }
2559
2560   my $text .= qq!<TR><TD ALIGN="right">Domain</TD><TD><SELECT NAME="domsvc" SIZE=1 STYLE="width: 20em">!;
2561
2562   $text .= '<OPTION>(Choose Domain)' unless $domsvc;
2563
2564   foreach my $domain ( sort { $domains->{$a} cmp $domains->{$b} } keys %$domains ) {
2565     $text .= qq!<OPTION VALUE="!. $domain. '"'.
2566              ( ( $domsvc && $domain == $domsvc ) ? ' SELECTED' : '' ). ">".
2567              $domains->{$domain};
2568   }
2569
2570   $text .= qq!</SELECT></TD></TR>!;
2571
2572   $text;
2573
2574 }
2575
2576 =item didselector HASHREF | LIST
2577
2578 Takes as input a hashref or list of key/value pairs with the following keys:
2579
2580 =over 4
2581
2582 =item field
2583
2584 Field name for the returned HTML fragment.
2585
2586 =item svcpart
2587
2588 Service definition (see L<FS::part_svc>)
2589
2590 =back
2591
2592 Returns an HTML fragment for DID selection.
2593
2594 =cut
2595
2596 sub didselector {
2597   my $param;
2598   if ( ref($_[0]) ) {
2599     $param = shift;
2600   } else {
2601     $param = { @_ };
2602   }
2603
2604   my $rv = mason_comp( 'comp'=>'/elements/select-did.html',
2605                        'args'=>[ %$param ],
2606                      );
2607
2608   #hmm.
2609   $rv->{'error'} || $rv->{'output'};
2610
2611 }
2612
2613 =back
2614
2615 =head1 RESELLER FUNCTIONS
2616
2617 Note: Resellers can also use the B<signup_info> and B<new_customer> functions
2618 with their active session, and the B<customer_info> and B<order_pkg> functions
2619 with their active session and an additional I<custnum> parameter.
2620
2621 For the most part, development of the reseller web interface has been
2622 superceded by agent-virtualized access to the backend.
2623
2624 =over 4
2625
2626 =item agent_login
2627
2628 Agent login
2629
2630 =item agent_info
2631
2632 Agent info
2633
2634 =item agent_list_customers
2635
2636 List agent's customers.
2637
2638 =back
2639
2640 =head1 BUGS
2641
2642 =head1 SEE ALSO
2643
2644 L<freeside-selfservice-clientd>, L<freeside-selfservice-server>
2645
2646 =cut
2647
2648 1;
2649