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