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