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 'switch_cust' => 'MyAccount/switch_cust',
34 'customer_info' => 'MyAccount/customer_info',
35 'customer_info_short' => 'MyAccount/customer_info_short',
36 'billing_history' => 'MyAccount/billing_history',
37 'edit_info' => 'MyAccount/edit_info', #add to ss cgi!
38 'invoice' => 'MyAccount/invoice',
39 'invoice_pdf' => 'MyAccount/invoice_pdf',
40 'legacy_invoice' => 'MyAccount/legacy_invoice',
41 'legacy_invoice_pdf' => 'MyAccount/legacy_invoice_pdf',
42 'invoice_logo' => 'MyAccount/invoice_logo',
43 'list_invoices' => 'MyAccount/list_invoices', #?
44 'cancel' => 'MyAccount/cancel', #add to ss cgi!
45 'payment_info' => 'MyAccount/payment_info',
46 'payment_info_renew_info' => 'MyAccount/payment_info_renew_info',
47 'process_payment' => 'MyAccount/process_payment',
48 'store_payment' => 'MyAccount/store_payment',
49 'process_stored_payment' => 'MyAccount/process_stored_payment',
50 'process_payment_order_pkg' => 'MyAccount/process_payment_order_pkg',
51 'process_payment_change_pkg' => 'MyAccount/process_payment_change_pkg',
52 'process_payment_order_renew' => 'MyAccount/process_payment_order_renew',
53 'process_prepay' => 'MyAccount/process_prepay',
54 'realtime_collect' => 'MyAccount/realtime_collect',
55 'list_pkgs' => 'MyAccount/list_pkgs', #add to ss (added?)
56 'list_svcs' => 'MyAccount/list_svcs', #add to ss (added?)
57 'list_svc_usage' => 'MyAccount/list_svc_usage',
58 'svc_status_html' => 'MyAccount/svc_status_html',
59 'svc_status_hash' => 'MyAccount/svc_status_hash',
60 'set_svc_status_hash' => 'MyAccount/set_svc_status_hash',
61 'set_svc_status_listadd' => 'MyAccount/set_svc_status_listadd',
62 'set_svc_status_listdel' => 'MyAccount/set_svc_status_listdel',
63 'set_svc_status_vacationadd'=> 'MyAccount/set_svc_status_vacationadd',
64 'set_svc_status_vacationdel'=> 'MyAccount/set_svc_status_vacationdel',
65 'acct_forward_info' => 'MyAccount/acct_forward_info',
66 'process_acct_forward' => 'MyAccount/process_acct_forward',
67 'list_dsl_devices' => 'MyAccount/list_dsl_devices',
68 'add_dsl_device' => 'MyAccount/add_dsl_device',
69 'delete_dsl_device' => 'MyAccount/delete_dsl_device',
70 'port_graph' => 'MyAccount/port_graph',
71 'list_cdr_usage' => 'MyAccount/list_cdr_usage',
72 'list_support_usage' => 'MyAccount/list_support_usage',
73 'order_pkg' => 'MyAccount/order_pkg', #add to ss cgi!
74 'change_pkg' => 'MyAccount/change_pkg',
75 'order_recharge' => 'MyAccount/order_recharge',
76 'renew_info' => 'MyAccount/renew_info',
77 'order_renew' => 'MyAccount/order_renew',
78 'cancel_pkg' => 'MyAccount/cancel_pkg', #add to ss cgi!
79 'suspend_pkg' => 'MyAccount/suspend_pkg', #add to ss cgi!
80 'charge' => 'MyAccount/charge', #?
81 'part_svc_info' => 'MyAccount/part_svc_info',
82 'provision_acct' => 'MyAccount/provision_acct',
83 'provision_phone' => 'MyAccount/provision_phone',
84 'provision_external' => 'MyAccount/provision_external',
85 'unprovision_svc' => 'MyAccount/unprovision_svc',
86 'myaccount_passwd' => 'MyAccount/myaccount_passwd',
87 'reset_passwd' => 'MyAccount/reset_passwd',
88 'check_reset_passwd' => 'MyAccount/check_reset_passwd',
89 'process_reset_passwd' => 'MyAccount/process_reset_passwd',
90 'list_tickets' => 'MyAccount/list_tickets',
91 'create_ticket' => 'MyAccount/create_ticket',
92 'get_ticket' => 'MyAccount/get_ticket',
93 'adjust_ticket_priority' => 'MyAccount/adjust_ticket_priority',
94 'did_report' => 'MyAccount/did_report',
95 'signup_info' => 'Signup/signup_info',
96 'skin_info' => 'MyAccount/skin_info',
97 'access_info' => 'MyAccount/access_info',
98 'domain_select_hash' => 'Signup/domain_select_hash', # expose?
99 'new_customer' => 'Signup/new_customer',
100 'new_customer_minimal' => 'Signup/new_customer_minimal',
101 'capture_payment' => 'Signup/capture_payment',
102 #N/A 'clear_signup_cache' => 'Signup/clear_cache',
103 'new_agent' => 'Agent/new_agent',
104 'agent_login' => 'Agent/agent_login',
105 'agent_logout' => 'Agent/agent_logout',
106 'agent_info' => 'Agent/agent_info',
107 'agent_list_customers' => 'Agent/agent_list_customers',
108 'check_username' => 'Agent/check_username',
109 'suspend_username' => 'Agent/suspend_username',
110 'unsuspend_username' => 'Agent/unsuspend_username',
111 'mason_comp' => 'MasonComponent/mason_comp',
112 'call_time' => 'PrepaidPhone/call_time',
113 'call_time_nanpa' => 'PrepaidPhone/call_time_nanpa',
114 'phonenum_balance' => 'PrepaidPhone/phonenum_balance',
116 'start_thirdparty' => 'MyAccount/start_thirdparty',
117 'finish_thirdparty' => 'MyAccount/finish_thirdparty',
121 qw( regionselector regionselector_hashref location_form
122 expselect popselector domainselector didselector
126 $ENV{'PATH'} ='/usr/bin:/usr/ucb:/bin';
127 $ENV{'SHELL'} = '/bin/sh';
128 $ENV{'IFS'} = " \t\n";
131 $ENV{'BASH_ENV'} = '';
133 #you can add BEGIN { $FS::SelfService::skip_uid_check = 1; }
134 #if you grant appropriate permissions to whatever user
135 my $freeside_uid = scalar(getpwnam('freeside'));
136 die "not running as the freeside user\n"
137 if $> != $freeside_uid && ! $skip_uid_check;
139 -e $dir or die "FATAL: $dir doesn't exist!";
140 -d $dir or die "FATAL: $dir isn't a directory!";
141 -r $dir or die "FATAL: Can't read $dir as freeside user!";
142 -x $dir or die "FATAL: $dir not searchable (executable) as freeside user!";
144 foreach my $autoload ( keys %autoload ) {
147 "sub $autoload { ". '
152 #warn scalar(@_). ": ". join(" / ", @_);
156 $param->{_packet} = \''. $autoload{$autoload}. '\';
158 simple_packet($param);
168 warn "sending ". $packet->{_packet}. " to server"
170 socket(SOCK, PF_UNIX, SOCK_STREAM, 0) or die "socket: $!";
171 connect(SOCK, sockaddr_un($socket)) or die "connect to $socket: $!";
172 nstore_fd($packet, \*SOCK) or die "can't send packet: $!";
175 #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
177 #block until there is a message on socket
178 # my $w = new IO::Select;
180 # my @wait = $w->can_read;
182 warn "reading message from server"
185 my $return = fd_retrieve(\*SOCK) or die "error reading result: $!";
186 die $return->{'_error'} if defined $return->{_error} && $return->{_error};
188 warn "returning message to client"
196 FS::SelfService - Freeside self-service API
200 # password and shell account changes
201 use FS::SelfService qw(passwd chfn chsh);
203 # "my account" functionality
204 use FS::SelfService qw( login customer_info invoice cancel payment_info process_payment );
206 #new-style login with an email address and password
207 # can also be used for svc_acct login, set $emailaddress to username@domain
208 my $rv = login ( { 'email' => $emailaddress,
209 'password' => $password,
212 if ( $rv->{'error'} ) {
213 #handle login error...
216 $session_id = $rv->{'session_id'};
219 #classic svc_acct-based login with separate username and password
220 my $rv = login( { 'username' => $username,
222 'password' => $password,
225 if ( $rv->{'error'} ) {
226 #handle login error...
229 $session_id = $rv->{'session_id'};
232 #svc_phone login with phone number and PIN
233 my $rv = login( { 'username' => $phone_number,
234 'domain' => 'svc_phone',
238 if ( $rv->{'error'} ) {
239 #handle login error...
242 $session_id = $rv->{'session_id'};
245 my $customer_info = customer_info( { 'session_id' => $session_id } );
247 #payment_info and process_payment are available in 1.5+ only
248 my $payment_info = payment_info( { 'session_id' => $session_id } );
250 #!!! process_payment example
252 #!!! list_pkgs example
254 #!!! order_pkg example
256 #!!! cancel_pkg example
258 # signup functionality
259 use FS::SelfService qw( signup_info new_customer );
261 my $signup_info = signup_info;
263 $rv = new_customer( {
266 'company' => $company,
267 'address1' => $address1,
268 'address2' => $address2,
272 'country' => $country,
273 'daytime' => $daytime,
277 'payinfo' => $payinfo,
279 'paystart_month' => $paystart_month
280 'paystart_year' => $paystart_year,
281 'payissue' => $payissue,
283 'paydate' => $paydate,
284 'payname' => $payname,
285 'invoicing_list' => $invoicing_list,
286 'referral_custnum' => $referral_custnum,
287 'agentnum' => $agentnum,
288 'pkgpart' => $pkgpart,
290 'username' => $username,
291 '_password' => $password,
295 'phonenum' => $phonenum,
300 my $error = $rv->{'error'};
301 if ( $error eq '_decline' ) {
311 Use this API to implement your own client "self-service" module.
313 If you just want to customize the look of the existing "self-service" module,
316 =head1 PASSWORD, GECOS, SHELL CHANGING FUNCTIONS
322 Changes the password for an existing user in svc_acct. Takes a hash
323 reference with the following keys:
329 Username of the account (required)
333 Domain of the account (required)
337 Old password (required)
341 New password (required)
359 =head1 "MY ACCOUNT" FUNCTIONS
365 Creates a user session. Takes a hash reference as parameter with the
372 Email address (username@domain), instead of username and domain. Required for
373 contact-based self-service login, can also be used for svc_acct-based login.
389 Returns a hash reference with the following keys:
395 Empty on success, or an error message on errors.
399 Session identifier for successful logins
403 =item customer_info HASHREF
405 Returns general customer information.
407 Takes a hash reference as parameter with a single key: B<session_id>
409 Returns a hash reference with the following keys:
423 Array reference of hash references of open inoices. Each hash reference has
424 the following keys: invnum, date, owed
428 An HTML fragment containing shipping and billing addresses.
430 =item The following fields are also returned
432 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
436 =item edit_info HASHREF
438 Takes a hash reference as parameter with any of the following keys:
440 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
442 If a field exists, the customer record is updated with the new value of that
443 field. If a field does not exist, that field is not changed on the customer
446 Returns a hash reference with a single key, B<error>, empty on success, or an
447 error message on errors
449 =item invoice HASHREF
451 Returns an invoice. Takes a hash reference as parameter with two keys:
452 session_id and invnum
454 Returns a hash reference with the following keys:
460 Empty on success, or an error message on errors
472 =item list_invoices HASHREF
474 Returns a list of all customer invoices. Takes a hash references with a single
477 Returns a hash reference with the following keys:
483 Empty on success, or an error message on errors
487 Reference to array of hash references with the following keys:
497 Invoice date, in UNIX epoch time
505 Cancels this customer.
507 Takes a hash reference as parameter with a single key: B<session_id>
509 Returns a hash reference with a single key, B<error>, which is empty on
510 success or an error message on errors.
512 =item payment_info HASHREF
514 Returns information that may be useful in displaying a payment page.
516 Takes a hash reference as parameter with a single key: B<session_id>.
518 Returns a hash reference with the following keys:
524 Empty on success, or an error message on errors
532 Exact name on credit card (CARD/DCRD)
556 Customer's current default payment type.
560 For CARD/DCRD payment types, the card type (Visa card, MasterCard, Discover card, American Express card, etc.)
564 For CARD/DCRD payment types, the card number
568 For CARD/DCRD payment types, expiration month
572 For CARD/DCRD payment types, expiration year
574 =item cust_main_county
576 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.
580 Array reference of all states in the current default country.
584 Hash reference of card types; keys are card types, values are the exact strings
585 passed to the process_payment function
589 #this doesn't actually work yet
593 #Unique transaction identifier (prevents multiple charges), passed to the
594 #process_payment function
598 =item process_payment HASHREF
600 Processes a payment and possible change of address or payment type. Takes a
601 hash reference as parameter with the following keys:
615 If true, address and card information entered will be saved for subsequent
620 If true, future credit card payments will be done automatically (sets payby to
621 CARD). If false, future credit card payments will be done on-demand (sets
622 payby to DCRD). This option only has meaning if B<save> is set true.
650 Two-letter country code
658 Card expiration month
666 #this doesn't actually work yet
670 #Unique transaction identifier, returned from the payment_info function.
671 #Prevents multiple charges.
675 Returns a hash reference with a single key, B<error>, empty on success, or an
676 error message on errors.
678 =item process_payment_order_pkg
680 Combines the B<process_payment> and B<order_pkg> functions in one step. If the
681 payment processes sucessfully, the package is ordered. Takes a hash reference
682 as parameter with the keys of both methods.
684 Returns a hash reference with a single key, B<error>, empty on success, or an
685 error message on errors.
687 =item process_payment_change_pkg
689 Combines the B<process_payment> and B<change_pkg> functions in one step. If the
690 payment processes sucessfully, the package is ordered. Takes a hash reference
691 as parameter with the keys of both methods.
693 Returns a hash reference with a single key, B<error>, empty on success, or an
694 error message on errors.
697 =item process_payment_order_renew
699 Combines the B<process_payment> and B<order_renew> functions in one step. If
700 the payment processes sucessfully, the renewal is processed. Takes a hash
701 reference as parameter with the keys of both methods.
703 Returns a hash reference with a single key, B<error>, empty on success, or an
704 error message on errors.
708 Returns package information for this customer. For more detail on services,
711 Takes a hash reference as parameter with a single key: B<session_id>
713 Returns a hash reference containing customer package information. The hash reference contains the following keys:
723 Empty on success, or an error message on errors.
725 =item cust_pkg HASHREF
727 Array reference of hash references, each of which has the fields of a cust_pkg
728 record (see L<FS::cust_pkg>) as well as the fields below. Note these are not
729 the internal FS:: objects, but hash references of columns and values.
733 =item part_pkg fields
735 All fields of part_pkg for this specific cust_pkg (be careful with this
736 information - it may reveal more about your available packages than you would
737 like users to know in aggregate)
741 #XXX pare part_pkg fields down to a more secure subset
745 An array of hash references indicating information on unprovisioned services
746 available for provisioning for this specific cust_pkg. Each has the following
751 =item part_svc fields
753 All fields of part_svc (be careful with this information - it may reveal more
754 about your available packages than you would like users to know in aggregate)
758 #XXX pare part_svc fields down to a more secure subset
764 An array of hash references indicating information on the customer services
765 already provisioned for this specific cust_pkg. Each has the following keys:
771 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.
777 Primary key for this service
781 Service definition (see L<FS::part_svc>)
785 Customer package (see L<FS::cust_pkg>)
789 Blank if the service is not over limit, or the date the service exceeded its usage limit (as a UNIX timestamp).
797 Returns service information for this customer.
799 Takes a hash reference as parameter with a single key: B<session_id>
801 Returns a hash reference containing customer package information. The hash reference contains the following keys:
811 An array of hash references indicating information on all of this customer's
812 services. Each has the following keys:
818 Primary key for this service
826 Meaningful user-specific identifier for the service (i.e. username, domain, or
831 Account (svc_acct) services also have the following keys:
849 Upload bytes remaining
853 Download bytes remaining
857 Total bytes remaining
859 =item recharge_amount
863 =item recharge_seconds
865 Number of seconds gained by recharge
867 =item recharge_upbytes
869 Number of upload bytes gained by recharge
871 =item recharge_downbytes
873 Number of download bytes gained by recharge
875 =item recharge_totalbytes
877 Number of total bytes gained by recharge
885 Orders a package for this customer.
887 Takes a hash reference as parameter with the following keys:
897 Package to order (see L<FS::part_pkg>).
901 Quantity for this package order (default 1).
905 Optional locationnum for this package order, for existing locations.
907 Or, for new locations, pass the following fields: address1*, address2, city*,
908 county, state*, zip*, country. (* = required in this case)
920 Service to order (see L<FS::part_svc>).
922 Normally optional; required only to provision a non-svc_acct service, or if the
923 package definition does not contain one svc_acct service definition with
924 quantity 1 (it may contain others with quantity >1). A svcpart of "none" can
925 also be specified to indicate that no initial service should be provisioned.
929 Fields used when provisioning an svc_acct service:
943 Optional security phrase
947 Optional Access number number
951 Fields used when provisioning an svc_domain service:
961 Fields used when provisioning an svc_phone service:
979 Fields used when provisioning an svc_external service:
993 Fields used when provisioning an svc_pbx service:
1007 Returns a hash reference with a single key, B<error>, empty on success, or an
1008 error message on errors. The special error '_decline' is returned for
1009 declined transactions.
1013 Changes a package for this customer.
1015 Takes a hash reference as parameter with the following keys:
1025 Existing customer package.
1029 New package to order (see L<FS::part_pkg>).
1033 Quantity for this package order (default 1).
1037 Returns a hash reference with the following keys:
1043 Empty on success, or an error message on errors.
1047 On success, the new pkgnum
1054 Provides useful info for early renewals.
1056 Takes a hash reference as parameter with the following keys:
1066 Returns a hash reference. On errors, it contains a single key, B<error>, with
1067 the error message. Otherwise, contains a single key, B<dates>, pointing to
1068 an array refernce of hash references. Each hash reference contains the
1075 (Future) Bill date. Indicates a future date for which billing could be run.
1076 Specified as a integer UNIX timestamp. Pass this value to the B<order_renew>
1079 =item bill_date_pretty
1081 (Future) Bill date as a human-readable string. (Convenience for display;
1082 subject to change, so best not to parse for the date.)
1086 Base amount which will be charged if renewed early as of this date.
1090 Renewal date; i.e. even-futher future date at which the customer will be paid
1091 through if the early renewal is completed with the given B<bill-date>.
1092 Specified as a integer UNIX timestamp.
1094 =item renew_date_pretty
1096 Renewal date as a human-readable string. (Convenience for display;
1097 subject to change, so best not to parse for the date.)
1101 Package that will be renewed.
1105 Expiration date of the package that will be renewed.
1107 =item expire_date_pretty
1109 Expiration date of the package that will be renewed, as a human-readable
1110 string. (Convenience for display; subject to change, so best not to parse for
1117 Renews this customer early; i.e. runs billing for this customer in advance.
1119 Takes a hash reference as parameter with the following keys:
1129 Integer date as returned by the B<renew_info> function, indicating the advance
1130 date for which to run billing.
1134 Returns a hash reference with a single key, B<error>, empty on success, or an
1135 error message on errors.
1139 Cancels a package for this customer.
1141 Takes a hash reference as parameter with the following keys:
1151 pkgpart of package to cancel
1155 Returns a hash reference with a single key, B<error>, empty on success, or an
1156 error message on errors.
1160 =head1 SIGNUP FUNCTIONS
1164 =item signup_info HASHREF
1166 Takes a hash reference as parameter with the following keys:
1170 =item session_id - Optional agent/reseller interface session
1174 Returns a hash reference containing information that may be useful in
1175 displaying a signup page. The hash reference contains the following keys:
1179 =item cust_main_county
1181 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.
1185 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
1186 an agentnum specified explicitly via reseller interface session_id in the
1191 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.
1193 =item agentnum2part_pkg
1195 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.
1199 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.
1201 =item security_phrase
1203 True if the "security_phrase" feature is enabled
1207 Array reference of acceptable payment types for signup
1213 credit card - automatic
1217 credit card - on-demand - version 1.5+ only
1221 electronic check - automatic
1225 electronic check - on-demand - version 1.5+ only
1233 billing, not recommended for signups
1237 free, definitely not recommended for signups
1241 special billing type: applies a credit (see FS::prepay_credit) and sets billing type to BILL
1247 True if CVV features are available (1.5+ or 1.4.2 with CVV schema patch)
1251 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".
1257 =item countrydefault
1263 =item new_customer HASHREF
1265 Creates a new customer. Takes a hash reference as parameter with the
1272 first name (required)
1276 last name (required)
1280 (not typically collected; mostly used for ACH transactions)
1286 =item address1 (required)
1294 =item city (required)
1302 =item state (required)
1306 =item zip (required)
1312 Daytime phone number
1316 Evening phone number
1324 CARD, DCRD, CHEK, DCHK, LECB, BILL, COMP or PREPAY (see L</signup_info> (required)
1328 Card number for CARD/DCRD, account_number@aba_number for CHEK/DCHK, prepaid "pin" for PREPAY, purchase order number for BILL
1332 Credit card CVV2 number (1.5+ or 1.4.2 with CVV schema patch)
1336 Expiration date for CARD/DCRD
1340 Exact name on credit card for CARD/DCRD, bank name for CHEK/DCHK
1342 =item invoicing_list
1344 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),
1346 =item referral_custnum
1348 referring customer number
1356 pkgpart of initial package
1372 Access number (index, not the literal number)
1376 Country code (to be provisioned as a service)
1380 Phone number (to be provisioned as a service)
1388 Returns a hash reference with the following keys:
1394 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)
1398 =item regionselector HASHREF | LIST
1400 Takes as input a hashref or list of key/value pairs with the following keys:
1404 =item selected_county
1406 Currently selected county
1408 =item selected_state
1410 Currently selected state
1412 =item selected_country
1414 Currently selected country
1418 Specify a unique prefix string if you intend to use the HTML output multiple time son one page.
1422 Specify a javascript subroutine to call on changes
1428 =item default_country
1434 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>.
1438 Returns a list consisting of three HTML fragments for county selection,
1439 state selection and country selection, respectively.
1443 #false laziness w/FS::cust_main_county (this is currently the "newest" version)
1444 sub regionselector {
1451 $param->{'selected_country'} ||= $param->{'default_country'};
1452 $param->{'selected_state'} ||= $param->{'default_state'};
1454 my $prefix = exists($param->{'prefix'}) ? $param->{'prefix'} : '';
1458 my %cust_main_county;
1460 # unless ( @cust_main_county ) { #cache
1461 #@cust_main_county = qsearch('cust_main_county', {} );
1462 #foreach my $c ( @cust_main_county ) {
1463 foreach my $c ( @{ $param->{'locales'} } ) {
1464 #$countyflag=1 if $c->county;
1465 $countyflag=1 if $c->{county};
1466 #push @{$cust_main_county{$c->country}{$c->state}}, $c->county;
1467 #$cust_main_county{$c->country}{$c->state}{$c->county} = 1;
1468 $cust_main_county{$c->{country}}{$c->{state}}{$c->{county}} = 1;
1471 $countyflag=1 if $param->{selected_county};
1473 my $script_html = <<END;
1475 function opt(what,value,text) {
1476 var optionName = new Option(text, value, false, false);
1477 var length = what.length;
1478 what.options[length] = optionName;
1480 function ${prefix}country_changed(what) {
1481 country = what.options[what.selectedIndex].text;
1482 for ( var i = what.form.${prefix}state.length; i >= 0; i-- )
1483 what.form.${prefix}state.options[i] = null;
1485 #what.form.${prefix}state.options[0] = new Option('', '', false, true);
1487 foreach my $country ( sort keys %cust_main_county ) {
1488 $script_html .= "\nif ( country == \"$country\" ) {\n";
1489 foreach my $state ( sort keys %{$cust_main_county{$country}} ) {
1490 my $text = $state || '(n/a)';
1491 $script_html .= qq!opt(what.form.${prefix}state, "$state", "$text");\n!;
1493 $script_html .= "}\n";
1496 $script_html .= <<END;
1498 function ${prefix}state_changed(what) {
1501 if ( $countyflag ) {
1502 $script_html .= <<END;
1503 state = what.options[what.selectedIndex].text;
1504 country = what.form.${prefix}country.options[what.form.${prefix}country.selectedIndex].text;
1505 for ( var i = what.form.${prefix}county.length; i >= 0; i-- )
1506 what.form.${prefix}county.options[i] = null;
1509 foreach my $country ( sort keys %cust_main_county ) {
1510 $script_html .= "\nif ( country == \"$country\" ) {\n";
1511 foreach my $state ( sort keys %{$cust_main_county{$country}} ) {
1512 $script_html .= "\nif ( state == \"$state\" ) {\n";
1513 #foreach my $county ( sort @{$cust_main_county{$country}{$state}} ) {
1514 foreach my $county ( sort keys %{$cust_main_county{$country}{$state}} ) {
1515 my $text = $county || '(n/a)';
1517 qq!opt(what.form.${prefix}county, "$county", "$text");\n!;
1519 $script_html .= "}\n";
1521 $script_html .= "}\n";
1525 $script_html .= <<END;
1530 my $county_html = $script_html;
1531 if ( $countyflag ) {
1532 $county_html .= qq!<SELECT NAME="${prefix}county" onChange="$param->{'onchange'}">!;
1533 foreach my $county (
1534 sort keys %{ $cust_main_county{$param->{'selected_country'}}{$param->{'selected_state'}} }
1536 my $text = $county || '(n/a)';
1537 $county_html .= qq!<OPTION VALUE="$county"!.
1538 ($county eq $param->{'selected_county'} ?
1545 $county_html .= '</SELECT>';
1548 qq!<INPUT TYPE="hidden" NAME="${prefix}county" VALUE="$param->{'selected_county'}">!;
1551 my $state_html = qq!<SELECT NAME="${prefix}state" !.
1552 qq!onChange="${prefix}state_changed(this); $param->{'onchange'}">!;
1553 foreach my $state ( sort keys %{ $cust_main_county{$param->{'selected_country'}} } ) {
1554 my $text = $state || '(n/a)';
1555 my $selected = $state eq $param->{'selected_state'} ? 'SELECTED' : '';
1556 $state_html .= "\n<OPTION $selected VALUE=\"$state\">$text</OPTION>"
1558 $state_html .= '</SELECT>';
1560 my $country_html = '';
1561 if ( scalar( keys %cust_main_county ) > 1 ) {
1563 $country_html = qq(<SELECT NAME="${prefix}country" ).
1564 qq(onChange="${prefix}country_changed(this); ).
1565 $param->{'onchange'}.
1568 my $countrydefault = $param->{default_country} || 'US';
1569 foreach my $country (
1570 sort { ($b eq $countrydefault) <=> ($a eq $countrydefault) or $a cmp $b }
1571 keys %cust_main_county
1573 my $selected = $country eq $param->{'selected_country'}
1576 $country_html .= "\n<OPTION $selected>$country</OPTION>"
1578 $country_html .= '</SELECT>';
1581 $country_html = qq(<INPUT TYPE="hidden" NAME="${prefix}country" ).
1582 ' VALUE="'. (keys %cust_main_county )[0]. '">';
1586 ($county_html, $state_html, $country_html);
1590 sub regionselector_hashref {
1591 my ($county_html, $state_html, $country_html) = regionselector(@_);
1593 'county_html' => $county_html,
1594 'state_html' => $state_html,
1595 'country_html' => $country_html,
1599 =item location_form HASHREF | LIST
1601 Takes as input a hashref or list of key/value pairs with the following keys:
1607 Current customer session_id
1611 Omit red asterisks from required fields.
1613 =item address1_label
1615 Label for first address line.
1619 Returns an HTML fragment for a location form (address, city, state, zip,
1632 my $session_id = delete $param->{'session_id'};
1634 my $rv = mason_comp( 'session_id' => $session_id,
1635 'comp' => '/elements/location.html',
1636 'args' => [ %$param ],
1640 $rv->{'error'} || $rv->{'output'};
1645 #=item expselect HASHREF | LIST
1647 #Takes as input a hashref or list of key/value pairs with the following keys:
1651 #=item prefix - Specify a unique prefix string if you intend to use the HTML output multiple time son one page.
1653 #=item date - current date, in yyyy-mm-dd or m-d-yyyy format
1657 =item expselect PREFIX [ DATE ]
1659 Takes as input a unique prefix string and the current expiration date, in
1660 yyyy-mm-dd or m-d-yyyy format
1662 Returns an HTML fragments for expiration date selection.
1668 #if ( ref($_[0]) ) {
1672 #my $prefix = $param->{'prefix'};
1673 #my $prefix = exists($param->{'prefix'}) ? $param->{'prefix'} : '';
1674 #my $date = exists($param->{'date'}) ? $param->{'date'} : '';
1676 my $date = scalar(@_) ? shift : '';
1678 my( $m, $y ) = ( 0, 0 );
1679 if ( $date =~ /^(\d{4})-(\d{2})-\d{2}$/ ) { #PostgreSQL date format
1680 ( $m, $y ) = ( $2, $1 );
1681 } elsif ( $date =~ /^(\d{1,2})-(\d{1,2}-)?(\d{4}$)/ ) {
1682 ( $m, $y ) = ( $1, $3 );
1684 my $return = qq!<SELECT NAME="$prefix!. qq!_month" SIZE="1">!;
1686 $return .= qq!<OPTION VALUE="$_"!;
1687 $return .= " SELECTED" if $_ == $m;
1690 $return .= qq!</SELECT>/<SELECT NAME="$prefix!. qq!_year" SIZE="1">!;
1692 my $thisYear = $t[5] + 1900;
1693 for ( ($thisYear > $y && $y > 0 ? $y : $thisYear) .. ($thisYear+10) ) {
1694 $return .= qq!<OPTION VALUE="$_"!;
1695 $return .= " SELECTED" if $_ == $y;
1698 $return .= "</SELECT>";
1703 =item popselector HASHREF | LIST
1705 Takes as input a hashref or list of key/value pairs with the following keys:
1711 Access number number
1715 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>.
1719 Returns an HTML fragment for access number selection.
1723 #horrible false laziness with FS/FS/svc_acct_pop.pm::popselector
1731 my $popnum = $param->{'popnum'};
1732 my $pops = $param->{'pops'};
1734 return '<INPUT TYPE="hidden" NAME="popnum" VALUE="">' unless @$pops;
1735 return $pops->[0]{city}. ', '. $pops->[0]{state}.
1736 ' ('. $pops->[0]{ac}. ')/'. $pops->[0]{exch}. '-'. $pops->[0]{loc}.
1737 '<INPUT TYPE="hidden" NAME="popnum" VALUE="'. $pops->[0]{popnum}. '">'
1738 if scalar(@$pops) == 1;
1741 my %popnum2pop = ();
1743 push @{ $pop{ $_->{state} }->{ $_->{ac} } }, $_;
1744 $popnum2pop{$_->{popnum}} = $_;
1749 function opt(what,href,text) {
1750 var optionName = new Option(text, href, false, false)
1751 var length = what.length;
1752 what.options[length] = optionName;
1756 my $init_popstate = $param->{'init_popstate'};
1757 if ( $init_popstate ) {
1758 $text .= '<INPUT TYPE="hidden" NAME="init_popstate" VALUE="'.
1759 $init_popstate. '">';
1762 function acstate_changed(what) {
1763 state = what.options[what.selectedIndex].text;
1764 what.form.popac.options.length = 0
1765 what.form.popac.options[0] = new Option("Area code", "-1", false, true);
1769 my @states = $init_popstate ? ( $init_popstate ) : keys %pop;
1770 foreach my $state ( sort { $a cmp $b } @states ) {
1771 $text .= "\nif ( state == \"$state\" ) {\n" unless $init_popstate;
1773 foreach my $ac ( sort { $a cmp $b } keys %{ $pop{$state} }) {
1774 $text .= "opt(what.form.popac, \"$ac\", \"$ac\");\n";
1775 if ($ac eq $param->{'popac'}) {
1776 $text .= "what.form.popac.options[what.form.popac.length-1].selected = true;\n";
1779 $text .= "}\n" unless $init_popstate;
1781 $text .= "popac_changed(what.form.popac)}\n";
1784 function popac_changed(what) {
1785 ac = what.options[what.selectedIndex].text;
1786 what.form.popnum.options.length = 0;
1787 what.form.popnum.options[0] = new Option("City", "-1", false, true);
1791 foreach my $state ( @states ) {
1792 foreach my $popac ( keys %{ $pop{$state} } ) {
1793 $text .= "\nif ( ac == \"$popac\" ) {\n";
1795 foreach my $pop ( @{$pop{$state}->{$popac}}) {
1796 my $o_popnum = $pop->{popnum};
1797 my $poptext = $pop->{city}. ', '. $pop->{state}.
1798 ' ('. $pop->{ac}. ')/'. $pop->{exch}. '-'. $pop->{loc};
1800 $text .= "opt(what.form.popnum, \"$o_popnum\", \"$poptext\");\n";
1801 if ($popnum == $o_popnum) {
1802 $text .= "what.form.popnum.options[what.form.popnum.length-1].selected = true;\n";
1810 $text .= "}\n</SCRIPT>\n";
1812 $param->{'acstate'} = '' unless defined($param->{'acstate'});
1815 qq!<TABLE CELLPADDING="0"><TR><TD><SELECT NAME="acstate"! .
1816 qq!SIZE=1 onChange="acstate_changed(this)"><OPTION VALUE=-1>State!;
1817 $text .= "<OPTION" . ($_ eq $param->{'acstate'} ? " SELECTED" : "") .
1818 ">$_" foreach sort { $a cmp $b } @states;
1819 $text .= '</SELECT>'; #callback? return 3 html pieces? #'</TD>';
1822 qq!<SELECT NAME="popac" SIZE=1 onChange="popac_changed(this)">!.
1823 qq!<OPTION>Area code</SELECT></TR><TR VALIGN="top">!;
1825 $text .= qq!<TR><TD><SELECT NAME="popnum" SIZE=1 STYLE="width: 20em"><OPTION>City!;
1828 #comment this block to disable initial list polulation
1829 my @initial_select = ();
1830 if ( scalar( @$pops ) > 100 ) {
1831 push @initial_select, $popnum2pop{$popnum} if $popnum2pop{$popnum};
1833 @initial_select = @$pops;
1835 foreach my $pop ( sort { $a->{state} cmp $b->{state} } @initial_select ) {
1836 $text .= qq!<OPTION VALUE="!. $pop->{popnum}. '"'.
1837 ( ( $popnum && $pop->{popnum} == $popnum ) ? ' SELECTED' : '' ). ">".
1838 $pop->{city}. ', '. $pop->{state}.
1839 ' ('. $pop->{ac}. ')/'. $pop->{exch}. '-'. $pop->{loc};
1842 $text .= qq!</SELECT></TD></TR></TABLE>!;
1848 =item domainselector HASHREF | LIST
1850 Takes as input a hashref or list of key/value pairs with the following keys:
1860 Service number of the selected item.
1864 Returns an HTML fragment for domain selection.
1868 sub domainselector {
1875 my $domsvc= $param->{'domsvc'};
1877 domain_select_hash(map {$_ => $param->{$_}} qw(pkgnum svcpart pkgpart) );
1878 my $domains = $rv->{'domains'};
1879 $domsvc = $rv->{'domsvc'} unless $domsvc;
1881 return '<INPUT TYPE="hidden" NAME="domsvc" VALUE="">'
1882 unless scalar(keys %$domains);
1884 if (scalar(keys %$domains) == 1) {
1886 foreach(keys %$domains) {
1889 return '<TR><TD ALIGN="right">Domain</TD><TD>'. $domains->{$key}.
1890 '<INPUT TYPE="hidden" NAME="domsvc" VALUE="'. $key. '"></TD></TR>'
1893 my $text .= qq!<TR><TD ALIGN="right">Domain</TD><TD><SELECT NAME="domsvc" SIZE=1 STYLE="width: 20em">!;
1895 $text .= '<OPTION>(Choose Domain)' unless $domsvc;
1897 foreach my $domain ( sort { $domains->{$a} cmp $domains->{$b} } keys %$domains ) {
1898 $text .= qq!<OPTION VALUE="!. $domain. '"'.
1899 ( ( $domsvc && $domain == $domsvc ) ? ' SELECTED' : '' ). ">".
1900 $domains->{$domain};
1903 $text .= qq!</SELECT></TD></TR>!;
1909 =item didselector HASHREF | LIST
1911 Takes as input a hashref or list of key/value pairs with the following keys:
1917 Field name for the returned HTML fragment.
1921 Service definition (see L<FS::part_svc>)
1925 Returns an HTML fragment for DID selection.
1937 my $rv = mason_comp( 'comp'=>'/elements/select-did.html',
1938 'args'=>[ %$param ],
1942 $rv->{'error'} || $rv->{'output'};
1948 =head1 RESELLER FUNCTIONS
1950 Note: Resellers can also use the B<signup_info> and B<new_customer> functions
1951 with their active session, and the B<customer_info> and B<order_pkg> functions
1952 with their active session and an additional I<custnum> parameter.
1954 For the most part, development of the reseller web interface has been
1955 superceded by agent-virtualized access to the backend.
1967 =item agent_list_customers
1969 List agent's customers.
1977 L<freeside-selfservice-clientd>, L<freeside-selfservice-server>