run_bill_events option for order_pkg, RT#42053
[freeside.git] / fs_selfservice / FS-SelfService / SelfService.pm
index 92f2017..bc54b1e 100644 (file)
@@ -26,11 +26,21 @@ $socket .= '.'.$tag if defined $tag && length($tag);
   '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',
+
+  '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',
@@ -38,6 +48,9 @@ $socket .= '.'.$tag if defined $tag && length($tag);
   'legacy_invoice_pdf'        => 'MyAccount/legacy_invoice_pdf',
   'invoice_logo'              => 'MyAccount/invoice_logo',
   'list_invoices'             => 'MyAccount/list_invoices', #?
+  'list_payby'                => 'MyAccount/list_payby',
+  'insert_payby'              => 'MyAccount/insert_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',
@@ -52,6 +65,15 @@ $socket .= '.'.$tag if defined $tag && length($tag);
   'list_pkgs'                 => 'MyAccount/list_pkgs',     #add to ss (added?)
   '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',   
@@ -69,12 +91,16 @@ $socket .= '.'.$tag if defined $tag && length($tag);
   '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',
@@ -84,7 +110,9 @@ $socket .= '.'.$tag if defined $tag && length($tag);
   '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',
@@ -98,22 +126,21 @@ $socket .= '.'.$tag if defined $tag && length($tag);
   '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),
@@ -202,17 +229,43 @@ FS::SelfService - Freeside self-service API
   # "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 } );
@@ -224,12 +277,60 @@ FS::SelfService - Freeside self-service API
 
   #!!! 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;
 
@@ -292,6 +393,37 @@ see XXXX instead.
 
 =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
@@ -309,6 +441,11 @@ following keys:
 
 =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
@@ -408,7 +545,7 @@ Invoice text
 
 =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:
@@ -437,6 +574,120 @@ Invoice date, in UNIX epoch time
 
 =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 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.
@@ -821,6 +1072,10 @@ Number of total bytes gained by recharge
 
 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
@@ -833,6 +1088,39 @@ Session identifier
 
 Package to order (see L<FS::part_pkg>).
 
