put the footer in one frigging file, whew
[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 =item paybatch
476
477 Unique transaction identifier (prevents multiple charges), passed to the
478 process_payment function
479
480 =back
481
482 =item process_payment HASHREF
483
484 Processes a payment and possible change of address or payment type.  Takes a
485 hash reference as parameter with the following keys:
486
487 =over 4
488
489 =item session_id
490
491 Session identifier
492
493 =item amount
494
495 Amount
496
497 =item save
498
499 If true, address and card information entered will be saved for subsequent
500 transactions.
501
502 =item auto
503
504 If true, future credit card payments will be done automatically (sets payby to
505 CARD).  If false, future credit card payments will be done on-demand (sets
506 payby to DCRD).  This option only has meaning if B<save> is set true.  
507
508 =item payname
509
510 Name on card
511
512 =item address1
513
514 Address line one
515
516 =item address2
517
518 Address line two
519
520 =item city
521
522 City
523
524 =item state
525
526 State
527
528 =item zip
529
530 Zip or postal code
531
532 =item payinfo
533
534 Card number
535
536 =item month
537
538 Card expiration month
539
540 =item year
541
542 Card expiration year
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_order_renew
564
565 Combines the B<process_payment> and B<order_renew> functions in one step.  If
566 the payment processes sucessfully, the renewal is processed.  Takes a hash
567 reference 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 =item list_pkgs
573
574 Returns package information for this customer.  For more detail on services,
575 see L</list_svcs>.
576
577 Takes a hash reference as parameter with a single key: B<session_id>
578
579 Returns a hash reference containing customer package information.  The hash reference contains the following keys:
580
581 =over 4
582
583 =item custnum
584
585 Customer number
586
587 =item cust_pkg HASHREF
588
589 Array reference of hash references, each of which has the fields of a cust_pkg
590 record (see L<FS::cust_pkg>) as well as the fields below.  Note these are not
591 the internal FS:: objects, but hash references of columns and values.
592
593 =over 4
594
595 =item part_pkg fields
596
597 All fields of part_pkg for this specific cust_pkg (be careful with this
598 information - it may reveal more about your available packages than you would
599 like users to know in aggregate) 
600
601 =cut
602
603 #XXX pare part_pkg fields down to a more secure subset
604
605 =item part_svc
606
607 An array of hash references indicating information on unprovisioned services
608 available for provisioning for this specific cust_pkg.  Each has the following
609 keys:
610
611 =over 4
612
613 =item part_svc fields
614
615 All fields of part_svc (be careful with this information - it may reveal more
616 about your available packages than you would like users to know in aggregate) 
617
618 =cut
619
620 #XXX pare part_svc fields down to a more secure subset
621
622 =back
623
624 =item cust_svc
625
626 An array of hash references indicating information on the customer services
627 already provisioned for this specific cust_pkg.  Each has the following keys:
628
629 =over 4
630
631 =item label
632
633 Array reference with three elements:
634
635 =over 4
636
637 =item
638
639 Name of this service
640
641 =item
642
643 Meaningful user-specific identifier for the service (i.e. username, domain or mail alias)
644
645 =item
646
647 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 (part_pkg)
658
659 =item pkgnum
660
661 Customer package (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 error
672
673 Empty on success, or an error message on errors.
674
675 =back
676
677 =item list_svcs
678
679 Returns service information for this customer.
680
681 Takes a hash reference as parameter with a single key: B<session_id>
682
683 Returns a hash reference containing customer package information.  The hash reference contains the following keys:
684
685 =over 4
686
687 =item custnum
688
689 Customer number
690
691 =item svcs
692
693 An array of hash references indicating information on all of this customer's
694 services.  Each has the following keys:
695
696 =over 4
697
698 =item svcnum
699
700 Primary key for this service
701
702 =item label
703
704 Name of this service
705
706 =item value
707
708 Meaningful user-specific identifier for the service (i.e. username, domain, or
709 mail alias).
710
711 =back
712
713 Account (svc_acct) services also have the following keys:
714
715 =item username
716
717 Username
718
719 =item email
720
721 username@domain
722
723 =item seconds
724
725 Seconds remaining
726
727 =item upbytes
728
729 Upload bytes remaining
730
731 =item downbytes
732
733 Download bytes remaining
734
735 =item totalbytes
736
737 Total bytes remaining
738
739 =item recharge_amount
740
741 Cost of a recharge
742
743 =item recharge_seconds
744
745 Number of seconds gained by recharge
746
747 =item recharge_upbytes
748
749 Number of upload bytes gained by recharge
750
751 =item recharge_downbytes
752
753 Number of download bytes gained by recharge
754
755 =item recharge_totalbytes
756
757 Number of total bytes gained by recharge
758
759 =back
760
761 =back
762
763 =item order_pkg
764
765 Orders a package for this customer.
766
767 Takes a hash reference as parameter with the following keys:
768
769 =over 4
770
771 =item session_id
772
773 Session identifier
774
775 =item pkgpart
776
777 pkgpart of package to order
778
779 =item svcpart
780
781 optional svcpart, required only if the package definition does not contain
782 one svc_acct service definition with quantity 1 (it may contain others with
783 quantity >1)
784
785 =item username
786
787 Username
788
789 =item _password
790
791 Password
792
793 =item sec_phrase
794
795 Optional security phrase
796
797 =item popnum
798
799 Optional Access number number
800
801 =back
802
803 Returns a hash reference with a single key, B<error>, empty on success, or an
804 error message on errors.  The special error '_decline' is returned for
805 declined transactions.
806
807 =item renew_info
808
809 Provides useful info for early renewals.
810
811 Takes a hash reference as parameter with the following keys:
812
813 =over 4
814
815 =item session_id
816
817 Session identifier
818
819 =back
820
821 Returns a hash reference.  On errors, it contains a single key, B<error>, with
822 the error message.  Otherwise, contains a single key, B<dates>, pointing to
823 an array refernce of hash references.  Each hash reference contains the
824 following keys:
825
826 =over 4
827
828 =item bill_date
829
830 (Future) Bill date.  Indicates a future date for which billing could be run.
831 Specified as a integer UNIX timestamp.  Pass this value to the B<order_renew>
832 function.
833
834 =item bill_date_pretty
835
836 (Future) Bill date as a human-readable string.  (Convenience for display;
837 subject to change, so best not to parse for the date.)
838
839 =item amount
840
841 Base amount which will be charged if renewed early as of this date.
842
843 =item renew_date
844
845 Renewal date; i.e. even-futher future date at which the customer will be paid
846 through if the early renewal is completed with the given B<bill-date>.
847 Specified as a integer UNIX timestamp.
848
849 =item renew_date_pretty
850
851 Renewal date as a human-readable string.  (Convenience for display;
852 subject to change, so best not to parse for the date.)
853
854 =back
855
856 =item order_renew
857
858 Renews this customer early; i.e. runs billing for this customer in advance.
859
860 Takes a hash reference as parameter with the following keys:
861
862 =over 4
863
864 =item session_id
865
866 Session identifier
867
868 =item date
869
870 Integer date as returned by the B<renew_info> function, indicating the advance
871 date for which to run billing.
872
873 =back
874
875 Returns a hash reference with a single key, B<error>, empty on success, or an
876 error message on errors.
877
878 =item cancel_pkg
879
880 Cancels a package for this customer.
881
882 Takes a hash reference as parameter with the following keys:
883
884 =over 4
885
886 =item session_id
887
888 Session identifier
889
890 =item pkgpart
891
892 pkgpart of package to cancel
893
894 =back
895
896 Returns a hash reference with a single key, B<error>, empty on success, or an
897 error message on errors.
898
899 =back
900
901 =head1 SIGNUP FUNCTIONS
902
903 =over 4
904
905 =item signup_info HASHREF
906
907 Takes a hash reference as parameter with the following keys:
908
909 =over 4
910
911 =item session_id - Optional agent/reseller interface session
912
913 =back
914
915 Returns a hash reference containing information that may be useful in
916 displaying a signup page.  The hash reference contains the following keys:
917
918 =over 4
919
920 =item cust_main_county
921
922 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.
923
924 =item part_pkg
925
926 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
927 an agentnum specified explicitly via reseller interface session_id in the
928 options.
929
930 =item agent
931
932 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.
933
934 =item agentnum2part_pkg
935
936 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.
937
938 =item svc_acct_pop
939
940 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.
941
942 =item security_phrase
943
944 True if the "security_phrase" feature is enabled
945
946 =item payby
947
948 Array reference of acceptable payment types for signup
949
950 =over 4
951
952 =item CARD
953
954 credit card - automatic
955
956 =item DCRD
957
958 credit card - on-demand - version 1.5+ only
959
960 =item CHEK
961
962 electronic check - automatic
963
964 =item DCHK
965
966 electronic check - on-demand - version 1.5+ only
967
968 =item LECB
969
970 Phone bill billing
971
972 =item BILL
973
974 billing, not recommended for signups
975
976 =item COMP
977
978 free, definitely not recommended for signups
979
980 =item PREPAY
981
982 special billing type: applies a credit (see FS::prepay_credit) and sets billing type to BILL
983
984 =back
985
986 =item cvv_enabled
987
988 True if CVV features are available (1.5+ or 1.4.2 with CVV schema patch)
989
990 =item msgcat
991
992 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".
993
994 =item statedefault
995
996 Default state
997
998 =item countrydefault
999
1000 Default country
1001
1002 =back
1003
1004 =item new_customer HASHREF
1005
1006 Creates a new customer.  Takes a hash reference as parameter with the
1007 following keys:
1008
1009 =over 4
1010
1011 =item first
1012
1013 first name (required)
1014
1015 =item last
1016
1017 last name (required)
1018
1019 =item ss
1020
1021 (not typically collected; mostly used for ACH transactions)
1022
1023 =item company
1024
1025 Company name
1026
1027 =item address1 (required)
1028
1029 Address line one
1030
1031 =item address2
1032
1033 Address line two
1034
1035 =item city (required)
1036
1037 City
1038
1039 =item county
1040
1041 County
1042
1043 =item state (required)
1044
1045 State
1046
1047 =item zip (required)
1048
1049 Zip or postal code
1050
1051 =item daytime
1052
1053 Daytime phone number
1054
1055 =item night
1056
1057 Evening phone number
1058
1059 =item fax
1060
1061 Fax number
1062
1063 =item payby
1064
1065 CARD, DCRD, CHEK, DCHK, LECB, BILL, COMP or PREPAY (see L</signup_info> (required)
1066
1067 =item payinfo
1068
1069 Card number for CARD/DCRD, account_number@aba_number for CHEK/DCHK, prepaid "pin" for PREPAY, purchase order number for BILL
1070
1071 =item paycvv
1072
1073 Credit card CVV2 number (1.5+ or 1.4.2 with CVV schema patch)
1074
1075 =item paydate
1076
1077 Expiration date for CARD/DCRD
1078
1079 =item payname
1080
1081 Exact name on credit card for CARD/DCRD, bank name for CHEK/DCHK
1082
1083 =item invoicing_list
1084
1085 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),
1086
1087 =item referral_custnum
1088
1089 referring customer number
1090
1091 =item agentnum
1092
1093 Agent number
1094
1095 =item pkgpart
1096
1097 pkgpart of initial package
1098
1099 =item username
1100
1101 Username
1102
1103 =item _password
1104
1105 Password
1106
1107 =item sec_phrase
1108
1109 Security phrase
1110
1111 =item popnum
1112
1113 Access number (index, not the literal number)
1114
1115 =item countrycode
1116
1117 Country code (to be provisioned as a service)
1118
1119 =item phonenum
1120
1121 Phone number (to be provisioned as a service)
1122
1123 =item pin
1124
1125 Voicemail PIN
1126
1127 =back
1128
1129 Returns a hash reference with the following keys:
1130
1131 =over 4
1132
1133 =item error
1134
1135 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)
1136
1137 =back
1138
1139 =item regionselector HASHREF | LIST
1140
1141 Takes as input a hashref or list of key/value pairs with the following keys:
1142
1143 =over 4
1144
1145 =item selected_county
1146
1147 Currently selected county
1148
1149 =item selected_state
1150
1151 Currently selected state
1152
1153 =item selected_country
1154
1155 Currently selected country
1156
1157 =item prefix
1158
1159 Specify a unique prefix string  if you intend to use the HTML output multiple time son one page.
1160
1161 =item onchange
1162
1163 Specify a javascript subroutine to call on changes
1164
1165 =item default_state
1166
1167 Default state
1168
1169 =item default_country
1170
1171 Default country
1172
1173 =item locales
1174
1175 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>.
1176
1177 =back
1178
1179 Returns a list consisting of three HTML fragments for county selection,
1180 state selection and country selection, respectively.
1181
1182 =cut
1183
1184 #false laziness w/FS::cust_main_county (this is currently the "newest" version)
1185 sub regionselector {
1186   my $param;
1187   if ( ref($_[0]) ) {
1188     $param = shift;
1189   } else {
1190     $param = { @_ };
1191   }
1192   $param->{'selected_country'} ||= $param->{'default_country'};
1193   $param->{'selected_state'} ||= $param->{'default_state'};
1194
1195   my $prefix = exists($param->{'prefix'}) ? $param->{'prefix'} : '';
1196
1197   my $countyflag = 0;
1198
1199   my %cust_main_county;
1200
1201 #  unless ( @cust_main_county ) { #cache 
1202     #@cust_main_county = qsearch('cust_main_county', {} );
1203     #foreach my $c ( @cust_main_county ) {
1204     foreach my $c ( @{ $param->{'locales'} } ) {
1205       #$countyflag=1 if $c->county;
1206       $countyflag=1 if $c->{county};
1207       #push @{$cust_main_county{$c->country}{$c->state}}, $c->county;
1208       #$cust_main_county{$c->country}{$c->state}{$c->county} = 1;
1209       $cust_main_county{$c->{country}}{$c->{state}}{$c->{county}} = 1;
1210     }
1211 #  }
1212   $countyflag=1 if $param->{selected_county};
1213
1214   my $script_html = <<END;
1215     <SCRIPT>
1216     function opt(what,value,text) {
1217       var optionName = new Option(text, value, false, false);
1218       var length = what.length;
1219       what.options[length] = optionName;
1220     }
1221     function ${prefix}country_changed(what) {
1222       country = what.options[what.selectedIndex].text;
1223       for ( var i = what.form.${prefix}state.length; i >= 0; i-- )
1224           what.form.${prefix}state.options[i] = null;
1225 END
1226       #what.form.${prefix}state.options[0] = new Option('', '', false, true);
1227
1228   foreach my $country ( sort keys %cust_main_county ) {
1229     $script_html .= "\nif ( country == \"$country\" ) {\n";
1230     foreach my $state ( sort keys %{$cust_main_county{$country}} ) {
1231       my $text = $state || '(n/a)';
1232       $script_html .= qq!opt(what.form.${prefix}state, "$state", "$text");\n!;
1233     }
1234     $script_html .= "}\n";
1235   }
1236
1237   $script_html .= <<END;
1238     }
1239     function ${prefix}state_changed(what) {
1240 END
1241
1242   if ( $countyflag ) {
1243     $script_html .= <<END;
1244       state = what.options[what.selectedIndex].text;
1245       country = what.form.${prefix}country.options[what.form.${prefix}country.selectedIndex].text;
1246       for ( var i = what.form.${prefix}county.length; i >= 0; i-- )
1247           what.form.${prefix}county.options[i] = null;
1248 END
1249
1250     foreach my $country ( sort keys %cust_main_county ) {
1251       $script_html .= "\nif ( country == \"$country\" ) {\n";
1252       foreach my $state ( sort keys %{$cust_main_county{$country}} ) {
1253         $script_html .= "\nif ( state == \"$state\" ) {\n";
1254           #foreach my $county ( sort @{$cust_main_county{$country}{$state}} ) {
1255           foreach my $county ( sort keys %{$cust_main_county{$country}{$state}} ) {
1256             my $text = $county || '(n/a)';
1257             $script_html .=
1258               qq!opt(what.form.${prefix}county, "$county", "$text");\n!;
1259           }
1260         $script_html .= "}\n";
1261       }
1262       $script_html .= "}\n";
1263     }
1264   }
1265
1266   $script_html .= <<END;
1267     }
1268     </SCRIPT>
1269 END
1270
1271   my $county_html = $script_html;
1272   if ( $countyflag ) {
1273     $county_html .= qq!<SELECT NAME="${prefix}county" onChange="$param->{'onchange'}">!;
1274     $county_html .= '</SELECT>';
1275   } else {
1276     $county_html .=
1277       qq!<INPUT TYPE="hidden" NAME="${prefix}county" VALUE="$param->{'selected_county'}">!;
1278   }
1279
1280   my $state_html = qq!<SELECT NAME="${prefix}state" !.
1281                    qq!onChange="${prefix}state_changed(this); $param->{'onchange'}">!;
1282   foreach my $state ( sort keys %{ $cust_main_county{$param->{'selected_country'}} } ) {
1283     my $text = $state || '(n/a)';
1284     my $selected = $state eq $param->{'selected_state'} ? 'SELECTED' : '';
1285     $state_html .= "\n<OPTION $selected VALUE=$state>$text</OPTION>"
1286   }
1287   $state_html .= '</SELECT>';
1288
1289   my $country_html = '';
1290   if ( scalar( keys %cust_main_county ) > 1 )  {
1291
1292     $country_html = qq(<SELECT NAME="${prefix}country" ).
1293                     qq(onChange="${prefix}country_changed(this); ).
1294                                  $param->{'onchange'}.
1295                                '"'.
1296                       '>';
1297     my $countrydefault = $param->{default_country} || 'US';
1298     foreach my $country (
1299       sort { ($b eq $countrydefault) <=> ($a eq $countrydefault) or $a cmp $b }
1300         keys %cust_main_county
1301     ) {
1302       my $selected = $country eq $param->{'selected_country'}
1303                        ? ' SELECTED'
1304                        : '';
1305       $country_html .= "\n<OPTION$selected>$country</OPTION>"
1306     }
1307     $country_html .= '</SELECT>';
1308   } else {
1309
1310     $country_html = qq(<INPUT TYPE="hidden" NAME="${prefix}country" ).
1311                             ' VALUE="'. (keys %cust_main_county )[0]. '">';
1312
1313   }
1314
1315   ($county_html, $state_html, $country_html);
1316
1317 }
1318
1319 sub regionselector_hashref {
1320   my ($county_html, $state_html, $country_html) = regionselector(@_);
1321   {
1322     'county_html'  => $county_html,
1323     'state_html'   => $state_html,
1324     'country_html' => $country_html,
1325   };
1326 }
1327
1328 #=item expselect HASHREF | LIST
1329 #
1330 #Takes as input a hashref or list of key/value pairs with the following keys:
1331 #
1332 #=over 4
1333 #
1334 #=item prefix - Specify a unique prefix string  if you intend to use the HTML output multiple time son one page.
1335 #
1336 #=item date - current date, in yyyy-mm-dd or m-d-yyyy format
1337 #
1338 #=back
1339
1340 =item expselect PREFIX [ DATE ]
1341
1342 Takes as input a unique prefix string and the current expiration date, in
1343 yyyy-mm-dd or m-d-yyyy format
1344
1345 Returns an HTML fragments for expiration date selection.
1346
1347 =cut
1348
1349 sub expselect {
1350   #my $param;
1351   #if ( ref($_[0]) ) {
1352   #  $param = shift;
1353   #} else {
1354   #  $param = { @_ };
1355   #my $prefix = $param->{'prefix'};
1356   #my $prefix = exists($param->{'prefix'}) ? $param->{'prefix'} : '';
1357   #my $date =   exists($param->{'date'})   ? $param->{'date'}   : '';
1358   my $prefix = shift;
1359   my $date = scalar(@_) ? shift : '';
1360
1361   my( $m, $y ) = ( 0, 0 );
1362   if ( $date  =~ /^(\d{4})-(\d{2})-\d{2}$/ ) { #PostgreSQL date format
1363     ( $m, $y ) = ( $2, $1 );
1364   } elsif ( $date =~ /^(\d{1,2})-(\d{1,2}-)?(\d{4}$)/ ) {
1365     ( $m, $y ) = ( $1, $3 );
1366   }
1367   my $return = qq!<SELECT NAME="$prefix!. qq!_month" SIZE="1">!;
1368   for ( 1 .. 12 ) {
1369     $return .= qq!<OPTION VALUE="$_"!;
1370     $return .= " SELECTED" if $_ == $m;
1371     $return .= ">$_";
1372   }
1373   $return .= qq!</SELECT>/<SELECT NAME="$prefix!. qq!_year" SIZE="1">!;
1374   my @t = localtime;
1375   my $thisYear = $t[5] + 1900;
1376   for ( ($thisYear > $y && $y > 0 ? $y : $thisYear) .. ($thisYear+10) ) {
1377     $return .= qq!<OPTION VALUE="$_"!;
1378     $return .= " SELECTED" if $_ == $y;
1379     $return .= ">$_";
1380   }
1381   $return .= "</SELECT>";
1382
1383   $return;
1384 }
1385
1386 =item popselector HASHREF | LIST
1387
1388 Takes as input a hashref or list of key/value pairs with the following keys:
1389
1390 =over 4
1391
1392 =item popnum
1393
1394 Access number number
1395
1396 =item pops
1397
1398 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>.
1399
1400 =back
1401
1402 Returns an HTML fragment for access number selection.
1403
1404 =cut
1405
1406 #horrible false laziness with FS/FS/svc_acct_pop.pm::popselector
1407 sub popselector {
1408   my $param;
1409   if ( ref($_[0]) ) {
1410     $param = shift;
1411   } else {
1412     $param = { @_ };
1413   }
1414   my $popnum = $param->{'popnum'};
1415   my $pops = $param->{'pops'};
1416
1417   return '<INPUT TYPE="hidden" NAME="popnum" VALUE="">' unless @$pops;
1418   return $pops->[0]{city}. ', '. $pops->[0]{state}.
1419          ' ('. $pops->[0]{ac}. ')/'. $pops->[0]{exch}. '-'. $pops->[0]{loc}.
1420          '<INPUT TYPE="hidden" NAME="popnum" VALUE="'. $pops->[0]{popnum}. '">'
1421     if scalar(@$pops) == 1;
1422
1423   my %pop = ();
1424   my %popnum2pop = ();
1425   foreach (@$pops) {
1426     push @{ $pop{ $_->{state} }->{ $_->{ac} } }, $_;
1427     $popnum2pop{$_->{popnum}} = $_;
1428   }
1429
1430   my $text = <<END;
1431     <SCRIPT>
1432     function opt(what,href,text) {
1433       var optionName = new Option(text, href, false, false)
1434       var length = what.length;
1435       what.options[length] = optionName;
1436     }
1437 END
1438
1439   my $init_popstate = $param->{'init_popstate'};
1440   if ( $init_popstate ) {
1441     $text .= '<INPUT TYPE="hidden" NAME="init_popstate" VALUE="'.
1442              $init_popstate. '">';
1443   } else {
1444     $text .= <<END;
1445       function acstate_changed(what) {
1446         state = what.options[what.selectedIndex].text;
1447         what.form.popac.options.length = 0
1448         what.form.popac.options[0] = new Option("Area code", "-1", false, true);
1449 END
1450   } 
1451
1452   my @states = $init_popstate ? ( $init_popstate ) : keys %pop;
1453   foreach my $state ( sort { $a cmp $b } @states ) {
1454     $text .= "\nif ( state == \"$state\" ) {\n" unless $init_popstate;
1455
1456     foreach my $ac ( sort { $a cmp $b } keys %{ $pop{$state} }) {
1457       $text .= "opt(what.form.popac, \"$ac\", \"$ac\");\n";
1458       if ($ac eq $param->{'popac'}) {
1459         $text .= "what.form.popac.options[what.form.popac.length-1].selected = true;\n";
1460       }
1461     }
1462     $text .= "}\n" unless $init_popstate;
1463   }
1464   $text .= "popac_changed(what.form.popac)}\n";
1465
1466   $text .= <<END;
1467   function popac_changed(what) {
1468     ac = what.options[what.selectedIndex].text;
1469     what.form.popnum.options.length = 0;
1470     what.form.popnum.options[0] = new Option("City", "-1", false, true);
1471
1472 END
1473
1474   foreach my $state ( @states ) {
1475     foreach my $popac ( keys %{ $pop{$state} } ) {
1476       $text .= "\nif ( ac == \"$popac\" ) {\n";
1477
1478       foreach my $pop ( @{$pop{$state}->{$popac}}) {
1479         my $o_popnum = $pop->{popnum};
1480         my $poptext =  $pop->{city}. ', '. $pop->{state}.
1481                        ' ('. $pop->{ac}. ')/'. $pop->{exch}. '-'. $pop->{loc};
1482
1483         $text .= "opt(what.form.popnum, \"$o_popnum\", \"$poptext\");\n";
1484         if ($popnum == $o_popnum) {
1485           $text .= "what.form.popnum.options[what.form.popnum.length-1].selected = true;\n";
1486         }
1487       }
1488       $text .= "}\n";
1489     }
1490   }
1491
1492
1493   $text .= "}\n</SCRIPT>\n";
1494
1495   $text .=
1496     qq!<TABLE CELLPADDING="0"><TR><TD><SELECT NAME="acstate"! .
1497     qq!SIZE=1 onChange="acstate_changed(this)"><OPTION VALUE=-1>State!;
1498   $text .= "<OPTION" . ($_ eq $param->{'acstate'} ? " SELECTED" : "") .
1499            ">$_" foreach sort { $a cmp $b } @states;
1500   $text .= '</SELECT>'; #callback? return 3 html pieces?  #'</TD>';
1501
1502   $text .=
1503     qq!<SELECT NAME="popac" SIZE=1 onChange="popac_changed(this)">!.
1504     qq!<OPTION>Area code</SELECT></TR><TR VALIGN="top">!;
1505
1506   $text .= qq!<TR><TD><SELECT NAME="popnum" SIZE=1 STYLE="width: 20em"><OPTION>City!;
1507
1508
1509   #comment this block to disable initial list polulation
1510   my @initial_select = ();
1511   if ( scalar( @$pops ) > 100 ) {
1512     push @initial_select, $popnum2pop{$popnum} if $popnum2pop{$popnum};
1513   } else {
1514     @initial_select = @$pops;
1515   }
1516   foreach my $pop ( sort { $a->{state} cmp $b->{state} } @initial_select ) {
1517     $text .= qq!<OPTION VALUE="!. $pop->{popnum}. '"'.
1518              ( ( $popnum && $pop->{popnum} == $popnum ) ? ' SELECTED' : '' ). ">".
1519              $pop->{city}. ', '. $pop->{state}.
1520                ' ('. $pop->{ac}. ')/'. $pop->{exch}. '-'. $pop->{loc};
1521   }
1522
1523   $text .= qq!</SELECT></TD></TR></TABLE>!;
1524
1525   $text;
1526
1527 }
1528
1529 =item domainselector HASHREF | LIST
1530
1531 Takes as input a hashref or list of key/value pairs with the following keys:
1532
1533 =over 4
1534
1535 =item pkgnum
1536
1537 Package number
1538
1539 =item domsvc
1540
1541 Service number of the selected item.
1542
1543 =back
1544
1545 Returns an HTML fragment for domain selection.
1546
1547 =cut
1548
1549 sub domainselector {
1550   my $param;
1551   if ( ref($_[0]) ) {
1552     $param = shift;
1553   } else {
1554     $param = { @_ };
1555   }
1556   my $domsvc= $param->{'domsvc'};
1557   my $rv = 
1558       domain_select_hash(map {$_ => $param->{$_}} qw(pkgnum svcpart pkgpart) );
1559   my $domains = $rv->{'domains'};
1560   $domsvc = $rv->{'domsvc'} unless $domsvc;
1561
1562   return '<INPUT TYPE="hidden" NAME="domsvc" VALUE="">'
1563     unless scalar(keys %$domains);
1564
1565   if (scalar(keys %$domains) == 1) {
1566     my $key;
1567     foreach(keys %$domains) {
1568       $key = $_;
1569     }
1570     return '<TR><TD ALIGN="right">Domain</TD><TD>'. $domains->{$key}.
1571            '<INPUT TYPE="hidden" NAME="domsvc" VALUE="'. $key. '"></TD></TR>'
1572   }
1573
1574   my $text .= qq!<TR><TD ALIGN="right">Domain</TD><TD><SELECT NAME="domsvc" SIZE=1 STYLE="width: 20em"><OPTION>(Choose Domain)!;
1575
1576
1577   foreach my $domain ( sort { $domains->{$a} cmp $domains->{$b} } keys %$domains ) {
1578     $text .= qq!<OPTION VALUE="!. $domain. '"'.
1579              ( ( $domsvc && $domain == $domsvc ) ? ' SELECTED' : '' ). ">".
1580              $domains->{$domain};
1581   }
1582
1583   $text .= qq!</SELECT></TD></TR>!;
1584
1585   $text;
1586
1587 }
1588
1589 =item didselector HASHREF | LIST
1590
1591 Takes as input a hashref or list of key/value pairs with the following keys:
1592
1593 =over 4
1594
1595 =item field
1596
1597 =item svcpart
1598
1599 =back
1600
1601 Returns an HTML fragment for DID selection.
1602
1603 =cut
1604
1605 sub didselector {
1606   my $param;
1607   if ( ref($_[0]) ) {
1608     $param = shift;
1609   } else {
1610     $param = { @_ };
1611   }
1612
1613   my $rv = mason_comp( 'comp'=>'/elements/select-did.html',
1614                        'args'=>[ %$param ],
1615                      );
1616
1617   #hmm.
1618   $rv->{'error'} || $rv->{'output'};
1619
1620 }
1621
1622 =back
1623
1624 =head1 RESELLER FUNCTIONS
1625
1626 Note: Resellers can also use the B<signup_info> and B<new_customer> functions
1627 with their active session, and the B<customer_info> and B<order_pkg> functions
1628 with their active session and an additional I<custnum> parameter.
1629
1630 =over 4
1631
1632 =item agent_login
1633
1634 =item agent_info
1635
1636 =item agent_list_customers
1637
1638 =back
1639
1640 =head1 BUGS
1641
1642 =head1 SEE ALSO
1643
1644 L<freeside-selfservice-clientd>, L<freeside-selfservice-server>
1645
1646 =cut
1647
1648 1;
1649