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 'customer_recurring' => 'MyAccount/customer_recurring',
38 'contact_passwd' => 'MyAccount/contact/contact_passwd',
39 'list_contacts' => 'MyAccount/contact/list_contacts',
40 'edit_contact' => 'MyAccount/contact/edit_contact',
41 'delete_contact' => 'MyAccount/contact/delete_contact',
42 'new_contact' => 'MyAccount/contact/new_contact',
44 'billing_history' => 'MyAccount/billing_history',
45 'edit_info' => 'MyAccount/edit_info', #add to ss cgi!
46 'invoice' => 'MyAccount/invoice',
47 'invoice_pdf' => 'MyAccount/invoice_pdf',
48 'legacy_invoice' => 'MyAccount/legacy_invoice',
49 'legacy_invoice_pdf' => 'MyAccount/legacy_invoice_pdf',
50 'invoice_logo' => 'MyAccount/invoice_logo',
51 'list_invoices' => 'MyAccount/list_invoices', #?
52 'list_payments' => 'MyAccount/list_payments',
53 'payment_receipt' => 'MyAccount/payment_receipt',
54 'list_payby' => 'MyAccount/list_payby',
55 'insert_payby' => 'MyAccount/insert_payby',
56 'update_payby' => 'MyAccount/update_payby',
57 'delete_payby' => 'MyAccount/delete_payby',
58 'cancel' => 'MyAccount/cancel', #add to ss cgi!
59 'payment_info' => 'MyAccount/payment_info',
60 'payment_info_renew_info' => 'MyAccount/payment_info_renew_info',
61 'process_payment' => 'MyAccount/process_payment',
62 'store_payment' => 'MyAccount/store_payment',
63 'process_stored_payment' => 'MyAccount/process_stored_payment',
64 'process_payment_order_pkg' => 'MyAccount/process_payment_order_pkg',
65 'process_payment_change_pkg' => 'MyAccount/process_payment_change_pkg',
66 'process_payment_order_renew' => 'MyAccount/process_payment_order_renew',
67 'process_prepay' => 'MyAccount/process_prepay',
68 'realtime_collect' => 'MyAccount/realtime_collect',
69 'list_pkgs' => 'MyAccount/list_pkgs', #add to ss (added?)
70 'list_svcs' => 'MyAccount/list_svcs', #add to ss (added?)
71 'list_svc_usage' => 'MyAccount/list_svc_usage',
72 'svc_status_html' => 'MyAccount/svc_status_html',
73 'svc_status_hash' => 'MyAccount/svc_status_hash',
74 'set_svc_status_hash' => 'MyAccount/set_svc_status_hash',
75 'set_svc_status_listadd' => 'MyAccount/set_svc_status_listadd',
76 'set_svc_status_listdel' => 'MyAccount/set_svc_status_listdel',
77 'set_svc_status_vacationadd'=> 'MyAccount/set_svc_status_vacationadd',
78 'set_svc_status_vacationdel'=> 'MyAccount/set_svc_status_vacationdel',
79 'acct_forward_info' => 'MyAccount/acct_forward_info',
80 'process_acct_forward' => 'MyAccount/process_acct_forward',
81 'list_dsl_devices' => 'MyAccount/list_dsl_devices',
82 'add_dsl_device' => 'MyAccount/add_dsl_device',
83 'delete_dsl_device' => 'MyAccount/delete_dsl_device',
84 'port_graph' => 'MyAccount/port_graph',
85 'list_cdr_usage' => 'MyAccount/list_cdr_usage',
86 'list_support_usage' => 'MyAccount/list_support_usage',
87 'order_pkg' => 'MyAccount/order_pkg', #add to ss cgi!
88 'change_pkg' => 'MyAccount/change_pkg',
89 'order_recharge' => 'MyAccount/order_recharge',
90 'renew_info' => 'MyAccount/renew_info',
91 'order_renew' => 'MyAccount/order_renew',
92 'cancel_pkg' => 'MyAccount/cancel_pkg', #add to ss cgi!
93 'suspend_pkg' => 'MyAccount/suspend_pkg', #add to ss cgi!
94 'charge' => 'MyAccount/charge', #?
95 'part_svc_info' => 'MyAccount/part_svc_info',
96 'provision_acct' => 'MyAccount/provision_acct',
97 'provision_phone' => 'MyAccount/provision_phone',
98 'provision_pbx' => 'MyAccount/provision_pbx',
99 'provision_external' => 'MyAccount/provision_external',
100 'provision_forward' => 'MyAccount/provision_forward',
101 'unprovision_svc' => 'MyAccount/unprovision_svc',
102 'myaccount_passwd' => 'MyAccount/myaccount_passwd',
103 'reset_passwd' => 'MyAccount/reset_passwd',
104 'check_reset_passwd' => 'MyAccount/check_reset_passwd',
105 'process_reset_passwd' => 'MyAccount/process_reset_passwd',
106 'validate_passwd' => 'MyAccount/validate_passwd',
107 'list_tickets' => 'MyAccount/list_tickets',
108 'create_ticket' => 'MyAccount/create_ticket',
109 'get_ticket' => 'MyAccount/get_ticket',
110 'adjust_ticket_priority' => 'MyAccount/adjust_ticket_priority',
111 'did_report' => 'MyAccount/did_report',
112 'signup_info' => 'Signup/signup_info',
113 'skin_info' => 'MyAccount/skin_info',
114 'access_info' => 'MyAccount/access_info',
115 'domain_select_hash' => 'Signup/domain_select_hash', # expose?
116 'new_customer' => 'Signup/new_customer',
117 'new_customer_minimal' => 'Signup/new_customer_minimal',
118 'capture_payment' => 'Signup/capture_payment',
119 'new_prospect' => 'Signup/new_prospect',
120 #N/A 'clear_signup_cache' => 'Signup/clear_cache',
121 'new_agent' => 'Agent/new_agent',
122 'agent_login' => 'Agent/agent_login',
123 'agent_logout' => 'Agent/agent_logout',
124 'agent_info' => 'Agent/agent_info',
125 'agent_list_customers' => 'Agent/agent_list_customers',
126 'check_username' => 'Agent/check_username',
127 'suspend_username' => 'Agent/suspend_username',
128 'unsuspend_username' => 'Agent/unsuspend_username',
129 'mason_comp' => 'MasonComponent/mason_comp',
130 'call_time' => 'PrepaidPhone/call_time',
131 'call_time_nanpa' => 'PrepaidPhone/call_time_nanpa',
132 'phonenum_balance' => 'PrepaidPhone/phonenum_balance',
134 'start_thirdparty' => 'MyAccount/start_thirdparty',
135 'finish_thirdparty' => 'MyAccount/finish_thirdparty',
137 'list_quotations' => 'MyAccount/quotation/list_quotations',
138 'quotation_new' => 'MyAccount/quotation/quotation_new',
139 'quotation_delete' => 'MyAccount/quotation/quotation_delete',
140 'quotation_info' => 'MyAccount/quotation/quotation_info',
141 'quotation_print' => 'MyAccount/quotation/quotation_print',
142 'quotation_add_pkg' => 'MyAccount/quotation/quotation_add_pkg',
143 'quotation_remove_pkg' => 'MyAccount/quotation/quotation_remove_pkg',
144 'quotation_order' => 'MyAccount/quotation/quotation_order',
146 'freesideinc_service' => 'Freeside/freesideinc_service',
151 qw( regionselector regionselector_hashref location_form
152 expselect popselector domainselector didselector
156 $ENV{'PATH'} ='/usr/bin:/usr/ucb:/bin';
157 $ENV{'SHELL'} = '/bin/sh';
158 $ENV{'IFS'} = " \t\n";
161 $ENV{'BASH_ENV'} = '';
163 #you can add BEGIN { $FS::SelfService::skip_uid_check = 1; }
164 #if you grant appropriate permissions to whatever user
165 my $freeside_uid = scalar(getpwnam('freeside'));
166 die "not running as the freeside user\n"
167 if $> != $freeside_uid && ! $skip_uid_check;
169 -e $dir or die "FATAL: $dir doesn't exist!";
170 -d $dir or die "FATAL: $dir isn't a directory!";
171 -r $dir or die "FATAL: Can't read $dir as freeside user!";
172 -x $dir or die "FATAL: $dir not searchable (executable) as freeside user!";
174 foreach my $autoload ( keys %autoload ) {
177 "sub $autoload { ". '
182 #warn scalar(@_). ": ". join(" / ", @_);
186 $param->{_packet} = \''. $autoload{$autoload}. '\';
188 simple_packet($param);
198 warn "sending ". $packet->{_packet}. " to server"
200 socket(SOCK, PF_UNIX, SOCK_STREAM, 0) or die "socket: $!";
201 connect(SOCK, sockaddr_un($socket)) or die "connect to $socket: $!";
202 nstore_fd($packet, \*SOCK) or die "can't send packet: $!";
205 #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
207 #block until there is a message on socket
208 # my $w = new IO::Select;
210 # my @wait = $w->can_read;
212 warn "reading message from server"
215 my $return = fd_retrieve(\*SOCK) or die "error reading result: $!";
216 die $return->{'_error'} if defined $return->{_error} && $return->{_error};
218 warn "returning message to client"
226 FS::SelfService - Freeside self-service API
230 # password and shell account changes
231 use FS::SelfService qw(passwd chfn chsh);
233 # "my account" functionality
234 use FS::SelfService qw( login customer_info invoice cancel payment_info process_payment );
236 #new-style login with an email address and password
237 # can also be used for svc_acct login, set $emailaddress to username@domain
238 my $rv = login ( { 'email' => $emailaddress,
239 'password' => $password,
242 if ( $rv->{'error'} ) {
243 #handle login error...
246 $session_id = $rv->{'session_id'};
249 #classic svc_acct-based login with separate username and password
250 my $rv = login( { 'username' => $username,
252 'password' => $password,
255 if ( $rv->{'error'} ) {
256 #handle login error...
259 $session_id = $rv->{'session_id'};
262 #svc_phone login with phone number and PIN
263 my $rv = login( { 'username' => $phone_number,
264 'domain' => 'svc_phone',
268 if ( $rv->{'error'} ) {
269 #handle login error...
272 $session_id = $rv->{'session_id'};
275 my $customer_info = customer_info( { 'session_id' => $session_id } );
277 my $payment_info = payment_info( { 'session_id' => $session_id } );
279 #!!! process_payment example
281 #!!! list_pkgs example
283 #ordering a package with an svc_acct service
284 my $rv = order_pkg( { 'session_id' => $session_id,
285 'pkgpart' => $pkgpart,
286 'svcpart' => $svcpart,
287 'username' => $username,
288 'domsvc' => $domsvc, #svcnum of svc_domain
289 '_password' => $password,
293 #!!! ordering a package with an svc_domain service example
295 #!!! ordering a package with an svc_phone service example
297 #!!! ordering a package with an svc_external service example
299 #!!! ordering a package with an svc_pbx service
301 #ordering a package with no service
302 my $rv = order_pkg( { 'session_id' => $session_id,
303 'pkgpart' => $pkgpart,
308 #quoting a package, then ordering after confirmation
310 my $rv = quotation_new({ 'session_id' => $session_id });
311 my $qnum = $rv->{quotationnum};
312 # add packages to the quotation
313 $rv = quotation_add_pkg({ 'session_id' => $session_id,
314 'quotationnum' => $qnum,
315 'pkgpart' => $pkgpart,
316 'quantity' => $quantity, # defaults to 1
318 # repeat until all packages are added
319 # view the pricing information
320 $rv = quotation_info({ 'session_id' => $session_id,
321 'quotationnum' => $qnum,
323 print "Total setup charges: ".$rv->{total_setup}."\n".
324 "Total recurring charges: ".$rv->{total_recur}."\n";
325 # quotation_info also provides a detailed breakdown of charges, in
328 # ask customer for confirmation, then:
329 $rv = quotation_order({ 'session_id' => $session_id,
330 'quotationnum' => $qnum,
333 #!!! cancel_pkg example
335 # signup functionality
336 use FS::SelfService qw( signup_info new_customer new_customer_minimal );
338 my $signup_info = signup_info;
340 $rv = new_customer( {
343 'company' => $company,
344 'address1' => $address1,
345 'address2' => $address2,
349 'country' => $country,
350 'daytime' => $daytime,
354 'payinfo' => $payinfo,
356 'paystart_month' => $paystart_month
357 'paystart_year' => $paystart_year,
358 'payissue' => $payissue,
360 'paydate' => $paydate,
361 'payname' => $payname,
362 'invoicing_list' => $invoicing_list,
363 'referral_custnum' => $referral_custnum,
364 'agentnum' => $agentnum,
365 'pkgpart' => $pkgpart,
367 'username' => $username,
368 '_password' => $password,
372 'phonenum' => $phonenum,
377 my $error = $rv->{'error'};
378 if ( $error eq '_decline' ) {
388 Use this API to implement your own client "self-service" module.
390 If you just want to customize the look of the existing "self-service" module,
393 =head1 PASSWORD, GECOS, SHELL CHANGING FUNCTIONS
399 Changes the password for an existing user in svc_acct. Takes a hash
400 reference with the following keys:
406 Username of the account (required)
410 Domain of the account (required)
414 Old password (required)
418 New password (required)
436 =head1 "MY ACCOUNT" FUNCTIONS
442 Creates a user session. Takes a hash reference as parameter with the
449 Email address (username@domain), instead of username and domain. Required for
450 contact-based self-service login, can also be used for svc_acct-based login.
466 Returns a hash reference with the following keys:
472 Empty on success, or an error message on errors.
476 Session identifier for successful logins
480 =item customer_info HASHREF
482 Returns general customer information.
484 Takes a hash reference as parameter with a single key: B<session_id>
486 Returns a hash reference with the following keys:
500 Array reference of hash references of open inoices. Each hash reference has
501 the following keys: invnum, date, owed
505 An HTML fragment containing shipping and billing addresses.
507 =item The following fields are also returned
509 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
513 =item customer_recurring HASHREF
515 Takes a hash reference as parameter with a single key B<session_id>
516 or keys B<agent_session_id> and B<custnum>.
518 Returns a hash reference with the keys error, custnum and display_recurring.
520 display_recurring is an arrayref of hashrefs with the following keys:
526 frequency of charge, in months unless units are specified
530 frequency of charge, suitable for display
534 amount charged at this frequency
538 =item edit_info HASHREF
540 Takes a hash reference as parameter with any of the following keys:
542 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
544 If a field exists, the customer record is updated with the new value of that
545 field. If a field does not exist, that field is not changed on the customer
548 Returns a hash reference with a single key, B<error>, empty on success, or an
549 error message on errors
551 =item invoice HASHREF
553 Returns an invoice. Takes a hash reference as parameter with two keys:
554 session_id and invnum
556 Returns a hash reference with the following keys:
562 Empty on success, or an error message on errors
574 =item list_invoices HASHREF
576 Returns a list of all customer invoices. Takes a hash reference with a single
579 Returns a hash reference with the following keys:
585 Empty on success, or an error message on errors
589 Reference to array of hash references with the following keys:
599 Invoice date, in UNIX epoch time
605 =item list_payments HASHREF
607 Returns a list of all customer payments. Takes a hash reference with a single
610 Returns a hash reference with the following keys:
616 Empty on success, or an error message on errors
620 Reference to array of hash references with the following keys:
630 Payument date, in UNIX epoch time
634 Payment date, in a human-readable format
638 Payment date, in a shorter human-readable format
646 Payment method: CARD, CHEK (electronic check), or BILL (physical check).
658 Processor for cards and electronic checks
672 =item list_payby HASHREF
674 Returns a list of all stored customer payment information (credit cards and
675 electronic check accounts). Takes a hash reference with a single key,
678 Returns a hash reference with the following keys:
684 Empty on success, or an error message on errors
688 Reference to array of hash references with the following keys:
696 Numeric weighting. Stored payment information with a lower weight is attempted
701 CARD (Automatic credit card), CHEK (Automatic electronic check), DCRD
702 (on-demand credit card) or DCHK (on-demand electronic check).
706 Masked credit card number (or, masked account and routing numbers)
710 Credit card expiration date
714 Exact name on card (or bank name, for electronic checks)
718 For electronic checks, bank state
722 For electronic checks, account type (Personal/Business, Checking/Savings)
728 =item insert_payby HASHREF
730 Adds new stored payment information for this customer. Takes a hash reference
731 with the following keys:
739 Numeric weighting. Stored payment information with a lower weight is attempted
744 CARD (Automatic credit card), CHEK (Automatic electronic check), DCRD
745 (on-demand credit card) or DCHK (on-demand electronic check).
749 Credit card number (or electronic check "account@routing")
753 CVV2 number / security code
757 Credit card expiration date
761 Exact name on card (or bank name, for electronic checks)
765 For electronic checks, bank state
769 For electronic checks, account type (i.e. "Personal Savings", "Personal Checking", "Business Checking")A
773 Optional IP address from which payment was submitted
777 If there is an error, returns a hash reference with a single key, B<error>,
778 otherwise returns a hash reference with a single key, B<custpaybynum>.
780 =item update_payby HASHREF
782 Updates stored payment information. Takes a hash reference with the same
783 keys as insert_payby, as well as B<custpaybynum> to specify which record
784 to update. All keys except B<session_id> and B<custpaybynum> are optional;
785 if omitted, the previous values in the record will be preserved.
787 If there is an error, returns a hash reference with a single key, B<error>,
788 otherwise returns a hash reference with a single key, B<custpaybynum>.
790 =item delete_payby HASHREF
792 Removes stored payment information. Takes a hash reference with two keys,
793 B<session_id> and B<custpaybynum>. Returns a hash reference with a single key,
794 B<error>, which is an error message or empty for successful removal.
798 Cancels this customer.
800 Takes a hash reference as parameter with a single key: B<session_id>
802 Returns a hash reference with a single key, B<error>, which is empty on
803 success or an error message on errors.
805 =item payment_info HASHREF
807 Returns information that may be useful in displaying a payment page.
809 Takes a hash reference as parameter with the following keys:
819 =item omit_cust_main_county
821 Optional, pass a true value to omit cust_main_county data for performance.
825 Returns a hash reference with the following keys:
831 Empty on success, or an error message on errors
839 Exact name on credit card (CARD/DCRD)
863 Customer's current default payment type.
867 For CARD/DCRD payment types, the card type (Visa card, MasterCard, Discover card, American Express card, etc.)
871 For CARD/DCRD payment types, the card number
875 For CARD/DCRD payment types, expiration month
879 For CARD/DCRD payment types, expiration year
881 =item cust_main_county
883 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.
887 Array reference of all states in the current default country.
891 Hash reference of card types; keys are card types, values are the exact strings
892 passed to the process_payment function
896 #this doesn't actually work yet
900 #Unique transaction identifier (prevents multiple charges), passed to the
901 #process_payment function
905 =item process_payment HASHREF
907 Processes a payment and possible change of address or payment type. Takes a
908 hash reference as parameter with the following keys:
922 If true, address and card information entered will be saved for subsequent
927 If true, future credit card payments will be done automatically (sets payby to
928 CARD). If false, future credit card payments will be done on-demand (sets
929 payby to DCRD). This option only has meaning if B<save> is set true.
957 Two-letter country code
965 Card expiration month
973 #this doesn't actually work yet
977 #Unique transaction identifier, returned from the payment_info function.
978 #Prevents multiple charges.
982 Returns a hash reference with a single key, B<error>, empty on success, or an
983 error message on errors.
985 =item process_payment_order_pkg
987 Combines the B<process_payment> and B<order_pkg> functions in one step. If the
988 payment processes sucessfully, the package is ordered. Takes a hash reference
989 as parameter with the keys of both methods.
991 Returns a hash reference with a single key, B<error>, empty on success, or an
992 error message on errors.
994 =item process_payment_change_pkg
996 Combines the B<process_payment> and B<change_pkg> functions in one step. If the
997 payment processes sucessfully, the package is ordered. Takes a hash reference
998 as parameter with the keys of both methods.
1000 Returns a hash reference with a single key, B<error>, empty on success, or an
1001 error message on errors.
1004 =item process_payment_order_renew
1006 Combines the B<process_payment> and B<order_renew> functions in one step. If
1007 the payment processes sucessfully, the renewal is processed. Takes a hash
1008 reference as parameter with the keys of both methods.
1010 Returns a hash reference with a single key, B<error>, empty on success, or an
1011 error message on errors.
1015 Returns package information for this customer. For more detail on services,
1018 Takes a hash reference as parameter with a single key: B<session_id>
1020 Returns a hash reference containing customer package information. The hash reference contains the following keys:
1030 Empty on success, or an error message on errors.
1032 =item cust_pkg HASHREF
1034 Array reference of hash references, each of which has the fields of a cust_pkg
1035 record (see L<FS::cust_pkg>) as well as the fields below. Note these are not
1036 the internal FS:: objects, but hash references of columns and values.
1040 =item part_pkg fields
1042 All fields of part_pkg for this specific cust_pkg (be careful with this
1043 information - it may reveal more about your available packages than you would
1044 like users to know in aggregate)
1048 #XXX pare part_pkg fields down to a more secure subset
1052 An array of hash references indicating information on unprovisioned services
1053 available for provisioning for this specific cust_pkg. Each has the following
1058 =item part_svc fields
1060 All fields of part_svc (be careful with this information - it may reveal more
1061 about your available packages than you would like users to know in aggregate)
1065 #XXX pare part_svc fields down to a more secure subset
1071 An array of hash references indicating information on the customer services
1072 already provisioned for this specific cust_pkg. Each has the following keys:
1078 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.
1084 Primary key for this service
1088 Service definition (see L<FS::part_svc>)
1092 Customer package (see L<FS::cust_pkg>)
1096 Blank if the service is not over limit, or the date the service exceeded its usage limit (as a UNIX timestamp).
1104 Returns service information for this customer.
1106 Takes a hash reference as parameter with a single key: B<session_id>
1108 Returns a hash reference containing customer package information. The hash reference contains the following keys:
1118 An array of hash references indicating information on all of this customer's
1119 services. Each has the following keys:
1125 Primary key for this service
1129 Name of this service
1133 Meaningful user-specific identifier for the service (i.e. username, domain, or
1138 Account (svc_acct) services also have the following keys:
1156 Upload bytes remaining
1160 Download bytes remaining
1164 Total bytes remaining
1166 =item recharge_amount
1170 =item recharge_seconds
1172 Number of seconds gained by recharge
1174 =item recharge_upbytes
1176 Number of upload bytes gained by recharge
1178 =item recharge_downbytes
1180 Number of download bytes gained by recharge
1182 =item recharge_totalbytes
1184 Number of total bytes gained by recharge
1192 Orders a package for this customer.
1194 If signup_server-realtime is set, bills the new package, attemps to collect
1195 payment and (for auto-payment customers) cancels the package if the payment is
1198 Takes a hash reference as parameter with the following keys:
1208 Package to order (see L<FS::part_pkg>).
1212 Quantity for this package order (default 1).
1214 =item run_bill_events
1216 If true, runs billing events for the customer after ordering and billing the
1217 package (signup_server-realtime must be set).
1221 Optional locationnum for this package order, for existing locations.
1223 Or, for new locations, pass the following fields: address1*, address2, city*,
1224 county, state*, zip*, country. (* = required in this case)
1226 (None of this is required at all if you are just ordering a package
1227 at the customer's existing default service location.)
1245 Service to order (see L<FS::part_svc>).
1247 Normally optional; required only to provision a non-svc_acct service, or if the
1248 package definition does not contain one svc_acct service definition with
1249 quantity 1 (it may contain others with quantity >1). A svcpart of "none" can
1250 also be specified to indicate that no initial service should be provisioned.
1254 Fields used when provisioning an svc_acct service:
1268 Optional security phrase
1272 Optional Access number number
1276 Fields used when provisioning an svc_domain service:
1286 Fields used when provisioning an svc_phone service:
1304 Fields used when provisioning an svc_external service:
1310 External numeric ID.
1314 External text title.
1318 Fields used when provisioning an svc_pbx service:
1332 Returns a hash reference with a single key, B<error>, empty on success, or an
1333 error message on errors. The special error '_decline' is returned for
1334 declined transactions.
1338 Changes a package for this customer.
1340 Takes a hash reference as parameter with the following keys:
1350 Existing customer package.
1354 New package to order (see L<FS::part_pkg>).
1358 Quantity for this package order (default 1).
1362 Returns a hash reference with the following keys:
1368 Empty on success, or an error message on errors.
1372 On success, the new pkgnum
1379 Provides useful info for early renewals.
1381 Takes a hash reference as parameter with the following keys:
1391 Returns a hash reference. On errors, it contains a single key, B<error>, with
1392 the error message. Otherwise, contains a single key, B<dates>, pointing to
1393 an array refernce of hash references. Each hash reference contains the
1400 (Future) Bill date. Indicates a future date for which billing could be run.
1401 Specified as an integer UNIX timestamp. Pass this value to the B<order_renew>
1404 =item bill_date_pretty
1406 (Future) Bill date as a human-readable string. (Convenience for display;
1407 subject to change, so best not to parse for the date.)
1411 Base amount which will be charged if renewed early as of this date.
1415 Renewal date; i.e. even-futher future date at which the customer will be paid
1416 through if the early renewal is completed with the given B<bill-date>.
1417 Specified as an integer UNIX timestamp.
1419 =item renew_date_pretty
1421 Renewal date as a human-readable string. (Convenience for display;
1422 subject to change, so best not to parse for the date.)
1426 Package that will be renewed.
1430 Expiration date of the package that will be renewed.
1432 =item expire_date_pretty
1434 Expiration date of the package that will be renewed, as a human-readable
1435 string. (Convenience for display; subject to change, so best not to parse for
1442 Renews this customer early; i.e. runs billing for this customer in advance.
1444 Takes a hash reference as parameter with the following keys:
1454 Integer date as returned by the B<renew_info> function, indicating the advance
1455 date for which to run billing.
1459 Returns a hash reference with a single key, B<error>, empty on success, or an
1460 error message on errors.
1464 Cancels a package for this customer.
1466 Takes a hash reference as parameter with the following keys:
1476 pkgpart of package to cancel
1480 Optional date, for future cancellation (expiration) instead of immediate
1481 cancellation. Specified as an integer UNIX timestamp ("epoch time").
1485 Returns a hash reference with a single key, B<error>, empty on success, or an
1486 error message on errors.
1488 =item provision_acct
1490 Provisions an account (svc_acct).
1492 Takes a hash reference as parameter with the following keys:
1502 pkgnum of package into which this service is provisioned
1506 svcpart or service definition to provision
1516 =item provision_phone
1518 Provisions a phone number (svc_phone).
1520 Takes a hash reference as parameter with the following keys:
1530 pkgnum of package into which this service is provisioned
1534 svcpart or service definition to provision
1554 E911 Address (optional)
1560 Provisions a customer PBX (svc_pbx).
1562 Takes a hash reference as parameter with the following keys:
1572 pkgnum of package into which this service is provisioned
1576 svcpart or service definition to provision
1582 =item max_extensions
1584 =item max_simultaneous
1590 =item provision_external
1592 Provisions an external service (svc_external).
1594 Takes a hash reference as parameter with the following keys:
1604 pkgnum of package into which this service is provisioned
1608 svcpart or service definition to provision
1618 =head2 "MY ACCOUNT" CONTACT FUNCTIONS
1622 =item contact_passwd
1624 Changes the password for the currently-logged in contact.
1626 Takes a hash reference as parameter with the following keys:
1636 Returns a hash reference with a single parameter, B<error>, which contains an
1637 error message, or empty on success.
1641 Takes a hash reference as parameter with a single key, B<session_id>.
1643 Returns a hash reference with two parameters: B<error>, which contains an error
1644 message, or empty on success, and B<contacts>, a list of contacts.
1646 B<contacts> is an array reference of hash references (i.e. an array of structs,
1647 in XML-RPC). Each hash reference (struct) has the following keys:
1655 Contact class name (contact type).
1667 Position ("Director of Silly Walks"), NOT honorific ("Mr." or "Mrs.")
1671 Comma-separated list of email addresses
1675 =item selfservice_access
1683 Updates information for the currently-logged in contact, or (optionally) the
1686 Takes a hash reference as parameter with the following keys:
1694 If already logged in as a contact, this is optional.
1704 Returns a hash reference with a single parameter, B<error>, which contains an
1705 error message, or empty on success.
1709 Creates a new contact.
1711 Takes a hash reference as parameter with the following keys:
1725 Optional contact classnum (TODO: or name)
1729 =item selfservice_access
1731 Y to enable self-service access
1737 Returns a hash reference with a single parameter, B<error>, which contains an
1738 error message, or empty on success.
1740 =item delete_contact
1742 Deletes a contact. (Note: Cannot at this time delete the currently-logged in
1745 Takes a hash reference as parameter with the following keys:
1755 Returns a hash reference with a single parameter, B<error>, which contains an
1756 error message, or empty on success.
1760 =head2 "MY ACCOUNT" QUOTATION FUNCTIONS
1762 All of these functions require the user to be logged in, and the 'session_id'
1763 key to be included in the argument hashref.`
1767 =item list_quotations HASHREF
1769 Returns a hashref listing this customer's active self-service quotations.
1776 an arrayref containing an element for each quotation.
1784 the date it was started
1788 the number of packages
1792 the sum of setup fees
1796 the sum of recurring charges
1800 =item quotation_new HASHREF
1802 Creates an empty quotation and returns a hashref containing 'quotationnum',
1803 the primary key of the new quotation.
1805 =item quotation_delete HASHREF
1807 Disables (does not really delete) a quotation. Takes the following arguments:
1813 =item quotationnum - the quotation to delete
1817 Returns 'error' => a string, which will be empty on success.
1819 =item quotation_info HASHREF
1821 Returns total and detailed pricing information on a quotation.
1823 Takes the following arguments:
1829 =item quotationnum - the quotation to return
1833 Returns a hashref containing:
1835 - total_setup, the total of setup fees (and their taxes)
1836 - total_recur, the total of all recurring charges (and their taxes)
1837 - sections, an arrayref containing an element for each quotation section.
1838 - description, a line of text describing the group of charges
1839 - subtotal, the total of charges in this group (if appropriate)
1840 - detail_items, an arrayref of line items
1841 - pkgnum, the reference number of the package
1842 - description, the package name (or tax name)
1844 - amount, the amount charged
1845 If the detail item represents a subtotal, it will instead contain:
1846 - total_item: description of the subtotal
1847 - total_amount: the subtotal amount
1850 =item quotation_print HASHREF
1852 Renders the quotation as HTML or PDF. Takes the following arguments:
1858 =item quotationnum - the quotation to return
1860 =item format - 'html' or 'pdf'
1864 Returns a hashref containing 'document', the contents of the file.
1866 =item quotation_add_pkg HASHREF
1868 Adds a package to a quotation. Takes the following arguments:
1874 =item pkgpart - the package to add
1876 =item quotationnum - the quotation to add it to
1878 =item quantity - the package quantity (defaults to 1)
1880 =item address1, address2, city, state, zip, country - address fields to set
1881 the service location
1885 Returns 'error' => a string, which will be empty on success.
1887 =item quotation_remove_pkg HASHREF
1889 Removes a package from a quotation. Takes the following arguments:
1895 =item pkgnum - the primary key (quotationpkgnum) of the package to remove
1897 =item quotationnum - the quotation to remove it from
1901 Returns 'error' => a string, which will be empty on success.
1903 =item quotation_order HASHREF
1905 Converts the packages in a quotation into real packages. Takes the following
1908 Takes the following arguments:
1914 =item quotationnum - the quotation to order
1920 =head1 SIGNUP FUNCTIONS
1924 =item signup_info HASHREF
1926 Takes a hash reference as parameter with the following keys:
1930 =item session_id - Optional agent/reseller interface session
1934 Returns a hash reference containing information that may be useful in
1935 displaying a signup page. The hash reference contains the following keys:
1939 =item cust_main_county
1941 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.
1945 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
1946 an agentnum specified explicitly via reseller interface session_id in the
1951 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.
1953 =item agentnum2part_pkg
1955 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.
1959 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.
1961 =item security_phrase
1963 True if the "security_phrase" feature is enabled
1967 Array reference of acceptable payment types for signup
1973 credit card - automatic
1977 credit card - on-demand - version 1.5+ only
1981 electronic check - automatic
1985 electronic check - on-demand - version 1.5+ only
1993 billing, not recommended for signups
1997 free, definitely not recommended for signups
2001 special billing type: applies a credit (see FS::prepay_credit) and sets billing type to BILL
2007 True if CVV features are available (1.5+ or 1.4.2 with CVV schema patch)
2011 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".
2017 =item countrydefault
2023 =item new_customer_minimal HASHREF
2025 Creates a new customer.
2027 Current differences from new_customer: An address is not required. promo_code
2028 and reg_code are not supported. If invoicing_list and _password is passed, a
2029 contact will be created with self-service access (no pkgpart or username is
2030 necessary). No initial billing is run (this may change in a future version).
2032 Takes a hash reference as parameter with the following keys:
2038 first name (required)
2042 last name (required)
2046 (not typically collected; mostly used for ACH transactions)
2078 Daytime phone number
2082 Evening phone number
2090 CARD, DCRD, CHEK, DCHK, LECB, BILL, COMP or PREPAY (see L</signup_info> (required)
2094 Card number for CARD/DCRD, account_number@aba_number for CHEK/DCHK, prepaid "pin" for PREPAY, purchase order number for BILL
2098 Credit card CVV2 number (1.5+ or 1.4.2 with CVV schema patch)
2102 Expiration date for CARD/DCRD
2106 Exact name on credit card for CARD/DCRD, bank name for CHEK/DCHK
2108 =item invoicing_list
2110 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),
2112 =item referral_custnum
2114 referring customer number
2122 pkgpart of initial package
2138 Access number (index, not the literal number)
2142 Country code (to be provisioned as a service)
2146 Phone number (to be provisioned as a service)
2154 Returns a hash reference with the following keys:
2160 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)
2164 =item new_customer HASHREF
2166 Creates a new customer. Takes a hash reference as parameter with the
2173 first name (required)
2177 last name (required)
2181 (not typically collected; mostly used for ACH transactions)
2187 =item address1 (required)
2195 =item city (required)
2203 =item state (required)
2207 =item zip (required)
2223 Optional shipping address fields. If sending an optional shipping address,
2224 ship_address1, ship_city, ship_state and ship_zip are required.
2228 Daytime phone number
2232 Evening phone number
2240 CARD, DCRD, CHEK, DCHK, LECB, BILL, COMP or PREPAY (see L</signup_info> (required)
2244 Card number for CARD/DCRD, account_number@aba_number for CHEK/DCHK, prepaid "pin" for PREPAY, purchase order number for BILL
2248 Credit card CVV2 number (1.5+ or 1.4.2 with CVV schema patch)
2252 Expiration date for CARD/DCRD
2256 Exact name on credit card for CARD/DCRD, bank name for CHEK/DCHK
2258 =item invoicing_list
2260 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),
2262 =item referral_custnum
2264 referring customer number
2272 pkgpart of initial package
2288 Access number (index, not the literal number)
2292 Country code (to be provisioned as a service)
2296 Phone number (to be provisioned as a service)
2304 Returns a hash reference with the following keys:
2310 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)
2314 =item regionselector HASHREF | LIST
2316 Takes as input a hashref or list of key/value pairs with the following keys:
2320 =item selected_county
2322 Currently selected county
2324 =item selected_state
2326 Currently selected state
2328 =item selected_country
2330 Currently selected country
2334 Specify a unique prefix string if you intend to use the HTML output multiple time son one page.
2338 Specify a javascript subroutine to call on changes
2344 =item default_country
2350 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>.
2354 Returns a list consisting of three HTML fragments for county selection,
2355 state selection and country selection, respectively.
2359 #false laziness w/FS::cust_main_county (this is currently the "newest" version)
2360 sub regionselector {
2367 $param->{'selected_country'} ||= $param->{'default_country'};
2368 $param->{'selected_state'} ||= $param->{'default_state'};
2370 my $prefix = exists($param->{'prefix'}) ? $param->{'prefix'} : '';
2374 my %cust_main_county;
2376 # unless ( @cust_main_county ) { #cache
2377 #@cust_main_county = qsearch('cust_main_county', {} );
2378 #foreach my $c ( @cust_main_county ) {
2379 foreach my $c ( @{ $param->{'locales'} } ) {
2380 #$countyflag=1 if $c->county;
2381 $countyflag=1 if $c->{county};
2382 #push @{$cust_main_county{$c->country}{$c->state}}, $c->county;
2383 #$cust_main_county{$c->country}{$c->state}{$c->county} = 1;
2384 $cust_main_county{$c->{country}}{$c->{state}}{$c->{county}} = 1;
2387 $countyflag=1 if $param->{selected_county};
2389 my $script_html = <<END;
2391 function opt(what,value,text) {
2392 var optionName = new Option(text, value, false, false);
2393 var length = what.length;
2394 what.options[length] = optionName;
2396 function ${prefix}country_changed(what) {
2397 country = what.options[what.selectedIndex].text;
2398 for ( var i = what.form.${prefix}state.length; i >= 0; i-- )
2399 what.form.${prefix}state.options[i] = null;
2401 #what.form.${prefix}state.options[0] = new Option('', '', false, true);
2403 foreach my $country ( sort keys %cust_main_county ) {
2404 $script_html .= "\nif ( country == \"$country\" ) {\n";
2405 foreach my $state ( sort keys %{$cust_main_county{$country}} ) {
2406 my $text = $state || '(n/a)';
2407 $script_html .= qq!opt(what.form.${prefix}state, "$state", "$text");\n!;
2409 $script_html .= "}\n";
2412 $script_html .= <<END;
2414 function ${prefix}state_changed(what) {
2417 if ( $countyflag ) {
2418 $script_html .= <<END;
2419 state = what.options[what.selectedIndex].text;
2420 country = what.form.${prefix}country.options[what.form.${prefix}country.selectedIndex].text;
2421 for ( var i = what.form.${prefix}county.length; i >= 0; i-- )
2422 what.form.${prefix}county.options[i] = null;
2425 foreach my $country ( sort keys %cust_main_county ) {
2426 $script_html .= "\nif ( country == \"$country\" ) {\n";
2427 foreach my $state ( sort keys %{$cust_main_county{$country}} ) {
2428 $script_html .= "\nif ( state == \"$state\" ) {\n";
2429 #foreach my $county ( sort @{$cust_main_county{$country}{$state}} ) {
2430 foreach my $county ( sort keys %{$cust_main_county{$country}{$state}} ) {
2431 my $text = $county || '(n/a)';
2433 qq!opt(what.form.${prefix}county, "$county", "$text");\n!;
2435 $script_html .= "}\n";
2437 $script_html .= "}\n";
2441 $script_html .= <<END;
2446 my $county_html = $script_html;
2447 if ( $countyflag ) {
2448 $county_html .= qq!<SELECT NAME="${prefix}county" onChange="$param->{'onchange'}">!;
2449 foreach my $county (
2450 sort keys %{ $cust_main_county{$param->{'selected_country'}}{$param->{'selected_state'}} }
2452 my $text = $county || '(n/a)';
2453 $county_html .= qq!<OPTION VALUE="$county"!.
2454 ($county eq $param->{'selected_county'} ?
2461 $county_html .= '</SELECT>';
2464 qq!<INPUT TYPE="hidden" NAME="${prefix}county" VALUE="$param->{'selected_county'}">!;
2467 my $state_html = qq!<SELECT NAME="${prefix}state" !.
2468 qq!onChange="${prefix}state_changed(this); $param->{'onchange'}">!;
2469 foreach my $state ( sort keys %{ $cust_main_county{$param->{'selected_country'}} } ) {
2470 my $text = $state || '(n/a)';
2471 my $selected = $state eq $param->{'selected_state'} ? 'SELECTED' : '';
2472 $state_html .= "\n<OPTION $selected VALUE=\"$state\">$text</OPTION>"
2474 $state_html .= '</SELECT>';
2476 my $country_html = '';
2477 if ( scalar( keys %cust_main_county ) > 1 ) {
2479 $country_html = qq(<SELECT NAME="${prefix}country" ).
2480 qq(onChange="${prefix}country_changed(this); ).
2481 $param->{'onchange'}.
2484 my $countrydefault = $param->{default_country} || 'US';
2485 foreach my $country (
2486 sort { ($b eq $countrydefault) <=> ($a eq $countrydefault) or $a cmp $b }
2487 keys %cust_main_county
2489 my $selected = $country eq $param->{'selected_country'}
2492 $country_html .= "\n<OPTION $selected>$country</OPTION>"
2494 $country_html .= '</SELECT>';
2497 $country_html = qq(<INPUT TYPE="hidden" NAME="${prefix}country" ).
2498 ' VALUE="'. (keys %cust_main_county )[0]. '">';
2502 ($county_html, $state_html, $country_html);
2506 sub regionselector_hashref {
2507 my ($county_html, $state_html, $country_html) = regionselector(@_);
2509 'county_html' => $county_html,
2510 'state_html' => $state_html,
2511 'country_html' => $country_html,
2515 =item location_form HASHREF | LIST
2517 Takes as input a hashref or list of key/value pairs with the following keys:
2523 Current customer session_id
2527 Omit red asterisks from required fields.
2529 =item address1_label
2531 Label for first address line.
2535 Returns an HTML fragment for a location form (address, city, state, zip,
2548 my $session_id = delete $param->{'session_id'};
2550 my $rv = mason_comp( 'session_id' => $session_id,
2551 'comp' => '/elements/location.html',
2552 'args' => [ %$param ],
2556 $rv->{'error'} || $rv->{'output'};
2561 #=item expselect HASHREF | LIST
2563 #Takes as input a hashref or list of key/value pairs with the following keys:
2567 #=item prefix - Specify a unique prefix string if you intend to use the HTML output multiple time son one page.
2569 #=item date - current date, in yyyy-mm-dd or m-d-yyyy format
2573 =item expselect PREFIX [ DATE ]
2575 Takes as input a unique prefix string and the current expiration date, in
2576 yyyy-mm-dd or m-d-yyyy format
2578 Returns an HTML fragments for expiration date selection.
2584 #if ( ref($_[0]) ) {
2588 #my $prefix = $param->{'prefix'};
2589 #my $prefix = exists($param->{'prefix'}) ? $param->{'prefix'} : '';
2590 #my $date = exists($param->{'date'}) ? $param->{'date'} : '';
2592 my $date = scalar(@_) ? shift : '';
2594 my( $m, $y ) = ( 0, 0 );
2595 if ( $date =~ /^(\d{4})-(\d{2})-\d{2}$/ ) { #PostgreSQL date format
2596 ( $m, $y ) = ( $2, $1 );
2597 } elsif ( $date =~ /^(\d{1,2})-(\d{1,2}-)?(\d{4}$)/ ) {
2598 ( $m, $y ) = ( $1, $3 );
2600 my $return = qq!<SELECT NAME="$prefix!. qq!_month" SIZE="1">!;
2602 $return .= qq!<OPTION VALUE="$_"!;
2603 $return .= " SELECTED" if $_ == $m;
2606 $return .= qq!</SELECT>/<SELECT NAME="$prefix!. qq!_year" SIZE="1">!;
2608 my $thisYear = $t[5] + 1900;
2609 for ( ($thisYear > $y && $y > 0 ? $y : $thisYear) .. ($thisYear+10) ) {
2610 $return .= qq!<OPTION VALUE="$_"!;
2611 $return .= " SELECTED" if $_ == $y;
2614 $return .= "</SELECT>";
2619 =item popselector HASHREF | LIST
2621 Takes as input a hashref or list of key/value pairs with the following keys:
2627 Access number number
2631 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>.
2635 Returns an HTML fragment for access number selection.
2639 #horrible false laziness with FS/FS/svc_acct_pop.pm::popselector
2647 my $popnum = $param->{'popnum'};
2648 my $pops = $param->{'pops'};
2650 return '<INPUT TYPE="hidden" NAME="popnum" VALUE="">' unless @$pops;
2651 return $pops->[0]{city}. ', '. $pops->[0]{state}.
2652 ' ('. $pops->[0]{ac}. ')/'. $pops->[0]{exch}. '-'. $pops->[0]{loc}.
2653 '<INPUT TYPE="hidden" NAME="popnum" VALUE="'. $pops->[0]{popnum}. '">'
2654 if scalar(@$pops) == 1;
2657 my %popnum2pop = ();
2659 push @{ $pop{ $_->{state} }->{ $_->{ac} } }, $_;
2660 $popnum2pop{$_->{popnum}} = $_;
2665 function opt(what,href,text) {
2666 var optionName = new Option(text, href, false, false)
2667 var length = what.length;
2668 what.options[length] = optionName;
2672 my $init_popstate = $param->{'init_popstate'};
2673 if ( $init_popstate ) {
2674 $text .= '<INPUT TYPE="hidden" NAME="init_popstate" VALUE="'.
2675 $init_popstate. '">';
2678 function acstate_changed(what) {
2679 state = what.options[what.selectedIndex].text;
2680 what.form.popac.options.length = 0
2681 what.form.popac.options[0] = new Option("Area code", "-1", false, true);
2685 my @states = $init_popstate ? ( $init_popstate ) : keys %pop;
2686 foreach my $state ( sort { $a cmp $b } @states ) {
2687 $text .= "\nif ( state == \"$state\" ) {\n" unless $init_popstate;
2689 foreach my $ac ( sort { $a cmp $b } keys %{ $pop{$state} }) {
2690 $text .= "opt(what.form.popac, \"$ac\", \"$ac\");\n";
2691 if ($ac eq $param->{'popac'}) {
2692 $text .= "what.form.popac.options[what.form.popac.length-1].selected = true;\n";
2695 $text .= "}\n" unless $init_popstate;
2697 $text .= "popac_changed(what.form.popac)}\n";
2700 function popac_changed(what) {
2701 ac = what.options[what.selectedIndex].text;
2702 what.form.popnum.options.length = 0;
2703 what.form.popnum.options[0] = new Option("City", "-1", false, true);
2707 foreach my $state ( @states ) {
2708 foreach my $popac ( keys %{ $pop{$state} } ) {
2709 $text .= "\nif ( ac == \"$popac\" ) {\n";
2711 foreach my $pop ( @{$pop{$state}->{$popac}}) {
2712 my $o_popnum = $pop->{popnum};
2713 my $poptext = $pop->{city}. ', '. $pop->{state}.
2714 ' ('. $pop->{ac}. ')/'. $pop->{exch}. '-'. $pop->{loc};
2716 $text .= "opt(what.form.popnum, \"$o_popnum\", \"$poptext\");\n";
2717 if ($popnum == $o_popnum) {
2718 $text .= "what.form.popnum.options[what.form.popnum.length-1].selected = true;\n";
2726 $text .= "}\n</SCRIPT>\n";
2728 $param->{'acstate'} = '' unless defined($param->{'acstate'});
2731 qq!<TABLE CELLPADDING="0"><TR><TD><SELECT NAME="acstate"! .
2732 qq!SIZE=1 onChange="acstate_changed(this)"><OPTION VALUE=-1>State!;
2733 $text .= "<OPTION" . ($_ eq $param->{'acstate'} ? " SELECTED" : "") .
2734 ">$_" foreach sort { $a cmp $b } @states;
2735 $text .= '</SELECT>'; #callback? return 3 html pieces? #'</TD>';
2738 qq!<SELECT NAME="popac" SIZE=1 onChange="popac_changed(this)">!.
2739 qq!<OPTION>Area code</SELECT></TR><TR VALIGN="top">!;
2741 $text .= qq!<TR><TD><SELECT NAME="popnum" SIZE=1 STYLE="width: 20em"><OPTION>City!;
2744 #comment this block to disable initial list polulation
2745 my @initial_select = ();
2746 if ( scalar( @$pops ) > 100 ) {
2747 push @initial_select, $popnum2pop{$popnum} if $popnum2pop{$popnum};
2749 @initial_select = @$pops;
2751 foreach my $pop ( sort { $a->{state} cmp $b->{state} } @initial_select ) {
2752 $text .= qq!<OPTION VALUE="!. $pop->{popnum}. '"'.
2753 ( ( $popnum && $pop->{popnum} == $popnum ) ? ' SELECTED' : '' ). ">".
2754 $pop->{city}. ', '. $pop->{state}.
2755 ' ('. $pop->{ac}. ')/'. $pop->{exch}. '-'. $pop->{loc};
2758 $text .= qq!</SELECT></TD></TR></TABLE>!;
2764 =item domainselector HASHREF | LIST
2766 Takes as input a hashref or list of key/value pairs with the following keys:
2776 Service number of the selected item.
2780 Returns an HTML fragment for domain selection.
2784 sub domainselector {
2791 my $domsvc= $param->{'domsvc'};
2793 domain_select_hash(map {$_ => $param->{$_}} qw(pkgnum svcpart pkgpart) );
2794 my $domains = $rv->{'domains'};
2795 $domsvc = $rv->{'domsvc'} unless $domsvc;
2797 return '<INPUT TYPE="hidden" NAME="domsvc" VALUE="">'
2798 unless scalar(keys %$domains);
2800 if (scalar(keys %$domains) == 1) {
2802 foreach(keys %$domains) {
2805 return '<TR><TD ALIGN="right">Domain</TD><TD>'. $domains->{$key}.
2806 '<INPUT TYPE="hidden" NAME="domsvc" VALUE="'. $key. '"></TD></TR>'
2809 my $text .= qq!<TR><TD ALIGN="right">Domain</TD><TD><SELECT NAME="domsvc" SIZE=1 STYLE="width: 20em">!;
2811 $text .= '<OPTION>(Choose Domain)' unless $domsvc;
2813 foreach my $domain ( sort { $domains->{$a} cmp $domains->{$b} } keys %$domains ) {
2814 $text .= qq!<OPTION VALUE="!. $domain. '"'.
2815 ( ( $domsvc && $domain == $domsvc ) ? ' SELECTED' : '' ). ">".
2816 $domains->{$domain};
2819 $text .= qq!</SELECT></TD></TR>!;
2825 =item didselector HASHREF | LIST
2827 Takes as input a hashref or list of key/value pairs with the following keys:
2833 Field name for the returned HTML fragment.
2837 Service definition (see L<FS::part_svc>)
2841 Returns an HTML fragment for DID selection.
2853 my $rv = mason_comp( 'comp'=>'/elements/select-did.html',
2854 'args'=>[ %$param ],
2858 $rv->{'error'} || $rv->{'output'};
2864 =head1 RESELLER FUNCTIONS
2866 Note: Resellers can also use the B<signup_info> and B<new_customer> functions
2867 with their active session, and the B<customer_info> and B<order_pkg> functions
2868 with their active session and an additional I<custnum> parameter.
2870 For the most part, development of the reseller web interface has been
2871 superceded by agent-virtualized access to the backend.
2883 =item agent_list_customers
2885 List agent's customers.
2893 L<freeside-selfservice-clientd>, L<freeside-selfservice-server>