merge webpay support in with autoselection of old realtime_bop and realtime_refund_bop
[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'                     => 'MyAccount/login',
30   'logout'                    => 'MyAccount/logout',
31   'customer_info'             => 'MyAccount/customer_info',
32   'edit_info'                 => 'MyAccount/edit_info',     #add to ss cgi!
33   'invoice'                   => 'MyAccount/invoice',
34   'invoice_logo'              => 'MyAccount/invoice_logo',
35   'list_invoices'             => 'MyAccount/list_invoices', #?
36   'cancel'                    => 'MyAccount/cancel',        #add to ss cgi!
37   'payment_info'              => 'MyAccount/payment_info',
38   'process_payment'           => 'MyAccount/process_payment',
39   'process_payment_order_pkg' => 'MyAccount/process_payment_order_pkg',
40   'process_payment_order_renew' => 'MyAccount/process_payment_order_renew',
41   'process_prepay'            => 'MyAccount/process_prepay',
42   'realtime_collect'          => 'MyAccount/realtime_collect',
43   'list_pkgs'                 => 'MyAccount/list_pkgs',     #add to ss (added?)
44   'list_svcs'                 => 'MyAccount/list_svcs',     #add to ss (added?)
45   'list_svc_usage'            => 'MyAccount/list_svc_usage',   
46   'list_support_usage'        => 'MyAccount/list_support_usage',   
47   'order_pkg'                 => 'MyAccount/order_pkg',     #add to ss cgi!
48   'change_pkg'                => 'MyAccount/change_pkg', 
49   'order_recharge'            => 'MyAccount/order_recharge',
50   'renew_info'                => 'MyAccount/renew_info',
51   'order_renew'               => 'MyAccount/order_renew',
52   'cancel_pkg'                => 'MyAccount/cancel_pkg',    #add to ss cgi!
53   'charge'                    => 'MyAccount/charge',        #?
54   'part_svc_info'             => 'MyAccount/part_svc_info',
55   'provision_acct'            => 'MyAccount/provision_acct',
56   'provision_external'        => 'MyAccount/provision_external',
57   'unprovision_svc'           => 'MyAccount/unprovision_svc',
58   'myaccount_passwd'          => 'MyAccount/myaccount_passwd',
59   'signup_info'               => 'Signup/signup_info',
60   'domain_select_hash'        => 'Signup/domain_select_hash',  # expose?
61   'new_customer'              => 'Signup/new_customer',
62   'capture_payment'           => 'Signup/capture_payment',
63   'agent_login'               => 'Agent/agent_login',
64   'agent_logout'              => 'Agent/agent_logout',
65   'agent_info'                => 'Agent/agent_info',
66   'agent_list_customers'      => 'Agent/agent_list_customers',
67   'mason_comp'                => 'MasonComponent/mason_comp',
68   'call_time'                 => 'PrepaidPhone/call_time',
69   'call_time_nanpa'           => 'PrepaidPhone/call_time_nanpa',
70   'phonenum_balance'          => 'PrepaidPhone/phonenum_balance',
71 );
72 @EXPORT_OK = (
73   keys(%autoload),
74   qw( regionselector regionselector_hashref
75       expselect popselector domainselector didselector )
76 );
77
78 $ENV{'PATH'} ='/usr/bin:/usr/ucb:/bin';
79 $ENV{'SHELL'} = '/bin/sh';
80 $ENV{'IFS'} = " \t\n";
81 $ENV{'CDPATH'} = '';
82 $ENV{'ENV'} = '';
83 $ENV{'BASH_ENV'} = '';
84
85 #you can add BEGIN { $FS::SelfService::skip_uid_check = 1; } 
86 #if you grant appropriate permissions to whatever user
87 my $freeside_uid = scalar(getpwnam('freeside'));
88 die "not running as the freeside user\n"
89   if $> != $freeside_uid && ! $skip_uid_check;
90
91 -e $dir or die "FATAL: $dir doesn't exist!";
92 -d $dir or die "FATAL: $dir isn't a directory!";
93 -r $dir or die "FATAL: Can't read $dir as freeside user!";
94 -x $dir or die "FATAL: $dir not searchable (executable) as freeside user!";
95
96 foreach my $autoload ( keys %autoload ) {
97
98   my $eval =
99   "sub $autoload { ". '
100                    my $param;
101                    if ( ref($_[0]) ) {
102                      $param = shift;
103                    } else {
104                      #warn scalar(@_). ": ". join(" / ", @_);
105                      $param = { @_ };
106                    }
107
108                    $param->{_packet} = \''. $autoload{$autoload}. '\';
109
110                    simple_packet($param);
111                  }';
112
113   eval $eval;
114   die $@ if $@;
115
116 }
117
118 sub simple_packet {
119   my $packet = shift;
120   warn "sending ". $packet->{_packet}. " to server"
121     if $DEBUG;
122   socket(SOCK, PF_UNIX, SOCK_STREAM, 0) or die "socket: $!";
123   connect(SOCK, sockaddr_un($socket)) or die "connect to $socket: $!";
124   nstore_fd($packet, \*SOCK) or die "can't send packet: $!";
125   SOCK->flush;
126
127   #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
128
129   #block until there is a message on socket
130 #  my $w = new IO::Select;
131 #  $w->add(\*SOCK);
132 #  my @wait = $w->can_read;
133
134   warn "reading message from server"
135     if $DEBUG;
136
137   my $return = fd_retrieve(\*SOCK) or die "error reading result: $!";
138   die $return->{'_error'} if defined $return->{_error} && $return->{_error};
139
140   warn "returning message to client"
141     if $DEBUG;
142
143   $return;
144 }
145
146 =head1 NAME
147
148 FS::SelfService - Freeside self-service API
149
150 =head1 SYNOPSIS
151
152   # password and shell account changes
153   use FS::SelfService qw(passwd chfn chsh);
154
155   # "my account" functionality
156   use FS::SelfService qw( login customer_info invoice cancel payment_info process_payment );
157
158   my $rv = login( { 'username' => $username,
159                     'domain'   => $domain,
160                     'password' => $password,
161                   }
162                 );
163
164   if ( $rv->{'error'} ) {
165     #handle login error...
166   } else {
167     #successful login
168     my $session_id = $rv->{'session_id'};
169   }
170
171   my $customer_info = customer_info( { 'session_id' => $session_id } );
172
173   #payment_info and process_payment are available in 1.5+ only
174   my $payment_info = payment_info( { 'session_id' => $session_id } );
175
176   #!!! process_payment example
177
178   #!!! list_pkgs example
179
180   #!!! order_pkg example
181
182   #!!! cancel_pkg example
183
184   # signup functionality
185   use FS::SelfService qw( signup_info new_customer );
186
187   my $signup_info = signup_info;
188
189   $rv = new_customer( {
190                         'first'            => $first,
191                         'last'             => $last,
192                         'company'          => $company,
193                         'address1'         => $address1,
194                         'address2'         => $address2,
195                         'city'             => $city,
196                         'state'            => $state,
197                         'zip'              => $zip,
198                         'country'          => $country,
199                         'daytime'          => $daytime,
200                         'night'            => $night,
201                         'fax'              => $fax,
202                         'payby'            => $payby,
203                         'payinfo'          => $payinfo,
204                         'paycvv'           => $paycvv,
205                         'paystart_month'   => $paystart_month
206                         'paystart_year'    => $paystart_year,
207                         'payissue'         => $payissue,
208                         'payip'            => $payip
209                         'paydate'          => $paydate,
210                         'payname'          => $payname,
211                         'invoicing_list'   => $invoicing_list,
212                         'referral_custnum' => $referral_custnum,
213                         'agentnum'         => $agentnum,
214                         'pkgpart'          => $pkgpart,
215
216                         'username'         => $username,
217                         '_password'        => $password,
218                         'popnum'           => $popnum,
219                         #OR
220                         'countrycode'      => 1,
221                         'phonenum'         => $phonenum,
222                         'pin'              => $pin,
223                       }
224                     );
225   
226   my $error = $rv->{'error'};
227   if ( $error eq '_decline' ) {
228     print_decline();
229   } elsif ( $error ) {
230     reprint_signup();
231   } else {
232     print_success();
233   }
234
235 =head1 DESCRIPTION
236
237 Use this API to implement your own client "self-service" module.
238
239 If you just want to customize the look of the existing "self-service" module,
240 see XXXX instead.
241
242 =head1 PASSWORD, GECOS, SHELL CHANGING FUNCTIONS
243
244 =over 4
245
246 =item passwd
247
248 =item chfn
249
250 =item chsh
251
252 =back
253
254 =head1 "MY ACCOUNT" FUNCTIONS
255
256 =over 4
257
258 =item login HASHREF
259
260 Creates a user session.  Takes a hash reference as parameter with the
261 following keys:
262
263 =over 4
264
265 =item username
266
267 Username
268
269 =item domain
270
271 Domain
272
273 =item password
274
275 Password
276
277 =back
278
279 Returns a hash reference with the following keys:
280
281 =over 4
282
283 =item error
284
285 Empty on success, or an error message on errors.
286
287 =item session_id
288
289 Session identifier for successful logins
290
291 =back
292
293 =item customer_info HASHREF
294
295 Returns general customer information.
296
297 Takes a hash reference as parameter with a single key: B<session_id>
298
299 Returns a hash reference with the following keys:
300
301 =over 4
302
303 =item name
304
305 Customer name
306
307 =item balance
308
309 Balance owed
310
311 =item open
312
313 Array reference of hash references of open inoices.  Each hash reference has
314 the following keys: invnum, date, owed
315
316 =item small_custview
317
318 An HTML fragment containing shipping and billing addresses.
319
320 =item The following fields are also returned
321
322 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
323
324 =back
325
326 =item edit_info HASHREF
327
328 Takes a hash reference as parameter with any of the following keys:
329
330 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
331
332 If a field exists, the customer record is updated with the new value of that
333 field.  If a field does not exist, that field is not changed on the customer
334 record.
335
336 Returns a hash reference with a single key, B<error>, empty on success, or an
337 error message on errors
338
339 =item invoice HASHREF
340
341 Returns an invoice.  Takes a hash reference as parameter with two keys:
342 session_id and invnum
343
344 Returns a hash reference with the following keys:
345
346 =over 4
347
348 =item error
349
350 Empty on success, or an error message on errors
351
352 =item invnum
353
354 Invoice number
355
356 =item invoice_text
357
358 Invoice text
359
360 =back
361
362 =item list_invoices HASHREF
363
364 Returns a list of all customer invoices.  Takes a hash references with a single
365 key, session_id.
366
367 Returns a hash reference with the following keys:
368
369 =over 4
370
371 =item error
372
373 Empty on success, or an error message on errors
374
375 =item invoices
376
377 Reference to array of hash references with the following keys:
378
379 =over 4
380
381 =item invnum
382
383 Invoice ID
384
385 =item _date
386
387 Invoice date, in UNIX epoch time
388
389 =back
390
391 =back
392
393 =item cancel HASHREF
394
395 Cancels this customer.
396
397 Takes a hash reference as parameter with a single key: B<session_id>
398
399 Returns a hash reference with a single key, B<error>, which is empty on
400 success or an error message on errors.
401
402 =item payment_info HASHREF
403
404 Returns information that may be useful in displaying a payment page.
405
406 Takes a hash reference as parameter with a single key: B<session_id>.
407
408 Returns a hash reference with the following keys:
409
410 =over 4
411
412 =item error
413
414 Empty on success, or an error message on errors
415
416 =item balance
417
418 Balance owed
419
420 =item payname
421
422 Exact name on credit card (CARD/DCRD)
423
424 =item address1
425
426 Address line one
427
428 =item address2
429
430 Address line two
431
432 =item city
433
434 City
435
436 =item state
437
438 State
439
440 =item zip
441
442 Zip or postal code
443
444 =item payby
445
446 Customer's current default payment type.
447
448 =item card_type
449
450 For CARD/DCRD payment types, the card type (Visa card, MasterCard, Discover card, American Express card, etc.)
451
452 =item payinfo
453
454 For CARD/DCRD payment types, the card number
455
456 =item month
457
458 For CARD/DCRD payment types, expiration month
459
460 =item year
461
462 For CARD/DCRD payment types, expiration year
463
464 =item cust_main_county
465
466 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.
467
468 =item states
469
470 Array reference of all states in the current default country.
471
472 =item card_types
473
474 Hash reference of card types; keys are card types, values are the exact strings
475 passed to the process_payment function
476
477 =cut
478
479 #this doesn't actually work yet
480 #
481 #=item paybatch
482 #
483 #Unique transaction identifier (prevents multiple charges), passed to the
484 #process_payment function
485
486 =back
487
488 =item process_payment HASHREF
489
490 Processes a payment and possible change of address or payment type.  Takes a
491 hash reference as parameter with the following keys:
492
493 =over 4
494
495 =item session_id
496
497 Session identifier
498
499 =item amount
500
501 Amount
502
503 =item save
504
505 If true, address and card information entered will be saved for subsequent
506 transactions.
507
508 =item auto
509
510 If true, future credit card payments will be done automatically (sets payby to
511 CARD).  If false, future credit card payments will be done on-demand (sets
512 payby to DCRD).  This option only has meaning if B<save> is set true.  
513
514 =item payname
515
516 Name on card
517
518 =item address1
519
520 Address line one
521
522 =item address2
523
524 Address line two
525
526 =item city
527
528 City
529
530 =item state
531
532 State
533
534 =item zip
535
536 Zip or postal code
537
538 =item payinfo
539
540 Card number
541
542 =item month
543
544 Card expiration month
545
546 =item year
547
548 Card expiration year
549
550 =cut
551
552 #this doesn't actually work yet
553 #
554 #=item paybatch
555 #
556 #Unique transaction identifier, returned from the payment_info function.
557 #Prevents multiple charges.
558
559 =back
560
561 Returns a hash reference with a single key, B<error>, empty on success, or an
562 error message on errors.
563
564 =item process_payment_order_pkg
565
566 Combines the B<process_payment> and B<order_pkg> functions in one step.  If the
567 payment processes sucessfully, the package is ordered.  Takes a hash reference
568 as parameter with the keys of both methods.
569
570 Returns a hash reference with a single key, B<error>, empty on success, or an
571 error message on errors.
572
573 =item process_payment_order_renew
574
575 Combines the B<process_payment> and B<order_renew> functions in one step.  If
576 the payment processes sucessfully, the renewal is processed.  Takes a hash
577 reference as parameter with the keys of both methods.
578
579 Returns a hash reference with a single key, B<error>, empty on success, or an
580 error message on errors.
581
582 =item list_pkgs
583
584 Returns package information for this customer.  For more detail on services,
585 see L</list_svcs>.
586
587 Takes a hash reference as parameter with a single key: B<session_id>
588
589 Returns a hash reference containing customer package information.  The hash reference contains the following keys:
590
591 =over 4
592
593 =item custnum
594
595 Customer number
596
597 =item error
598
599 Empty on success, or an error message on errors.
600
601 =item cust_pkg HASHREF
602
603 Array reference of hash references, each of which has the fields of a cust_pkg
604 record (see L<FS::cust_pkg>) as well as the fields below.  Note these are not
605 the internal FS:: objects, but hash references of columns and values.
606
607 =over 4
608
609 =item part_pkg fields
610
611 All fields of part_pkg for this specific cust_pkg (be careful with this
612 information - it may reveal more about your available packages than you would
613 like users to know in aggregate) 
614
615 =cut
616
617 #XXX pare part_pkg fields down to a more secure subset
618
619 =item part_svc
620
621 An array of hash references indicating information on unprovisioned services
622 available for provisioning for this specific cust_pkg.  Each has the following
623 keys:
624
625 =over 4
626
627 =item part_svc fields
628
629 All fields of part_svc (be careful with this information - it may reveal more
630 about your available packages than you would like users to know in aggregate) 
631
632 =cut
633
634 #XXX pare part_svc fields down to a more secure subset
635
636 =back
637
638 =item cust_svc
639
640 An array of hash references indicating information on the customer services
641 already provisioned for this specific cust_pkg.  Each has the following keys:
642
643 =over 4
644
645 =item label
646
647 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.
648
649 =back
650
651 =item svcnum
652
653 Primary key for this service
654
655 =item svcpart
656
657 Service definition (see L<FS::part_svc>)
658
659 =item pkgnum
660
661 Customer package (see L<FS::cust_pkg>)
662
663 =item overlimit
664
665 Blank if the service is not over limit, or the date the service exceeded its usage limit (as a UNIX timestamp).
666
667 =back
668
669 =back
670
671 =item list_svcs
672
673 Returns service information for this customer.
674
675 Takes a hash reference as parameter with a single key: B<session_id>
676
677 Returns a hash reference containing customer package information.  The hash reference contains the following keys:
678
679 =over 4
680
681 =item custnum
682
683 Customer number
684
685 =item svcs
686
687 An array of hash references indicating information on all of this customer's
688 services.  Each has the following keys:
689
690 =over 4
691
692 =item svcnum
693
694 Primary key for this service
695
696 =item label
697
698 Name of this service
699
700 =item value
701
702 Meaningful user-specific identifier for the service (i.e. username, domain, or
703 mail alias).
704
705 =back
706
707 Account (svc_acct) services also have the following keys:
708
709 =over 4
710
711 =item username
712
713 Username
714
715 =item email
716
717 username@domain
718
719 =item seconds
720
721 Seconds remaining
722
723 =item upbytes
724
725 Upload bytes remaining
726
727 =item downbytes
728
729 Download bytes remaining
730
731 =item totalbytes
732
733 Total bytes remaining
734
735 =item recharge_amount
736
737 Cost of a recharge
738
739 =item recharge_seconds
740
741 Number of seconds gained by recharge
742
743 =item recharge_upbytes
744
745 Number of upload bytes gained by recharge
746
747 =item recharge_downbytes
748
749 Number of download bytes gained by recharge
750
751 =item recharge_totalbytes
752
753 Number of total bytes gained by recharge
754
755 =back
756
757 =back
758
759 =item order_pkg
760
761 Orders a package for this customer.
762
763 Takes a hash reference as parameter with the following keys:
764
765 =over 4
766
767 =item session_id
768
769 Session identifier
770
771 =item pkgpart
772
773 Package to order (see L<FS::part_pkg>).
774
775 =item svcpart
776
777 Service to order (see L<FS::part_svc>).
778
779 Normally optional; required only to provision a non-svc_acct service, or if the
780 package definition does not contain one svc_acct service definition with
781 quantity 1 (it may contain others with quantity >1).  A svcpart of "none" can
782 also be specified to indicate that no initial service should be provisioned.
783
784 =back
785
786 Fields used when provisioning an svc_acct service:
787
788 =over 4
789
790 =item username
791
792 Username
793
794 =item _password
795
796 Password
797
798 =item sec_phrase
799
800 Optional security phrase
801
802 =item popnum
803
804 Optional Access number number
805
806 =back
807
808 Fields used when provisioning an svc_domain service:
809
810 =over 4
811
812 =item domain
813
814 Domain
815
816 =back
817
818 Fields used when provisioning an svc_phone service:
819
820 =over 4
821
822 =item phonenum
823
824 Phone number
825
826 =item pin
827
828 Voicemail PIN
829
830 =item sip_password
831
832 SIP password
833
834 =back
835
836 Fields used when provisioning an svc_external service:
837
838 =over 4
839
840 =item id
841
842 External numeric ID.
843
844 =item title
845
846 External text title.
847
848 =back
849
850 Returns a hash reference with a single key, B<error>, empty on success, or an
851 error message on errors.  The special error '_decline' is returned for
852 declined transactions.
853
854 =item renew_info
855
856 Provides useful info for early renewals.
857
858 Takes a hash reference as parameter with the following keys:
859
860 =over 4
861
862 =item session_id
863
864 Session identifier
865
866 =back
867
868 Returns a hash reference.  On errors, it contains a single key, B<error>, with
869 the error message.  Otherwise, contains a single key, B<dates>, pointing to
870 an array refernce of hash references.  Each hash reference contains the
871 following keys:
872
873 =over 4
874
875 =item bill_date
876
877 (Future) Bill date.  Indicates a future date for which billing could be run.
878 Specified as a integer UNIX timestamp.  Pass this value to the B<order_renew>
879 function.
880
881 =item bill_date_pretty
882
883 (Future) Bill date as a human-readable string.  (Convenience for display;
884 subject to change, so best not to parse for the date.)
885
886 =item amount
887
888 Base amount which will be charged if renewed early as of this date.
889
890 =item renew_date
891
892 Renewal date; i.e. even-futher future date at which the customer will be paid
893 through if the early renewal is completed with the given B<bill-date>.
894 Specified as a integer UNIX timestamp.
895
896 =item renew_date_pretty
897
898 Renewal date as a human-readable string.  (Convenience for display;
899 subject to change, so best not to parse for the date.)
900
901 =back
902
903 =item order_renew
904
905 Renews this customer early; i.e. runs billing for this customer in advance.
906
907 Takes a hash reference as parameter with the following keys:
908
909 =over 4
910
911 =item session_id
912
913 Session identifier
914
915 =item date
916
917 Integer date as returned by the B<renew_info> function, indicating the advance
918 date for which to run billing.
919
920 =back
921
922 Returns a hash reference with a single key, B<error>, empty on success, or an
923 error message on errors.
924
925 =item cancel_pkg
926
927 Cancels a package for this customer.
928
929 Takes a hash reference as parameter with the following keys:
930
931 =over 4
932
933 =item session_id
934
935 Session identifier
936
937 =item pkgpart
938
939 pkgpart of package to cancel
940
941 =back
942
943 Returns a hash reference with a single key, B<error>, empty on success, or an
944 error message on errors.
945
946 =back
947
948 =head1 SIGNUP FUNCTIONS
949
950 =over 4
951
952 =item signup_info HASHREF
953
954 Takes a hash reference as parameter with the following keys:
955
956 =over 4
957
958 =item session_id - Optional agent/reseller interface session
959
960 =back
961
962 Returns a hash reference containing information that may be useful in
963 displaying a signup page.  The hash reference contains the following keys:
964
965 =over 4
966
967 =item cust_main_county
968
969 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.
970
971 =item part_pkg
972
973 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
974 an agentnum specified explicitly via reseller interface session_id in the
975 options.
976
977 =item agent
978
979 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.
980
981 =item agentnum2part_pkg
982
983 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.
984
985 =item svc_acct_pop
986
987 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.
988
989 =item security_phrase
990
991 True if the "security_phrase" feature is enabled
992
993 =item payby
994
995 Array reference of acceptable payment types for signup
996
997 =over 4
998
999 =item CARD
1000
1001 credit card - automatic
1002
1003 =item DCRD
1004
1005 credit card - on-demand - version 1.5+ only
1006
1007 =item CHEK
1008
1009 electronic check - automatic
1010
1011 =item DCHK
1012
1013 electronic check - on-demand - version 1.5+ only
1014
1015 =item LECB
1016
1017 Phone bill billing
1018
1019 =item BILL
1020
1021 billing, not recommended for signups
1022
1023 =item COMP
1024
1025 free, definitely not recommended for signups
1026
1027 =item PREPAY
1028
1029 special billing type: applies a credit (see FS::prepay_credit) and sets billing type to BILL
1030
1031 =back
1032
1033 =item cvv_enabled
1034
1035 True if CVV features are available (1.5+ or 1.4.2 with CVV schema patch)
1036
1037 =item msgcat
1038
1039 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".
1040
1041 =item statedefault
1042
1043 Default state
1044
1045 =item countrydefault
1046
1047 Default country
1048
1049 =back
1050
1051 =item new_customer HASHREF
1052
1053 Creates a new customer.  Takes a hash reference as parameter with the
1054 following keys:
1055
1056 =over 4
1057
1058 =item first
1059
1060 first name (required)
1061
1062 =item last
1063
1064 last name (required)
1065
1066 =item ss
1067
1068 (not typically collected; mostly used for ACH transactions)
1069
1070 =item company
1071
1072 Company name
1073
1074 =item address1 (required)
1075
1076 Address line one
1077
1078 =item address2
1079
1080 Address line two
1081
1082 =item city (required)
1083
1084 City
1085
1086 =item county
1087
1088 County
1089
1090 =item state (required)
1091
1092 State
1093
1094 =item zip (required)
1095
1096 Zip or postal code
1097
1098 =item daytime
1099
1100 Daytime phone number
1101
1102 =item night
1103
1104 Evening phone number
1105
1106 =item fax
1107
1108 Fax number
1109
1110 =item payby
1111
1112 CARD, DCRD, CHEK, DCHK, LECB, BILL, COMP or PREPAY (see L</signup_info> (required)
1113
1114 =item payinfo
1115
1116 Card number for CARD/DCRD, account_number@aba_number for CHEK/DCHK, prepaid "pin" for PREPAY, purchase order number for BILL
1117
1118 =item paycvv
1119
1120 Credit card CVV2 number (1.5+ or 1.4.2 with CVV schema patch)
1121
1122 =item paydate
1123
1124 Expiration date for CARD/DCRD
1125
1126 =item payname
1127
1128 Exact name on credit card for CARD/DCRD, bank name for CHEK/DCHK
1129
1130 =item invoicing_list
1131
1132 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),
1133
1134 =item referral_custnum
1135
1136 referring customer number
1137
1138 =item agentnum
1139
1140 Agent number
1141
1142 =item pkgpart
1143
1144 pkgpart of initial package
1145
1146 =item username
1147
1148 Username
1149
1150 =item _password
1151
1152 Password
1153
1154 =item sec_phrase
1155
1156 Security phrase
1157
1158 =item popnum
1159
1160 Access number (index, not the literal number)
1161
1162 =item countrycode
1163
1164 Country code (to be provisioned as a service)
1165
1166 =item phonenum
1167
1168 Phone number (to be provisioned as a service)
1169
1170 =item pin
1171
1172 Voicemail PIN
1173
1174 =back
1175
1176 Returns a hash reference with the following keys:
1177
1178 =over 4
1179
1180 =item error
1181
1182 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)
1183
1184 =back
1185
1186 =item regionselector HASHREF | LIST
1187
1188 Takes as input a hashref or list of key/value pairs with the following keys:
1189
1190 =over 4
1191
1192 =item selected_county
1193
1194 Currently selected county
1195
1196 =item selected_state
1197
1198 Currently selected state
1199
1200 =item selected_country
1201
1202 Currently selected country
1203
1204 =item prefix
1205
1206 Specify a unique prefix string  if you intend to use the HTML output multiple time son one page.
1207
1208 =item onchange
1209
1210 Specify a javascript subroutine to call on changes
1211
1212 =item default_state
1213
1214 Default state
1215
1216 =item default_country
1217
1218 Default country
1219
1220 =item locales
1221
1222 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>.
1223
1224 =back
1225
1226 Returns a list consisting of three HTML fragments for county selection,
1227 state selection and country selection, respectively.
1228
1229 =cut
1230
1231 #false laziness w/FS::cust_main_county (this is currently the "newest" version)
1232 sub regionselector {
1233   my $param;
1234   if ( ref($_[0]) ) {
1235     $param = shift;
1236   } else {
1237     $param = { @_ };
1238   }
1239   $param->{'selected_country'} ||= $param->{'default_country'};
1240   $param->{'selected_state'} ||= $param->{'default_state'};
1241
1242   my $prefix = exists($param->{'prefix'}) ? $param->{'prefix'} : '';
1243
1244   my $countyflag = 0;
1245
1246   my %cust_main_county;
1247
1248 #  unless ( @cust_main_county ) { #cache 
1249     #@cust_main_county = qsearch('cust_main_county', {} );
1250     #foreach my $c ( @cust_main_county ) {
1251     foreach my $c ( @{ $param->{'locales'} } ) {
1252       #$countyflag=1 if $c->county;
1253       $countyflag=1 if $c->{county};
1254       #push @{$cust_main_county{$c->country}{$c->state}}, $c->county;
1255       #$cust_main_county{$c->country}{$c->state}{$c->county} = 1;
1256       $cust_main_county{$c->{country}}{$c->{state}}{$c->{county}} = 1;
1257     }
1258 #  }
1259   $countyflag=1 if $param->{selected_county};
1260
1261   my $script_html = <<END;
1262     <SCRIPT>
1263     function opt(what,value,text) {
1264       var optionName = new Option(text, value, false, false);
1265       var length = what.length;
1266       what.options[length] = optionName;
1267     }
1268     function ${prefix}country_changed(what) {
1269       country = what.options[what.selectedIndex].text;
1270       for ( var i = what.form.${prefix}state.length; i >= 0; i-- )
1271           what.form.${prefix}state.options[i] = null;
1272 END
1273       #what.form.${prefix}state.options[0] = new Option('', '', false, true);
1274
1275   foreach my $country ( sort keys %cust_main_county ) {
1276     $script_html .= "\nif ( country == \"$country\" ) {\n";
1277     foreach my $state ( sort keys %{$cust_main_county{$country}} ) {
1278       my $text = $state || '(n/a)';
1279       $script_html .= qq!opt(what.form.${prefix}state, "$state", "$text");\n!;
1280     }
1281     $script_html .= "}\n";
1282   }
1283
1284   $script_html .= <<END;
1285     }
1286     function ${prefix}state_changed(what) {
1287 END
1288
1289   if ( $countyflag ) {
1290     $script_html .= <<END;
1291       state = what.options[what.selectedIndex].text;
1292       country = what.form.${prefix}country.options[what.form.${prefix}country.selectedIndex].text;
1293       for ( var i = what.form.${prefix}county.length; i >= 0; i-- )
1294           what.form.${prefix}county.options[i] = null;
1295 END
1296
1297     foreach my $country ( sort keys %cust_main_county ) {
1298       $script_html .= "\nif ( country == \"$country\" ) {\n";
1299       foreach my $state ( sort keys %{$cust_main_county{$country}} ) {
1300         $script_html .= "\nif ( state == \"$state\" ) {\n";
1301           #foreach my $county ( sort @{$cust_main_county{$country}{$state}} ) {
1302           foreach my $county ( sort keys %{$cust_main_county{$country}{$state}} ) {
1303             my $text = $county || '(n/a)';
1304             $script_html .=
1305               qq!opt(what.form.${prefix}county, "$county", "$text");\n!;
1306           }
1307         $script_html .= "}\n";
1308       }
1309       $script_html .= "}\n";
1310     }
1311   }
1312
1313   $script_html .= <<END;
1314     }
1315     </SCRIPT>
1316 END
1317
1318   my $county_html = $script_html;
1319   if ( $countyflag ) {
1320     $county_html .= qq!<SELECT NAME="${prefix}county" onChange="$param->{'onchange'}">!;
1321     $county_html .= '</SELECT>';
1322   } else {
1323     $county_html .=
1324       qq!<INPUT TYPE="hidden" NAME="${prefix}county" VALUE="$param->{'selected_county'}">!;
1325   }
1326
1327   my $state_html = qq!<SELECT NAME="${prefix}state" !.
1328                    qq!onChange="${prefix}state_changed(this); $param->{'onchange'}">!;
1329   foreach my $state ( sort keys %{ $cust_main_county{$param->{'selected_country'}} } ) {
1330     my $text = $state || '(n/a)';
1331     my $selected = $state eq $param->{'selected_state'} ? 'SELECTED' : '';
1332     $state_html .= "\n<OPTION $selected VALUE=$state>$text</OPTION>"
1333   }
1334   $state_html .= '</SELECT>';
1335
1336   my $country_html = '';
1337   if ( scalar( keys %cust_main_county ) > 1 )  {
1338
1339     $country_html = qq(<SELECT NAME="${prefix}country" ).
1340                     qq(onChange="${prefix}country_changed(this); ).
1341                                  $param->{'onchange'}.
1342                                '"'.
1343                       '>';
1344     my $countrydefault = $param->{default_country} || 'US';
1345     foreach my $country (
1346       sort { ($b eq $countrydefault) <=> ($a eq $countrydefault) or $a cmp $b }
1347         keys %cust_main_county
1348     ) {
1349       my $selected = $country eq $param->{'selected_country'}
1350                        ? ' SELECTED'
1351                        : '';
1352       $country_html .= "\n<OPTION$selected>$country</OPTION>"
1353     }
1354     $country_html .= '</SELECT>';
1355   } else {
1356
1357     $country_html = qq(<INPUT TYPE="hidden" NAME="${prefix}country" ).
1358                             ' VALUE="'. (keys %cust_main_county )[0]. '">';
1359
1360   }
1361
1362   ($county_html, $state_html, $country_html);
1363
1364 }
1365
1366 sub regionselector_hashref {
1367   my ($county_html, $state_html, $country_html) = regionselector(@_);
1368   {
1369     'county_html'  => $county_html,
1370     'state_html'   => $state_html,
1371     'country_html' => $country_html,
1372   };
1373 }
1374
1375 #=item expselect HASHREF | LIST
1376 #
1377 #Takes as input a hashref or list of key/value pairs with the following keys:
1378 #
1379 #=over 4
1380 #
1381 #=item prefix - Specify a unique prefix string  if you intend to use the HTML output multiple time son one page.
1382 #
1383 #=item date - current date, in yyyy-mm-dd or m-d-yyyy format
1384 #
1385 #=back
1386
1387 =item expselect PREFIX [ DATE ]
1388
1389 Takes as input a unique prefix string and the current expiration date, in
1390 yyyy-mm-dd or m-d-yyyy format
1391
1392 Returns an HTML fragments for expiration date selection.
1393
1394 =cut
1395
1396 sub expselect {
1397   #my $param;
1398   #if ( ref($_[0]) ) {
1399   #  $param = shift;
1400   #} else {
1401   #  $param = { @_ };
1402   #my $prefix = $param->{'prefix'};
1403   #my $prefix = exists($param->{'prefix'}) ? $param->{'prefix'} : '';
1404   #my $date =   exists($param->{'date'})   ? $param->{'date'}   : '';
1405   my $prefix = shift;
1406   my $date = scalar(@_) ? shift : '';
1407
1408   my( $m, $y ) = ( 0, 0 );
1409   if ( $date  =~ /^(\d{4})-(\d{2})-\d{2}$/ ) { #PostgreSQL date format
1410     ( $m, $y ) = ( $2, $1 );
1411   } elsif ( $date =~ /^(\d{1,2})-(\d{1,2}-)?(\d{4}$)/ ) {
1412     ( $m, $y ) = ( $1, $3 );
1413   }
1414   my $return = qq!<SELECT NAME="$prefix!. qq!_month" SIZE="1">!;
1415   for ( 1 .. 12 ) {
1416     $return .= qq!<OPTION VALUE="$_"!;
1417     $return .= " SELECTED" if $_ == $m;
1418     $return .= ">$_";
1419   }
1420   $return .= qq!</SELECT>/<SELECT NAME="$prefix!. qq!_year" SIZE="1">!;
1421   my @t = localtime;
1422   my $thisYear = $t[5] + 1900;
1423   for ( ($thisYear > $y && $y > 0 ? $y : $thisYear) .. ($thisYear+10) ) {
1424     $return .= qq!<OPTION VALUE="$_"!;
1425     $return .= " SELECTED" if $_ == $y;
1426     $return .= ">$_";
1427   }
1428   $return .= "</SELECT>";
1429
1430   $return;
1431 }
1432
1433 =item popselector HASHREF | LIST
1434
1435 Takes as input a hashref or list of key/value pairs with the following keys:
1436
1437 =over 4
1438
1439 =item popnum
1440
1441 Access number number
1442
1443 =item pops
1444
1445 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>.
1446
1447 =back
1448
1449 Returns an HTML fragment for access number selection.
1450
1451 =cut
1452
1453 #horrible false laziness with FS/FS/svc_acct_pop.pm::popselector
1454 sub popselector {
1455   my $param;
1456   if ( ref($_[0]) ) {
1457     $param = shift;
1458   } else {
1459     $param = { @_ };
1460   }
1461   my $popnum = $param->{'popnum'};
1462   my $pops = $param->{'pops'};
1463
1464   return '<INPUT TYPE="hidden" NAME="popnum" VALUE="">' unless @$pops;
1465   return $pops->[0]{city}. ', '. $pops->[0]{state}.
1466          ' ('. $pops->[0]{ac}. ')/'. $pops->[0]{exch}. '-'. $pops->[0]{loc}.
1467          '<INPUT TYPE="hidden" NAME="popnum" VALUE="'. $pops->[0]{popnum}. '">'
1468     if scalar(@$pops) == 1;
1469
1470   my %pop = ();
1471   my %popnum2pop = ();
1472   foreach (@$pops) {
1473     push @{ $pop{ $_->{state} }->{ $_->{ac} } }, $_;
1474     $popnum2pop{$_->{popnum}} = $_;
1475   }
1476
1477   my $text = <<END;
1478     <SCRIPT>
1479     function opt(what,href,text) {
1480       var optionName = new Option(text, href, false, false)
1481       var length = what.length;
1482       what.options[length] = optionName;
1483     }
1484 END
1485
1486   my $init_popstate = $param->{'init_popstate'};
1487   if ( $init_popstate ) {
1488     $text .= '<INPUT TYPE="hidden" NAME="init_popstate" VALUE="'.
1489              $init_popstate. '">';
1490   } else {
1491     $text .= <<END;
1492       function acstate_changed(what) {
1493         state = what.options[what.selectedIndex].text;
1494         what.form.popac.options.length = 0
1495         what.form.popac.options[0] = new Option("Area code", "-1", false, true);
1496 END
1497   } 
1498
1499   my @states = $init_popstate ? ( $init_popstate ) : keys %pop;
1500   foreach my $state ( sort { $a cmp $b } @states ) {
1501     $text .= "\nif ( state == \"$state\" ) {\n" unless $init_popstate;
1502
1503     foreach my $ac ( sort { $a cmp $b } keys %{ $pop{$state} }) {
1504       $text .= "opt(what.form.popac, \"$ac\", \"$ac\");\n";
1505       if ($ac eq $param->{'popac'}) {
1506         $text .= "what.form.popac.options[what.form.popac.length-1].selected = true;\n";
1507       }
1508     }
1509     $text .= "}\n" unless $init_popstate;
1510   }
1511   $text .= "popac_changed(what.form.popac)}\n";
1512
1513   $text .= <<END;
1514   function popac_changed(what) {
1515     ac = what.options[what.selectedIndex].text;
1516     what.form.popnum.options.length = 0;
1517     what.form.popnum.options[0] = new Option("City", "-1", false, true);
1518
1519 END
1520
1521   foreach my $state ( @states ) {
1522     foreach my $popac ( keys %{ $pop{$state} } ) {
1523       $text .= "\nif ( ac == \"$popac\" ) {\n";
1524
1525       foreach my $pop ( @{$pop{$state}->{$popac}}) {
1526         my $o_popnum = $pop->{popnum};
1527         my $poptext =  $pop->{city}. ', '. $pop->{state}.
1528                        ' ('. $pop->{ac}. ')/'. $pop->{exch}. '-'. $pop->{loc};
1529
1530         $text .= "opt(what.form.popnum, \"$o_popnum\", \"$poptext\");\n";
1531         if ($popnum == $o_popnum) {
1532           $text .= "what.form.popnum.options[what.form.popnum.length-1].selected = true;\n";
1533         }
1534       }
1535       $text .= "}\n";
1536     }
1537   }
1538
1539
1540   $text .= "}\n</SCRIPT>\n";
1541
1542   $text .=
1543     qq!<TABLE CELLPADDING="0"><TR><TD><SELECT NAME="acstate"! .
1544     qq!SIZE=1 onChange="acstate_changed(this)"><OPTION VALUE=-1>State!;
1545   $text .= "<OPTION" . ($_ eq $param->{'acstate'} ? " SELECTED" : "") .
1546            ">$_" foreach sort { $a cmp $b } @states;
1547   $text .= '</SELECT>'; #callback? return 3 html pieces?  #'</TD>';
1548
1549   $text .=
1550     qq!<SELECT NAME="popac" SIZE=1 onChange="popac_changed(this)">!.
1551     qq!<OPTION>Area code</SELECT></TR><TR VALIGN="top">!;
1552
1553   $text .= qq!<TR><TD><SELECT NAME="popnum" SIZE=1 STYLE="width: 20em"><OPTION>City!;
1554
1555
1556   #comment this block to disable initial list polulation
1557   my @initial_select = ();
1558   if ( scalar( @$pops ) > 100 ) {
1559     push @initial_select, $popnum2pop{$popnum} if $popnum2pop{$popnum};
1560   } else {
1561     @initial_select = @$pops;
1562   }
1563   foreach my $pop ( sort { $a->{state} cmp $b->{state} } @initial_select ) {
1564     $text .= qq!<OPTION VALUE="!. $pop->{popnum}. '"'.
1565              ( ( $popnum && $pop->{popnum} == $popnum ) ? ' SELECTED' : '' ). ">".
1566              $pop->{city}. ', '. $pop->{state}.
1567                ' ('. $pop->{ac}. ')/'. $pop->{exch}. '-'. $pop->{loc};
1568   }
1569
1570   $text .= qq!</SELECT></TD></TR></TABLE>!;
1571
1572   $text;
1573
1574 }
1575
1576 =item domainselector HASHREF | LIST
1577
1578 Takes as input a hashref or list of key/value pairs with the following keys:
1579
1580 =over 4
1581
1582 =item pkgnum
1583
1584 Package number
1585
1586 =item domsvc
1587
1588 Service number of the selected item.
1589
1590 =back
1591
1592 Returns an HTML fragment for domain selection.
1593
1594 =cut
1595
1596 sub domainselector {
1597   my $param;
1598   if ( ref($_[0]) ) {
1599     $param = shift;
1600   } else {
1601     $param = { @_ };
1602   }
1603   my $domsvc= $param->{'domsvc'};
1604   my $rv = 
1605       domain_select_hash(map {$_ => $param->{$_}} qw(pkgnum svcpart pkgpart) );
1606   my $domains = $rv->{'domains'};
1607   $domsvc = $rv->{'domsvc'} unless $domsvc;
1608
1609   return '<INPUT TYPE="hidden" NAME="domsvc" VALUE="">'
1610     unless scalar(keys %$domains);
1611
1612   if (scalar(keys %$domains) == 1) {
1613     my $key;
1614     foreach(keys %$domains) {
1615       $key = $_;
1616     }
1617     return '<TR><TD ALIGN="right">Domain</TD><TD>'. $domains->{$key}.
1618            '<INPUT TYPE="hidden" NAME="domsvc" VALUE="'. $key. '"></TD></TR>'
1619   }
1620
1621   my $text .= qq!<TR><TD ALIGN="right">Domain</TD><TD><SELECT NAME="domsvc" SIZE=1 STYLE="width: 20em"><OPTION>(Choose Domain)!;
1622
1623
1624   foreach my $domain ( sort { $domains->{$a} cmp $domains->{$b} } keys %$domains ) {
1625     $text .= qq!<OPTION VALUE="!. $domain. '"'.
1626              ( ( $domsvc && $domain == $domsvc ) ? ' SELECTED' : '' ). ">".
1627              $domains->{$domain};
1628   }
1629
1630   $text .= qq!</SELECT></TD></TR>!;
1631
1632   $text;
1633
1634 }
1635
1636 =item didselector HASHREF | LIST
1637
1638 Takes as input a hashref or list of key/value pairs with the following keys:
1639
1640 =over 4
1641
1642 =item field
1643
1644 Field name for the returned HTML fragment.
1645
1646 =item svcpart
1647
1648 Service definition (see L<FS::part_svc>)
1649
1650 =back
1651
1652 Returns an HTML fragment for DID selection.
1653
1654 =cut
1655
1656 sub didselector {
1657   my $param;
1658   if ( ref($_[0]) ) {
1659     $param = shift;
1660   } else {
1661     $param = { @_ };
1662   }
1663
1664   my $rv = mason_comp( 'comp'=>'/elements/select-did.html',
1665                        'args'=>[ %$param ],
1666                      );
1667
1668   #hmm.
1669   $rv->{'error'} || $rv->{'output'};
1670
1671 }
1672
1673 =back
1674
1675 =head1 RESELLER FUNCTIONS
1676
1677 Note: Resellers can also use the B<signup_info> and B<new_customer> functions
1678 with their active session, and the B<customer_info> and B<order_pkg> functions
1679 with their active session and an additional I<custnum> parameter.
1680
1681 For the most part, development of the reseller web interface has been
1682 superceded by agent-virtualized access to the backend.
1683
1684 =over 4
1685
1686 =item agent_login
1687
1688 Agent login
1689
1690 =item agent_info
1691
1692 Agent info
1693
1694 =item agent_list_customers
1695
1696 List agent's customers.
1697
1698 =back
1699
1700 =head1 BUGS
1701
1702 =head1 SEE ALSO
1703
1704 L<freeside-selfservice-clientd>, L<freeside-selfservice-server>
1705
1706 =cut
1707
1708 1;
1709