self-service delete_contact, RT#37375
[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 Deletes a contact.  (Note: Cannot at this time delete the currently-logged in
1382 contact.)
1383
1384 Takes a hash reference as parameter with the following keys:
1385
1386 =over 4
1387
1388 =item session_id
1389
1390 =item contactnum
1391
1392 =back
1393
1394 Returns a hash reference with a single parameter, B<error>, which contains an
1395 error message, or empty on success.
1396
1397 =head2 "MY ACCOUNT" QUOTATION FUNCTIONS
1398
1399 All of these functions require the user to be logged in, and the 'session_id'
1400 key to be included in the argument hashref.`
1401
1402 =over 4
1403
1404 =item list_quotations HASHREF
1405
1406 Returns a hashref listing this customer's active self-service quotations.
1407 Contents are:
1408
1409 =over 4
1410
1411 =item quotations
1412
1413 an arrayref containing an element for each quotation.
1414
1415 =item quotationnum
1416
1417 the primary key
1418
1419 =item _date
1420
1421 the date it was started
1422
1423 =item num_pkgs
1424
1425 the number of packages
1426
1427 =item total_setup
1428
1429 the sum of setup fees
1430
1431 =item total_recur
1432
1433 the sum of recurring charges
1434
1435 =back
1436
1437 =item quotation_new HASHREF
1438
1439 Creates an empty quotation and returns a hashref containing 'quotationnum',
1440 the primary key of the new quotation.
1441
1442 =item quotation_delete HASHREF
1443
1444 Disables (does not really delete) a quotation. Takes the following arguments:
1445
1446 =over 4
1447
1448 =item session_id
1449
1450 =item quotationnum - the quotation to delete
1451
1452 =back
1453
1454 Returns 'error' => a string, which will be empty on success.
1455
1456 =item quotation_info HASHREF
1457
1458 Returns total and detailed pricing information on a quotation.
1459
1460 Takes the following arguments:
1461
1462 =over 4
1463
1464 =item session_id
1465
1466 =item quotationnum - the quotation to return
1467
1468 =back
1469
1470 Returns a hashref containing:
1471
1472 - total_setup, the total of setup fees (and their taxes)
1473 - total_recur, the total of all recurring charges (and their taxes)
1474 - sections, an arrayref containing an element for each quotation section.
1475   - description, a line of text describing the group of charges
1476   - subtotal, the total of charges in this group (if appropriate)
1477   - detail_items, an arrayref of line items
1478     - pkgnum, the reference number of the package
1479     - description, the package name (or tax name)
1480     - quantity
1481     - amount, the amount charged
1482     If the detail item represents a subtotal, it will instead contain:
1483     - total_item: description of the subtotal
1484     - total_amount: the subtotal amount
1485
1486
1487 =item quotation_print HASHREF
1488
1489 Renders the quotation as HTML or PDF. Takes the following arguments:
1490
1491 =over 4
1492
1493 =item session_id
1494
1495 =item quotationnum - the quotation to return
1496
1497 =item format - 'html' or 'pdf'
1498
1499 =back
1500
1501 Returns a hashref containing 'document', the contents of the file.
1502
1503 =item quotation_add_pkg HASHREF
1504
1505 Adds a package to a quotation. Takes the following arguments:
1506
1507 =over 4
1508
1509 =item session_id
1510
1511 =item pkgpart - the package to add
1512
1513 =item quotationnum - the quotation to add it to
1514
1515 =item quantity - the package quantity (defaults to 1)
1516
1517 =item address1, address2, city, state, zip, country - address fields to set
1518 the service location
1519
1520 =back
1521
1522 Returns 'error' => a string, which will be empty on success.
1523
1524 =item quotation_remove_pkg HASHREF
1525
1526 Removes a package from a quotation. Takes the following arguments:
1527
1528 =over 4
1529
1530 =item session_id
1531
1532 =item pkgnum - the primary key (quotationpkgnum) of the package to remove
1533
1534 =item quotationnum - the quotation to remove it from
1535
1536 =back
1537
1538 Returns 'error' => a string, which will be empty on success.
1539
1540 =back
1541
1542 =item quotation_order HASHREF
1543
1544 Converts the packages in a quotation into real packages. Takes the following
1545 arguments:
1546
1547 Takes the following arguments:
1548
1549 =over 4
1550
1551 =item session_id
1552
1553 =item quotationnum - the quotation to order
1554
1555 =back
1556
1557 =back
1558
1559 =head1 SIGNUP FUNCTIONS
1560
1561 =over 4
1562
1563 =item signup_info HASHREF
1564
1565 Takes a hash reference as parameter with the following keys:
1566
1567 =over 4
1568
1569 =item session_id - Optional agent/reseller interface session
1570
1571 =back
1572
1573 Returns a hash reference containing information that may be useful in
1574 displaying a signup page.  The hash reference contains the following keys:
1575
1576 =over 4
1577
1578 =item cust_main_county
1579
1580 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.
1581
1582 =item part_pkg
1583
1584 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
1585 an agentnum specified explicitly via reseller interface session_id in the
1586 options.
1587
1588 =item agent
1589
1590 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.
1591
1592 =item agentnum2part_pkg
1593
1594 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.
1595
1596 =item svc_acct_pop
1597
1598 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.
1599
1600 =item security_phrase
1601
1602 True if the "security_phrase" feature is enabled
1603
1604 =item payby
1605
1606 Array reference of acceptable payment types for signup
1607
1608 =over 4
1609
1610 =item CARD
1611
1612 credit card - automatic
1613
1614 =item DCRD
1615
1616 credit card - on-demand - version 1.5+ only
1617
1618 =item CHEK
1619
1620 electronic check - automatic
1621
1622 =item DCHK
1623
1624 electronic check - on-demand - version 1.5+ only
1625
1626 =item LECB
1627
1628 Phone bill billing
1629
1630 =item BILL
1631
1632 billing, not recommended for signups
1633
1634 =item COMP
1635
1636 free, definitely not recommended for signups
1637
1638 =item PREPAY
1639
1640 special billing type: applies a credit (see FS::prepay_credit) and sets billing type to BILL
1641
1642 =back
1643
1644 =item cvv_enabled
1645
1646 True if CVV features are available (1.5+ or 1.4.2 with CVV schema patch)
1647
1648 =item msgcat
1649
1650 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".
1651
1652 =item statedefault
1653
1654 Default state
1655
1656 =item countrydefault
1657
1658 Default country
1659
1660 =back
1661
1662 =item new_customer_minimal HASHREF
1663
1664 Creates a new customer.
1665
1666 Current differences from new_customer: An address is not required.  promo_code
1667 and reg_code are not supported.  If invoicing_list and _password is passed, a
1668 contact will be created with self-service access (no pkgpart or username is
1669 necessary).  No initial billing is run (this may change in a future version).
1670
1671 Takes a hash reference as parameter with the following keys:
1672
1673 =over 4
1674
1675 =item first
1676
1677 first name (required)
1678
1679 =item last
1680
1681 last name (required)
1682
1683 =item ss
1684
1685 (not typically collected; mostly used for ACH transactions)
1686
1687 =item company
1688
1689 Company name
1690
1691 =item address1
1692
1693 Address line one
1694
1695 =item address2
1696
1697 Address line two
1698
1699 =item city
1700
1701 City
1702
1703 =item county
1704
1705 County
1706
1707 =item state
1708
1709 State
1710
1711 =item zip
1712
1713 Zip or postal code
1714
1715 =item daytime
1716
1717 Daytime phone number
1718
1719 =item night
1720
1721 Evening phone number
1722
1723 =item fax
1724
1725 Fax number
1726
1727 =item payby
1728
1729 CARD, DCRD, CHEK, DCHK, LECB, BILL, COMP or PREPAY (see L</signup_info> (required)
1730
1731 =item payinfo
1732
1733 Card number for CARD/DCRD, account_number@aba_number for CHEK/DCHK, prepaid "pin" for PREPAY, purchase order number for BILL
1734
1735 =item paycvv
1736
1737 Credit card CVV2 number (1.5+ or 1.4.2 with CVV schema patch)
1738
1739 =item paydate
1740
1741 Expiration date for CARD/DCRD
1742
1743 =item payname
1744
1745 Exact name on credit card for CARD/DCRD, bank name for CHEK/DCHK
1746
1747 =item invoicing_list
1748
1749 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),
1750
1751 =item referral_custnum
1752
1753 referring customer number
1754
1755 =item agentnum
1756
1757 Agent number
1758
1759 =item pkgpart
1760
1761 pkgpart of initial package
1762
1763 =item username
1764
1765 Username
1766
1767 =item _password
1768
1769 Password
1770
1771 =item sec_phrase
1772
1773 Security phrase
1774
1775 =item popnum
1776
1777 Access number (index, not the literal number)
1778
1779 =item countrycode
1780
1781 Country code (to be provisioned as a service)
1782
1783 =item phonenum
1784
1785 Phone number (to be provisioned as a service)
1786
1787 =item pin
1788
1789 Voicemail PIN
1790
1791 =back
1792
1793 Returns a hash reference with the following keys:
1794
1795 =over 4
1796
1797 =item error
1798
1799 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)
1800
1801 =back
1802
1803 =item new_customer HASHREF
1804
1805 Creates a new customer.  Takes a hash reference as parameter with the
1806 following keys:
1807
1808 =over 4
1809
1810 =item first
1811
1812 first name (required)
1813
1814 =item last
1815
1816 last name (required)
1817
1818 =item ss
1819
1820 (not typically collected; mostly used for ACH transactions)
1821
1822 =item company
1823
1824 Company name
1825
1826 =item address1 (required)
1827
1828 Address line one
1829
1830 =item address2
1831
1832 Address line two
1833
1834 =item city (required)
1835
1836 City
1837
1838 =item county
1839
1840 County
1841
1842 =item state (required)
1843
1844 State
1845
1846 =item zip (required)
1847
1848 Zip or postal code
1849
1850 =item daytime
1851
1852 Daytime phone number
1853
1854 =item night
1855
1856 Evening phone number
1857
1858 =item fax
1859
1860 Fax number
1861
1862 =item payby
1863
1864 CARD, DCRD, CHEK, DCHK, LECB, BILL, COMP or PREPAY (see L</signup_info> (required)
1865
1866 =item payinfo
1867
1868 Card number for CARD/DCRD, account_number@aba_number for CHEK/DCHK, prepaid "pin" for PREPAY, purchase order number for BILL
1869
1870 =item paycvv
1871
1872 Credit card CVV2 number (1.5+ or 1.4.2 with CVV schema patch)
1873
1874 =item paydate
1875
1876 Expiration date for CARD/DCRD
1877
1878 =item payname
1879
1880 Exact name on credit card for CARD/DCRD, bank name for CHEK/DCHK
1881
1882 =item invoicing_list
1883
1884 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),
1885
1886 =item referral_custnum
1887
1888 referring customer number
1889
1890 =item agentnum
1891
1892 Agent number
1893
1894 =item pkgpart
1895
1896 pkgpart of initial package
1897
1898 =item username
1899
1900 Username
1901
1902 =item _password
1903
1904 Password
1905
1906 =item sec_phrase
1907
1908 Security phrase
1909
1910 =item popnum
1911
1912 Access number (index, not the literal number)
1913
1914 =item countrycode
1915
1916 Country code (to be provisioned as a service)
1917
1918 =item phonenum
1919
1920 Phone number (to be provisioned as a service)
1921
1922 =item pin
1923
1924 Voicemail PIN
1925
1926 =back
1927
1928 Returns a hash reference with the following keys:
1929
1930 =over 4
1931
1932 =item error
1933
1934 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)
1935
1936 =back
1937
1938 =item regionselector HASHREF | LIST
1939
1940 Takes as input a hashref or list of key/value pairs with the following keys:
1941
1942 =over 4
1943
1944 =item selected_county
1945
1946 Currently selected county
1947
1948 =item selected_state
1949
1950 Currently selected state
1951
1952 =item selected_country
1953
1954 Currently selected country
1955
1956 =item prefix
1957
1958 Specify a unique prefix string  if you intend to use the HTML output multiple time son one page.
1959
1960 =item onchange
1961
1962 Specify a javascript subroutine to call on changes
1963
1964 =item default_state
1965
1966 Default state
1967
1968 =item default_country
1969
1970 Default country
1971
1972 =item locales
1973
1974 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>.
1975
1976 =back
1977
1978 Returns a list consisting of three HTML fragments for county selection,
1979 state selection and country selection, respectively.
1980
1981 =cut
1982
1983 #false laziness w/FS::cust_main_county (this is currently the "newest" version)
1984 sub regionselector {
1985   my $param;
1986   if ( ref($_[0]) ) {
1987     $param = shift;
1988   } else {
1989     $param = { @_ };
1990   }
1991   $param->{'selected_country'} ||= $param->{'default_country'};
1992   $param->{'selected_state'} ||= $param->{'default_state'};
1993
1994   my $prefix = exists($param->{'prefix'}) ? $param->{'prefix'} : '';
1995
1996   my $countyflag = 0;
1997
1998   my %cust_main_county;
1999
2000 #  unless ( @cust_main_county ) { #cache 
2001     #@cust_main_county = qsearch('cust_main_county', {} );
2002     #foreach my $c ( @cust_main_county ) {
2003     foreach my $c ( @{ $param->{'locales'} } ) {
2004       #$countyflag=1 if $c->county;
2005       $countyflag=1 if $c->{county};
2006       #push @{$cust_main_county{$c->country}{$c->state}}, $c->county;
2007       #$cust_main_county{$c->country}{$c->state}{$c->county} = 1;
2008       $cust_main_county{$c->{country}}{$c->{state}}{$c->{county}} = 1;
2009     }
2010 #  }
2011   $countyflag=1 if $param->{selected_county};
2012
2013   my $script_html = <<END;
2014     <SCRIPT>
2015     function opt(what,value,text) {
2016       var optionName = new Option(text, value, false, false);
2017       var length = what.length;
2018       what.options[length] = optionName;
2019     }
2020     function ${prefix}country_changed(what) {
2021       country = what.options[what.selectedIndex].text;
2022       for ( var i = what.form.${prefix}state.length; i >= 0; i-- )
2023           what.form.${prefix}state.options[i] = null;
2024 END
2025       #what.form.${prefix}state.options[0] = new Option('', '', false, true);
2026
2027   foreach my $country ( sort keys %cust_main_county ) {
2028     $script_html .= "\nif ( country == \"$country\" ) {\n";
2029     foreach my $state ( sort keys %{$cust_main_county{$country}} ) {
2030       my $text = $state || '(n/a)';
2031       $script_html .= qq!opt(what.form.${prefix}state, "$state", "$text");\n!;
2032     }
2033     $script_html .= "}\n";
2034   }
2035
2036   $script_html .= <<END;
2037     }
2038     function ${prefix}state_changed(what) {
2039 END
2040
2041   if ( $countyflag ) {
2042     $script_html .= <<END;
2043       state = what.options[what.selectedIndex].text;
2044       country = what.form.${prefix}country.options[what.form.${prefix}country.selectedIndex].text;
2045       for ( var i = what.form.${prefix}county.length; i >= 0; i-- )
2046           what.form.${prefix}county.options[i] = null;
2047 END
2048
2049     foreach my $country ( sort keys %cust_main_county ) {
2050       $script_html .= "\nif ( country == \"$country\" ) {\n";
2051       foreach my $state ( sort keys %{$cust_main_county{$country}} ) {
2052         $script_html .= "\nif ( state == \"$state\" ) {\n";
2053           #foreach my $county ( sort @{$cust_main_county{$country}{$state}} ) {
2054           foreach my $county ( sort keys %{$cust_main_county{$country}{$state}} ) {
2055             my $text = $county || '(n/a)';
2056             $script_html .=
2057               qq!opt(what.form.${prefix}county, "$county", "$text");\n!;
2058           }
2059         $script_html .= "}\n";
2060       }
2061       $script_html .= "}\n";
2062     }
2063   }
2064
2065   $script_html .= <<END;
2066     }
2067     </SCRIPT>
2068 END
2069
2070   my $county_html = $script_html;
2071   if ( $countyflag ) {
2072     $county_html .= qq!<SELECT NAME="${prefix}county" onChange="$param->{'onchange'}">!;
2073     foreach my $county ( 
2074       sort keys %{ $cust_main_county{$param->{'selected_country'}}{$param->{'selected_state'}} }
2075     ) {
2076       my $text = $county || '(n/a)';
2077       $county_html .= qq!<OPTION VALUE="$county"!.
2078                       ($county eq $param->{'selected_county'} ? 
2079                         ' SELECTED>' : 
2080                         '>'
2081                       ).
2082                       $text.
2083                       '</OPTION>';
2084     }
2085     $county_html .= '</SELECT>';
2086   } else {
2087     $county_html .=
2088       qq!<INPUT TYPE="hidden" NAME="${prefix}county" VALUE="$param->{'selected_county'}">!;
2089   }
2090
2091   my $state_html = qq!<SELECT NAME="${prefix}state" !.
2092                    qq!onChange="${prefix}state_changed(this); $param->{'onchange'}">!;
2093   foreach my $state ( sort keys %{ $cust_main_county{$param->{'selected_country'}} } ) {
2094     my $text = $state || '(n/a)';
2095     my $selected = $state eq $param->{'selected_state'} ? 'SELECTED' : '';
2096     $state_html .= "\n<OPTION $selected VALUE=$state>$text</OPTION>"
2097   }
2098   $state_html .= '</SELECT>';
2099
2100   my $country_html = '';
2101   if ( scalar( keys %cust_main_county ) > 1 )  {
2102
2103     $country_html = qq(<SELECT NAME="${prefix}country" ).
2104                     qq(onChange="${prefix}country_changed(this); ).
2105                                  $param->{'onchange'}.
2106                                '"'.
2107                       '>';
2108     my $countrydefault = $param->{default_country} || 'US';
2109     foreach my $country (
2110       sort { ($b eq $countrydefault) <=> ($a eq $countrydefault) or $a cmp $b }
2111         keys %cust_main_county
2112     ) {
2113       my $selected = $country eq $param->{'selected_country'}
2114                        ? ' SELECTED'
2115                        : '';
2116       $country_html .= "\n<OPTION$selected>$country</OPTION>"
2117     }
2118     $country_html .= '</SELECT>';
2119   } else {
2120
2121     $country_html = qq(<INPUT TYPE="hidden" NAME="${prefix}country" ).
2122                             ' VALUE="'. (keys %cust_main_county )[0]. '">';
2123
2124   }
2125
2126   ($county_html, $state_html, $country_html);
2127
2128 }
2129
2130 sub regionselector_hashref {
2131   my ($county_html, $state_html, $country_html) = regionselector(@_);
2132   {
2133     'county_html'  => $county_html,
2134     'state_html'   => $state_html,
2135     'country_html' => $country_html,
2136   };
2137 }
2138
2139 =item location_form HASHREF | LIST
2140
2141 Takes as input a hashref or list of key/value pairs with the following keys:
2142
2143 =over 4
2144
2145 =item session_id
2146
2147 Current customer session_id
2148
2149 =item no_asterisks
2150
2151 Omit red asterisks from required fields.
2152
2153 =item address1_label
2154
2155 Label for first address line.
2156
2157 =back
2158
2159 Returns an HTML fragment for a location form (address, city, state, zip,
2160 country)
2161
2162 =cut
2163
2164 sub location_form {
2165   my $param;
2166   if ( ref($_[0]) ) {
2167     $param = shift;
2168   } else {
2169     $param = { @_ };
2170   }
2171
2172   my $session_id = delete $param->{'session_id'};
2173
2174   my $rv = mason_comp( 'session_id' => $session_id,
2175                        'comp'       => '/elements/location.html',
2176                        'args'       => [ %$param ],
2177                      );
2178
2179   #hmm.
2180   $rv->{'error'} || $rv->{'output'};
2181
2182 }
2183
2184
2185 #=item expselect HASHREF | LIST
2186 #
2187 #Takes as input a hashref or list of key/value pairs with the following keys:
2188 #
2189 #=over 4
2190 #
2191 #=item prefix - Specify a unique prefix string  if you intend to use the HTML output multiple time son one page.
2192 #
2193 #=item date - current date, in yyyy-mm-dd or m-d-yyyy format
2194 #
2195 #=back
2196
2197 =item expselect PREFIX [ DATE ]
2198
2199 Takes as input a unique prefix string and the current expiration date, in
2200 yyyy-mm-dd or m-d-yyyy format
2201
2202 Returns an HTML fragments for expiration date selection.
2203
2204 =cut
2205
2206 sub expselect {
2207   #my $param;
2208   #if ( ref($_[0]) ) {
2209   #  $param = shift;
2210   #} else {
2211   #  $param = { @_ };
2212   #my $prefix = $param->{'prefix'};
2213   #my $prefix = exists($param->{'prefix'}) ? $param->{'prefix'} : '';
2214   #my $date =   exists($param->{'date'})   ? $param->{'date'}   : '';
2215   my $prefix = shift;
2216   my $date = scalar(@_) ? shift : '';
2217
2218   my( $m, $y ) = ( 0, 0 );
2219   if ( $date  =~ /^(\d{4})-(\d{2})-\d{2}$/ ) { #PostgreSQL date format
2220     ( $m, $y ) = ( $2, $1 );
2221   } elsif ( $date =~ /^(\d{1,2})-(\d{1,2}-)?(\d{4}$)/ ) {
2222     ( $m, $y ) = ( $1, $3 );
2223   }
2224   my $return = qq!<SELECT NAME="$prefix!. qq!_month" SIZE="1">!;
2225   for ( 1 .. 12 ) {
2226     $return .= qq!<OPTION VALUE="$_"!;
2227     $return .= " SELECTED" if $_ == $m;
2228     $return .= ">$_";
2229   }
2230   $return .= qq!</SELECT>/<SELECT NAME="$prefix!. qq!_year" SIZE="1">!;
2231   my @t = localtime;
2232   my $thisYear = $t[5] + 1900;
2233   for ( ($thisYear > $y && $y > 0 ? $y : $thisYear) .. ($thisYear+10) ) {
2234     $return .= qq!<OPTION VALUE="$_"!;
2235     $return .= " SELECTED" if $_ == $y;
2236     $return .= ">$_";
2237   }
2238   $return .= "</SELECT>";
2239
2240   $return;
2241 }
2242
2243 =item popselector HASHREF | LIST
2244
2245 Takes as input a hashref or list of key/value pairs with the following keys:
2246
2247 =over 4
2248
2249 =item popnum
2250
2251 Access number number
2252
2253 =item pops
2254
2255 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>.
2256
2257 =back
2258
2259 Returns an HTML fragment for access number selection.
2260
2261 =cut
2262
2263 #horrible false laziness with FS/FS/svc_acct_pop.pm::popselector
2264 sub popselector {
2265   my $param;
2266   if ( ref($_[0]) ) {
2267     $param = shift;
2268   } else {
2269     $param = { @_ };
2270   }
2271   my $popnum = $param->{'popnum'};
2272   my $pops = $param->{'pops'};
2273
2274   return '<INPUT TYPE="hidden" NAME="popnum" VALUE="">' unless @$pops;
2275   return $pops->[0]{city}. ', '. $pops->[0]{state}.
2276          ' ('. $pops->[0]{ac}. ')/'. $pops->[0]{exch}. '-'. $pops->[0]{loc}.
2277          '<INPUT TYPE="hidden" NAME="popnum" VALUE="'. $pops->[0]{popnum}. '">'
2278     if scalar(@$pops) == 1;
2279
2280   my %pop = ();
2281   my %popnum2pop = ();
2282   foreach (@$pops) {
2283     push @{ $pop{ $_->{state} }->{ $_->{ac} } }, $_;
2284     $popnum2pop{$_->{popnum}} = $_;
2285   }
2286
2287   my $text = <<END;
2288     <SCRIPT>
2289     function opt(what,href,text) {
2290       var optionName = new Option(text, href, false, false)
2291       var length = what.length;
2292       what.options[length] = optionName;
2293     }
2294 END
2295
2296   my $init_popstate = $param->{'init_popstate'};
2297   if ( $init_popstate ) {
2298     $text .= '<INPUT TYPE="hidden" NAME="init_popstate" VALUE="'.
2299              $init_popstate. '">';
2300   } else {
2301     $text .= <<END;
2302       function acstate_changed(what) {
2303         state = what.options[what.selectedIndex].text;
2304         what.form.popac.options.length = 0
2305         what.form.popac.options[0] = new Option("Area code", "-1", false, true);
2306 END
2307   } 
2308
2309   my @states = $init_popstate ? ( $init_popstate ) : keys %pop;
2310   foreach my $state ( sort { $a cmp $b } @states ) {
2311     $text .= "\nif ( state == \"$state\" ) {\n" unless $init_popstate;
2312
2313     foreach my $ac ( sort { $a cmp $b } keys %{ $pop{$state} }) {
2314       $text .= "opt(what.form.popac, \"$ac\", \"$ac\");\n";
2315       if ($ac eq $param->{'popac'}) {
2316         $text .= "what.form.popac.options[what.form.popac.length-1].selected = true;\n";
2317       }
2318     }
2319     $text .= "}\n" unless $init_popstate;
2320   }
2321   $text .= "popac_changed(what.form.popac)}\n";
2322
2323   $text .= <<END;
2324   function popac_changed(what) {
2325     ac = what.options[what.selectedIndex].text;
2326     what.form.popnum.options.length = 0;
2327     what.form.popnum.options[0] = new Option("City", "-1", false, true);
2328
2329 END
2330
2331   foreach my $state ( @states ) {
2332     foreach my $popac ( keys %{ $pop{$state} } ) {
2333       $text .= "\nif ( ac == \"$popac\" ) {\n";
2334
2335       foreach my $pop ( @{$pop{$state}->{$popac}}) {
2336         my $o_popnum = $pop->{popnum};
2337         my $poptext =  $pop->{city}. ', '. $pop->{state}.
2338                        ' ('. $pop->{ac}. ')/'. $pop->{exch}. '-'. $pop->{loc};
2339
2340         $text .= "opt(what.form.popnum, \"$o_popnum\", \"$poptext\");\n";
2341         if ($popnum == $o_popnum) {
2342           $text .= "what.form.popnum.options[what.form.popnum.length-1].selected = true;\n";
2343         }
2344       }
2345       $text .= "}\n";
2346     }
2347   }
2348
2349
2350   $text .= "}\n</SCRIPT>\n";
2351
2352   $param->{'acstate'} = '' unless defined($param->{'acstate'});
2353
2354   $text .=
2355     qq!<TABLE CELLPADDING="0"><TR><TD><SELECT NAME="acstate"! .
2356     qq!SIZE=1 onChange="acstate_changed(this)"><OPTION VALUE=-1>State!;
2357   $text .= "<OPTION" . ($_ eq $param->{'acstate'} ? " SELECTED" : "") .
2358            ">$_" foreach sort { $a cmp $b } @states;
2359   $text .= '</SELECT>'; #callback? return 3 html pieces?  #'</TD>';
2360
2361   $text .=
2362     qq!<SELECT NAME="popac" SIZE=1 onChange="popac_changed(this)">!.
2363     qq!<OPTION>Area code</SELECT></TR><TR VALIGN="top">!;
2364
2365   $text .= qq!<TR><TD><SELECT NAME="popnum" SIZE=1 STYLE="width: 20em"><OPTION>City!;
2366
2367
2368   #comment this block to disable initial list polulation
2369   my @initial_select = ();
2370   if ( scalar( @$pops ) > 100 ) {
2371     push @initial_select, $popnum2pop{$popnum} if $popnum2pop{$popnum};
2372   } else {
2373     @initial_select = @$pops;
2374   }
2375   foreach my $pop ( sort { $a->{state} cmp $b->{state} } @initial_select ) {
2376     $text .= qq!<OPTION VALUE="!. $pop->{popnum}. '"'.
2377              ( ( $popnum && $pop->{popnum} == $popnum ) ? ' SELECTED' : '' ). ">".
2378              $pop->{city}. ', '. $pop->{state}.
2379                ' ('. $pop->{ac}. ')/'. $pop->{exch}. '-'. $pop->{loc};
2380   }
2381
2382   $text .= qq!</SELECT></TD></TR></TABLE>!;
2383
2384   $text;
2385
2386 }
2387
2388 =item domainselector HASHREF | LIST
2389
2390 Takes as input a hashref or list of key/value pairs with the following keys:
2391
2392 =over 4
2393
2394 =item pkgnum
2395
2396 Package number
2397
2398 =item domsvc
2399
2400 Service number of the selected item.
2401
2402 =back
2403
2404 Returns an HTML fragment for domain selection.
2405
2406 =cut
2407
2408 sub domainselector {
2409   my $param;
2410   if ( ref($_[0]) ) {
2411     $param = shift;
2412   } else {
2413     $param = { @_ };
2414   }
2415   my $domsvc= $param->{'domsvc'};
2416   my $rv = 
2417       domain_select_hash(map {$_ => $param->{$_}} qw(pkgnum svcpart pkgpart) );
2418   my $domains = $rv->{'domains'};
2419   $domsvc = $rv->{'domsvc'} unless $domsvc;
2420
2421   return '<INPUT TYPE="hidden" NAME="domsvc" VALUE="">'
2422     unless scalar(keys %$domains);
2423
2424   if (scalar(keys %$domains) == 1) {
2425     my $key;
2426     foreach(keys %$domains) {
2427       $key = $_;
2428     }
2429     return '<TR><TD ALIGN="right">Domain</TD><TD>'. $domains->{$key}.
2430            '<INPUT TYPE="hidden" NAME="domsvc" VALUE="'. $key. '"></TD></TR>'
2431   }
2432
2433   my $text .= qq!<TR><TD ALIGN="right">Domain</TD><TD><SELECT NAME="domsvc" SIZE=1 STYLE="width: 20em">!;
2434
2435   $text .= '<OPTION>(Choose Domain)' unless $domsvc;
2436
2437   foreach my $domain ( sort { $domains->{$a} cmp $domains->{$b} } keys %$domains ) {
2438     $text .= qq!<OPTION VALUE="!. $domain. '"'.
2439              ( ( $domsvc && $domain == $domsvc ) ? ' SELECTED' : '' ). ">".
2440              $domains->{$domain};
2441   }
2442
2443   $text .= qq!</SELECT></TD></TR>!;
2444
2445   $text;
2446
2447 }
2448
2449 =item didselector HASHREF | LIST
2450
2451 Takes as input a hashref or list of key/value pairs with the following keys:
2452
2453 =over 4
2454
2455 =item field
2456
2457 Field name for the returned HTML fragment.
2458
2459 =item svcpart
2460
2461 Service definition (see L<FS::part_svc>)
2462
2463 =back
2464
2465 Returns an HTML fragment for DID selection.
2466
2467 =cut
2468
2469 sub didselector {
2470   my $param;
2471   if ( ref($_[0]) ) {
2472     $param = shift;
2473   } else {
2474     $param = { @_ };
2475   }
2476
2477   my $rv = mason_comp( 'comp'=>'/elements/select-did.html',
2478                        'args'=>[ %$param ],
2479                      );
2480
2481   #hmm.
2482   $rv->{'error'} || $rv->{'output'};
2483
2484 }
2485
2486 =back
2487
2488 =head1 RESELLER FUNCTIONS
2489
2490 Note: Resellers can also use the B<signup_info> and B<new_customer> functions
2491 with their active session, and the B<customer_info> and B<order_pkg> functions
2492 with their active session and an additional I<custnum> parameter.
2493
2494 For the most part, development of the reseller web interface has been
2495 superceded by agent-virtualized access to the backend.
2496
2497 =over 4
2498
2499 =item agent_login
2500
2501 Agent login
2502
2503 =item agent_info
2504
2505 Agent info
2506
2507 =item agent_list_customers
2508
2509 List agent's customers.
2510
2511 =back
2512
2513 =head1 BUGS
2514
2515 =head1 SEE ALSO
2516
2517 L<freeside-selfservice-clientd>, L<freeside-selfservice-server>
2518
2519 =cut
2520
2521 1;
2522