abstract out the amount + fee + input, javascript, display so it can be reused by...
[freeside.git] / FS / FS / Conf.pm
index 3987fac..e3f8a5a 100644 (file)
@@ -13,6 +13,8 @@ use FS::payby;
 use FS::conf;
 use FS::Record qw(qsearch qsearchs);
 use FS::UID qw(dbh datasrc use_confcompat);
+use FS::Misc::Invoicing qw( spool_formats );
+use FS::Misc::Geo;
 
 $base_dir = '%%%FREESIDE_CONF%%%';
 
@@ -182,7 +184,7 @@ sub exists {
   my $self = shift;
   return $self->_usecompat('exists', @_) if use_confcompat;
 
-  my($name, $agentnum)=@_;
+  #my($name, $agentnum)=@_;
 
   carp "FS::Conf->exists(". join(', ', @_). ") called"
     if $DEBUG > 1;
@@ -190,6 +192,54 @@ sub exists {
   defined($self->_config(@_));
 }
 
+#maybe this should just be the new exists instead of getting a method of its
+#own, but i wanted to avoid possible fallout
+
+sub config_bool {
+  my $self = shift;
+  return $self->_usecompat('exists', @_) if use_confcompat;
+
+  my($name,$agentnum,$agentonly) = @_;
+
+  carp "FS::Conf->config_bool(". join(', ', @_). ") called"
+    if $DEBUG > 1;
+
+  #defined($self->_config(@_));
+
+  #false laziness w/_config
+  my $hashref = { 'name' => $name };
+  local $FS::Record::conf = undef;  # XXX evil hack prevents recursion
+  my $cv;
+  my @a = (
+    ($agentnum || ()),
+    ($agentonly && $agentnum ? () : '')
+  );
+  my @l = (
+    ($self->{locale} || ()),
+    ($self->{localeonly} && $self->{locale} ? () : '')
+  );
+  # try with the agentnum first, then fall back to no agentnum if allowed
+  foreach my $a (@a) {
+    $hashref->{agentnum} = $a;
+    foreach my $l (@l) {
+      $hashref->{locale} = $l;
+      $cv = FS::Record::qsearchs('conf', $hashref);
+      if ( $cv ) {
+        if ( $cv->value eq '0'
+               && ($hashref->{agentnum} || $hashref->{locale} )
+           ) 
+        {
+          return 0; #an explicit false override, don't continue looking
+        } else {
+          return 1;
+        }
+      }
+    }
+  }
+  return 0;
+
+}
+
 =item config_orbase KEY SUFFIX
 
 Returns the configuration value or values (depending on context) for 
@@ -268,8 +318,13 @@ sub touch {
   return $self->_usecompat('touch', @_) if use_confcompat;
 
   my($name, $agentnum) = @_;
-  unless ( $self->exists($name, $agentnum) ) {
-    $self->set($name, '', $agentnum);
+  #unless ( $self->exists($name, $agentnum) ) {
+  unless ( $self->config_bool($name, $agentnum) ) {
+    if ( $agentnum && $self->exists($name) && $self->config($name,$agentnum) eq '0' ) {
+      $self->delete($name, $agentnum);
+    } else {
+      $self->set($name, '', $agentnum);
+    }
   }
 }
 
@@ -356,6 +411,31 @@ sub delete {
   }
 }
 
+#maybe this should just be the new delete instead of getting a method of its
+#own, but i wanted to avoid possible fallout
+
+sub delete_bool {
+  my $self = shift;
+  return $self->_usecompat('delete', @_) if use_confcompat;
+
+  my($name, $agentnum) = @_;
+
+  warn "[FS::Conf] DELETE $name\n" if $DEBUG;
+
+  my $cv = FS::Record::qsearchs('conf', { name     => $name,
+                                          agentnum => $agentnum,
+                                          locale   => $self->{locale},
+                                        });
+
+  if ( $cv ) {
+    my $error = $cv->delete;
+    die $error if $error;
+  } elsif ( $agentnum ) {
+    $self->set($name, '0', $agentnum);
+  }
+
+}
+
 =item import_config_item CONFITEM DIR 
 
   Imports the item specified by the CONFITEM (see L<FS::ConfItem>) into
@@ -610,12 +690,6 @@ my %msg_template_options = (
   'per_agent' => 1,
 );
 
-my $_gateway_name = sub {
-  my $g = shift;
-  return '' if !$g;
-  ($g->gateway_username . '@' . $g->gateway_module);
-};
-
 my %payment_gateway_options = (
   'type'        => 'select-sub',
   'options_sub' => sub {
@@ -623,14 +697,42 @@ my %payment_gateway_options = (
         'table' => 'payment_gateway',
         'hashref' => { 'disabled' => '' },
       });
-    map { $_->gatewaynum, $_gateway_name->($_) } @gateways;
+    map { $_->gatewaynum, $_->label } @gateways;
   },
   'option_sub'  => sub {
     my $gateway = FS::payment_gateway->by_key(shift);
-    $_gateway_name->($gateway);
+    $gateway ? $gateway->label : ''
   },
 );
 
