#use IO::Handle;
use IO::Select;
use Storable 2.09 qw(nstore_fd fd_retrieve);
+use Time::HiRes;
$VERSION = '0.03';
'chfn' => 'passwd/passwd',
'chsh' => 'passwd/passwd',
'login_info' => 'MyAccount/login_info',
+ 'login_banner_image' => 'MyAccount/login_banner_image',
'login' => 'MyAccount/login',
'logout' => 'MyAccount/logout',
'switch_acct' => 'MyAccount/switch_acct',
+ 'switch_cust' => 'MyAccount/switch_cust',
'customer_info' => 'MyAccount/customer_info',
'customer_info_short' => 'MyAccount/customer_info_short',
+ 'customer_recurring' => 'MyAccount/customer_recurring',
+
+ 'contact_passwd' => 'MyAccount/contact/contact_passwd',
+ 'list_contacts' => 'MyAccount/contact/list_contacts',
+ 'edit_contact' => 'MyAccount/contact/edit_contact',
+ 'delete_contact' => 'MyAccount/contact/delete_contact',
+ 'new_contact' => 'MyAccount/contact/new_contact',
+
+ 'billing_history' => 'MyAccount/billing_history',
'edit_info' => 'MyAccount/edit_info', #add to ss cgi!
'invoice' => 'MyAccount/invoice',
'invoice_pdf' => 'MyAccount/invoice_pdf',
'legacy_invoice_pdf' => 'MyAccount/legacy_invoice_pdf',
'invoice_logo' => 'MyAccount/invoice_logo',
'list_invoices' => 'MyAccount/list_invoices', #?
+ 'list_payments' => 'MyAccount/list_payments',
+ 'payment_receipt' => 'MyAccount/payment_receipt',
+ 'list_payby' => 'MyAccount/list_payby',
+ 'insert_payby' => 'MyAccount/insert_payby',
+ 'update_payby' => 'MyAccount/update_payby',
+ 'delete_payby' => 'MyAccount/delete_payby',
'cancel' => 'MyAccount/cancel', #add to ss cgi!
'payment_info' => 'MyAccount/payment_info',
'payment_info_renew_info' => 'MyAccount/payment_info_renew_info',
'process_prepay' => 'MyAccount/process_prepay',
'realtime_collect' => 'MyAccount/realtime_collect',
'list_pkgs' => 'MyAccount/list_pkgs', #add to ss (added?)
+ 'pkg_info' => 'MyAccount/pkg_info',
'list_svcs' => 'MyAccount/list_svcs', #add to ss (added?)
'list_svc_usage' => 'MyAccount/list_svc_usage',
'svc_status_html' => 'MyAccount/svc_status_html',
+ 'svc_status_hash' => 'MyAccount/svc_status_hash',
+ 'set_svc_status_hash' => 'MyAccount/set_svc_status_hash',
+ 'set_svc_status_listadd' => 'MyAccount/set_svc_status_listadd',
+ 'set_svc_status_listdel' => 'MyAccount/set_svc_status_listdel',
+ 'set_svc_status_vacationadd'=> 'MyAccount/set_svc_status_vacationadd',
+ 'set_svc_status_vacationdel'=> 'MyAccount/set_svc_status_vacationdel',
'acct_forward_info' => 'MyAccount/acct_forward_info',
+ 'process_acct_forward' => 'MyAccount/process_acct_forward',
'list_dsl_devices' => 'MyAccount/list_dsl_devices',
'add_dsl_device' => 'MyAccount/add_dsl_device',
'delete_dsl_device' => 'MyAccount/delete_dsl_device',
'part_svc_info' => 'MyAccount/part_svc_info',
'provision_acct' => 'MyAccount/provision_acct',
'provision_phone' => 'MyAccount/provision_phone',
+ 'provision_pbx' => 'MyAccount/provision_pbx',
'provision_external' => 'MyAccount/provision_external',
+ 'provision_forward' => 'MyAccount/provision_forward',
'unprovision_svc' => 'MyAccount/unprovision_svc',
'myaccount_passwd' => 'MyAccount/myaccount_passwd',
'reset_passwd' => 'MyAccount/reset_passwd',
'check_reset_passwd' => 'MyAccount/check_reset_passwd',
'process_reset_passwd' => 'MyAccount/process_reset_passwd',
+ 'validate_passwd' => 'MyAccount/validate_passwd',
+ 'list_tickets' => 'MyAccount/list_tickets',
'create_ticket' => 'MyAccount/create_ticket',
'get_ticket' => 'MyAccount/get_ticket',
'adjust_ticket_priority' => 'MyAccount/adjust_ticket_priority',
'access_info' => 'MyAccount/access_info',
'domain_select_hash' => 'Signup/domain_select_hash', # expose?
'new_customer' => 'Signup/new_customer',
+ 'new_customer_minimal' => 'Signup/new_customer_minimal',
'capture_payment' => 'Signup/capture_payment',
+ 'new_prospect' => 'Signup/new_prospect',
#N/A 'clear_signup_cache' => 'Signup/clear_cache',
'new_agent' => 'Agent/new_agent',
'agent_login' => 'Agent/agent_login',
'call_time' => 'PrepaidPhone/call_time',
'call_time_nanpa' => 'PrepaidPhone/call_time_nanpa',
'phonenum_balance' => 'PrepaidPhone/phonenum_balance',
- #izoom
- #'bulk_processrow' => 'Bulk/processrow',
- #conflicts w/Agent one# 'check_username' => 'Bulk/check_username',
- #sg
- 'ping' => 'SGNG/ping',
- 'decompify_pkgs' => 'SGNG/decompify_pkgs',
- 'previous_payment_info' => 'SGNG/previous_payment_info',
- 'previous_payment_info_renew_info'
- => 'SGNG/previous_payment_info_renew_info',
- 'previous_process_payment' => 'SGNG/previous_process_payment',
- 'previous_process_payment_order_pkg'
- => 'SGNG/previous_process_payment_order_pkg',
- 'previous_process_payment_change_pkg'
- => 'SGNG/previous_process_payment_change_pkg',
- 'previous_process_payment_order_renew'
- => 'SGNG/previous_process_payment_order_renew',
+
+ 'start_thirdparty' => 'MyAccount/start_thirdparty',
+ 'finish_thirdparty' => 'MyAccount/finish_thirdparty',
+
+ 'list_quotations' => 'MyAccount/quotation/list_quotations',
+ 'quotation_new' => 'MyAccount/quotation/quotation_new',
+ 'quotation_delete' => 'MyAccount/quotation/quotation_delete',
+ 'quotation_info' => 'MyAccount/quotation/quotation_info',
+ 'quotation_print' => 'MyAccount/quotation/quotation_print',
+ 'quotation_add_pkg' => 'MyAccount/quotation/quotation_add_pkg',
+ 'quotation_remove_pkg' => 'MyAccount/quotation/quotation_remove_pkg',
+ 'quotation_order' => 'MyAccount/quotation/quotation_order',
+
+ 'freesideinc_service' => 'Freeside/freesideinc_service',
+
);
@EXPORT_OK = (
keys(%autoload),
my $packet = shift;
warn "sending ". $packet->{_packet}. " to server"
if $DEBUG;
- socket(SOCK, PF_UNIX, SOCK_STREAM, 0) or die "socket: $!";
- connect(SOCK, sockaddr_un($socket)) or die "connect to $socket: $!";
- nstore_fd($packet, \*SOCK) or die "can't send packet: $!";
- SOCK->flush;
- #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
+ # Retry socket operation 5 times per second
+ # until successful or $max_retry
+ my $max_retry = 25;
+ my $sock_timeout = 5;
+ my $enable_sock_timeout = 0;
- #block until there is a message on socket
-# my $w = new IO::Select;
-# $w->add(\*SOCK);
-# my @wait = $w->can_read;
+ for my $try ( 1..$max_retry ) {
+ local $@;
- warn "reading message from server"
- if $DEBUG;
+ my $return;
- my $return = fd_retrieve(\*SOCK) or die "error reading result: $!";
- die $return->{'_error'} if defined $return->{_error} && $return->{_error};
+ eval {
+ local $SIG{ALRM} = sub{die "socket $socket: timeout ${sock_timeout}s"};
+ alarm $sock_timeout
+ if $enable_sock_timeout;
- warn "returning message to client"
- if $DEBUG;
+ socket(SOCK, PF_UNIX, SOCK_STREAM, 0) or die "socket: $!";
+ connect(SOCK, sockaddr_un($socket)) or die "connect to $socket: $!";
+ nstore_fd($packet, \*SOCK) or die "can't send packet: $!";
+ SOCK->flush;
- $return;
+ # 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
+ # block until there is a message on socket
+ # my $w = new IO::Select;
+ # $w->add(\*SOCK);
+ # my @wait = $w->can_read;
+
+ warn "reading message from server"
+ if $DEBUG;
+
+ $return = fd_retrieve(\*SOCK) or die "error reading result: $!";
+ die $return->{'_error'} if defined $return->{_error} && $return->{_error};
+
+ warn "returning message to client"
+ if $DEBUG;
+
+ return $return;
+ };
+
+ return $return
+ unless $@;
+
+ die "(Attempt $try) $@"
+ if $try == $max_retry;
+
+ warn "(Attempt $try) $@"
+ if $DEBUG;
+
+ Time::HiRes::sleep(0.2);
+ }
}
=head1 NAME
# "my account" functionality
use FS::SelfService qw( login customer_info invoice cancel payment_info process_payment );
+ #new-style login with an email address and password
+ # can also be used for svc_acct login, set $emailaddress to username@domain
+ my $rv = login ( { 'email' => $emailaddress,
+ 'password' => $password,
+ },
+ );
+ if ( $rv->{'error'} ) {
+ #handle login error...
+ } else {
+ #successful login
+ $session_id = $rv->{'session_id'};
+ }
+
+ #classic svc_acct-based login with separate username and password
my $rv = login( { 'username' => $username,
'domain' => $domain,
'password' => $password,
}
);
+ if ( $rv->{'error'} ) {
+ #handle login error...
+ } else {
+ #successful login
+ $session_id = $rv->{'session_id'};
+ }
+ #svc_phone login with phone number and PIN
+ my $rv = login( { 'username' => $phone_number,
+ 'domain' => 'svc_phone',
+ 'password' => $pin,
+ }
+ );
if ( $rv->{'error'} ) {
#handle login error...
} else {
#successful login
- my $session_id = $rv->{'session_id'};
+ $session_id = $rv->{'session_id'};
}
my $customer_info = customer_info( { 'session_id' => $session_id } );
- #payment_info and process_payment are available in 1.5+ only
my $payment_info = payment_info( { 'session_id' => $session_id } );
#!!! process_payment example
#!!! list_pkgs example
- #!!! order_pkg example
+ #ordering a package with an svc_acct service
+ my $rv = order_pkg( { 'session_id' => $session_id,
+ 'pkgpart' => $pkgpart,
+ 'svcpart' => $svcpart,
+ 'username' => $username,
+ 'domsvc' => $domsvc, #svcnum of svc_domain
+ '_password' => $password,
+ }
+ );
+
+ #!!! ordering a package with an svc_domain service example
+
+ #!!! ordering a package with an svc_phone service example
+
+ #!!! ordering a package with an svc_external service example
+
+ #!!! ordering a package with an svc_pbx service
+
+ #ordering a package with no service
+ my $rv = order_pkg( { 'session_id' => $session_id,
+ 'pkgpart' => $pkgpart,
+ 'svcpart' => 'none',
+ }
+ );
+
+ #quoting a package, then ordering after confirmation
+
+ my $rv = quotation_new({ 'session_id' => $session_id });
+ my $qnum = $rv->{quotationnum};
+ # add packages to the quotation
+ $rv = quotation_add_pkg({ 'session_id' => $session_id,
+ 'quotationnum' => $qnum,
+ 'pkgpart' => $pkgpart,
+ 'quantity' => $quantity, # defaults to 1
+ });
+ # repeat until all packages are added
+ # view the pricing information
+ $rv = quotation_info({ 'session_id' => $session_id,
+ 'quotationnum' => $qnum,
+ });
+ print "Total setup charges: ".$rv->{total_setup}."\n".
+ "Total recurring charges: ".$rv->{total_recur}."\n";
+ # quotation_info also provides a detailed breakdown of charges, in
+ # $rv->{sections}.
+
+ # ask customer for confirmation, then:
+ $rv = quotation_order({ 'session_id' => $session_id,
+ 'quotationnum' => $qnum,
+ });
#!!! cancel_pkg example
# signup functionality
- use FS::SelfService qw( signup_info new_customer );
+ use FS::SelfService qw( signup_info new_customer new_customer_minimal );
my $signup_info = signup_info;
=item passwd
+Changes the password for an existing user in svc_acct. Takes a hash
+reference with the following keys:
+
+=over 4
+
+=item username
+
+Username of the account (required)
+
+=item domain
+
+Domain of the account (required)
+
+=item old_password
+
+Old password (required)
+
+=item new_password
+
+New password (required)
+
+=item new_gecos
+
+New gecos
+
+=item new_shell
+
+New Shell
+
+=back
+
=item chfn
=item chsh
=over 4
+=item email
+
+Email address (username@domain), instead of username and domain. Required for
+contact-based self-service login, can also be used for svc_acct-based login.
+
=item username
Username
=back
+=item customer_recurring HASHREF
+
+Takes a hash reference as parameter with a single key B<session_id>
+or keys B<agent_session_id> and B<custnum>.
+
+Returns a hash reference with the keys error, custnum and display_recurring.
+
+display_recurring is an arrayref of hashrefs with the following keys:
+
+=over 4
+
+=item freq
+
+frequency of charge, in months unless units are specified
+
+=item freq_pretty
+
+frequency of charge, suitable for display
+
+=item amount
+
+amount charged at this frequency
+
+=back
+
=item edit_info HASHREF
Takes a hash reference as parameter with any of the following keys:
=item list_invoices HASHREF
-Returns a list of all customer invoices. Takes a hash references with a single
+Returns a list of all customer invoices. Takes a hash reference with a single
key, session_id.
Returns a hash reference with the following keys:
=back
+=item list_payments HASHREF
+
+Returns a list of all customer payments. Takes a hash reference with a single
+key, session_id.
+
+Returns a hash reference with the following keys:
+
+=over 4
+
+=item error
+
+Empty on success, or an error message on errors
+
+=item payments
+
+Reference to array of hash references with the following keys:
+
+=over 4
+
+=item paynum
+
+Payment #
+
+=item _date
+
+Payument date, in UNIX epoch time
+
+=item date
+
+Payment date, in a human-readable format
+
+=item date_short
+
+Payment date, in a shorter human-readable format
+
+=item paid
+
+Amount paid
+
+=item payby
+
+Payment method: CARD, CHEK (electronic check), or BILL (physical check).
+
+=item paycardtype
+
+Payment card type
+
+=item paymask
+
+Payment card mask
+
+=item processor
+
+Processor for cards and electronic checks
+
+=item auth
+
+Authorization number
+
+=item order_number
+
+Order number
+
+=back
+
+=back
+
+=item list_payby HASHREF
+
+Returns a list of all stored customer payment information (credit cards and
+electronic check accounts). Takes a hash reference with a single key,
+session_id.
+
+Returns a hash reference with the following keys:
+
+=over 4
+
+=item error
+
+Empty on success, or an error message on errors
+
+=item payby
+
+Reference to array of hash references with the following keys:
+
+=over 4
+
+=item custpaybynum
+
+=item weight
+
+Numeric weighting. Stored payment information with a lower weight is attempted
+first.
+
+=item payby
+
+CARD (Automatic credit card), CHEK (Automatic electronic check), DCRD
+(on-demand credit card) or DCHK (on-demand electronic check).
+
+=item paymask
+
+Masked credit card number (or, masked account and routing numbers)
+
+=item paydate
+
+Credit card expiration date
+
+=item payname
+
+Exact name on card (or bank name, for electronic checks)
+
+=item paystate
+
+For electronic checks, bank state
+
+=item paytype
+
+For electronic checks, account type (Personal/Business, Checking/Savings)
+
+=back
+
+=back
+
+=item insert_payby HASHREF
+
+Adds new stored payment information for this customer. Takes a hash reference
+with the following keys:
+
+=over 4
+
+=item session_id
+
+=item weight
+
+Numeric weighting. Stored payment information with a lower weight is attempted
+first.
+
+=item payby
+
+CARD (Automatic credit card), CHEK (Automatic electronic check), DCRD
+(on-demand credit card) or DCHK (on-demand electronic check).
+
+=item payinfo
+
+Credit card number (or electronic check "account@routing")
+
+=item paycvv
+
+CVV2 number / security code
+
+=item paydate
+
+Credit card expiration date
+
+=item payname
+
+Exact name on card (or bank name, for electronic checks)
+
+=item paystate
+
+For electronic checks, bank state
+
+=item paytype
+
+For electronic checks, account type (i.e. "Personal Savings", "Personal Checking", "Business Checking")A
+
+=item payip
+
+Optional IP address from which payment was submitted
+
+=back
+
+If there is an error, returns a hash reference with a single key, B<error>,
+otherwise returns a hash reference with a single key, B<custpaybynum>.
+
+=item update_payby HASHREF
+
+Updates stored payment information. Takes a hash reference with the same
+keys as insert_payby, as well as B<custpaybynum> to specify which record
+to update. All keys except B<session_id> and B<custpaybynum> are optional;
+if omitted, the previous values in the record will be preserved.
+
+If there is an error, returns a hash reference with a single key, B<error>,
+otherwise returns a hash reference with a single key, B<custpaybynum>.
+
+=item delete_payby HASHREF
+
+Removes stored payment information. Takes a hash reference with two keys,
+B<session_id> and B<custpaybynum>. Returns a hash reference with a single key,
+B<error>, which is an error message or empty for successful removal.
+
=item cancel HASHREF
Cancels this customer.
Returns information that may be useful in displaying a payment page.
-Takes a hash reference as parameter with a single key: B<session_id>.
+Takes a hash reference as parameter with the following keys:
+
+=over 4
+
+=item session_id
+
+Required session ID
+
+=item payment_payby
+
+=item omit_cust_main_county
+
+Optional, pass a true value to omit cust_main_county data for performance.
+
+=back
Returns a hash reference with the following keys:
=back
+=item pkg_info
+
+Returns package information for package.
+
+Takes a hash reference as parameter with the following keys:
+
+=over 4
+
+=item session_id
+
+Session identifier
+
+=item pkgnum
+
+Package Number
+
+=back
+
+Returns a hash reference containing customer package information. The hash reference contains the following keys:
+
+=over 4
+
+=item pkg_label
+
+Name of this package
+
+=item pkgpart
+
+Part package primary key
+
+=item classnum
+
+Package class number
+
+=item error
+
+error message if errror.
+
+=back
+
=item list_svcs
Returns service information for this customer.
Orders a package for this customer.
+If signup_server-realtime is set, bills the new package, attemps to collect
+payment and (for auto-payment customers) cancels the package if the payment is
+declined.
+
Takes a hash reference as parameter with the following keys:
=over 4
Package to order (see L<FS::part_pkg>).
-=item svcpart
+=item quantity
-Service to order (see L<FS::part_svc>).
+Quantity for this package order (default 1).
-Normally optional; required only to provision a non-svc_acct service, or if the
-package definition does not contain one svc_acct service definition with
-quantity 1 (it may contain others with quantity >1). A svcpart of "none" can
-also be specified to indicate that no initial service should be provisioned.
+=item run_bill_events
-=back
+If true, runs billing events for the customer after ordering and billing the
+package (signup_server-realtime must be set).
-Fields used when provisioning an svc_acct service:
+=item locationnum
-=over 4
+Optional locationnum for this package order, for existing locations.
-=item username
+Or, for new locations, pass the following fields: address1*, address2, city*,
+county, state*, zip*, country. (* = required in this case)
-Username
+(None of this is required at all if you are just ordering a package
+at the customer's existing default service location.)
-=item _password
+=item address1
-Password
+=item address2
-=item sec_phrase
+=item city
+
+=item county
+
+=item state
+
+=item zip
+
+=item country
+
+=item svcpart
+
+Service to order (see L<FS::part_svc>).
+
+Normally optional; required only to provision a non-svc_acct service, or if the
+package definition does not contain one svc_acct service definition with
+quantity 1 (it may contain others with quantity >1). A svcpart of "none" can
+also be specified to indicate that no initial service should be provisioned.
+
+=back
+
+Fields used when provisioning an svc_acct service:
+
+=over 4
+
+=item username
+
+Username
+
+=item _password
+
+Password
+
+=item sec_phrase
Optional security phrase
error message on errors. The special error '_decline' is returned for
declined transactions.
-=item change_pkg
+=item change_pkg
+
+Changes a package for this customer.
+
+Takes a hash reference as parameter with the following keys:
+
+=over 4
+
+=item session_id
+
+Session identifier
+
+=item pkgnum
+
+Existing customer package.
+
+=item pkgpart
+
+New package to order (see L<FS::part_pkg>).
+
+=item quantity
+
+Quantity for this package order (default 1).
+
+=back
+
+Returns a hash reference with the following keys:
+
+=over 4
+
+=item error
+
+Empty on success, or an error message on errors.
+
+=item pkgnum
+
+On success, the new pkgnum
+
+=back
+
+
+=item renew_info
+
+Provides useful info for early renewals.
+
+Takes a hash reference as parameter with the following keys:
+
+=over 4
+
+=item session_id
+
+Session identifier
+
+=back
+
+Returns a hash reference. On errors, it contains a single key, B<error>, with
+the error message. Otherwise, contains a single key, B<dates>, pointing to
+an array refernce of hash references. Each hash reference contains the
+following keys:
+
+=over 4
+
+=item bill_date
+
+(Future) Bill date. Indicates a future date for which billing could be run.
+Specified as an integer UNIX timestamp. Pass this value to the B<order_renew>
+function.
+
+=item bill_date_pretty
+
+(Future) Bill date as a human-readable string. (Convenience for display;
+subject to change, so best not to parse for the date.)
+
+=item amount
+
+Base amount which will be charged if renewed early as of this date.
+
+=item renew_date
+
+Renewal date; i.e. even-futher future date at which the customer will be paid
+through if the early renewal is completed with the given B<bill-date>.
+Specified as an integer UNIX timestamp.
+
+=item renew_date_pretty
+
+Renewal date as a human-readable string. (Convenience for display;
+subject to change, so best not to parse for the date.)
+
+=item pkgnum
+
+Package that will be renewed.
+
+=item expire_date
+
+Expiration date of the package that will be renewed.
+
+=item expire_date_pretty
+
+Expiration date of the package that will be renewed, as a human-readable
+string. (Convenience for display; subject to change, so best not to parse for
+the date.)
+
+=back
+
+=item order_renew
+
+Renews this customer early; i.e. runs billing for this customer in advance.
+
+Takes a hash reference as parameter with the following keys:
+
+=over 4
+
+=item session_id
+
+Session identifier
+
+=item date
+
+Integer date as returned by the B<renew_info> function, indicating the advance
+date for which to run billing.
+
+=back
+
+Returns a hash reference with a single key, B<error>, empty on success, or an
+error message on errors.
+
+=item cancel_pkg
+
+Cancels a package for this customer.
+
+Takes a hash reference as parameter with the following keys:
+
+=over 4
+
+=item session_id
+
+Session identifier
+
+=item pkgpart
+
+pkgpart of package to cancel
+
+=item date
+
+Optional date, for future cancellation (expiration) instead of immediate
+cancellation. Specified as an integer UNIX timestamp ("epoch time").
+
+=back
+
+Returns a hash reference with a single key, B<error>, empty on success, or an
+error message on errors.
+
+=item provision_acct
+
+Provisions an account (svc_acct).
+
+Takes a hash reference as parameter with the following keys:
+
+=over 4
+
+=item session_id
+
+Session identifier
+
+=item pkgnum
+
+pkgnum of package into which this service is provisioned
+
+=item svcpart
+
+svcpart or service definition to provision
+
+=item username
+
+=item domsvc
+
+=item _password
+
+=back
+
+=item provision_phone
+
+Provisions a phone number (svc_phone).
+
+Takes a hash reference as parameter with the following keys:
+
+=over 4
+
+=item session_id
+
+Session identifier
+
+=item pkgnum
+
+pkgnum of package into which this service is provisioned
+
+=item svcpart
+
+svcpart or service definition to provision
+
+=item countrycode
+
+=item phonenum
+
+=item address1
+
+=item address2
+
+=item city
+
+=item county
+
+=item state
+
+=item zip
+
+=item country
+
+E911 Address (optional)
+
+=back
+
+=item provision_pbx
+
+Provisions a customer PBX (svc_pbx).
+
+Takes a hash reference as parameter with the following keys:
+
+=over 4
+
+=item session_id
+
+Session identifier
+
+=item pkgnum
+
+pkgnum of package into which this service is provisioned
+
+=item svcpart
+
+svcpart or service definition to provision
+
+=item id
+
+=item title
+
+=item max_extensions
+
+=item max_simultaneous
+
+=item ip_addr
+
+=back
+
+=item provision_external
+
+Provisions an external service (svc_external).
+
+Takes a hash reference as parameter with the following keys:
+
+=over 4
+
+=item session_id
+
+Session identifier
+
+=item pkgnum
+
+pkgnum of package into which this service is provisioned
+
+=item svcpart
+
+svcpart or service definition to provision
+
+=item id
+
+=item title
+
+=back
+
+=back
+
+=head2 "MY ACCOUNT" CONTACT FUNCTIONS
+
+=over 4
+
+=item contact_passwd
+
+Changes the password for the currently-logged in contact.
+
+Takes a hash reference as parameter with the following keys:
+
+=over 4
+
+=item session_id
+
+=item new_password
+
+=back
+
+Returns a hash reference with a single parameter, B<error>, which contains an
+error message, or empty on success.
+
+=item list_contacts
+
+Takes a hash reference as parameter with a single key, B<session_id>.
+
+Returns a hash reference with two parameters: B<error>, which contains an error
+message, or empty on success, and B<contacts>, a list of contacts.
+
+B<contacts> is an array reference of hash references (i.e. an array of structs,
+ in XML-RPC). Each hash reference (struct) has the following keys:
+
+=over 4
+
+=item contactnum
+
+=item class
+
+Contact class name (contact type).
+
+=item first
+
+First name
+
+=item last
+
+Last name
+
+=item title
+
+Position ("Director of Silly Walks"), NOT honorific ("Mr." or "Mrs.")
+
+=item emailaddress
+
+Comma-separated list of email addresses
+
+=item comment
+
+=item selfservice_access
+
+Y when enabled
+
+=back
+
+=item edit_contact
+
+Updates information for the currently-logged in contact, or (optionally) the
+specified contact.
+
+Takes a hash reference as parameter with the following keys:
+
+=over 4
+
+=item session_id
+
+=item contactnum
+
+If already logged in as a contact, this is optional.
+
+=item first
+
+=item last
+
+=item emailaddress
+
+=back
+
+Returns a hash reference with a single parameter, B<error>, which contains an
+error message, or empty on success.
+
+=item new_contact
+
+Creates a new contact.
+
+Takes a hash reference as parameter with the following keys:
+
+=over 4
+
+=item session_id
+
+=item first
+
+=item last
+
+=item emailaddress
+
+=item classnum
+
+Optional contact classnum (TODO: or name)
+
+=item comment
+
+=item selfservice_access
+
+Y to enable self-service access
+
+=item _password
+
+=back
+
+Returns a hash reference with a single parameter, B<error>, which contains an
+error message, or empty on success.
+
+=item delete_contact
+
+Deletes a contact. (Note: Cannot at this time delete the currently-logged in
+contact.)
+
+Takes a hash reference as parameter with the following keys:
+
+=over 4
+
+=item session_id
+
+=item contactnum
+
+=back
+
+Returns a hash reference with a single parameter, B<error>, which contains an
+error message, or empty on success.
+
+=back
+
+=head2 "MY ACCOUNT" QUOTATION FUNCTIONS
+
+All of these functions require the user to be logged in, and the 'session_id'
+key to be included in the argument hashref.`
+
+=over 4
+
+=item list_quotations HASHREF
+
+Returns a hashref listing this customer's active self-service quotations.
+Contents are:
+
+=over 4
+
+=item quotations
+
+an arrayref containing an element for each quotation.
+
+=item quotationnum
-Changes a package for this customer.
+the primary key
-Takes a hash reference as parameter with the following keys:
+=item _date
-=over 4
+the date it was started
-=item session_id
+=item num_pkgs
-Session identifier
+the number of packages
-=item pkgnum
+=item total_setup
-Existing customer package.
+the sum of setup fees
-=item pkgpart
+=item total_recur
-New package to order (see L<FS::part_pkg>).
+the sum of recurring charges
=back
-Returns a hash reference with the following keys:
+=item quotation_new HASHREF
-=over 4
+Creates an empty quotation and returns a hashref containing 'quotationnum',
+the primary key of the new quotation.
-=item error
+=item quotation_delete HASHREF
-Empty on success, or an error message on errors.
+Disables (does not really delete) a quotation. Takes the following arguments:
-=item pkgnum
+=over 4
-On success, the new pkgnum
+=item session_id
+
+=item quotationnum - the quotation to delete
=back
+Returns 'error' => a string, which will be empty on success.
-=item renew_info
+=item quotation_info HASHREF
-Provides useful info for early renewals.
+Returns total and detailed pricing information on a quotation.
-Takes a hash reference as parameter with the following keys:
+Takes the following arguments:
=over 4
=item session_id
-Session identifier
+=item quotationnum - the quotation to return
=back
-Returns a hash reference. On errors, it contains a single key, B<error>, with
-the error message. Otherwise, contains a single key, B<dates>, pointing to
-an array refernce of hash references. Each hash reference contains the
-following keys:
+Returns a hashref containing:
-=over 4
+- total_setup, the total of setup fees (and their taxes)
+- total_recur, the total of all recurring charges (and their taxes)
+- sections, an arrayref containing an element for each quotation section.
+ - description, a line of text describing the group of charges
+ - subtotal, the total of charges in this group (if appropriate)
+ - detail_items, an arrayref of line items
+ - pkgnum, the reference number of the package
+ - description, the package name (or tax name)
+ - quantity
+ - amount, the amount charged
+ If the detail item represents a subtotal, it will instead contain:
+ - total_item: description of the subtotal
+ - total_amount: the subtotal amount
-=item bill_date
-(Future) Bill date. Indicates a future date for which billing could be run.
-Specified as a integer UNIX timestamp. Pass this value to the B<order_renew>
-function.
+=item quotation_print HASHREF
-=item bill_date_pretty
+Renders the quotation as HTML or PDF. Takes the following arguments:
-(Future) Bill date as a human-readable string. (Convenience for display;
-subject to change, so best not to parse for the date.)
+=over 4
-=item amount
+=item session_id
-Base amount which will be charged if renewed early as of this date.
+=item quotationnum - the quotation to return
-=item renew_date
+=item format - 'html' or 'pdf'
-Renewal date; i.e. even-futher future date at which the customer will be paid
-through if the early renewal is completed with the given B<bill-date>.
-Specified as a integer UNIX timestamp.
+=back
-=item renew_date_pretty
+Returns a hashref containing 'document', the contents of the file.
-Renewal date as a human-readable string. (Convenience for display;
-subject to change, so best not to parse for the date.)
+=item quotation_add_pkg HASHREF
-=item pkgnum
+Adds a package to a quotation. Takes the following arguments:
-Package that will be renewed.
+=over 4
-=item expire_date
+=item session_id
-Expiration date of the package that will be renewed.
+=item pkgpart - the package to add
-=item expire_date_pretty
+=item quotationnum - the quotation to add it to
-Expiration date of the package that will be renewed, as a human-readable
-string. (Convenience for display; subject to change, so best not to parse for
-the date.)
+=item quantity - the package quantity (defaults to 1)
+
+=item address1, address2, city, state, zip, country - address fields to set
+the service location
=back
-=item order_renew
+Returns 'error' => a string, which will be empty on success.
-Renews this customer early; i.e. runs billing for this customer in advance.
+=item quotation_remove_pkg HASHREF
-Takes a hash reference as parameter with the following keys:
+Removes a package from a quotation. Takes the following arguments:
=over 4
=item session_id
-Session identifier
-
-=item date
+=item pkgnum - the primary key (quotationpkgnum) of the package to remove
-Integer date as returned by the B<renew_info> function, indicating the advance
-date for which to run billing.
+=item quotationnum - the quotation to remove it from
=back
-Returns a hash reference with a single key, B<error>, empty on success, or an
-error message on errors.
+Returns 'error' => a string, which will be empty on success.
-=item cancel_pkg
+=item quotation_order HASHREF
-Cancels a package for this customer.
+Converts the packages in a quotation into real packages. Takes the following
+arguments:
-Takes a hash reference as parameter with the following keys:
+Takes the following arguments:
=over 4
=item session_id
-Session identifier
-
-=item pkgpart
-
-pkgpart of package to cancel
+=item quotationnum - the quotation to order
=back
-Returns a hash reference with a single key, B<error>, empty on success, or an
-error message on errors.
-
=back
=head1 SIGNUP FUNCTIONS
=back
+=item new_customer_minimal HASHREF
+
+Creates a new customer.
+
+Current differences from new_customer: An address is not required. promo_code
+and reg_code are not supported. If invoicing_list and _password is passed, a
+contact will be created with self-service access (no pkgpart or username is
+necessary). No initial billing is run (this may change in a future version).
+
+Takes a hash reference as parameter with the following keys:
+
+=over 4
+
+=item first
+
+first name (required)
+
+=item last
+
+last name (required)
+
+=item ss
+
+(not typically collected; mostly used for ACH transactions)
+
+=item company
+
+Company name
+
+=item address1
+
+Address line one
+
+=item address2
+
+Address line two
+
+=item city
+
+City
+
+=item county
+
+County
+
+=item state
+
+State
+
+=item zip
+
+Zip or postal code
+
+=item daytime
+
+Daytime phone number
+
+=item night
+
+Evening phone number
+
+=item fax
+
+Fax number
+
+=item payby
+
+CARD, DCRD, CHEK, DCHK, LECB, BILL, COMP or PREPAY (see L</signup_info> (required)
+
+=item payinfo
+
+Card number for CARD/DCRD, account_number@aba_number for CHEK/DCHK, prepaid "pin" for PREPAY, purchase order number for BILL
+
+=item paycvv
+
+Credit card CVV2 number (1.5+ or 1.4.2 with CVV schema patch)
+
+=item paydate
+
+Expiration date for CARD/DCRD
+
+=item payname
+
+Exact name on credit card for CARD/DCRD, bank name for CHEK/DCHK
+
+=item invoicing_list
+
+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),
+
+=item referral_custnum
+
+referring customer number
+
+=item agentnum
+
+Agent number
+
+=item pkgpart
+
+pkgpart of initial package
+
+=item username
+
+Username
+
+=item _password
+
+Password
+
+=item sec_phrase
+
+Security phrase
+
+=item popnum
+
+Access number (index, not the literal number)
+
+=item countrycode
+
+Country code (to be provisioned as a service)
+
+=item phonenum
+
+Phone number (to be provisioned as a service)
+
+=item pin
+
+Voicemail PIN
+
+=back
+
+Returns a hash reference with the following keys:
+
+=over 4
+
+=item error
+
+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)
+
+=back
+
=item new_customer HASHREF
Creates a new customer. Takes a hash reference as parameter with the
Zip or postal code
+=item ship_address1
+
+=item ship_address2
+
+=item ship_city
+
+=item ship_county
+
+=item ship_state
+
+=item ship_zip
+
+Optional shipping address fields. If sending an optional shipping address,
+ship_address1, ship_city, ship_state and ship_zip are required.
+
=item daytime
Daytime phone number
my $prefix = exists($param->{'prefix'}) ? $param->{'prefix'} : '';
- my $countyflag = 0;
+ my $disabled = $param->{'disabled'};
+
+ my $countyflag = $param->{selected_county} ? 1 : 0;
+ my $cityflag = $param->{selected_city} ? 1 : 0;
my %cust_main_county;
foreach my $c ( @{ $param->{'locales'} } ) {
#$countyflag=1 if $c->county;
$countyflag=1 if $c->{county};
+ $cityflag=1 if ($c->{city} && $cityflag);
#push @{$cust_main_county{$c->country}{$c->state}}, $c->county;
#$cust_main_county{$c->country}{$c->state}{$c->county} = 1;
- $cust_main_county{$c->{country}}{$c->{state}}{$c->{county}} = 1;
+ $cust_main_county{$c->{country}}{$c->{state}}{$c->{county}}{$c->{city}} = 1;
}
# }
- $countyflag=1 if $param->{selected_county};
my $script_html = <<END;
<SCRIPT>
- function opt(what,value,text) {
- var optionName = new Option(text, value, false, false);
+ function opt(what,value,text,selected) {
+ var optionName = new Option(text, value, false, selected);
var length = what.length;
what.options[length] = optionName;
}
#foreach my $county ( sort @{$cust_main_county{$country}{$state}} ) {
foreach my $county ( sort keys %{$cust_main_county{$country}{$state}} ) {
my $text = $county || '(n/a)';
- $script_html .=
- qq!opt(what.form.${prefix}county, "$county", "$text");\n!;
+ if (!$county) {
+ if ( $cityflag) {
+ $script_html .= qq!what.form.${prefix}city.style.display='';\n
+ what.form.${prefix}city_select.style.display='none';\n!
+ }
+ $script_html .= qq!opt(what.form.${prefix}county, "$county", "$text");\n!
+ #$script_html .= qq!what.form.${prefix}county.style.display='none';\n!
+ }
+ else {
+ $script_html .= qq!var countySelected = false; if ("$param->{selected_county}" == "$text") { countySelected = true; }\n
+ opt(what.form.${prefix}county, "$county", "$text", countySelected);\n
+ what.form.${prefix}county.style.display='';\n
+ county = what.form.${prefix}county.options[what.form.${prefix}county.selectedIndex].text;\n!;
+ if ( $cityflag) {
+ $script_html .= qq!\nif ( county == \"$county\" ) {\n!;
+ foreach my $city ( sort keys %{$cust_main_county{$country}{$state}{$county}} ) {
+ my $text = $city || '(n/a)';
+ if (!$city) {
+ $script_html .= qq!what.form.${prefix}city.style.display='';\n
+ what.form.${prefix}city_select.style.display='none';\n!
+ }
+ else {
+ $script_html .= qq!var citySelected = false; if ("$param->{selected_city}" == "$text") { citySelected = true; }\n
+ opt(what.form.${prefix}city_select, "$city", "$text", citySelected);\n
+ what.form.${prefix}city.style.display='none';\n
+ what.form.${prefix}city_select.style.display='';\n!
+ }
+ }
+ $script_html .= "}\n";
+ }
+ }
}
$script_html .= "}\n";
}
}
}
+ $script_html .= <<END;
+ }
+ function ${prefix}county_changed(what) {
+END
+
+ if ( $cityflag) {
+ $script_html .= <<END;
+ saved_city = "$param->{selected_city}";
+ county = what.options[what.selectedIndex].text;
+ state = what.form.${prefix}state.options[what.form.${prefix}state.selectedIndex].text;
+ country = what.form.${prefix}country.options[what.form.${prefix}country.selectedIndex].text;
+ for ( var i = what.form.${prefix}city_select.length; i >= 0; i-- )
+ what.form.${prefix}city_select.options[i] = null;
+END
+
+ foreach my $country ( sort keys %cust_main_county ) {
+ $script_html .= "\nif ( country == \"$country\" ) {\n";
+ foreach my $state ( sort keys %{$cust_main_county{$country}} ) {
+ $script_html .= "\nif ( state == \"$state\" ) {\n";
+ #foreach my $county ( sort @{$cust_main_county{$country}{$state}} ) {
+ foreach my $county ( sort keys %{$cust_main_county{$country}{$state}} ) {
+ $script_html .= "\nif ( county == \"$county\" ) {\n";
+ foreach my $city ( sort keys %{$cust_main_county{$country}{$state}{$county}} ) {
+ my $text = $city || '(n/a)';
+ if (!$city) {
+ $script_html .= qq!what.form.${prefix}city.style.display='';\n
+ what.form.${prefix}city_select.style.display='none';\n!
+ }
+ else {
+ $script_html .= qq!var citySelected = false; if (saved_city == "$text") { citySelected = true; }\n
+ opt(what.form.${prefix}city_select, "$city", "$text", citySelected);\n
+ what.form.${prefix}city.style.display='none';\n
+ what.form.${prefix}city_select.style.display='';\n!
+ }
+ }
+ $script_html .= "}\n";
+ }
+ $script_html .= "}\n";
+ }
+ $script_html .= "}\n";
+ }
+ }
+
+ $script_html .= <<END;
+ }
+ function ${prefix}city_select_changed(what) {
+END
+
+ if ( $cityflag ) {
+ $script_html .= <<END;
+ what.form.${prefix}city.value = what.options[what.selectedIndex].value;
+END
+ }
+
$script_html .= <<END;
}
</SCRIPT>
END
+ my $city_html = '';
+ if ( $cityflag ) {
+ if ( scalar (keys %{ $cust_main_county{$param->{'selected_country'}}{$param->{'selected_state'}}{$param->{'selected_county'}} }) > 1 ) {
+ $city_html .= qq!<SELECT NAME="${prefix}city_select" onChange="${prefix}city_select_changed(this); $param->{'onchange'}">!;
+ foreach my $city (
+ sort keys %{ $cust_main_county{$param->{'selected_country'}}{$param->{'selected_state'}}{$param->{'selected_county'}} }
+ ) {
+ my $text = $city || '(n/a)';
+ $city_html .= qq!<OPTION VALUE="$city"!.
+ ($city eq $param->{'selected_city'} ?
+ ' SELECTED>' :
+ '>'
+ ).
+ $text;
+ }
+ $city_html .= qq!</OPTION><INPUT TYPE="text" ID="${prefix}city" NAME="${prefix}city" VALUE="$param->{'selected_city'}" style="display:none">!;
+ } else {
+ $city_html .= qq!<SELECT NAME="${prefix}city_select" onChange="${prefix}city_select_changed(this); $param->{'onchange'}" style="display:none"></SELECT>
+ <INPUT TYPE="text" ID="${prefix}city" NAME="${prefix}city" VALUE="$param->{'selected_city'}" style="display:''">!;
+ }
+ }
+
my $county_html = $script_html;
if ( $countyflag ) {
- $county_html .= qq!<SELECT NAME="${prefix}county" onChange="$param->{'onchange'}">!;
+ $county_html .= qq!<SELECT NAME="${prefix}county" !.
+ qq!onChange="${prefix}county_changed(this); $param->{'onchange'}">!;
foreach my $county (
sort keys %{ $cust_main_county{$param->{'selected_country'}}{$param->{'selected_state'}} }
) {
foreach my $state ( sort keys %{ $cust_main_county{$param->{'selected_country'}} } ) {
my $text = $state || '(n/a)';
my $selected = $state eq $param->{'selected_state'} ? 'SELECTED' : '';
- $state_html .= "\n<OPTION $selected VALUE=$state>$text</OPTION>"
+ $state_html .= "\n<OPTION $selected VALUE=\"$state\">$text</OPTION>"
}
$state_html .= '</SELECT>';
my $selected = $country eq $param->{'selected_country'}
? ' SELECTED'
: '';
- $country_html .= "\n<OPTION$selected>$country</OPTION>"
+ $country_html .= "\n<OPTION $selected>$country</OPTION>"
}
$country_html .= '</SELECT>';
} else {
}
- ($county_html, $state_html, $country_html);
+ ($county_html, $state_html, $country_html, $city_html);
}
'<INPUT TYPE="hidden" NAME="domsvc" VALUE="'. $key. '"></TD></TR>'
}
- my $text .= qq!<TR><TD ALIGN="right">Domain</TD><TD><SELECT NAME="domsvc" SIZE=1 STYLE="width: 20em"><OPTION>(Choose Domain)!;
+ my $text .= qq!<TR><TD ALIGN="right">Domain</TD><TD><SELECT NAME="domsvc" SIZE=1 STYLE="width: 20em">!;
+ $text .= '<OPTION>(Choose Domain)' unless $domsvc;
foreach my $domain ( sort { $domains->{$a} cmp $domains->{$b} } keys %$domains ) {
$text .= qq!<OPTION VALUE="!. $domain. '"'.