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