1 package FS::SelfService;
4 use vars qw( $VERSION @ISA @EXPORT_OK $DEBUG
5 $skip_uid_check $dir $socket %autoload $tag );
11 use Storable 2.09 qw(nstore_fd fd_retrieve);
15 @ISA = qw( Exporter );
19 $dir = "/usr/local/freeside";
20 $socket = "$dir/selfservice_socket";
21 $socket .= '.'.$tag if defined $tag && length($tag);
23 #maybe should ask ClientAPI for this list
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 'customer_info' => 'MyAccount/customer_info',
34 'customer_info_short' => 'MyAccount/customer_info_short',
35 'customer_recurring' => 'MyAccount/customer_recurring',
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',
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 'cancel' => 'MyAccount/cancel', #add to ss cgi!
52 'payment_info' => 'MyAccount/payment_info',
53 'payment_info_renew_info' => 'MyAccount/payment_info_renew_info',
54 'process_payment' => 'MyAccount/process_payment',
55 'store_payment' => 'MyAccount/store_payment',
56 'process_stored_payment' => 'MyAccount/process_stored_payment',
57 'process_payment_order_pkg' => 'MyAccount/process_payment_order_pkg',
58 'process_payment_change_pkg' => 'MyAccount/process_payment_change_pkg',
59 'process_payment_order_renew' => 'MyAccount/process_payment_order_renew',
60 'process_prepay' => 'MyAccount/process_prepay',
61 'realtime_collect' => 'MyAccount/realtime_collect',
62 'list_pkgs' => 'MyAccount/list_pkgs', #add to ss (added?)
63 'list_svcs' => 'MyAccount/list_svcs', #add to ss (added?)
64 'list_svc_usage' => 'MyAccount/list_svc_usage',
65 'svc_status_html' => 'MyAccount/svc_status_html',
66 'svc_status_hash' => 'MyAccount/svc_status_hash',
67 'set_svc_status_hash' => 'MyAccount/set_svc_status_hash',
68 'set_svc_status_listadd' => 'MyAccount/set_svc_status_listadd',
69 'set_svc_status_listdel' => 'MyAccount/set_svc_status_listdel',
70 'set_svc_status_vacationadd'=> 'MyAccount/set_svc_status_vacationadd',
71 'set_svc_status_vacationdel'=> 'MyAccount/set_svc_status_vacationdel',
72 'acct_forward_info' => 'MyAccount/acct_forward_info',
73 'process_acct_forward' => 'MyAccount/process_acct_forward',
74 'list_dsl_devices' => 'MyAccount/list_dsl_devices',
75 'add_dsl_device' => 'MyAccount/add_dsl_device',
76 'delete_dsl_device' => 'MyAccount/delete_dsl_device',
77 'port_graph' => 'MyAccount/port_graph',
78 'list_cdr_usage' => 'MyAccount/list_cdr_usage',
79 'list_support_usage' => 'MyAccount/list_support_usage',
80 'order_pkg' => 'MyAccount/order_pkg', #add to ss cgi!
81 'change_pkg' => 'MyAccount/change_pkg',
82 'order_recharge' => 'MyAccount/order_recharge',
83 'renew_info' => 'MyAccount/renew_info',
84 'order_renew' => 'MyAccount/order_renew',
85 'cancel_pkg' => 'MyAccount/cancel_pkg', #add to ss cgi!
86 'suspend_pkg' => 'MyAccount/suspend_pkg', #add to ss cgi!
87 'charge' => 'MyAccount/charge', #?
88 'part_svc_info' => 'MyAccount/part_svc_info',
89 'provision_acct' => 'MyAccount/provision_acct',
90 'provision_phone' => 'MyAccount/provision_phone',
91 'provision_pbx' => 'MyAccount/provision_pbx',
92 'provision_external' => 'MyAccount/provision_external',
93 'provision_forward' => 'MyAccount/provision_forward',
94 'unprovision_svc' => 'MyAccount/unprovision_svc',
95 'myaccount_passwd' => 'MyAccount/myaccount_passwd',
96 'reset_passwd' => 'MyAccount/reset_passwd',
97 'check_reset_passwd' => 'MyAccount/check_reset_passwd',
98 'process_reset_passwd' => 'MyAccount/process_reset_passwd',
99 'validate_passwd' => 'MyAccount/validate_passwd',
100 'list_tickets' => 'MyAccount/list_tickets',
101 'create_ticket' => 'MyAccount/create_ticket',
102 'get_ticket' => 'MyAccount/get_ticket',
103 'adjust_ticket_priority' => 'MyAccount/adjust_ticket_priority',
104 'did_report' => 'MyAccount/did_report',
105 'signup_info' => 'Signup/signup_info',
106 'skin_info' => 'MyAccount/skin_info',
107 'access_info' => 'MyAccount/access_info',
108 'domain_select_hash' => 'Signup/domain_select_hash', # expose?
109 'new_customer' => 'Signup/new_customer',
110 'new_customer_minimal' => 'Signup/new_customer_minimal',
111 'capture_payment' => 'Signup/capture_payment',
112 'new_prospect' => 'Signup/new_prospect',
113 #N/A 'clear_signup_cache' => 'Signup/clear_cache',
114 'new_agent' => 'Agent/new_agent',
115 'agent_login' => 'Agent/agent_login',
116 'agent_logout' => 'Agent/agent_logout',
117 'agent_info' => 'Agent/agent_info',
118 'agent_list_customers' => 'Agent/agent_list_customers',
119 'check_username' => 'Agent/check_username',
120 'suspend_username' => 'Agent/suspend_username',
121 'unsuspend_username' => 'Agent/unsuspend_username',
122 'mason_comp' => 'MasonComponent/mason_comp',
123 'call_time' => 'PrepaidPhone/call_time',
124 'call_time_nanpa' => 'PrepaidPhone/call_time_nanpa',
125 'phonenum_balance' => 'PrepaidPhone/phonenum_balance',
127 'start_thirdparty' => 'MyAccount/start_thirdparty',
128 'finish_thirdparty' => 'MyAccount/finish_thirdparty',
130 'list_quotations' => 'MyAccount/quotation/list_quotations',
131 'quotation_new' => 'MyAccount/quotation/quotation_new',
132 'quotation_delete' => 'MyAccount/quotation/quotation_delete',
133 'quotation_info' => 'MyAccount/quotation/quotation_info',
134 'quotation_print' => 'MyAccount/quotation/quotation_print',
135 'quotation_add_pkg' => 'MyAccount/quotation/quotation_add_pkg',
136 'quotation_remove_pkg' => 'MyAccount/quotation/quotation_remove_pkg',
137 'quotation_order' => 'MyAccount/quotation/quotation_order',
139 'freesideinc_service' => 'Freeside/freesideinc_service',
144 qw( regionselector regionselector_hashref location_form
145 expselect popselector domainselector didselector
149 $ENV{'PATH'} ='/usr/bin:/usr/ucb:/bin';
150 $ENV{'SHELL'} = '/bin/sh';
151 $ENV{'IFS'} = " \t\n";
154 $ENV{'BASH_ENV'} = '';
156 #you can add BEGIN { $FS::SelfService::skip_uid_check = 1; }
157 #if you grant appropriate permissions to whatever user
158 my $freeside_uid = scalar(getpwnam('freeside'));
159 die "not running as the freeside user\n"
160 if $> != $freeside_uid && ! $skip_uid_check;
162 -e $dir or die "FATAL: $dir doesn't exist!";
163 -d $dir or die "FATAL: $dir isn't a directory!";
164 -r $dir or die "FATAL: Can't read $dir as freeside user!";
165 -x $dir or die "FATAL: $dir not searchable (executable) as freeside user!";
167 foreach my $autoload ( keys %autoload ) {
170 "sub $autoload { ". '
175 #warn scalar(@_). ": ". join(" / ", @_);
179 $param->{_packet} = \''. $autoload{$autoload}. '\';
181 simple_packet($param);
191 warn "sending ". $packet->{_packet}. " to server"
193 socket(SOCK, PF_UNIX, SOCK_STREAM, 0) or die "socket: $!";
194 connect(SOCK, sockaddr_un($socket)) or die "connect to $socket: $!";
195 nstore_fd($packet, \*SOCK) or die "can't send packet: $!";
198 #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
200 #block until there is a message on socket
201 # my $w = new IO::Select;
203 # my @wait = $w->can_read;
205 warn "reading message from server"
208 my $return = fd_retrieve(\*SOCK) or die "error reading result: $!";
209 die $return->{'_error'} if defined $return->{_error} && $return->{_error};
211 warn "returning message to client"
219 FS::SelfService - Freeside self-service API
223 # password and shell account changes
224 use FS::SelfService qw(passwd chfn chsh);
226 # "my account" functionality
227 use FS::SelfService qw( login customer_info invoice cancel payment_info process_payment );
229 #new-style login with an email address and password
230 # can also be used for svc_acct login, set $emailaddress to username@domain
231 my $rv = login ( { 'email' => $emailaddress,
232 'password' => $password,
235 if ( $rv->{'error'} ) {
236 #handle login error...
239 $session_id = $rv->{'session_id'};
242 #classic svc_acct-based login with separate username and password
243 my $rv = login( { 'username' => $username,
245 'password' => $password,
248 if ( $rv->{'error'} ) {
249 #handle login error...
252 $session_id = $rv->{'session_id'};
255 #svc_phone login with phone number and PIN
256 my $rv = login( { 'username' => $phone_number,
257 'domain' => 'svc_phone',
261 if ( $rv->{'error'} ) {
262 #handle login error...
265 $session_id = $rv->{'session_id'};
268 my $customer_info = customer_info( { 'session_id' => $session_id } );
270 #payment_info and process_payment are available in 1.5+ only
271 my $payment_info = payment_info( { 'session_id' => $session_id } );
273 #!!! process_payment example
275 #!!! list_pkgs example
277 #!!! order_pkg example
279 #quoting a package, then ordering after confirmation
281 my $rv = quotation_new({ 'session_id' => $session_id });
282 my $qnum = $rv->{quotationnum};
283 # add packages to the quotation
284 $rv = quotation_add_pkg({ 'session_id' => $session_id,
285 'quotationnum' => $qnum,
286 'pkgpart' => $pkgpart,
287 'quantity' => $quantity, # defaults to 1
289 # repeat until all packages are added
290 # view the pricing information
291 $rv = quotation_info({ 'session_id' => $session_id,
292 'quotationnum' => $qnum,
294 print "Total setup charges: ".$rv->{total_setup}."\n".
295 "Total recurring charges: ".$rv->{total_recur}."\n";
296 # quotation_info also provides a detailed breakdown of charges, in
299 # ask customer for confirmation, then:
300 $rv = quotation_order({ 'session_id' => $session_id,
301 'quotationnum' => $qnum,
304 #!!! cancel_pkg example
306 # signup functionality
307 use FS::SelfService qw( signup_info new_customer new_customer_minimal );
309 my $signup_info = signup_info;
311 $rv = new_customer( {
314 'company' => $company,
315 'address1' => $address1,
316 'address2' => $address2,
320 'country' => $country,
321 'daytime' => $daytime,
325 'payinfo' => $payinfo,
327 'paystart_month' => $paystart_month
328 'paystart_year' => $paystart_year,
329 'payissue' => $payissue,
331 'paydate' => $paydate,
332 'payname' => $payname,
333 'invoicing_list' => $invoicing_list,
334 'referral_custnum' => $referral_custnum,
335 'agentnum' => $agentnum,
336 'pkgpart' => $pkgpart,
338 'username' => $username,
339 '_password' => $password,
343 'phonenum' => $phonenum,
348 my $error = $rv->{'error'};
349 if ( $error eq '_decline' ) {
359 Use this API to implement your own client "self-service" module.
361 If you just want to customize the look of the existing "self-service" module,
364 =head1 PASSWORD, GECOS, SHELL CHANGING FUNCTIONS
370 Changes the password for an existing user in svc_acct. Takes a hash
371 reference with the following keys:
377 Username of the account (required)
381 Domain of the account (required)
385 Old password (required)
389 New password (required)
407 =head1 "MY ACCOUNT" FUNCTIONS
413 Creates a user session. Takes a hash reference as parameter with the
420 Email address (username@domain), instead of username and domain. Required for
421 contact-based self-service login, can also be used for svc_acct-based login.
437 Returns a hash reference with the following keys:
443 Empty on success, or an error message on errors.
447 Session identifier for successful logins
451 =item customer_info HASHREF
453 Returns general customer information.
455 Takes a hash reference as parameter with a single key: B<session_id>
457 Returns a hash reference with the following keys:
471 Array reference of hash references of open inoices. Each hash reference has
472 the following keys: invnum, date, owed
476 An HTML fragment containing shipping and billing addresses.
478 =item The following fields are also returned
480 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
484 =item customer_recurring HASHREF
486 Takes a hash reference as parameter with a single key B<session_id>
487 or keys B<agent_session_id> and B<custnum>.
489 Returns a hash reference with the keys error, custnum and display_recurring.
491 display_recurring is an arrayref of hashrefs with the following keys:
497 frequency of charge, in months unless units are specified
501 frequency of charge, suitable for display
505 amount charged at this frequency
509 =item edit_info HASHREF
511 Takes a hash reference as parameter with any of the following keys:
513 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 If a field exists, the customer record is updated with the new value of that
516 field. If a field does not exist, that field is not changed on the customer
519 Returns a hash reference with a single key, B<error>, empty on success, or an
520 error message on errors
522 =item invoice HASHREF
524 Returns an invoice. Takes a hash reference as parameter with two keys:
525 session_id and invnum
527 Returns a hash reference with the following keys:
533 Empty on success, or an error message on errors
545 =item list_invoices HASHREF
547 Returns a list of all customer invoices. Takes a hash reference with a single
550 Returns a hash reference with the following keys:
556 Empty on success, or an error message on errors
560 Reference to array of hash references with the following keys:
570 Invoice date, in UNIX epoch time
578 Cancels this customer.
580 Takes a hash reference as parameter with a single key: B<session_id>
582 Returns a hash reference with a single key, B<error>, which is empty on
583 success or an error message on errors.
585 =item payment_info HASHREF
587 Returns information that may be useful in displaying a payment page.
589 Takes a hash reference as parameter with a single key: B<session_id>.
591 Returns a hash reference with the following keys:
597 Empty on success, or an error message on errors
605 Exact name on credit card (CARD/DCRD)
629 Customer's current default payment type.
633 For CARD/DCRD payment types, the card type (Visa card, MasterCard, Discover card, American Express card, etc.)
637 For CARD/DCRD payment types, the card number
641 For CARD/DCRD payment types, expiration month
645 For CARD/DCRD payment types, expiration year
647 =item cust_main_county
649 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.
653 Array reference of all states in the current default country.
657 Hash reference of card types; keys are card types, values are the exact strings
658 passed to the process_payment function
662 #this doesn't actually work yet
666 #Unique transaction identifier (prevents multiple charges), passed to the
667 #process_payment function
671 =item process_payment HASHREF
673 Processes a payment and possible change of address or payment type. Takes a
674 hash reference as parameter with the following keys:
688 If true, address and card information entered will be saved for subsequent
693 If true, future credit card payments will be done automatically (sets payby to
694 CARD). If false, future credit card payments will be done on-demand (sets
695 payby to DCRD). This option only has meaning if B<save> is set true.
723 Two-letter country code
731 Card expiration month
739 #this doesn't actually work yet
743 #Unique transaction identifier, returned from the payment_info function.
744 #Prevents multiple charges.
748 Returns a hash reference with a single key, B<error>, empty on success, or an
749 error message on errors.
751 =item process_payment_order_pkg
753 Combines the B<process_payment> and B<order_pkg> functions in one step. If the
754 payment processes sucessfully, the package is ordered. Takes a hash reference
755 as parameter with the keys of both methods.
757 Returns a hash reference with a single key, B<error>, empty on success, or an
758 error message on errors.
760 =item process_payment_change_pkg
762 Combines the B<process_payment> and B<change_pkg> functions in one step. If the
763 payment processes sucessfully, the package is ordered. Takes a hash reference
764 as parameter with the keys of both methods.
766 Returns a hash reference with a single key, B<error>, empty on success, or an
767 error message on errors.
770 =item process_payment_order_renew
772 Combines the B<process_payment> and B<order_renew> functions in one step. If
773 the payment processes sucessfully, the renewal is processed. Takes a hash
774 reference as parameter with the keys of both methods.
776 Returns a hash reference with a single key, B<error>, empty on success, or an
777 error message on errors.
781 Returns package information for this customer. For more detail on services,
784 Takes a hash reference as parameter with a single key: B<session_id>
786 Returns a hash reference containing customer package information. The hash reference contains the following keys:
796 Empty on success, or an error message on errors.
798 =item cust_pkg HASHREF
800 Array reference of hash references, each of which has the fields of a cust_pkg
801 record (see L<FS::cust_pkg>) as well as the fields below. Note these are not
802 the internal FS:: objects, but hash references of columns and values.
806 =item part_pkg fields
808 All fields of part_pkg for this specific cust_pkg (be careful with this
809 information - it may reveal more about your available packages than you would
810 like users to know in aggregate)
814 #XXX pare part_pkg fields down to a more secure subset
818 An array of hash references indicating information on unprovisioned services
819 available for provisioning for this specific cust_pkg. Each has the following
824 =item part_svc fields
826 All fields of part_svc (be careful with this information - it may reveal more
827 about your available packages than you would like users to know in aggregate)
831 #XXX pare part_svc fields down to a more secure subset
837 An array of hash references indicating information on the customer services
838 already provisioned for this specific cust_pkg. Each has the following keys:
844 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.
850 Primary key for this service
854 Service definition (see L<FS::part_svc>)
858 Customer package (see L<FS::cust_pkg>)
862 Blank if the service is not over limit, or the date the service exceeded its usage limit (as a UNIX timestamp).
870 Returns service information for this customer.
872 Takes a hash reference as parameter with a single key: B<session_id>
874 Returns a hash reference containing customer package information. The hash reference contains the following keys:
884 An array of hash references indicating information on all of this customer's
885 services. Each has the following keys:
891 Primary key for this service
899 Meaningful user-specific identifier for the service (i.e. username, domain, or
904 Account (svc_acct) services also have the following keys:
922 Upload bytes remaining
926 Download bytes remaining
930 Total bytes remaining
932 =item recharge_amount
936 =item recharge_seconds
938 Number of seconds gained by recharge
940 =item recharge_upbytes
942 Number of upload bytes gained by recharge
944 =item recharge_downbytes
946 Number of download bytes gained by recharge
948 =item recharge_totalbytes
950 Number of total bytes gained by recharge
958 Orders a package for this customer.
960 If signup_server-realtime is set, bills the new package, attemps to collect
961 payment and (for auto-payment customers) cancels the package if the payment is
964 Takes a hash reference as parameter with the following keys:
974 Package to order (see L<FS::part_pkg>).
978 Quantity for this package order (default 1).
980 =item run_bill_events
982 If true, runs billing events for the customer after ordering and billing the
983 package (signup_server-realtime must be set).
987 Optional locationnum for this package order, for existing locations.
989 Or, for new locations, pass the following fields: address1*, address2, city*,
990 county, state*, zip*, country. (* = required in this case)
992 (None of this is required at all if you are just ordering a package
993 at the customer's existing default service location.)
1011 Service to order (see L<FS::part_svc>).
1013 Normally optional; required only to provision a non-svc_acct service, or if the
1014 package definition does not contain one svc_acct service definition with
1015 quantity 1 (it may contain others with quantity >1). A svcpart of "none" can
1016 also be specified to indicate that no initial service should be provisioned.
1020 Fields used when provisioning an svc_acct service:
1034 Optional security phrase
1038 Optional Access number number
1042 Fields used when provisioning an svc_domain service:
1052 Fields used when provisioning an svc_phone service:
1070 Fields used when provisioning an svc_external service:
1076 External numeric ID.
1080 External text title.
1084 Fields used when provisioning an svc_pbx service:
1098 Returns a hash reference with a single key, B<error>, empty on success, or an
1099 error message on errors. The special error '_decline' is returned for
1100 declined transactions.
1104 Changes a package for this customer.
1106 Takes a hash reference as parameter with the following keys:
1116 Existing customer package.
1120 New package to order (see L<FS::part_pkg>).
1124 Quantity for this package order (default 1).
1128 Returns a hash reference with the following keys:
1134 Empty on success, or an error message on errors.
1138 On success, the new pkgnum
1145 Provides useful info for early renewals.
1147 Takes a hash reference as parameter with the following keys:
1157 Returns a hash reference. On errors, it contains a single key, B<error>, with
1158 the error message. Otherwise, contains a single key, B<dates>, pointing to
1159 an array refernce of hash references. Each hash reference contains the
1166 (Future) Bill date. Indicates a future date for which billing could be run.
1167 Specified as an integer UNIX timestamp. Pass this value to the B<order_renew>
1170 =item bill_date_pretty
1172 (Future) Bill date as a human-readable string. (Convenience for display;
1173 subject to change, so best not to parse for the date.)
1177 Base amount which will be charged if renewed early as of this date.
1181 Renewal date; i.e. even-futher future date at which the customer will be paid
1182 through if the early renewal is completed with the given B<bill-date>.
1183 Specified as an integer UNIX timestamp.
1185 =item renew_date_pretty
1187 Renewal date as a human-readable string. (Convenience for display;
1188 subject to change, so best not to parse for the date.)
1192 Package that will be renewed.
1196 Expiration date of the package that will be renewed.
1198 =item expire_date_pretty
1200 Expiration date of the package that will be renewed, as a human-readable
1201 string. (Convenience for display; subject to change, so best not to parse for
1208 Renews this customer early; i.e. runs billing for this customer in advance.
1210 Takes a hash reference as parameter with the following keys:
1220 Integer date as returned by the B<renew_info> function, indicating the advance
1221 date for which to run billing.
1225 Returns a hash reference with a single key, B<error>, empty on success, or an
1226 error message on errors.
1230 Cancels a package for this customer.
1232 Takes a hash reference as parameter with the following keys:
1242 pkgpart of package to cancel
1246 Optional date, for future cancellation (expiration) instead of immediate
1247 cancellation. Specified as an integer UNIX timestamp ("epoch time").
1251 Returns a hash reference with a single key, B<error>, empty on success, or an
1252 error message on errors.
1254 =item provision_acct
1256 Provisions an account (svc_acct).
1258 Takes a hash reference as parameter with the following keys:
1268 pkgnum of package into which this service is provisioned
1272 svcpart or service definition to provision
1282 =item provision_phone
1284 Provisions a phone number (svc_phone).
1286 Takes a hash reference as parameter with the following keys:
1296 pkgnum of package into which this service is provisioned
1300 svcpart or service definition to provision
1320 E911 Address (optional)
1326 Provisions a customer PBX (svc_pbx).
1328 Takes a hash reference as parameter with the following keys:
1338 pkgnum of package into which this service is provisioned
1342 svcpart or service definition to provision
1348 =item max_extensions
1350 =item max_simultaneous
1356 =item provision_external
1358 Provisions an external service (svc_external).
1360 Takes a hash reference as parameter with the following keys:
1370 pkgnum of package into which this service is provisioned
1374 svcpart or service definition to provision
1384 =head2 "MY ACCOUNT" CONTACT FUNCTIONS
1388 =item contact_passwd
1390 Changes the password for the currently-logged in contact.
1392 Takes a hash reference as parameter with the following keys:
1402 Returns a hash reference with a single parameter, B<error>, which contains an
1403 error message, or empty on success.
1407 Takes a hash reference as parameter with a single key, B<session_id>.
1409 Returns a hash reference with two parameters: B<error>, which contains an error
1410 message, or empty on success, and B<contacts>, a list of contacts.
1412 B<contacts> is an array reference of hash references (i.e. an array of structs,
1413 in XML-RPC). Each hash reference (struct) has the following keys:
1421 Contact class name (contact type).
1433 Position ("Director of Silly Walks"), NOT honorific ("Mr." or "Mrs.")
1437 Comma-separated list of email addresses
1441 =item selfservice_access
1449 Updates information for the currently-logged in contact, or (optionally) the
1452 Takes a hash reference as parameter with the following keys:
1460 If already logged in as a contact, this is optional.
1470 Returns a hash reference with a single parameter, B<error>, which contains an
1471 error message, or empty on success.
1475 Creates a new contact.
1477 Takes a hash reference as parameter with the following keys:
1491 Optional contact classnum (TODO: or name)
1495 =item selfservice_access
1497 Y to enable self-service access
1503 Returns a hash reference with a single parameter, B<error>, which contains an
1504 error message, or empty on success.
1506 =item delete_contact
1508 Deletes a contact. (Note: Cannot at this time delete the currently-logged in
1511 Takes a hash reference as parameter with the following keys:
1521 Returns a hash reference with a single parameter, B<error>, which contains an
1522 error message, or empty on success.
1526 =head2 "MY ACCOUNT" QUOTATION FUNCTIONS
1528 All of these functions require the user to be logged in, and the 'session_id'
1529 key to be included in the argument hashref.`
1533 =item list_quotations HASHREF
1535 Returns a hashref listing this customer's active self-service quotations.
1542 an arrayref containing an element for each quotation.
1550 the date it was started
1554 the number of packages
1558 the sum of setup fees
1562 the sum of recurring charges
1566 =item quotation_new HASHREF
1568 Creates an empty quotation and returns a hashref containing 'quotationnum',
1569 the primary key of the new quotation.
1571 =item quotation_delete HASHREF
1573 Disables (does not really delete) a quotation. Takes the following arguments:
1579 =item quotationnum - the quotation to delete
1583 Returns 'error' => a string, which will be empty on success.
1585 =item quotation_info HASHREF
1587 Returns total and detailed pricing information on a quotation.
1589 Takes the following arguments:
1595 =item quotationnum - the quotation to return
1599 Returns a hashref containing:
1601 - total_setup, the total of setup fees (and their taxes)
1602 - total_recur, the total of all recurring charges (and their taxes)
1603 - sections, an arrayref containing an element for each quotation section.
1604 - description, a line of text describing the group of charges
1605 - subtotal, the total of charges in this group (if appropriate)
1606 - detail_items, an arrayref of line items
1607 - pkgnum, the reference number of the package
1608 - description, the package name (or tax name)
1610 - amount, the amount charged
1611 If the detail item represents a subtotal, it will instead contain:
1612 - total_item: description of the subtotal
1613 - total_amount: the subtotal amount
1616 =item quotation_print HASHREF
1618 Renders the quotation as HTML or PDF. Takes the following arguments:
1624 =item quotationnum - the quotation to return
1626 =item format - 'html' or 'pdf'
1630 Returns a hashref containing 'document', the contents of the file.
1632 =item quotation_add_pkg HASHREF
1634 Adds a package to a quotation. Takes the following arguments:
1640 =item pkgpart - the package to add
1642 =item quotationnum - the quotation to add it to
1644 =item quantity - the package quantity (defaults to 1)
1646 =item address1, address2, city, state, zip, country - address fields to set
1647 the service location
1651 Returns 'error' => a string, which will be empty on success.
1653 =item quotation_remove_pkg HASHREF
1655 Removes a package from a quotation. Takes the following arguments:
1661 =item pkgnum - the primary key (quotationpkgnum) of the package to remove
1663 =item quotationnum - the quotation to remove it from
1667 Returns 'error' => a string, which will be empty on success.
1669 =item quotation_order HASHREF
1671 Converts the packages in a quotation into real packages. Takes the following
1674 Takes the following arguments:
1680 =item quotationnum - the quotation to order
1686 =head1 SIGNUP FUNCTIONS
1690 =item signup_info HASHREF
1692 Takes a hash reference as parameter with the following keys:
1696 =item session_id - Optional agent/reseller interface session
1700 Returns a hash reference containing information that may be useful in
1701 displaying a signup page. The hash reference contains the following keys:
1705 =item cust_main_county
1707 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.
1711 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
1712 an agentnum specified explicitly via reseller interface session_id in the
1717 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.
1719 =item agentnum2part_pkg
1721 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.
1725 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.
1727 =item security_phrase
1729 True if the "security_phrase" feature is enabled
1733 Array reference of acceptable payment types for signup
1739 credit card - automatic
1743 credit card - on-demand - version 1.5+ only
1747 electronic check - automatic
1751 electronic check - on-demand - version 1.5+ only
1759 billing, not recommended for signups
1763 free, definitely not recommended for signups
1767 special billing type: applies a credit (see FS::prepay_credit) and sets billing type to BILL
1773 True if CVV features are available (1.5+ or 1.4.2 with CVV schema patch)
1777 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".
1783 =item countrydefault
1789 =item new_customer_minimal HASHREF
1791 Creates a new customer.
1793 Current differences from new_customer: An address is not required. promo_code
1794 and reg_code are not supported. If invoicing_list and _password is passed, a
1795 contact will be created with self-service access (no pkgpart or username is
1796 necessary). No initial billing is run (this may change in a future version).
1798 Takes a hash reference as parameter with the following keys:
1804 first name (required)
1808 last name (required)
1812 (not typically collected; mostly used for ACH transactions)
1844 Daytime phone number
1848 Evening phone number
1856 CARD, DCRD, CHEK, DCHK, LECB, BILL, COMP or PREPAY (see L</signup_info> (required)
1860 Card number for CARD/DCRD, account_number@aba_number for CHEK/DCHK, prepaid "pin" for PREPAY, purchase order number for BILL
1864 Credit card CVV2 number (1.5+ or 1.4.2 with CVV schema patch)
1868 Expiration date for CARD/DCRD
1872 Exact name on credit card for CARD/DCRD, bank name for CHEK/DCHK
1874 =item invoicing_list
1876 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),
1878 =item referral_custnum
1880 referring customer number
1888 pkgpart of initial package
1904 Access number (index, not the literal number)
1908 Country code (to be provisioned as a service)
1912 Phone number (to be provisioned as a service)
1920 Returns a hash reference with the following keys:
1926 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)
1930 =item new_customer HASHREF
1932 Creates a new customer. Takes a hash reference as parameter with the
1939 first name (required)
1943 last name (required)
1947 (not typically collected; mostly used for ACH transactions)
1953 =item address1 (required)
1961 =item city (required)
1969 =item state (required)
1973 =item zip (required)
1979 Daytime phone number
1983 Evening phone number
1991 CARD, DCRD, CHEK, DCHK, LECB, BILL, COMP or PREPAY (see L</signup_info> (required)
1995 Card number for CARD/DCRD, account_number@aba_number for CHEK/DCHK, prepaid "pin" for PREPAY, purchase order number for BILL
1999 Credit card CVV2 number (1.5+ or 1.4.2 with CVV schema patch)
2003 Expiration date for CARD/DCRD
2007 Exact name on credit card for CARD/DCRD, bank name for CHEK/DCHK
2009 =item invoicing_list
2011 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),
2013 =item referral_custnum
2015 referring customer number
2023 pkgpart of initial package
2039 Access number (index, not the literal number)
2043 Country code (to be provisioned as a service)
2047 Phone number (to be provisioned as a service)
2055 Returns a hash reference with the following keys:
2061 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)
2065 =item regionselector HASHREF | LIST
2067 Takes as input a hashref or list of key/value pairs with the following keys:
2071 =item selected_county
2073 Currently selected county
2075 =item selected_state
2077 Currently selected state
2079 =item selected_country
2081 Currently selected country
2085 Specify a unique prefix string if you intend to use the HTML output multiple time son one page.
2089 Specify a javascript subroutine to call on changes
2095 =item default_country
2101 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>.
2105 Returns a list consisting of three HTML fragments for county selection,
2106 state selection and country selection, respectively.
2110 #false laziness w/FS::cust_main_county (this is currently the "newest" version)
2111 sub regionselector {
2118 $param->{'selected_country'} ||= $param->{'default_country'};
2119 $param->{'selected_state'} ||= $param->{'default_state'};
2121 my $prefix = exists($param->{'prefix'}) ? $param->{'prefix'} : '';
2125 my %cust_main_county;
2127 # unless ( @cust_main_county ) { #cache
2128 #@cust_main_county = qsearch('cust_main_county', {} );
2129 #foreach my $c ( @cust_main_county ) {
2130 foreach my $c ( @{ $param->{'locales'} } ) {
2131 #$countyflag=1 if $c->county;
2132 $countyflag=1 if $c->{county};
2133 #push @{$cust_main_county{$c->country}{$c->state}}, $c->county;
2134 #$cust_main_county{$c->country}{$c->state}{$c->county} = 1;
2135 $cust_main_county{$c->{country}}{$c->{state}}{$c->{county}} = 1;
2138 $countyflag=1 if $param->{selected_county};
2140 my $script_html = <<END;
2142 function opt(what,value,text) {
2143 var optionName = new Option(text, value, false, false);
2144 var length = what.length;
2145 what.options[length] = optionName;
2147 function ${prefix}country_changed(what) {
2148 country = what.options[what.selectedIndex].text;
2149 for ( var i = what.form.${prefix}state.length; i >= 0; i-- )
2150 what.form.${prefix}state.options[i] = null;
2152 #what.form.${prefix}state.options[0] = new Option('', '', false, true);
2154 foreach my $country ( sort keys %cust_main_county ) {
2155 $script_html .= "\nif ( country == \"$country\" ) {\n";
2156 foreach my $state ( sort keys %{$cust_main_county{$country}} ) {
2157 my $text = $state || '(n/a)';
2158 $script_html .= qq!opt(what.form.${prefix}state, "$state", "$text");\n!;
2160 $script_html .= "}\n";
2163 $script_html .= <<END;
2165 function ${prefix}state_changed(what) {
2168 if ( $countyflag ) {
2169 $script_html .= <<END;
2170 state = what.options[what.selectedIndex].text;
2171 country = what.form.${prefix}country.options[what.form.${prefix}country.selectedIndex].text;
2172 for ( var i = what.form.${prefix}county.length; i >= 0; i-- )
2173 what.form.${prefix}county.options[i] = null;
2176 foreach my $country ( sort keys %cust_main_county ) {
2177 $script_html .= "\nif ( country == \"$country\" ) {\n";
2178 foreach my $state ( sort keys %{$cust_main_county{$country}} ) {
2179 $script_html .= "\nif ( state == \"$state\" ) {\n";
2180 #foreach my $county ( sort @{$cust_main_county{$country}{$state}} ) {
2181 foreach my $county ( sort keys %{$cust_main_county{$country}{$state}} ) {
2182 my $text = $county || '(n/a)';
2184 qq!opt(what.form.${prefix}county, "$county", "$text");\n!;
2186 $script_html .= "}\n";
2188 $script_html .= "}\n";
2192 $script_html .= <<END;
2197 my $county_html = $script_html;
2198 if ( $countyflag ) {
2199 $county_html .= qq!<SELECT NAME="${prefix}county" onChange="$param->{'onchange'}">!;
2200 foreach my $county (
2201 sort keys %{ $cust_main_county{$param->{'selected_country'}}{$param->{'selected_state'}} }
2203 my $text = $county || '(n/a)';
2204 $county_html .= qq!<OPTION VALUE="$county"!.
2205 ($county eq $param->{'selected_county'} ?
2212 $county_html .= '</SELECT>';
2215 qq!<INPUT TYPE="hidden" NAME="${prefix}county" VALUE="$param->{'selected_county'}">!;
2218 my $state_html = qq!<SELECT NAME="${prefix}state" !.
2219 qq!onChange="${prefix}state_changed(this); $param->{'onchange'}">!;
2220 foreach my $state ( sort keys %{ $cust_main_county{$param->{'selected_country'}} } ) {
2221 my $text = $state || '(n/a)';
2222 my $selected = $state eq $param->{'selected_state'} ? 'SELECTED' : '';
2223 $state_html .= "\n<OPTION $selected VALUE=$state>$text</OPTION>"
2225 $state_html .= '</SELECT>';
2227 my $country_html = '';
2228 if ( scalar( keys %cust_main_county ) > 1 ) {
2230 $country_html = qq(<SELECT NAME="${prefix}country" ).
2231 qq(onChange="${prefix}country_changed(this); ).
2232 $param->{'onchange'}.
2235 my $countrydefault = $param->{default_country} || 'US';
2236 foreach my $country (
2237 sort { ($b eq $countrydefault) <=> ($a eq $countrydefault) or $a cmp $b }
2238 keys %cust_main_county
2240 my $selected = $country eq $param->{'selected_country'}
2243 $country_html .= "\n<OPTION$selected>$country</OPTION>"
2245 $country_html .= '</SELECT>';
2248 $country_html = qq(<INPUT TYPE="hidden" NAME="${prefix}country" ).
2249 ' VALUE="'. (keys %cust_main_county )[0]. '">';
2253 ($county_html, $state_html, $country_html);
2257 sub regionselector_hashref {
2258 my ($county_html, $state_html, $country_html) = regionselector(@_);
2260 'county_html' => $county_html,
2261 'state_html' => $state_html,
2262 'country_html' => $country_html,
2266 =item location_form HASHREF | LIST
2268 Takes as input a hashref or list of key/value pairs with the following keys:
2274 Current customer session_id
2278 Omit red asterisks from required fields.
2280 =item address1_label
2282 Label for first address line.
2286 Returns an HTML fragment for a location form (address, city, state, zip,
2299 my $session_id = delete $param->{'session_id'};
2301 my $rv = mason_comp( 'session_id' => $session_id,
2302 'comp' => '/elements/location.html',
2303 'args' => [ %$param ],
2307 $rv->{'error'} || $rv->{'output'};
2312 #=item expselect HASHREF | LIST
2314 #Takes as input a hashref or list of key/value pairs with the following keys:
2318 #=item prefix - Specify a unique prefix string if you intend to use the HTML output multiple time son one page.
2320 #=item date - current date, in yyyy-mm-dd or m-d-yyyy format
2324 =item expselect PREFIX [ DATE ]
2326 Takes as input a unique prefix string and the current expiration date, in
2327 yyyy-mm-dd or m-d-yyyy format
2329 Returns an HTML fragments for expiration date selection.
2335 #if ( ref($_[0]) ) {
2339 #my $prefix = $param->{'prefix'};
2340 #my $prefix = exists($param->{'prefix'}) ? $param->{'prefix'} : '';
2341 #my $date = exists($param->{'date'}) ? $param->{'date'} : '';
2343 my $date = scalar(@_) ? shift : '';
2345 my( $m, $y ) = ( 0, 0 );
2346 if ( $date =~ /^(\d{4})-(\d{2})-\d{2}$/ ) { #PostgreSQL date format
2347 ( $m, $y ) = ( $2, $1 );
2348 } elsif ( $date =~ /^(\d{1,2})-(\d{1,2}-)?(\d{4}$)/ ) {
2349 ( $m, $y ) = ( $1, $3 );
2351 my $return = qq!<SELECT NAME="$prefix!. qq!_month" SIZE="1">!;
2353 $return .= qq!<OPTION VALUE="$_"!;
2354 $return .= " SELECTED" if $_ == $m;
2357 $return .= qq!</SELECT>/<SELECT NAME="$prefix!. qq!_year" SIZE="1">!;
2359 my $thisYear = $t[5] + 1900;
2360 for ( ($thisYear > $y && $y > 0 ? $y : $thisYear) .. ($thisYear+10) ) {
2361 $return .= qq!<OPTION VALUE="$_"!;
2362 $return .= " SELECTED" if $_ == $y;
2365 $return .= "</SELECT>";
2370 =item popselector HASHREF | LIST
2372 Takes as input a hashref or list of key/value pairs with the following keys:
2378 Access number number
2382 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>.
2386 Returns an HTML fragment for access number selection.
2390 #horrible false laziness with FS/FS/svc_acct_pop.pm::popselector
2398 my $popnum = $param->{'popnum'};
2399 my $pops = $param->{'pops'};
2401 return '<INPUT TYPE="hidden" NAME="popnum" VALUE="">' unless @$pops;
2402 return $pops->[0]{city}. ', '. $pops->[0]{state}.
2403 ' ('. $pops->[0]{ac}. ')/'. $pops->[0]{exch}. '-'. $pops->[0]{loc}.
2404 '<INPUT TYPE="hidden" NAME="popnum" VALUE="'. $pops->[0]{popnum}. '">'
2405 if scalar(@$pops) == 1;
2408 my %popnum2pop = ();
2410 push @{ $pop{ $_->{state} }->{ $_->{ac} } }, $_;
2411 $popnum2pop{$_->{popnum}} = $_;
2416 function opt(what,href,text) {
2417 var optionName = new Option(text, href, false, false)
2418 var length = what.length;
2419 what.options[length] = optionName;
2423 my $init_popstate = $param->{'init_popstate'};
2424 if ( $init_popstate ) {
2425 $text .= '<INPUT TYPE="hidden" NAME="init_popstate" VALUE="'.
2426 $init_popstate. '">';
2429 function acstate_changed(what) {
2430 state = what.options[what.selectedIndex].text;
2431 what.form.popac.options.length = 0
2432 what.form.popac.options[0] = new Option("Area code", "-1", false, true);
2436 my @states = $init_popstate ? ( $init_popstate ) : keys %pop;
2437 foreach my $state ( sort { $a cmp $b } @states ) {
2438 $text .= "\nif ( state == \"$state\" ) {\n" unless $init_popstate;
2440 foreach my $ac ( sort { $a cmp $b } keys %{ $pop{$state} }) {
2441 $text .= "opt(what.form.popac, \"$ac\", \"$ac\");\n";
2442 if ($ac eq $param->{'popac'}) {
2443 $text .= "what.form.popac.options[what.form.popac.length-1].selected = true;\n";
2446 $text .= "}\n" unless $init_popstate;
2448 $text .= "popac_changed(what.form.popac)}\n";
2451 function popac_changed(what) {
2452 ac = what.options[what.selectedIndex].text;
2453 what.form.popnum.options.length = 0;
2454 what.form.popnum.options[0] = new Option("City", "-1", false, true);
2458 foreach my $state ( @states ) {
2459 foreach my $popac ( keys %{ $pop{$state} } ) {
2460 $text .= "\nif ( ac == \"$popac\" ) {\n";
2462 foreach my $pop ( @{$pop{$state}->{$popac}}) {
2463 my $o_popnum = $pop->{popnum};
2464 my $poptext = $pop->{city}. ', '. $pop->{state}.
2465 ' ('. $pop->{ac}. ')/'. $pop->{exch}. '-'. $pop->{loc};
2467 $text .= "opt(what.form.popnum, \"$o_popnum\", \"$poptext\");\n";
2468 if ($popnum == $o_popnum) {
2469 $text .= "what.form.popnum.options[what.form.popnum.length-1].selected = true;\n";
2477 $text .= "}\n</SCRIPT>\n";
2479 $param->{'acstate'} = '' unless defined($param->{'acstate'});
2482 qq!<TABLE CELLPADDING="0"><TR><TD><SELECT NAME="acstate"! .
2483 qq!SIZE=1 onChange="acstate_changed(this)"><OPTION VALUE=-1>State!;
2484 $text .= "<OPTION" . ($_ eq $param->{'acstate'} ? " SELECTED" : "") .
2485 ">$_" foreach sort { $a cmp $b } @states;
2486 $text .= '</SELECT>'; #callback? return 3 html pieces? #'</TD>';
2489 qq!<SELECT NAME="popac" SIZE=1 onChange="popac_changed(this)">!.
2490 qq!<OPTION>Area code</SELECT></TR><TR VALIGN="top">!;
2492 $text .= qq!<TR><TD><SELECT NAME="popnum" SIZE=1 STYLE="width: 20em"><OPTION>City!;
2495 #comment this block to disable initial list polulation
2496 my @initial_select = ();
2497 if ( scalar( @$pops ) > 100 ) {
2498 push @initial_select, $popnum2pop{$popnum} if $popnum2pop{$popnum};
2500 @initial_select = @$pops;
2502 foreach my $pop ( sort { $a->{state} cmp $b->{state} } @initial_select ) {
2503 $text .= qq!<OPTION VALUE="!. $pop->{popnum}. '"'.
2504 ( ( $popnum && $pop->{popnum} == $popnum ) ? ' SELECTED' : '' ). ">".
2505 $pop->{city}. ', '. $pop->{state}.
2506 ' ('. $pop->{ac}. ')/'. $pop->{exch}. '-'. $pop->{loc};
2509 $text .= qq!</SELECT></TD></TR></TABLE>!;
2515 =item domainselector HASHREF | LIST
2517 Takes as input a hashref or list of key/value pairs with the following keys:
2527 Service number of the selected item.
2531 Returns an HTML fragment for domain selection.
2535 sub domainselector {
2542 my $domsvc= $param->{'domsvc'};
2544 domain_select_hash(map {$_ => $param->{$_}} qw(pkgnum svcpart pkgpart) );
2545 my $domains = $rv->{'domains'};
2546 $domsvc = $rv->{'domsvc'} unless $domsvc;
2548 return '<INPUT TYPE="hidden" NAME="domsvc" VALUE="">'
2549 unless scalar(keys %$domains);
2551 if (scalar(keys %$domains) == 1) {
2553 foreach(keys %$domains) {
2556 return '<TR><TD ALIGN="right">Domain</TD><TD>'. $domains->{$key}.
2557 '<INPUT TYPE="hidden" NAME="domsvc" VALUE="'. $key. '"></TD></TR>'
2560 my $text .= qq!<TR><TD ALIGN="right">Domain</TD><TD><SELECT NAME="domsvc" SIZE=1 STYLE="width: 20em">!;
2562 $text .= '<OPTION>(Choose Domain)' unless $domsvc;
2564 foreach my $domain ( sort { $domains->{$a} cmp $domains->{$b} } keys %$domains ) {
2565 $text .= qq!<OPTION VALUE="!. $domain. '"'.
2566 ( ( $domsvc && $domain == $domsvc ) ? ' SELECTED' : '' ). ">".
2567 $domains->{$domain};
2570 $text .= qq!</SELECT></TD></TR>!;
2576 =item didselector HASHREF | LIST
2578 Takes as input a hashref or list of key/value pairs with the following keys:
2584 Field name for the returned HTML fragment.
2588 Service definition (see L<FS::part_svc>)
2592 Returns an HTML fragment for DID selection.
2604 my $rv = mason_comp( 'comp'=>'/elements/select-did.html',
2605 'args'=>[ %$param ],
2609 $rv->{'error'} || $rv->{'output'};
2615 =head1 RESELLER FUNCTIONS
2617 Note: Resellers can also use the B<signup_info> and B<new_customer> functions
2618 with their active session, and the B<customer_info> and B<order_pkg> functions
2619 with their active session and an additional I<custnum> parameter.
2621 For the most part, development of the reseller web interface has been
2622 superceded by agent-virtualized access to the backend.
2634 =item agent_list_customers
2636 List agent's customers.
2644 L<freeside-selfservice-clientd>, L<freeside-selfservice-server>