contact self-service pw changes, RT#37023
[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_banner_image'        => 'MyAccount/login_banner_image',
30   'login'                     => 'MyAccount/login',
31   'logout'                    => 'MyAccount/logout',
32   'switch_acct'               => 'MyAccount/switch_acct',
33   'customer_info'             => 'MyAccount/customer_info',
34   'customer_info_short'       => 'MyAccount/customer_info_short',
35
36   'contact_passwd'            => 'MyAccount/contact/contact_passwd',
37   'list_contacts'             => 'MyAccount/contact/list_contacts',
38   'edit_contact'              => 'MyAccount/contact/edit_contact',
39   'delete_contact'            => 'MyAccount/contact/delete_contact',
40
41   'billing_history'           => 'MyAccount/billing_history',
42   'edit_info'                 => 'MyAccount/edit_info',     #add to ss cgi!
43   'invoice'                   => 'MyAccount/invoice',
44   'invoice_pdf'               => 'MyAccount/invoice_pdf',
45   'legacy_invoice'            => 'MyAccount/legacy_invoice',
46   'legacy_invoice_pdf'        => 'MyAccount/legacy_invoice_pdf',
47   'invoice_logo'              => 'MyAccount/invoice_logo',
48   'list_invoices'             => 'MyAccount/list_invoices', #?
49   'cancel'                    => 'MyAccount/cancel',        #add to ss cgi!
50   'payment_info'              => 'MyAccount/payment_info',
51   'payment_info_renew_info'   => 'MyAccount/payment_info_renew_info',
52   'process_payment'           => 'MyAccount/process_payment',
53   'store_payment'             => 'MyAccount/store_payment',
54   'process_stored_payment'    => 'MyAccount/process_stored_payment',
55   'process_payment_order_pkg' => 'MyAccount/process_payment_order_pkg',
56   'process_payment_change_pkg' => 'MyAccount/process_payment_change_pkg',
57   'process_payment_order_renew' => 'MyAccount/process_payment_order_renew',
58   'process_prepay'            => 'MyAccount/process_prepay',
59   'realtime_collect'          => 'MyAccount/realtime_collect',
60   'list_pkgs'                 => 'MyAccount/list_pkgs',     #add to ss (added?)
61   'list_svcs'                 => 'MyAccount/list_svcs',     #add to ss (added?)
62   'list_svc_usage'            => 'MyAccount/list_svc_usage',   
63   'svc_status_html'           => 'MyAccount/svc_status_html',
64   'svc_status_hash'           => 'MyAccount/svc_status_hash',
65   'set_svc_status_hash'       => 'MyAccount/set_svc_status_hash',
66   'set_svc_status_listadd'    => 'MyAccount/set_svc_status_listadd',
67   'set_svc_status_listdel'    => 'MyAccount/set_svc_status_listdel',
68   'set_svc_status_vacationadd'=> 'MyAccount/set_svc_status_vacationadd',
69   'set_svc_status_vacationdel'=> 'MyAccount/set_svc_status_vacationdel',
70   'acct_forward_info'         => 'MyAccount/acct_forward_info',
71   'process_acct_forward'      => 'MyAccount/process_acct_forward',
72   'list_dsl_devices'          => 'MyAccount/list_dsl_devices',   
73   'add_dsl_device'            => 'MyAccount/add_dsl_device',   
74   'delete_dsl_device'         => 'MyAccount/delete_dsl_device',   
75   'port_graph'                => 'MyAccount/port_graph',   
76   'list_cdr_usage'            => 'MyAccount/list_cdr_usage',   
77   'list_support_usage'        => 'MyAccount/list_support_usage',   
78   'order_pkg'                 => 'MyAccount/order_pkg',     #add to ss cgi!
79   'change_pkg'                => 'MyAccount/change_pkg', 
80   'order_recharge'            => 'MyAccount/order_recharge',
81   'renew_info'                => 'MyAccount/renew_info',
82   'order_renew'               => 'MyAccount/order_renew',
83   'cancel_pkg'                => 'MyAccount/cancel_pkg',    #add to ss cgi!
84   'suspend_pkg'               => 'MyAccount/suspend_pkg',   #add to ss cgi!
85   'charge'                    => 'MyAccount/charge',        #?
86   'part_svc_info'             => 'MyAccount/part_svc_info',
87   'provision_acct'            => 'MyAccount/provision_acct',
88   'provision_phone'           => 'MyAccount/provision_phone',
89   'provision_pbx'             => 'MyAccount/provision_pbx',
90   'provision_external'        => 'MyAccount/provision_external',
91   'provision_forward'         => 'MyAccount/provision_forward',
92   'unprovision_svc'           => 'MyAccount/unprovision_svc',
93   'myaccount_passwd'          => 'MyAccount/myaccount_passwd',
94   'reset_passwd'              => 'MyAccount/reset_passwd',
95   'check_reset_passwd'        => 'MyAccount/check_reset_passwd',
96   'process_reset_passwd'      => 'MyAccount/process_reset_passwd',
97   'list_tickets'              => 'MyAccount/list_tickets',
98   'create_ticket'             => 'MyAccount/create_ticket',
99   'get_ticket'                => 'MyAccount/get_ticket',
100   'adjust_ticket_priority'    => 'MyAccount/adjust_ticket_priority',
101   'did_report'                => 'MyAccount/did_report',
102   'signup_info'               => 'Signup/signup_info',
103   'skin_info'                 => 'MyAccount/skin_info',
104   'access_info'               => 'MyAccount/access_info',
105   'domain_select_hash'        => 'Signup/domain_select_hash',  # expose?
106   'new_customer'              => 'Signup/new_customer',
107   'new_customer_minimal'      => 'Signup/new_customer_minimal',
108   'capture_payment'           => 'Signup/capture_payment',
109   #N/A 'clear_signup_cache'        => 'Signup/clear_cache',
110   'new_agent'                 => 'Agent/new_agent',
111   'agent_login'               => 'Agent/agent_login',
112   'agent_logout'              => 'Agent/agent_logout',
113   'agent_info'                => 'Agent/agent_info',
114   'agent_list_customers'      => 'Agent/agent_list_customers',
115   'check_username'            => 'Agent/check_username',
116   'suspend_username'          => 'Agent/suspend_username',
117   'unsuspend_username'        => 'Agent/unsuspend_username',
118   'mason_comp'                => 'MasonComponent/mason_comp',
119   'call_time'                 => 'PrepaidPhone/call_time',
120   'call_time_nanpa'           => 'PrepaidPhone/call_time_nanpa',
121   'phonenum_balance'          => 'PrepaidPhone/phonenum_balance',
122
123   'start_thirdparty'          => 'MyAccount/start_thirdparty',
124   'finish_thirdparty'         => 'MyAccount/finish_thirdparty',
125
126   'list_quotations'           => 'MyAccount/quotation/list_quotations',
127   'quotation_new'             => 'MyAccount/quotation/quotation_new',
128   'quotation_delete'          => 'MyAccount/quotation/quotation_delete',
129   'quotation_info'            => 'MyAccount/quotation/quotation_info',
130   'quotation_print'           => 'MyAccount/quotation/quotation_print',
131   'quotation_add_pkg'         => 'MyAccount/quotation/quotation_add_pkg',
132   'quotation_remove_pkg'      => 'MyAccount/quotation/quotation_remove_pkg',
133   'quotation_order'           => 'MyAccount/quotation/quotation_order',
134
135 );
136 @EXPORT_OK = (
137   keys(%autoload),
138   qw( regionselector regionselector_hashref location_form
139       expselect popselector domainselector didselector
140     )
141 );
142
143 $ENV{'PATH'} ='/usr/bin:/usr/ucb:/bin';
144 $ENV{'SHELL'} = '/bin/sh';
145 $ENV{'IFS'} = " \t\n";
146 $ENV{'CDPATH'} = '';
147 $ENV{'ENV'} = '';
148 $ENV{'BASH_ENV'} = '';
149
150 #you can add BEGIN { $FS::SelfService::skip_uid_check = 1; } 
151 #if you grant appropriate permissions to whatever user
152 my $freeside_uid = scalar(getpwnam('freeside'));
153 die "not running as the freeside user\n"
154   if $> != $freeside_uid && ! $skip_uid_check;
155
156 -e $dir or die "FATAL: $dir doesn't exist!";
157 -d $dir or die "FATAL: $dir isn't a directory!";
158 -r $dir or die "FATAL: Can't read $dir as freeside user!";
159 -x $dir or die "FATAL: $dir not searchable (executable) as freeside user!";
160
161 foreach my $autoload ( keys %autoload ) {
162
163   my $eval =
164   "sub $autoload { ". '
165                    my $param;
166                    if ( ref($_[0]) ) {
167                      $param = shift;
168                    } else {
169                      #warn scalar(@_). ": ". join(" / ", @_);
170                      $param = { @_ };
171                    }
172
173                    $param->{_packet} = \''. $autoload{$autoload}. '\';
174
175                    simple_packet($param);
176                  }';
177
178   eval $eval;
179   die $@ if $@;
180
181 }
182
183 sub simple_packet {
184   my $packet = shift;
185   warn "sending ". $packet->{_packet}. " to server"
186     if $DEBUG;
187   socket(SOCK, PF_UNIX, SOCK_STREAM, 0) or die "socket: $!";
188   connect(SOCK, sockaddr_un($socket)) or die "connect to $socket: $!";
189   nstore_fd($packet, \*SOCK) or die "can't send packet: $!";
190   SOCK->flush;
191
192   #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
193
194   #block until there is a message on socket
195 #  my $w = new IO::Select;
196 #  $w->add(\*SOCK);
197 #  my @wait = $w->can_read;
198
199   warn "reading message from server"
200     if $DEBUG;
201
202   my $return = fd_retrieve(\*SOCK) or die "error reading result: $!";
203   die $return->{'_error'} if defined $return->{_error} && $return->{_error};
204
205   warn "returning message to client"
206     if $DEBUG;
207
208   $return;
209 }
210
211 =head1 NAME
212
213 FS::SelfService - Freeside self-service API
214
215 =head1 SYNOPSIS
216
217   # password and shell account changes
218   use FS::SelfService qw(passwd chfn chsh);
219
220   # "my account" functionality
221   use FS::SelfService qw( login customer_info invoice cancel payment_info process_payment );
222
223   #new-style login with an email address and password
224   # can also be used for svc_acct login, set $emailaddress to username@domain
225   my $rv = login ( { 'email'    => $emailaddress,
226                      'password' => $password,
227                    },
228                  );
229   if ( $rv->{'error'} ) {
230     #handle login error...
231   } else {
232     #successful login
233     $session_id = $rv->{'session_id'};
234   }
235
236   #classic svc_acct-based login with separate username and password
237   my $rv = login( { 'username' => $username,
238                     'domain'   => $domain,
239                     'password' => $password,
240                   }
241                 );
242   if ( $rv->{'error'} ) {
243     #handle login error...
244   } else {
245     #successful login
246     $session_id = $rv->{'session_id'};
247   }
248
249   #svc_phone login with phone number and PIN
250   my $rv = login( { 'username' => $phone_number,
251                     'domain'   => 'svc_phone',
252                     'password' => $pin,
253                   }
254                 );
255   if ( $rv->{'error'} ) {
256     #handle login error...
257   } else {
258     #successful login
259     $session_id = $rv->{'session_id'};
260   }
261
262   my $customer_info = customer_info( { 'session_id' => $session_id } );
263
264   #payment_info and process_payment are available in 1.5+ only
265   my $payment_info = payment_info( { 'session_id' => $session_id } );
266
267   #!!! process_payment example
268
269   #!!! list_pkgs example
270
271   #!!! order_pkg example
272
273   #quoting a package, then ordering after confirmation
274
275   my $rv = quotation_new({ 'session_id' => $session_id });
276   my $qnum = $rv->{quotationnum};
277   #  add packages to the quotation
278   $rv = quotation_add_pkg({ 'session_id'   => $session_id,
279                             'quotationnum' => $qnum,
280                             'pkgpart'      => $pkgpart,
281                             'quantity'     => $quantity, # defaults to 1
282                           });
283   #  repeat until all packages are added
284   #  view the pricing information
285   $rv = quotation_info({ 'session_id'   => $session_id,
286                          'quotationnum' => $qnum,
287                       });
288   print "Total setup charges: ".$rv->{total_setup}."\n".
289         "Total recurring charges: ".$rv->{total_recur}."\n";
290   #  quotation_info also provides a detailed breakdown of charges, in
291   #  $rv->{sections}.
292
293   #  ask customer for confirmation, then:
294   $rv = quotation_order({ 'session_id'   => $session_id,
295                           'quotationnum' => $qnum,
296                         });
297
298   #!!! cancel_pkg example
299
300   # signup functionality
301   use FS::SelfService qw( signup_info new_customer new_customer_minimal );
302
303   my $signup_info = signup_info;
304
305   $rv = new_customer( {
306                         'first'            => $first,
307                         'last'             => $last,
308                         'company'          => $company,
309                         'address1'         => $address1,
310                         'address2'         => $address2,
311                         'city'             => $city,
312                         'state'            => $state,
313                         'zip'              => $zip,
314                         'country'          => $country,
315                         'daytime'          => $daytime,
316                         'night'            => $night,
317                         'fax'              => $fax,
318                         'payby'            => $payby,
319                         'payinfo'          => $payinfo,
320                         'paycvv'           => $paycvv,
321                         'paystart_month'   => $paystart_month
322                         'paystart_year'    => $paystart_year,
323                         'payissue'         => $payissue,
324                         'payip'            => $payip
325                         'paydate'          => $paydate,
326                         'payname'          => $payname,
327                         'invoicing_list'   => $invoicing_list,
328                         'referral_custnum' => $referral_custnum,
329                         'agentnum'         => $agentnum,
330                         'pkgpart'          => $pkgpart,
331
332                         'username'         => $username,
333                         '_password'        => $password,
334                         'popnum'           => $popnum,
335                         #OR
336                         'countrycode'      => 1,
337                         'phonenum'         => $phonenum,
338                         'pin'              => $pin,
339                       }
340                     );
341   
342   my $error = $rv->{'error'};
343   if ( $error eq '_decline' ) {
344     print_decline();
345   } elsif ( $error ) {
346     reprint_signup();
347   } else {
348     print_success();
349   }
350
351 =head1 DESCRIPTION
352
353 Use this API to implement your own client "self-service" module.
354
355 If you just want to customize the look of the existing "self-service" module,
356 see XXXX instead.
357
358 =head1 PASSWORD, GECOS, SHELL CHANGING FUNCTIONS
359
360 =over 4
361
362 =item passwd
363
364 Changes the password for an existing user in svc_acct.  Takes a hash
365 reference with the following keys:
366
367 =over 4
368
369 =item username
370
371 Username of the account (required)
372
373 =item domain
374
375 Domain of the account (required)
376
377 =item old_password
378
379 Old password (required)
380
381 =item new_password
382  
383 New password (required)
384
385 =item new_gecos
386
387 New gecos
388
389 =item new_shell
390
391 New Shell
392
393 =back 
394
395 =item chfn
396
397 =item chsh
398
399 =back
400
401 =head1 "MY ACCOUNT" FUNCTIONS
402
403 =over 4
404
405 =item login HASHREF
406
407 Creates a user session.  Takes a hash reference as parameter with the
408 following keys:
409
410 =over 4
411
412 =item email
413
414 Email address (username@domain), instead of username and domain.  Required for
415 contact-based self-service login, can also be used for svc_acct-based login.
416
417 =item username
418
419 Username
420
421 =item domain
422
423 Domain
424
425 =item password
426
427 Password
428
429 =back
430
431 Returns a hash reference with the following keys:
432
433 =over 4
434
435 =item error
436
437 Empty on success, or an error message on errors.
438
439 =item session_id
440
441 Session identifier for successful logins
442
443 =back
444
445 =item customer_info HASHREF
446
447 Returns general customer information.
448
449 Takes a hash reference as parameter with a single key: B<session_id>
450
451 Returns a hash reference with the following keys:
452
453 =over 4
454
455 =item name
456
457 Customer name
458
459 =item balance
460
461 Balance owed
462
463 =item open
464
465 Array reference of hash references of open inoices.  Each hash reference has
466 the following keys: invnum, date, owed
467
468 =item small_custview
469
470 An HTML fragment containing shipping and billing addresses.
471
472 =item The following fields are also returned
473
474 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
475
476 =back
477
478 =item edit_info HASHREF
479
480 Takes a hash reference as parameter with any of the following keys:
481
482 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
483
484 If a field exists, the customer record is updated with the new value of that
485 field.  If a field does not exist, that field is not changed on the customer
486 record.
487
488 Returns a hash reference with a single key, B<error>, empty on success, or an
489 error message on errors
490
491 =item invoice HASHREF
492
493 Returns an invoice.  Takes a hash reference as parameter with two keys:
494 session_id and invnum
495
496 Returns a hash reference with the following keys:
497
498 =over 4
499
500 =item error
501
502 Empty on success, or an error message on errors
503
504 =item invnum
505
506 Invoice number
507
508 =item invoice_text
509
510 Invoice text
511
512 =back
513
514 =item list_invoices HASHREF
515
516 Returns a list of all customer invoices.  Takes a hash reference with a single
517 key, session_id.
518
519 Returns a hash reference with the following keys:
520
521 =over 4
522
523 =item error
524
525 Empty on success, or an error message on errors
526
527 =item invoices
528
529 Reference to array of hash references with the following keys:
530
531 =over 4
532
533 =item invnum
534
535 Invoice ID
536
537 =item _date
538
539 Invoice date, in UNIX epoch time
540
541 =back
542
543 =back
544
545 =item cancel HASHREF
546
547 Cancels this customer.
548
549 Takes a hash reference as parameter with a single key: B<session_id>
550
551 Returns a hash reference with a single key, B<error>, which is empty on
552 success or an error message on errors.
553
554 =item payment_info HASHREF
555
556 Returns information that may be useful in displaying a payment page.
557
558 Takes a hash reference as parameter with a single key: B<session_id>.
559
560 Returns a hash reference with the following keys:
561
562 =over 4
563
564 =item error
565
566 Empty on success, or an error message on errors
567
568 =item balance
569
570 Balance owed
571
572 =item payname
573
574 Exact name on credit card (CARD/DCRD)
575
576 =item address1
577
578 Address line one
579
580 =item address2
581
582 Address line two
583
584 =item city
585
586 City
587
588 =item state
589
590 State
591
592 =item zip
593
594 Zip or postal code
595
596 =item payby
597
598 Customer's current default payment type.
599
600 =item card_type
601
602 For CARD/DCRD payment types, the card type (Visa card, MasterCard, Discover card, American Express card, etc.)
603
604 =item payinfo
605
606 For CARD/DCRD payment types, the card number
607
608 =item month
609
610 For CARD/DCRD payment types, expiration month
611
612 =item year
613
614 For CARD/DCRD payment types, expiration year
615
616 =item cust_main_county
617
618 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.
619
620 =item states
621
622 Array reference of all states in the current default country.
623
624 =item card_types
625
626 Hash reference of card types; keys are card types, values are the exact strings
627 passed to the process_payment function
628
629 =cut
630
631 #this doesn't actually work yet
632 #
633 #=item paybatch
634 #
635 #Unique transaction identifier (prevents multiple charges), passed to the
636 #process_payment function
637
638 =back
639
640 =item process_payment HASHREF
641
642 Processes a payment and possible change of address or payment type.  Takes a
643 hash reference as parameter with the following keys:
644
645 =over 4
646
647 =item session_id
648
649 Session identifier
650
651 =item amount
652
653 Amount
654
655 =item save
656
657 If true, address and card information entered will be saved for subsequent
658 transactions.
659
660 =item auto
661
662 If true, future credit card payments will be done automatically (sets payby to
663 CARD).  If false, future credit card payments will be done on-demand (sets
664 payby to DCRD).  This option only has meaning if B<save> is set true.  
665
666 =item payname
667
668 Name on card
669
670 =item address1
671
672 Address line one
673
674 =item address2
675
676 Address line two
677
678 =item city
679
680 City
681
682 =item state
683
684 State
685
686 =item zip
687
688 Zip or postal code
689
690 =item country
691
692 Two-letter country code
693
694 =item payinfo
695
696 Card number
697
698 =item month
699
700 Card expiration month
701
702 =item year
703
704 Card expiration year
705
706 =cut
707
708 #this doesn't actually work yet
709 #
710 #=item paybatch
711 #
712 #Unique transaction identifier, returned from the payment_info function.
713 #Prevents multiple charges.
714
715 =back
716
717 Returns a hash reference with a single key, B<error>, empty on success, or an
718 error message on errors.
719
720 =item process_payment_order_pkg
721
722 Combines the B<process_payment> and B<order_pkg> functions in one step.  If the
723 payment processes sucessfully, the package is ordered.  Takes a hash reference
724 as parameter with the keys of both methods.
725
726 Returns a hash reference with a single key, B<error>, empty on success, or an
727 error message on errors.
728
729 =item process_payment_change_pkg
730
731 Combines the B<process_payment> and B<change_pkg> functions in one step.  If the
732 payment processes sucessfully, the package is ordered.  Takes a hash reference
733 as parameter with the keys of both methods.
734
735 Returns a hash reference with a single key, B<error>, empty on success, or an
736 error message on errors.
737
738
739 =item process_payment_order_renew
740
741 Combines the B<process_payment> and B<order_renew> functions in one step.  If
742 the payment processes sucessfully, the renewal is processed.  Takes a hash
743 reference as parameter with the keys of both methods.
744
745 Returns a hash reference with a single key, B<error>, empty on success, or an
746 error message on errors.
747
748 =item list_pkgs
749
750 Returns package information for this customer.  For more detail on services,
751 see L</list_svcs>.
752
753 Takes a hash reference as parameter with a single key: B<session_id>
754
755 Returns a hash reference containing customer package information.  The hash reference contains the following keys:
756
757 =over 4
758
759 =item custnum
760
761 Customer number
762
763 =item error
764
765 Empty on success, or an error message on errors.
766
767 =item cust_pkg HASHREF
768
769 Array reference of hash references, each of which has the fields of a cust_pkg
770 record (see L<FS::cust_pkg>) as well as the fields below.  Note these are not
771 the internal FS:: objects, but hash references of columns and values.
772
773 =over 4
774
775 =item part_pkg fields
776
777 All fields of part_pkg for this specific cust_pkg (be careful with this
778 information - it may reveal more about your available packages than you would
779 like users to know in aggregate) 
780
781 =cut
782
783 #XXX pare part_pkg fields down to a more secure subset
784
785 =item part_svc
786
787 An array of hash references indicating information on unprovisioned services
788 available for provisioning for this specific cust_pkg.  Each has the following
789 keys:
790
791 =over 4
792
793 =item part_svc fields
794
795 All fields of part_svc (be careful with this information - it may reveal more
796 about your available packages than you would like users to know in aggregate) 
797
798 =cut
799
800 #XXX pare part_svc fields down to a more secure subset
801
802 =back
803
804 =item cust_svc
805
806 An array of hash references indicating information on the customer services
807 already provisioned for this specific cust_pkg.  Each has the following keys:
808
809 =over 4
810
811 =item label
812
813 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.
814
815 =back
816
817 =item svcnum
818
819 Primary key for this service
820
821 =item svcpart
822
823 Service definition (see L<FS::part_svc>)
824
825 =item pkgnum
826
827 Customer package (see L<FS::cust_pkg>)
828
829 =item overlimit
830
831 Blank if the service is not over limit, or the date the service exceeded its usage limit (as a UNIX timestamp).
832
833 =back
834
835 =back
836
837 =item list_svcs
838
839 Returns service information for this customer.
840
841 Takes a hash reference as parameter with a single key: B<session_id>
842
843 Returns a hash reference containing customer package information.  The hash reference contains the following keys:
844
845 =over 4
846
847 =item custnum
848
849 Customer number
850
851 =item svcs
852
853 An array of hash references indicating information on all of this customer's
854 services.  Each has the following keys:
855
856 =over 4
857
858 =item svcnum
859
860 Primary key for this service
861
862 =item label
863
864 Name of this service
865
866 =item value
867
868 Meaningful user-specific identifier for the service (i.e. username, domain, or
869 mail alias).
870
871 =back
872
873 Account (svc_acct) services also have the following keys:
874
875 =over 4
876
877 =item username
878
879 Username
880
881 =item email
882
883 username@domain
884
885 =item seconds
886
887 Seconds remaining
888
889 =item upbytes
890
891 Upload bytes remaining
892
893 =item downbytes
894
895 Download bytes remaining
896
897 =item totalbytes
898
899 Total bytes remaining
900
901 =item recharge_amount
902
903 Cost of a recharge
904
905 =item recharge_seconds
906
907 Number of seconds gained by recharge
908
909 =item recharge_upbytes
910
911 Number of upload bytes gained by recharge
912
913 =item recharge_downbytes
914
915 Number of download bytes gained by recharge
916
917 =item recharge_totalbytes
918
919 Number of total bytes gained by recharge
920
921 =back
922
923 =back
924
925 =item order_pkg
926
927 Orders a package for this customer.
928
929 Takes a hash reference as parameter with the following keys:
930
931 =over 4
932
933 =item session_id
934
935 Session identifier
936
937 =item pkgpart
938
939 Package to order (see L<FS::part_pkg>).
940
941 =item quantity
942
943 Quantity for this package order (default 1).
944
945 =item locationnum
946
947 Optional locationnum for this package order, for existing locations.
948
949 Or, for new locations, pass the following fields: address1*, address2, city*,
950 county, state*, zip*, country.  (* = required in this case)
951
952 =item address1
953
954 =item address 2
955
956 =item city
957
958 =item 
959
960 =item svcpart
961
962 Service to order (see L<FS::part_svc>).
963
964 Normally optional; required only to provision a non-svc_acct service, or if the
965 package definition does not contain one svc_acct service definition with
966 quantity 1 (it may contain others with quantity >1).  A svcpart of "none" can
967 also be specified to indicate that no initial service should be provisioned.
968
969 =back
970
971 Fields used when provisioning an svc_acct service:
972
973 =over 4
974
975 =item username
976
977 Username
978
979 =item _password
980
981 Password
982
983 =item sec_phrase
984
985 Optional security phrase
986
987 =item popnum
988
989 Optional Access number number
990
991 =back
992
993 Fields used when provisioning an svc_domain service:
994
995 =over 4
996
997 =item domain
998
999 Domain
1000
1001 =back
1002
1003 Fields used when provisioning an svc_phone service:
1004
1005 =over 4
1006
1007 =item phonenum
1008
1009 Phone number
1010
1011 =item pin
1012
1013 Voicemail PIN
1014
1015 =item sip_password
1016
1017 SIP password
1018
1019 =back
1020
1021 Fields used when provisioning an svc_external service:
1022
1023 =over 4
1024
1025 =item id
1026
1027 External numeric ID.
1028
1029 =item title
1030
1031 External text title.
1032
1033 =back
1034
1035 Fields used when provisioning an svc_pbx service:
1036
1037 =over 4
1038
1039 =item id
1040
1041 Numeric ID.
1042
1043 =item name
1044
1045 Text name.
1046
1047 =back
1048
1049 Returns a hash reference with a single key, B<error>, empty on success, or an
1050 error message on errors.  The special error '_decline' is returned for
1051 declined transactions.
1052
1053 =item change_pkg
1054
1055 Changes a package for this customer.
1056
1057 Takes a hash reference as parameter with the following keys:
1058
1059 =over 4
1060
1061 =item session_id
1062
1063 Session identifier
1064
1065 =item pkgnum
1066
1067 Existing customer package.
1068
1069 =item pkgpart
1070
1071 New package to order (see L<FS::part_pkg>).
1072
1073 =item quantity
1074
1075 Quantity for this package order (default 1).
1076
1077 =back
1078
1079 Returns a hash reference with the following keys:
1080
1081 =over 4
1082
1083 =item error
1084
1085 Empty on success, or an error message on errors.  
1086
1087 =item pkgnum
1088
1089 On success, the new pkgnum
1090
1091 =back
1092
1093
1094 =item renew_info
1095
1096 Provides useful info for early renewals.
1097
1098 Takes a hash reference as parameter with the following keys:
1099
1100 =over 4
1101
1102 =item session_id
1103
1104 Session identifier
1105
1106 =back
1107
1108 Returns a hash reference.  On errors, it contains a single key, B<error>, with
1109 the error message.  Otherwise, contains a single key, B<dates>, pointing to
1110 an array refernce of hash references.  Each hash reference contains the
1111 following keys:
1112
1113 =over 4
1114
1115 =item bill_date
1116
1117 (Future) Bill date.  Indicates a future date for which billing could be run.
1118 Specified as a integer UNIX timestamp.  Pass this value to the B<order_renew>
1119 function.
1120
1121 =item bill_date_pretty
1122
1123 (Future) Bill date as a human-readable string.  (Convenience for display;
1124 subject to change, so best not to parse for the date.)
1125
1126 =item amount
1127
1128 Base amount which will be charged if renewed early as of this date.
1129
1130 =item renew_date
1131
1132 Renewal date; i.e. even-futher future date at which the customer will be paid
1133 through if the early renewal is completed with the given B<bill-date>.
1134 Specified as a integer UNIX timestamp.
1135
1136 =item renew_date_pretty
1137
1138 Renewal date as a human-readable string.  (Convenience for display;
1139 subject to change, so best not to parse for the date.)
1140
1141 =item pkgnum
1142
1143 Package that will be renewed.
1144
1145 =item expire_date
1146
1147 Expiration date of the package that will be renewed.
1148
1149 =item expire_date_pretty
1150
1151 Expiration date of the package that will be renewed, as a human-readable
1152 string.  (Convenience for display; subject to change, so best not to parse for
1153 the date.)
1154
1155 =back
1156
1157 =item order_renew
1158
1159 Renews this customer early; i.e. runs billing for this customer in advance.
1160
1161 Takes a hash reference as parameter with the following keys:
1162
1163 =over 4
1164
1165 =item session_id
1166
1167 Session identifier
1168
1169 =item date
1170
1171 Integer date as returned by the B<renew_info> function, indicating the advance
1172 date for which to run billing.
1173
1174 =back
1175
1176 Returns a hash reference with a single key, B<error>, empty on success, or an
1177 error message on errors.
1178
1179 =item cancel_pkg
1180
1181 Cancels a package for this customer.
1182
1183 Takes a hash reference as parameter with the following keys:
1184
1185 =over 4
1186
1187 =item session_id
1188
1189 Session identifier
1190
1191 =item pkgpart
1192
1193 pkgpart of package to cancel
1194
1195 =back
1196
1197 Returns a hash reference with a single key, B<error>, empty on success, or an
1198 error message on errors.
1199
1200 =item provision_acct 
1201
1202 Provisions an account (svc_acct).
1203
1204 Takes a hash reference as parameter with the following keys:
1205
1206 =over 4
1207
1208 =item session_id
1209
1210 Session identifier
1211
1212 =item pkgnum
1213
1214 pkgnum of package into which this service is provisioned
1215
1216 =item svcpart
1217
1218 svcpart or service definition to provision
1219
1220 =item username
1221
1222 =item domsvc
1223
1224 =item _password
1225
1226 =back
1227
1228 =item provision_phone
1229
1230 Provisions a phone number (svc_phone).
1231
1232 Takes a hash reference as parameter with the following keys:
1233
1234 =over 4
1235
1236 =item session_id
1237
1238 Session identifier
1239
1240 =item pkgnum
1241
1242 pkgnum of package into which this service is provisioned
1243
1244 =item svcpart
1245
1246 svcpart or service definition to provision
1247
1248 =item countrycode
1249
1250 =item phonenum
1251
1252 =item address1
1253
1254 =item address2
1255
1256 =item city
1257
1258 =item county
1259
1260 =item state
1261
1262 =item zip
1263
1264 =item country
1265
1266 E911 Address (optional)
1267
1268 =back
1269
1270 =item provision_pbx
1271
1272 Provisions a customer PBX (svc_pbx).
1273
1274 Takes a hash reference as parameter with the following keys:
1275
1276 =over 4
1277
1278 =item session_id
1279
1280 Session identifier
1281
1282 =item pkgnum
1283
1284 pkgnum of package into which this service is provisioned
1285
1286 =item svcpart
1287
1288 svcpart or service definition to provision
1289
1290 =item id
1291
1292 =item title
1293
1294 =item max_extensions
1295
1296 =item max_simultaneous
1297
1298 =item ip_addr
1299
1300 =back
1301
1302 =item provision_external
1303
1304 Provisions an external service (svc_external).
1305
1306 Takes a hash reference as parameter with the following keys:
1307
1308 =over 4
1309
1310 =item session_id
1311
1312 Session identifier
1313
1314 =item pkgnum
1315
1316 pkgnum of package into which this service is provisioned
1317
1318 =item svcpart
1319
1320 svcpart or service definition to provision
1321
1322 =item id
1323
1324 =item title
1325
1326 =back
1327
1328 =back
1329
1330 =head2 "MY ACCOUNT" CONTACT FUNCTIONS
1331
1332 =over 4
1333
1334 =item contact_passwd
1335
1336 Changes the password for the currently-logged in contact.
1337
1338 Takes a hash reference as parameter with the following keys:
1339
1340 =over 4
1341
1342 =item session_id
1343
1344 =item new_password
1345
1346 =back
1347
1348 Returns a hash reference with a single parameter, B<error>, which contains an
1349 error message, or empty on success.
1350
1351 =item list_contacts
1352
1353 =item edit_contact
1354
1355 Updates information for the currently-logged in contact, or (optionally) the
1356 specified contact
1357
1358 Takes a hash reference as parameter with the following keys:
1359
1360 =over 4
1361
1362 =item session_id
1363
1364 =item contactnum
1365
1366 If already logged in as a contact, this is optional.
1367
1368 =item first
1369
1370 =item last
1371
1372 =item emailaddress
1373
1374 =back
1375
1376 Returns a hash reference with a single parameter, B<error>, which contains an
1377 error message, or empty on success.
1378
1379 =item delete_contact
1380
1381 =head2 "MY ACCOUNT" QUOTATION FUNCTIONS
1382
1383 All of these functions require the user to be logged in, and the 'session_id'
1384 key to be included in the argument hashref.`
1385
1386 =over 4
1387
1388 =item list_quotations HASHREF
1389
1390 Returns a hashref listing this customer's active self-service quotations.
1391 Contents are:
1392
1393 =over 4
1394
1395 =item quotations
1396
1397 an arrayref containing an element for each quotation.
1398
1399 =item quotationnum
1400
1401 the primary key
1402
1403 =item _date
1404
1405 the date it was started
1406
1407 =item num_pkgs
1408
1409 the number of packages
1410
1411 =item total_setup
1412
1413 the sum of setup fees
1414
1415 =item total_recur
1416
1417 the sum of recurring charges
1418
1419 =back
1420
1421 =item quotation_new HASHREF
1422
1423 Creates an empty quotation and returns a hashref containing 'quotationnum',
1424 the primary key of the new quotation.
1425
1426 =item quotation_delete HASHREF
1427
1428 Disables (does not really delete) a quotation. Takes the following arguments:
1429
1430 =over 4
1431
1432 =item session_id
1433
1434 =item quotationnum - the quotation to delete
1435
1436 =back
1437
1438 Returns 'error' => a string, which will be empty on success.
1439
1440 =item quotation_info HASHREF
1441
1442 Returns total and detailed pricing information on a quotation.
1443
1444 Takes the following arguments:
1445
1446 =over 4
1447
1448 =item session_id
1449
1450 =item quotationnum - the quotation to return
1451
1452 =back
1453
1454 Returns a hashref containing:
1455
1456 - total_setup, the total of setup fees (and their taxes)
1457 - total_recur, the total of all recurring charges (and their taxes)
1458 - sections, an arrayref containing an element for each quotation section.
1459   - description, a line of text describing the group of charges
1460   - subtotal, the total of charges in this group (if appropriate)
1461   - detail_items, an arrayref of line items
1462     - pkgnum, the reference number of the package
1463     - description, the package name (or tax name)
1464     - quantity
1465     - amount, the amount charged
1466     If the detail item represents a subtotal, it will instead contain:
1467     - total_item: description of the subtotal
1468     - total_amount: the subtotal amount
1469
1470
1471 =item quotation_print HASHREF
1472
1473 Renders the quotation as HTML or PDF. Takes the following arguments:
1474
1475 =over 4
1476
1477 =item session_id
1478
1479 =item quotationnum - the quotation to return
1480
1481 =item format - 'html' or 'pdf'
1482
1483 =back
1484
1485 Returns a hashref containing 'document', the contents of the file.
1486
1487 =item quotation_add_pkg HASHREF
1488
1489 Adds a package to a quotation. Takes the following arguments:
1490
1491 =over 4
1492
1493 =item session_id
1494
1495 =item pkgpart - the package to add
1496
1497 =item quotationnum - the quotation to add it to
1498
1499 =item quantity - the package quantity (defaults to 1)
1500
1501 =item address1, address2, city, state, zip, country - address fields to set
1502 the service location
1503
1504 =back
1505
1506 Returns 'error' => a string, which will be empty on success.
1507
1508 =item quotation_remove_pkg HASHREF
1509
1510 Removes a package from a quotation. Takes the following arguments:
1511
1512 =over 4
1513
1514 =item session_id
1515
1516 =item pkgnum - the primary key (quotationpkgnum) of the package to remove
1517
1518 =item quotationnum - the quotation to remove it from
1519
1520 =back
1521
1522 Returns 'error' => a string, which will be empty on success.
1523
1524 =back
1525
1526 =item quotation_order HASHREF
1527
1528 Converts the packages in a quotation into real packages. Takes the following
1529 arguments:
1530
1531 Takes the following arguments:
1532
1533 =over 4
1534
1535 =item session_id
1536
1537 =item quotationnum - the quotation to order
1538
1539 =back
1540
1541 =back
1542
1543 =head1 SIGNUP FUNCTIONS
1544
1545 =over 4
1546
1547 =item signup_info HASHREF
1548
1549 Takes a hash reference as parameter with the following keys:
1550
1551 =over 4
1552
1553 =item session_id - Optional agent/reseller interface session
1554
1555 =back
1556
1557 Returns a hash reference containing information that may be useful in
1558 displaying a signup page.  The hash reference contains the following keys:
1559
1560 =over 4
1561
1562 =item cust_main_county
1563
1564 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.
1565
1566 =item part_pkg
1567
1568 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
1569 an agentnum specified explicitly via reseller interface session_id in the
1570 options.
1571
1572 =item agent
1573
1574 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.
1575
1576 =item agentnum2part_pkg
1577
1578 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.
1579
1580 =item svc_acct_pop
1581
1582 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.
1583
1584 =item security_phrase
1585
1586 True if the "security_phrase" feature is enabled
1587
1588 =item payby
1589
1590 Array reference of acceptable payment types for signup
1591
1592 =over 4
1593
1594 =item CARD
1595
1596 credit card - automatic
1597
1598 =item DCRD
1599
1600 credit card - on-demand - version 1.5+ only
1601
1602 =item CHEK
1603
1604 electronic check - automatic
1605
1606 =item DCHK
1607
1608 electronic check - on-demand - version 1.5+ only
1609
1610 =item LECB
1611
1612 Phone bill billing
1613
1614 =item BILL
1615
1616 billing, not recommended for signups
1617
1618 =item COMP
1619
1620 free, definitely not recommended for signups
1621
1622 =item PREPAY
1623
1624 special billing type: applies a credit (see FS::prepay_credit) and sets billing type to BILL
1625
1626 =back
1627
1628 =item cvv_enabled
1629
1630 True if CVV features are available (1.5+ or 1.4.2 with CVV schema patch)
1631
1632 =item msgcat
1633
1634 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".
1635
1636 =item statedefault
1637
1638 Default state
1639
1640 =item countrydefault
1641
1642 Default country
1643
1644 =back
1645
1646 =item new_customer_minimal HASHREF
1647
1648 Creates a new customer.
1649
1650 Current differences from new_customer: An address is not required.  promo_code
1651 and reg_code are not supported.  If invoicing_list and _password is passed, a
1652 contact will be created with self-service access (no pkgpart or username is
1653 necessary).  No initial billing is run (this may change in a future version).
1654
1655 Takes a hash reference as parameter with the following keys:
1656
1657 =over 4
1658
1659 =item first
1660
1661 first name (required)
1662
1663 =item last
1664
1665 last name (required)
1666
1667 =item ss
1668
1669 (not typically collected; mostly used for ACH transactions)
1670
1671 =item company
1672
1673 Company name
1674
1675 =item address1
1676
1677 Address line one
1678
1679 =item address2
1680
1681 Address line two
1682
1683 =item city
1684
1685 City
1686
1687 =item county
1688
1689 County
1690
1691 =item state
1692
1693 State
1694
1695 =item zip
1696
1697 Zip or postal code
1698
1699 =item daytime
1700
1701 Daytime phone number
1702
1703 =item night
1704
1705 Evening phone number
1706
1707 =item fax
1708
1709 Fax number
1710
1711 =item payby
1712
1713 CARD, DCRD, CHEK, DCHK, LECB, BILL, COMP or PREPAY (see L</signup_info> (required)
1714
1715 =item payinfo
1716
1717 Card number for CARD/DCRD, account_number@aba_number for CHEK/DCHK, prepaid "pin" for PREPAY, purchase order number for BILL
1718
1719 =item paycvv
1720
1721 Credit card CVV2 number (1.5+ or 1.4.2 with CVV schema patch)
1722
1723 =item paydate
1724
1725 Expiration date for CARD/DCRD
1726
1727 =item payname
1728
1729 Exact name on credit card for CARD/DCRD, bank name for CHEK/DCHK
1730
1731 =item invoicing_list
1732
1733 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),
1734
1735 =item referral_custnum
1736
1737 referring customer number
1738
1739 =item agentnum
1740
1741 Agent number
1742
1743 =item pkgpart
1744
1745 pkgpart of initial package
1746
1747 =item username
1748
1749 Username
1750
1751 =item _password
1752
1753 Password
1754
1755 =item sec_phrase
1756
1757 Security phrase
1758
1759 =item popnum
1760
1761 Access number (index, not the literal number)
1762
1763 =item countrycode
1764
1765 Country code (to be provisioned as a service)
1766
1767 =item phonenum
1768
1769 Phone number (to be provisioned as a service)
1770
1771 =item pin
1772
1773 Voicemail PIN
1774
1775 =back
1776
1777 Returns a hash reference with the following keys:
1778
1779 =over 4
1780
1781 =item error
1782
1783 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)
1784
1785 =back
1786
1787 =item new_customer HASHREF
1788
1789 Creates a new customer.  Takes a hash reference as parameter with the
1790 following keys:
1791
1792 =over 4
1793
1794 =item first
1795
1796 first name (required)
1797
1798 =item last
1799
1800 last name (required)
1801
1802 =item ss
1803
1804 (not typically collected; mostly used for ACH transactions)
1805
1806 =item company
1807
1808 Company name
1809
1810 =item address1 (required)
1811
1812 Address line one
1813
1814 =item address2
1815
1816 Address line two
1817
1818 =item city (required)
1819
1820 City
1821
1822 =item county
1823
1824 County
1825
1826 =item state (required)
1827
1828 State
1829
1830 =item zip (required)
1831
1832 Zip or postal code
1833
1834 =item daytime
1835
1836 Daytime phone number
1837
1838 =item night
1839
1840 Evening phone number
1841
1842 =item fax
1843
1844 Fax number
1845
1846 =item payby
1847
1848 CARD, DCRD, CHEK, DCHK, LECB, BILL, COMP or PREPAY (see L</signup_info> (required)
1849
1850 =item payinfo
1851
1852 Card number for CARD/DCRD, account_number@aba_number for CHEK/DCHK, prepaid "pin" for PREPAY, purchase order number for BILL
1853
1854 =item paycvv
1855
1856 Credit card CVV2 number (1.5+ or 1.4.2 with CVV schema patch)
1857
1858 =item paydate
1859
1860 Expiration date for CARD/DCRD
1861
1862 =item payname
1863
1864 Exact name on credit card for CARD/DCRD, bank name for CHEK/DCHK
1865
1866 =item invoicing_list
1867
1868 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),
1869
1870 =item referral_custnum
1871
1872 referring customer number
1873
1874 =item agentnum
1875
1876 Agent number
1877
1878 =item pkgpart
1879
1880 pkgpart of initial package
1881
1882 =item username
1883
1884 Username
1885
1886 =item _password
1887
1888 Password
1889
1890 =item sec_phrase
1891
1892 Security phrase
1893
1894 =item popnum
1895
1896 Access number (index, not the literal number)
1897
1898 =item countrycode
1899
1900 Country code (to be provisioned as a service)
1901
1902 =item phonenum
1903
1904 Phone number (to be provisioned as a service)
1905
1906 =item pin
1907
1908 Voicemail PIN
1909
1910 =back
1911
1912 Returns a hash reference with the following keys:
1913
1914 =over 4
1915
1916 =item error
1917
1918 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)
1919
1920 =back
1921
1922 =item regionselector HASHREF | LIST
1923
1924 Takes as input a hashref or list of key/value pairs with the following keys:
1925
1926 =over 4
1927
1928 =item selected_county
1929
1930 Currently selected county
1931
1932 =item selected_state
1933
1934 Currently selected state
1935
1936 =item selected_country
1937
1938 Currently selected country
1939
1940 =item prefix
1941
1942 Specify a unique prefix string  if you intend to use the HTML output multiple time son one page.
1943
1944 =item onchange
1945
1946 Specify a javascript subroutine to call on changes
1947
1948 =item default_state
1949
1950 Default state
1951
1952 =item default_country
1953
1954 Default country
1955
1956 =item locales
1957
1958 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>.
1959
1960 =back
1961
1962 Returns a list consisting of three HTML fragments for county selection,
1963 state selection and country selection, respectively.
1964
1965 =cut
1966
1967 #false laziness w/FS::cust_main_county (this is currently the "newest" version)
1968 sub regionselector {
1969   my $param;
1970   if ( ref($_[0]) ) {
1971     $param = shift;
1972   } else {
1973     $param = { @_ };
1974   }
1975   $param->{'selected_country'} ||= $param->{'default_country'};
1976   $param->{'selected_state'} ||= $param->{'default_state'};
1977
1978   my $prefix = exists($param->{'prefix'}) ? $param->{'prefix'} : '';
1979
1980   my $countyflag = 0;
1981
1982   my %cust_main_county;
1983
1984 #  unless ( @cust_main_county ) { #cache 
1985     #@cust_main_county = qsearch('cust_main_county', {} );
1986     #foreach my $c ( @cust_main_county ) {
1987     foreach my $c ( @{ $param->{'locales'} } ) {
1988       #$countyflag=1 if $c->county;
1989       $countyflag=1 if $c->{county};
1990       #push @{$cust_main_county{$c->country}{$c->state}}, $c->county;
1991       #$cust_main_county{$c->country}{$c->state}{$c->county} = 1;
1992       $cust_main_county{$c->{country}}{$c->{state}}{$c->{county}} = 1;
1993     }
1994 #  }
1995   $countyflag=1 if $param->{selected_county};
1996
1997   my $script_html = <<END;
1998     <SCRIPT>
1999     function opt(what,value,text) {
2000       var optionName = new Option(text, value, false, false);
2001       var length = what.length;
2002       what.options[length] = optionName;
2003     }
2004     function ${prefix}country_changed(what) {
2005       country = what.options[what.selectedIndex].text;
2006       for ( var i = what.form.${prefix}state.length; i >= 0; i-- )
2007           what.form.${prefix}state.options[i] = null;
2008 END
2009       #what.form.${prefix}state.options[0] = new Option('', '', false, true);
2010
2011   foreach my $country ( sort keys %cust_main_county ) {
2012     $script_html .= "\nif ( country == \"$country\" ) {\n";
2013     foreach my $state ( sort keys %{$cust_main_county{$country}} ) {
2014       my $text = $state || '(n/a)';
2015       $script_html .= qq!opt(what.form.${prefix}state, "$state", "$text");\n!;
2016     }
2017     $script_html .= "}\n";
2018   }
2019
2020   $script_html .= <<END;
2021     }
2022     function ${prefix}state_changed(what) {
2023 END
2024
2025   if ( $countyflag ) {
2026     $script_html .= <<END;
2027       state = what.options[what.selectedIndex].text;
2028       country = what.form.${prefix}country.options[what.form.${prefix}country.selectedIndex].text;
2029       for ( var i = what.form.${prefix}county.length; i >= 0; i-- )
2030           what.form.${prefix}county.options[i] = null;
2031 END
2032
2033     foreach my $country ( sort keys %cust_main_county ) {
2034       $script_html .= "\nif ( country == \"$country\" ) {\n";
2035       foreach my $state ( sort keys %{$cust_main_county{$country}} ) {
2036         $script_html .= "\nif ( state == \"$state\" ) {\n";
2037           #foreach my $county ( sort @{$cust_main_county{$country}{$state}} ) {
2038           foreach my $county ( sort keys %{$cust_main_county{$country}{$state}} ) {
2039             my $text = $county || '(n/a)';
2040             $script_html .=
2041               qq!opt(what.form.${prefix}county, "$county", "$text");\n!;
2042           }
2043         $script_html .= "}\n";
2044       }
2045       $script_html .= "}\n";
2046     }
2047   }
2048
2049   $script_html .= <<END;
2050     }
2051     </SCRIPT>
2052 END
2053
2054   my $county_html = $script_html;
2055   if ( $countyflag ) {
2056     $county_html .= qq!<SELECT NAME="${prefix}county" onChange="$param->{'onchange'}">!;
2057     foreach my $county ( 
2058       sort keys %{ $cust_main_county{$param->{'selected_country'}}{$param->{'selected_state'}} }
2059     ) {
2060       my $text = $county || '(n/a)';
2061       $county_html .= qq!<OPTION VALUE="$county"!.
2062                       ($county eq $param->{'selected_county'} ? 
2063                         ' SELECTED>' : 
2064                         '>'
2065                       ).
2066                       $text.
2067                       '</OPTION>';
2068     }
2069     $county_html .= '</SELECT>';
2070   } else {
2071     $county_html .=
2072       qq!<INPUT TYPE="hidden" NAME="${prefix}county" VALUE="$param->{'selected_county'}">!;
2073   }
2074
2075   my $state_html = qq!<SELECT NAME="${prefix}state" !.
2076                    qq!onChange="${prefix}state_changed(this); $param->{'onchange'}">!;
2077   foreach my $state ( sort keys %{ $cust_main_county{$param->{'selected_country'}} } ) {
2078     my $text = $state || '(n/a)';
2079     my $selected = $state eq $param->{'selected_state'} ? 'SELECTED' : '';
2080     $state_html .= "\n<OPTION $selected VALUE=$state>$text</OPTION>"
2081   }
2082   $state_html .= '</SELECT>';
2083
2084   my $country_html = '';
2085   if ( scalar( keys %cust_main_county ) > 1 )  {
2086
2087     $country_html = qq(<SELECT NAME="${prefix}country" ).
2088                     qq(onChange="${prefix}country_changed(this); ).
2089                                  $param->{'onchange'}.
2090                                '"'.
2091                       '>';
2092     my $countrydefault = $param->{default_country} || 'US';
2093     foreach my $country (
2094       sort { ($b eq $countrydefault) <=> ($a eq $countrydefault) or $a cmp $b }
2095         keys %cust_main_county
2096     ) {
2097       my $selected = $country eq $param->{'selected_country'}
2098                        ? ' SELECTED'
2099                        : '';
2100       $country_html .= "\n<OPTION$selected>$country</OPTION>"
2101     }
2102     $country_html .= '</SELECT>';
2103   } else {
2104
2105     $country_html = qq(<INPUT TYPE="hidden" NAME="${prefix}country" ).
2106                             ' VALUE="'. (keys %cust_main_county )[0]. '">';
2107
2108   }
2109
2110   ($county_html, $state_html, $country_html);
2111
2112 }
2113
2114 sub regionselector_hashref {
2115   my ($county_html, $state_html, $country_html) = regionselector(@_);
2116   {
2117     'county_html'  => $county_html,
2118     'state_html'   => $state_html,
2119     'country_html' => $country_html,
2120   };
2121 }
2122
2123 =item location_form HASHREF | LIST
2124
2125 Takes as input a hashref or list of key/value pairs with the following keys:
2126
2127 =over 4
2128
2129 =item session_id
2130
2131 Current customer session_id
2132
2133 =item no_asterisks
2134
2135 Omit red asterisks from required fields.
2136
2137 =item address1_label
2138
2139 Label for first address line.
2140
2141 =back
2142
2143 Returns an HTML fragment for a location form (address, city, state, zip,
2144 country)
2145
2146 =cut
2147
2148 sub location_form {
2149   my $param;
2150   if ( ref($_[0]) ) {
2151     $param = shift;
2152   } else {
2153     $param = { @_ };
2154   }
2155
2156   my $session_id = delete $param->{'session_id'};
2157
2158   my $rv = mason_comp( 'session_id' => $session_id,
2159                        'comp'       => '/elements/location.html',
2160                        'args'       => [ %$param ],
2161                      );
2162
2163   #hmm.
2164   $rv->{'error'} || $rv->{'output'};
2165
2166 }
2167
2168
2169 #=item expselect HASHREF | LIST
2170 #
2171 #Takes as input a hashref or list of key/value pairs with the following keys:
2172 #
2173 #=over 4
2174 #
2175 #=item prefix - Specify a unique prefix string  if you intend to use the HTML output multiple time son one page.
2176 #
2177 #=item date - current date, in yyyy-mm-dd or m-d-yyyy format
2178 #
2179 #=back
2180
2181 =item expselect PREFIX [ DATE ]
2182
2183 Takes as input a unique prefix string and the current expiration date, in
2184 yyyy-mm-dd or m-d-yyyy format
2185
2186 Returns an HTML fragments for expiration date selection.
2187
2188 =cut
2189
2190 sub expselect {
2191   #my $param;
2192   #if ( ref($_[0]) ) {
2193   #  $param = shift;
2194   #} else {
2195   #  $param = { @_ };
2196   #my $prefix = $param->{'prefix'};
2197   #my $prefix = exists($param->{'prefix'}) ? $param->{'prefix'} : '';
2198   #my $date =   exists($param->{'date'})   ? $param->{'date'}   : '';
2199   my $prefix = shift;
2200   my $date = scalar(@_) ? shift : '';
2201
2202   my( $m, $y ) = ( 0, 0 );
2203   if ( $date  =~ /^(\d{4})-(\d{2})-\d{2}$/ ) { #PostgreSQL date format
2204     ( $m, $y ) = ( $2, $1 );
2205   } elsif ( $date =~ /^(\d{1,2})-(\d{1,2}-)?(\d{4}$)/ ) {
2206     ( $m, $y ) = ( $1, $3 );
2207   }
2208   my $return = qq!<SELECT NAME="$prefix!. qq!_month" SIZE="1">!;
2209   for ( 1 .. 12 ) {
2210     $return .= qq!<OPTION VALUE="$_"!;
2211     $return .= " SELECTED" if $_ == $m;
2212     $return .= ">$_";
2213   }
2214   $return .= qq!</SELECT>/<SELECT NAME="$prefix!. qq!_year" SIZE="1">!;
2215   my @t = localtime;
2216   my $thisYear = $t[5] + 1900;
2217   for ( ($thisYear > $y && $y > 0 ? $y : $thisYear) .. ($thisYear+10) ) {
2218     $return .= qq!<OPTION VALUE="$_"!;
2219     $return .= " SELECTED" if $_ == $y;
2220     $return .= ">$_";
2221   }
2222   $return .= "</SELECT>";
2223
2224   $return;
2225 }
2226
2227 =item popselector HASHREF | LIST
2228
2229 Takes as input a hashref or list of key/value pairs with the following keys:
2230
2231 =over 4
2232
2233 =item popnum
2234
2235 Access number number
2236
2237 =item pops
2238
2239 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>.
2240
2241 =back
2242
2243 Returns an HTML fragment for access number selection.
2244
2245 =cut
2246
2247 #horrible false laziness with FS/FS/svc_acct_pop.pm::popselector
2248 sub popselector {
2249   my $param;
2250   if ( ref($_[0]) ) {
2251     $param = shift;
2252   } else {
2253     $param = { @_ };
2254   }
2255   my $popnum = $param->{'popnum'};
2256   my $pops = $param->{'pops'};
2257
2258   return '<INPUT TYPE="hidden" NAME="popnum" VALUE="">' unless @$pops;
2259   return $pops->[0]{city}. ', '. $pops->[0]{state}.
2260          ' ('. $pops->[0]{ac}. ')/'. $pops->[0]{exch}. '-'. $pops->[0]{loc}.
2261          '<INPUT TYPE="hidden" NAME="popnum" VALUE="'. $pops->[0]{popnum}. '">'
2262     if scalar(@$pops) == 1;
2263
2264   my %pop = ();
2265   my %popnum2pop = ();
2266   foreach (@$pops) {
2267     push @{ $pop{ $_->{state} }->{ $_->{ac} } }, $_;
2268     $popnum2pop{$_->{popnum}} = $_;
2269   }
2270
2271   my $text = <<END;
2272     <SCRIPT>
2273     function opt(what,href,text) {
2274       var optionName = new Option(text, href, false, false)
2275       var length = what.length;
2276       what.options[length] = optionName;
2277     }
2278 END
2279
2280   my $init_popstate = $param->{'init_popstate'};
2281   if ( $init_popstate ) {
2282     $text .= '<INPUT TYPE="hidden" NAME="init_popstate" VALUE="'.
2283              $init_popstate. '">';
2284   } else {
2285     $text .= <<END;
2286       function acstate_changed(what) {
2287         state = what.options[what.selectedIndex].text;
2288         what.form.popac.options.length = 0
2289         what.form.popac.options[0] = new Option("Area code", "-1", false, true);
2290 END
2291   } 
2292
2293   my @states = $init_popstate ? ( $init_popstate ) : keys %pop;
2294   foreach my $state ( sort { $a cmp $b } @states ) {
2295     $text .= "\nif ( state == \"$state\" ) {\n" unless $init_popstate;
2296
2297     foreach my $ac ( sort { $a cmp $b } keys %{ $pop{$state} }) {
2298       $text .= "opt(what.form.popac, \"$ac\", \"$ac\");\n";
2299       if ($ac eq $param->{'popac'}) {
2300         $text .= "what.form.popac.options[what.form.popac.length-1].selected = true;\n";
2301       }
2302     }
2303     $text .= "}\n" unless $init_popstate;
2304   }
2305   $text .= "popac_changed(what.form.popac)}\n";
2306
2307   $text .= <<END;
2308   function popac_changed(what) {
2309     ac = what.options[what.selectedIndex].text;
2310     what.form.popnum.options.length = 0;
2311     what.form.popnum.options[0] = new Option("City", "-1", false, true);
2312
2313 END
2314
2315   foreach my $state ( @states ) {
2316     foreach my $popac ( keys %{ $pop{$state} } ) {
2317       $text .= "\nif ( ac == \"$popac\" ) {\n";
2318
2319       foreach my $pop ( @{$pop{$state}->{$popac}}) {
2320         my $o_popnum = $pop->{popnum};
2321         my $poptext =  $pop->{city}. ', '. $pop->{state}.
2322                        ' ('. $pop->{ac}. ')/'. $pop->{exch}. '-'. $pop->{loc};
2323
2324         $text .= "opt(what.form.popnum, \"$o_popnum\", \"$poptext\");\n";
2325         if ($popnum == $o_popnum) {
2326           $text .= "what.form.popnum.options[what.form.popnum.length-1].selected = true;\n";
2327         }
2328       }
2329       $text .= "}\n";
2330     }
2331   }
2332
2333
2334   $text .= "}\n</SCRIPT>\n";
2335
2336   $param->{'acstate'} = '' unless defined($param->{'acstate'});
2337
2338   $text .=
2339     qq!<TABLE CELLPADDING="0"><TR><TD><SELECT NAME="acstate"! .
2340     qq!SIZE=1 onChange="acstate_changed(this)"><OPTION VALUE=-1>State!;
2341   $text .= "<OPTION" . ($_ eq $param->{'acstate'} ? " SELECTED" : "") .
2342            ">$_" foreach sort { $a cmp $b } @states;
2343   $text .= '</SELECT>'; #callback? return 3 html pieces?  #'</TD>';
2344
2345   $text .=
2346     qq!<SELECT NAME="popac" SIZE=1 onChange="popac_changed(this)">!.
2347     qq!<OPTION>Area code</SELECT></TR><TR VALIGN="top">!;
2348
2349   $text .= qq!<TR><TD><SELECT NAME="popnum" SIZE=1 STYLE="width: 20em"><OPTION>City!;
2350
2351
2352   #comment this block to disable initial list polulation
2353   my @initial_select = ();
2354   if ( scalar( @$pops ) > 100 ) {
2355     push @initial_select, $popnum2pop{$popnum} if $popnum2pop{$popnum};
2356   } else {
2357     @initial_select = @$pops;
2358   }
2359   foreach my $pop ( sort { $a->{state} cmp $b->{state} } @initial_select ) {
2360     $text .= qq!<OPTION VALUE="!. $pop->{popnum}. '"'.
2361              ( ( $popnum && $pop->{popnum} == $popnum ) ? ' SELECTED' : '' ). ">".
2362              $pop->{city}. ', '. $pop->{state}.
2363                ' ('. $pop->{ac}. ')/'. $pop->{exch}. '-'. $pop->{loc};
2364   }
2365
2366   $text .= qq!</SELECT></TD></TR></TABLE>!;
2367
2368   $text;
2369
2370 }
2371
2372 =item domainselector HASHREF | LIST
2373
2374 Takes as input a hashref or list of key/value pairs with the following keys:
2375
2376 =over 4
2377
2378 =item pkgnum
2379
2380 Package number
2381
2382 =item domsvc
2383
2384 Service number of the selected item.
2385
2386 =back
2387
2388 Returns an HTML fragment for domain selection.
2389
2390 =cut
2391
2392 sub domainselector {
2393   my $param;
2394   if ( ref($_[0]) ) {
2395     $param = shift;
2396   } else {
2397     $param = { @_ };
2398   }
2399   my $domsvc= $param->{'domsvc'};
2400   my $rv = 
2401       domain_select_hash(map {$_ => $param->{$_}} qw(pkgnum svcpart pkgpart) );
2402   my $domains = $rv->{'domains'};
2403   $domsvc = $rv->{'domsvc'} unless $domsvc;
2404
2405   return '<INPUT TYPE="hidden" NAME="domsvc" VALUE="">'
2406     unless scalar(keys %$domains);
2407
2408   if (scalar(keys %$domains) == 1) {
2409     my $key;
2410     foreach(keys %$domains) {
2411       $key = $_;
2412     }
2413     return '<TR><TD ALIGN="right">Domain</TD><TD>'. $domains->{$key}.
2414            '<INPUT TYPE="hidden" NAME="domsvc" VALUE="'. $key. '"></TD></TR>'
2415   }
2416
2417   my $text .= qq!<TR><TD ALIGN="right">Domain</TD><TD><SELECT NAME="domsvc" SIZE=1 STYLE="width: 20em">!;
2418
2419   $text .= '<OPTION>(Choose Domain)' unless $domsvc;
2420
2421   foreach my $domain ( sort { $domains->{$a} cmp $domains->{$b} } keys %$domains ) {
2422     $text .= qq!<OPTION VALUE="!. $domain. '"'.
2423              ( ( $domsvc && $domain == $domsvc ) ? ' SELECTED' : '' ). ">".
2424              $domains->{$domain};
2425   }
2426
2427   $text .= qq!</SELECT></TD></TR>!;
2428
2429   $text;
2430
2431 }
2432
2433 =item didselector HASHREF | LIST
2434
2435 Takes as input a hashref or list of key/value pairs with the following keys:
2436
2437 =over 4
2438
2439 =item field
2440
2441 Field name for the returned HTML fragment.
2442
2443 =item svcpart
2444
2445 Service definition (see L<FS::part_svc>)
2446
2447 =back
2448
2449 Returns an HTML fragment for DID selection.
2450
2451 =cut
2452
2453 sub didselector {
2454   my $param;
2455   if ( ref($_[0]) ) {
2456     $param = shift;
2457   } else {
2458     $param = { @_ };
2459   }
2460
2461   my $rv = mason_comp( 'comp'=>'/elements/select-did.html',
2462                        'args'=>[ %$param ],
2463                      );
2464
2465   #hmm.
2466   $rv->{'error'} || $rv->{'output'};
2467
2468 }
2469
2470 =back
2471
2472 =head1 RESELLER FUNCTIONS
2473
2474 Note: Resellers can also use the B<signup_info> and B<new_customer> functions
2475 with their active session, and the B<customer_info> and B<order_pkg> functions
2476 with their active session and an additional I<custnum> parameter.
2477
2478 For the most part, development of the reseller web interface has been
2479 superceded by agent-virtualized access to the backend.
2480
2481 =over 4
2482
2483 =item agent_login
2484
2485 Agent login
2486
2487 =item agent_info
2488
2489 Agent info
2490
2491 =item agent_list_customers
2492
2493 List agent's customers.
2494
2495 =back
2496
2497 =head1 BUGS
2498
2499 =head1 SEE ALSO
2500
2501 L<freeside-selfservice-clientd>, L<freeside-selfservice-server>
2502
2503 =cut
2504
2505 1;
2506