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