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 'list_payments' => 'MyAccount/list_payments',
52 'payment_receipt' => 'MyAccount/payment_receipt',
53 'cancel' => 'MyAccount/cancel', #add to ss cgi!
54 'payment_info' => 'MyAccount/payment_info',
55 'payment_info_renew_info' => 'MyAccount/payment_info_renew_info',
56 'process_payment' => 'MyAccount/process_payment',
57 'store_payment' => 'MyAccount/store_payment',
58 'process_stored_payment' => 'MyAccount/process_stored_payment',
59 'process_payment_order_pkg' => 'MyAccount/process_payment_order_pkg',
60 'process_payment_change_pkg' => 'MyAccount/process_payment_change_pkg',
61 'process_payment_order_renew' => 'MyAccount/process_payment_order_renew',
62 'process_prepay' => 'MyAccount/process_prepay',
63 'realtime_collect' => 'MyAccount/realtime_collect',
64 'list_pkgs' => 'MyAccount/list_pkgs', #add to ss (added?)
65 'list_svcs' => 'MyAccount/list_svcs', #add to ss (added?)
66 'list_svc_usage' => 'MyAccount/list_svc_usage',
67 'svc_status_html' => 'MyAccount/svc_status_html',
68 'svc_status_hash' => 'MyAccount/svc_status_hash',
69 'set_svc_status_hash' => 'MyAccount/set_svc_status_hash',
70 'set_svc_status_listadd' => 'MyAccount/set_svc_status_listadd',
71 'set_svc_status_listdel' => 'MyAccount/set_svc_status_listdel',
72 'set_svc_status_vacationadd'=> 'MyAccount/set_svc_status_vacationadd',
73 'set_svc_status_vacationdel'=> 'MyAccount/set_svc_status_vacationdel',
74 'acct_forward_info' => 'MyAccount/acct_forward_info',
75 'process_acct_forward' => 'MyAccount/process_acct_forward',
76 'list_dsl_devices' => 'MyAccount/list_dsl_devices',
77 'add_dsl_device' => 'MyAccount/add_dsl_device',
78 'delete_dsl_device' => 'MyAccount/delete_dsl_device',
79 'port_graph' => 'MyAccount/port_graph',
80 'list_cdr_usage' => 'MyAccount/list_cdr_usage',
81 'list_support_usage' => 'MyAccount/list_support_usage',
82 'order_pkg' => 'MyAccount/order_pkg', #add to ss cgi!
83 'change_pkg' => 'MyAccount/change_pkg',
84 'order_recharge' => 'MyAccount/order_recharge',
85 'renew_info' => 'MyAccount/renew_info',
86 'order_renew' => 'MyAccount/order_renew',
87 'cancel_pkg' => 'MyAccount/cancel_pkg', #add to ss cgi!
88 'suspend_pkg' => 'MyAccount/suspend_pkg', #add to ss cgi!
89 'charge' => 'MyAccount/charge', #?
90 'part_svc_info' => 'MyAccount/part_svc_info',
91 'provision_acct' => 'MyAccount/provision_acct',
92 'provision_phone' => 'MyAccount/provision_phone',
93 'provision_pbx' => 'MyAccount/provision_pbx',
94 'provision_external' => 'MyAccount/provision_external',
95 'provision_forward' => 'MyAccount/provision_forward',
96 'unprovision_svc' => 'MyAccount/unprovision_svc',
97 'myaccount_passwd' => 'MyAccount/myaccount_passwd',
98 'reset_passwd' => 'MyAccount/reset_passwd',
99 'check_reset_passwd' => 'MyAccount/check_reset_passwd',
100 'process_reset_passwd' => 'MyAccount/process_reset_passwd',
101 'validate_passwd' => 'MyAccount/validate_passwd',
102 'list_tickets' => 'MyAccount/list_tickets',
103 'create_ticket' => 'MyAccount/create_ticket',
104 'get_ticket' => 'MyAccount/get_ticket',
105 'adjust_ticket_priority' => 'MyAccount/adjust_ticket_priority',
106 'did_report' => 'MyAccount/did_report',
107 'signup_info' => 'Signup/signup_info',
108 'skin_info' => 'MyAccount/skin_info',
109 'access_info' => 'MyAccount/access_info',
110 'domain_select_hash' => 'Signup/domain_select_hash', # expose?
111 'new_customer' => 'Signup/new_customer',
112 'new_customer_minimal' => 'Signup/new_customer_minimal',
113 'capture_payment' => 'Signup/capture_payment',
114 'new_prospect' => 'Signup/new_prospect',
115 #N/A 'clear_signup_cache' => 'Signup/clear_cache',
116 'new_agent' => 'Agent/new_agent',
117 'agent_login' => 'Agent/agent_login',
118 'agent_logout' => 'Agent/agent_logout',
119 'agent_info' => 'Agent/agent_info',
120 'agent_list_customers' => 'Agent/agent_list_customers',
121 'check_username' => 'Agent/check_username',
122 'suspend_username' => 'Agent/suspend_username',
123 'unsuspend_username' => 'Agent/unsuspend_username',
124 'mason_comp' => 'MasonComponent/mason_comp',
125 'call_time' => 'PrepaidPhone/call_time',
126 'call_time_nanpa' => 'PrepaidPhone/call_time_nanpa',
127 'phonenum_balance' => 'PrepaidPhone/phonenum_balance',
129 'start_thirdparty' => 'MyAccount/start_thirdparty',
130 'finish_thirdparty' => 'MyAccount/finish_thirdparty',
132 'list_quotations' => 'MyAccount/quotation/list_quotations',
133 'quotation_new' => 'MyAccount/quotation/quotation_new',
134 'quotation_delete' => 'MyAccount/quotation/quotation_delete',
135 'quotation_info' => 'MyAccount/quotation/quotation_info',
136 'quotation_print' => 'MyAccount/quotation/quotation_print',
137 'quotation_add_pkg' => 'MyAccount/quotation/quotation_add_pkg',
138 'quotation_remove_pkg' => 'MyAccount/quotation/quotation_remove_pkg',
139 'quotation_order' => 'MyAccount/quotation/quotation_order',
141 'freesideinc_service' => 'Freeside/freesideinc_service',
146 qw( regionselector regionselector_hashref location_form
147 expselect popselector domainselector didselector
151 $ENV{'PATH'} ='/usr/bin:/usr/ucb:/bin';
152 $ENV{'SHELL'} = '/bin/sh';
153 $ENV{'IFS'} = " \t\n";
156 $ENV{'BASH_ENV'} = '';
158 #you can add BEGIN { $FS::SelfService::skip_uid_check = 1; }
159 #if you grant appropriate permissions to whatever user
160 my $freeside_uid = scalar(getpwnam('freeside'));
161 die "not running as the freeside user\n"
162 if $> != $freeside_uid && ! $skip_uid_check;
164 -e $dir or die "FATAL: $dir doesn't exist!";
165 -d $dir or die "FATAL: $dir isn't a directory!";
166 -r $dir or die "FATAL: Can't read $dir as freeside user!";
167 -x $dir or die "FATAL: $dir not searchable (executable) as freeside user!";
169 foreach my $autoload ( keys %autoload ) {
172 "sub $autoload { ". '
177 #warn scalar(@_). ": ". join(" / ", @_);
181 $param->{_packet} = \''. $autoload{$autoload}. '\';
183 simple_packet($param);
193 warn "sending ". $packet->{_packet}. " to server"
195 socket(SOCK, PF_UNIX, SOCK_STREAM, 0) or die "socket: $!";
196 connect(SOCK, sockaddr_un($socket)) or die "connect to $socket: $!";
197 nstore_fd($packet, \*SOCK) or die "can't send packet: $!";
200 #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 #block until there is a message on socket
203 # my $w = new IO::Select;
205 # my @wait = $w->can_read;
207 warn "reading message from server"
210 my $return = fd_retrieve(\*SOCK) or die "error reading result: $!";
211 die $return->{'_error'} if defined $return->{_error} && $return->{_error};
213 warn "returning message to client"
221 FS::SelfService - Freeside self-service API
225 # password and shell account changes
226 use FS::SelfService qw(passwd chfn chsh);
228 # "my account" functionality
229 use FS::SelfService qw( login customer_info invoice cancel payment_info process_payment );
231 #new-style login with an email address and password
232 # can also be used for svc_acct login, set $emailaddress to username@domain
233 my $rv = login ( { 'email' => $emailaddress,
234 'password' => $password,
237 if ( $rv->{'error'} ) {
238 #handle login error...
241 $session_id = $rv->{'session_id'};
244 #classic svc_acct-based login with separate username and password
245 my $rv = login( { 'username' => $username,
247 'password' => $password,
250 if ( $rv->{'error'} ) {
251 #handle login error...
254 $session_id = $rv->{'session_id'};
257 #svc_phone login with phone number and PIN
258 my $rv = login( { 'username' => $phone_number,
259 'domain' => 'svc_phone',
263 if ( $rv->{'error'} ) {
264 #handle login error...
267 $session_id = $rv->{'session_id'};
270 my $customer_info = customer_info( { 'session_id' => $session_id } );
272 my $payment_info = payment_info( { 'session_id' => $session_id } );
274 #!!! process_payment example
276 #!!! list_pkgs example
278 #!!! order_pkg example
280 #quoting a package, then ordering after confirmation
282 my $rv = quotation_new({ 'session_id' => $session_id });
283 my $qnum = $rv->{quotationnum};
284 # add packages to the quotation
285 $rv = quotation_add_pkg({ 'session_id' => $session_id,
286 'quotationnum' => $qnum,
287 'pkgpart' => $pkgpart,
288 'quantity' => $quantity, # defaults to 1
290 # repeat until all packages are added
291 # view the pricing information
292 $rv = quotation_info({ 'session_id' => $session_id,
293 'quotationnum' => $qnum,
295 print "Total setup charges: ".$rv->{total_setup}."\n".
296 "Total recurring charges: ".$rv->{total_recur}."\n";
297 # quotation_info also provides a detailed breakdown of charges, in
300 # ask customer for confirmation, then:
301 $rv = quotation_order({ 'session_id' => $session_id,
302 'quotationnum' => $qnum,
305 #!!! cancel_pkg example
307 # signup functionality
308 use FS::SelfService qw( signup_info new_customer new_customer_minimal );
310 my $signup_info = signup_info;
312 $rv = new_customer( {
315 'company' => $company,
316 'address1' => $address1,
317 'address2' => $address2,
321 'country' => $country,
322 'daytime' => $daytime,
326 'payinfo' => $payinfo,
328 'paystart_month' => $paystart_month
329 'paystart_year' => $paystart_year,
330 'payissue' => $payissue,
332 'paydate' => $paydate,
333 'payname' => $payname,
334 'invoicing_list' => $invoicing_list,
335 'referral_custnum' => $referral_custnum,
336 'agentnum' => $agentnum,
337 'pkgpart' => $pkgpart,
339 'username' => $username,
340 '_password' => $password,
344 'phonenum' => $phonenum,
349 my $error = $rv->{'error'};
350 if ( $error eq '_decline' ) {
360 Use this API to implement your own client "self-service" module.
362 If you just want to customize the look of the existing "self-service" module,
365 =head1 PASSWORD, GECOS, SHELL CHANGING FUNCTIONS
371 Changes the password for an existing user in svc_acct. Takes a hash
372 reference with the following keys:
378 Username of the account (required)
382 Domain of the account (required)
386 Old password (required)
390 New password (required)
408 =head1 "MY ACCOUNT" FUNCTIONS
414 Creates a user session. Takes a hash reference as parameter with the
421 Email address (username@domain), instead of username and domain. Required for
422 contact-based self-service login, can also be used for svc_acct-based login.
438 Returns a hash reference with the following keys:
444 Empty on success, or an error message on errors.
448 Session identifier for successful logins
452 =item customer_info HASHREF
454 Returns general customer information.
456 Takes a hash reference as parameter with a single key: B<session_id>
458 Returns a hash reference with the following keys:
472 Array reference of hash references of open inoices. Each hash reference has
473 the following keys: invnum, date, owed
477 An HTML fragment containing shipping and billing addresses.
479 =item The following fields are also returned
481 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
485 =item customer_recurring HASHREF
487 Takes a hash reference as parameter with a single key B<session_id>
488 or keys B<agent_session_id> and B<custnum>.
490 Returns a hash reference with the keys error, custnum and display_recurring.
492 display_recurring is an arrayref of hashrefs with the following keys:
498 frequency of charge, in months unless units are specified
502 frequency of charge, suitable for display
506 amount charged at this frequency
510 =item edit_info HASHREF
512 Takes a hash reference as parameter with any of the following keys:
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
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
520 Returns a hash reference with a single key, B<error>, empty on success, or an
521 error message on errors
523 =item invoice HASHREF
525 Returns an invoice. Takes a hash reference as parameter with two keys:
526 session_id and invnum
528 Returns a hash reference with the following keys:
534 Empty on success, or an error message on errors
546 =item list_invoices HASHREF
548 Returns a list of all customer invoices. Takes a hash reference with a single
551 Returns a hash reference with the following keys:
557 Empty on success, or an error message on errors
561 Reference to array of hash references with the following keys:
571 Invoice date, in UNIX epoch time
579 Cancels this customer.
581 Takes a hash reference as parameter with a single key: B<session_id>
583 Returns a hash reference with a single key, B<error>, which is empty on
584 success or an error message on errors.
586 =item payment_info HASHREF
588 Returns information that may be useful in displaying a payment page.
590 Takes a hash reference as parameter with the following keys:
598 =item omit_cust_main_county
600 Optional, pass a true value to omit cust_main_county data for performance.
604 Returns a hash reference with the following keys:
610 Empty on success, or an error message on errors
618 Exact name on credit card (CARD/DCRD)
642 Customer's current default payment type.
646 For CARD/DCRD payment types, the card type (Visa card, MasterCard, Discover card, American Express card, etc.)
650 For CARD/DCRD payment types, the card number
654 For CARD/DCRD payment types, expiration month
658 For CARD/DCRD payment types, expiration year
660 =item cust_main_county
662 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.
666 Array reference of all states in the current default country.
670 Hash reference of card types; keys are card types, values are the exact strings
671 passed to the process_payment function
675 #this doesn't actually work yet
679 #Unique transaction identifier (prevents multiple charges), passed to the
680 #process_payment function
684 =item process_payment HASHREF
686 Processes a payment and possible change of address or payment type. Takes a
687 hash reference as parameter with the following keys:
701 If true, address and card information entered will be saved for subsequent
706 If true, future credit card payments will be done automatically (sets payby to
707 CARD). If false, future credit card payments will be done on-demand (sets
708 payby to DCRD). This option only has meaning if B<save> is set true.
736 Two-letter country code
744 Card expiration month
752 #this doesn't actually work yet
756 #Unique transaction identifier, returned from the payment_info function.
757 #Prevents multiple charges.
761 Returns a hash reference with a single key, B<error>, empty on success, or an
762 error message on errors.
764 =item process_payment_order_pkg
766 Combines the B<process_payment> and B<order_pkg> functions in one step. If the
767 payment processes sucessfully, the package is ordered. Takes a hash reference
768 as parameter with the keys of both methods.
770 Returns a hash reference with a single key, B<error>, empty on success, or an
771 error message on errors.
773 =item process_payment_change_pkg
775 Combines the B<process_payment> and B<change_pkg> functions in one step. If the
776 payment processes sucessfully, the package is ordered. Takes a hash reference
777 as parameter with the keys of both methods.
779 Returns a hash reference with a single key, B<error>, empty on success, or an
780 error message on errors.
783 =item process_payment_order_renew
785 Combines the B<process_payment> and B<order_renew> functions in one step. If
786 the payment processes sucessfully, the renewal is processed. Takes a hash
787 reference as parameter with the keys of both methods.
789 Returns a hash reference with a single key, B<error>, empty on success, or an
790 error message on errors.
794 Returns package information for this customer. For more detail on services,
797 Takes a hash reference as parameter with a single key: B<session_id>
799 Returns a hash reference containing customer package information. The hash reference contains the following keys:
809 Empty on success, or an error message on errors.
811 =item cust_pkg HASHREF
813 Array reference of hash references, each of which has the fields of a cust_pkg
814 record (see L<FS::cust_pkg>) as well as the fields below. Note these are not
815 the internal FS:: objects, but hash references of columns and values.
819 =item part_pkg fields
821 All fields of part_pkg for this specific cust_pkg (be careful with this
822 information - it may reveal more about your available packages than you would
823 like users to know in aggregate)
827 #XXX pare part_pkg fields down to a more secure subset
831 An array of hash references indicating information on unprovisioned services
832 available for provisioning for this specific cust_pkg. Each has the following
837 =item part_svc fields
839 All fields of part_svc (be careful with this information - it may reveal more
840 about your available packages than you would like users to know in aggregate)
844 #XXX pare part_svc fields down to a more secure subset
850 An array of hash references indicating information on the customer services
851 already provisioned for this specific cust_pkg. Each has the following keys:
857 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.
863 Primary key for this service
867 Service definition (see L<FS::part_svc>)
871 Customer package (see L<FS::cust_pkg>)
875 Blank if the service is not over limit, or the date the service exceeded its usage limit (as a UNIX timestamp).
883 Returns service information for this customer.
885 Takes a hash reference as parameter with a single key: B<session_id>
887 Returns a hash reference containing customer package information. The hash reference contains the following keys:
897 An array of hash references indicating information on all of this customer's
898 services. Each has the following keys:
904 Primary key for this service
912 Meaningful user-specific identifier for the service (i.e. username, domain, or
917 Account (svc_acct) services also have the following keys:
935 Upload bytes remaining
939 Download bytes remaining
943 Total bytes remaining
945 =item recharge_amount
949 =item recharge_seconds
951 Number of seconds gained by recharge
953 =item recharge_upbytes
955 Number of upload bytes gained by recharge
957 =item recharge_downbytes
959 Number of download bytes gained by recharge
961 =item recharge_totalbytes
963 Number of total bytes gained by recharge
971 Orders a package for this customer.
973 If signup_server-realtime is set, bills the new package, attemps to collect
974 payment and (for auto-payment customers) cancels the package if the payment is
977 Takes a hash reference as parameter with the following keys:
987 Package to order (see L<FS::part_pkg>).
991 Quantity for this package order (default 1).
993 =item run_bill_events
995 If true, runs billing events for the customer after ordering and billing the
996 package (signup_server-realtime must be set).
1000 Optional locationnum for this package order, for existing locations.
1002 Or, for new locations, pass the following fields: address1*, address2, city*,
1003 county, state*, zip*, country. (* = required in this case)
1005 (None of this is required at all if you are just ordering a package
1006 at the customer's existing default service location.)
1024 Service to order (see L<FS::part_svc>).
1026 Normally optional; required only to provision a non-svc_acct service, or if the
1027 package definition does not contain one svc_acct service definition with
1028 quantity 1 (it may contain others with quantity >1). A svcpart of "none" can
1029 also be specified to indicate that no initial service should be provisioned.
1033 Fields used when provisioning an svc_acct service:
1047 Optional security phrase
1051 Optional Access number number
1055 Fields used when provisioning an svc_domain service:
1065 Fields used when provisioning an svc_phone service:
1083 Fields used when provisioning an svc_external service:
1089 External numeric ID.
1093 External text title.
1097 Fields used when provisioning an svc_pbx service:
1111 Returns a hash reference with a single key, B<error>, empty on success, or an
1112 error message on errors. The special error '_decline' is returned for
1113 declined transactions.
1117 Changes a package for this customer.
1119 Takes a hash reference as parameter with the following keys:
1129 Existing customer package.
1133 New package to order (see L<FS::part_pkg>).
1137 Quantity for this package order (default 1).
1141 Returns a hash reference with the following keys:
1147 Empty on success, or an error message on errors.
1151 On success, the new pkgnum
1158 Provides useful info for early renewals.
1160 Takes a hash reference as parameter with the following keys:
1170 Returns a hash reference. On errors, it contains a single key, B<error>, with
1171 the error message. Otherwise, contains a single key, B<dates>, pointing to
1172 an array refernce of hash references. Each hash reference contains the
1179 (Future) Bill date. Indicates a future date for which billing could be run.
1180 Specified as an integer UNIX timestamp. Pass this value to the B<order_renew>
1183 =item bill_date_pretty
1185 (Future) Bill date as a human-readable string. (Convenience for display;
1186 subject to change, so best not to parse for the date.)
1190 Base amount which will be charged if renewed early as of this date.
1194 Renewal date; i.e. even-futher future date at which the customer will be paid
1195 through if the early renewal is completed with the given B<bill-date>.
1196 Specified as an integer UNIX timestamp.
1198 =item renew_date_pretty
1200 Renewal date as a human-readable string. (Convenience for display;
1201 subject to change, so best not to parse for the date.)
1205 Package that will be renewed.
1209 Expiration date of the package that will be renewed.
1211 =item expire_date_pretty
1213 Expiration date of the package that will be renewed, as a human-readable
1214 string. (Convenience for display; subject to change, so best not to parse for
1221 Renews this customer early; i.e. runs billing for this customer in advance.
1223 Takes a hash reference as parameter with the following keys:
1233 Integer date as returned by the B<renew_info> function, indicating the advance
1234 date for which to run billing.
1238 Returns a hash reference with a single key, B<error>, empty on success, or an
1239 error message on errors.
1243 Cancels a package for this customer.
1245 Takes a hash reference as parameter with the following keys:
1255 pkgpart of package to cancel
1259 Optional date, for future cancellation (expiration) instead of immediate
1260 cancellation. Specified as an integer UNIX timestamp ("epoch time").
1264 Returns a hash reference with a single key, B<error>, empty on success, or an
1265 error message on errors.
1267 =item provision_acct
1269 Provisions an account (svc_acct).
1271 Takes a hash reference as parameter with the following keys:
1281 pkgnum of package into which this service is provisioned
1285 svcpart or service definition to provision
1295 =item provision_phone
1297 Provisions a phone number (svc_phone).
1299 Takes a hash reference as parameter with the following keys:
1309 pkgnum of package into which this service is provisioned
1313 svcpart or service definition to provision
1333 E911 Address (optional)
1339 Provisions a customer PBX (svc_pbx).
1341 Takes a hash reference as parameter with the following keys:
1351 pkgnum of package into which this service is provisioned
1355 svcpart or service definition to provision
1361 =item max_extensions
1363 =item max_simultaneous
1369 =item provision_external
1371 Provisions an external service (svc_external).
1373 Takes a hash reference as parameter with the following keys:
1383 pkgnum of package into which this service is provisioned
1387 svcpart or service definition to provision
1397 =head2 "MY ACCOUNT" CONTACT FUNCTIONS
1401 =item contact_passwd
1403 Changes the password for the currently-logged in contact.
1405 Takes a hash reference as parameter with the following keys:
1415 Returns a hash reference with a single parameter, B<error>, which contains an
1416 error message, or empty on success.
1420 Takes a hash reference as parameter with a single key, B<session_id>.
1422 Returns a hash reference with two parameters: B<error>, which contains an error
1423 message, or empty on success, and B<contacts>, a list of contacts.
1425 B<contacts> is an array reference of hash references (i.e. an array of structs,
1426 in XML-RPC). Each hash reference (struct) has the following keys:
1434 Contact class name (contact type).
1446 Position ("Director of Silly Walks"), NOT honorific ("Mr." or "Mrs.")
1450 Comma-separated list of email addresses
1454 =item selfservice_access
1462 Updates information for the currently-logged in contact, or (optionally) the
1465 Takes a hash reference as parameter with the following keys:
1473 If already logged in as a contact, this is optional.
1483 Returns a hash reference with a single parameter, B<error>, which contains an
1484 error message, or empty on success.
1488 Creates a new contact.
1490 Takes a hash reference as parameter with the following keys:
1504 Optional contact classnum (TODO: or name)
1508 =item selfservice_access
1510 Y to enable self-service access
1516 Returns a hash reference with a single parameter, B<error>, which contains an
1517 error message, or empty on success.
1519 =item delete_contact
1521 Deletes a contact. (Note: Cannot at this time delete the currently-logged in
1524 Takes a hash reference as parameter with the following keys:
1534 Returns a hash reference with a single parameter, B<error>, which contains an
1535 error message, or empty on success.
1539 =head2 "MY ACCOUNT" QUOTATION FUNCTIONS
1541 All of these functions require the user to be logged in, and the 'session_id'
1542 key to be included in the argument hashref.`
1546 =item list_quotations HASHREF
1548 Returns a hashref listing this customer's active self-service quotations.
1555 an arrayref containing an element for each quotation.
1563 the date it was started
1567 the number of packages
1571 the sum of setup fees
1575 the sum of recurring charges
1579 =item quotation_new HASHREF
1581 Creates an empty quotation and returns a hashref containing 'quotationnum',
1582 the primary key of the new quotation.
1584 =item quotation_delete HASHREF
1586 Disables (does not really delete) a quotation. Takes the following arguments:
1592 =item quotationnum - the quotation to delete
1596 Returns 'error' => a string, which will be empty on success.
1598 =item quotation_info HASHREF
1600 Returns total and detailed pricing information on a quotation.
1602 Takes the following arguments:
1608 =item quotationnum - the quotation to return
1612 Returns a hashref containing:
1614 - total_setup, the total of setup fees (and their taxes)
1615 - total_recur, the total of all recurring charges (and their taxes)
1616 - sections, an arrayref containing an element for each quotation section.
1617 - description, a line of text describing the group of charges
1618 - subtotal, the total of charges in this group (if appropriate)
1619 - detail_items, an arrayref of line items
1620 - pkgnum, the reference number of the package
1621 - description, the package name (or tax name)
1623 - amount, the amount charged
1624 If the detail item represents a subtotal, it will instead contain:
1625 - total_item: description of the subtotal
1626 - total_amount: the subtotal amount
1629 =item quotation_print HASHREF
1631 Renders the quotation as HTML or PDF. Takes the following arguments:
1637 =item quotationnum - the quotation to return
1639 =item format - 'html' or 'pdf'
1643 Returns a hashref containing 'document', the contents of the file.
1645 =item quotation_add_pkg HASHREF
1647 Adds a package to a quotation. Takes the following arguments:
1653 =item pkgpart - the package to add
1655 =item quotationnum - the quotation to add it to
1657 =item quantity - the package quantity (defaults to 1)
1659 =item address1, address2, city, state, zip, country - address fields to set
1660 the service location
1664 Returns 'error' => a string, which will be empty on success.
1666 =item quotation_remove_pkg HASHREF
1668 Removes a package from a quotation. Takes the following arguments:
1674 =item pkgnum - the primary key (quotationpkgnum) of the package to remove
1676 =item quotationnum - the quotation to remove it from
1680 Returns 'error' => a string, which will be empty on success.
1682 =item quotation_order HASHREF
1684 Converts the packages in a quotation into real packages. Takes the following
1687 Takes the following arguments:
1693 =item quotationnum - the quotation to order
1699 =head1 SIGNUP FUNCTIONS
1703 =item signup_info HASHREF
1705 Takes a hash reference as parameter with the following keys:
1709 =item session_id - Optional agent/reseller interface session
1713 Returns a hash reference containing information that may be useful in
1714 displaying a signup page. The hash reference contains the following keys:
1718 =item cust_main_county
1720 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.
1724 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
1725 an agentnum specified explicitly via reseller interface session_id in the
1730 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.
1732 =item agentnum2part_pkg
1734 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.
1738 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.
1740 =item security_phrase
1742 True if the "security_phrase" feature is enabled
1746 Array reference of acceptable payment types for signup
1752 credit card - automatic
1756 credit card - on-demand - version 1.5+ only
1760 electronic check - automatic
1764 electronic check - on-demand - version 1.5+ only
1772 billing, not recommended for signups
1776 free, definitely not recommended for signups
1780 special billing type: applies a credit (see FS::prepay_credit) and sets billing type to BILL
1786 True if CVV features are available (1.5+ or 1.4.2 with CVV schema patch)
1790 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".
1796 =item countrydefault
1802 =item new_customer_minimal HASHREF
1804 Creates a new customer.
1806 Current differences from new_customer: An address is not required. promo_code
1807 and reg_code are not supported. If invoicing_list and _password is passed, a
1808 contact will be created with self-service access (no pkgpart or username is
1809 necessary). No initial billing is run (this may change in a future version).
1811 Takes a hash reference as parameter with the following keys:
1817 first name (required)
1821 last name (required)
1825 (not typically collected; mostly used for ACH transactions)
1857 Daytime phone number
1861 Evening phone number
1869 CARD, DCRD, CHEK, DCHK, LECB, BILL, COMP or PREPAY (see L</signup_info> (required)
1873 Card number for CARD/DCRD, account_number@aba_number for CHEK/DCHK, prepaid "pin" for PREPAY, purchase order number for BILL
1877 Credit card CVV2 number (1.5+ or 1.4.2 with CVV schema patch)
1881 Expiration date for CARD/DCRD
1885 Exact name on credit card for CARD/DCRD, bank name for CHEK/DCHK
1887 =item invoicing_list
1889 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),
1891 =item referral_custnum
1893 referring customer number
1901 pkgpart of initial package
1917 Access number (index, not the literal number)
1921 Country code (to be provisioned as a service)
1925 Phone number (to be provisioned as a service)
1933 Returns a hash reference with the following keys:
1939 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)
1943 =item new_customer HASHREF
1945 Creates a new customer. Takes a hash reference as parameter with the
1952 first name (required)
1956 last name (required)
1960 (not typically collected; mostly used for ACH transactions)
1966 =item address1 (required)
1974 =item city (required)
1982 =item state (required)
1986 =item zip (required)
2002 Optional shipping address fields. If sending an optional shipping address,
2003 ship_address1, ship_city, ship_state and ship_zip are required.
2007 Daytime phone number
2011 Evening phone number
2019 CARD, DCRD, CHEK, DCHK, LECB, BILL, COMP or PREPAY (see L</signup_info> (required)
2023 Card number for CARD/DCRD, account_number@aba_number for CHEK/DCHK, prepaid "pin" for PREPAY, purchase order number for BILL
2027 Credit card CVV2 number (1.5+ or 1.4.2 with CVV schema patch)
2031 Expiration date for CARD/DCRD
2035 Exact name on credit card for CARD/DCRD, bank name for CHEK/DCHK
2037 =item invoicing_list
2039 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),
2041 =item referral_custnum
2043 referring customer number
2051 pkgpart of initial package
2067 Access number (index, not the literal number)
2071 Country code (to be provisioned as a service)
2075 Phone number (to be provisioned as a service)
2083 Returns a hash reference with the following keys:
2089 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)
2093 =item regionselector HASHREF | LIST
2095 Takes as input a hashref or list of key/value pairs with the following keys:
2099 =item selected_county
2101 Currently selected county
2103 =item selected_state
2105 Currently selected state
2107 =item selected_country
2109 Currently selected country
2113 Specify a unique prefix string if you intend to use the HTML output multiple time son one page.
2117 Specify a javascript subroutine to call on changes
2123 =item default_country
2129 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>.
2133 Returns a list consisting of three HTML fragments for county selection,
2134 state selection and country selection, respectively.
2138 #false laziness w/FS::cust_main_county (this is currently the "newest" version)
2139 sub regionselector {
2146 $param->{'selected_country'} ||= $param->{'default_country'};
2147 $param->{'selected_state'} ||= $param->{'default_state'};
2149 my $prefix = exists($param->{'prefix'}) ? $param->{'prefix'} : '';
2153 my %cust_main_county;
2155 # unless ( @cust_main_county ) { #cache
2156 #@cust_main_county = qsearch('cust_main_county', {} );
2157 #foreach my $c ( @cust_main_county ) {
2158 foreach my $c ( @{ $param->{'locales'} } ) {
2159 #$countyflag=1 if $c->county;
2160 $countyflag=1 if $c->{county};
2161 #push @{$cust_main_county{$c->country}{$c->state}}, $c->county;
2162 #$cust_main_county{$c->country}{$c->state}{$c->county} = 1;
2163 $cust_main_county{$c->{country}}{$c->{state}}{$c->{county}} = 1;
2166 $countyflag=1 if $param->{selected_county};
2168 my $script_html = <<END;
2170 function opt(what,value,text) {
2171 var optionName = new Option(text, value, false, false);
2172 var length = what.length;
2173 what.options[length] = optionName;
2175 function ${prefix}country_changed(what) {
2176 country = what.options[what.selectedIndex].text;
2177 for ( var i = what.form.${prefix}state.length; i >= 0; i-- )
2178 what.form.${prefix}state.options[i] = null;
2180 #what.form.${prefix}state.options[0] = new Option('', '', false, true);
2182 foreach my $country ( sort keys %cust_main_county ) {
2183 $script_html .= "\nif ( country == \"$country\" ) {\n";
2184 foreach my $state ( sort keys %{$cust_main_county{$country}} ) {
2185 my $text = $state || '(n/a)';
2186 $script_html .= qq!opt(what.form.${prefix}state, "$state", "$text");\n!;
2188 $script_html .= "}\n";
2191 $script_html .= <<END;
2193 function ${prefix}state_changed(what) {
2196 if ( $countyflag ) {
2197 $script_html .= <<END;
2198 state = what.options[what.selectedIndex].text;
2199 country = what.form.${prefix}country.options[what.form.${prefix}country.selectedIndex].text;
2200 for ( var i = what.form.${prefix}county.length; i >= 0; i-- )
2201 what.form.${prefix}county.options[i] = null;
2204 foreach my $country ( sort keys %cust_main_county ) {
2205 $script_html .= "\nif ( country == \"$country\" ) {\n";
2206 foreach my $state ( sort keys %{$cust_main_county{$country}} ) {
2207 $script_html .= "\nif ( state == \"$state\" ) {\n";
2208 #foreach my $county ( sort @{$cust_main_county{$country}{$state}} ) {
2209 foreach my $county ( sort keys %{$cust_main_county{$country}{$state}} ) {
2210 my $text = $county || '(n/a)';
2212 qq!opt(what.form.${prefix}county, "$county", "$text");\n!;
2214 $script_html .= "}\n";
2216 $script_html .= "}\n";
2220 $script_html .= <<END;
2225 my $county_html = $script_html;
2226 if ( $countyflag ) {
2227 $county_html .= qq!<SELECT NAME="${prefix}county" onChange="$param->{'onchange'}">!;
2228 foreach my $county (
2229 sort keys %{ $cust_main_county{$param->{'selected_country'}}{$param->{'selected_state'}} }
2231 my $text = $county || '(n/a)';
2232 $county_html .= qq!<OPTION VALUE="$county"!.
2233 ($county eq $param->{'selected_county'} ?
2240 $county_html .= '</SELECT>';
2243 qq!<INPUT TYPE="hidden" NAME="${prefix}county" VALUE="$param->{'selected_county'}">!;
2246 my $state_html = qq!<SELECT NAME="${prefix}state" !.
2247 qq!onChange="${prefix}state_changed(this); $param->{'onchange'}">!;
2248 foreach my $state ( sort keys %{ $cust_main_county{$param->{'selected_country'}} } ) {
2249 my $text = $state || '(n/a)';
2250 my $selected = $state eq $param->{'selected_state'} ? 'SELECTED' : '';
2251 $state_html .= "\n<OPTION $selected VALUE=$state>$text</OPTION>"
2253 $state_html .= '</SELECT>';
2255 my $country_html = '';
2256 if ( scalar( keys %cust_main_county ) > 1 ) {
2258 $country_html = qq(<SELECT NAME="${prefix}country" ).
2259 qq(onChange="${prefix}country_changed(this); ).
2260 $param->{'onchange'}.
2263 my $countrydefault = $param->{default_country} || 'US';
2264 foreach my $country (
2265 sort { ($b eq $countrydefault) <=> ($a eq $countrydefault) or $a cmp $b }
2266 keys %cust_main_county
2268 my $selected = $country eq $param->{'selected_country'}
2271 $country_html .= "\n<OPTION$selected>$country</OPTION>"
2273 $country_html .= '</SELECT>';
2276 $country_html = qq(<INPUT TYPE="hidden" NAME="${prefix}country" ).
2277 ' VALUE="'. (keys %cust_main_county )[0]. '">';
2281 ($county_html, $state_html, $country_html);
2285 sub regionselector_hashref {
2286 my ($county_html, $state_html, $country_html) = regionselector(@_);
2288 'county_html' => $county_html,
2289 'state_html' => $state_html,
2290 'country_html' => $country_html,
2294 =item location_form HASHREF | LIST
2296 Takes as input a hashref or list of key/value pairs with the following keys:
2302 Current customer session_id
2306 Omit red asterisks from required fields.
2308 =item address1_label
2310 Label for first address line.
2314 Returns an HTML fragment for a location form (address, city, state, zip,
2327 my $session_id = delete $param->{'session_id'};
2329 my $rv = mason_comp( 'session_id' => $session_id,
2330 'comp' => '/elements/location.html',
2331 'args' => [ %$param ],
2335 $rv->{'error'} || $rv->{'output'};
2340 #=item expselect HASHREF | LIST
2342 #Takes as input a hashref or list of key/value pairs with the following keys:
2346 #=item prefix - Specify a unique prefix string if you intend to use the HTML output multiple time son one page.
2348 #=item date - current date, in yyyy-mm-dd or m-d-yyyy format
2352 =item expselect PREFIX [ DATE ]
2354 Takes as input a unique prefix string and the current expiration date, in
2355 yyyy-mm-dd or m-d-yyyy format
2357 Returns an HTML fragments for expiration date selection.
2363 #if ( ref($_[0]) ) {
2367 #my $prefix = $param->{'prefix'};
2368 #my $prefix = exists($param->{'prefix'}) ? $param->{'prefix'} : '';
2369 #my $date = exists($param->{'date'}) ? $param->{'date'} : '';
2371 my $date = scalar(@_) ? shift : '';
2373 my( $m, $y ) = ( 0, 0 );
2374 if ( $date =~ /^(\d{4})-(\d{2})-\d{2}$/ ) { #PostgreSQL date format
2375 ( $m, $y ) = ( $2, $1 );
2376 } elsif ( $date =~ /^(\d{1,2})-(\d{1,2}-)?(\d{4}$)/ ) {
2377 ( $m, $y ) = ( $1, $3 );
2379 my $return = qq!<SELECT NAME="$prefix!. qq!_month" SIZE="1">!;
2381 $return .= qq!<OPTION VALUE="$_"!;
2382 $return .= " SELECTED" if $_ == $m;
2385 $return .= qq!</SELECT>/<SELECT NAME="$prefix!. qq!_year" SIZE="1">!;
2387 my $thisYear = $t[5] + 1900;
2388 for ( ($thisYear > $y && $y > 0 ? $y : $thisYear) .. ($thisYear+10) ) {
2389 $return .= qq!<OPTION VALUE="$_"!;
2390 $return .= " SELECTED" if $_ == $y;
2393 $return .= "</SELECT>";
2398 =item popselector HASHREF | LIST
2400 Takes as input a hashref or list of key/value pairs with the following keys:
2406 Access number number
2410 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>.
2414 Returns an HTML fragment for access number selection.
2418 #horrible false laziness with FS/FS/svc_acct_pop.pm::popselector
2426 my $popnum = $param->{'popnum'};
2427 my $pops = $param->{'pops'};
2429 return '<INPUT TYPE="hidden" NAME="popnum" VALUE="">' unless @$pops;
2430 return $pops->[0]{city}. ', '. $pops->[0]{state}.
2431 ' ('. $pops->[0]{ac}. ')/'. $pops->[0]{exch}. '-'. $pops->[0]{loc}.
2432 '<INPUT TYPE="hidden" NAME="popnum" VALUE="'. $pops->[0]{popnum}. '">'
2433 if scalar(@$pops) == 1;
2436 my %popnum2pop = ();
2438 push @{ $pop{ $_->{state} }->{ $_->{ac} } }, $_;
2439 $popnum2pop{$_->{popnum}} = $_;
2444 function opt(what,href,text) {
2445 var optionName = new Option(text, href, false, false)
2446 var length = what.length;
2447 what.options[length] = optionName;
2451 my $init_popstate = $param->{'init_popstate'};
2452 if ( $init_popstate ) {
2453 $text .= '<INPUT TYPE="hidden" NAME="init_popstate" VALUE="'.
2454 $init_popstate. '">';
2457 function acstate_changed(what) {
2458 state = what.options[what.selectedIndex].text;
2459 what.form.popac.options.length = 0
2460 what.form.popac.options[0] = new Option("Area code", "-1", false, true);
2464 my @states = $init_popstate ? ( $init_popstate ) : keys %pop;
2465 foreach my $state ( sort { $a cmp $b } @states ) {
2466 $text .= "\nif ( state == \"$state\" ) {\n" unless $init_popstate;
2468 foreach my $ac ( sort { $a cmp $b } keys %{ $pop{$state} }) {
2469 $text .= "opt(what.form.popac, \"$ac\", \"$ac\");\n";
2470 if ($ac eq $param->{'popac'}) {
2471 $text .= "what.form.popac.options[what.form.popac.length-1].selected = true;\n";
2474 $text .= "}\n" unless $init_popstate;
2476 $text .= "popac_changed(what.form.popac)}\n";
2479 function popac_changed(what) {
2480 ac = what.options[what.selectedIndex].text;
2481 what.form.popnum.options.length = 0;
2482 what.form.popnum.options[0] = new Option("City", "-1", false, true);
2486 foreach my $state ( @states ) {
2487 foreach my $popac ( keys %{ $pop{$state} } ) {
2488 $text .= "\nif ( ac == \"$popac\" ) {\n";
2490 foreach my $pop ( @{$pop{$state}->{$popac}}) {
2491 my $o_popnum = $pop->{popnum};
2492 my $poptext = $pop->{city}. ', '. $pop->{state}.
2493 ' ('. $pop->{ac}. ')/'. $pop->{exch}. '-'. $pop->{loc};
2495 $text .= "opt(what.form.popnum, \"$o_popnum\", \"$poptext\");\n";
2496 if ($popnum == $o_popnum) {
2497 $text .= "what.form.popnum.options[what.form.popnum.length-1].selected = true;\n";
2505 $text .= "}\n</SCRIPT>\n";
2507 $param->{'acstate'} = '' unless defined($param->{'acstate'});
2510 qq!<TABLE CELLPADDING="0"><TR><TD><SELECT NAME="acstate"! .
2511 qq!SIZE=1 onChange="acstate_changed(this)"><OPTION VALUE=-1>State!;
2512 $text .= "<OPTION" . ($_ eq $param->{'acstate'} ? " SELECTED" : "") .
2513 ">$_" foreach sort { $a cmp $b } @states;
2514 $text .= '</SELECT>'; #callback? return 3 html pieces? #'</TD>';
2517 qq!<SELECT NAME="popac" SIZE=1 onChange="popac_changed(this)">!.
2518 qq!<OPTION>Area code</SELECT></TR><TR VALIGN="top">!;
2520 $text .= qq!<TR><TD><SELECT NAME="popnum" SIZE=1 STYLE="width: 20em"><OPTION>City!;
2523 #comment this block to disable initial list polulation
2524 my @initial_select = ();
2525 if ( scalar( @$pops ) > 100 ) {
2526 push @initial_select, $popnum2pop{$popnum} if $popnum2pop{$popnum};
2528 @initial_select = @$pops;
2530 foreach my $pop ( sort { $a->{state} cmp $b->{state} } @initial_select ) {
2531 $text .= qq!<OPTION VALUE="!. $pop->{popnum}. '"'.
2532 ( ( $popnum && $pop->{popnum} == $popnum ) ? ' SELECTED' : '' ). ">".
2533 $pop->{city}. ', '. $pop->{state}.
2534 ' ('. $pop->{ac}. ')/'. $pop->{exch}. '-'. $pop->{loc};
2537 $text .= qq!</SELECT></TD></TR></TABLE>!;
2543 =item domainselector HASHREF | LIST
2545 Takes as input a hashref or list of key/value pairs with the following keys:
2555 Service number of the selected item.
2559 Returns an HTML fragment for domain selection.
2563 sub domainselector {
2570 my $domsvc= $param->{'domsvc'};
2572 domain_select_hash(map {$_ => $param->{$_}} qw(pkgnum svcpart pkgpart) );
2573 my $domains = $rv->{'domains'};
2574 $domsvc = $rv->{'domsvc'} unless $domsvc;
2576 return '<INPUT TYPE="hidden" NAME="domsvc" VALUE="">'
2577 unless scalar(keys %$domains);
2579 if (scalar(keys %$domains) == 1) {
2581 foreach(keys %$domains) {
2584 return '<TR><TD ALIGN="right">Domain</TD><TD>'. $domains->{$key}.
2585 '<INPUT TYPE="hidden" NAME="domsvc" VALUE="'. $key. '"></TD></TR>'
2588 my $text .= qq!<TR><TD ALIGN="right">Domain</TD><TD><SELECT NAME="domsvc" SIZE=1 STYLE="width: 20em">!;
2590 $text .= '<OPTION>(Choose Domain)' unless $domsvc;
2592 foreach my $domain ( sort { $domains->{$a} cmp $domains->{$b} } keys %$domains ) {
2593 $text .= qq!<OPTION VALUE="!. $domain. '"'.
2594 ( ( $domsvc && $domain == $domsvc ) ? ' SELECTED' : '' ). ">".
2595 $domains->{$domain};
2598 $text .= qq!</SELECT></TD></TR>!;
2604 =item didselector HASHREF | LIST
2606 Takes as input a hashref or list of key/value pairs with the following keys:
2612 Field name for the returned HTML fragment.
2616 Service definition (see L<FS::part_svc>)
2620 Returns an HTML fragment for DID selection.
2632 my $rv = mason_comp( 'comp'=>'/elements/select-did.html',
2633 'args'=>[ %$param ],
2637 $rv->{'error'} || $rv->{'output'};
2643 =head1 RESELLER FUNCTIONS
2645 Note: Resellers can also use the B<signup_info> and B<new_customer> functions
2646 with their active session, and the B<customer_info> and B<order_pkg> functions
2647 with their active session and an additional I<custnum> parameter.
2649 For the most part, development of the reseller web interface has been
2650 superceded by agent-virtualized access to the backend.
2662 =item agent_list_customers
2664 List agent's customers.
2672 L<freeside-selfservice-clientd>, L<freeside-selfservice-server>