+my %batch_gateway_options = (
+  %payment_gateway_options,
+  'options_sub' => sub {
+    my @gateways = qsearch('payment_gateway',
+      {
+        'disabled'          => '',
+        'gateway_namespace' => 'Business::BatchPayment',
+      }
+    );
+    map { $_->gatewaynum, $_->label } @gateways;
+  },
+);
+
+# takes the reason class (C, R, S) as an argument
+sub reason_type_options {
+  my $reason_class = shift;
+
+  'type'        => 'select-sub',
+  'options_sub' => sub {
+    map { $_->typenum => $_->type } 
+      qsearch('reason_type', { class => $reason_class });
+  },
+  'option_sub'  => sub {
+    my $type = FS::reason_type->by_key(shift);
+    $type ? $type->type : '';
+  }
+}
+
 #Billing (81 items)
 #Invoicing (50 items)
 #UI (69 items)
@@ -678,6 +780,13 @@ my %payment_gateway_options = (
   },
 
   {
+    'key'         => 'part_pkg-lineage',
+    'section'     => '',
+    'description' => 'When editing a package definition, if setup or recur fees are changed, create a new package rather than changing the existing package.',
+    'type'        => 'checkbox',
+  },
+
+  {
     'key'         => 'apacheip',
     #not actually deprecated yet
     #'section'     => 'deprecated',
@@ -861,6 +970,13 @@ my %payment_gateway_options = (
   },
 
   {
+    'key'         => 'business-batchpayment-test_transaction',
+    'section'     => 'billing',
+    'description' => 'Turns on the Business::BatchPayment test_mode flag.  Note that not all gateway modules support this flag; if yours does not, using the batch gateway will fail.',
+    'type'        => 'checkbox',
+  },
+
+  {
     'key'         => 'countrydefault',
     'section'     => 'UI',
     'description' => 'Default two-letter country code (if not supplied, the default is `US\')',
@@ -1081,7 +1197,15 @@ my %payment_gateway_options = (
   {
     'key'         => 'invoice_html',
     'section'     => 'invoicing',
-    'description' => 'Optional HTML template for invoices.  See the <a href="http://www.freeside.biz/mediawiki/index.php/Freeside:2.1:Documentation:Administration#HTML_invoice_templates">billing documentation</a> for details.',
+    'description' => 'HTML template for invoices.  See the <a href="http://www.freeside.biz/mediawiki/index.php/Freeside:2.1:Documentation:Administration#HTML_invoice_templates">billing documentation</a> for details.',
+
+    'type'        => 'textarea',
+  },
+
+  {
+    'key'         => 'quotation_html',
+    'section'     => '',
+    'description' => 'HTML template for quotations.',
 
     'type'        => 'textarea',
   },
@@ -1129,6 +1253,13 @@ my %payment_gateway_options = (
   },
 
   {
+    'key'         => 'quotation_latex',
+    'section'     => '',
+    'description' => 'LaTeX template for typeset PostScript quotations.',
+    'type'        => 'textarea',
+  },
+
+  {
     'key'         => 'invoice_latextopmargin',
     'section'     => 'invoicing',
     'description' => 'Optional LaTeX invoice topmargin setting. Include units.',
@@ -1187,6 +1318,15 @@ and customer address. Include units.',
   },
 
   {
+    'key'         => 'quotation_latexnotes',
+    'section'     => '',
+    'description' => 'Notes section for LaTeX typeset PostScript quotations.',
+    'type'        => 'textarea',
+    'per_agent'   => 1,
+    'per_locale'  => 1,
+  },
+
+  {
     'key'         => 'invoice_latexfooter',
     'section'     => 'invoicing',
     'description' => 'Footer for LaTeX typeset PostScript invoices.',
@@ -1397,6 +1537,7 @@ and customer address. Include units.',
     'description' => 'Send payment receipts.',
     'type'        => 'checkbox',
     'per_agent'   => 1,
+    'agent_bool'  => 1,
   },
 
   {
@@ -1571,6 +1712,13 @@ and customer address. Include units.',
   },
 
   {
+    'key'         => 'disable_maxselect',
+    'section'     => 'UI',
+    'description' => 'Prevent changing the number of records per page.',
+    'type'        => 'checkbox',
+  },
+
+  {
     'key'         => 'session-start',
     'section'     => 'session',
     'description' => 'If defined, the command which is executed on the Freeside machine when a session begins.  The contents of the file are treated as a double-quoted perl string, with the following variables available: <code>$ip</code>, <code>$nasip</code> and <code>$nasfqdn</code>, which are the IP address of the starting session, and the IP address and fully-qualified domain name of the NAS this session is on.',
@@ -1770,6 +1918,7 @@ and customer address. Include units.',
     'section'     => 'username',
     'description' => 'Allow uppercase characters in usernames.  Not recommended for use with FreeRADIUS with MySQL backend, which is case-insensitive by default.',
     'type'        => 'checkbox',
+    'per_agent'   => 1,
   },
 
   { 
@@ -1815,6 +1964,13 @@ and customer address. Include units.',
   },
 
   {
+    'key'         => 'unmask_ss',
+    'section'     => 'UI',
+    'description' => "Don't mask social security numbers in the web interface.",
+    'type'        => 'checkbox',
+  },
+
+  {
     'key'         => 'show_stateid',
     'section'     => 'UI',
     'description' => "Turns on display/collection of driver's license/state issued id numbers in the web interface.  Sometimes required by electronic check (ACH) processors.",
@@ -1981,6 +2137,14 @@ and customer address. Include units.',
   },
 
   {
+    'key'         => 'signup_server-terms_of_service',
+    'section'     => 'self-service',
+    'description' => 'Terms of Service for the signup server.  May contain HTML.',
+    'type'        => 'textarea',
+    'per_agent'   => 1,
+  },
+
+  {
     'key'         => 'selfservice_server-base_url',
     'section'     => 'self-service',
     'description' => 'Base URL for the self-service web interface - necessary for some widgets to find their way, including retrieval of non-US state information and phone number provisioning.',
@@ -2259,6 +2423,13 @@ and customer address. Include units.',
   },
 
   {
+    'key'         => 'require_cash_deposit_info',
+    'section'     => 'billing',
+    'description' => 'When recording cash payments, display bank deposit information fields.',
+    'type'        => 'checkbox',
+  },
+
+  {
     'key'         => 'paymentforcedtobatch',
     'section'     => 'deprecated',
     'description' => 'See batch-enable_payby and realtime-disable_payby.  Used to (for CHEK): Cause per customer payment entry to be forced to a batch processor rather than performed realtime.',
@@ -2367,7 +2538,7 @@ and customer address. Include units.',
   {
     'key'         => 'manual_process-pkgpart',
     'section'     => 'billing',
-    'description' => 'Package to add to each manual credit card and ACH payments entered from the backend.  Enabling this option may be in violation of your merchant agreement(s), so please check them carefully before enabling this option.',
+    'description' => 'Package to add to each manual credit card and ACH payment entered by employees from the backend.  Enabling this option may be in violation of your merchant agreement(s), so please check it(/them) carefully before enabling this option.',
     'type'        => 'select-part_pkg',
   },
 
@@ -2390,6 +2561,56 @@ and customer address. Include units.',
   },
 
   {
+    'key'         => 'selfservice_process-pkgpart',
+    'section'     => 'billing',
+    'description' => 'Package to add to each manual credit card and ACH payment entered by the customer themselves in the self-service interface.  Enabling this option may be in violation of your merchant agreement(s), so please check it(/them) carefully before enabling this option.',
+    'type'        => 'select-part_pkg',
+  },
+
+  {
+    'key'         => 'selfservice_process-display',
+    'section'     => 'billing',
+    'description' => 'When using selfservice_process-pkgpart, add the fee to the amount entered (default), or subtract the fee from the amount entered.',
+    'type'        => 'select',
+    'select_hash' => [
+                       'add'      => 'Add fee to amount entered',
+                       'subtract' => 'Subtract fee from amount entered',
+                     ],
+  },
+
+  {
+    'key'         => 'selfservice_process-skip_first',
+    'section'     => 'billing',
+    'description' => "When using selfservice_process-pkgpart, omit the fee if it is the customer's first payment.",
+    'type'        => 'checkbox',
+  },
+
+  {
+    'key'         => 'suto_process-pkgpart',
+    'section'     => 'billing',
+    'description' => 'Package to add to each automatic credit card and ACH payment processed by billing events.  Enabling this option may be in violation of your merchant agreement(s), so please check them carefully before enabling this option.',
+    'type'        => 'select-part_pkg',
+  },
+
+#  {
+#    'key'         => 'auto_process-display',
+#    'section'     => 'billing',
+#    'description' => 'When using auto_process-pkgpart, add the fee to the amount entered (default), or subtract the fee from the amount entered.',
+#    'type'        => 'select',
+#    'select_hash' => [
+#                       'add'      => 'Add fee to amount entered',
+#                       'subtract' => 'Subtract fee from amount entered',
+#                     ],
+#  },
+
+  {
+    'key'         => 'auto_process-skip_first',
+    'section'     => 'billing',
+    'description' => "When using auto_process-pkgpart, omit the fee if it is the customer's first payment.",
+    'type'        => 'checkbox',
+  },
+
+  {
     'key'         => 'allow_negative_charges',
     'section'     => 'billing',
     'description' => 'Allow negative charges.  Normally not used unless importing data from a legacy system that requires this.',
@@ -2650,14 +2871,28 @@ and customer address. Include units.',
 
   {
     'key'         => 'network_monitoring_system',
-    'section'     => '',
+    'section'     => 'network_monitoring',
     'description' => 'Networking monitoring system (NMS) integration.  <b>Torrus_Internal</b> uses the built-in Torrus ticketing system (see the <a href="http://www.freeside.biz/mediawiki/index.php/Freeside:2.1:Documentation:Torrus_Installation">integrated networking monitoring system installation instructions</a>).',
     'type'        => 'select',
-    #'select_enum' => [ '', qw(RT_Internal RT_Libs RT_External) ],
     'select_enum' => [ '', qw(Torrus_Internal) ],
   },
 
   {
+    'key'         => 'nms-auto_add-svc_ips',
+    'section'     => 'network_monitoring',
+    'description' => 'Automatically add (and remove) IP addresses from these service tables to the network monitoring system.',
+    'type'        => 'selectmultiple',
+    'select_enum' => [ 'svc_acct', 'svc_broadband', 'svc_dsl' ],
+  },
+
+  {
+    'key'         => 'nms-auto_add-community',
+    'section'     => 'network_monitoring',
+    'description' => 'SNMP community string to use when automatically adding IP addresses from these services to the network monitoring system.',
+    'type'        => 'text',
+  },
+
+  {
     'key'         => 'ticket_system-default_queueid',
     'section'     => 'ticketing',
     'description' => 'Default queue used when creating new customer tickets.',
@@ -2797,6 +3032,14 @@ and customer address. Include units.',
   },
 
   {
+    'key'         => 'company_url',
+    'section'     => 'UI',
+    'description' => 'Your company URL',
+    'type'        => 'text',
+    'per_agent'   => 1,
+  },
+
+  {
     'key'         => 'company_address',
     'section'     => 'required',
     'description' => 'Your company address',
@@ -2859,6 +3102,7 @@ and customer address. Include units.',
     'section'     => '',
     'description' => "Use the agent's master service address as the service address (only ship_address2 can be entered, if blank on the master address).  Useful for multi-tenant applications.",
     'type'        => 'checkbox',
+    'per_agent'   => 1,
   },
 
   { 'key'         => 'referral_credit',
@@ -2886,7 +3130,7 @@ and customer address. Include units.',
     'section'     => 'invoicing',
     'description' => 'Enable FTP of raw invoice data - format.',
     'type'        => 'select',
-    'select_enum' => [ '', 'default', 'billco', ],
+    'options'     => [ spool_formats() ],
   },
 
   {
@@ -2922,7 +3166,7 @@ and customer address. Include units.',
     'section'     => 'invoicing',
     'description' => 'Enable spooling of raw invoice data - format.',
     'type'        => 'select',
-    'select_enum' => [ '', 'default', 'billco', ],
+    'options'     => [ spool_formats() ],
   },
 
   {
@@ -2933,6 +3177,32 @@ and customer address. Include units.',
   },
 
   {
+    'key'         => 'bridgestone-batch_counter',
+    'section'     => '',
+    'description' => 'Batch counter for spool files.  Increments every time a spool file is uploaded.',
+    'type'        => 'text',
+    'per_agent'   => 1,
+  },
+
+  {
+    'key'         => 'bridgestone-prefix',
+    'section'     => '',
+    'description' => 'Agent identifier for uploading to BABT printing service.',
+    'type'        => 'text',
+    'per_agent'   => 1,
+  },
+
+  {
+    'key'         => 'bridgestone-confirm_template',
+    'section'     => '',
+    'description' => 'Confirmation email template for uploading to BABT service.  Text::Template format, with variables "$zipfile" (name of the zipped file), "$seq" (sequence number), "$prefix" (user ID string), and "$rows" (number of records in the file).  Should include Subject: and To: headers, separated from the rest of the message by a blank line.',
+    # this could use a true message template, but it's hard to see how that
+    # would make the world a better place
+    'type'        => 'textarea',
+    'per_agent'   => 1,
+  },
+
+  {
     'key'         => 'svc_acct-usage_suspend',
     'section'     => 'billing',
     'description' => 'Suspends the package an account belongs to when svc_acct.seconds or a bytecount is decremented to 0 or below (accounts with an empty seconds and up|down|totalbytes value are ignored).  Typically used in conjunction with prepaid packages and freeside-sqlradius-radacctd.',
@@ -2983,6 +3253,16 @@ and customer address. Include units.',
   },
 
   {
+    'key'         => 'cust_location-label_prefix',
+    'section'     => 'UI',
+    'description' => 'Optional "site ID" to show in the location label',
+    'type'        => 'select',
+    'select_hash' => [ '' => '',
+                       'CoStAg' => 'CoStAgXXXXX (country, state, agent name, locationnum)',
+                      ],
+  },
+
+  {
     'key'         => 'cust_pkg-display_times',
     'section'     => 'UI',
     'description' => 'Display full timestamps (not just dates) for customer packages.  Useful if you are doing real-time things like hourly prepaid.',
@@ -3053,6 +3333,17 @@ and customer address. Include units.',
   },
 
   {
+    'key'         => 'echeck-country',
+    'section'     => 'billing',
+    'description' => 'Format electronic check information for the specified country.',
+    'type'        => 'select',
+    'select_hash' => [ 'US' => 'United States',
+                       'CA' => 'Canada (enables branch)',
+                       'XX' => 'Other',
+                     ],
+  },
+
+  {
     'key'         => 'voip-cust_accountcode_cdr',
     'section'     => 'telephony',
     'description' => 'Enable the per-customer option for CDR breakdown by accountcode.',
@@ -3200,6 +3491,40 @@ and customer address. Include units.',
                     ]
   },
 
+  { 'key'         => 'batch-gateway-CARD',
+    'section'     => 'billing',
+    'description' => 'Business::BatchPayment gateway for credit card batches.',
+    %batch_gateway_options,
+  },
+
+  { 'key'         => 'batch-gateway-CHEK',
+    'section'     => 'billing', 
+    'description' => 'Business::BatchPayment gateway for check batches.',
+    %batch_gateway_options,
+  },
+
+  {
+    'key'         => 'batch-reconsider',
+    'section'     => 'billing',
+    'description' => 'Allow imported batch results to change the status of payments from previous imports.  Enable this only if your gateway is known to send both positive and negative results for the same batch.',
+    'type'        => 'checkbox',
+  },
+
+  {
+    'key'         => 'batch-auto_resolve_days',
+    'section'     => 'billing',
+    'description' => 'Automatically resolve payment batches this many days after they were first downloaded.',
+    'type'        => 'text',
+  },
+
+  {
+    'key'         => 'batch-auto_resolve_status',
+    'section'     => 'billing',
+    'description' => 'When automatically resolving payment batches, take this action for payments of unknown status.',
+    'type'        => 'select',
+    'select_enum' => [ 'approve', 'decline' ],
+  },
+
   #lists could be auto-generated from pay_batch info
   {
     'key'         => 'batch-fixed_format-CARD',
@@ -3366,7 +3691,14 @@ and customer address. Include units.',
   {
     'key'         => 'cust_main-enable_birthdate',
     'section'     => 'UI',
-    'descritpion' => 'Enable tracking of a birth date with each customer record',
+    'description' => 'Enable tracking of a birth date with each customer record',
+    'type'        => 'checkbox',
+  },
+
+  {
+    'key'         => 'cust_main-enable_spouse_birthdate',
+    'section'     => 'UI',
+    'description' => 'Enable tracking of a spouse birth date with each customer record',
     'type'        => 'checkbox',
   },
 
@@ -3518,6 +3850,26 @@ and customer address. Include units.',
   },
 
   {
+    'key'         => 'cust_bill-line_item-date_style',
+    'section'     => 'billing',
+    'description' => 'Display format for line item date ranges on invoice line items.',
+    'type'        => 'select',
+    'select_hash' => [ ''           => 'STARTDATE-ENDDATE',
+                       'month_of'   => 'Month of MONTHNAME',
+                       'X_month'    => 'DATE_DESC MONTHNAME',
+                     ],
+    'per_agent'   => 1,
+  },
+
+  {
+    'key'         => 'cust_bill-line_item-date_description',
+    'section'     => 'billing',
+    'description' => 'Text to display for "DATE_DESC" when using cust_bill-line_item-date_style DATE_DESC MONTHNAME.',
+    'type'        => 'text',
+    'per_agent'   => 1,
+  },
+
+  {
     'key'         => 'support_packages',
     'section'     => '',
     'description' => 'A list of packages eligible for RT ticket time transfer, one pkgpart per line.', #this should really be a select multiple, or specified in the packages themselves...
@@ -3530,6 +3882,7 @@ and customer address. Include units.',
     'section'     => '',
     'description' => 'Require daytime or night phone for all customer records.',
     'type'        => 'checkbox',
+    'per_agent'   => 1,
   },
 
   {
@@ -3537,6 +3890,18 @@ and customer address. Include units.',
     'section'     => '',
     'description' => 'Email address field is required: require at least one invoicing email address for all customer records.',
     'type'        => 'checkbox',
+    'per_agent'   => 1,
+  },
+
+  {
+    'key'         => 'cust_main-check_unique',
+    'section'     => '',
+    'description' => 'Warn before creating a customer record where these fields duplicate another customer.',
+    'type'        => 'select',
+    'multiple'    => 1,
+    'select_hash' => [ 
+      'address1' => 'Billing address',
+    ],
   },
 
   {
@@ -3550,77 +3915,35 @@ and customer address. Include units.',
     'key'         => 'cancel_credit_type',
     'section'     => 'billing',
     'description' => 'The group to use for new, automatically generated credit reasons resulting from cancellation.',
-    'type'        => 'select-sub',
-    'options_sub' => sub { require FS::Record;
-                           require FS::reason_type;
-                          map { $_->typenum => $_->type }
-                               FS::Record::qsearch('reason_type', { class=>'R' } );
-                        },
-    'option_sub'  => sub { require FS::Record;
-                           require FS::reason_type;
-                          my $reason_type = FS::Record::qsearchs(
-                            'reason_type', { 'typenum' => shift }
-                          );
-                           $reason_type ? $reason_type->type : '';
-                        },
+    reason_type_options('R'),
+  },
+
+  {
+    'key'         => 'suspend_credit_type',
+    'section'     => 'billing',
+    'description' => 'The group to use for new, automatically generated credit reasons resulting from package suspension.',
+    reason_type_options('R'),
   },
 
   {
     'key'         => 'referral_credit_type',
     'section'     => 'deprecated',
     'description' => 'Used to be the group to use for new, automatically generated credit reasons resulting from referrals.  Now set in a package billing event for the referral.',
-    'type'        => 'select-sub',
-    'options_sub' => sub { require FS::Record;
-                           require FS::reason_type;
-                          map { $_->typenum => $_->type }
-                               FS::Record::qsearch('reason_type', { class=>'R' } );
-                        },
-    'option_sub'  => sub { require FS::Record;
-                           require FS::reason_type;
-                          my $reason_type = FS::Record::qsearchs(
-                            'reason_type', { 'typenum' => shift }
-                          );
-                           $reason_type ? $reason_type->type : '';
-                        },
+    reason_type_options('R'),
   },
 
   {
     'key'         => 'signup_credit_type',
     'section'     => 'billing', #self-service?
     'description' => 'The group to use for new, automatically generated credit reasons resulting from signup and self-service declines.',
-    'type'        => 'select-sub',
-    'options_sub' => sub { require FS::Record;
-                           require FS::reason_type;
-                          map { $_->typenum => $_->type }
-                               FS::Record::qsearch('reason_type', { class=>'R' } );
-                        },
-    'option_sub'  => sub { require FS::Record;
-                           require FS::reason_type;
-                          my $reason_type = FS::Record::qsearchs(
-                            'reason_type', { 'typenum' => shift }
-                          );
-                           $reason_type ? $reason_type->type : '';
-                        },
+    reason_type_options('R'),
   },
 
   {
     'key'         => 'prepayment_discounts-credit_type',
     'section'     => 'billing',
     'description' => 'Enables the offering of prepayment discounts and establishes the credit reason type.',
-    'type'        => 'select-sub',
-    'options_sub' => sub { require FS::Record;
-                           require FS::reason_type;
-                           map { $_->typenum => $_->type }
-                               FS::Record::qsearch('reason_type', { class=>'R' } );
-                         },
-    'option_sub'  => sub { require FS::Record;
-                           require FS::reason_type;
-                           my $reason_type = FS::Record::qsearchs(
-                             'reason_type', { 'typenum' => shift }
-                           );
-                           $reason_type ? $reason_type->type : '';
-                         },
-
+    reason_type_options('R'),
   },
 
   {
@@ -3657,6 +3980,7 @@ and customer address. Include units.',
     'section'     => 'invoicing',
     'description' => 'Disable inclusion of previous balance, payment, and credit lines on invoices',
     'type'        => 'checkbox',
+    'per_agent'   => 1,
   },
 
   {
@@ -3720,7 +4044,15 @@ and customer address. Include units.',
     'section'     => 'UI',
     'description' => 'The year to use in census tract lookups',
     'type'        => 'select',
-    'select_enum' => [ qw( 2010 2009 2008 ) ],
+    'select_enum' => [ qw( 2012 2011 2010 ) ],
+  },
+
+  {
+    'key'         => 'tax_district_method',
+    'section'     => 'UI',
+    'description' => 'The method to use to look up tax district codes.',
+    'type'        => 'select',
+    'select_hash' => [ FS::Misc::Geo::get_district_methods() ],
   },
 
   {
@@ -3738,6 +4070,21 @@ and customer address. Include units.',
   },
 
   {
+    'key'         => 'geocode_module',
+    'section'     => '',
+    'description' => 'Module to geocode (retrieve a latitude and longitude for) addresses',
+    'type'        => 'select',
+    'select_enum' => [ 'Geo::Coder::Googlev3' ],
+  },
+
+  {
+    'key'         => 'geocode-require_nw_coordinates',
+    'section'     => 'UI',
+    'description' => 'Require latitude and longitude in the North Western quadrant, e.g. for North American co-ordinates, etc.',
+    'type'        => 'checkbox',
+  },
+
+  {
     'key'         => 'disable_acl_changes',
     'section'     => '',
     'description' => 'Disable all ACL changes, for demos.',
@@ -3792,9 +4139,26 @@ and customer address. Include units.',
   {
     'key'         => 'cust_main-custnum-display_prefix',
     'section'     => 'UI',
-    'description' => 'Prefix the customer number with this number for display purposes (and zero fill to 8 digits).',
+    'description' => 'Prefix the customer number with this string for display purposes.',
+    'type'        => 'text',
+    'per_agent'   => 1,
+  },
+
+  {
+    'key'         => 'cust_main-custnum-display_special',
+    'section'     => 'UI',
+    'description' => 'Use this customer number prefix format',
+    'type'        => 'select',
+    'select_hash' => [ '' => '',
+                       'CoStAg' => 'CoStAg (country, state, agent name or display_prefix)',
+                       'CoStCl' => 'CoStCl (country, state, class name)' ],
+  },
+
+  {
+    'key'         => 'cust_main-custnum-display_length',
+    'section'     => 'UI',
+    'description' => 'Zero fill the customer number to this many digits for display purposes.',
     'type'        => 'text',
-    #and then probably agent-virt this to merge these instances
   },
 
   {
@@ -3840,6 +4204,13 @@ and customer address. Include units.',
   },
 
   {
+    'key'         => 'unsuspend_email_admin',
+    'section'     => '',
+    'description' => 'Destination admin email address to enable unsuspension notices',
+    'type'        => 'text',
+  },
+  
+  {
     'key'         => 'email_report-subject',
     'section'     => '',
     'description' => 'Subject for reports emailed by freeside-fetch.  Defaults to "Freeside report".',
@@ -3889,6 +4260,22 @@ and customer address. Include units.',
   },
 
   {
+    'key'         => 'selfservice-stripe1_bgcolor',
+    'section'     => 'self-service',
+    'description' => 'HTML color for self-service interface lists (primary stripe), for example, #FFFFFF',
+    'type'        => 'text',
+    'per_agent'   => 1,
+  },
+
+  {
+    'key'         => 'selfservice-stripe2_bgcolor',
+    'section'     => 'self-service',
+    'description' => 'HTML color for self-service interface lists (alternate stripe), for example, #DDDDDD',
+    'type'        => 'text',
+    'per_agent'   => 1,
+  },
+
+  {
     'key'         => 'selfservice-text_color',
     'section'     => 'self-service',
     'description' => 'HTML text color for the self-service interface, for example, #000000',
@@ -3937,6 +4324,14 @@ and customer address. Include units.',
   },
 
   {
+    'key'         => 'selfservice-no_logo',
+    'section'     => 'self-service',
+    'description' => 'Disable the logo in self-service',
+    'type'        => 'checkbox',
+    'per_agent'   => 1,
+  },
+
+  {
     'key'         => 'selfservice-title_color',
     'section'     => 'self-service',
     'description' => 'HTML color for the self-service title, for example, #000000',
@@ -4047,6 +4442,20 @@ and customer address. Include units.',
   },
 
   {
+    'key'         => 'selfservice-login_banner_image',
+    'section'     => 'self-service',
+    'description' => 'Banner image shown on the login page, in PNG format.',
+    'type'        => 'image',
+  },
+
+  {
+    'key'         => 'selfservice-login_banner_url',
+    'section'     => 'self-service',
+    'description' => 'Link for the login banner.',
+    'type'        => 'text',
+  },
+
+  {
     'key'         => 'selfservice-bulk_format',
     'section'     => 'deprecated',
     'description' => 'Parameter arrangement for selfservice bulk features',
@@ -4118,7 +4527,14 @@ and customer address. Include units.',
     'description' => 'Maximum length of the phone service "Name" field (svc_phone.phone_name).  Sometimes useful to limit this (to 15?) when exporting as Caller ID data.',
     'type'        => 'text',
   },
-  
+
+  {
+    'key'         => 'svc_phone-random_pin',
+    'section'     => 'telephony',
+    'description' => 'Number of random digits to generate in the "PIN" field, if empty.',
+    'type'        => 'text',
+  },
+
   {
     'key'         => 'svc_phone-lnp',
     'section'     => 'telephony',
@@ -4199,6 +4615,13 @@ and customer address. Include units.',
   },
 
   {
+    'key'         => 'cdr-asterisk_australia_rewrite',
+    'section'     => 'telephony',
+    'description' => 'For Asterisk CDRs, assign CDR type numbers based on Australian conventions.',
+    'type'        => 'checkbox',
+  },
+
+  {
     'key'         => 'cust_pkg-show_autosuspend',
     'section'     => 'UI',
     'description' => 'Show package auto-suspend dates.  Use with caution for now; can slow down customer view for large insallations.',
@@ -4213,34 +4636,6 @@ and customer address. Include units.',
   },
 
   {
-    'key'         => 'sg-multicustomer_hack',
-    'section'     => '',
-    'description' => "Don't use this.",
-    'type'        => 'checkbox',
-  },
-
-  {
-    'key'         => 'sg-ping_username',
-    'section'     => '',
-    'description' => "Don't use this.",
-    'type'        => 'text',
-  },
-
-  {
-    'key'         => 'sg-ping_password',
-    'section'     => '',
-    'description' => "Don't use this.",
-    'type'        => 'text',
-  },
-
-  {
-    'key'         => 'sg-login_username',
-    'section'     => '',
-    'description' => "Don't use this.",
-    'type'        => 'text',
-  },
-
-  {
     'key'         => 'mc-outbound_packages',
     'section'     => '',
     'description' => "Don't use this.",
@@ -4324,6 +4719,13 @@ and customer address. Include units.',
   },
 
   {
+    'key'         => 'svc_hardware-check_mac_addr',
+    'section'     => '', #?
+    'description' => 'Require the "hardware address" field in hardware services to be a valid MAC address.',
+    'type'        => 'checkbox',
+  },
+
+  {
     'key'         => 'tax-report_groups',
     'section'     => '',
     'description' => 'List of grouping possibilities for tax names on reports, one per line, "label op value" (op can be = or !=).',
@@ -4338,6 +4740,13 @@ and customer address. Include units.',
   },
 
   {
+    'key'         => 'tax-cust_exempt-groups-require_individual_nums',
+    'section'     => '',
+    'description' => 'When using tax-cust_exempt-groups, require an individual tax exemption number for each exemption from different taxes.',
+    'type'        => 'checkbox',
+  },
+
+  {
     'key'         => 'cust_main-default_view',
     'section'     => 'UI',
     'description' => 'Default customer view, for users who have not selected a default view in their preferences.',
@@ -4385,14 +4794,14 @@ and customer address. Include units.',
   {
     'key'         => 'cust_main-edit_signupdate',
     'section'     => 'UI',
-    'descritpion' => 'Enable manual editing of the signup date.',
+    'description' => 'Enable manual editing of the signup date.',
     'type'        => 'checkbox',
   },
 
   {
     'key'         => 'svc_acct-disable_access_number',
     'section'     => 'UI',
-    'descritpion' => 'Disable access number selection.',
+    'description' => 'Disable access number selection.',
     'type'        => 'checkbox',
   },
 
@@ -4495,6 +4904,13 @@ and customer address. Include units.',
   },
 
   {
+    'key'         => 'cust_main-custom_content',
+    'section'     => 'UI',
+    'description' => 'As an alternative to cust_main-custom_link (leave it blank), the contant to display on this customer page, one item per line.  Available iems are: small_custview, birthdate, spouse_birthdate, svc_acct, svc_phone and svc_external.',
+    'type'        => 'textarea',
+  },
+
+  {
     'key'         => 'cust_main-custom_title',
     'section'     => 'UI',
     'description' => 'Title for the "Custom" tab in the View Customer page.',
@@ -4625,6 +5041,13 @@ and customer address. Include units.',
     'select_enum' => [ 'Classic', 'Recurring' ],
   },
 
+  {
+    'key'         => 'cust_main-print_statement_link',
+    'section'     => 'UI',
+    'description' => 'Show a link to download a current statement for the customer.',
+    'type'        => 'checkbox',
+  },
+
   { 
     'key'         => 'username-pound',
     'section'     => 'username',
@@ -4656,8 +5079,8 @@ and customer address. Include units.',
   
   {
     'key'         => 'svc_broadband-require-nw-coordinates',
-    'section'     => 'UI',
-    'description' => 'On svc_broadband add/edit, require latitude and longitude in the North Western quadrant, e.g. for North American co-ordinates, etc.',
+    'section'     => 'deprecated',
+    'description' => 'Deprecated; see geocode-require_nw_coordinates instead',
     'type'        => 'checkbox',
   },
   
@@ -4669,16 +5092,16 @@ and customer address. Include units.',
   },
   
   {
-    'key'         => 'cust_main-require-bank-branch',
+    'key'         => 'cust-edit-alt-field-order',
     'section'     => 'UI',
-    'description' => 'An alternate DCHK/CHEK format; require entry of bank branch number.',
+    'description' => 'An alternate ordering of fields for the New Customer and Edit Customer screens.',
     'type'        => 'checkbox',
   },
-  
+
   {
-    'key'         => 'cust-edit-alt-field-order',
+    'key'         => 'cust_bill-enable_promised_date',
     'section'     => 'UI',
-    'description' => 'An alternate ordering of fields for the New Customer and Edit Customer screens.',
+    'description' => 'Enable display/editing of the "promised payment date" field on invoices.',
     'type'        => 'checkbox',
   },
   
@@ -4695,17 +5118,84 @@ and customer address. Include units.',
     },
     'option_sub'  => sub { FS::Locales->description(shift) },
   },
+
+  {
+    'key'         => 'cust_main-require_locale',
+    'section'     => 'UI',
+    'description' => 'Require an explicit locale to be chosen for new customers.',
+    'type'        => 'checkbox',
+  },
   
   {
     'key'         => 'translate-auto-insert',
     'section'     => '',
-    'description' => 'Auto-insert untranslated strings for selected non-en_US locales with their default/en_US values. DO NOT TURN THIS ON.',
+    'description' => 'Auto-insert untranslated strings for selected non-en_US locales with their default/en_US values.  Do not turn this on unless translating the interface into a new language.',
+    'type'        => 'select',
+    'multiple'    => 1,
+    'select_enum' => [ grep { $_ ne 'en_US' } FS::Locales::locales ],
+  },
+
+  {
+    'key'         => 'svc_acct-tower_sector',
+    'section'     => '',
+    'description' => 'Track tower and sector for svc_acct (account) services.',
+    'type'        => 'checkbox',
+  },
+
+  {
+    'key'         => 'cdr-prerate',
+    'section'     => 'telephony',
+    'description' => 'Experimental feature to rate CDRs immediately, rather than waiting until invoice generation time.  Can reduce invoice generation time when processing lots of CDRs.  Currently works with "VoIP/telco CDR rating (standard)" price plans using "Phone numbers (svc_phone.phonenum)" CDR service matching, without any included minutes.',
+    'type'        => 'checkbox',
+  },
+
+  {
+    'key'         => 'cdr-prerate-cdrtypenums',
+    'section'     => 'telephony',
+    'description' => 'When using cdr-prerate to rate CDRs immediately, limit processing to these CDR types.',
     'type'        => 'select-sub',
     'multiple'    => 1,
-    'options_sub' => sub { map { $_ => '' } 
-                            grep { $_ ne 'en_US' } FS::Locales::locales;
-                                    },
-    'option_sub'  => sub { ''; },
+    'options_sub' => sub { require FS::Record;
+                           require FS::cdr_type;
+                           map { $_->cdrtypenum => $_->cdrtypename }
+                               FS::Record::qsearch( 'cdr_type', 
+                                                   {} #{ 'disabled' => '' }
+                                                 );
+                        },
+    'option_sub'  => sub { require FS::Record;
+                           require FS::cdr_type;
+                           my $cdr_type = FS::Record::qsearchs(
+                            'cdr_type', { 'cdrtypenum'=>shift } );
+                           $cdr_type ? $cdr_type->cdrtypename : '';
+                        },
+  },
+  
+  {
+    'key'         => 'brand-agent',
+    'section'     => 'UI',
+    'description' => 'Brand the backoffice interface (currently Help->About) using the company_name, company_url and logo.png configuration settings of the selected agent.  Typically used when selling or bundling hosted access to the backoffice interface.  NOTE: The AGPL software license has specific requirements for source code availability in this situation.',
+    'type'        => 'select-agent',
+  },
+
+  {
+    'key'         => 'cust_class-tax_exempt',
+    'section'     => 'billing',
+    'description' => 'Control the tax exemption flag per customer class rather than per indivual customer.',
+    'type'        => 'checkbox',
+  },
+
+  {
+    'key'         => 'selfservice-billing_history-line_items',
+    'section'     => 'self-service',
+    'description' => 'Return line item billing detail for the self-service billing_history API call.',
+    'type'        => 'checkbox',
+  },
+
+  {
+    'key'         => 'logout-timeout',
+    'section'     => 'UI',
+    'description' => 'If set, automatically log users out of the backoffice after this many minutes.',
+    'type'       => 'text',
   },
 
   { key => "apacheroot", section => "deprecated", description => "<b>DEPRECATED</b>", type => "text" },