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