add billing_history selfservice API call, RT#17617
[freeside.git] / FS / FS / Conf.pm
index 331645e..bf2f189 100644 (file)
@@ -8,10 +8,12 @@ use MIME::Base64;
 use FS::ConfItem;
 use FS::ConfDefaults;
 use FS::Conf_compat17;
+use FS::Locales;
 use FS::payby;
 use FS::conf;
 use FS::Record qw(qsearch qsearchs);
 use FS::UID qw(dbh datasrc use_confcompat);
+use FS::Misc::Geo;
 
 $base_dir = '%%%FREESIDE_CONF%%%';
 
@@ -46,16 +48,25 @@ but this may change in the future.
 
 =over 4
 
-=item new
+=item new [ HASHREF ]
 
 Create a new configuration object.
 
+HASHREF may contain options to set the configuration context.  Currently 
+accepts C<locale>, and C<localeonly> to disable fallback to the null locale.
+
 =cut
 
 sub new {
-  my($proto) = @_;
+  my($proto) = shift;
+  my $opts = shift || {};
   my($class) = ref($proto) || $proto;
-  my($self) = { 'base_dir' => $base_dir };
+  my $self = {
+    'base_dir'    => $base_dir,
+    'locale'      => $opts->{locale},
+    'localeonly'  => $opts->{localeonly}, # for config-view.cgi ONLY
+  };
+  warn "FS::Conf created with no locale fallback.\n" if $self->{localeonly};
   bless ($self, $class);
 }
 
@@ -107,14 +118,26 @@ sub _usecompat {
 sub _config {
   my($self,$name,$agentnum,$agentonly)=@_;
   my $hashref = { 'name' => $name };
-  $hashref->{agentnum} = $agentnum;
   local $FS::Record::conf = undef;  # XXX evil hack prevents recursion
-  my $cv = FS::Record::qsearchs('conf', $hashref);
-  if (!$agentonly && !$cv && defined($agentnum) && $agentnum) {
-    $hashref->{agentnum} = '';
-    $cv = FS::Record::qsearchs('conf', $hashref);
+  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);
+      return $cv if $cv;
+    }
   }
-  return $cv;
+  return undef;
 }
 
 sub config {
@@ -160,7 +183,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;
@@ -168,6 +191,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 
@@ -246,8 +317,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);
+    }
   }
 }
 
