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