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