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