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