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