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' => 'MyAccount/login',
30 'logout' => 'MyAccount/logout',
31 'customer_info' => 'MyAccount/customer_info',
32 'edit_info' => 'MyAccount/edit_info', #add to ss cgi!
33 'invoice' => 'MyAccount/invoice',
34 'invoice_logo' => 'MyAccount/invoice_logo',
35 'list_invoices' => 'MyAccount/list_invoices', #?
36 'cancel' => 'MyAccount/cancel', #add to ss cgi!
37 'payment_info' => 'MyAccount/payment_info',
38 'process_payment' => 'MyAccount/process_payment',
39 'process_payment_order_pkg' => 'MyAccount/process_payment_order_pkg',
40 'process_payment_order_renew' => 'MyAccount/process_payment_order_renew',
41 'process_prepay' => 'MyAccount/process_prepay',
42 'list_pkgs' => 'MyAccount/list_pkgs', #add to ss (added?)
43 'list_svcs' => 'MyAccount/list_svcs', #add to ss (added?)
44 'list_svc_usage' => 'MyAccount/list_svc_usage',
45 'list_support_usage' => 'MyAccount/list_support_usage',
46 'order_pkg' => 'MyAccount/order_pkg', #add to ss cgi!
47 'change_pkg' => 'MyAccount/change_pkg',
48 'order_recharge' => 'MyAccount/order_recharge',
49 'renew_info' => 'MyAccount/renew_info',
50 'order_renew' => 'MyAccount/order_renew',
51 'cancel_pkg' => 'MyAccount/cancel_pkg', #add to ss cgi!
52 'charge' => 'MyAccount/charge', #?
53 'part_svc_info' => 'MyAccount/part_svc_info',
54 'provision_acct' => 'MyAccount/provision_acct',
55 'provision_external' => 'MyAccount/provision_external',
56 'unprovision_svc' => 'MyAccount/unprovision_svc',
57 'myaccount_passwd' => 'MyAccount/myaccount_passwd',
58 'signup_info' => 'Signup/signup_info',
59 'domain_select_hash' => 'Signup/domain_select_hash', # expose?
60 'new_customer' => 'Signup/new_customer',
61 'agent_login' => 'Agent/agent_login',
62 'agent_logout' => 'Agent/agent_logout',
63 'agent_info' => 'Agent/agent_info',
64 'agent_list_customers' => 'Agent/agent_list_customers',
65 'mason_comp' => 'MasonComponent/mason_comp',
66 'call_time' => 'PrepaidPhone/call_time',
67 'call_time_nanpa' => 'PrepaidPhone/call_time_nanpa',
68 'phonenum_balance' => 'PrepaidPhone/phonenum_balance',
72 qw( regionselector regionselector_hashref
73 expselect popselector domainselector didselector )
76 $ENV{'PATH'} ='/usr/bin:/usr/ucb:/bin';
77 $ENV{'SHELL'} = '/bin/sh';
78 $ENV{'IFS'} = " \t\n";
81 $ENV{'BASH_ENV'} = '';
83 #you can add BEGIN { $FS::SelfService::skip_uid_check = 1; }
84 #if you grant appropriate permissions to whatever user
85 my $freeside_uid = scalar(getpwnam('freeside'));
86 die "not running as the freeside user\n"
87 if $> != $freeside_uid && ! $skip_uid_check;
89 -e $dir or die "FATAL: $dir doesn't exist!";
90 -d $dir or die "FATAL: $dir isn't a directory!";
91 -r $dir or die "FATAL: Can't read $dir as freeside user!";
92 -x $dir or die "FATAL: $dir not searchable (executable) as freeside user!";
94 foreach my $autoload ( keys %autoload ) {
102 #warn scalar(@_). ": ". join(" / ", @_);
106 $param->{_packet} = \''. $autoload{$autoload}. '\';
108 simple_packet($param);
118 warn "sending ". $packet->{_packet}. " to server"
120 socket(SOCK, PF_UNIX, SOCK_STREAM, 0) or die "socket: $!";
121 connect(SOCK, sockaddr_un($socket)) or die "connect to $socket: $!";
122 nstore_fd($packet, \*SOCK) or die "can't send packet: $!";
125 #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
127 #block until there is a message on socket
128 # my $w = new IO::Select;
130 # my @wait = $w->can_read;
132 warn "reading message from server"
135 my $return = fd_retrieve(\*SOCK) or die "error reading result: $!";
136 die $return->{'_error'} if defined $return->{_error} && $return->{_error};
138 warn "returning message to client"
146 FS::SelfService - Freeside self-service API
150 # password and shell account changes
151 use FS::SelfService qw(passwd chfn chsh);
153 # "my account" functionality
154 use FS::SelfService qw( login customer_info invoice cancel payment_info process_payment );
156 my $rv = login( { 'username' => $username,
158 'password' => $password,
162 if ( $rv->{'error'} ) {
163 #handle login error...
166 my $session_id = $rv->{'session_id'};
169 my $customer_info = customer_info( { 'session_id' => $session_id } );
171 #payment_info and process_payment are available in 1.5+ only
172 my $payment_info = payment_info( { 'session_id' => $session_id } );
174 #!!! process_payment example
176 #!!! list_pkgs example
178 #!!! order_pkg example
180 #!!! cancel_pkg example
182 # signup functionality
183 use FS::SelfService qw( signup_info new_customer );
185 my $signup_info = signup_info;
187 $rv = new_customer( {
190 'company' => $company,
191 'address1' => $address1,
192 'address2' => $address2,
196 'country' => $country,
197 'daytime' => $daytime,
201 'payinfo' => $payinfo,
203 'paystart_month' => $paystart_month
204 'paystart_year' => $paystart_year,
205 'payissue' => $payissue,
207 'paydate' => $paydate,
208 'payname' => $payname,
209 'invoicing_list' => $invoicing_list,
210 'referral_custnum' => $referral_custnum,
211 'agentnum' => $agentnum,
212 'pkgpart' => $pkgpart,
214 'username' => $username,
215 '_password' => $password,
219 'phonenum' => $phonenum,
224 my $error = $rv->{'error'};
225 if ( $error eq '_decline' ) {
235 Use this API to implement your own client "self-service" module.
237 If you just want to customize the look of the existing "self-service" module,
240 =head1 PASSWORD, GECOS, SHELL CHANGING FUNCTIONS
252 =head1 "MY ACCOUNT" FUNCTIONS
258 Creates a user session. Takes a hash reference as parameter with the
277 Returns a hash reference with the following keys:
283 Empty on success, or an error message on errors.
287 Session identifier for successful logins
291 =item customer_info HASHREF
293 Returns general customer information.
295 Takes a hash reference as parameter with a single key: B<session_id>
297 Returns a hash reference with the following keys:
311 Array reference of hash references of open inoices. Each hash reference has
312 the following keys: invnum, date, owed
316 An HTML fragment containing shipping and billing addresses.
318 =item The following fields are also returned
320 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
324 =item edit_info HASHREF
326 Takes a hash reference as parameter with any of the following keys:
328 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
330 If a field exists, the customer record is updated with the new value of that
331 field. If a field does not exist, that field is not changed on the customer
334 Returns a hash reference with a single key, B<error>, empty on success, or an
335 error message on errors
337 =item invoice HASHREF
339 Returns an invoice. Takes a hash reference as parameter with two keys:
340 session_id and invnum
342 Returns a hash reference with the following keys:
348 Empty on success, or an error message on errors
360 =item list_invoices HASHREF
362 Returns a list of all customer invoices. Takes a hash references with a single
365 Returns a hash reference with the following keys:
371 Empty on success, or an error message on errors
375 Reference to array of hash references with the following keys:
385 Invoice date, in UNIX epoch time
393 Cancels this customer.
395 Takes a hash reference as parameter with a single key: B<session_id>
397 Returns a hash reference with a single key, B<error>, which is empty on
398 success or an error message on errors.
400 =item payment_info HASHREF
402 Returns information that may be useful in displaying a payment page.
404 Takes a hash reference as parameter with a single key: B<session_id>.
406 Returns a hash reference with the following keys:
412 Empty on success, or an error message on errors
420 Exact name on credit card (CARD/DCRD)
444 Customer's current default payment type.
448 For CARD/DCRD payment types, the card type (Visa card, MasterCard, Discover card, American Express card, etc.)
452 For CARD/DCRD payment types, the card number
456 For CARD/DCRD payment types, expiration month
460 For CARD/DCRD payment types, expiration year
462 =item cust_main_county
464 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.
468 Array reference of all states in the current default country.
472 Hash reference of card types; keys are card types, values are the exact strings
473 passed to the process_payment function
477 Unique transaction identifier (prevents multiple charges), passed to the
478 process_payment function
482 =item process_payment HASHREF
484 Processes a payment and possible change of address or payment type. Takes a
485 hash reference as parameter with the following keys:
499 If true, address and card information entered will be saved for subsequent
504 If true, future credit card payments will be done automatically (sets payby to
505 CARD). If false, future credit card payments will be done on-demand (sets
506 payby to DCRD). This option only has meaning if B<save> is set true.
538 Card expiration month
546 Unique transaction identifier, returned from the payment_info function.
547 Prevents multiple charges.
551 Returns a hash reference with a single key, B<error>, empty on success, or an
552 error message on errors
554 =item process_payment_order_pkg
556 Combines the B<process_payment> and B<order_pkg> functions in one step. If the
557 payment processes sucessfully, the package is ordered. Takes a hash reference
558 as parameter with the keys of both methods.
560 Returns a hash reference with a single key, B<error>, empty on success, or an
561 error message on errors.
563 =item process_payment_order_renew
565 Combines the B<process_payment> and B<order_renew> functions in one step. If
566 the payment processes sucessfully, the renewal is processed. Takes a hash
567 reference as parameter with the keys of both methods.
569 Returns a hash reference with a single key, B<error>, empty on success, or an
570 error message on errors.
574 Returns package information for this customer. For more detail on services,
577 Takes a hash reference as parameter with a single key: B<session_id>
579 Returns a hash reference containing customer package information. The hash reference contains the following keys:
587 =item cust_pkg HASHREF
589 Array reference of hash references, each of which has the fields of a cust_pkg
590 record (see L<FS::cust_pkg>) as well as the fields below. Note these are not
591 the internal FS:: objects, but hash references of columns and values.
595 =item part_pkg fields
597 All fields of part_pkg for this specific cust_pkg (be careful with this
598 information - it may reveal more about your available packages than you would
599 like users to know in aggregate)
603 #XXX pare part_pkg fields down to a more secure subset
607 An array of hash references indicating information on unprovisioned services
608 available for provisioning for this specific cust_pkg. Each has the following
613 =item part_svc fields
615 All fields of part_svc (be careful with this information - it may reveal more
616 about your available packages than you would like users to know in aggregate)
620 #XXX pare part_svc fields down to a more secure subset
626 An array of hash references indicating information on the customer services
627 already provisioned for this specific cust_pkg. Each has the following keys:
633 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.
639 Primary key for this service
643 Service definition (part_pkg)
647 Customer package (cust_pkg)
651 Blank if the service is not over limit, or the date the service exceeded its usage limit (as a UNIX timestamp).
659 Empty on success, or an error message on errors.
665 Returns service information for this customer.
667 Takes a hash reference as parameter with a single key: B<session_id>
669 Returns a hash reference containing customer package information. The hash reference contains the following keys:
679 An array of hash references indicating information on all of this customer's
680 services. Each has the following keys:
686 Primary key for this service
694 Meaningful user-specific identifier for the service (i.e. username, domain, or
699 Account (svc_acct) services also have the following keys:
715 Upload bytes remaining
719 Download bytes remaining
723 Total bytes remaining
725 =item recharge_amount
729 =item recharge_seconds
731 Number of seconds gained by recharge
733 =item recharge_upbytes
735 Number of upload bytes gained by recharge
737 =item recharge_downbytes
739 Number of download bytes gained by recharge
741 =item recharge_totalbytes
743 Number of total bytes gained by recharge
749 Orders a package for this customer.
751 Takes a hash reference as parameter with the following keys:
761 pkgpart of package to order
765 optional svcpart, required only if the package definition does not contain
766 one svc_acct service definition with quantity 1 (it may contain others with
779 Optional security phrase
783 Optional Access number number
787 Returns a hash reference with a single key, B<error>, empty on success, or an
788 error message on errors. The special error '_decline' is returned for
789 declined transactions.
793 Provides useful info for early renewals.
795 Takes a hash reference as parameter with the following keys:
805 Returns a hash reference. On errors, it contains a single key, B<error>, with
806 the error message. Otherwise, contains a single key, B<dates>, pointing to
807 an array refernce of hash references. Each hash reference contains the
814 (Future) Bill date. Indicates a future date for which billing could be run.
815 Specified as a integer UNIX timestamp. Pass this value to the B<order_renew>
818 =item bill_date_pretty
820 (Future) Bill date as a human-readable string. (Convenience for display;
821 subject to change, so best not to parse for the date.)
825 Base amount which will be charged if renewed early as of this date.
829 Renewal date; i.e. even-futher future date at which the customer will be paid
830 through if the early renewal is completed with the given B<bill-date>.
831 Specified as a integer UNIX timestamp.
833 =item renew_date_pretty
835 Renewal date as a human-readable string. (Convenience for display;
836 subject to change, so best not to parse for the date.)
842 Renews this customer early; i.e. runs billing for this customer in advance.
844 Takes a hash reference as parameter with the following keys:
854 Integer date as returned by the B<renew_info> function, indicating the advance
855 date for which to run billing.
859 Returns a hash reference with a single key, B<error>, empty on success, or an
860 error message on errors.
864 Cancels a package for this customer.
866 Takes a hash reference as parameter with the following keys:
876 pkgpart of package to cancel
880 Returns a hash reference with a single key, B<error>, empty on success, or an
881 error message on errors.
885 =head1 SIGNUP FUNCTIONS
889 =item signup_info HASHREF
891 Takes a hash reference as parameter with the following keys:
895 =item session_id - Optional agent/reseller interface session
899 Returns a hash reference containing information that may be useful in
900 displaying a signup page. The hash reference contains the following keys:
904 =item cust_main_county
906 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.
910 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
911 an agentnum specified explicitly via reseller interface session_id in the
916 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.
918 =item agentnum2part_pkg
920 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.
924 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.
926 =item security_phrase
928 True if the "security_phrase" feature is enabled
932 Array reference of acceptable payment types for signup
938 credit card - automatic
942 credit card - on-demand - version 1.5+ only
946 electronic check - automatic
950 electronic check - on-demand - version 1.5+ only
958 billing, not recommended for signups
962 free, definitely not recommended for signups
966 special billing type: applies a credit (see FS::prepay_credit) and sets billing type to BILL
972 True if CVV features are available (1.5+ or 1.4.2 with CVV schema patch)
976 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".
988 =item new_customer HASHREF
990 Creates a new customer. Takes a hash reference as parameter with the
997 first name (required)
1001 last name (required)
1005 (not typically collected; mostly used for ACH transactions)
1011 =item address1 (required)
1019 =item city (required)
1027 =item state (required)
1031 =item zip (required)
1037 Daytime phone number
1041 Evening phone number
1049 CARD, DCRD, CHEK, DCHK, LECB, BILL, COMP or PREPAY (see L</signup_info> (required)
1053 Card number for CARD/DCRD, account_number@aba_number for CHEK/DCHK, prepaid "pin" for PREPAY, purchase order number for BILL
1057 Credit card CVV2 number (1.5+ or 1.4.2 with CVV schema patch)
1061 Expiration date for CARD/DCRD
1065 Exact name on credit card for CARD/DCRD, bank name for CHEK/DCHK
1067 =item invoicing_list
1069 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),
1071 =item referral_custnum
1073 referring customer number
1081 pkgpart of initial package
1097 Access number (index, not the literal number)
1101 Country code (to be provisioned as a service)
1105 Phone number (to be provisioned as a service)
1113 Returns a hash reference with the following keys:
1119 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)
1123 =item regionselector HASHREF | LIST
1125 Takes as input a hashref or list of key/value pairs with the following keys:
1129 =item selected_county
1131 Currently selected county
1133 =item selected_state
1135 Currently selected state
1137 =item selected_country
1139 Currently selected country
1143 Specify a unique prefix string if you intend to use the HTML output multiple time son one page.
1147 Specify a javascript subroutine to call on changes
1153 =item default_country
1159 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>.
1163 Returns a list consisting of three HTML fragments for county selection,
1164 state selection and country selection, respectively.
1168 #false laziness w/FS::cust_main_county (this is currently the "newest" version)
1169 sub regionselector {
1176 $param->{'selected_country'} ||= $param->{'default_country'};
1177 $param->{'selected_state'} ||= $param->{'default_state'};
1179 my $prefix = exists($param->{'prefix'}) ? $param->{'prefix'} : '';
1183 my %cust_main_county;
1185 # unless ( @cust_main_county ) { #cache
1186 #@cust_main_county = qsearch('cust_main_county', {} );
1187 #foreach my $c ( @cust_main_county ) {
1188 foreach my $c ( @{ $param->{'locales'} } ) {
1189 #$countyflag=1 if $c->county;
1190 $countyflag=1 if $c->{county};
1191 #push @{$cust_main_county{$c->country}{$c->state}}, $c->county;
1192 #$cust_main_county{$c->country}{$c->state}{$c->county} = 1;
1193 $cust_main_county{$c->{country}}{$c->{state}}{$c->{county}} = 1;
1196 $countyflag=1 if $param->{selected_county};
1198 my $script_html = <<END;
1200 function opt(what,value,text) {
1201 var optionName = new Option(text, value, false, false);
1202 var length = what.length;
1203 what.options[length] = optionName;
1205 function ${prefix}country_changed(what) {
1206 country = what.options[what.selectedIndex].text;
1207 for ( var i = what.form.${prefix}state.length; i >= 0; i-- )
1208 what.form.${prefix}state.options[i] = null;
1210 #what.form.${prefix}state.options[0] = new Option('', '', false, true);
1212 foreach my $country ( sort keys %cust_main_county ) {
1213 $script_html .= "\nif ( country == \"$country\" ) {\n";
1214 foreach my $state ( sort keys %{$cust_main_county{$country}} ) {
1215 my $text = $state || '(n/a)';
1216 $script_html .= qq!opt(what.form.${prefix}state, "$state", "$text");\n!;
1218 $script_html .= "}\n";
1221 $script_html .= <<END;
1223 function ${prefix}state_changed(what) {
1226 if ( $countyflag ) {
1227 $script_html .= <<END;
1228 state = what.options[what.selectedIndex].text;
1229 country = what.form.${prefix}country.options[what.form.${prefix}country.selectedIndex].text;
1230 for ( var i = what.form.${prefix}county.length; i >= 0; i-- )
1231 what.form.${prefix}county.options[i] = null;
1234 foreach my $country ( sort keys %cust_main_county ) {
1235 $script_html .= "\nif ( country == \"$country\" ) {\n";
1236 foreach my $state ( sort keys %{$cust_main_county{$country}} ) {
1237 $script_html .= "\nif ( state == \"$state\" ) {\n";
1238 #foreach my $county ( sort @{$cust_main_county{$country}{$state}} ) {
1239 foreach my $county ( sort keys %{$cust_main_county{$country}{$state}} ) {
1240 my $text = $county || '(n/a)';
1242 qq!opt(what.form.${prefix}county, "$county", "$text");\n!;
1244 $script_html .= "}\n";
1246 $script_html .= "}\n";
1250 $script_html .= <<END;
1255 my $county_html = $script_html;
1256 if ( $countyflag ) {
1257 $county_html .= qq!<SELECT NAME="${prefix}county" onChange="$param->{'onchange'}">!;
1258 $county_html .= '</SELECT>';
1261 qq!<INPUT TYPE="hidden" NAME="${prefix}county" VALUE="$param->{'selected_county'}">!;
1264 my $state_html = qq!<SELECT NAME="${prefix}state" !.
1265 qq!onChange="${prefix}state_changed(this); $param->{'onchange'}">!;
1266 foreach my $state ( sort keys %{ $cust_main_county{$param->{'selected_country'}} } ) {
1267 my $text = $state || '(n/a)';
1268 my $selected = $state eq $param->{'selected_state'} ? 'SELECTED' : '';
1269 $state_html .= "\n<OPTION $selected VALUE=$state>$text</OPTION>"
1271 $state_html .= '</SELECT>';
1273 my $country_html = '';
1274 if ( scalar( keys %cust_main_county ) > 1 ) {
1276 $country_html = qq(<SELECT NAME="${prefix}country" ).
1277 qq(onChange="${prefix}country_changed(this); ).
1278 $param->{'onchange'}.
1281 my $countrydefault = $param->{default_country} || 'US';
1282 foreach my $country (
1283 sort { ($b eq $countrydefault) <=> ($a eq $countrydefault) or $a cmp $b }
1284 keys %cust_main_county
1286 my $selected = $country eq $param->{'selected_country'}
1289 $country_html .= "\n<OPTION$selected>$country</OPTION>"
1291 $country_html .= '</SELECT>';
1294 $country_html = qq(<INPUT TYPE="hidden" NAME="${prefix}country" ).
1295 ' VALUE="'. (keys %cust_main_county )[0]. '">';
1299 ($county_html, $state_html, $country_html);
1303 sub regionselector_hashref {
1304 my ($county_html, $state_html, $country_html) = regionselector(@_);
1306 'county_html' => $county_html,
1307 'state_html' => $state_html,
1308 'country_html' => $country_html,
1312 #=item expselect HASHREF | LIST
1314 #Takes as input a hashref or list of key/value pairs with the following keys:
1318 #=item prefix - Specify a unique prefix string if you intend to use the HTML output multiple time son one page.
1320 #=item date - current date, in yyyy-mm-dd or m-d-yyyy format
1324 =item expselect PREFIX [ DATE ]
1326 Takes as input a unique prefix string and the current expiration date, in
1327 yyyy-mm-dd or m-d-yyyy format
1329 Returns an HTML fragments for expiration date selection.
1335 #if ( ref($_[0]) ) {
1339 #my $prefix = $param->{'prefix'};
1340 #my $prefix = exists($param->{'prefix'}) ? $param->{'prefix'} : '';
1341 #my $date = exists($param->{'date'}) ? $param->{'date'} : '';
1343 my $date = scalar(@_) ? shift : '';
1345 my( $m, $y ) = ( 0, 0 );
1346 if ( $date =~ /^(\d{4})-(\d{2})-\d{2}$/ ) { #PostgreSQL date format
1347 ( $m, $y ) = ( $2, $1 );
1348 } elsif ( $date =~ /^(\d{1,2})-(\d{1,2}-)?(\d{4}$)/ ) {
1349 ( $m, $y ) = ( $1, $3 );
1351 my $return = qq!<SELECT NAME="$prefix!. qq!_month" SIZE="1">!;
1353 $return .= qq!<OPTION VALUE="$_"!;
1354 $return .= " SELECTED" if $_ == $m;
1357 $return .= qq!</SELECT>/<SELECT NAME="$prefix!. qq!_year" SIZE="1">!;
1359 my $thisYear = $t[5] + 1900;
1360 for ( ($thisYear > $y && $y > 0 ? $y : $thisYear) .. ($thisYear+10) ) {
1361 $return .= qq!<OPTION VALUE="$_"!;
1362 $return .= " SELECTED" if $_ == $y;
1365 $return .= "</SELECT>";
1370 =item popselector HASHREF | LIST
1372 Takes as input a hashref or list of key/value pairs with the following keys:
1378 Access number number
1382 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>.
1386 Returns an HTML fragment for access number selection.
1390 #horrible false laziness with FS/FS/svc_acct_pop.pm::popselector
1398 my $popnum = $param->{'popnum'};
1399 my $pops = $param->{'pops'};
1401 return '<INPUT TYPE="hidden" NAME="popnum" VALUE="">' unless @$pops;
1402 return $pops->[0]{city}. ', '. $pops->[0]{state}.
1403 ' ('. $pops->[0]{ac}. ')/'. $pops->[0]{exch}. '-'. $pops->[0]{loc}.
1404 '<INPUT TYPE="hidden" NAME="popnum" VALUE="'. $pops->[0]{popnum}. '">'
1405 if scalar(@$pops) == 1;
1408 my %popnum2pop = ();
1410 push @{ $pop{ $_->{state} }->{ $_->{ac} } }, $_;
1411 $popnum2pop{$_->{popnum}} = $_;
1416 function opt(what,href,text) {
1417 var optionName = new Option(text, href, false, false)
1418 var length = what.length;
1419 what.options[length] = optionName;
1423 my $init_popstate = $param->{'init_popstate'};
1424 if ( $init_popstate ) {
1425 $text .= '<INPUT TYPE="hidden" NAME="init_popstate" VALUE="'.
1426 $init_popstate. '">';
1429 function acstate_changed(what) {
1430 state = what.options[what.selectedIndex].text;
1431 what.form.popac.options.length = 0
1432 what.form.popac.options[0] = new Option("Area code", "-1", false, true);
1436 my @states = $init_popstate ? ( $init_popstate ) : keys %pop;
1437 foreach my $state ( sort { $a cmp $b } @states ) {
1438 $text .= "\nif ( state == \"$state\" ) {\n" unless $init_popstate;
1440 foreach my $ac ( sort { $a cmp $b } keys %{ $pop{$state} }) {
1441 $text .= "opt(what.form.popac, \"$ac\", \"$ac\");\n";
1442 if ($ac eq $param->{'popac'}) {
1443 $text .= "what.form.popac.options[what.form.popac.length-1].selected = true;\n";
1446 $text .= "}\n" unless $init_popstate;
1448 $text .= "popac_changed(what.form.popac)}\n";
1451 function popac_changed(what) {
1452 ac = what.options[what.selectedIndex].text;
1453 what.form.popnum.options.length = 0;
1454 what.form.popnum.options[0] = new Option("City", "-1", false, true);
1458 foreach my $state ( @states ) {
1459 foreach my $popac ( keys %{ $pop{$state} } ) {
1460 $text .= "\nif ( ac == \"$popac\" ) {\n";
1462 foreach my $pop ( @{$pop{$state}->{$popac}}) {
1463 my $o_popnum = $pop->{popnum};
1464 my $poptext = $pop->{city}. ', '. $pop->{state}.
1465 ' ('. $pop->{ac}. ')/'. $pop->{exch}. '-'. $pop->{loc};
1467 $text .= "opt(what.form.popnum, \"$o_popnum\", \"$poptext\");\n";
1468 if ($popnum == $o_popnum) {
1469 $text .= "what.form.popnum.options[what.form.popnum.length-1].selected = true;\n";
1477 $text .= "}\n</SCRIPT>\n";
1480 qq!<TABLE CELLPADDING="0"><TR><TD><SELECT NAME="acstate"! .
1481 qq!SIZE=1 onChange="acstate_changed(this)"><OPTION VALUE=-1>State!;
1482 $text .= "<OPTION" . ($_ eq $param->{'acstate'} ? " SELECTED" : "") .
1483 ">$_" foreach sort { $a cmp $b } @states;
1484 $text .= '</SELECT>'; #callback? return 3 html pieces? #'</TD>';
1487 qq!<SELECT NAME="popac" SIZE=1 onChange="popac_changed(this)">!.
1488 qq!<OPTION>Area code</SELECT></TR><TR VALIGN="top">!;
1490 $text .= qq!<TR><TD><SELECT NAME="popnum" SIZE=1 STYLE="width: 20em"><OPTION>City!;
1493 #comment this block to disable initial list polulation
1494 my @initial_select = ();
1495 if ( scalar( @$pops ) > 100 ) {
1496 push @initial_select, $popnum2pop{$popnum} if $popnum2pop{$popnum};
1498 @initial_select = @$pops;
1500 foreach my $pop ( sort { $a->{state} cmp $b->{state} } @initial_select ) {
1501 $text .= qq!<OPTION VALUE="!. $pop->{popnum}. '"'.
1502 ( ( $popnum && $pop->{popnum} == $popnum ) ? ' SELECTED' : '' ). ">".
1503 $pop->{city}. ', '. $pop->{state}.
1504 ' ('. $pop->{ac}. ')/'. $pop->{exch}. '-'. $pop->{loc};
1507 $text .= qq!</SELECT></TD></TR></TABLE>!;
1513 =item domainselector HASHREF | LIST
1515 Takes as input a hashref or list of key/value pairs with the following keys:
1525 Service number of the selected item.
1529 Returns an HTML fragment for domain selection.
1533 sub domainselector {
1540 my $domsvc= $param->{'domsvc'};
1542 domain_select_hash(map {$_ => $param->{$_}} qw(pkgnum svcpart pkgpart) );
1543 my $domains = $rv->{'domains'};
1544 $domsvc = $rv->{'domsvc'} unless $domsvc;
1546 return '<INPUT TYPE="hidden" NAME="domsvc" VALUE="">'
1547 unless scalar(keys %$domains);
1549 if (scalar(keys %$domains) == 1) {
1551 foreach(keys %$domains) {
1554 return '<TR><TD ALIGN="right">Domain</TD><TD>'. $domains->{$key}.
1555 '<INPUT TYPE="hidden" NAME="domsvc" VALUE="'. $key. '"></TD></TR>'
1558 my $text .= qq!<TR><TD ALIGN="right">Domain</TD><TD><SELECT NAME="domsvc" SIZE=1 STYLE="width: 20em"><OPTION>(Choose Domain)!;
1561 foreach my $domain ( sort { $domains->{$a} cmp $domains->{$b} } keys %$domains ) {
1562 $text .= qq!<OPTION VALUE="!. $domain. '"'.
1563 ( ( $domsvc && $domain == $domsvc ) ? ' SELECTED' : '' ). ">".
1564 $domains->{$domain};
1567 $text .= qq!</SELECT></TD></TR>!;
1573 =item didselector HASHREF | LIST
1575 Takes as input a hashref or list of key/value pairs with the following keys:
1585 Returns an HTML fragment for DID selection.
1597 my $rv = mason_comp( 'comp'=>'/elements/select-did.html',
1598 'args'=>[ %$param ],
1602 $rv->{'error'} || $rv->{'output'};
1608 =head1 RESELLER FUNCTIONS
1610 Note: Resellers can also use the B<signup_info> and B<new_customer> functions
1611 with their active session, and the B<customer_info> and B<order_pkg> functions
1612 with their active session and an additional I<custnum> parameter.
1620 =item agent_list_customers
1628 L<freeside-selfservice-clientd>, L<freeside-selfservice-server>