f7d72dc58c191810901ae8cd452193777b31e4ae
[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 (None of this is required at all if you are just ordering a package
957 at the customer's existing default service location.)
958
959 =item address1
960
961 =item address2
962
963 =item city
964
965 =item county
966
967 =item state
968
969 =item zip
970
971 =item country
972
973 =item svcpart
974
975 Service to order (see L<FS::part_svc>).
976
977 Normally optional; required only to provision a non-svc_acct service, or if the
978 package definition does not contain one svc_acct service definition with
979 quantity 1 (it may contain others with quantity >1).  A svcpart of "none" can
980 also be specified to indicate that no initial service should be provisioned.
981
982 =back
983
984 Fields used when provisioning an svc_acct service:
985
986 =over 4
987
988 =item username
989
990 Username
991
992 =item _password
993
994 Password
995
996 =item sec_phrase
997
998 Optional security phrase
999
1000 =item popnum
1001
1002 Optional Access number number
1003
1004 =back
1005
1006 Fields used when provisioning an svc_domain service:
1007
1008 =over 4
1009
1010 =item domain
1011
1012 Domain
1013
1014 =back
1015
1016 Fields used when provisioning an svc_phone service:
1017
1018 =over 4
1019
1020 =item phonenum
1021
1022 Phone number
1023
1024 =item pin
1025
1026 Voicemail PIN
1027
1028 =item sip_password
1029
1030 SIP password
1031
1032 =back
1033
1034 Fields used when provisioning an svc_external service:
1035
1036 =over 4
1037
1038 =item id
1039
1040 External numeric ID.
1041
1042 =item title
1043
1044 External text title.
1045
1046 =back
1047
1048 Fields used when provisioning an svc_pbx service:
1049
1050 =over 4
1051
1052 =item id
1053
1054 Numeric ID.
1055
1056 =item name
1057
1058 Text name.
1059
1060 =back
1061
1062 Returns a hash reference with a single key, B<error>, empty on success, or an
1063 error message on errors.  The special error '_decline' is returned for
1064 declined transactions.
1065
1066 =item change_pkg
1067
1068 Changes a package for this customer.
1069
1070 Takes a hash reference as parameter with the following keys:
1071
1072 =over 4
1073
1074 =item session_id
1075
1076 Session identifier
1077
1078 =item pkgnum
1079
1080 Existing customer package.
1081
1082 =item pkgpart
1083
1084 New package to order (see L<FS::part_pkg>).
1085
1086 =item quantity
1087
1088 Quantity for this package order (default 1).
1089
1090 =back
1091
1092 Returns a hash reference with the following keys:
1093
1094 =over 4
1095
1096 =item error
1097
1098 Empty on success, or an error message on errors.  
1099
1100 =item pkgnum
1101
1102 On success, the new pkgnum
1103
1104 =back
1105
1106
1107 =item renew_info
1108
1109 Provides useful info for early renewals.
1110
1111 Takes a hash reference as parameter with the following keys:
1112
1113 =over 4
1114
1115 =item session_id
1116
1117 Session identifier
1118
1119 =back
1120
1121 Returns a hash reference.  On errors, it contains a single key, B<error>, with
1122 the error message.  Otherwise, contains a single key, B<dates>, pointing to
1123 an array refernce of hash references.  Each hash reference contains the
1124 following keys:
1125
1126 =over 4
1127
1128 =item bill_date
1129
1130 (Future) Bill date.  Indicates a future date for which billing could be run.
1131 Specified as a integer UNIX timestamp.  Pass this value to the B<order_renew>
1132 function.
1133
1134 =item bill_date_pretty
1135
1136 (Future) Bill date as a human-readable string.  (Convenience for display;
1137 subject to change, so best not to parse for the date.)
1138
1139 =item amount
1140
1141 Base amount which will be charged if renewed early as of this date.
1142
1143 =item renew_date
1144
1145 Renewal date; i.e. even-futher future date at which the customer will be paid
1146 through if the early renewal is completed with the given B<bill-date>.
1147 Specified as a integer UNIX timestamp.
1148
1149 =item renew_date_pretty
1150
1151 Renewal date as a human-readable string.  (Convenience for display;
1152 subject to change, so best not to parse for the date.)
1153
1154 =item pkgnum
1155
1156 Package that will be renewed.
1157
1158 =item expire_date
1159
1160 Expiration date of the package that will be renewed.
1161
1162 =item expire_date_pretty
1163
1164 Expiration date of the package that will be renewed, as a human-readable
1165 string.  (Convenience for display; subject to change, so best not to parse for
1166 the date.)
1167
1168 =back
1169
1170 =item order_renew
1171
1172 Renews this customer early; i.e. runs billing for this customer in advance.
1173
1174 Takes a hash reference as parameter with the following keys:
1175
1176 =over 4
1177
1178 =item session_id
1179
1180 Session identifier
1181
1182 =item date
1183
1184 Integer date as returned by the B<renew_info> function, indicating the advance
1185 date for which to run billing.
1186
1187 =back
1188
1189 Returns a hash reference with a single key, B<error>, empty on success, or an
1190 error message on errors.
1191
1192 =item cancel_pkg
1193
1194 Cancels a package for this customer.
1195
1196 Takes a hash reference as parameter with the following keys:
1197
1198 =over 4
1199
1200 =item session_id
1201
1202 Session identifier
1203
1204 =item pkgpart
1205
1206 pkgpart of package to cancel
1207
1208 =back
1209
1210 Returns a hash reference with a single key, B<error>, empty on success, or an
1211 error message on errors.
1212
1213 =item provision_acct 
1214
1215 Provisions an account (svc_acct).
1216
1217 Takes a hash reference as parameter with the following keys:
1218
1219 =over 4
1220
1221 =item session_id
1222
1223 Session identifier
1224
1225 =item pkgnum
1226
1227 pkgnum of package into which this service is provisioned
1228
1229 =item svcpart
1230
1231 svcpart or service definition to provision
1232
1233 =item username
1234
1235 =item domsvc
1236
1237 =item _password
1238
1239 =back
1240
1241 =item provision_phone
1242
1243 Provisions a phone number (svc_phone).
1244
1245 Takes a hash reference as parameter with the following keys:
1246
1247 =over 4
1248
1249 =item session_id
1250
1251 Session identifier
1252
1253 =item pkgnum
1254
1255 pkgnum of package into which this service is provisioned
1256
1257 =item svcpart
1258
1259 svcpart or service definition to provision
1260
1261 =item countrycode
1262
1263 =item phonenum
1264
1265 =item address1
1266
1267 =item address2
1268
1269 =item city
1270
1271 =item county
1272
1273 =item state
1274
1275 =item zip
1276
1277 =item country
1278
1279 E911 Address (optional)
1280
1281 =back
1282
1283 =item provision_pbx
1284
1285 Provisions a customer PBX (svc_pbx).
1286
1287 Takes a hash reference as parameter with the following keys:
1288
1289 =over 4
1290
1291 =item session_id
1292
1293 Session identifier
1294
1295 =item pkgnum
1296
1297 pkgnum of package into which this service is provisioned
1298
1299 =item svcpart
1300
1301 svcpart or service definition to provision
1302
1303 =item id
1304
1305 =item title
1306
1307 =item max_extensions
1308
1309 =item max_simultaneous
1310
1311 =item ip_addr
1312
1313 =back
1314
1315 =item provision_external
1316
1317 Provisions an external service (svc_external).
1318
1319 Takes a hash reference as parameter with the following keys:
1320
1321 =over 4
1322
1323 =item session_id
1324
1325 Session identifier
1326
1327 =item pkgnum
1328
1329 pkgnum of package into which this service is provisioned
1330
1331 =item svcpart
1332
1333 svcpart or service definition to provision
1334
1335 =item id
1336
1337 =item title
1338
1339 =back
1340
1341 =back
1342
1343 =head2 "MY ACCOUNT" CONTACT FUNCTIONS
1344
1345 =over 4
1346
1347 =item contact_passwd
1348
1349 Changes the password for the currently-logged in contact.
1350
1351 Takes a hash reference as parameter with the following keys:
1352
1353 =over 4
1354
1355 =item session_id
1356
1357 =item new_password
1358
1359 =back
1360
1361 Returns a hash reference with a single parameter, B<error>, which contains an
1362 error message, or empty on success.
1363
1364 =item list_contacts
1365
1366 Takes a hash reference as parameter with a single key, B<session_id>.
1367
1368 Returns a hash reference with two parameters: B<error>, which contains an error
1369 message, or empty on success, and B<contacts>, a list of contacts.
1370
1371 B<contacts> is an array reference of hash references (i.e. an array of structs,
1372  in XML-RPC).  Each hash reference (struct) has the following keys:
1373
1374 =over 4
1375
1376 =item contactnum
1377
1378 =item class
1379
1380 Contact class name (contact type).
1381
1382 =item first
1383
1384 First name
1385
1386 =item last
1387
1388 Last name
1389
1390 =item title
1391
1392 Position ("Director of Silly Walks"), NOT honorific ("Mr." or "Mrs.")
1393
1394 =item emailaddress
1395
1396 Comma-separated list of email addresses
1397
1398 =item comment
1399
1400 =item selfservice_access
1401
1402 Y when enabled
1403
1404 =back
1405
1406 =item edit_contact
1407
1408 Updates information for the currently-logged in contact, or (optionally) the
1409 specified contact.
1410
1411 Takes a hash reference as parameter with the following keys:
1412
1413 =over 4
1414
1415 =item session_id
1416
1417 =item contactnum
1418
1419 If already logged in as a contact, this is optional.
1420
1421 =item first
1422
1423 =item last
1424
1425 =item emailaddress
1426
1427 =back
1428
1429 Returns a hash reference with a single parameter, B<error>, which contains an
1430 error message, or empty on success.
1431
1432 =item new_contact
1433
1434 Creates a new contact.
1435
1436 Takes a hash reference as parameter with the following keys:
1437
1438 =over 4
1439
1440 =item session_id
1441
1442 =item first
1443
1444 =item last
1445
1446 =item emailaddress
1447
1448 =item classnum
1449
1450 Optional contact classnum (TODO: or name)
1451
1452 =item comment
1453
1454 =item selfservice_access
1455
1456 Y to enable self-service access
1457
1458 =item _password
1459
1460 =back
1461
1462 Returns a hash reference with a single parameter, B<error>, which contains an
1463 error message, or empty on success.
1464
1465 =item delete_contact
1466
1467 Deletes a contact.  (Note: Cannot at this time delete the currently-logged in
1468 contact.)
1469
1470 Takes a hash reference as parameter with the following keys:
1471
1472 =over 4
1473
1474 =item session_id
1475
1476 =item contactnum
1477
1478 =back
1479
1480 Returns a hash reference with a single parameter, B<error>, which contains an
1481 error message, or empty on success.
1482
1483 =back
1484
1485 =head2 "MY ACCOUNT" QUOTATION FUNCTIONS
1486
1487 All of these functions require the user to be logged in, and the 'session_id'
1488 key to be included in the argument hashref.`
1489
1490 =over 4
1491
1492 =item list_quotations HASHREF
1493
1494 Returns a hashref listing this customer's active self-service quotations.
1495 Contents are:
1496
1497 =over 4
1498
1499 =item quotations
1500
1501 an arrayref containing an element for each quotation.
1502
1503 =item quotationnum
1504
1505 the primary key
1506
1507 =item _date
1508
1509 the date it was started
1510
1511 =item num_pkgs
1512
1513 the number of packages
1514
1515 =item total_setup
1516
1517 the sum of setup fees
1518
1519 =item total_recur
1520
1521 the sum of recurring charges
1522
1523 =back
1524
1525 =item quotation_new HASHREF
1526
1527 Creates an empty quotation and returns a hashref containing 'quotationnum',
1528 the primary key of the new quotation.
1529
1530 =item quotation_delete HASHREF
1531
1532 Disables (does not really delete) a quotation. Takes the following arguments:
1533
1534 =over 4
1535
1536 =item session_id
1537
1538 =item quotationnum - the quotation to delete
1539
1540 =back
1541
1542 Returns 'error' => a string, which will be empty on success.
1543
1544 =item quotation_info HASHREF
1545
1546 Returns total and detailed pricing information on a quotation.
1547
1548 Takes the following arguments:
1549
1550 =over 4
1551
1552 =item session_id
1553
1554 =item quotationnum - the quotation to return
1555
1556 =back
1557
1558 Returns a hashref containing:
1559
1560 - total_setup, the total of setup fees (and their taxes)
1561 - total_recur, the total of all recurring charges (and their taxes)
1562 - sections, an arrayref containing an element for each quotation section.
1563   - description, a line of text describing the group of charges
1564   - subtotal, the total of charges in this group (if appropriate)
1565   - detail_items, an arrayref of line items
1566     - pkgnum, the reference number of the package
1567     - description, the package name (or tax name)
1568     - quantity
1569     - amount, the amount charged
1570     If the detail item represents a subtotal, it will instead contain:
1571     - total_item: description of the subtotal
1572     - total_amount: the subtotal amount
1573
1574
1575 =item quotation_print HASHREF
1576
1577 Renders the quotation as HTML or PDF. Takes the following arguments:
1578
1579 =over 4
1580
1581 =item session_id
1582
1583 =item quotationnum - the quotation to return
1584
1585 =item format - 'html' or 'pdf'
1586
1587 =back
1588
1589 Returns a hashref containing 'document', the contents of the file.
1590
1591 =item quotation_add_pkg HASHREF
1592
1593 Adds a package to a quotation. Takes the following arguments:
1594
1595 =over 4
1596
1597 =item session_id
1598
1599 =item pkgpart - the package to add
1600
1601 =item quotationnum - the quotation to add it to
1602
1603 =item quantity - the package quantity (defaults to 1)
1604
1605 =item address1, address2, city, state, zip, country - address fields to set
1606 the service location
1607
1608 =back
1609
1610 Returns 'error' => a string, which will be empty on success.
1611
1612 =item quotation_remove_pkg HASHREF
1613
1614 Removes a package from a quotation. Takes the following arguments:
1615
1616 =over 4
1617
1618 =item session_id
1619
1620 =item pkgnum - the primary key (quotationpkgnum) of the package to remove
1621
1622 =item quotationnum - the quotation to remove it from
1623
1624 =back
1625
1626 Returns 'error' => a string, which will be empty on success.
1627
1628 =item quotation_order HASHREF
1629
1630 Converts the packages in a quotation into real packages. Takes the following
1631 arguments:
1632
1633 Takes the following arguments:
1634
1635 =over 4
1636
1637 =item session_id
1638
1639 =item quotationnum - the quotation to order
1640
1641 =back
1642
1643 =back
1644
1645 =head1 SIGNUP FUNCTIONS
1646
1647 =over 4
1648
1649 =item signup_info HASHREF
1650
1651 Takes a hash reference as parameter with the following keys:
1652
1653 =over 4
1654
1655 =item session_id - Optional agent/reseller interface session
1656
1657 =back
1658
1659 Returns a hash reference containing information that may be useful in
1660 displaying a signup page.  The hash reference contains the following keys:
1661
1662 =over 4
1663
1664 =item cust_main_county
1665
1666 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.
1667
1668 =item part_pkg
1669
1670 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
1671 an agentnum specified explicitly via reseller interface session_id in the
1672 options.
1673
1674 =item agent
1675
1676 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.
1677
1678 =item agentnum2part_pkg
1679
1680 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.
1681
1682 =item svc_acct_pop
1683
1684 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.
1685
1686 =item security_phrase
1687
1688 True if the "security_phrase" feature is enabled
1689
1690 =item payby
1691
1692 Array reference of acceptable payment types for signup
1693
1694 =over 4
1695
1696 =item CARD
1697
1698 credit card - automatic
1699
1700 =item DCRD
1701
1702 credit card - on-demand - version 1.5+ only
1703
1704 =item CHEK
1705
1706 electronic check - automatic
1707
1708 =item DCHK
1709
1710 electronic check - on-demand - version 1.5+ only
1711
1712 =item LECB
1713
1714 Phone bill billing
1715
1716 =item BILL
1717
1718 billing, not recommended for signups
1719
1720 =item COMP
1721
1722 free, definitely not recommended for signups
1723
1724 =item PREPAY
1725
1726 special billing type: applies a credit (see FS::prepay_credit) and sets billing type to BILL
1727
1728 =back
1729
1730 =item cvv_enabled
1731
1732 True if CVV features are available (1.5+ or 1.4.2 with CVV schema patch)
1733
1734 =item msgcat
1735
1736 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".
1737
1738 =item statedefault
1739
1740 Default state
1741
1742 =item countrydefault
1743
1744 Default country
1745
1746 =back
1747
1748 =item new_customer_minimal HASHREF
1749
1750 Creates a new customer.
1751
1752 Current differences from new_customer: An address is not required.  promo_code
1753 and reg_code are not supported.  If invoicing_list and _password is passed, a
1754 contact will be created with self-service access (no pkgpart or username is
1755 necessary).  No initial billing is run (this may change in a future version).
1756
1757 Takes a hash reference as parameter with the following keys:
1758
1759 =over 4
1760
1761 =item first
1762
1763 first name (required)
1764
1765 =item last
1766
1767 last name (required)
1768
1769 =item ss
1770
1771 (not typically collected; mostly used for ACH transactions)
1772
1773 =item company
1774
1775 Company name
1776
1777 =item address1
1778
1779 Address line one
1780
1781 =item address2
1782
1783 Address line two
1784
1785 =item city
1786
1787 City
1788
1789 =item county
1790
1791 County
1792
1793 =item state
1794
1795 State
1796
1797 =item zip
1798
1799 Zip or postal code
1800
1801 =item daytime
1802
1803 Daytime phone number
1804
1805 =item night
1806
1807 Evening phone number
1808
1809 =item fax
1810
1811 Fax number
1812
1813 =item payby
1814
1815 CARD, DCRD, CHEK, DCHK, LECB, BILL, COMP or PREPAY (see L</signup_info> (required)
1816
1817 =item payinfo
1818
1819 Card number for CARD/DCRD, account_number@aba_number for CHEK/DCHK, prepaid "pin" for PREPAY, purchase order number for BILL
1820
1821 =item paycvv
1822
1823 Credit card CVV2 number (1.5+ or 1.4.2 with CVV schema patch)
1824
1825 =item paydate
1826
1827 Expiration date for CARD/DCRD
1828
1829 =item payname
1830
1831 Exact name on credit card for CARD/DCRD, bank name for CHEK/DCHK
1832
1833 =item invoicing_list
1834
1835 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),
1836
1837 =item referral_custnum
1838
1839 referring customer number
1840
1841 =item agentnum
1842
1843 Agent number
1844
1845 =item pkgpart
1846
1847 pkgpart of initial package
1848
1849 =item username
1850
1851 Username
1852
1853 =item _password
1854
1855 Password
1856
1857 =item sec_phrase
1858
1859 Security phrase
1860
1861 =item popnum
1862
1863 Access number (index, not the literal number)
1864
1865 =item countrycode
1866
1867 Country code (to be provisioned as a service)
1868
1869 =item phonenum
1870
1871 Phone number (to be provisioned as a service)
1872
1873 =item pin
1874
1875 Voicemail PIN
1876
1877 =back
1878
1879 Returns a hash reference with the following keys:
1880
1881 =over 4
1882
1883 =item error
1884
1885 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)
1886
1887 =back
1888
1889 =item new_customer HASHREF
1890
1891 Creates a new customer.  Takes a hash reference as parameter with the
1892 following keys:
1893
1894 =over 4
1895
1896 =item first
1897
1898 first name (required)
1899
1900 =item last
1901
1902 last name (required)
1903
1904 =item ss
1905
1906 (not typically collected; mostly used for ACH transactions)
1907
1908 =item company
1909
1910 Company name
1911
1912 =item address1 (required)
1913
1914 Address line one
1915
1916 =item address2
1917
1918 Address line two
1919
1920 =item city (required)
1921
1922 City
1923
1924 =item county
1925
1926 County
1927
1928 =item state (required)
1929
1930 State
1931
1932 =item zip (required)
1933
1934 Zip or postal code
1935
1936 =item daytime
1937
1938 Daytime phone number
1939
1940 =item night
1941
1942 Evening phone number
1943
1944 =item fax
1945
1946 Fax number
1947
1948 =item payby
1949
1950 CARD, DCRD, CHEK, DCHK, LECB, BILL, COMP or PREPAY (see L</signup_info> (required)
1951
1952 =item payinfo
1953
1954 Card number for CARD/DCRD, account_number@aba_number for CHEK/DCHK, prepaid "pin" for PREPAY, purchase order number for BILL
1955
1956 =item paycvv
1957
1958 Credit card CVV2 number (1.5+ or 1.4.2 with CVV schema patch)
1959
1960 =item paydate
1961
1962 Expiration date for CARD/DCRD
1963
1964 =item payname
1965
1966 Exact name on credit card for CARD/DCRD, bank name for CHEK/DCHK
1967
1968 =item invoicing_list
1969
1970 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),
1971
1972 =item referral_custnum
1973
1974 referring customer number
1975
1976 =item agentnum
1977
1978 Agent number
1979
1980 =item pkgpart
1981
1982 pkgpart of initial package
1983
1984 =item username
1985
1986 Username
1987
1988 =item _password
1989
1990 Password
1991
1992 =item sec_phrase
1993
1994 Security phrase
1995
1996 =item popnum
1997
1998 Access number (index, not the literal number)
1999
2000 =item countrycode
2001
2002 Country code (to be provisioned as a service)
2003
2004 =item phonenum
2005
2006 Phone number (to be provisioned as a service)
2007
2008 =item pin
2009
2010 Voicemail PIN
2011
2012 =back
2013
2014 Returns a hash reference with the following keys:
2015
2016 =over 4
2017
2018 =item error
2019
2020 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)
2021
2022 =back
2023
2024 =item regionselector HASHREF | LIST
2025
2026 Takes as input a hashref or list of key/value pairs with the following keys:
2027
2028 =over 4
2029
2030 =item selected_county
2031
2032 Currently selected county
2033
2034 =item selected_state
2035
2036 Currently selected state
2037
2038 =item selected_country
2039
2040 Currently selected country
2041
2042 =item prefix
2043
2044 Specify a unique prefix string  if you intend to use the HTML output multiple time son one page.
2045
2046 =item onchange
2047
2048 Specify a javascript subroutine to call on changes
2049
2050 =item default_state
2051
2052 Default state
2053
2054 =item default_country
2055
2056 Default country
2057
2058 =item locales
2059
2060 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>.
2061
2062 =back
2063
2064 Returns a list consisting of three HTML fragments for county selection,
2065 state selection and country selection, respectively.
2066
2067 =cut
2068
2069 #false laziness w/FS::cust_main_county (this is currently the "newest" version)
2070 sub regionselector {
2071   my $param;
2072   if ( ref($_[0]) ) {
2073     $param = shift;
2074   } else {
2075     $param = { @_ };
2076   }
2077   $param->{'selected_country'} ||= $param->{'default_country'};
2078   $param->{'selected_state'} ||= $param->{'default_state'};
2079
2080   my $prefix = exists($param->{'prefix'}) ? $param->{'prefix'} : '';
2081
2082   my $countyflag = 0;
2083
2084   my %cust_main_county;
2085
2086 #  unless ( @cust_main_county ) { #cache 
2087     #@cust_main_county = qsearch('cust_main_county', {} );
2088     #foreach my $c ( @cust_main_county ) {
2089     foreach my $c ( @{ $param->{'locales'} } ) {
2090       #$countyflag=1 if $c->county;
2091       $countyflag=1 if $c->{county};
2092       #push @{$cust_main_county{$c->country}{$c->state}}, $c->county;
2093       #$cust_main_county{$c->country}{$c->state}{$c->county} = 1;
2094       $cust_main_county{$c->{country}}{$c->{state}}{$c->{county}} = 1;
2095     }
2096 #  }
2097   $countyflag=1 if $param->{selected_county};
2098
2099   my $script_html = <<END;
2100     <SCRIPT>
2101     function opt(what,value,text) {
2102       var optionName = new Option(text, value, false, false);
2103       var length = what.length;
2104       what.options[length] = optionName;
2105     }
2106     function ${prefix}country_changed(what) {
2107       country = what.options[what.selectedIndex].text;
2108       for ( var i = what.form.${prefix}state.length; i >= 0; i-- )
2109           what.form.${prefix}state.options[i] = null;
2110 END
2111       #what.form.${prefix}state.options[0] = new Option('', '', false, true);
2112
2113   foreach my $country ( sort keys %cust_main_county ) {
2114     $script_html .= "\nif ( country == \"$country\" ) {\n";
2115     foreach my $state ( sort keys %{$cust_main_county{$country}} ) {
2116       my $text = $state || '(n/a)';
2117       $script_html .= qq!opt(what.form.${prefix}state, "$state", "$text");\n!;
2118     }
2119     $script_html .= "}\n";
2120   }
2121
2122   $script_html .= <<END;
2123     }
2124     function ${prefix}state_changed(what) {
2125 END
2126
2127   if ( $countyflag ) {
2128     $script_html .= <<END;
2129       state = what.options[what.selectedIndex].text;
2130       country = what.form.${prefix}country.options[what.form.${prefix}country.selectedIndex].text;
2131       for ( var i = what.form.${prefix}county.length; i >= 0; i-- )
2132           what.form.${prefix}county.options[i] = null;
2133 END
2134
2135     foreach my $country ( sort keys %cust_main_county ) {
2136       $script_html .= "\nif ( country == \"$country\" ) {\n";
2137       foreach my $state ( sort keys %{$cust_main_county{$country}} ) {
2138         $script_html .= "\nif ( state == \"$state\" ) {\n";
2139           #foreach my $county ( sort @{$cust_main_county{$country}{$state}} ) {
2140           foreach my $county ( sort keys %{$cust_main_county{$country}{$state}} ) {
2141             my $text = $county || '(n/a)';
2142             $script_html .=
2143               qq!opt(what.form.${prefix}county, "$county", "$text");\n!;
2144           }
2145         $script_html .= "}\n";
2146       }
2147       $script_html .= "}\n";
2148     }
2149   }
2150
2151   $script_html .= <<END;
2152     }
2153     </SCRIPT>
2154 END
2155
2156   my $county_html = $script_html;
2157   if ( $countyflag ) {
2158     $county_html .= qq!<SELECT NAME="${prefix}county" onChange="$param->{'onchange'}">!;
2159     foreach my $county ( 
2160       sort keys %{ $cust_main_county{$param->{'selected_country'}}{$param->{'selected_state'}} }
2161     ) {
2162       my $text = $county || '(n/a)';
2163       $county_html .= qq!<OPTION VALUE="$county"!.
2164                       ($county eq $param->{'selected_county'} ? 
2165                         ' SELECTED>' : 
2166                         '>'
2167                       ).
2168                       $text.
2169                       '</OPTION>';
2170     }
2171     $county_html .= '</SELECT>';
2172   } else {
2173     $county_html .=
2174       qq!<INPUT TYPE="hidden" NAME="${prefix}county" VALUE="$param->{'selected_county'}">!;
2175   }
2176
2177   my $state_html = qq!<SELECT NAME="${prefix}state" !.
2178                    qq!onChange="${prefix}state_changed(this); $param->{'onchange'}">!;
2179   foreach my $state ( sort keys %{ $cust_main_county{$param->{'selected_country'}} } ) {
2180     my $text = $state || '(n/a)';
2181     my $selected = $state eq $param->{'selected_state'} ? 'SELECTED' : '';
2182     $state_html .= "\n<OPTION $selected VALUE=$state>$text</OPTION>"
2183   }
2184   $state_html .= '</SELECT>';
2185
2186   my $country_html = '';
2187   if ( scalar( keys %cust_main_county ) > 1 )  {
2188
2189     $country_html = qq(<SELECT NAME="${prefix}country" ).
2190                     qq(onChange="${prefix}country_changed(this); ).
2191                                  $param->{'onchange'}.
2192                                '"'.
2193                       '>';
2194     my $countrydefault = $param->{default_country} || 'US';
2195     foreach my $country (
2196       sort { ($b eq $countrydefault) <=> ($a eq $countrydefault) or $a cmp $b }
2197         keys %cust_main_county
2198     ) {
2199       my $selected = $country eq $param->{'selected_country'}
2200                        ? ' SELECTED'
2201                        : '';
2202       $country_html .= "\n<OPTION$selected>$country</OPTION>"
2203     }
2204     $country_html .= '</SELECT>';
2205   } else {
2206
2207     $country_html = qq(<INPUT TYPE="hidden" NAME="${prefix}country" ).
2208                             ' VALUE="'. (keys %cust_main_county )[0]. '">';
2209
2210   }
2211
2212   ($county_html, $state_html, $country_html);
2213
2214 }
2215
2216 sub regionselector_hashref {
2217   my ($county_html, $state_html, $country_html) = regionselector(@_);
2218   {
2219     'county_html'  => $county_html,
2220     'state_html'   => $state_html,
2221     'country_html' => $country_html,
2222   };
2223 }
2224
2225 =item location_form HASHREF | LIST
2226
2227 Takes as input a hashref or list of key/value pairs with the following keys:
2228
2229 =over 4
2230
2231 =item session_id
2232
2233 Current customer session_id
2234
2235 =item no_asterisks
2236
2237 Omit red asterisks from required fields.
2238
2239 =item address1_label
2240
2241 Label for first address line.
2242
2243 =back
2244
2245 Returns an HTML fragment for a location form (address, city, state, zip,
2246 country)
2247
2248 =cut
2249
2250 sub location_form {
2251   my $param;
2252   if ( ref($_[0]) ) {
2253     $param = shift;
2254   } else {
2255     $param = { @_ };
2256   }
2257
2258   my $session_id = delete $param->{'session_id'};
2259
2260   my $rv = mason_comp( 'session_id' => $session_id,
2261                        'comp'       => '/elements/location.html',
2262                        'args'       => [ %$param ],
2263                      );
2264
2265   #hmm.
2266   $rv->{'error'} || $rv->{'output'};
2267
2268 }
2269
2270
2271 #=item expselect HASHREF | LIST
2272 #
2273 #Takes as input a hashref or list of key/value pairs with the following keys:
2274 #
2275 #=over 4
2276 #
2277 #=item prefix - Specify a unique prefix string  if you intend to use the HTML output multiple time son one page.
2278 #
2279 #=item date - current date, in yyyy-mm-dd or m-d-yyyy format
2280 #
2281 #=back
2282
2283 =item expselect PREFIX [ DATE ]
2284
2285 Takes as input a unique prefix string and the current expiration date, in
2286 yyyy-mm-dd or m-d-yyyy format
2287
2288 Returns an HTML fragments for expiration date selection.
2289
2290 =cut
2291
2292 sub expselect {
2293   #my $param;
2294   #if ( ref($_[0]) ) {
2295   #  $param = shift;
2296   #} else {
2297   #  $param = { @_ };
2298   #my $prefix = $param->{'prefix'};
2299   #my $prefix = exists($param->{'prefix'}) ? $param->{'prefix'} : '';
2300   #my $date =   exists($param->{'date'})   ? $param->{'date'}   : '';
2301   my $prefix = shift;
2302   my $date = scalar(@_) ? shift : '';
2303
2304   my( $m, $y ) = ( 0, 0 );
2305   if ( $date  =~ /^(\d{4})-(\d{2})-\d{2}$/ ) { #PostgreSQL date format
2306     ( $m, $y ) = ( $2, $1 );
2307   } elsif ( $date =~ /^(\d{1,2})-(\d{1,2}-)?(\d{4}$)/ ) {
2308     ( $m, $y ) = ( $1, $3 );
2309   }
2310   my $return = qq!<SELECT NAME="$prefix!. qq!_month" SIZE="1">!;
2311   for ( 1 .. 12 ) {
2312     $return .= qq!<OPTION VALUE="$_"!;
2313     $return .= " SELECTED" if $_ == $m;
2314     $return .= ">$_";
2315   }
2316   $return .= qq!</SELECT>/<SELECT NAME="$prefix!. qq!_year" SIZE="1">!;
2317   my @t = localtime;
2318   my $thisYear = $t[5] + 1900;
2319   for ( ($thisYear > $y && $y > 0 ? $y : $thisYear) .. ($thisYear+10) ) {
2320     $return .= qq!<OPTION VALUE="$_"!;
2321     $return .= " SELECTED" if $_ == $y;
2322     $return .= ">$_";
2323   }
2324   $return .= "</SELECT>";
2325
2326   $return;
2327 }
2328
2329 =item popselector HASHREF | LIST
2330
2331 Takes as input a hashref or list of key/value pairs with the following keys:
2332
2333 =over 4
2334
2335 =item popnum
2336
2337 Access number number
2338
2339 =item pops
2340
2341 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>.
2342
2343 =back
2344
2345 Returns an HTML fragment for access number selection.
2346
2347 =cut
2348
2349 #horrible false laziness with FS/FS/svc_acct_pop.pm::popselector
2350 sub popselector {
2351   my $param;
2352   if ( ref($_[0]) ) {
2353     $param = shift;
2354   } else {
2355     $param = { @_ };
2356   }
2357   my $popnum = $param->{'popnum'};
2358   my $pops = $param->{'pops'};
2359
2360   return '<INPUT TYPE="hidden" NAME="popnum" VALUE="">' unless @$pops;
2361   return $pops->[0]{city}. ', '. $pops->[0]{state}.
2362          ' ('. $pops->[0]{ac}. ')/'. $pops->[0]{exch}. '-'. $pops->[0]{loc}.
2363          '<INPUT TYPE="hidden" NAME="popnum" VALUE="'. $pops->[0]{popnum}. '">'
2364     if scalar(@$pops) == 1;
2365
2366   my %pop = ();
2367   my %popnum2pop = ();
2368   foreach (@$pops) {
2369     push @{ $pop{ $_->{state} }->{ $_->{ac} } }, $_;
2370     $popnum2pop{$_->{popnum}} = $_;
2371   }
2372
2373   my $text = <<END;
2374     <SCRIPT>
2375     function opt(what,href,text) {
2376       var optionName = new Option(text, href, false, false)
2377       var length = what.length;
2378       what.options[length] = optionName;
2379     }
2380 END
2381
2382   my $init_popstate = $param->{'init_popstate'};
2383   if ( $init_popstate ) {
2384     $text .= '<INPUT TYPE="hidden" NAME="init_popstate" VALUE="'.
2385              $init_popstate. '">';
2386   } else {
2387     $text .= <<END;
2388       function acstate_changed(what) {
2389         state = what.options[what.selectedIndex].text;
2390         what.form.popac.options.length = 0
2391         what.form.popac.options[0] = new Option("Area code", "-1", false, true);
2392 END
2393   } 
2394
2395   my @states = $init_popstate ? ( $init_popstate ) : keys %pop;
2396   foreach my $state ( sort { $a cmp $b } @states ) {
2397     $text .= "\nif ( state == \"$state\" ) {\n" unless $init_popstate;
2398
2399     foreach my $ac ( sort { $a cmp $b } keys %{ $pop{$state} }) {
2400       $text .= "opt(what.form.popac, \"$ac\", \"$ac\");\n";
2401       if ($ac eq $param->{'popac'}) {
2402         $text .= "what.form.popac.options[what.form.popac.length-1].selected = true;\n";
2403       }
2404     }
2405     $text .= "}\n" unless $init_popstate;
2406   }
2407   $text .= "popac_changed(what.form.popac)}\n";
2408
2409   $text .= <<END;
2410   function popac_changed(what) {
2411     ac = what.options[what.selectedIndex].text;
2412     what.form.popnum.options.length = 0;
2413     what.form.popnum.options[0] = new Option("City", "-1", false, true);
2414
2415 END
2416
2417   foreach my $state ( @states ) {
2418     foreach my $popac ( keys %{ $pop{$state} } ) {
2419       $text .= "\nif ( ac == \"$popac\" ) {\n";
2420
2421       foreach my $pop ( @{$pop{$state}->{$popac}}) {
2422         my $o_popnum = $pop->{popnum};
2423         my $poptext =  $pop->{city}. ', '. $pop->{state}.
2424                        ' ('. $pop->{ac}. ')/'. $pop->{exch}. '-'. $pop->{loc};
2425
2426         $text .= "opt(what.form.popnum, \"$o_popnum\", \"$poptext\");\n";
2427         if ($popnum == $o_popnum) {
2428           $text .= "what.form.popnum.options[what.form.popnum.length-1].selected = true;\n";
2429         }
2430       }
2431       $text .= "}\n";
2432     }
2433   }
2434
2435
2436   $text .= "}\n</SCRIPT>\n";
2437
2438   $param->{'acstate'} = '' unless defined($param->{'acstate'});
2439
2440   $text .=
2441     qq!<TABLE CELLPADDING="0"><TR><TD><SELECT NAME="acstate"! .
2442     qq!SIZE=1 onChange="acstate_changed(this)"><OPTION VALUE=-1>State!;
2443   $text .= "<OPTION" . ($_ eq $param->{'acstate'} ? " SELECTED" : "") .
2444            ">$_" foreach sort { $a cmp $b } @states;
2445   $text .= '</SELECT>'; #callback? return 3 html pieces?  #'</TD>';
2446
2447   $text .=
2448     qq!<SELECT NAME="popac" SIZE=1 onChange="popac_changed(this)">!.
2449     qq!<OPTION>Area code</SELECT></TR><TR VALIGN="top">!;
2450
2451   $text .= qq!<TR><TD><SELECT NAME="popnum" SIZE=1 STYLE="width: 20em"><OPTION>City!;
2452
2453
2454   #comment this block to disable initial list polulation
2455   my @initial_select = ();
2456   if ( scalar( @$pops ) > 100 ) {
2457     push @initial_select, $popnum2pop{$popnum} if $popnum2pop{$popnum};
2458   } else {
2459     @initial_select = @$pops;
2460   }
2461   foreach my $pop ( sort { $a->{state} cmp $b->{state} } @initial_select ) {
2462     $text .= qq!<OPTION VALUE="!. $pop->{popnum}. '"'.
2463              ( ( $popnum && $pop->{popnum} == $popnum ) ? ' SELECTED' : '' ). ">".
2464              $pop->{city}. ', '. $pop->{state}.
2465                ' ('. $pop->{ac}. ')/'. $pop->{exch}. '-'. $pop->{loc};
2466   }
2467
2468   $text .= qq!</SELECT></TD></TR></TABLE>!;
2469
2470   $text;
2471
2472 }
2473
2474 =item domainselector HASHREF | LIST
2475
2476 Takes as input a hashref or list of key/value pairs with the following keys:
2477
2478 =over 4
2479
2480 =item pkgnum
2481
2482 Package number
2483
2484 =item domsvc
2485
2486 Service number of the selected item.
2487
2488 =back
2489
2490 Returns an HTML fragment for domain selection.
2491
2492 =cut
2493
2494 sub domainselector {
2495   my $param;
2496   if ( ref($_[0]) ) {
2497     $param = shift;
2498   } else {
2499     $param = { @_ };
2500   }
2501   my $domsvc= $param->{'domsvc'};
2502   my $rv = 
2503       domain_select_hash(map {$_ => $param->{$_}} qw(pkgnum svcpart pkgpart) );
2504   my $domains = $rv->{'domains'};
2505   $domsvc = $rv->{'domsvc'} unless $domsvc;
2506
2507   return '<INPUT TYPE="hidden" NAME="domsvc" VALUE="">'
2508     unless scalar(keys %$domains);
2509
2510   if (scalar(keys %$domains) == 1) {
2511     my $key;
2512     foreach(keys %$domains) {
2513       $key = $_;
2514     }
2515     return '<TR><TD ALIGN="right">Domain</TD><TD>'. $domains->{$key}.
2516            '<INPUT TYPE="hidden" NAME="domsvc" VALUE="'. $key. '"></TD></TR>'
2517   }
2518
2519   my $text .= qq!<TR><TD ALIGN="right">Domain</TD><TD><SELECT NAME="domsvc" SIZE=1 STYLE="width: 20em">!;
2520
2521   $text .= '<OPTION>(Choose Domain)' unless $domsvc;
2522
2523   foreach my $domain ( sort { $domains->{$a} cmp $domains->{$b} } keys %$domains ) {
2524     $text .= qq!<OPTION VALUE="!. $domain. '"'.
2525              ( ( $domsvc && $domain == $domsvc ) ? ' SELECTED' : '' ). ">".
2526              $domains->{$domain};
2527   }
2528
2529   $text .= qq!</SELECT></TD></TR>!;
2530
2531   $text;
2532
2533 }
2534
2535 =item didselector HASHREF | LIST
2536
2537 Takes as input a hashref or list of key/value pairs with the following keys:
2538
2539 =over 4
2540
2541 =item field
2542
2543 Field name for the returned HTML fragment.
2544
2545 =item svcpart
2546
2547 Service definition (see L<FS::part_svc>)
2548
2549 =back
2550
2551 Returns an HTML fragment for DID selection.
2552
2553 =cut
2554
2555 sub didselector {
2556   my $param;
2557   if ( ref($_[0]) ) {
2558     $param = shift;
2559   } else {
2560     $param = { @_ };
2561   }
2562
2563   my $rv = mason_comp( 'comp'=>'/elements/select-did.html',
2564                        'args'=>[ %$param ],
2565                      );
2566
2567   #hmm.
2568   $rv->{'error'} || $rv->{'output'};
2569
2570 }
2571
2572 =back
2573
2574 =head1 RESELLER FUNCTIONS
2575
2576 Note: Resellers can also use the B<signup_info> and B<new_customer> functions
2577 with their active session, and the B<customer_info> and B<order_pkg> functions
2578 with their active session and an additional I<custnum> parameter.
2579
2580 For the most part, development of the reseller web interface has been
2581 superceded by agent-virtualized access to the backend.
2582
2583 =over 4
2584
2585 =item agent_login
2586
2587 Agent login
2588
2589 =item agent_info
2590
2591 Agent info
2592
2593 =item agent_list_customers
2594
2595 List agent's customers.
2596
2597 =back
2598
2599 =head1 BUGS
2600
2601 =head1 SEE ALSO
2602
2603 L<freeside-selfservice-clientd>, L<freeside-selfservice-server>
2604
2605 =cut
2606
2607 1;
2608