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