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