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