add list_payments and payment_receipt self-service API calls, RT#75517
[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 daytime
2212
2213 Daytime phone number
2214
2215 =item night
2216
2217 Evening phone number
2218
2219 =item fax
2220
2221 Fax number
2222
2223 =item payby
2224
2225 CARD, DCRD, CHEK, DCHK, LECB, BILL, COMP or PREPAY (see L</signup_info> (required)
2226
2227 =item payinfo
2228
2229 Card number for CARD/DCRD, account_number@aba_number for CHEK/DCHK, prepaid "pin" for PREPAY, purchase order number for BILL
2230
2231 =item paycvv
2232
2233 Credit card CVV2 number (1.5+ or 1.4.2 with CVV schema patch)
2234
2235 =item paydate
2236
2237 Expiration date for CARD/DCRD
2238
2239 =item payname
2240
2241 Exact name on credit card for CARD/DCRD, bank name for CHEK/DCHK
2242
2243 =item invoicing_list
2244
2245 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),
2246
2247 =item referral_custnum
2248
2249 referring customer number
2250
2251 =item agentnum
2252
2253 Agent number
2254
2255 =item pkgpart
2256
2257 pkgpart of initial package
2258
2259 =item username
2260
2261 Username
2262
2263 =item _password
2264
2265 Password
2266
2267 =item sec_phrase
2268
2269 Security phrase
2270
2271 =item popnum
2272
2273 Access number (index, not the literal number)
2274
2275 =item countrycode
2276
2277 Country code (to be provisioned as a service)
2278
2279 =item phonenum
2280
2281 Phone number (to be provisioned as a service)
2282
2283 =item pin
2284
2285 Voicemail PIN
2286
2287 =back
2288
2289 Returns a hash reference with the following keys:
2290
2291 =over 4
2292
2293 =item error
2294
2295 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)
2296
2297 =back
2298
2299 =item regionselector HASHREF | LIST
2300
2301 Takes as input a hashref or list of key/value pairs with the following keys:
2302
2303 =over 4
2304
2305 =item selected_county
2306
2307 Currently selected county
2308
2309 =item selected_state
2310
2311 Currently selected state
2312
2313 =item selected_country
2314
2315 Currently selected country
2316
2317 =item prefix
2318
2319 Specify a unique prefix string  if you intend to use the HTML output multiple time son one page.
2320
2321 =item onchange
2322
2323 Specify a javascript subroutine to call on changes
2324
2325 =item default_state
2326
2327 Default state
2328
2329 =item default_country
2330
2331 Default country
2332
2333 =item locales
2334
2335 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>.
2336
2337 =back
2338
2339 Returns a list consisting of three HTML fragments for county selection,
2340 state selection and country selection, respectively.
2341
2342 =cut
2343
2344 #false laziness w/FS::cust_main_county (this is currently the "newest" version)
2345 sub regionselector {
2346   my $param;
2347   if ( ref($_[0]) ) {
2348     $param = shift;
2349   } else {
2350     $param = { @_ };
2351   }
2352   $param->{'selected_country'} ||= $param->{'default_country'};
2353   $param->{'selected_state'} ||= $param->{'default_state'};
2354
2355   my $prefix = exists($param->{'prefix'}) ? $param->{'prefix'} : '';
2356
2357   my $countyflag = 0;
2358
2359   my %cust_main_county;
2360
2361 #  unless ( @cust_main_county ) { #cache 
2362     #@cust_main_county = qsearch('cust_main_county', {} );
2363     #foreach my $c ( @cust_main_county ) {
2364     foreach my $c ( @{ $param->{'locales'} } ) {
2365       #$countyflag=1 if $c->county;
2366       $countyflag=1 if $c->{county};
2367       #push @{$cust_main_county{$c->country}{$c->state}}, $c->county;
2368       #$cust_main_county{$c->country}{$c->state}{$c->county} = 1;
2369       $cust_main_county{$c->{country}}{$c->{state}}{$c->{county}} = 1;
2370     }
2371 #  }
2372   $countyflag=1 if $param->{selected_county};
2373
2374   my $script_html = <<END;
2375     <SCRIPT>
2376     function opt(what,value,text) {
2377       var optionName = new Option(text, value, false, false);
2378       var length = what.length;
2379       what.options[length] = optionName;
2380     }
2381     function ${prefix}country_changed(what) {
2382       country = what.options[what.selectedIndex].text;
2383       for ( var i = what.form.${prefix}state.length; i >= 0; i-- )
2384           what.form.${prefix}state.options[i] = null;
2385 END
2386       #what.form.${prefix}state.options[0] = new Option('', '', false, true);
2387
2388   foreach my $country ( sort keys %cust_main_county ) {
2389     $script_html .= "\nif ( country == \"$country\" ) {\n";
2390     foreach my $state ( sort keys %{$cust_main_county{$country}} ) {
2391       my $text = $state || '(n/a)';
2392       $script_html .= qq!opt(what.form.${prefix}state, "$state", "$text");\n!;
2393     }
2394     $script_html .= "}\n";
2395   }
2396
2397   $script_html .= <<END;
2398     }
2399     function ${prefix}state_changed(what) {
2400 END
2401
2402   if ( $countyflag ) {
2403     $script_html .= <<END;
2404       state = what.options[what.selectedIndex].text;
2405       country = what.form.${prefix}country.options[what.form.${prefix}country.selectedIndex].text;
2406       for ( var i = what.form.${prefix}county.length; i >= 0; i-- )
2407           what.form.${prefix}county.options[i] = null;
2408 END
2409
2410     foreach my $country ( sort keys %cust_main_county ) {
2411       $script_html .= "\nif ( country == \"$country\" ) {\n";
2412       foreach my $state ( sort keys %{$cust_main_county{$country}} ) {
2413         $script_html .= "\nif ( state == \"$state\" ) {\n";
2414           #foreach my $county ( sort @{$cust_main_county{$country}{$state}} ) {
2415           foreach my $county ( sort keys %{$cust_main_county{$country}{$state}} ) {
2416             my $text = $county || '(n/a)';
2417             $script_html .=
2418               qq!opt(what.form.${prefix}county, "$county", "$text");\n!;
2419           }
2420         $script_html .= "}\n";
2421       }
2422       $script_html .= "}\n";
2423     }
2424   }
2425
2426   $script_html .= <<END;
2427     }
2428     </SCRIPT>
2429 END
2430
2431   my $county_html = $script_html;
2432   if ( $countyflag ) {
2433     $county_html .= qq!<SELECT NAME="${prefix}county" onChange="$param->{'onchange'}">!;
2434     foreach my $county ( 
2435       sort keys %{ $cust_main_county{$param->{'selected_country'}}{$param->{'selected_state'}} }
2436     ) {
2437       my $text = $county || '(n/a)';
2438       $county_html .= qq!<OPTION VALUE="$county"!.
2439                       ($county eq $param->{'selected_county'} ? 
2440                         ' SELECTED>' : 
2441                         '>'
2442                       ).
2443                       $text.
2444                       '</OPTION>';
2445     }
2446     $county_html .= '</SELECT>';
2447   } else {
2448     $county_html .=
2449       qq!<INPUT TYPE="hidden" NAME="${prefix}county" VALUE="$param->{'selected_county'}">!;
2450   }
2451
2452   my $state_html = qq!<SELECT NAME="${prefix}state" !.
2453                    qq!onChange="${prefix}state_changed(this); $param->{'onchange'}">!;
2454   foreach my $state ( sort keys %{ $cust_main_county{$param->{'selected_country'}} } ) {
2455     my $text = $state || '(n/a)';
2456     my $selected = $state eq $param->{'selected_state'} ? 'SELECTED' : '';
2457     $state_html .= "\n<OPTION $selected VALUE=\"$state\">$text</OPTION>"
2458   }
2459   $state_html .= '</SELECT>';
2460
2461   my $country_html = '';
2462   if ( scalar( keys %cust_main_county ) > 1 )  {
2463
2464     $country_html = qq(<SELECT NAME="${prefix}country" ).
2465                     qq(onChange="${prefix}country_changed(this); ).
2466                                  $param->{'onchange'}.
2467                                '"'.
2468                       '>';
2469     my $countrydefault = $param->{default_country} || 'US';
2470     foreach my $country (
2471       sort { ($b eq $countrydefault) <=> ($a eq $countrydefault) or $a cmp $b }
2472         keys %cust_main_county
2473     ) {
2474       my $selected = $country eq $param->{'selected_country'}
2475                        ? ' SELECTED'
2476                        : '';
2477       $country_html .= "\n<OPTION $selected>$country</OPTION>"
2478     }
2479     $country_html .= '</SELECT>';
2480   } else {
2481
2482     $country_html = qq(<INPUT TYPE="hidden" NAME="${prefix}country" ).
2483                             ' VALUE="'. (keys %cust_main_county )[0]. '">';
2484
2485   }
2486
2487   ($county_html, $state_html, $country_html);
2488
2489 }
2490
2491 sub regionselector_hashref {
2492   my ($county_html, $state_html, $country_html) = regionselector(@_);
2493   {
2494     'county_html'  => $county_html,
2495     'state_html'   => $state_html,
2496     'country_html' => $country_html,
2497   };
2498 }
2499
2500 =item location_form HASHREF | LIST
2501
2502 Takes as input a hashref or list of key/value pairs with the following keys:
2503
2504 =over 4
2505
2506 =item session_id
2507
2508 Current customer session_id
2509
2510 =item no_asterisks
2511
2512 Omit red asterisks from required fields.
2513
2514 =item address1_label
2515
2516 Label for first address line.
2517
2518 =back
2519
2520 Returns an HTML fragment for a location form (address, city, state, zip,
2521 country)
2522
2523 =cut
2524
2525 sub location_form {
2526   my $param;
2527   if ( ref($_[0]) ) {
2528     $param = shift;
2529   } else {
2530     $param = { @_ };
2531   }
2532
2533   my $session_id = delete $param->{'session_id'};
2534
2535   my $rv = mason_comp( 'session_id' => $session_id,
2536                        'comp'       => '/elements/location.html',
2537                        'args'       => [ %$param ],
2538                      );
2539
2540   #hmm.
2541   $rv->{'error'} || $rv->{'output'};
2542
2543 }
2544
2545
2546 #=item expselect HASHREF | LIST
2547 #
2548 #Takes as input a hashref or list of key/value pairs with the following keys:
2549 #
2550 #=over 4
2551 #
2552 #=item prefix - Specify a unique prefix string  if you intend to use the HTML output multiple time son one page.
2553 #
2554 #=item date - current date, in yyyy-mm-dd or m-d-yyyy format
2555 #
2556 #=back
2557
2558 =item expselect PREFIX [ DATE ]
2559
2560 Takes as input a unique prefix string and the current expiration date, in
2561 yyyy-mm-dd or m-d-yyyy format
2562
2563 Returns an HTML fragments for expiration date selection.
2564
2565 =cut
2566
2567 sub expselect {
2568   #my $param;
2569   #if ( ref($_[0]) ) {
2570   #  $param = shift;
2571   #} else {
2572   #  $param = { @_ };
2573   #my $prefix = $param->{'prefix'};
2574   #my $prefix = exists($param->{'prefix'}) ? $param->{'prefix'} : '';
2575   #my $date =   exists($param->{'date'})   ? $param->{'date'}   : '';
2576   my $prefix = shift;
2577   my $date = scalar(@_) ? shift : '';
2578
2579   my( $m, $y ) = ( 0, 0 );
2580   if ( $date  =~ /^(\d{4})-(\d{2})-\d{2}$/ ) { #PostgreSQL date format
2581     ( $m, $y ) = ( $2, $1 );
2582   } elsif ( $date =~ /^(\d{1,2})-(\d{1,2}-)?(\d{4}$)/ ) {
2583     ( $m, $y ) = ( $1, $3 );
2584   }
2585   my $return = qq!<SELECT NAME="$prefix!. qq!_month" SIZE="1">!;
2586   for ( 1 .. 12 ) {
2587     $return .= qq!<OPTION VALUE="$_"!;
2588     $return .= " SELECTED" if $_ == $m;
2589     $return .= ">$_";
2590   }
2591   $return .= qq!</SELECT>/<SELECT NAME="$prefix!. qq!_year" SIZE="1">!;
2592   my @t = localtime;
2593   my $thisYear = $t[5] + 1900;
2594   for ( ($thisYear > $y && $y > 0 ? $y : $thisYear) .. ($thisYear+10) ) {
2595     $return .= qq!<OPTION VALUE="$_"!;
2596     $return .= " SELECTED" if $_ == $y;
2597     $return .= ">$_";
2598   }
2599   $return .= "</SELECT>";
2600
2601   $return;
2602 }
2603
2604 =item popselector HASHREF | LIST
2605
2606 Takes as input a hashref or list of key/value pairs with the following keys:
2607
2608 =over 4
2609
2610 =item popnum
2611
2612 Access number number
2613
2614 =item pops
2615
2616 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>.
2617
2618 =back
2619
2620 Returns an HTML fragment for access number selection.
2621
2622 =cut
2623
2624 #horrible false laziness with FS/FS/svc_acct_pop.pm::popselector
2625 sub popselector {
2626   my $param;
2627   if ( ref($_[0]) ) {
2628     $param = shift;
2629   } else {
2630     $param = { @_ };
2631   }
2632   my $popnum = $param->{'popnum'};
2633   my $pops = $param->{'pops'};
2634
2635   return '<INPUT TYPE="hidden" NAME="popnum" VALUE="">' unless @$pops;
2636   return $pops->[0]{city}. ', '. $pops->[0]{state}.
2637          ' ('. $pops->[0]{ac}. ')/'. $pops->[0]{exch}. '-'. $pops->[0]{loc}.
2638          '<INPUT TYPE="hidden" NAME="popnum" VALUE="'. $pops->[0]{popnum}. '">'
2639     if scalar(@$pops) == 1;
2640
2641   my %pop = ();
2642   my %popnum2pop = ();
2643   foreach (@$pops) {
2644     push @{ $pop{ $_->{state} }->{ $_->{ac} } }, $_;
2645     $popnum2pop{$_->{popnum}} = $_;
2646   }
2647
2648   my $text = <<END;
2649     <SCRIPT>
2650     function opt(what,href,text) {
2651       var optionName = new Option(text, href, false, false)
2652       var length = what.length;
2653       what.options[length] = optionName;
2654     }
2655 END
2656
2657   my $init_popstate = $param->{'init_popstate'};
2658   if ( $init_popstate ) {
2659     $text .= '<INPUT TYPE="hidden" NAME="init_popstate" VALUE="'.
2660              $init_popstate. '">';
2661   } else {
2662     $text .= <<END;
2663       function acstate_changed(what) {
2664         state = what.options[what.selectedIndex].text;
2665         what.form.popac.options.length = 0
2666         what.form.popac.options[0] = new Option("Area code", "-1", false, true);
2667 END
2668   } 
2669
2670   my @states = $init_popstate ? ( $init_popstate ) : keys %pop;
2671   foreach my $state ( sort { $a cmp $b } @states ) {
2672     $text .= "\nif ( state == \"$state\" ) {\n" unless $init_popstate;
2673
2674     foreach my $ac ( sort { $a cmp $b } keys %{ $pop{$state} }) {
2675       $text .= "opt(what.form.popac, \"$ac\", \"$ac\");\n";
2676       if ($ac eq $param->{'popac'}) {
2677         $text .= "what.form.popac.options[what.form.popac.length-1].selected = true;\n";
2678       }
2679     }
2680     $text .= "}\n" unless $init_popstate;
2681   }
2682   $text .= "popac_changed(what.form.popac)}\n";
2683
2684   $text .= <<END;
2685   function popac_changed(what) {
2686     ac = what.options[what.selectedIndex].text;
2687     what.form.popnum.options.length = 0;
2688     what.form.popnum.options[0] = new Option("City", "-1", false, true);
2689
2690 END
2691
2692   foreach my $state ( @states ) {
2693     foreach my $popac ( keys %{ $pop{$state} } ) {
2694       $text .= "\nif ( ac == \"$popac\" ) {\n";
2695
2696       foreach my $pop ( @{$pop{$state}->{$popac}}) {
2697         my $o_popnum = $pop->{popnum};
2698         my $poptext =  $pop->{city}. ', '. $pop->{state}.
2699                        ' ('. $pop->{ac}. ')/'. $pop->{exch}. '-'. $pop->{loc};
2700
2701         $text .= "opt(what.form.popnum, \"$o_popnum\", \"$poptext\");\n";
2702         if ($popnum == $o_popnum) {
2703           $text .= "what.form.popnum.options[what.form.popnum.length-1].selected = true;\n";
2704         }
2705       }
2706       $text .= "}\n";
2707     }
2708   }
2709
2710
2711   $text .= "}\n</SCRIPT>\n";
2712
2713   $param->{'acstate'} = '' unless defined($param->{'acstate'});
2714
2715   $text .=
2716     qq!<TABLE CELLPADDING="0"><TR><TD><SELECT NAME="acstate"! .
2717     qq!SIZE=1 onChange="acstate_changed(this)"><OPTION VALUE=-1>State!;
2718   $text .= "<OPTION" . ($_ eq $param->{'acstate'} ? " SELECTED" : "") .
2719            ">$_" foreach sort { $a cmp $b } @states;
2720   $text .= '</SELECT>'; #callback? return 3 html pieces?  #'</TD>';
2721
2722   $text .=
2723     qq!<SELECT NAME="popac" SIZE=1 onChange="popac_changed(this)">!.
2724     qq!<OPTION>Area code</SELECT></TR><TR VALIGN="top">!;
2725
2726   $text .= qq!<TR><TD><SELECT NAME="popnum" SIZE=1 STYLE="width: 20em"><OPTION>City!;
2727
2728
2729   #comment this block to disable initial list polulation
2730   my @initial_select = ();
2731   if ( scalar( @$pops ) > 100 ) {
2732     push @initial_select, $popnum2pop{$popnum} if $popnum2pop{$popnum};
2733   } else {
2734     @initial_select = @$pops;
2735   }
2736   foreach my $pop ( sort { $a->{state} cmp $b->{state} } @initial_select ) {
2737     $text .= qq!<OPTION VALUE="!. $pop->{popnum}. '"'.
2738              ( ( $popnum && $pop->{popnum} == $popnum ) ? ' SELECTED' : '' ). ">".
2739              $pop->{city}. ', '. $pop->{state}.
2740                ' ('. $pop->{ac}. ')/'. $pop->{exch}. '-'. $pop->{loc};
2741   }
2742
2743   $text .= qq!</SELECT></TD></TR></TABLE>!;
2744
2745   $text;
2746
2747 }
2748
2749 =item domainselector HASHREF | LIST
2750
2751 Takes as input a hashref or list of key/value pairs with the following keys:
2752
2753 =over 4
2754
2755 =item pkgnum
2756
2757 Package number
2758
2759 =item domsvc
2760
2761 Service number of the selected item.
2762
2763 =back
2764
2765 Returns an HTML fragment for domain selection.
2766
2767 =cut
2768
2769 sub domainselector {
2770   my $param;
2771   if ( ref($_[0]) ) {
2772     $param = shift;
2773   } else {
2774     $param = { @_ };
2775   }
2776   my $domsvc= $param->{'domsvc'};
2777   my $rv = 
2778       domain_select_hash(map {$_ => $param->{$_}} qw(pkgnum svcpart pkgpart) );
2779   my $domains = $rv->{'domains'};
2780   $domsvc = $rv->{'domsvc'} unless $domsvc;
2781
2782   return '<INPUT TYPE="hidden" NAME="domsvc" VALUE="">'
2783     unless scalar(keys %$domains);
2784
2785   if (scalar(keys %$domains) == 1) {
2786     my $key;
2787     foreach(keys %$domains) {
2788       $key = $_;
2789     }
2790     return '<TR><TD ALIGN="right">Domain</TD><TD>'. $domains->{$key}.
2791            '<INPUT TYPE="hidden" NAME="domsvc" VALUE="'. $key. '"></TD></TR>'
2792   }
2793
2794   my $text .= qq!<TR><TD ALIGN="right">Domain</TD><TD><SELECT NAME="domsvc" SIZE=1 STYLE="width: 20em">!;
2795
2796   $text .= '<OPTION>(Choose Domain)' unless $domsvc;
2797
2798   foreach my $domain ( sort { $domains->{$a} cmp $domains->{$b} } keys %$domains ) {
2799     $text .= qq!<OPTION VALUE="!. $domain. '"'.
2800              ( ( $domsvc && $domain == $domsvc ) ? ' SELECTED' : '' ). ">".
2801              $domains->{$domain};
2802   }
2803
2804   $text .= qq!</SELECT></TD></TR>!;
2805
2806   $text;
2807
2808 }
2809
2810 =item didselector HASHREF | LIST
2811
2812 Takes as input a hashref or list of key/value pairs with the following keys:
2813
2814 =over 4
2815
2816 =item field
2817
2818 Field name for the returned HTML fragment.
2819
2820 =item svcpart
2821
2822 Service definition (see L<FS::part_svc>)
2823
2824 =back
2825
2826 Returns an HTML fragment for DID selection.
2827
2828 =cut
2829
2830 sub didselector {
2831   my $param;
2832   if ( ref($_[0]) ) {
2833     $param = shift;
2834   } else {
2835     $param = { @_ };
2836   }
2837
2838   my $rv = mason_comp( 'comp'=>'/elements/select-did.html',
2839                        'args'=>[ %$param ],
2840                      );
2841
2842   #hmm.
2843   $rv->{'error'} || $rv->{'output'};
2844
2845 }
2846
2847 =back
2848
2849 =head1 RESELLER FUNCTIONS
2850
2851 Note: Resellers can also use the B<signup_info> and B<new_customer> functions
2852 with their active session, and the B<customer_info> and B<order_pkg> functions
2853 with their active session and an additional I<custnum> parameter.
2854
2855 For the most part, development of the reseller web interface has been
2856 superceded by agent-virtualized access to the backend.
2857
2858 =over 4
2859
2860 =item agent_login
2861
2862 Agent login
2863
2864 =item agent_info
2865
2866 Agent info
2867
2868 =item agent_list_customers
2869
2870 List agent's customers.
2871
2872 =back
2873
2874 =head1 BUGS
2875
2876 =head1 SEE ALSO
2877
2878 L<freeside-selfservice-clientd>, L<freeside-selfservice-server>
2879
2880 =cut
2881
2882 1;
2883