@@ -267,10 +343,14 @@ sub set {
 
   warn "[FS::Conf] SET $name\n" if $DEBUG;
 
-  my $old = FS::Record::qsearchs('conf', {name => $name, agentnum => $agentnum});
-  my $new = new FS::conf { $old ? $old->hash 
-                                : ('name' => $name, 'agentnum' => $agentnum)
-                         };
+  my $hashref = {
+    name => $name,
+    agentnum => $agentnum,
+    locale => $self->{locale}
+  };
+
+  my $old = FS::Record::qsearchs('conf', $hashref);
+  my $new = new FS::conf { $old ? $old->hash : %$hashref };
   $new->value($value);
 
   my $error;
@@ -311,7 +391,7 @@ sub delete {
   return $self->_usecompat('delete', @_) if use_confcompat;
 
   my($name, $agentnum) = @_;
-  if ( my $cv = FS::Record::qsearchs('conf', {name => $name, agentnum => $agentnum}) ) {
+  if ( my $cv = FS::Record::qsearchs('conf', {name => $name, agentnum => $agentnum, locale => $self->{locale}}) ) {
     warn "[FS::Conf] DELETE $name\n" if $DEBUG;
 
     my $oldAutoCommit = $FS::UID::AutoCommit;
@@ -330,6 +410,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
@@ -605,6 +710,21 @@ my %payment_gateway_options = (
   },
 );
 
+# 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)
@@ -622,6 +742,13 @@ my %payment_gateway_options = (
   },
 
   {
+    'key'         => 'log_sent_mail',
+    'section'     => 'notification',
+    'description' => 'Enable logging of template-generated email.',
+    'type'        => 'checkbox',
+  },
+
+  {
     'key'         => 'alert_expiration',
     'section'     => 'notification',
     'description' => 'Enable alerts about billing method expiration (i.e. expiring credit cards).',
@@ -645,6 +772,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',
@@ -674,7 +808,14 @@ my %payment_gateway_options = (
     'description' => 'Generate a line item on an invoice even when a package is discounted 100%',
     'type'        => 'checkbox',
   },
-  
+
+  {
+    'key'         => 'discount-show_available',
+    'section'     => 'billing',
+    'description' => 'Show available prepayment discounts on invoices.',
+    'type'        => 'checkbox',
+  },
+
   {
     'key'         => 'invoice-barcode',
     'section'     => 'billing',
@@ -692,7 +833,7 @@ my %payment_gateway_options = (
   {
     'key'         => 'encryption',
     'section'     => 'billing',
-    'description' => 'Enable encryption of credit cards.',
+    'description' => 'Enable encryption of credit cards and echeck numbers',
     'type'        => 'checkbox',
   },
 
@@ -700,20 +841,21 @@ my %payment_gateway_options = (
     'key'         => 'encryptionmodule',
     'section'     => 'billing',
     'description' => 'Use which module for encryption?',
-    'type'        => 'text',
+    'type'        => 'select',
+    'select_enum' => [ '', 'Crypt::OpenSSL::RSA', ],
   },
 
   {
     'key'         => 'encryptionpublickey',
     'section'     => 'billing',
-    'description' => 'Your RSA Public Key - Required if Encryption is turned on.',
+    'description' => 'Encryption public key',
     'type'        => 'textarea',
   },
 
   {
     'key'         => 'encryptionprivatekey',
     'section'     => 'billing',
-    'description' => 'Your RSA Private Key - Including this will enable the "Bill Now" feature.  However if the system is compromised, a hacker can use this key to decode the stored credit card information.  This is generally not a good idea.',
+    'description' => 'Encryption private key',
     'type'        => 'textarea',
   },
 
@@ -750,7 +892,14 @@ my %payment_gateway_options = (
     'type'        => 'text',
     'per_agent'   => 1,
   },
-
+  
+  {
+    'key'         => 'next-bill-ignore-time',
+    'section'     => 'billing',
+    'description' => 'Ignore the time portion of next bill dates when billing, matching anything from 00:00:00 to 23:59:59 on the billing day.',
+    'type'        => 'checkbox',
+  },
+  
   {
     'key'         => 'business-onlinepayment',
     'section'     => 'billing',
@@ -805,6 +954,14 @@ my %payment_gateway_options = (
   },
 
   {
+    'key'         => 'business-onlinepayment-currency',
+    'section'     => 'billing',
+    'description' => 'Currency parameter for Business::OnlinePayment transactions.',
+    'type'        => 'select',
+    'select_enum' => [ '', qw( USD AUD CAD DKK EUR GBP ILS JPY NZD ) ],
+  },
+
+  {
     'key'         => 'countrydefault',
     'section'     => 'UI',
     'description' => 'Default two-letter country code (if not supplied, the default is `US\')',
@@ -1005,6 +1162,7 @@ my %payment_gateway_options = (
     'description' => 'Subject: header on email invoices.  Defaults to "Invoice".  The following substitutions are available: $name, $name_short, $invoice_number, and $invoice_date.',
     'type'        => 'text',
     'per_agent'   => 1,
+    'per_locale'  => 1,
   },
 
   {
@@ -1035,6 +1193,7 @@ my %payment_gateway_options = (
     'description' => 'Notes section for HTML invoices.  Defaults to the same data in invoice_latexnotes if not specified.',
     'type'        => 'textarea',
     'per_agent'   => 1,
+    'per_locale'  => 1,
   },
 
   {
@@ -1043,6 +1202,7 @@ my %payment_gateway_options = (
     'description' => 'Footer for HTML invoices.  Defaults to the same data in invoice_latexfooter if not specified.',
     'type'        => 'textarea',
     'per_agent'   => 1,
+    'per_locale'  => 1,
   },
 
   {
@@ -1051,6 +1211,7 @@ my %payment_gateway_options = (
     'description' => 'Summary initial page for HTML invoices.',
     'type'        => 'textarea',
     'per_agent'   => 1,
+    'per_locale'  => 1,
   },
 
   {
@@ -1058,6 +1219,7 @@ my %payment_gateway_options = (
     'section'     => 'invoicing',
     'description' => 'Return address for HTML invoices.  Defaults to the same data in invoice_latexreturnaddress if not specified.',
     'type'        => 'textarea',
+    'per_locale'  => 1,
   },
 
   {
@@ -1122,6 +1284,7 @@ and customer address. Include units.',
     'description' => 'Notes section for LaTeX typeset PostScript invoices.',
     'type'        => 'textarea',
     'per_agent'   => 1,
+    'per_locale'  => 1,
   },
 
   {
@@ -1130,6 +1293,7 @@ and customer address. Include units.',
     'description' => 'Footer for LaTeX typeset PostScript invoices.',
     'type'        => 'textarea',
     'per_agent'   => 1,
+    'per_locale'  => 1,
   },
 
   {
@@ -1138,6 +1302,7 @@ and customer address. Include units.',
     'description' => 'Summary initial page for LaTeX typeset PostScript invoices.',
     'type'        => 'textarea',
     'per_agent'   => 1,
+    'per_locale'  => 1,
   },
 
   {
@@ -1146,6 +1311,7 @@ and customer address. Include units.',
     'description' => 'Remittance coupon for LaTeX typeset PostScript invoices.',
     'type'        => 'textarea',
     'per_agent'   => 1,
+    'per_locale'  => 1,
   },
 
   {
@@ -1224,6 +1390,7 @@ and customer address. Include units.',
     'description' => 'Optional small footer for multi-page LaTeX typeset PostScript invoices.',
     'type'        => 'textarea',
     'per_agent'   => 1,
+    'per_locale'  => 1,
   },
 
   {
@@ -1243,7 +1410,14 @@ and customer address. Include units.',
   {
     'key'         => 'invoice_print_pdf',
     'section'     => 'invoicing',
-    'description' => 'Store postal invoices for download in PDF format rather than printing them directly.',
+    'description' => 'For all invoice print operations, store postal invoices for download in PDF format rather than printing them directly.',
+    'type'        => 'checkbox',
+  },
+
+  {
+    'key'         => 'invoice_print_pdf-spoolagent',
+    'section'     => 'invoicing',
+    'description' => 'Store postal invoices PDF downloads in per-agent spools.',
     'type'        => 'checkbox',
   },
 
@@ -1252,7 +1426,7 @@ and customer address. Include units.',
     'section'     => 'invoicing',
     'description' => 'Optional default invoice term, used to calculate a due date printed on invoices.',
     'type'        => 'select',
-    'select_enum' => [ '', 'Payable upon receipt', 'Net 0', 'Net 3', 'Net 10', 'Net 15', 'Net 20', 'Net 21', 'Net 30', 'Net 45', 'Net 60', 'Net 90' ],
+    'select_enum' => [ '', 'Payable upon receipt', 'Net 0', 'Net 3', 'Net 9', 'Net 10', 'Net 15', 'Net 20', 'Net 21', 'Net 30', 'Net 45', 'Net 60', 'Net 90' ],
   },
 
   { 
@@ -1324,6 +1498,7 @@ and customer address. Include units.',
     'description' => 'Send payment receipts.',
     'type'        => 'checkbox',
     'per_agent'   => 1,
+    'agent_bool'  => 1,
   },
 
   {
@@ -1498,6 +1673,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.',
@@ -1697,6 +1879,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,
   },
 
   { 
@@ -1793,9 +1976,14 @@ and customer address. Include units.',
   {
     'key'         => 'locale',
     'section'     => 'UI',
-    'description' => 'Message locale',
+    'description' => 'Default locale',
     'type'        => 'select',
-    'select_enum' => [ qw(en_US) ],
+    'options_sub' => sub {
+      map { $_ => FS::Locales->description($_) } FS::Locales->locales;
+    },
+    'option_sub'  => sub {
+      FS::Locales->description(shift)
+    },
   },
 
   {
@@ -1821,22 +2009,17 @@ and customer address. Include units.',
   },
 
   {
+    'key'         => 'default_agentnum',
+    'section'     => 'UI',
+    'description' => 'Default agent for the backoffice',
+    'type'        => 'select-agent',
+  },
+
+  {
     'key'         => 'signup_server-default_agentnum',
     'section'     => 'self-service',
     'description' => 'Default agent for the signup server',
-    'type'        => 'select-sub',
-    'options_sub' => sub { require FS::Record;
-                           require FS::agent;
-                          map { $_->agentnum => $_->agent }
-                               FS::Record::qsearch('agent', { disabled=>'' } );
-                        },
-    'option_sub'  => sub { require FS::Record;
-                           require FS::agent;
-                          my $agent = FS::Record::qsearchs(
-                            'agent', { 'agentnum'=>shift }
-                          );
-                           $agent ? $agent->agent : '';
-                        },
+    'type'        => 'select-agent',
   },
 
   {
@@ -1908,6 +2091,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.',
@@ -2186,6 +2377,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.',
@@ -2224,6 +2422,13 @@ and customer address. Include units.',
   },
 
   {
+    'key'         => 'svc_broadband-radius',
+    'section'     => '',
+    'description' => 'Enable RADIUS groups for broadband services.',
+    'type'        => 'checkbox',
+  },
+
+  {
     'key'         => 'svc_acct-alldomains',
     'section'     => '',
     'description' => 'Allow accounts to select any domain in the database.  Normally accounts can only select from the domain set in the service definition and those purchased by the customer.',
@@ -2393,7 +2598,38 @@ and customer address. Include units.',
     'type'        => 'select-part_svc',
     'multiple'    => 1,
   },
-  
+
+  {
+    'key'         => 'selfservice-svc_forward_svcpart',
+    'section'     => 'self-service',
+    'description' => 'Service for self-service forward editing.',
+    'type'        => 'select-part_svc',
+  },
+
+  {
+    'key'         => 'selfservice-password_reset_verification',
+    'section'     => 'self-service',
+    'description' => 'If enabled, specifies the type of verification required for self-service password resets.',
+    'type'        => 'select',
+    'select_hash' => [ '' => 'Password reset disabled',
+                       'paymask,amount,zip' => 'Verify with credit card (or bank account) last 4 digits, payment amount and zip code',
+                     ],
+  },
+
+  {
+    'key'         => 'selfservice-password_reset_msgnum',
+    'section'     => 'self-service',
+    'description' => 'Template to use for password reset emails.',
+    %msg_template_options,
+  },
+
+  {
+    'key'         => 'selfservice-hide_invoices-taxclass',
+    'section'     => 'self-service',
+    'description' => 'Hide invoices with only this package tax class from self-service and supress sending (emailing, printing, faxing) them.  Typically set to something like "Previous balance" and used when importing legacy invoices into legacy_cust_bill.',
+    'type'        => 'text',
+  },
+
   {
     'key'         => 'selfservice-recent-did-age',
     'section'     => 'self-service',
@@ -2530,7 +2766,7 @@ and customer address. Include units.',
 
   {
     'key'         => 'ticket_system',
-    'section'     => '',
+    'section'     => 'ticketing',
     'description' => 'Ticketing system integration.  <b>RT_Internal</b> uses the built-in RT ticketing system (see the <a href="http://www.freeside.biz/mediawiki/index.php/Freeside:2.1:Documentation:RT_Installation">integrated ticketing installation instructions</a>).   <b>RT_External</b> accesses an external RT installation in a separate database (local or remote).',
     'type'        => 'select',
     #'select_enum' => [ '', qw(RT_Internal RT_Libs RT_External) ],
@@ -2539,16 +2775,30 @@ 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'     => '',
+    'section'     => 'ticketing',
     'description' => 'Default queue used when creating new customer tickets.',
     'type'        => 'select-sub',
     'options_sub' => sub {
@@ -2574,13 +2824,13 @@ and customer address. Include units.',
   },
   {
     'key'         => 'ticket_system-force_default_queueid',
-    'section'     => '',
+    'section'     => 'ticketing',
     'description' => 'Disallow queue selection when creating new tickets from customer view.',
     'type'        => 'checkbox',
   },
   {
     'key'         => 'ticket_system-selfservice_queueid',
-    'section'     => '',
+    'section'     => 'ticketing',
     'description' => 'Queue used when creating new customer tickets from self-service.  Defautls to ticket_system-default_queueid if not specified.',
     #false laziness w/above
     'type'        => 'select-sub',
@@ -2608,49 +2858,63 @@ and customer address. Include units.',
 
   {
     'key'         => 'ticket_system-requestor',
-    'section'     => '',
+    'section'     => 'ticketing',
     'description' => 'Email address to use as the requestor for new tickets.  If blank, the customer\'s invoicing address(es) will be used.',
     'type'        => 'text',
   },
 
   {
     'key'         => 'ticket_system-priority_reverse',
-    'section'     => '',
+    'section'     => 'ticketing',
     'description' => 'Enable this to consider lower numbered priorities more important.  A bad habit we picked up somewhere.  You probably want to avoid it and use the default.',
     'type'        => 'checkbox',
   },
 
   {
     'key'         => 'ticket_system-custom_priority_field',
-    'section'     => '',
+    'section'     => 'ticketing',
     'description' => 'Custom field from the ticketing system to use as a custom priority classification.',
     'type'        => 'text',
   },
 
   {
     'key'         => 'ticket_system-custom_priority_field-values',
-    'section'     => '',
+    'section'     => 'ticketing',
     'description' => 'Values for the custom field from the ticketing system to break down and sort customer ticket lists.',
     'type'        => 'textarea',
   },
 
   {
     'key'         => 'ticket_system-custom_priority_field_queue',
-    'section'     => '',
+    'section'     => 'ticketing',
     'description' => 'Ticketing system queue in which the custom field specified in ticket_system-custom_priority_field is located.',
     'type'        => 'text',
   },
 
   {
+    'key'         => 'ticket_system-selfservice_priority_field',
+    'section'     => 'ticketing',
+    'description' => 'Custom field from the ticket system to use as a customer-managed priority field.',
+    'type'        => 'text',
+  },
+
+  {
+    'key'         => 'ticket_system-selfservice_edit_subject',
+    'section'     => 'ticketing',
+    'description' => 'Allow customers to edit ticket subjects through selfservice.',
+    'type'        => 'checkbox',
+  },
+
+  {
     'key'         => 'ticket_system-escalation',
-    'section'     => '',
+    'section'     => 'ticketing',
     'description' => 'Enable priority escalation of tickets as part of daily batch processing.',
     'type'        => 'checkbox',
   },
 
   {
     'key'         => 'ticket_system-rt_external_datasrc',
-    'section'     => '',
+    'section'     => 'ticketing',
     'description' => 'With external RT integration, the DBI data source for the external RT installation, for example, <code>DBI:Pg:user=rt_user;password=rt_word;host=rt.example.com;dbname=rt</code>',
     'type'        => 'text',
 
@@ -2658,7 +2922,7 @@ and customer address. Include units.',
 
   {
     'key'         => 'ticket_system-rt_external_url',
-    'section'     => '',
+    'section'     => 'ticketing',
     'description' => 'With external RT integration, the URL for the external RT installation, for example, <code>https://rt.example.com/rt</code>',
     'type'        => 'text',
   },
@@ -2672,6 +2936,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',
@@ -2734,6 +3006,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',
@@ -2761,7 +3034,7 @@ and customer address. Include units.',
     'section'     => 'invoicing',
     'description' => 'Enable FTP of raw invoice data - format.',
     'type'        => 'select',
-    'select_enum' => [ '', 'default', 'billco', ],
+    'select_enum' => [ '', 'default', 'oneline', 'billco', ],
   },
 
   {
@@ -2797,7 +3070,7 @@ and customer address. Include units.',
     'section'     => 'invoicing',
     'description' => 'Enable spooling of raw invoice data - format.',
     'type'        => 'select',
-    'select_enum' => [ '', 'default', 'billco', ],
+    'select_enum' => [ '', 'default', 'oneline', 'billco', ],
   },
 
   {
@@ -2831,9 +3104,22 @@ and customer address. Include units.',
   {
     'key'         => 'overlimit_groups',
     'section'     => '',
-    'description' => 'RADIUS group (or comma-separated groups) to assign to svc_acct which has exceeded its bandwidth or time limit.',
-    'type'        => 'text',
+    'description' => 'RADIUS group(s) to assign to svc_acct which has exceeded its bandwidth or time limit.',
+    'type'        => 'select-sub',
     'per_agent'   => 1,
+    'multiple'    => 1,
+    'options_sub' => sub { require FS::Record;
+                           require FS::radius_group;
+                          map { $_->groupnum => $_->long_description }
+                               FS::Record::qsearch('radius_group', {} );
+                        },
+    'option_sub'  => sub { require FS::Record;
+                           require FS::radius_group;
+                          my $radius_group = FS::Record::qsearchs(
+                            'radius_group', { 'groupnum' => shift }
+                          );
+               $radius_group ? $radius_group->long_description : '';
+                        },
   },
 
   {
@@ -2845,6 +3131,24 @@ 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_location-agent_code',
+    'section'     => 'UI',
+    'description' => 'Optional agent string for cust_location-label_prefix',
+    'type'        => 'text',
+    'per_agent'   => 1,
+  },
+
+  {
     '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.',
@@ -2868,7 +3172,7 @@ and customer address. Include units.',
   {
     'key'         => 'cust_pkg-show_fcc_voice_grade_equivalent',
     'section'     => 'UI',
-    'description' => "Show a field on package definitions for assigning a DSO equivalency number suitable for use on FCC form 477.",
+    'description' => "Show a field on package definitions for assigning a DS0 equivalency number suitable for use on FCC form 477.",
     'type'        => 'checkbox',
   },
 
@@ -2894,6 +3198,13 @@ and customer address. Include units.',
   },
 
   {
+    'key'         => 'svc_acct-no_edit_username',
+    'section'     => 'shell',
+    'description' => 'Disallow username editing.',
+    'type'        => 'checkbox',
+  },
+
+  {
     'key'         => 'zone-underscore',
     'section'     => 'BIND',
     'description' => 'Allow underscores in zone names.  As underscores are illegal characters in zone names, this option is not recommended.',
@@ -2908,6 +3219,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.',
@@ -3010,6 +3332,7 @@ and customer address. Include units.',
     'section'     => 'billing',
     'description' => 'This allows selection of a package to insert on invoices for customers with postal invoices selected.',
     'type'        => 'select-part_pkg',
+    'per_agent'   => 1,
   },
 
   {
@@ -3070,7 +3393,8 @@ and customer address. Include units.',
     'description' => 'Fixed (unchangeable) format for electronic check batches.',
     'type'        => 'select',
     'select_enum' => [ 'csv-td_canada_trust-merchant_pc_batch', 'BoM', 'PAP',
-                       'paymentech', 'ach-spiritone', 'RBC', 'td_eft1464'
+                       'paymentech', 'ach-spiritone', 'RBC', 'td_eft1464',
+                       'eft_canada'
                      ]
   },
 
@@ -3131,6 +3455,21 @@ and customer address. Include units.',
   },
 
   {
+    'key'         => 'batchconfig-eft_canada',
+    'section'     => 'billing',
+    'description' => 'Configuration for EFT Canada batching, four lines: 1. SFTP username, 2. SFTP password, 3. Transaction code, 4. Number of days to delay process date.',
+    'type'        => 'textarea',
+    'per_agent'   => 1,
+  },
+
+  {
+    'key'         => 'batch-spoolagent',
+    'section'     => 'billing',
+    'description' => 'Store payment batches per-agent.',
+    'type'        => 'checkbox',
+  },
+
+  {
     'key'         => 'payment_history-years',
     'section'     => 'UI',
     'description' => 'Number of years of payment history to show by default.  Currently defaults to 2.',
@@ -3209,6 +3548,13 @@ and customer address. Include units.',
   },
 
   {
+    'key'         => 'cust_main-edit_calling_list_exempt',
+    'section'     => 'UI',
+    'description' => 'Display the "calling_list_exempt" checkbox on customer edit.',
+    'type'        => 'checkbox',
+  },
+
+  {
     'key'         => 'support-key',
     'section'     => '',
     'description' => 'A support key enables access to commercial services delivered over the network, such as the payroll module, access to the internal ticket system, priority support and optional backups.',
@@ -3279,6 +3625,7 @@ and customer address. Include units.',
     'type'        => 'image',
     'per_agent'   => 1, #XXX just view/logo.cgi, which is for the global
                         #old-style editor anyway...?
+    'per_locale'  => 1,
   },
 
   {
@@ -3287,6 +3634,7 @@ and customer address. Include units.',
     'description' => 'Company logo for printed and PDF invoices, in EPS format.',
     'type'        => 'image',
     'per_agent'   => 1, #XXX as above, kinda
+    'per_locale'  => 1,
   },
 
   {
@@ -3347,6 +3695,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...
@@ -3359,6 +3727,7 @@ and customer address. Include units.',
     'section'     => '',
     'description' => 'Require daytime or night phone for all customer records.',
     'type'        => 'checkbox',
+    'per_agent'   => 1,
   },
 
   {
@@ -3366,6 +3735,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',
+    ],
   },
 
   {
@@ -3379,77 +3760,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'),
   },
 
   {
@@ -3458,9 +3797,9 @@ and customer address. Include units.',
     'description' => 'Enables searching of various formatted values in cust_main.agent_custid',
     'type'        => 'select',
     'select_hash' => [
-                       ''      => 'Numeric only',
-                       '\d{7}' => 'Numeric only, exactly 7 digits',
-                       'ww?d+' => 'Numeric with one or two letter prefix',
+                       ''       => 'Numeric only',
+                       '\d{7}'  => 'Numeric only, exactly 7 digits',
+                       'ww?d+'  => 'Numeric with one or two letter prefix',
                      ],
   },
 
@@ -3486,6 +3825,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,
   },
 
   {
@@ -3549,7 +3889,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() ],
   },
 
   {
@@ -3567,6 +3915,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.',
@@ -3619,6 +3982,31 @@ and customer address. Include units.',
   },
 
   {
+    'key'         => 'cust_main-custnum-display_prefix',
+    'section'     => 'UI',
+    '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',
+  },
+
+  {
     'key'         => 'cust_main-default_areacode',
     'section'     => 'UI',
     'description' => 'Default area code for customers.',
@@ -3661,6 +4049,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".',
@@ -3710,6 +4105,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',
@@ -3758,6 +4169,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',
@@ -3859,6 +4278,27 @@ and customer address. Include units.',
     'type'        => 'image',
     'per_agent'   => 1,
   },
+  
+  {
+    'key'         => 'selfservice-view_usage_nodomain',
+    'section'     => 'self-service',
+    'description' => 'Show usernames without their domains in "View my usage" in the self-service interface.',
+    'type'        => 'checkbox',
+  },
+
+  {
+    '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',
@@ -3894,11 +4334,18 @@ and customer address. Include units.',
   {
     'key'         => 'signup-recommend_daytime',
     'section'     => 'self-service',
-    'description' => 'Encourage the entry of a daytime phone number  invoicing email address on signup.',
+    'description' => 'Encourage the entry of a daytime phone number on signup.',
     'type'        => 'checkbox',
   },
 
   {
+    'key'         => 'signup-duplicate_cc-warn_hours',
+    'section'     => 'self-service',
+    'description' => 'Issue a warning if the same credit card is used for multiple signups within this many hours.',
+    'type'        => 'text',
+  },
+
+  {
     'key'         => 'svc_phone-radius-default_password',
     'section'     => 'telephony',
     'description' => 'Default password when exporting svc_phone records to RADIUS',
@@ -3925,7 +4372,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',
@@ -4006,6 +4460,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.',
@@ -4097,6 +4558,31 @@ and customer address. Include units.',
     'type'        => 'text',
   },
 
+  {
+    'key'         => 'svc_broadband-manage_link_text',
+    'section'     => 'UI',
+    'description' => 'Label for "Manage Device" link',
+    'type'        => 'text',
+  },
+
+  {
+    'key'         => 'svc_broadband-manage_link_loc',
+    'section'     => 'UI',
+    'description' => 'Location for "Manage Device" link',
+    'type'        => 'select',
+    'select_hash' => [
+      'bottom' => 'Near Unprovision link',
+      'right'  => 'With export-related links',
+    ],
+  },
+
+  {
+    'key'         => 'svc_broadband-manage_link-new_window',
+    'section'     => 'UI',
+    'description' => 'Open the "Manage Device" link in a new window',
+    'type'        => 'checkbox',
+  },
+
   #more fine-grained, service def-level control could be useful eventually?
   {
     'key'         => 'svc_broadband-allow_null_ip_addr',
@@ -4106,6 +4592,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 !=).',
@@ -4272,8 +4765,8 @@ and customer address. Include units.',
   {
     'key'         => 'cust_main-custom_link',
     'section'     => 'UI',
-    'description' => 'URL to use as source for the "Custom" tab in the View Customer page.  The custnum will be appended.',
-    'type'        => 'text',
+    'description' => 'URL to use as source for the "Custom" tab in the View Customer page.  The customer number will be appended, or you can insert "$custnum" to have it inserted elsewhere.  "$agentnum" will be replaced with the agent number, and "$usernum" will be replaced with the employee number.',
+    'type'        => 'textarea',
   },
 
   {
@@ -4347,7 +4840,14 @@ and customer address. Include units.',
   {
     'key'         => 'svc_phone-did-summary',
     'section'     => 'invoicing',
-    'description' => 'Enable DID activity summary for past 30 days on invoices, showing # DIDs activated/deactivated/ported-in/ported-out and total minutes usage',
+    'description' => 'Enable DID activity summary on invoices, showing # DIDs activated/deactivated/ported-in/ported-out and total minutes usage, covering period since last invoice.',
+    'type'        => 'checkbox',
+  },
+
+  {
+    'key'         => 'svc_acct-usage_seconds',
+    'section'     => 'invoicing',
+    'description' => 'Enable calculation of RADIUS usage time for invoices.  You must modify your template to display this information.',
     'type'        => 'checkbox',
   },
   
@@ -4386,6 +4886,13 @@ and customer address. Include units.',
   },
 
   {
+    'key'         => 'cust_bill-latex_lineitem_maxlength',
+    'section'     => 'invoicing',
+    'description' => 'Truncate long line items to this number of characters on typeset invoices, to avoid losing things off the right margin.  Defaults to 50.  ',
+    'type'        => 'text',
+  },
+
+  {
     'key'         => 'cust_main-status_module',
     'section'     => 'UI',
     'description' => 'Which module to use for customer status display.  The "Classic" module (the default) considers accounts with cancelled recurring packages but un-cancelled one-time charges Inactive.  The "Recurring" module considers those customers Cancelled.  Similarly for customers with suspended recurring packages but one-time charges.', #other differences?
@@ -4393,6 +4900,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',
@@ -4414,7 +4928,127 @@ and customer address. Include units.',
     'description' => 'Disable the "Charge future payments to this (card|check) automatically" checkbox from defaulting to checked.',
     'type'        => 'checkbox',
   },
+  
+  {
+    'key'         => 'payment-history-report',
+    'section'     => 'UI',
+    'description' => 'Show a link to the raw database payment history report in the Reports menu.  DO NOT ENABLE THIS for modern installations.',
+    'type'        => 'checkbox',
+  },
+  
+  {
+    'key'         => 'svc_broadband-require-nw-coordinates',
+    'section'     => 'deprecated',
+    'description' => 'Deprecated; see geocode-require_nw_coordinates instead',
+    'type'        => 'checkbox',
+  },
+  
+  {
+    'key'         => 'cust-email-high-visibility',
+    'section'     => 'UI',
+    'description' => 'Move the invoicing e-mail address field to the top of the billing address section and highlight it.',
+    'type'        => 'checkbox',
+  },
+  
+  {
+    'key'         => 'cust-edit-alt-field-order',
+    'section'     => 'UI',
+    'description' => 'An alternate ordering of fields for the New Customer and Edit Customer screens.',
+    'type'        => 'checkbox',
+  },
+
+  {
+    'key'         => 'cust_bill-enable_promised_date',
+    'section'     => 'UI',
+    'description' => 'Enable display/editing of the "promised payment date" field on invoices.',
+    'type'        => 'checkbox',
+  },
+  
+  {
+    'key'         => 'available-locales',
+    'section'     => '',
+    'description' => 'Limit available locales (employee preferences, per-customer locale selection, etc.) to a particular set.',
+    'type'        => 'select-sub',
+    'multiple'    => 1,
+    'options_sub' => sub { 
+      map { $_ => FS::Locales->description($_) }
+      grep { $_ ne 'en_US' } 
+      FS::Locales->locales;
+    },
+    '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 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 { 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 => "apacheroot", section => "deprecated", description => "<b>DEPRECATED</b>", type => "text" },
   { key => "apachemachine", section => "deprecated", description => "<b>DEPRECATED</b>", type => "text" },