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