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