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