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.
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_minimal HASHREF
1265 Creates a new customer.
1267 Current differences from new_customer: An address is not required. promo_code
1268 and reg_code are not supported. If invoicing_list and _password is passed, a
1269 contact will be created with self-service access (no pkgpart or username is
1270 necessary). No initial billing is run (this may change in a future version).
1272 Takes a hash reference as parameter with the following keys:
1278 first name (required)
1282 last name (required)
1286 (not typically collected; mostly used for ACH transactions)
1318 Daytime phone number
1322 Evening phone number
1330 CARD, DCRD, CHEK, DCHK, LECB, BILL, COMP or PREPAY (see L</signup_info> (required)
1334 Card number for CARD/DCRD, account_number@aba_number for CHEK/DCHK, prepaid "pin" for PREPAY, purchase order number for BILL
1338 Credit card CVV2 number (1.5+ or 1.4.2 with CVV schema patch)
1342 Expiration date for CARD/DCRD
1346 Exact name on credit card for CARD/DCRD, bank name for CHEK/DCHK
1348 =item invoicing_list
1350 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),
1352 =item referral_custnum
1354 referring customer number
1362 pkgpart of initial package
1378 Access number (index, not the literal number)
1382 Country code (to be provisioned as a service)
1386 Phone number (to be provisioned as a service)
1394 Returns a hash reference with the following keys:
1400 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)
1404 =item new_customer HASHREF
1406 Creates a new customer. Takes a hash reference as parameter with the
1413 first name (required)
1417 last name (required)
1421 (not typically collected; mostly used for ACH transactions)
1427 =item address1 (required)
1435 =item city (required)
1443 =item state (required)
1447 =item zip (required)
1453 Daytime phone number
1457 Evening phone number
1465 CARD, DCRD, CHEK, DCHK, LECB, BILL, COMP or PREPAY (see L</signup_info> (required)
1469 Card number for CARD/DCRD, account_number@aba_number for CHEK/DCHK, prepaid "pin" for PREPAY, purchase order number for BILL
1473 Credit card CVV2 number (1.5+ or 1.4.2 with CVV schema patch)
1477 Expiration date for CARD/DCRD
1481 Exact name on credit card for CARD/DCRD, bank name for CHEK/DCHK
1483 =item invoicing_list
1485 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),
1487 =item referral_custnum
1489 referring customer number
1497 pkgpart of initial package
1513 Access number (index, not the literal number)
1517 Country code (to be provisioned as a service)
1521 Phone number (to be provisioned as a service)
1529 Returns a hash reference with the following keys:
1535 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)
1539 =item regionselector HASHREF | LIST
1541 Takes as input a hashref or list of key/value pairs with the following keys:
1545 =item selected_county
1547 Currently selected county
1549 =item selected_state
1551 Currently selected state
1553 =item selected_country
1555 Currently selected country
1559 Specify a unique prefix string if you intend to use the HTML output multiple time son one page.
1563 Specify a javascript subroutine to call on changes
1569 =item default_country
1575 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>.
1579 Returns a list consisting of three HTML fragments for county selection,
1580 state selection and country selection, respectively.
1584 #false laziness w/FS::cust_main_county (this is currently the "newest" version)
1585 sub regionselector {
1592 $param->{'selected_country'} ||= $param->{'default_country'};
1593 $param->{'selected_state'} ||= $param->{'default_state'};
1595 my $prefix = exists($param->{'prefix'}) ? $param->{'prefix'} : '';
1599 my %cust_main_county;
1601 # unless ( @cust_main_county ) { #cache
1602 #@cust_main_county = qsearch('cust_main_county', {} );
1603 #foreach my $c ( @cust_main_county ) {
1604 foreach my $c ( @{ $param->{'locales'} } ) {
1605 #$countyflag=1 if $c->county;
1606 $countyflag=1 if $c->{county};
1607 #push @{$cust_main_county{$c->country}{$c->state}}, $c->county;
1608 #$cust_main_county{$c->country}{$c->state}{$c->county} = 1;
1609 $cust_main_county{$c->{country}}{$c->{state}}{$c->{county}} = 1;
1612 $countyflag=1 if $param->{selected_county};
1614 my $script_html = <<END;
1616 function opt(what,value,text) {
1617 var optionName = new Option(text, value, false, false);
1618 var length = what.length;
1619 what.options[length] = optionName;
1621 function ${prefix}country_changed(what) {
1622 country = what.options[what.selectedIndex].text;
1623 for ( var i = what.form.${prefix}state.length; i >= 0; i-- )
1624 what.form.${prefix}state.options[i] = null;
1626 #what.form.${prefix}state.options[0] = new Option('', '', false, true);
1628 foreach my $country ( sort keys %cust_main_county ) {
1629 $script_html .= "\nif ( country == \"$country\" ) {\n";
1630 foreach my $state ( sort keys %{$cust_main_county{$country}} ) {
1631 my $text = $state || '(n/a)';
1632 $script_html .= qq!opt(what.form.${prefix}state, "$state", "$text");\n!;
1634 $script_html .= "}\n";
1637 $script_html .= <<END;
1639 function ${prefix}state_changed(what) {
1642 if ( $countyflag ) {
1643 $script_html .= <<END;
1644 state = what.options[what.selectedIndex].text;
1645 country = what.form.${prefix}country.options[what.form.${prefix}country.selectedIndex].text;
1646 for ( var i = what.form.${prefix}county.length; i >= 0; i-- )
1647 what.form.${prefix}county.options[i] = null;
1650 foreach my $country ( sort keys %cust_main_county ) {
1651 $script_html .= "\nif ( country == \"$country\" ) {\n";
1652 foreach my $state ( sort keys %{$cust_main_county{$country}} ) {
1653 $script_html .= "\nif ( state == \"$state\" ) {\n";
1654 #foreach my $county ( sort @{$cust_main_county{$country}{$state}} ) {
1655 foreach my $county ( sort keys %{$cust_main_county{$country}{$state}} ) {
1656 my $text = $county || '(n/a)';
1658 qq!opt(what.form.${prefix}county, "$county", "$text");\n!;
1660 $script_html .= "}\n";
1662 $script_html .= "}\n";
1666 $script_html .= <<END;
1671 my $county_html = $script_html;
1672 if ( $countyflag ) {
1673 $county_html .= qq!<SELECT NAME="${prefix}county" onChange="$param->{'onchange'}">!;
1674 foreach my $county (
1675 sort keys %{ $cust_main_county{$param->{'selected_country'}}{$param->{'selected_state'}} }
1677 my $text = $county || '(n/a)';
1678 $county_html .= qq!<OPTION VALUE="$county"!.
1679 ($county eq $param->{'selected_county'} ?
1686 $county_html .= '</SELECT>';
1689 qq!<INPUT TYPE="hidden" NAME="${prefix}county" VALUE="$param->{'selected_county'}">!;
1692 my $state_html = qq!<SELECT NAME="${prefix}state" !.
1693 qq!onChange="${prefix}state_changed(this); $param->{'onchange'}">!;
1694 foreach my $state ( sort keys %{ $cust_main_county{$param->{'selected_country'}} } ) {
1695 my $text = $state || '(n/a)';
1696 my $selected = $state eq $param->{'selected_state'} ? 'SELECTED' : '';
1697 $state_html .= "\n<OPTION $selected VALUE=\"$state\">$text</OPTION>"
1699 $state_html .= '</SELECT>';
1701 my $country_html = '';
1702 if ( scalar( keys %cust_main_county ) > 1 ) {
1704 $country_html = qq(<SELECT NAME="${prefix}country" ).
1705 qq(onChange="${prefix}country_changed(this); ).
1706 $param->{'onchange'}.
1709 my $countrydefault = $param->{default_country} || 'US';
1710 foreach my $country (
1711 sort { ($b eq $countrydefault) <=> ($a eq $countrydefault) or $a cmp $b }
1712 keys %cust_main_county
1714 my $selected = $country eq $param->{'selected_country'}
1717 $country_html .= "\n<OPTION $selected>$country</OPTION>"
1719 $country_html .= '</SELECT>';
1722 $country_html = qq(<INPUT TYPE="hidden" NAME="${prefix}country" ).
1723 ' VALUE="'. (keys %cust_main_county )[0]. '">';
1727 ($county_html, $state_html, $country_html);
1731 sub regionselector_hashref {
1732 my ($county_html, $state_html, $country_html) = regionselector(@_);
1734 'county_html' => $county_html,
1735 'state_html' => $state_html,
1736 'country_html' => $country_html,
1740 =item location_form HASHREF | LIST
1742 Takes as input a hashref or list of key/value pairs with the following keys:
1748 Current customer session_id
1752 Omit red asterisks from required fields.
1754 =item address1_label
1756 Label for first address line.
1760 Returns an HTML fragment for a location form (address, city, state, zip,
1773 my $session_id = delete $param->{'session_id'};
1775 my $rv = mason_comp( 'session_id' => $session_id,
1776 'comp' => '/elements/location.html',
1777 'args' => [ %$param ],
1781 $rv->{'error'} || $rv->{'output'};
1786 #=item expselect HASHREF | LIST
1788 #Takes as input a hashref or list of key/value pairs with the following keys:
1792 #=item prefix - Specify a unique prefix string if you intend to use the HTML output multiple time son one page.
1794 #=item date - current date, in yyyy-mm-dd or m-d-yyyy format
1798 =item expselect PREFIX [ DATE ]
1800 Takes as input a unique prefix string and the current expiration date, in
1801 yyyy-mm-dd or m-d-yyyy format
1803 Returns an HTML fragments for expiration date selection.
1809 #if ( ref($_[0]) ) {
1813 #my $prefix = $param->{'prefix'};
1814 #my $prefix = exists($param->{'prefix'}) ? $param->{'prefix'} : '';
1815 #my $date = exists($param->{'date'}) ? $param->{'date'} : '';
1817 my $date = scalar(@_) ? shift : '';
1819 my( $m, $y ) = ( 0, 0 );
1820 if ( $date =~ /^(\d{4})-(\d{2})-\d{2}$/ ) { #PostgreSQL date format
1821 ( $m, $y ) = ( $2, $1 );
1822 } elsif ( $date =~ /^(\d{1,2})-(\d{1,2}-)?(\d{4}$)/ ) {
1823 ( $m, $y ) = ( $1, $3 );
1825 my $return = qq!<SELECT NAME="$prefix!. qq!_month" SIZE="1">!;
1827 $return .= qq!<OPTION VALUE="$_"!;
1828 $return .= " SELECTED" if $_ == $m;
1831 $return .= qq!</SELECT>/<SELECT NAME="$prefix!. qq!_year" SIZE="1">!;
1833 my $thisYear = $t[5] + 1900;
1834 for ( ($thisYear > $y && $y > 0 ? $y : $thisYear) .. ($thisYear+10) ) {
1835 $return .= qq!<OPTION VALUE="$_"!;
1836 $return .= " SELECTED" if $_ == $y;
1839 $return .= "</SELECT>";
1844 =item popselector HASHREF | LIST
1846 Takes as input a hashref or list of key/value pairs with the following keys:
1852 Access number number
1856 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>.
1860 Returns an HTML fragment for access number selection.
1864 #horrible false laziness with FS/FS/svc_acct_pop.pm::popselector
1872 my $popnum = $param->{'popnum'};
1873 my $pops = $param->{'pops'};
1875 return '<INPUT TYPE="hidden" NAME="popnum" VALUE="">' unless @$pops;
1876 return $pops->[0]{city}. ', '. $pops->[0]{state}.
1877 ' ('. $pops->[0]{ac}. ')/'. $pops->[0]{exch}. '-'. $pops->[0]{loc}.
1878 '<INPUT TYPE="hidden" NAME="popnum" VALUE="'. $pops->[0]{popnum}. '">'
1879 if scalar(@$pops) == 1;
1882 my %popnum2pop = ();
1884 push @{ $pop{ $_->{state} }->{ $_->{ac} } }, $_;
1885 $popnum2pop{$_->{popnum}} = $_;
1890 function opt(what,href,text) {
1891 var optionName = new Option(text, href, false, false)
1892 var length = what.length;
1893 what.options[length] = optionName;
1897 my $init_popstate = $param->{'init_popstate'};
1898 if ( $init_popstate ) {
1899 $text .= '<INPUT TYPE="hidden" NAME="init_popstate" VALUE="'.
1900 $init_popstate. '">';
1903 function acstate_changed(what) {
1904 state = what.options[what.selectedIndex].text;
1905 what.form.popac.options.length = 0
1906 what.form.popac.options[0] = new Option("Area code", "-1", false, true);
1910 my @states = $init_popstate ? ( $init_popstate ) : keys %pop;
1911 foreach my $state ( sort { $a cmp $b } @states ) {
1912 $text .= "\nif ( state == \"$state\" ) {\n" unless $init_popstate;
1914 foreach my $ac ( sort { $a cmp $b } keys %{ $pop{$state} }) {
1915 $text .= "opt(what.form.popac, \"$ac\", \"$ac\");\n";
1916 if ($ac eq $param->{'popac'}) {
1917 $text .= "what.form.popac.options[what.form.popac.length-1].selected = true;\n";
1920 $text .= "}\n" unless $init_popstate;
1922 $text .= "popac_changed(what.form.popac)}\n";
1925 function popac_changed(what) {
1926 ac = what.options[what.selectedIndex].text;
1927 what.form.popnum.options.length = 0;
1928 what.form.popnum.options[0] = new Option("City", "-1", false, true);
1932 foreach my $state ( @states ) {
1933 foreach my $popac ( keys %{ $pop{$state} } ) {
1934 $text .= "\nif ( ac == \"$popac\" ) {\n";
1936 foreach my $pop ( @{$pop{$state}->{$popac}}) {
1937 my $o_popnum = $pop->{popnum};
1938 my $poptext = $pop->{city}. ', '. $pop->{state}.
1939 ' ('. $pop->{ac}. ')/'. $pop->{exch}. '-'. $pop->{loc};
1941 $text .= "opt(what.form.popnum, \"$o_popnum\", \"$poptext\");\n";
1942 if ($popnum == $o_popnum) {
1943 $text .= "what.form.popnum.options[what.form.popnum.length-1].selected = true;\n";
1951 $text .= "}\n</SCRIPT>\n";
1953 $param->{'acstate'} = '' unless defined($param->{'acstate'});
1956 qq!<TABLE CELLPADDING="0"><TR><TD><SELECT NAME="acstate"! .
1957 qq!SIZE=1 onChange="acstate_changed(this)"><OPTION VALUE=-1>State!;
1958 $text .= "<OPTION" . ($_ eq $param->{'acstate'} ? " SELECTED" : "") .
1959 ">$_" foreach sort { $a cmp $b } @states;
1960 $text .= '</SELECT>'; #callback? return 3 html pieces? #'</TD>';
1963 qq!<SELECT NAME="popac" SIZE=1 onChange="popac_changed(this)">!.
1964 qq!<OPTION>Area code</SELECT></TR><TR VALIGN="top">!;
1966 $text .= qq!<TR><TD><SELECT NAME="popnum" SIZE=1 STYLE="width: 20em"><OPTION>City!;
1969 #comment this block to disable initial list polulation
1970 my @initial_select = ();
1971 if ( scalar( @$pops ) > 100 ) {
1972 push @initial_select, $popnum2pop{$popnum} if $popnum2pop{$popnum};
1974 @initial_select = @$pops;
1976 foreach my $pop ( sort { $a->{state} cmp $b->{state} } @initial_select ) {
1977 $text .= qq!<OPTION VALUE="!. $pop->{popnum}. '"'.
1978 ( ( $popnum && $pop->{popnum} == $popnum ) ? ' SELECTED' : '' ). ">".
1979 $pop->{city}. ', '. $pop->{state}.
1980 ' ('. $pop->{ac}. ')/'. $pop->{exch}. '-'. $pop->{loc};
1983 $text .= qq!</SELECT></TD></TR></TABLE>!;
1989 =item domainselector HASHREF | LIST
1991 Takes as input a hashref or list of key/value pairs with the following keys:
2001 Service number of the selected item.
2005 Returns an HTML fragment for domain selection.
2009 sub domainselector {
2016 my $domsvc= $param->{'domsvc'};
2018 domain_select_hash(map {$_ => $param->{$_}} qw(pkgnum svcpart pkgpart) );
2019 my $domains = $rv->{'domains'};
2020 $domsvc = $rv->{'domsvc'} unless $domsvc;
2022 return '<INPUT TYPE="hidden" NAME="domsvc" VALUE="">'
2023 unless scalar(keys %$domains);
2025 if (scalar(keys %$domains) == 1) {
2027 foreach(keys %$domains) {
2030 return '<TR><TD ALIGN="right">Domain</TD><TD>'. $domains->{$key}.
2031 '<INPUT TYPE="hidden" NAME="domsvc" VALUE="'. $key. '"></TD></TR>'
2034 my $text .= qq!<TR><TD ALIGN="right">Domain</TD><TD><SELECT NAME="domsvc" SIZE=1 STYLE="width: 20em">!;
2036 $text .= '<OPTION>(Choose Domain)' unless $domsvc;
2038 foreach my $domain ( sort { $domains->{$a} cmp $domains->{$b} } keys %$domains ) {
2039 $text .= qq!<OPTION VALUE="!. $domain. '"'.
2040 ( ( $domsvc && $domain == $domsvc ) ? ' SELECTED' : '' ). ">".
2041 $domains->{$domain};
2044 $text .= qq!</SELECT></TD></TR>!;
2050 =item didselector HASHREF | LIST
2052 Takes as input a hashref or list of key/value pairs with the following keys:
2058 Field name for the returned HTML fragment.
2062 Service definition (see L<FS::part_svc>)
2066 Returns an HTML fragment for DID selection.
2078 my $rv = mason_comp( 'comp'=>'/elements/select-did.html',
2079 'args'=>[ %$param ],
2083 $rv->{'error'} || $rv->{'output'};
2089 =head1 RESELLER FUNCTIONS
2091 Note: Resellers can also use the B<signup_info> and B<new_customer> functions
2092 with their active session, and the B<customer_info> and B<order_pkg> functions
2093 with their active session and an additional I<custnum> parameter.
2095 For the most part, development of the reseller web interface has been
2096 superceded by agent-virtualized access to the backend.
2108 =item agent_list_customers
2110 List agent's customers.
2118 L<freeside-selfservice-clientd>, L<freeside-selfservice-server>