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 new_customer_minimal );
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.
1158 =item provision_acct
1160 Provisions an account (svc_acct).
1162 Takes a hash references as parameter with the following keys:
1172 pkgnum of package into which this service is provisioned
1176 svcpart or service definition to provision
1186 =item provision_phone
1188 Provisions a phone number (svc_phone).
1190 Takes a hash references as parameter with the following keys:
1200 pkgnum of package into which this service is provisioned
1204 svcpart or service definition to provision
1224 E911 Address (optional)
1228 =item provision_external
1230 Provisions an external service (svc_external).
1232 Takes a hash references as parameter with the following keys:
1242 pkgnum of package into which this service is provisioned
1246 svcpart or service definition to provision
1256 =head1 SIGNUP FUNCTIONS
1260 =item signup_info HASHREF
1262 Takes a hash reference as parameter with the following keys:
1266 =item session_id - Optional agent/reseller interface session
1270 Returns a hash reference containing information that may be useful in
1271 displaying a signup page. The hash reference contains the following keys:
1275 =item cust_main_county
1277 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.
1281 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
1282 an agentnum specified explicitly via reseller interface session_id in the
1287 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.
1289 =item agentnum2part_pkg
1291 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.
1295 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.
1297 =item security_phrase
1299 True if the "security_phrase" feature is enabled
1303 Array reference of acceptable payment types for signup
1309 credit card - automatic
1313 credit card - on-demand - version 1.5+ only
1317 electronic check - automatic
1321 electronic check - on-demand - version 1.5+ only
1329 billing, not recommended for signups
1333 free, definitely not recommended for signups
1337 special billing type: applies a credit (see FS::prepay_credit) and sets billing type to BILL
1343 True if CVV features are available (1.5+ or 1.4.2 with CVV schema patch)
1347 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".
1353 =item countrydefault
1359 =item new_customer_minimal HASHREF
1361 Creates a new customer.
1363 Current differences from new_customer: An address is not required. promo_code
1364 and reg_code are not supported. If invoicing_list and _password is passed, a
1365 contact will be created with self-service access (no pkgpart or username is
1366 necessary). No initial billing is run (this may change in a future version).
1368 Takes a hash reference as parameter with the following keys:
1374 first name (required)
1378 last name (required)
1382 (not typically collected; mostly used for ACH transactions)
1414 Daytime phone number
1418 Evening phone number
1426 CARD, DCRD, CHEK, DCHK, LECB, BILL, COMP or PREPAY (see L</signup_info> (required)
1430 Card number for CARD/DCRD, account_number@aba_number for CHEK/DCHK, prepaid "pin" for PREPAY, purchase order number for BILL
1434 Credit card CVV2 number (1.5+ or 1.4.2 with CVV schema patch)
1438 Expiration date for CARD/DCRD
1442 Exact name on credit card for CARD/DCRD, bank name for CHEK/DCHK
1444 =item invoicing_list
1446 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),
1448 =item referral_custnum
1450 referring customer number
1458 pkgpart of initial package
1474 Access number (index, not the literal number)
1478 Country code (to be provisioned as a service)
1482 Phone number (to be provisioned as a service)
1490 Returns a hash reference with the following keys:
1496 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)
1500 =item new_customer HASHREF
1502 Creates a new customer. Takes a hash reference as parameter with the
1509 first name (required)
1513 last name (required)
1517 (not typically collected; mostly used for ACH transactions)
1523 =item address1 (required)
1531 =item city (required)
1539 =item state (required)
1543 =item zip (required)
1549 Daytime phone number
1553 Evening phone number
1561 CARD, DCRD, CHEK, DCHK, LECB, BILL, COMP or PREPAY (see L</signup_info> (required)
1565 Card number for CARD/DCRD, account_number@aba_number for CHEK/DCHK, prepaid "pin" for PREPAY, purchase order number for BILL
1569 Credit card CVV2 number (1.5+ or 1.4.2 with CVV schema patch)
1573 Expiration date for CARD/DCRD
1577 Exact name on credit card for CARD/DCRD, bank name for CHEK/DCHK
1579 =item invoicing_list
1581 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),
1583 =item referral_custnum
1585 referring customer number
1593 pkgpart of initial package
1609 Access number (index, not the literal number)
1613 Country code (to be provisioned as a service)
1617 Phone number (to be provisioned as a service)
1625 Returns a hash reference with the following keys:
1631 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)
1635 =item regionselector HASHREF | LIST
1637 Takes as input a hashref or list of key/value pairs with the following keys:
1641 =item selected_county
1643 Currently selected county
1645 =item selected_state
1647 Currently selected state
1649 =item selected_country
1651 Currently selected country
1655 Specify a unique prefix string if you intend to use the HTML output multiple time son one page.
1659 Specify a javascript subroutine to call on changes
1665 =item default_country
1671 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>.
1675 Returns a list consisting of three HTML fragments for county selection,
1676 state selection and country selection, respectively.
1680 #false laziness w/FS::cust_main_county (this is currently the "newest" version)
1681 sub regionselector {
1688 $param->{'selected_country'} ||= $param->{'default_country'};
1689 $param->{'selected_state'} ||= $param->{'default_state'};
1691 my $prefix = exists($param->{'prefix'}) ? $param->{'prefix'} : '';
1695 my %cust_main_county;
1697 # unless ( @cust_main_county ) { #cache
1698 #@cust_main_county = qsearch('cust_main_county', {} );
1699 #foreach my $c ( @cust_main_county ) {
1700 foreach my $c ( @{ $param->{'locales'} } ) {
1701 #$countyflag=1 if $c->county;
1702 $countyflag=1 if $c->{county};
1703 #push @{$cust_main_county{$c->country}{$c->state}}, $c->county;
1704 #$cust_main_county{$c->country}{$c->state}{$c->county} = 1;
1705 $cust_main_county{$c->{country}}{$c->{state}}{$c->{county}} = 1;
1708 $countyflag=1 if $param->{selected_county};
1710 my $script_html = <<END;
1712 function opt(what,value,text) {
1713 var optionName = new Option(text, value, false, false);
1714 var length = what.length;
1715 what.options[length] = optionName;
1717 function ${prefix}country_changed(what) {
1718 country = what.options[what.selectedIndex].text;
1719 for ( var i = what.form.${prefix}state.length; i >= 0; i-- )
1720 what.form.${prefix}state.options[i] = null;
1722 #what.form.${prefix}state.options[0] = new Option('', '', false, true);
1724 foreach my $country ( sort keys %cust_main_county ) {
1725 $script_html .= "\nif ( country == \"$country\" ) {\n";
1726 foreach my $state ( sort keys %{$cust_main_county{$country}} ) {
1727 my $text = $state || '(n/a)';
1728 $script_html .= qq!opt(what.form.${prefix}state, "$state", "$text");\n!;
1730 $script_html .= "}\n";
1733 $script_html .= <<END;
1735 function ${prefix}state_changed(what) {
1738 if ( $countyflag ) {
1739 $script_html .= <<END;
1740 state = what.options[what.selectedIndex].text;
1741 country = what.form.${prefix}country.options[what.form.${prefix}country.selectedIndex].text;
1742 for ( var i = what.form.${prefix}county.length; i >= 0; i-- )
1743 what.form.${prefix}county.options[i] = null;
1746 foreach my $country ( sort keys %cust_main_county ) {
1747 $script_html .= "\nif ( country == \"$country\" ) {\n";
1748 foreach my $state ( sort keys %{$cust_main_county{$country}} ) {
1749 $script_html .= "\nif ( state == \"$state\" ) {\n";
1750 #foreach my $county ( sort @{$cust_main_county{$country}{$state}} ) {
1751 foreach my $county ( sort keys %{$cust_main_county{$country}{$state}} ) {
1752 my $text = $county || '(n/a)';
1754 qq!opt(what.form.${prefix}county, "$county", "$text");\n!;
1756 $script_html .= "}\n";
1758 $script_html .= "}\n";
1762 $script_html .= <<END;
1767 my $county_html = $script_html;
1768 if ( $countyflag ) {
1769 $county_html .= qq!<SELECT NAME="${prefix}county" onChange="$param->{'onchange'}">!;
1770 foreach my $county (
1771 sort keys %{ $cust_main_county{$param->{'selected_country'}}{$param->{'selected_state'}} }
1773 my $text = $county || '(n/a)';
1774 $county_html .= qq!<OPTION VALUE="$county"!.
1775 ($county eq $param->{'selected_county'} ?
1782 $county_html .= '</SELECT>';
1785 qq!<INPUT TYPE="hidden" NAME="${prefix}county" VALUE="$param->{'selected_county'}">!;
1788 my $state_html = qq!<SELECT NAME="${prefix}state" !.
1789 qq!onChange="${prefix}state_changed(this); $param->{'onchange'}">!;
1790 foreach my $state ( sort keys %{ $cust_main_county{$param->{'selected_country'}} } ) {
1791 my $text = $state || '(n/a)';
1792 my $selected = $state eq $param->{'selected_state'} ? 'SELECTED' : '';
1793 $state_html .= "\n<OPTION $selected VALUE=\"$state\">$text</OPTION>"
1795 $state_html .= '</SELECT>';
1797 my $country_html = '';
1798 if ( scalar( keys %cust_main_county ) > 1 ) {
1800 $country_html = qq(<SELECT NAME="${prefix}country" ).
1801 qq(onChange="${prefix}country_changed(this); ).
1802 $param->{'onchange'}.
1805 my $countrydefault = $param->{default_country} || 'US';
1806 foreach my $country (
1807 sort { ($b eq $countrydefault) <=> ($a eq $countrydefault) or $a cmp $b }
1808 keys %cust_main_county
1810 my $selected = $country eq $param->{'selected_country'}
1813 $country_html .= "\n<OPTION $selected>$country</OPTION>"
1815 $country_html .= '</SELECT>';
1818 $country_html = qq(<INPUT TYPE="hidden" NAME="${prefix}country" ).
1819 ' VALUE="'. (keys %cust_main_county )[0]. '">';
1823 ($county_html, $state_html, $country_html);
1827 sub regionselector_hashref {
1828 my ($county_html, $state_html, $country_html) = regionselector(@_);
1830 'county_html' => $county_html,
1831 'state_html' => $state_html,
1832 'country_html' => $country_html,
1836 =item location_form HASHREF | LIST
1838 Takes as input a hashref or list of key/value pairs with the following keys:
1844 Current customer session_id
1848 Omit red asterisks from required fields.
1850 =item address1_label
1852 Label for first address line.
1856 Returns an HTML fragment for a location form (address, city, state, zip,
1869 my $session_id = delete $param->{'session_id'};
1871 my $rv = mason_comp( 'session_id' => $session_id,
1872 'comp' => '/elements/location.html',
1873 'args' => [ %$param ],
1877 $rv->{'error'} || $rv->{'output'};
1882 #=item expselect HASHREF | LIST
1884 #Takes as input a hashref or list of key/value pairs with the following keys:
1888 #=item prefix - Specify a unique prefix string if you intend to use the HTML output multiple time son one page.
1890 #=item date - current date, in yyyy-mm-dd or m-d-yyyy format
1894 =item expselect PREFIX [ DATE ]
1896 Takes as input a unique prefix string and the current expiration date, in
1897 yyyy-mm-dd or m-d-yyyy format
1899 Returns an HTML fragments for expiration date selection.
1905 #if ( ref($_[0]) ) {
1909 #my $prefix = $param->{'prefix'};
1910 #my $prefix = exists($param->{'prefix'}) ? $param->{'prefix'} : '';
1911 #my $date = exists($param->{'date'}) ? $param->{'date'} : '';
1913 my $date = scalar(@_) ? shift : '';
1915 my( $m, $y ) = ( 0, 0 );
1916 if ( $date =~ /^(\d{4})-(\d{2})-\d{2}$/ ) { #PostgreSQL date format
1917 ( $m, $y ) = ( $2, $1 );
1918 } elsif ( $date =~ /^(\d{1,2})-(\d{1,2}-)?(\d{4}$)/ ) {
1919 ( $m, $y ) = ( $1, $3 );
1921 my $return = qq!<SELECT NAME="$prefix!. qq!_month" SIZE="1">!;
1923 $return .= qq!<OPTION VALUE="$_"!;
1924 $return .= " SELECTED" if $_ == $m;
1927 $return .= qq!</SELECT>/<SELECT NAME="$prefix!. qq!_year" SIZE="1">!;
1929 my $thisYear = $t[5] + 1900;
1930 for ( ($thisYear > $y && $y > 0 ? $y : $thisYear) .. ($thisYear+10) ) {
1931 $return .= qq!<OPTION VALUE="$_"!;
1932 $return .= " SELECTED" if $_ == $y;
1935 $return .= "</SELECT>";
1940 =item popselector HASHREF | LIST
1942 Takes as input a hashref or list of key/value pairs with the following keys:
1948 Access number number
1952 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>.
1956 Returns an HTML fragment for access number selection.
1960 #horrible false laziness with FS/FS/svc_acct_pop.pm::popselector
1968 my $popnum = $param->{'popnum'};
1969 my $pops = $param->{'pops'};
1971 return '<INPUT TYPE="hidden" NAME="popnum" VALUE="">' unless @$pops;
1972 return $pops->[0]{city}. ', '. $pops->[0]{state}.
1973 ' ('. $pops->[0]{ac}. ')/'. $pops->[0]{exch}. '-'. $pops->[0]{loc}.
1974 '<INPUT TYPE="hidden" NAME="popnum" VALUE="'. $pops->[0]{popnum}. '">'
1975 if scalar(@$pops) == 1;
1978 my %popnum2pop = ();
1980 push @{ $pop{ $_->{state} }->{ $_->{ac} } }, $_;
1981 $popnum2pop{$_->{popnum}} = $_;
1986 function opt(what,href,text) {
1987 var optionName = new Option(text, href, false, false)
1988 var length = what.length;
1989 what.options[length] = optionName;
1993 my $init_popstate = $param->{'init_popstate'};
1994 if ( $init_popstate ) {
1995 $text .= '<INPUT TYPE="hidden" NAME="init_popstate" VALUE="'.
1996 $init_popstate. '">';
1999 function acstate_changed(what) {
2000 state = what.options[what.selectedIndex].text;
2001 what.form.popac.options.length = 0
2002 what.form.popac.options[0] = new Option("Area code", "-1", false, true);
2006 my @states = $init_popstate ? ( $init_popstate ) : keys %pop;
2007 foreach my $state ( sort { $a cmp $b } @states ) {
2008 $text .= "\nif ( state == \"$state\" ) {\n" unless $init_popstate;
2010 foreach my $ac ( sort { $a cmp $b } keys %{ $pop{$state} }) {
2011 $text .= "opt(what.form.popac, \"$ac\", \"$ac\");\n";
2012 if ($ac eq $param->{'popac'}) {
2013 $text .= "what.form.popac.options[what.form.popac.length-1].selected = true;\n";
2016 $text .= "}\n" unless $init_popstate;
2018 $text .= "popac_changed(what.form.popac)}\n";
2021 function popac_changed(what) {
2022 ac = what.options[what.selectedIndex].text;
2023 what.form.popnum.options.length = 0;
2024 what.form.popnum.options[0] = new Option("City", "-1", false, true);
2028 foreach my $state ( @states ) {
2029 foreach my $popac ( keys %{ $pop{$state} } ) {
2030 $text .= "\nif ( ac == \"$popac\" ) {\n";
2032 foreach my $pop ( @{$pop{$state}->{$popac}}) {
2033 my $o_popnum = $pop->{popnum};
2034 my $poptext = $pop->{city}. ', '. $pop->{state}.
2035 ' ('. $pop->{ac}. ')/'. $pop->{exch}. '-'. $pop->{loc};
2037 $text .= "opt(what.form.popnum, \"$o_popnum\", \"$poptext\");\n";
2038 if ($popnum == $o_popnum) {
2039 $text .= "what.form.popnum.options[what.form.popnum.length-1].selected = true;\n";
2047 $text .= "}\n</SCRIPT>\n";
2049 $param->{'acstate'} = '' unless defined($param->{'acstate'});
2052 qq!<TABLE CELLPADDING="0"><TR><TD><SELECT NAME="acstate"! .
2053 qq!SIZE=1 onChange="acstate_changed(this)"><OPTION VALUE=-1>State!;
2054 $text .= "<OPTION" . ($_ eq $param->{'acstate'} ? " SELECTED" : "") .
2055 ">$_" foreach sort { $a cmp $b } @states;
2056 $text .= '</SELECT>'; #callback? return 3 html pieces? #'</TD>';
2059 qq!<SELECT NAME="popac" SIZE=1 onChange="popac_changed(this)">!.
2060 qq!<OPTION>Area code</SELECT></TR><TR VALIGN="top">!;
2062 $text .= qq!<TR><TD><SELECT NAME="popnum" SIZE=1 STYLE="width: 20em"><OPTION>City!;
2065 #comment this block to disable initial list polulation
2066 my @initial_select = ();
2067 if ( scalar( @$pops ) > 100 ) {
2068 push @initial_select, $popnum2pop{$popnum} if $popnum2pop{$popnum};
2070 @initial_select = @$pops;
2072 foreach my $pop ( sort { $a->{state} cmp $b->{state} } @initial_select ) {
2073 $text .= qq!<OPTION VALUE="!. $pop->{popnum}. '"'.
2074 ( ( $popnum && $pop->{popnum} == $popnum ) ? ' SELECTED' : '' ). ">".
2075 $pop->{city}. ', '. $pop->{state}.
2076 ' ('. $pop->{ac}. ')/'. $pop->{exch}. '-'. $pop->{loc};
2079 $text .= qq!</SELECT></TD></TR></TABLE>!;
2085 =item domainselector HASHREF | LIST
2087 Takes as input a hashref or list of key/value pairs with the following keys:
2097 Service number of the selected item.
2101 Returns an HTML fragment for domain selection.
2105 sub domainselector {
2112 my $domsvc= $param->{'domsvc'};
2114 domain_select_hash(map {$_ => $param->{$_}} qw(pkgnum svcpart pkgpart) );
2115 my $domains = $rv->{'domains'};
2116 $domsvc = $rv->{'domsvc'} unless $domsvc;
2118 return '<INPUT TYPE="hidden" NAME="domsvc" VALUE="">'
2119 unless scalar(keys %$domains);
2121 if (scalar(keys %$domains) == 1) {
2123 foreach(keys %$domains) {
2126 return '<TR><TD ALIGN="right">Domain</TD><TD>'. $domains->{$key}.
2127 '<INPUT TYPE="hidden" NAME="domsvc" VALUE="'. $key. '"></TD></TR>'
2130 my $text .= qq!<TR><TD ALIGN="right">Domain</TD><TD><SELECT NAME="domsvc" SIZE=1 STYLE="width: 20em">!;
2132 $text .= '<OPTION>(Choose Domain)' unless $domsvc;
2134 foreach my $domain ( sort { $domains->{$a} cmp $domains->{$b} } keys %$domains ) {
2135 $text .= qq!<OPTION VALUE="!. $domain. '"'.
2136 ( ( $domsvc && $domain == $domsvc ) ? ' SELECTED' : '' ). ">".
2137 $domains->{$domain};
2140 $text .= qq!</SELECT></TD></TR>!;
2146 =item didselector HASHREF | LIST
2148 Takes as input a hashref or list of key/value pairs with the following keys:
2154 Field name for the returned HTML fragment.
2158 Service definition (see L<FS::part_svc>)
2162 Returns an HTML fragment for DID selection.
2174 my $rv = mason_comp( 'comp'=>'/elements/select-did.html',
2175 'args'=>[ %$param ],
2179 $rv->{'error'} || $rv->{'output'};
2185 =head1 RESELLER FUNCTIONS
2187 Note: Resellers can also use the B<signup_info> and B<new_customer> functions
2188 with their active session, and the B<customer_info> and B<order_pkg> functions
2189 with their active session and an additional I<custnum> parameter.
2191 For the most part, development of the reseller web interface has been
2192 superceded by agent-virtualized access to the backend.
2204 =item agent_list_customers
2206 List agent's customers.
2214 L<freeside-selfservice-clientd>, L<freeside-selfservice-server>