+=item quantity
+
+Quantity for this package order (default 1).
+
+=item run_bill_events
+
+If true, runs billing events for the customer after ordering and billing the
+package (signup_server-realtime must be set).
+
+=item locationnum
+
+Optional locationnum for this package order, for existing locations.
+
+Or, for new locations, pass the following fields: address1*, address2, city*,
+county, state*, zip*, country.  (* = required in this case)
+
+(None of this is required at all if you are just ordering a package
+at the customer's existing default service location.)
+
+=item address1
+
+=item address2
+
+=item city
+
+=item county
+
+=item state
+
+=item zip
+
+=item country
+
 =item svcpart
 
 Service to order (see L<FS::part_svc>).
@@ -946,6 +1234,10 @@ Existing customer package.
 
 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:
@@ -987,7 +1279,7 @@ following keys:
 =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>
+Specified as an integer UNIX timestamp.  Pass this value to the B<order_renew>
 function.
 
 =item bill_date_pretty
@@ -1003,7 +1295,7 @@ Base amount which will be charged if renewed early as of this 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 a integer UNIX timestamp.
+Specified as an integer UNIX timestamp.
 
 =item renew_date_pretty
 
@@ -1064,113 +1356,689 @@ Session identifier
 
 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.
 
-=back
+=item provision_acct 
 
-=head1 SIGNUP FUNCTIONS
+Provisions an account (svc_acct).
+
+Takes a hash reference as parameter with the following keys:
 
 =over 4
 
-=item signup_info HASHREF
+=item session_id
 
-Takes a hash reference as parameter with the following keys:
+Session identifier
 
-=over 4
+=item pkgnum
 
-=item session_id - Optional agent/reseller interface session
+pkgnum of package into which this service is provisioned
+
+=item svcpart
+
+svcpart or service definition to provision
+
+=item username
+
+=item domsvc
+
+=item _password
 
 =back
 
-Returns a hash reference containing information that may be useful in
-displaying a signup page.  The hash reference contains the following keys:
+=item provision_phone
+
+Provisions a phone number (svc_phone).
+
+Takes a hash reference as parameter with the following keys:
 
 =over 4
 
-=item cust_main_county
+=item session_id
 
-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.
+Session identifier
 
-=item part_pkg
+=item pkgnum
 
-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
-an agentnum specified explicitly via reseller interface session_id in the
-options.
+pkgnum of package into which this service is provisioned
 
-=item agent
+=item svcpart
 
-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.
+svcpart or service definition to provision
 
-=item agentnum2part_pkg
+=item countrycode
 
-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.
+=item phonenum
 
-=item svc_acct_pop
+=item address1
 
-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.
+=item address2
 
-=item security_phrase
+=item city
 
-True if the "security_phrase" feature is enabled
+=item county
 
-=item payby
+=item state
 
-Array reference of acceptable payment types for signup
+=item zip
 
-=over 4
+=item country
 
-=item CARD
+E911 Address (optional)
 
-credit card - automatic
+=back
 
-=item DCRD
+=item provision_pbx
 
-credit card - on-demand - version 1.5+ only
+Provisions a customer PBX (svc_pbx).
 
-=item CHEK
+Takes a hash reference as parameter with the following keys:
 
-electronic check - automatic
+=over 4
 
-=item DCHK
+=item session_id
 
-electronic check - on-demand - version 1.5+ only
+Session identifier
 
-=item LECB
+=item pkgnum
 
-Phone bill billing
+pkgnum of package into which this service is provisioned
 
-=item BILL
+=item svcpart
 
-billing, not recommended for signups
+svcpart or service definition to provision
 
-=item COMP
+=item id
 
-free, definitely not recommended for signups
+=item title
 
-=item PREPAY
+=item max_extensions
 
-special billing type: applies a credit (see FS::prepay_credit) and sets billing type to BILL
+=item max_simultaneous
+
+=item ip_addr
 
 =back
 
-=item cvv_enabled
+=item provision_external
 
-True if CVV features are available (1.5+ or 1.4.2 with CVV schema patch)
+Provisions an external service (svc_external).
 
-=item msgcat
+Takes a hash reference as parameter with the following keys:
 
-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".
+=over 4
 
-=item statedefault
+=item session_id
 
-Default state
+Session identifier
 
-=item countrydefault
+=item pkgnum
 
-Default country
+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
+
+the primary key
+
+=item _date
+
+the date it was started
+
+=item num_pkgs
+
+the number of packages
+
+=item total_setup
+
+the sum of setup fees
+
+=item total_recur
+
+the sum of recurring charges
+
+=back
+
+=item quotation_new HASHREF
+
+Creates an empty quotation and returns a hashref containing 'quotationnum',
+the primary key of the new quotation.
+
+=item quotation_delete HASHREF
+
+Disables (does not really delete) a quotation. Takes the following arguments:
+
+=over 4
+
+=item session_id
+
+=item quotationnum - the quotation to delete
+
+=back
+
+Returns 'error' => a string, which will be empty on success.
+
+=item quotation_info HASHREF
+
+Returns total and detailed pricing information on a quotation.
+
+Takes the following arguments:
+
+=over 4
+
+=item session_id
+
+=item quotationnum - the quotation to return
+
+=back
+
+Returns a hashref containing:
+
+- 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 quotation_print HASHREF
+
+Renders the quotation as HTML or PDF. Takes the following arguments:
+
+=over 4
+
+=item session_id
+
+=item quotationnum - the quotation to return
+
+=item format - 'html' or 'pdf'
+
+=back
+
+Returns a hashref containing 'document', the contents of the file.
+
+=item quotation_add_pkg HASHREF
+
+Adds a package to a quotation. Takes the following arguments:
+
+=over 4
+
+=item session_id
+
+=item pkgpart - the package to add
+
+=item quotationnum - the quotation to add it to
+
+=item quantity - the package quantity (defaults to 1)
+
+=item address1, address2, city, state, zip, country - address fields to set
+the service location
+
+=back
+
+Returns 'error' => a string, which will be empty on success.
+
+=item quotation_remove_pkg HASHREF
+
+Removes a package from a quotation. Takes the following arguments:
+
+=over 4
+
+=item session_id
+
+=item pkgnum - the primary key (quotationpkgnum) of the package to remove
+
+=item quotationnum - the quotation to remove it from
+
+=back
+
+Returns 'error' => a string, which will be empty on success.
+
+=item quotation_order HASHREF
+
+Converts the packages in a quotation into real packages. Takes the following
+arguments:
+
+Takes the following arguments:
+
+=over 4
+
+=item session_id
+
+=item quotationnum - the quotation to order
+
+=back
+
+=back
+
+=head1 SIGNUP FUNCTIONS
+
+=over 4
+
+=item signup_info HASHREF
+
+Takes a hash reference as parameter with the following keys:
+
+=over 4
+
+=item session_id - Optional agent/reseller interface session
+
+=back
+
+Returns a hash reference containing information that may be useful in
+displaying a signup page.  The hash reference contains the following keys:
+
+=over 4
+
+=item cust_main_county
+
+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.
+
+=item part_pkg
+
+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
+an agentnum specified explicitly via reseller interface session_id in the
+options.
+
+=item agent
+
+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.
+
+=item agentnum2part_pkg
+
+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.
+
+=item svc_acct_pop
+
+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.
+
+=item security_phrase
+
+True if the "security_phrase" feature is enabled
+
+=item payby
+
+Array reference of acceptable payment types for signup
+
+=over 4
+
+=item CARD
+
+credit card - automatic
+
+=item DCRD
+
+credit card - on-demand - version 1.5+ only
+
+=item CHEK
+
+electronic check - automatic
+
+=item DCHK
+
+electronic check - on-demand - version 1.5+ only
+
+=item LECB
+
+Phone bill billing
+
+=item BILL
+
+billing, not recommended for signups
+
+=item COMP
+
+free, definitely not recommended for signups
+
+=item PREPAY
+
+special billing type: applies a credit (see FS::prepay_credit) and sets billing type to BILL
+
+=back
+
+=item cvv_enabled
+
+True if CVV features are available (1.5+ or 1.4.2 with CVV schema patch)
+
+=item msgcat
+
+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".
+
+=item statedefault
+
+Default state
+
+=item countrydefault
+
+Default country
+
+=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
 
@@ -1467,7 +2335,7 @@ END
   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>';
 
@@ -1487,7 +2355,7 @@ END
       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 {
@@ -1804,8 +2672,9 @@ sub domainselector {
            '<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. '"'.