1 package FS::SelfService;
4 use vars qw( $VERSION @ISA @EXPORT_OK $DEBUG
5 $skip_uid_check $dir $socket %autoload $tag );
11 use Storable 2.09 qw(nstore_fd fd_retrieve);
15 @ISA = qw( Exporter );
19 $dir = "/usr/local/freeside";
20 $socket = "$dir/selfservice_socket";
21 $socket .= '.'.$tag if defined $tag && length($tag);
23 #maybe should ask ClientAPI for this list
25 'passwd' => 'passwd/passwd',
26 'chfn' => 'passwd/passwd',
27 'chsh' => 'passwd/passwd',
28 'login_info' => 'MyAccount/login_info',
29 'login_banner_image' => 'MyAccount/login_banner_image',
30 'login' => 'MyAccount/login',
31 'logout' => 'MyAccount/logout',
32 'switch_acct' => 'MyAccount/switch_acct',
33 'customer_info' => 'MyAccount/customer_info',
34 'customer_info_short' => 'MyAccount/customer_info_short',
35 'billing_history' => 'MyAccount/billing_history',
36 'edit_info' => 'MyAccount/edit_info', #add to ss cgi!
37 'invoice' => 'MyAccount/invoice',
38 'invoice_pdf' => 'MyAccount/invoice_pdf',
39 'legacy_invoice' => 'MyAccount/legacy_invoice',
40 'legacy_invoice_pdf' => 'MyAccount/legacy_invoice_pdf',
41 'invoice_logo' => 'MyAccount/invoice_logo',
42 'list_invoices' => 'MyAccount/list_invoices', #?
43 'cancel' => 'MyAccount/cancel', #add to ss cgi!
44 'payment_info' => 'MyAccount/payment_info',
45 'payment_info_renew_info' => 'MyAccount/payment_info_renew_info',
46 'process_payment' => 'MyAccount/process_payment',
47 'store_payment' => 'MyAccount/store_payment',
48 'process_stored_payment' => 'MyAccount/process_stored_payment',
49 'process_payment_order_pkg' => 'MyAccount/process_payment_order_pkg',
50 'process_payment_change_pkg' => 'MyAccount/process_payment_change_pkg',
51 'process_payment_order_renew' => 'MyAccount/process_payment_order_renew',
52 'process_prepay' => 'MyAccount/process_prepay',
53 'realtime_collect' => 'MyAccount/realtime_collect',
54 'list_pkgs' => 'MyAccount/list_pkgs', #add to ss (added?)
55 'list_svcs' => 'MyAccount/list_svcs', #add to ss (added?)
56 'list_svc_usage' => 'MyAccount/list_svc_usage',
57 'svc_status_html' => 'MyAccount/svc_status_html',
58 'svc_status_hash' => 'MyAccount/svc_status_hash',
59 'set_svc_status_hash' => 'MyAccount/set_svc_status_hash',
60 'acct_forward_info' => 'MyAccount/acct_forward_info',
61 'process_acct_forward' => 'MyAccount/process_acct_forward',
62 'list_dsl_devices' => 'MyAccount/list_dsl_devices',
63 'add_dsl_device' => 'MyAccount/add_dsl_device',
64 'delete_dsl_device' => 'MyAccount/delete_dsl_device',
65 'port_graph' => 'MyAccount/port_graph',
66 'list_cdr_usage' => 'MyAccount/list_cdr_usage',
67 'list_support_usage' => 'MyAccount/list_support_usage',
68 'order_pkg' => 'MyAccount/order_pkg', #add to ss cgi!
69 'change_pkg' => 'MyAccount/change_pkg',
70 'order_recharge' => 'MyAccount/order_recharge',
71 'renew_info' => 'MyAccount/renew_info',
72 'order_renew' => 'MyAccount/order_renew',
73 'cancel_pkg' => 'MyAccount/cancel_pkg', #add to ss cgi!
74 'suspend_pkg' => 'MyAccount/suspend_pkg', #add to ss cgi!
75 'charge' => 'MyAccount/charge', #?
76 'part_svc_info' => 'MyAccount/part_svc_info',
77 'provision_acct' => 'MyAccount/provision_acct',
78 'provision_phone' => 'MyAccount/provision_phone',
79 'provision_external' => 'MyAccount/provision_external',
80 'unprovision_svc' => 'MyAccount/unprovision_svc',
81 'myaccount_passwd' => 'MyAccount/myaccount_passwd',
82 'reset_passwd' => 'MyAccount/reset_passwd',
83 'check_reset_passwd' => 'MyAccount/check_reset_passwd',
84 'process_reset_passwd' => 'MyAccount/process_reset_passwd',
85 'create_ticket' => 'MyAccount/create_ticket',
86 'get_ticket' => 'MyAccount/get_ticket',
87 'adjust_ticket_priority' => 'MyAccount/adjust_ticket_priority',
88 'did_report' => 'MyAccount/did_report',
89 'signup_info' => 'Signup/signup_info',
90 'skin_info' => 'MyAccount/skin_info',
91 'access_info' => 'MyAccount/access_info',
92 'domain_select_hash' => 'Signup/domain_select_hash', # expose?
93 'new_customer' => 'Signup/new_customer',
94 'capture_payment' => 'Signup/capture_payment',
95 #N/A 'clear_signup_cache' => 'Signup/clear_cache',
96 'new_agent' => 'Agent/new_agent',
97 'agent_login' => 'Agent/agent_login',
98 'agent_logout' => 'Agent/agent_logout',
99 'agent_info' => 'Agent/agent_info',
100 'agent_list_customers' => 'Agent/agent_list_customers',
101 'check_username' => 'Agent/check_username',
102 'suspend_username' => 'Agent/suspend_username',
103 'unsuspend_username' => 'Agent/unsuspend_username',
104 'mason_comp' => 'MasonComponent/mason_comp',
105 'call_time' => 'PrepaidPhone/call_time',
106 'call_time_nanpa' => 'PrepaidPhone/call_time_nanpa',
107 'phonenum_balance' => 'PrepaidPhone/phonenum_balance',
111 qw( regionselector regionselector_hashref location_form
112 expselect popselector domainselector didselector
116 $ENV{'PATH'} ='/usr/bin:/usr/ucb:/bin';
117 $ENV{'SHELL'} = '/bin/sh';
118 $ENV{'IFS'} = " \t\n";
121 $ENV{'BASH_ENV'} = '';
123 #you can add BEGIN { $FS::SelfService::skip_uid_check = 1; }
124 #if you grant appropriate permissions to whatever user
125 my $freeside_uid = scalar(getpwnam('freeside'));
126 die "not running as the freeside user\n"
127 if $> != $freeside_uid && ! $skip_uid_check;
129 -e $dir or die "FATAL: $dir doesn't exist!";
130 -d $dir or die "FATAL: $dir isn't a directory!";
131 -r $dir or die "FATAL: Can't read $dir as freeside user!";
132 -x $dir or die "FATAL: $dir not searchable (executable) as freeside user!";
134 foreach my $autoload ( keys %autoload ) {
137 "sub $autoload { ". '
142 #warn scalar(@_). ": ". join(" / ", @_);
146 $param->{_packet} = \''. $autoload{$autoload}. '\';
148 simple_packet($param);
158 warn "sending ". $packet->{_packet}. " to server"
160 socket(SOCK, PF_UNIX, SOCK_STREAM, 0) or die "socket: $!";
161 connect(SOCK, sockaddr_un($socket)) or die "connect to $socket: $!";
162 nstore_fd($packet, \*SOCK) or die "can't send packet: $!";
165 #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
167 #block until there is a message on socket
168 # my $w = new IO::Select;
170 # my @wait = $w->can_read;
172 warn "reading message from server"
175 my $return = fd_retrieve(\*SOCK) or die "error reading result: $!";
176 die $return->{'_error'} if defined $return->{_error} && $return->{_error};
178 warn "returning message to client"
186 FS::SelfService - Freeside self-service API
190 # password and shell account changes
191 use FS::SelfService qw(passwd chfn chsh);
193 # "my account" functionality
194 use FS::SelfService qw( login customer_info invoice cancel payment_info process_payment );
196 my $rv = login( { 'username' => $username,
198 'password' => $password,
202 if ( $rv->{'error'} ) {
203 #handle login error...
206 my $session_id = $rv->{'session_id'};
209 my $customer_info = customer_info( { 'session_id' => $session_id } );
211 #payment_info and process_payment are available in 1.5+ only
212 my $payment_info = payment_info( { 'session_id' => $session_id } );
214 #!!! process_payment example
216 #!!! list_pkgs example
218 #!!! order_pkg example
220 #!!! cancel_pkg example
222 # signup functionality
223 use FS::SelfService qw( signup_info new_customer );
225 my $signup_info = signup_info;
227 $rv = new_customer( {
230 'company' => $company,
231 'address1' => $address1,
232 'address2' => $address2,
236 'country' => $country,
237 'daytime' => $daytime,
241 'payinfo' => $payinfo,
243 'paystart_month' => $paystart_month
244 'paystart_year' => $paystart_year,
245 'payissue' => $payissue,
247 'paydate' => $paydate,
248 'payname' => $payname,
249 'invoicing_list' => $invoicing_list,
250 'referral_custnum' => $referral_custnum,
251 'agentnum' => $agentnum,
252 'pkgpart' => $pkgpart,
254 'username' => $username,
255 '_password' => $password,
259 'phonenum' => $phonenum,
264 my $error = $rv->{'error'};
265 if ( $error eq '_decline' ) {
275 Use this API to implement your own client "self-service" module.
277 If you just want to customize the look of the existing "self-service" module,
280 =head1 PASSWORD, GECOS, SHELL CHANGING FUNCTIONS
292 =head1 "MY ACCOUNT" FUNCTIONS
298 Creates a user session. Takes a hash reference as parameter with the
317 Returns a hash reference with the following keys:
323 Empty on success, or an error message on errors.
327 Session identifier for successful logins
331 =item customer_info HASHREF
333 Returns general customer information.
335 Takes a hash reference as parameter with a single key: B<session_id>
337 Returns a hash reference with the following keys:
351 Array reference of hash references of open inoices. Each hash reference has
352 the following keys: invnum, date, owed
356 An HTML fragment containing shipping and billing addresses.
358 =item The following fields are also returned
360 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
364 =item edit_info HASHREF
366 Takes a hash reference as parameter with any of the following keys:
368 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
370 If a field exists, the customer record is updated with the new value of that
371 field. If a field does not exist, that field is not changed on the customer
374 Returns a hash reference with a single key, B<error>, empty on success, or an
375 error message on errors
377 =item invoice HASHREF
379 Returns an invoice. Takes a hash reference as parameter with two keys:
380 session_id and invnum
382 Returns a hash reference with the following keys:
388 Empty on success, or an error message on errors
400 =item list_invoices HASHREF
402 Returns a list of all customer invoices. Takes a hash references with a single
405 Returns a hash reference with the following keys:
411 Empty on success, or an error message on errors
415 Reference to array of hash references with the following keys:
425 Invoice date, in UNIX epoch time
433 Cancels this customer.
435 Takes a hash reference as parameter with a single key: B<session_id>
437 Returns a hash reference with a single key, B<error>, which is empty on
438 success or an error message on errors.
440 =item payment_info HASHREF
442 Returns information that may be useful in displaying a payment page.
444 Takes a hash reference as parameter with a single key: B<session_id>.
446 Returns a hash reference with the following keys:
452 Empty on success, or an error message on errors
460 Exact name on credit card (CARD/DCRD)
484 Customer's current default payment type.
488 For CARD/DCRD payment types, the card type (Visa card, MasterCard, Discover card, American Express card, etc.)
492 For CARD/DCRD payment types, the card number
496 For CARD/DCRD payment types, expiration month
500 For CARD/DCRD payment types, expiration year
502 =item cust_main_county
504 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.
508 Array reference of all states in the current default country.
512 Hash reference of card types; keys are card types, values are the exact strings
513 passed to the process_payment function
517 #this doesn't actually work yet
521 #Unique transaction identifier (prevents multiple charges), passed to the
522 #process_payment function
526 =item process_payment HASHREF
528 Processes a payment and possible change of address or payment type. Takes a
529 hash reference as parameter with the following keys:
543 If true, address and card information entered will be saved for subsequent
548 If true, future credit card payments will be done automatically (sets payby to
549 CARD). If false, future credit card payments will be done on-demand (sets
550 payby to DCRD). This option only has meaning if B<save> is set true.
578 Two-letter country code
586 Card expiration month
594 #this doesn't actually work yet
598 #Unique transaction identifier, returned from the payment_info function.
599 #Prevents multiple charges.
603 Returns a hash reference with a single key, B<error>, empty on success, or an
604 error message on errors.
606 =item process_payment_order_pkg
608 Combines the B<process_payment> and B<order_pkg> functions in one step. If the
609 payment processes sucessfully, the package is ordered. Takes a hash reference
610 as parameter with the keys of both methods.
612 Returns a hash reference with a single key, B<error>, empty on success, or an
613 error message on errors.
615 =item process_payment_change_pkg
617 Combines the B<process_payment> and B<change_pkg> functions in one step. If the
618 payment processes sucessfully, the package is ordered. Takes a hash reference
619 as parameter with the keys of both methods.
621 Returns a hash reference with a single key, B<error>, empty on success, or an
622 error message on errors.
625 =item process_payment_order_renew
627 Combines the B<process_payment> and B<order_renew> functions in one step. If
628 the payment processes sucessfully, the renewal is processed. Takes a hash
629 reference as parameter with the keys of both methods.
631 Returns a hash reference with a single key, B<error>, empty on success, or an
632 error message on errors.
636 Returns package information for this customer. For more detail on services,
639 Takes a hash reference as parameter with a single key: B<session_id>
641 Returns a hash reference containing customer package information. The hash reference contains the following keys:
651 Empty on success, or an error message on errors.
653 =item cust_pkg HASHREF
655 Array reference of hash references, each of which has the fields of a cust_pkg
656 record (see L<FS::cust_pkg>) as well as the fields below. Note these are not
657 the internal FS:: objects, but hash references of columns and values.
661 =item part_pkg fields
663 All fields of part_pkg for this specific cust_pkg (be careful with this
664 information - it may reveal more about your available packages than you would
665 like users to know in aggregate)
669 #XXX pare part_pkg fields down to a more secure subset
673 An array of hash references indicating information on unprovisioned services
674 available for provisioning for this specific cust_pkg. Each has the following
679 =item part_svc fields
681 All fields of part_svc (be careful with this information - it may reveal more
682 about your available packages than you would like users to know in aggregate)
686 #XXX pare part_svc fields down to a more secure subset
692 An array of hash references indicating information on the customer services
693 already provisioned for this specific cust_pkg. Each has the following keys:
699 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.
705 Primary key for this service
709 Service definition (see L<FS::part_svc>)
713 Customer package (see L<FS::cust_pkg>)
717 Blank if the service is not over limit, or the date the service exceeded its usage limit (as a UNIX timestamp).
725 Returns service information for this customer.
727 Takes a hash reference as parameter with a single key: B<session_id>
729 Returns a hash reference containing customer package information. The hash reference contains the following keys:
739 An array of hash references indicating information on all of this customer's
740 services. Each has the following keys:
746 Primary key for this service
754 Meaningful user-specific identifier for the service (i.e. username, domain, or
759 Account (svc_acct) services also have the following keys:
777 Upload bytes remaining
781 Download bytes remaining
785 Total bytes remaining
787 =item recharge_amount
791 =item recharge_seconds
793 Number of seconds gained by recharge
795 =item recharge_upbytes
797 Number of upload bytes gained by recharge
799 =item recharge_downbytes
801 Number of download bytes gained by recharge
803 =item recharge_totalbytes
805 Number of total bytes gained by recharge
813 Orders a package for this customer.
815 Takes a hash reference as parameter with the following keys:
825 Package to order (see L<FS::part_pkg>).
829 Service to order (see L<FS::part_svc>).
831 Normally optional; required only to provision a non-svc_acct service, or if the
832 package definition does not contain one svc_acct service definition with
833 quantity 1 (it may contain others with quantity >1). A svcpart of "none" can
834 also be specified to indicate that no initial service should be provisioned.
838 Fields used when provisioning an svc_acct service:
852 Optional security phrase
856 Optional Access number number
860 Fields used when provisioning an svc_domain service:
870 Fields used when provisioning an svc_phone service:
888 Fields used when provisioning an svc_external service:
902 Fields used when provisioning an svc_pbx service:
916 Returns a hash reference with a single key, B<error>, empty on success, or an
917 error message on errors. The special error '_decline' is returned for
918 declined transactions.
922 Changes a package for this customer.
924 Takes a hash reference as parameter with the following keys:
934 Existing customer package.
938 New package to order (see L<FS::part_pkg>).
942 Returns a hash reference with the following keys:
948 Empty on success, or an error message on errors.
952 On success, the new pkgnum
959 Provides useful info for early renewals.
961 Takes a hash reference as parameter with the following keys:
971 Returns a hash reference. On errors, it contains a single key, B<error>, with
972 the error message. Otherwise, contains a single key, B<dates>, pointing to
973 an array refernce of hash references. Each hash reference contains the
980 (Future) Bill date. Indicates a future date for which billing could be run.
981 Specified as a integer UNIX timestamp. Pass this value to the B<order_renew>
984 =item bill_date_pretty
986 (Future) Bill date as a human-readable string. (Convenience for display;
987 subject to change, so best not to parse for the date.)
991 Base amount which will be charged if renewed early as of this date.
995 Renewal date; i.e. even-futher future date at which the customer will be paid
996 through if the early renewal is completed with the given B<bill-date>.
997 Specified as a integer UNIX timestamp.
999 =item renew_date_pretty
1001 Renewal date as a human-readable string. (Convenience for display;
1002 subject to change, so best not to parse for the date.)
1006 Package that will be renewed.
1010 Expiration date of the package that will be renewed.
1012 =item expire_date_pretty
1014 Expiration date of the package that will be renewed, as a human-readable
1015 string. (Convenience for display; subject to change, so best not to parse for
1022 Renews this customer early; i.e. runs billing for this customer in advance.
1024 Takes a hash reference as parameter with the following keys:
1034 Integer date as returned by the B<renew_info> function, indicating the advance
1035 date for which to run billing.
1039 Returns a hash reference with a single key, B<error>, empty on success, or an
1040 error message on errors.
1044 Cancels a package for this customer.
1046 Takes a hash reference as parameter with the following keys:
1056 pkgpart of package to cancel
1060 Returns a hash reference with a single key, B<error>, empty on success, or an
1061 error message on errors.
1065 =head1 SIGNUP FUNCTIONS
1069 =item signup_info HASHREF
1071 Takes a hash reference as parameter with the following keys:
1075 =item session_id - Optional agent/reseller interface session
1079 Returns a hash reference containing information that may be useful in
1080 displaying a signup page. The hash reference contains the following keys:
1084 =item cust_main_county
1086 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.
1090 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
1091 an agentnum specified explicitly via reseller interface session_id in the
1096 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.
1098 =item agentnum2part_pkg
1100 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.
1104 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.
1106 =item security_phrase
1108 True if the "security_phrase" feature is enabled
1112 Array reference of acceptable payment types for signup
1118 credit card - automatic
1122 credit card - on-demand - version 1.5+ only
1126 electronic check - automatic
1130 electronic check - on-demand - version 1.5+ only
1138 billing, not recommended for signups
1142 free, definitely not recommended for signups
1146 special billing type: applies a credit (see FS::prepay_credit) and sets billing type to BILL
1152 True if CVV features are available (1.5+ or 1.4.2 with CVV schema patch)
1156 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".
1162 =item countrydefault
1168 =item new_customer HASHREF
1170 Creates a new customer. Takes a hash reference as parameter with the
1177 first name (required)
1181 last name (required)
1185 (not typically collected; mostly used for ACH transactions)
1191 =item address1 (required)
1199 =item city (required)
1207 =item state (required)
1211 =item zip (required)
1217 Daytime phone number
1221 Evening phone number
1229 CARD, DCRD, CHEK, DCHK, LECB, BILL, COMP or PREPAY (see L</signup_info> (required)
1233 Card number for CARD/DCRD, account_number@aba_number for CHEK/DCHK, prepaid "pin" for PREPAY, purchase order number for BILL
1237 Credit card CVV2 number (1.5+ or 1.4.2 with CVV schema patch)
1241 Expiration date for CARD/DCRD
1245 Exact name on credit card for CARD/DCRD, bank name for CHEK/DCHK
1247 =item invoicing_list
1249 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),
1251 =item referral_custnum
1253 referring customer number
1261 pkgpart of initial package
1277 Access number (index, not the literal number)
1281 Country code (to be provisioned as a service)
1285 Phone number (to be provisioned as a service)
1293 Returns a hash reference with the following keys:
1299 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)
1303 =item regionselector HASHREF | LIST
1305 Takes as input a hashref or list of key/value pairs with the following keys:
1309 =item selected_county
1311 Currently selected county
1313 =item selected_state
1315 Currently selected state
1317 =item selected_country
1319 Currently selected country
1323 Specify a unique prefix string if you intend to use the HTML output multiple time son one page.
1327 Specify a javascript subroutine to call on changes
1333 =item default_country
1339 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>.
1343 Returns a list consisting of three HTML fragments for county selection,
1344 state selection and country selection, respectively.
1348 #false laziness w/FS::cust_main_county (this is currently the "newest" version)
1349 sub regionselector {
1356 $param->{'selected_country'} ||= $param->{'default_country'};
1357 $param->{'selected_state'} ||= $param->{'default_state'};
1359 my $prefix = exists($param->{'prefix'}) ? $param->{'prefix'} : '';
1363 my %cust_main_county;
1365 # unless ( @cust_main_county ) { #cache
1366 #@cust_main_county = qsearch('cust_main_county', {} );
1367 #foreach my $c ( @cust_main_county ) {
1368 foreach my $c ( @{ $param->{'locales'} } ) {
1369 #$countyflag=1 if $c->county;
1370 $countyflag=1 if $c->{county};
1371 #push @{$cust_main_county{$c->country}{$c->state}}, $c->county;
1372 #$cust_main_county{$c->country}{$c->state}{$c->county} = 1;
1373 $cust_main_county{$c->{country}}{$c->{state}}{$c->{county}} = 1;
1376 $countyflag=1 if $param->{selected_county};
1378 my $script_html = <<END;
1380 function opt(what,value,text) {
1381 var optionName = new Option(text, value, false, false);
1382 var length = what.length;
1383 what.options[length] = optionName;
1385 function ${prefix}country_changed(what) {
1386 country = what.options[what.selectedIndex].text;
1387 for ( var i = what.form.${prefix}state.length; i >= 0; i-- )
1388 what.form.${prefix}state.options[i] = null;
1390 #what.form.${prefix}state.options[0] = new Option('', '', false, true);
1392 foreach my $country ( sort keys %cust_main_county ) {
1393 $script_html .= "\nif ( country == \"$country\" ) {\n";
1394 foreach my $state ( sort keys %{$cust_main_county{$country}} ) {
1395 my $text = $state || '(n/a)';
1396 $script_html .= qq!opt(what.form.${prefix}state, "$state", "$text");\n!;
1398 $script_html .= "}\n";
1401 $script_html .= <<END;
1403 function ${prefix}state_changed(what) {
1406 if ( $countyflag ) {
1407 $script_html .= <<END;
1408 state = what.options[what.selectedIndex].text;
1409 country = what.form.${prefix}country.options[what.form.${prefix}country.selectedIndex].text;
1410 for ( var i = what.form.${prefix}county.length; i >= 0; i-- )
1411 what.form.${prefix}county.options[i] = null;
1414 foreach my $country ( sort keys %cust_main_county ) {
1415 $script_html .= "\nif ( country == \"$country\" ) {\n";
1416 foreach my $state ( sort keys %{$cust_main_county{$country}} ) {
1417 $script_html .= "\nif ( state == \"$state\" ) {\n";
1418 #foreach my $county ( sort @{$cust_main_county{$country}{$state}} ) {
1419 foreach my $county ( sort keys %{$cust_main_county{$country}{$state}} ) {
1420 my $text = $county || '(n/a)';
1422 qq!opt(what.form.${prefix}county, "$county", "$text");\n!;
1424 $script_html .= "}\n";
1426 $script_html .= "}\n";
1430 $script_html .= <<END;
1435 my $county_html = $script_html;
1436 if ( $countyflag ) {
1437 $county_html .= qq!<SELECT NAME="${prefix}county" onChange="$param->{'onchange'}">!;
1438 foreach my $county (
1439 sort keys %{ $cust_main_county{$param->{'selected_country'}}{$param->{'selected_state'}} }
1441 my $text = $county || '(n/a)';
1442 $county_html .= qq!<OPTION VALUE="$county"!.
1443 ($county eq $param->{'selected_county'} ?
1450 $county_html .= '</SELECT>';
1453 qq!<INPUT TYPE="hidden" NAME="${prefix}county" VALUE="$param->{'selected_county'}">!;
1456 my $state_html = qq!<SELECT NAME="${prefix}state" !.
1457 qq!onChange="${prefix}state_changed(this); $param->{'onchange'}">!;
1458 foreach my $state ( sort keys %{ $cust_main_county{$param->{'selected_country'}} } ) {
1459 my $text = $state || '(n/a)';
1460 my $selected = $state eq $param->{'selected_state'} ? 'SELECTED' : '';
1461 $state_html .= "\n<OPTION $selected VALUE=$state>$text</OPTION>"
1463 $state_html .= '</SELECT>';
1465 my $country_html = '';
1466 if ( scalar( keys %cust_main_county ) > 1 ) {
1468 $country_html = qq(<SELECT NAME="${prefix}country" ).
1469 qq(onChange="${prefix}country_changed(this); ).
1470 $param->{'onchange'}.
1473 my $countrydefault = $param->{default_country} || 'US';
1474 foreach my $country (
1475 sort { ($b eq $countrydefault) <=> ($a eq $countrydefault) or $a cmp $b }
1476 keys %cust_main_county
1478 my $selected = $country eq $param->{'selected_country'}
1481 $country_html .= "\n<OPTION$selected>$country</OPTION>"
1483 $country_html .= '</SELECT>';
1486 $country_html = qq(<INPUT TYPE="hidden" NAME="${prefix}country" ).
1487 ' VALUE="'. (keys %cust_main_county )[0]. '">';
1491 ($county_html, $state_html, $country_html);
1495 sub regionselector_hashref {
1496 my ($county_html, $state_html, $country_html) = regionselector(@_);
1498 'county_html' => $county_html,
1499 'state_html' => $state_html,
1500 'country_html' => $country_html,
1504 =item location_form HASHREF | LIST
1506 Takes as input a hashref or list of key/value pairs with the following keys:
1512 Current customer session_id
1516 Omit red asterisks from required fields.
1518 =item address1_label
1520 Label for first address line.
1524 Returns an HTML fragment for a location form (address, city, state, zip,
1537 my $session_id = delete $param->{'session_id'};
1539 my $rv = mason_comp( 'session_id' => $session_id,
1540 'comp' => '/elements/location.html',
1541 'args' => [ %$param ],
1545 $rv->{'error'} || $rv->{'output'};
1550 #=item expselect HASHREF | LIST
1552 #Takes as input a hashref or list of key/value pairs with the following keys:
1556 #=item prefix - Specify a unique prefix string if you intend to use the HTML output multiple time son one page.
1558 #=item date - current date, in yyyy-mm-dd or m-d-yyyy format
1562 =item expselect PREFIX [ DATE ]
1564 Takes as input a unique prefix string and the current expiration date, in
1565 yyyy-mm-dd or m-d-yyyy format
1567 Returns an HTML fragments for expiration date selection.
1573 #if ( ref($_[0]) ) {
1577 #my $prefix = $param->{'prefix'};
1578 #my $prefix = exists($param->{'prefix'}) ? $param->{'prefix'} : '';
1579 #my $date = exists($param->{'date'}) ? $param->{'date'} : '';
1581 my $date = scalar(@_) ? shift : '';
1583 my( $m, $y ) = ( 0, 0 );
1584 if ( $date =~ /^(\d{4})-(\d{2})-\d{2}$/ ) { #PostgreSQL date format
1585 ( $m, $y ) = ( $2, $1 );
1586 } elsif ( $date =~ /^(\d{1,2})-(\d{1,2}-)?(\d{4}$)/ ) {
1587 ( $m, $y ) = ( $1, $3 );
1589 my $return = qq!<SELECT NAME="$prefix!. qq!_month" SIZE="1">!;
1591 $return .= qq!<OPTION VALUE="$_"!;
1592 $return .= " SELECTED" if $_ == $m;
1595 $return .= qq!</SELECT>/<SELECT NAME="$prefix!. qq!_year" SIZE="1">!;
1597 my $thisYear = $t[5] + 1900;
1598 for ( ($thisYear > $y && $y > 0 ? $y : $thisYear) .. ($thisYear+10) ) {
1599 $return .= qq!<OPTION VALUE="$_"!;
1600 $return .= " SELECTED" if $_ == $y;
1603 $return .= "</SELECT>";
1608 =item popselector HASHREF | LIST
1610 Takes as input a hashref or list of key/value pairs with the following keys:
1616 Access number number
1620 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>.
1624 Returns an HTML fragment for access number selection.
1628 #horrible false laziness with FS/FS/svc_acct_pop.pm::popselector
1636 my $popnum = $param->{'popnum'};
1637 my $pops = $param->{'pops'};
1639 return '<INPUT TYPE="hidden" NAME="popnum" VALUE="">' unless @$pops;
1640 return $pops->[0]{city}. ', '. $pops->[0]{state}.
1641 ' ('. $pops->[0]{ac}. ')/'. $pops->[0]{exch}. '-'. $pops->[0]{loc}.
1642 '<INPUT TYPE="hidden" NAME="popnum" VALUE="'. $pops->[0]{popnum}. '">'
1643 if scalar(@$pops) == 1;
1646 my %popnum2pop = ();
1648 push @{ $pop{ $_->{state} }->{ $_->{ac} } }, $_;
1649 $popnum2pop{$_->{popnum}} = $_;
1654 function opt(what,href,text) {
1655 var optionName = new Option(text, href, false, false)
1656 var length = what.length;
1657 what.options[length] = optionName;
1661 my $init_popstate = $param->{'init_popstate'};
1662 if ( $init_popstate ) {
1663 $text .= '<INPUT TYPE="hidden" NAME="init_popstate" VALUE="'.
1664 $init_popstate. '">';
1667 function acstate_changed(what) {
1668 state = what.options[what.selectedIndex].text;
1669 what.form.popac.options.length = 0
1670 what.form.popac.options[0] = new Option("Area code", "-1", false, true);
1674 my @states = $init_popstate ? ( $init_popstate ) : keys %pop;
1675 foreach my $state ( sort { $a cmp $b } @states ) {
1676 $text .= "\nif ( state == \"$state\" ) {\n" unless $init_popstate;
1678 foreach my $ac ( sort { $a cmp $b } keys %{ $pop{$state} }) {
1679 $text .= "opt(what.form.popac, \"$ac\", \"$ac\");\n";
1680 if ($ac eq $param->{'popac'}) {
1681 $text .= "what.form.popac.options[what.form.popac.length-1].selected = true;\n";
1684 $text .= "}\n" unless $init_popstate;
1686 $text .= "popac_changed(what.form.popac)}\n";
1689 function popac_changed(what) {
1690 ac = what.options[what.selectedIndex].text;
1691 what.form.popnum.options.length = 0;
1692 what.form.popnum.options[0] = new Option("City", "-1", false, true);
1696 foreach my $state ( @states ) {
1697 foreach my $popac ( keys %{ $pop{$state} } ) {
1698 $text .= "\nif ( ac == \"$popac\" ) {\n";
1700 foreach my $pop ( @{$pop{$state}->{$popac}}) {
1701 my $o_popnum = $pop->{popnum};
1702 my $poptext = $pop->{city}. ', '. $pop->{state}.
1703 ' ('. $pop->{ac}. ')/'. $pop->{exch}. '-'. $pop->{loc};
1705 $text .= "opt(what.form.popnum, \"$o_popnum\", \"$poptext\");\n";
1706 if ($popnum == $o_popnum) {
1707 $text .= "what.form.popnum.options[what.form.popnum.length-1].selected = true;\n";
1715 $text .= "}\n</SCRIPT>\n";
1717 $param->{'acstate'} = '' unless defined($param->{'acstate'});
1720 qq!<TABLE CELLPADDING="0"><TR><TD><SELECT NAME="acstate"! .
1721 qq!SIZE=1 onChange="acstate_changed(this)"><OPTION VALUE=-1>State!;
1722 $text .= "<OPTION" . ($_ eq $param->{'acstate'} ? " SELECTED" : "") .
1723 ">$_" foreach sort { $a cmp $b } @states;
1724 $text .= '</SELECT>'; #callback? return 3 html pieces? #'</TD>';
1727 qq!<SELECT NAME="popac" SIZE=1 onChange="popac_changed(this)">!.
1728 qq!<OPTION>Area code</SELECT></TR><TR VALIGN="top">!;
1730 $text .= qq!<TR><TD><SELECT NAME="popnum" SIZE=1 STYLE="width: 20em"><OPTION>City!;
1733 #comment this block to disable initial list polulation
1734 my @initial_select = ();
1735 if ( scalar( @$pops ) > 100 ) {
1736 push @initial_select, $popnum2pop{$popnum} if $popnum2pop{$popnum};
1738 @initial_select = @$pops;
1740 foreach my $pop ( sort { $a->{state} cmp $b->{state} } @initial_select ) {
1741 $text .= qq!<OPTION VALUE="!. $pop->{popnum}. '"'.
1742 ( ( $popnum && $pop->{popnum} == $popnum ) ? ' SELECTED' : '' ). ">".
1743 $pop->{city}. ', '. $pop->{state}.
1744 ' ('. $pop->{ac}. ')/'. $pop->{exch}. '-'. $pop->{loc};
1747 $text .= qq!</SELECT></TD></TR></TABLE>!;
1753 =item domainselector HASHREF | LIST
1755 Takes as input a hashref or list of key/value pairs with the following keys:
1765 Service number of the selected item.
1769 Returns an HTML fragment for domain selection.
1773 sub domainselector {
1780 my $domsvc= $param->{'domsvc'};
1782 domain_select_hash(map {$_ => $param->{$_}} qw(pkgnum svcpart pkgpart) );
1783 my $domains = $rv->{'domains'};
1784 $domsvc = $rv->{'domsvc'} unless $domsvc;
1786 return '<INPUT TYPE="hidden" NAME="domsvc" VALUE="">'
1787 unless scalar(keys %$domains);
1789 if (scalar(keys %$domains) == 1) {
1791 foreach(keys %$domains) {
1794 return '<TR><TD ALIGN="right">Domain</TD><TD>'. $domains->{$key}.
1795 '<INPUT TYPE="hidden" NAME="domsvc" VALUE="'. $key. '"></TD></TR>'
1798 my $text .= qq!<TR><TD ALIGN="right">Domain</TD><TD><SELECT NAME="domsvc" SIZE=1 STYLE="width: 20em"><OPTION>(Choose Domain)!;
1801 foreach my $domain ( sort { $domains->{$a} cmp $domains->{$b} } keys %$domains ) {
1802 $text .= qq!<OPTION VALUE="!. $domain. '"'.
1803 ( ( $domsvc && $domain == $domsvc ) ? ' SELECTED' : '' ). ">".
1804 $domains->{$domain};
1807 $text .= qq!</SELECT></TD></TR>!;
1813 =item didselector HASHREF | LIST
1815 Takes as input a hashref or list of key/value pairs with the following keys:
1821 Field name for the returned HTML fragment.
1825 Service definition (see L<FS::part_svc>)
1829 Returns an HTML fragment for DID selection.
1841 my $rv = mason_comp( 'comp'=>'/elements/select-did.html',
1842 'args'=>[ %$param ],
1846 $rv->{'error'} || $rv->{'output'};
1852 =head1 RESELLER FUNCTIONS
1854 Note: Resellers can also use the B<signup_info> and B<new_customer> functions
1855 with their active session, and the B<customer_info> and B<order_pkg> functions
1856 with their active session and an additional I<custnum> parameter.
1858 For the most part, development of the reseller web interface has been
1859 superceded by agent-virtualized access to the backend.
1871 =item agent_list_customers
1873 List agent's customers.
1881 L<freeside-selfservice-clientd>, L<freeside-selfservice-server>