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