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',
36 'contact_passwd' => 'MyAccount/contact/contact_passwd',
37 'list_contacts' => 'MyAccount/contact/list_contacts',
38 'edit_contact' => 'MyAccount/contact/edit_contact',
39 'delete_contact' => 'MyAccount/contact/delete_contact',
41 'billing_history' => 'MyAccount/billing_history',
42 'edit_info' => 'MyAccount/edit_info', #add to ss cgi!
43 'invoice' => 'MyAccount/invoice',
44 'invoice_pdf' => 'MyAccount/invoice_pdf',
45 'legacy_invoice' => 'MyAccount/legacy_invoice',
46 'legacy_invoice_pdf' => 'MyAccount/legacy_invoice_pdf',
47 'invoice_logo' => 'MyAccount/invoice_logo',
48 'list_invoices' => 'MyAccount/list_invoices', #?
49 'cancel' => 'MyAccount/cancel', #add to ss cgi!
50 'payment_info' => 'MyAccount/payment_info',
51 'payment_info_renew_info' => 'MyAccount/payment_info_renew_info',
52 'process_payment' => 'MyAccount/process_payment',
53 'store_payment' => 'MyAccount/store_payment',
54 'process_stored_payment' => 'MyAccount/process_stored_payment',
55 'process_payment_order_pkg' => 'MyAccount/process_payment_order_pkg',
56 'process_payment_change_pkg' => 'MyAccount/process_payment_change_pkg',
57 'process_payment_order_renew' => 'MyAccount/process_payment_order_renew',
58 'process_prepay' => 'MyAccount/process_prepay',
59 'realtime_collect' => 'MyAccount/realtime_collect',
60 'list_pkgs' => 'MyAccount/list_pkgs', #add to ss (added?)
61 'list_svcs' => 'MyAccount/list_svcs', #add to ss (added?)
62 'list_svc_usage' => 'MyAccount/list_svc_usage',
63 'svc_status_html' => 'MyAccount/svc_status_html',
64 'svc_status_hash' => 'MyAccount/svc_status_hash',
65 'set_svc_status_hash' => 'MyAccount/set_svc_status_hash',
66 'set_svc_status_listadd' => 'MyAccount/set_svc_status_listadd',
67 'set_svc_status_listdel' => 'MyAccount/set_svc_status_listdel',
68 'set_svc_status_vacationadd'=> 'MyAccount/set_svc_status_vacationadd',
69 'set_svc_status_vacationdel'=> 'MyAccount/set_svc_status_vacationdel',
70 'acct_forward_info' => 'MyAccount/acct_forward_info',
71 'process_acct_forward' => 'MyAccount/process_acct_forward',
72 'list_dsl_devices' => 'MyAccount/list_dsl_devices',
73 'add_dsl_device' => 'MyAccount/add_dsl_device',
74 'delete_dsl_device' => 'MyAccount/delete_dsl_device',
75 'port_graph' => 'MyAccount/port_graph',
76 'list_cdr_usage' => 'MyAccount/list_cdr_usage',
77 'list_support_usage' => 'MyAccount/list_support_usage',
78 'order_pkg' => 'MyAccount/order_pkg', #add to ss cgi!
79 'change_pkg' => 'MyAccount/change_pkg',
80 'order_recharge' => 'MyAccount/order_recharge',
81 'renew_info' => 'MyAccount/renew_info',
82 'order_renew' => 'MyAccount/order_renew',
83 'cancel_pkg' => 'MyAccount/cancel_pkg', #add to ss cgi!
84 'suspend_pkg' => 'MyAccount/suspend_pkg', #add to ss cgi!
85 'charge' => 'MyAccount/charge', #?
86 'part_svc_info' => 'MyAccount/part_svc_info',
87 'provision_acct' => 'MyAccount/provision_acct',
88 'provision_phone' => 'MyAccount/provision_phone',
89 'provision_pbx' => 'MyAccount/provision_pbx',
90 'provision_external' => 'MyAccount/provision_external',
91 'provision_forward' => 'MyAccount/provision_forward',
92 'unprovision_svc' => 'MyAccount/unprovision_svc',
93 'myaccount_passwd' => 'MyAccount/myaccount_passwd',
94 'reset_passwd' => 'MyAccount/reset_passwd',
95 'check_reset_passwd' => 'MyAccount/check_reset_passwd',
96 'process_reset_passwd' => 'MyAccount/process_reset_passwd',
97 'list_tickets' => 'MyAccount/list_tickets',
98 'create_ticket' => 'MyAccount/create_ticket',
99 'get_ticket' => 'MyAccount/get_ticket',
100 'adjust_ticket_priority' => 'MyAccount/adjust_ticket_priority',
101 'did_report' => 'MyAccount/did_report',
102 'signup_info' => 'Signup/signup_info',
103 'skin_info' => 'MyAccount/skin_info',
104 'access_info' => 'MyAccount/access_info',
105 'domain_select_hash' => 'Signup/domain_select_hash', # expose?
106 'new_customer' => 'Signup/new_customer',
107 'new_customer_minimal' => 'Signup/new_customer_minimal',
108 'capture_payment' => 'Signup/capture_payment',
109 #N/A 'clear_signup_cache' => 'Signup/clear_cache',
110 'new_agent' => 'Agent/new_agent',
111 'agent_login' => 'Agent/agent_login',
112 'agent_logout' => 'Agent/agent_logout',
113 'agent_info' => 'Agent/agent_info',
114 'agent_list_customers' => 'Agent/agent_list_customers',
115 'check_username' => 'Agent/check_username',
116 'suspend_username' => 'Agent/suspend_username',
117 'unsuspend_username' => 'Agent/unsuspend_username',
118 'mason_comp' => 'MasonComponent/mason_comp',
119 'call_time' => 'PrepaidPhone/call_time',
120 'call_time_nanpa' => 'PrepaidPhone/call_time_nanpa',
121 'phonenum_balance' => 'PrepaidPhone/phonenum_balance',
123 'start_thirdparty' => 'MyAccount/start_thirdparty',
124 'finish_thirdparty' => 'MyAccount/finish_thirdparty',
126 'list_quotations' => 'MyAccount/quotation/list_quotations',
127 'quotation_new' => 'MyAccount/quotation/quotation_new',
128 'quotation_delete' => 'MyAccount/quotation/quotation_delete',
129 'quotation_info' => 'MyAccount/quotation/quotation_info',
130 'quotation_print' => 'MyAccount/quotation/quotation_print',
131 'quotation_add_pkg' => 'MyAccount/quotation/quotation_add_pkg',
132 'quotation_remove_pkg' => 'MyAccount/quotation/quotation_remove_pkg',
133 'quotation_order' => 'MyAccount/quotation/quotation_order',
138 qw( regionselector regionselector_hashref location_form
139 expselect popselector domainselector didselector
143 $ENV{'PATH'} ='/usr/bin:/usr/ucb:/bin';
144 $ENV{'SHELL'} = '/bin/sh';
145 $ENV{'IFS'} = " \t\n";
148 $ENV{'BASH_ENV'} = '';
150 #you can add BEGIN { $FS::SelfService::skip_uid_check = 1; }
151 #if you grant appropriate permissions to whatever user
152 my $freeside_uid = scalar(getpwnam('freeside'));
153 die "not running as the freeside user\n"
154 if $> != $freeside_uid && ! $skip_uid_check;
156 -e $dir or die "FATAL: $dir doesn't exist!";
157 -d $dir or die "FATAL: $dir isn't a directory!";
158 -r $dir or die "FATAL: Can't read $dir as freeside user!";
159 -x $dir or die "FATAL: $dir not searchable (executable) as freeside user!";
161 foreach my $autoload ( keys %autoload ) {
164 "sub $autoload { ". '
169 #warn scalar(@_). ": ". join(" / ", @_);
173 $param->{_packet} = \''. $autoload{$autoload}. '\';
175 simple_packet($param);
185 warn "sending ". $packet->{_packet}. " to server"
187 socket(SOCK, PF_UNIX, SOCK_STREAM, 0) or die "socket: $!";
188 connect(SOCK, sockaddr_un($socket)) or die "connect to $socket: $!";
189 nstore_fd($packet, \*SOCK) or die "can't send packet: $!";
192 #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
194 #block until there is a message on socket
195 # my $w = new IO::Select;
197 # my @wait = $w->can_read;
199 warn "reading message from server"
202 my $return = fd_retrieve(\*SOCK) or die "error reading result: $!";
203 die $return->{'_error'} if defined $return->{_error} && $return->{_error};
205 warn "returning message to client"
213 FS::SelfService - Freeside self-service API
217 # password and shell account changes
218 use FS::SelfService qw(passwd chfn chsh);
220 # "my account" functionality
221 use FS::SelfService qw( login customer_info invoice cancel payment_info process_payment );
223 #new-style login with an email address and password
224 # can also be used for svc_acct login, set $emailaddress to username@domain
225 my $rv = login ( { 'email' => $emailaddress,
226 'password' => $password,
229 if ( $rv->{'error'} ) {
230 #handle login error...
233 $session_id = $rv->{'session_id'};
236 #classic svc_acct-based login with separate username and password
237 my $rv = login( { 'username' => $username,
239 'password' => $password,
242 if ( $rv->{'error'} ) {
243 #handle login error...
246 $session_id = $rv->{'session_id'};
249 #svc_phone login with phone number and PIN
250 my $rv = login( { 'username' => $phone_number,
251 'domain' => 'svc_phone',
255 if ( $rv->{'error'} ) {
256 #handle login error...
259 $session_id = $rv->{'session_id'};
262 my $customer_info = customer_info( { 'session_id' => $session_id } );
264 #payment_info and process_payment are available in 1.5+ only
265 my $payment_info = payment_info( { 'session_id' => $session_id } );
267 #!!! process_payment example
269 #!!! list_pkgs example
271 #!!! order_pkg example
273 #quoting a package, then ordering after confirmation
275 my $rv = quotation_new({ 'session_id' => $session_id });
276 my $qnum = $rv->{quotationnum};
277 # add packages to the quotation
278 $rv = quotation_add_pkg({ 'session_id' => $session_id,
279 'quotationnum' => $qnum,
280 'pkgpart' => $pkgpart,
281 'quantity' => $quantity, # defaults to 1
283 # repeat until all packages are added
284 # view the pricing information
285 $rv = quotation_info({ 'session_id' => $session_id,
286 'quotationnum' => $qnum,
288 print "Total setup charges: ".$rv->{total_setup}."\n".
289 "Total recurring charges: ".$rv->{total_recur}."\n";
290 # quotation_info also provides a detailed breakdown of charges, in
293 # ask customer for confirmation, then:
294 $rv = quotation_order({ 'session_id' => $session_id,
295 'quotationnum' => $qnum,
298 #!!! cancel_pkg example
300 # signup functionality
301 use FS::SelfService qw( signup_info new_customer new_customer_minimal );
303 my $signup_info = signup_info;
305 $rv = new_customer( {
308 'company' => $company,
309 'address1' => $address1,
310 'address2' => $address2,
314 'country' => $country,
315 'daytime' => $daytime,
319 'payinfo' => $payinfo,
321 'paystart_month' => $paystart_month
322 'paystart_year' => $paystart_year,
323 'payissue' => $payissue,
325 'paydate' => $paydate,
326 'payname' => $payname,
327 'invoicing_list' => $invoicing_list,
328 'referral_custnum' => $referral_custnum,
329 'agentnum' => $agentnum,
330 'pkgpart' => $pkgpart,
332 'username' => $username,
333 '_password' => $password,
337 'phonenum' => $phonenum,
342 my $error = $rv->{'error'};
343 if ( $error eq '_decline' ) {
353 Use this API to implement your own client "self-service" module.
355 If you just want to customize the look of the existing "self-service" module,
358 =head1 PASSWORD, GECOS, SHELL CHANGING FUNCTIONS
364 Changes the password for an existing user in svc_acct. Takes a hash
365 reference with the following keys:
371 Username of the account (required)
375 Domain of the account (required)
379 Old password (required)
383 New password (required)
401 =head1 "MY ACCOUNT" FUNCTIONS
407 Creates a user session. Takes a hash reference as parameter with the
414 Email address (username@domain), instead of username and domain. Required for
415 contact-based self-service login, can also be used for svc_acct-based login.
431 Returns a hash reference with the following keys:
437 Empty on success, or an error message on errors.
441 Session identifier for successful logins
445 =item customer_info HASHREF
447 Returns general customer information.
449 Takes a hash reference as parameter with a single key: B<session_id>
451 Returns a hash reference with the following keys:
465 Array reference of hash references of open inoices. Each hash reference has
466 the following keys: invnum, date, owed
470 An HTML fragment containing shipping and billing addresses.
472 =item The following fields are also returned
474 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
478 =item edit_info HASHREF
480 Takes a hash reference as parameter with any of the following keys:
482 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
484 If a field exists, the customer record is updated with the new value of that
485 field. If a field does not exist, that field is not changed on the customer
488 Returns a hash reference with a single key, B<error>, empty on success, or an
489 error message on errors
491 =item invoice HASHREF
493 Returns an invoice. Takes a hash reference as parameter with two keys:
494 session_id and invnum
496 Returns a hash reference with the following keys:
502 Empty on success, or an error message on errors
514 =item list_invoices HASHREF
516 Returns a list of all customer invoices. Takes a hash reference with a single
519 Returns a hash reference with the following keys:
525 Empty on success, or an error message on errors
529 Reference to array of hash references with the following keys:
539 Invoice date, in UNIX epoch time
547 Cancels this customer.
549 Takes a hash reference as parameter with a single key: B<session_id>
551 Returns a hash reference with a single key, B<error>, which is empty on
552 success or an error message on errors.
554 =item payment_info HASHREF
556 Returns information that may be useful in displaying a payment page.
558 Takes a hash reference as parameter with a single key: B<session_id>.
560 Returns a hash reference with the following keys:
566 Empty on success, or an error message on errors
574 Exact name on credit card (CARD/DCRD)
598 Customer's current default payment type.
602 For CARD/DCRD payment types, the card type (Visa card, MasterCard, Discover card, American Express card, etc.)
606 For CARD/DCRD payment types, the card number
610 For CARD/DCRD payment types, expiration month
614 For CARD/DCRD payment types, expiration year
616 =item cust_main_county
618 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.
622 Array reference of all states in the current default country.
626 Hash reference of card types; keys are card types, values are the exact strings
627 passed to the process_payment function
631 #this doesn't actually work yet
635 #Unique transaction identifier (prevents multiple charges), passed to the
636 #process_payment function
640 =item process_payment HASHREF
642 Processes a payment and possible change of address or payment type. Takes a
643 hash reference as parameter with the following keys:
657 If true, address and card information entered will be saved for subsequent
662 If true, future credit card payments will be done automatically (sets payby to
663 CARD). If false, future credit card payments will be done on-demand (sets
664 payby to DCRD). This option only has meaning if B<save> is set true.
692 Two-letter country code
700 Card expiration month
708 #this doesn't actually work yet
712 #Unique transaction identifier, returned from the payment_info function.
713 #Prevents multiple charges.
717 Returns a hash reference with a single key, B<error>, empty on success, or an
718 error message on errors.
720 =item process_payment_order_pkg
722 Combines the B<process_payment> and B<order_pkg> functions in one step. If the
723 payment processes sucessfully, the package is ordered. Takes a hash reference
724 as parameter with the keys of both methods.
726 Returns a hash reference with a single key, B<error>, empty on success, or an
727 error message on errors.
729 =item process_payment_change_pkg
731 Combines the B<process_payment> and B<change_pkg> functions in one step. If the
732 payment processes sucessfully, the package is ordered. Takes a hash reference
733 as parameter with the keys of both methods.
735 Returns a hash reference with a single key, B<error>, empty on success, or an
736 error message on errors.
739 =item process_payment_order_renew
741 Combines the B<process_payment> and B<order_renew> functions in one step. If
742 the payment processes sucessfully, the renewal is processed. Takes a hash
743 reference as parameter with the keys of both methods.
745 Returns a hash reference with a single key, B<error>, empty on success, or an
746 error message on errors.
750 Returns package information for this customer. For more detail on services,
753 Takes a hash reference as parameter with a single key: B<session_id>
755 Returns a hash reference containing customer package information. The hash reference contains the following keys:
765 Empty on success, or an error message on errors.
767 =item cust_pkg HASHREF
769 Array reference of hash references, each of which has the fields of a cust_pkg
770 record (see L<FS::cust_pkg>) as well as the fields below. Note these are not
771 the internal FS:: objects, but hash references of columns and values.
775 =item part_pkg fields
777 All fields of part_pkg for this specific cust_pkg (be careful with this
778 information - it may reveal more about your available packages than you would
779 like users to know in aggregate)
783 #XXX pare part_pkg fields down to a more secure subset
787 An array of hash references indicating information on unprovisioned services
788 available for provisioning for this specific cust_pkg. Each has the following
793 =item part_svc fields
795 All fields of part_svc (be careful with this information - it may reveal more
796 about your available packages than you would like users to know in aggregate)
800 #XXX pare part_svc fields down to a more secure subset
806 An array of hash references indicating information on the customer services
807 already provisioned for this specific cust_pkg. Each has the following keys:
813 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.
819 Primary key for this service
823 Service definition (see L<FS::part_svc>)
827 Customer package (see L<FS::cust_pkg>)
831 Blank if the service is not over limit, or the date the service exceeded its usage limit (as a UNIX timestamp).
839 Returns service information for this customer.
841 Takes a hash reference as parameter with a single key: B<session_id>
843 Returns a hash reference containing customer package information. The hash reference contains the following keys:
853 An array of hash references indicating information on all of this customer's
854 services. Each has the following keys:
860 Primary key for this service
868 Meaningful user-specific identifier for the service (i.e. username, domain, or
873 Account (svc_acct) services also have the following keys:
891 Upload bytes remaining
895 Download bytes remaining
899 Total bytes remaining
901 =item recharge_amount
905 =item recharge_seconds
907 Number of seconds gained by recharge
909 =item recharge_upbytes
911 Number of upload bytes gained by recharge
913 =item recharge_downbytes
915 Number of download bytes gained by recharge
917 =item recharge_totalbytes
919 Number of total bytes gained by recharge
927 Orders a package for this customer.
929 Takes a hash reference as parameter with the following keys:
939 Package to order (see L<FS::part_pkg>).
943 Quantity for this package order (default 1).
947 Optional locationnum for this package order, for existing locations.
949 Or, for new locations, pass the following fields: address1*, address2, city*,
950 county, state*, zip*, country. (* = required in this case)
962 Service to order (see L<FS::part_svc>).
964 Normally optional; required only to provision a non-svc_acct service, or if the
965 package definition does not contain one svc_acct service definition with
966 quantity 1 (it may contain others with quantity >1). A svcpart of "none" can
967 also be specified to indicate that no initial service should be provisioned.
971 Fields used when provisioning an svc_acct service:
985 Optional security phrase
989 Optional Access number number
993 Fields used when provisioning an svc_domain service:
1003 Fields used when provisioning an svc_phone service:
1021 Fields used when provisioning an svc_external service:
1027 External numeric ID.
1031 External text title.
1035 Fields used when provisioning an svc_pbx service:
1049 Returns a hash reference with a single key, B<error>, empty on success, or an
1050 error message on errors. The special error '_decline' is returned for
1051 declined transactions.
1055 Changes a package for this customer.
1057 Takes a hash reference as parameter with the following keys:
1067 Existing customer package.
1071 New package to order (see L<FS::part_pkg>).
1075 Quantity for this package order (default 1).
1079 Returns a hash reference with the following keys:
1085 Empty on success, or an error message on errors.
1089 On success, the new pkgnum
1096 Provides useful info for early renewals.
1098 Takes a hash reference as parameter with the following keys:
1108 Returns a hash reference. On errors, it contains a single key, B<error>, with
1109 the error message. Otherwise, contains a single key, B<dates>, pointing to
1110 an array refernce of hash references. Each hash reference contains the
1117 (Future) Bill date. Indicates a future date for which billing could be run.
1118 Specified as a integer UNIX timestamp. Pass this value to the B<order_renew>
1121 =item bill_date_pretty
1123 (Future) Bill date as a human-readable string. (Convenience for display;
1124 subject to change, so best not to parse for the date.)
1128 Base amount which will be charged if renewed early as of this date.
1132 Renewal date; i.e. even-futher future date at which the customer will be paid
1133 through if the early renewal is completed with the given B<bill-date>.
1134 Specified as a integer UNIX timestamp.
1136 =item renew_date_pretty
1138 Renewal date as a human-readable string. (Convenience for display;
1139 subject to change, so best not to parse for the date.)
1143 Package that will be renewed.
1147 Expiration date of the package that will be renewed.
1149 =item expire_date_pretty
1151 Expiration date of the package that will be renewed, as a human-readable
1152 string. (Convenience for display; subject to change, so best not to parse for
1159 Renews this customer early; i.e. runs billing for this customer in advance.
1161 Takes a hash reference as parameter with the following keys:
1171 Integer date as returned by the B<renew_info> function, indicating the advance
1172 date for which to run billing.
1176 Returns a hash reference with a single key, B<error>, empty on success, or an
1177 error message on errors.
1181 Cancels a package for this customer.
1183 Takes a hash reference as parameter with the following keys:
1193 pkgpart of package to cancel
1197 Returns a hash reference with a single key, B<error>, empty on success, or an
1198 error message on errors.
1200 =item provision_acct
1202 Provisions an account (svc_acct).
1204 Takes a hash reference as parameter with the following keys:
1214 pkgnum of package into which this service is provisioned
1218 svcpart or service definition to provision
1228 =item provision_phone
1230 Provisions a phone number (svc_phone).
1232 Takes a hash reference as parameter with the following keys:
1242 pkgnum of package into which this service is provisioned
1246 svcpart or service definition to provision
1266 E911 Address (optional)
1272 Provisions a customer PBX (svc_pbx).
1274 Takes a hash reference as parameter with the following keys:
1284 pkgnum of package into which this service is provisioned
1288 svcpart or service definition to provision
1294 =item max_extensions
1296 =item max_simultaneous
1302 =item provision_external
1304 Provisions an external service (svc_external).
1306 Takes a hash reference as parameter with the following keys:
1316 pkgnum of package into which this service is provisioned
1320 svcpart or service definition to provision
1330 =head2 "MY ACCOUNT" CONTACT FUNCTIONS
1334 =item contact_passwd
1336 Changes the password for the currently-logged in contact.
1338 Takes a hash reference as parameter with the following keys:
1348 Returns a hash reference with a single parameter, B<error>, which contains an
1349 error message, or empty on success.
1355 Updates information for the currently-logged in contact, or (optionally) the
1358 Takes a hash reference as parameter with the following keys:
1366 If already logged in as a contact, this is optional.
1376 Returns a hash reference with a single parameter, B<error>, which contains an
1377 error message, or empty on success.
1379 =item delete_contact
1381 Deletes a contact. (Note: Cannot at this time delete the currently-logged in
1384 Takes a hash reference as parameter with the following keys:
1394 Returns a hash reference with a single parameter, B<error>, which contains an
1395 error message, or empty on success.
1397 =head2 "MY ACCOUNT" QUOTATION FUNCTIONS
1399 All of these functions require the user to be logged in, and the 'session_id'
1400 key to be included in the argument hashref.`
1404 =item list_quotations HASHREF
1406 Returns a hashref listing this customer's active self-service quotations.
1413 an arrayref containing an element for each quotation.
1421 the date it was started
1425 the number of packages
1429 the sum of setup fees
1433 the sum of recurring charges
1437 =item quotation_new HASHREF
1439 Creates an empty quotation and returns a hashref containing 'quotationnum',
1440 the primary key of the new quotation.
1442 =item quotation_delete HASHREF
1444 Disables (does not really delete) a quotation. Takes the following arguments:
1450 =item quotationnum - the quotation to delete
1454 Returns 'error' => a string, which will be empty on success.
1456 =item quotation_info HASHREF
1458 Returns total and detailed pricing information on a quotation.
1460 Takes the following arguments:
1466 =item quotationnum - the quotation to return
1470 Returns a hashref containing:
1472 - total_setup, the total of setup fees (and their taxes)
1473 - total_recur, the total of all recurring charges (and their taxes)
1474 - sections, an arrayref containing an element for each quotation section.
1475 - description, a line of text describing the group of charges
1476 - subtotal, the total of charges in this group (if appropriate)
1477 - detail_items, an arrayref of line items
1478 - pkgnum, the reference number of the package
1479 - description, the package name (or tax name)
1481 - amount, the amount charged
1482 If the detail item represents a subtotal, it will instead contain:
1483 - total_item: description of the subtotal
1484 - total_amount: the subtotal amount
1487 =item quotation_print HASHREF
1489 Renders the quotation as HTML or PDF. Takes the following arguments:
1495 =item quotationnum - the quotation to return
1497 =item format - 'html' or 'pdf'
1501 Returns a hashref containing 'document', the contents of the file.
1503 =item quotation_add_pkg HASHREF
1505 Adds a package to a quotation. Takes the following arguments:
1511 =item pkgpart - the package to add
1513 =item quotationnum - the quotation to add it to
1515 =item quantity - the package quantity (defaults to 1)
1517 =item address1, address2, city, state, zip, country - address fields to set
1518 the service location
1522 Returns 'error' => a string, which will be empty on success.
1524 =item quotation_remove_pkg HASHREF
1526 Removes a package from a quotation. Takes the following arguments:
1532 =item pkgnum - the primary key (quotationpkgnum) of the package to remove
1534 =item quotationnum - the quotation to remove it from
1538 Returns 'error' => a string, which will be empty on success.
1542 =item quotation_order HASHREF
1544 Converts the packages in a quotation into real packages. Takes the following
1547 Takes the following arguments:
1553 =item quotationnum - the quotation to order
1559 =head1 SIGNUP FUNCTIONS
1563 =item signup_info HASHREF
1565 Takes a hash reference as parameter with the following keys:
1569 =item session_id - Optional agent/reseller interface session
1573 Returns a hash reference containing information that may be useful in
1574 displaying a signup page. The hash reference contains the following keys:
1578 =item cust_main_county
1580 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.
1584 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
1585 an agentnum specified explicitly via reseller interface session_id in the
1590 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.
1592 =item agentnum2part_pkg
1594 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.
1598 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.
1600 =item security_phrase
1602 True if the "security_phrase" feature is enabled
1606 Array reference of acceptable payment types for signup
1612 credit card - automatic
1616 credit card - on-demand - version 1.5+ only
1620 electronic check - automatic
1624 electronic check - on-demand - version 1.5+ only
1632 billing, not recommended for signups
1636 free, definitely not recommended for signups
1640 special billing type: applies a credit (see FS::prepay_credit) and sets billing type to BILL
1646 True if CVV features are available (1.5+ or 1.4.2 with CVV schema patch)
1650 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".
1656 =item countrydefault
1662 =item new_customer_minimal HASHREF
1664 Creates a new customer.
1666 Current differences from new_customer: An address is not required. promo_code
1667 and reg_code are not supported. If invoicing_list and _password is passed, a
1668 contact will be created with self-service access (no pkgpart or username is
1669 necessary). No initial billing is run (this may change in a future version).
1671 Takes a hash reference as parameter with the following keys:
1677 first name (required)
1681 last name (required)
1685 (not typically collected; mostly used for ACH transactions)
1717 Daytime phone number
1721 Evening phone number
1729 CARD, DCRD, CHEK, DCHK, LECB, BILL, COMP or PREPAY (see L</signup_info> (required)
1733 Card number for CARD/DCRD, account_number@aba_number for CHEK/DCHK, prepaid "pin" for PREPAY, purchase order number for BILL
1737 Credit card CVV2 number (1.5+ or 1.4.2 with CVV schema patch)
1741 Expiration date for CARD/DCRD
1745 Exact name on credit card for CARD/DCRD, bank name for CHEK/DCHK
1747 =item invoicing_list
1749 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),
1751 =item referral_custnum
1753 referring customer number
1761 pkgpart of initial package
1777 Access number (index, not the literal number)
1781 Country code (to be provisioned as a service)
1785 Phone number (to be provisioned as a service)
1793 Returns a hash reference with the following keys:
1799 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)
1803 =item new_customer HASHREF
1805 Creates a new customer. Takes a hash reference as parameter with the
1812 first name (required)
1816 last name (required)
1820 (not typically collected; mostly used for ACH transactions)
1826 =item address1 (required)
1834 =item city (required)
1842 =item state (required)
1846 =item zip (required)
1852 Daytime phone number
1856 Evening phone number
1864 CARD, DCRD, CHEK, DCHK, LECB, BILL, COMP or PREPAY (see L</signup_info> (required)
1868 Card number for CARD/DCRD, account_number@aba_number for CHEK/DCHK, prepaid "pin" for PREPAY, purchase order number for BILL
1872 Credit card CVV2 number (1.5+ or 1.4.2 with CVV schema patch)
1876 Expiration date for CARD/DCRD
1880 Exact name on credit card for CARD/DCRD, bank name for CHEK/DCHK
1882 =item invoicing_list
1884 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),
1886 =item referral_custnum
1888 referring customer number
1896 pkgpart of initial package
1912 Access number (index, not the literal number)
1916 Country code (to be provisioned as a service)
1920 Phone number (to be provisioned as a service)
1928 Returns a hash reference with the following keys:
1934 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)
1938 =item regionselector HASHREF | LIST
1940 Takes as input a hashref or list of key/value pairs with the following keys:
1944 =item selected_county
1946 Currently selected county
1948 =item selected_state
1950 Currently selected state
1952 =item selected_country
1954 Currently selected country
1958 Specify a unique prefix string if you intend to use the HTML output multiple time son one page.
1962 Specify a javascript subroutine to call on changes
1968 =item default_country
1974 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>.
1978 Returns a list consisting of three HTML fragments for county selection,
1979 state selection and country selection, respectively.
1983 #false laziness w/FS::cust_main_county (this is currently the "newest" version)
1984 sub regionselector {
1991 $param->{'selected_country'} ||= $param->{'default_country'};
1992 $param->{'selected_state'} ||= $param->{'default_state'};
1994 my $prefix = exists($param->{'prefix'}) ? $param->{'prefix'} : '';
1998 my %cust_main_county;
2000 # unless ( @cust_main_county ) { #cache
2001 #@cust_main_county = qsearch('cust_main_county', {} );
2002 #foreach my $c ( @cust_main_county ) {
2003 foreach my $c ( @{ $param->{'locales'} } ) {
2004 #$countyflag=1 if $c->county;
2005 $countyflag=1 if $c->{county};
2006 #push @{$cust_main_county{$c->country}{$c->state}}, $c->county;
2007 #$cust_main_county{$c->country}{$c->state}{$c->county} = 1;
2008 $cust_main_county{$c->{country}}{$c->{state}}{$c->{county}} = 1;
2011 $countyflag=1 if $param->{selected_county};
2013 my $script_html = <<END;
2015 function opt(what,value,text) {
2016 var optionName = new Option(text, value, false, false);
2017 var length = what.length;
2018 what.options[length] = optionName;
2020 function ${prefix}country_changed(what) {
2021 country = what.options[what.selectedIndex].text;
2022 for ( var i = what.form.${prefix}state.length; i >= 0; i-- )
2023 what.form.${prefix}state.options[i] = null;
2025 #what.form.${prefix}state.options[0] = new Option('', '', false, true);
2027 foreach my $country ( sort keys %cust_main_county ) {
2028 $script_html .= "\nif ( country == \"$country\" ) {\n";
2029 foreach my $state ( sort keys %{$cust_main_county{$country}} ) {
2030 my $text = $state || '(n/a)';
2031 $script_html .= qq!opt(what.form.${prefix}state, "$state", "$text");\n!;
2033 $script_html .= "}\n";
2036 $script_html .= <<END;
2038 function ${prefix}state_changed(what) {
2041 if ( $countyflag ) {
2042 $script_html .= <<END;
2043 state = what.options[what.selectedIndex].text;
2044 country = what.form.${prefix}country.options[what.form.${prefix}country.selectedIndex].text;
2045 for ( var i = what.form.${prefix}county.length; i >= 0; i-- )
2046 what.form.${prefix}county.options[i] = null;
2049 foreach my $country ( sort keys %cust_main_county ) {
2050 $script_html .= "\nif ( country == \"$country\" ) {\n";
2051 foreach my $state ( sort keys %{$cust_main_county{$country}} ) {
2052 $script_html .= "\nif ( state == \"$state\" ) {\n";
2053 #foreach my $county ( sort @{$cust_main_county{$country}{$state}} ) {
2054 foreach my $county ( sort keys %{$cust_main_county{$country}{$state}} ) {
2055 my $text = $county || '(n/a)';
2057 qq!opt(what.form.${prefix}county, "$county", "$text");\n!;
2059 $script_html .= "}\n";
2061 $script_html .= "}\n";
2065 $script_html .= <<END;
2070 my $county_html = $script_html;
2071 if ( $countyflag ) {
2072 $county_html .= qq!<SELECT NAME="${prefix}county" onChange="$param->{'onchange'}">!;
2073 foreach my $county (
2074 sort keys %{ $cust_main_county{$param->{'selected_country'}}{$param->{'selected_state'}} }
2076 my $text = $county || '(n/a)';
2077 $county_html .= qq!<OPTION VALUE="$county"!.
2078 ($county eq $param->{'selected_county'} ?
2085 $county_html .= '</SELECT>';
2088 qq!<INPUT TYPE="hidden" NAME="${prefix}county" VALUE="$param->{'selected_county'}">!;
2091 my $state_html = qq!<SELECT NAME="${prefix}state" !.
2092 qq!onChange="${prefix}state_changed(this); $param->{'onchange'}">!;
2093 foreach my $state ( sort keys %{ $cust_main_county{$param->{'selected_country'}} } ) {
2094 my $text = $state || '(n/a)';
2095 my $selected = $state eq $param->{'selected_state'} ? 'SELECTED' : '';
2096 $state_html .= "\n<OPTION $selected VALUE=$state>$text</OPTION>"
2098 $state_html .= '</SELECT>';
2100 my $country_html = '';
2101 if ( scalar( keys %cust_main_county ) > 1 ) {
2103 $country_html = qq(<SELECT NAME="${prefix}country" ).
2104 qq(onChange="${prefix}country_changed(this); ).
2105 $param->{'onchange'}.
2108 my $countrydefault = $param->{default_country} || 'US';
2109 foreach my $country (
2110 sort { ($b eq $countrydefault) <=> ($a eq $countrydefault) or $a cmp $b }
2111 keys %cust_main_county
2113 my $selected = $country eq $param->{'selected_country'}
2116 $country_html .= "\n<OPTION$selected>$country</OPTION>"
2118 $country_html .= '</SELECT>';
2121 $country_html = qq(<INPUT TYPE="hidden" NAME="${prefix}country" ).
2122 ' VALUE="'. (keys %cust_main_county )[0]. '">';
2126 ($county_html, $state_html, $country_html);
2130 sub regionselector_hashref {
2131 my ($county_html, $state_html, $country_html) = regionselector(@_);
2133 'county_html' => $county_html,
2134 'state_html' => $state_html,
2135 'country_html' => $country_html,
2139 =item location_form HASHREF | LIST
2141 Takes as input a hashref or list of key/value pairs with the following keys:
2147 Current customer session_id
2151 Omit red asterisks from required fields.
2153 =item address1_label
2155 Label for first address line.
2159 Returns an HTML fragment for a location form (address, city, state, zip,
2172 my $session_id = delete $param->{'session_id'};
2174 my $rv = mason_comp( 'session_id' => $session_id,
2175 'comp' => '/elements/location.html',
2176 'args' => [ %$param ],
2180 $rv->{'error'} || $rv->{'output'};
2185 #=item expselect HASHREF | LIST
2187 #Takes as input a hashref or list of key/value pairs with the following keys:
2191 #=item prefix - Specify a unique prefix string if you intend to use the HTML output multiple time son one page.
2193 #=item date - current date, in yyyy-mm-dd or m-d-yyyy format
2197 =item expselect PREFIX [ DATE ]
2199 Takes as input a unique prefix string and the current expiration date, in
2200 yyyy-mm-dd or m-d-yyyy format
2202 Returns an HTML fragments for expiration date selection.
2208 #if ( ref($_[0]) ) {
2212 #my $prefix = $param->{'prefix'};
2213 #my $prefix = exists($param->{'prefix'}) ? $param->{'prefix'} : '';
2214 #my $date = exists($param->{'date'}) ? $param->{'date'} : '';
2216 my $date = scalar(@_) ? shift : '';
2218 my( $m, $y ) = ( 0, 0 );
2219 if ( $date =~ /^(\d{4})-(\d{2})-\d{2}$/ ) { #PostgreSQL date format
2220 ( $m, $y ) = ( $2, $1 );
2221 } elsif ( $date =~ /^(\d{1,2})-(\d{1,2}-)?(\d{4}$)/ ) {
2222 ( $m, $y ) = ( $1, $3 );
2224 my $return = qq!<SELECT NAME="$prefix!. qq!_month" SIZE="1">!;
2226 $return .= qq!<OPTION VALUE="$_"!;
2227 $return .= " SELECTED" if $_ == $m;
2230 $return .= qq!</SELECT>/<SELECT NAME="$prefix!. qq!_year" SIZE="1">!;
2232 my $thisYear = $t[5] + 1900;
2233 for ( ($thisYear > $y && $y > 0 ? $y : $thisYear) .. ($thisYear+10) ) {
2234 $return .= qq!<OPTION VALUE="$_"!;
2235 $return .= " SELECTED" if $_ == $y;
2238 $return .= "</SELECT>";
2243 =item popselector HASHREF | LIST
2245 Takes as input a hashref or list of key/value pairs with the following keys:
2251 Access number number
2255 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>.
2259 Returns an HTML fragment for access number selection.
2263 #horrible false laziness with FS/FS/svc_acct_pop.pm::popselector
2271 my $popnum = $param->{'popnum'};
2272 my $pops = $param->{'pops'};
2274 return '<INPUT TYPE="hidden" NAME="popnum" VALUE="">' unless @$pops;
2275 return $pops->[0]{city}. ', '. $pops->[0]{state}.
2276 ' ('. $pops->[0]{ac}. ')/'. $pops->[0]{exch}. '-'. $pops->[0]{loc}.
2277 '<INPUT TYPE="hidden" NAME="popnum" VALUE="'. $pops->[0]{popnum}. '">'
2278 if scalar(@$pops) == 1;
2281 my %popnum2pop = ();
2283 push @{ $pop{ $_->{state} }->{ $_->{ac} } }, $_;
2284 $popnum2pop{$_->{popnum}} = $_;
2289 function opt(what,href,text) {
2290 var optionName = new Option(text, href, false, false)
2291 var length = what.length;
2292 what.options[length] = optionName;
2296 my $init_popstate = $param->{'init_popstate'};
2297 if ( $init_popstate ) {
2298 $text .= '<INPUT TYPE="hidden" NAME="init_popstate" VALUE="'.
2299 $init_popstate. '">';
2302 function acstate_changed(what) {
2303 state = what.options[what.selectedIndex].text;
2304 what.form.popac.options.length = 0
2305 what.form.popac.options[0] = new Option("Area code", "-1", false, true);
2309 my @states = $init_popstate ? ( $init_popstate ) : keys %pop;
2310 foreach my $state ( sort { $a cmp $b } @states ) {
2311 $text .= "\nif ( state == \"$state\" ) {\n" unless $init_popstate;
2313 foreach my $ac ( sort { $a cmp $b } keys %{ $pop{$state} }) {
2314 $text .= "opt(what.form.popac, \"$ac\", \"$ac\");\n";
2315 if ($ac eq $param->{'popac'}) {
2316 $text .= "what.form.popac.options[what.form.popac.length-1].selected = true;\n";
2319 $text .= "}\n" unless $init_popstate;
2321 $text .= "popac_changed(what.form.popac)}\n";
2324 function popac_changed(what) {
2325 ac = what.options[what.selectedIndex].text;
2326 what.form.popnum.options.length = 0;
2327 what.form.popnum.options[0] = new Option("City", "-1", false, true);
2331 foreach my $state ( @states ) {
2332 foreach my $popac ( keys %{ $pop{$state} } ) {
2333 $text .= "\nif ( ac == \"$popac\" ) {\n";
2335 foreach my $pop ( @{$pop{$state}->{$popac}}) {
2336 my $o_popnum = $pop->{popnum};
2337 my $poptext = $pop->{city}. ', '. $pop->{state}.
2338 ' ('. $pop->{ac}. ')/'. $pop->{exch}. '-'. $pop->{loc};
2340 $text .= "opt(what.form.popnum, \"$o_popnum\", \"$poptext\");\n";
2341 if ($popnum == $o_popnum) {
2342 $text .= "what.form.popnum.options[what.form.popnum.length-1].selected = true;\n";
2350 $text .= "}\n</SCRIPT>\n";
2352 $param->{'acstate'} = '' unless defined($param->{'acstate'});
2355 qq!<TABLE CELLPADDING="0"><TR><TD><SELECT NAME="acstate"! .
2356 qq!SIZE=1 onChange="acstate_changed(this)"><OPTION VALUE=-1>State!;
2357 $text .= "<OPTION" . ($_ eq $param->{'acstate'} ? " SELECTED" : "") .
2358 ">$_" foreach sort { $a cmp $b } @states;
2359 $text .= '</SELECT>'; #callback? return 3 html pieces? #'</TD>';
2362 qq!<SELECT NAME="popac" SIZE=1 onChange="popac_changed(this)">!.
2363 qq!<OPTION>Area code</SELECT></TR><TR VALIGN="top">!;
2365 $text .= qq!<TR><TD><SELECT NAME="popnum" SIZE=1 STYLE="width: 20em"><OPTION>City!;
2368 #comment this block to disable initial list polulation
2369 my @initial_select = ();
2370 if ( scalar( @$pops ) > 100 ) {
2371 push @initial_select, $popnum2pop{$popnum} if $popnum2pop{$popnum};
2373 @initial_select = @$pops;
2375 foreach my $pop ( sort { $a->{state} cmp $b->{state} } @initial_select ) {
2376 $text .= qq!<OPTION VALUE="!. $pop->{popnum}. '"'.
2377 ( ( $popnum && $pop->{popnum} == $popnum ) ? ' SELECTED' : '' ). ">".
2378 $pop->{city}. ', '. $pop->{state}.
2379 ' ('. $pop->{ac}. ')/'. $pop->{exch}. '-'. $pop->{loc};
2382 $text .= qq!</SELECT></TD></TR></TABLE>!;
2388 =item domainselector HASHREF | LIST
2390 Takes as input a hashref or list of key/value pairs with the following keys:
2400 Service number of the selected item.
2404 Returns an HTML fragment for domain selection.
2408 sub domainselector {
2415 my $domsvc= $param->{'domsvc'};
2417 domain_select_hash(map {$_ => $param->{$_}} qw(pkgnum svcpart pkgpart) );
2418 my $domains = $rv->{'domains'};
2419 $domsvc = $rv->{'domsvc'} unless $domsvc;
2421 return '<INPUT TYPE="hidden" NAME="domsvc" VALUE="">'
2422 unless scalar(keys %$domains);
2424 if (scalar(keys %$domains) == 1) {
2426 foreach(keys %$domains) {
2429 return '<TR><TD ALIGN="right">Domain</TD><TD>'. $domains->{$key}.
2430 '<INPUT TYPE="hidden" NAME="domsvc" VALUE="'. $key. '"></TD></TR>'
2433 my $text .= qq!<TR><TD ALIGN="right">Domain</TD><TD><SELECT NAME="domsvc" SIZE=1 STYLE="width: 20em">!;
2435 $text .= '<OPTION>(Choose Domain)' unless $domsvc;
2437 foreach my $domain ( sort { $domains->{$a} cmp $domains->{$b} } keys %$domains ) {
2438 $text .= qq!<OPTION VALUE="!. $domain. '"'.
2439 ( ( $domsvc && $domain == $domsvc ) ? ' SELECTED' : '' ). ">".
2440 $domains->{$domain};
2443 $text .= qq!</SELECT></TD></TR>!;
2449 =item didselector HASHREF | LIST
2451 Takes as input a hashref or list of key/value pairs with the following keys:
2457 Field name for the returned HTML fragment.
2461 Service definition (see L<FS::part_svc>)
2465 Returns an HTML fragment for DID selection.
2477 my $rv = mason_comp( 'comp'=>'/elements/select-did.html',
2478 'args'=>[ %$param ],
2482 $rv->{'error'} || $rv->{'output'};
2488 =head1 RESELLER FUNCTIONS
2490 Note: Resellers can also use the B<signup_info> and B<new_customer> functions
2491 with their active session, and the B<customer_info> and B<order_pkg> functions
2492 with their active session and an additional I<custnum> parameter.
2494 For the most part, development of the reseller web interface has been
2495 superceded by agent-virtualized access to the backend.
2507 =item agent_list_customers
2509 List agent's customers.
2517 L<freeside-selfservice-clientd>, L<freeside-selfservice-server>