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