RT#24684: Payments for Online Bill Pay
[freeside.git] / FS / FS / Conf.pm
index f9aa0e6..17030d2 100644 (file)
@@ -11,12 +11,11 @@ use MIME::Base64;
 use Locale::Currency;
 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::UID qw(dbh datasrc);
 use FS::Misc::Invoicing qw( spool_formats );
 
 $base_dir = '%%%FREESIDE_CONF%%%';
@@ -113,14 +112,6 @@ specific value(s) is returned.
 
 =cut
 
-sub _usecompat {
-  my ($self, $method) = (shift, shift);
-  carp "NO CONFIGURATION RECORDS FOUND -- USING COMPATIBILITY MODE"
-    if use_confcompat;
-  my $compat = new FS::Conf_compat17 ("$base_dir/conf." . datasrc);
-  $compat->$method(@_);
-}
-
 sub _config {
   my($self,$name,$agentnum,$agentonly)=@_;
   my $hashref = { 'name' => $name };
@@ -155,7 +146,6 @@ sub _config {
 
 sub config {
   my $self = shift;
-  return $self->_usecompat('config', @_) if use_confcompat;
 
   carp "FS::Conf->config(". join(', ', @_). ") called"
     if $DEBUG > 1;
@@ -179,7 +169,6 @@ Returns the exact scalar value for key.
 
 sub config_binary {
   my $self = shift;
-  return $self->_usecompat('config_binary', @_) if use_confcompat;
 
   my $cv = $self->_config(@_) or return;
   length($cv->value) ? decode_base64($cv->value) : '';
@@ -194,7 +183,6 @@ is undefined.
 
 sub exists {
   my $self = shift;
-  return $self->_usecompat('exists', @_) if use_confcompat;
 
   #my($name, $agentnum)=@_;
 
@@ -209,7 +197,6 @@ sub exists {
 
 sub config_bool {
   my $self = shift;
-  return $self->_usecompat('exists', @_) if use_confcompat;
 
   my($name,$agentnum,$agentonly) = @_;
 
@@ -264,7 +251,6 @@ KEY_SUFFIX, if it exists, otherwise for KEY
 # these to fall back to standard values
 sub config_orbase {
   my $self = shift;
-  return $self->_usecompat('config_orbase', @_) if use_confcompat;
 
   my( $name, $suffix ) = @_;
   if ( $self->exists("${name}_$suffix") ) {
@@ -284,7 +270,6 @@ config_orbase.
 
 sub key_orbase {
   my $self = shift;
-  #no compat for this...return $self->_usecompat('config_orbase', @_) if use_confcompat;
 
   my( $name, $suffix ) = @_;
   if ( $self->exists("${name}_$suffix") ) {
@@ -327,7 +312,6 @@ Creates the specified configuration key if it does not exist.
 
 sub touch {
   my $self = shift;
-  return $self->_usecompat('touch', @_) if use_confcompat;
 
   my($name, $agentnum) = @_;
   #unless ( $self->exists($name, $agentnum) ) {
@@ -348,7 +332,6 @@ Sets the specified configuration key to the given value.
 
 sub set {
   my $self = shift;
-  return $self->_usecompat('set', @_) if use_confcompat;
 
   my($name, $value, $agentnum) = @_;
   $value =~ /^(.*)$/s;
@@ -393,7 +376,6 @@ can be retrieved with config_binary.
 
 sub set_binary {
   my $self  = shift;
-  return if use_confcompat;
 
   my($name, $value, $agentnum)=@_;
   $self->set($name, encode_base64($value), $agentnum);
@@ -407,7 +389,6 @@ Deletes the specified configuration key.
 
 sub delete {
   my $self = shift;
-  return $self->_usecompat('delete', @_) if use_confcompat;
 
   my($name, $agentnum) = @_;
   if ( my $cv = FS::Record::qsearchs('conf', {name => $name, agentnum => $agentnum, locale => $self->{locale}}) ) {
@@ -434,7 +415,6 @@ sub delete {
 
 sub delete_bool {
   my $self = shift;
-  return $self->_usecompat('delete', @_) if use_confcompat;
 
   my($name, $agentnum) = @_;
 
@@ -465,7 +445,7 @@ in the directory DIR.
 sub import_config_item { 
   my ($self,$item,$dir) = @_;
   my $key = $item->key;
-  if ( -e "$dir/$key" && ! use_confcompat ) {
+  if ( -e "$dir/$key" ) {
     warn "Inserting $key\n" if $DEBUG;
     local $/;
     my $value = readline(new IO::File "$dir/$key");
@@ -474,68 +454,9 @@ sub import_config_item {
     }else{
       $self->set($key, $value);
     }
-  }else {
-    warn "Not inserting $key\n" if $DEBUG;
-  }
-}
-
-=item verify_config_item CONFITEM DIR 
-
-  Compares the item specified by the CONFITEM (see L<FS::ConfItem>) in
-the database to the legacy file value in DIR.
-
-=cut
-
-sub verify_config_item { 
-  return '' if use_confcompat;
-  my ($self,$item,$dir) = @_;
-  my $key = $item->key;
-  my $type = $item->type;
-
-  my $compat = new FS::Conf_compat17 $dir;
-  my $error = '';
-  
-  $error .= "$key fails existential comparison; "
-    if $self->exists($key) xor $compat->exists($key);
-
-  if ( $type !~ /^(binary|image)$/ ) {
-
-    {
-      no warnings;
-      $error .= "$key fails scalar comparison; "
-        unless scalar($self->config($key)) eq scalar($compat->config($key));
-    }
-
-    my (@new) = $self->config($key);
-    my (@old) = $compat->config($key);
-    unless ( scalar(@new) == scalar(@old)) { 
-      $error .= "$key fails list comparison; ";
-    }else{
-      my $r=1;
-      foreach (@old) { $r=0 if ($_ cmp shift(@new)); }
-      $error .= "$key fails list comparison; "
-        unless $r;
-    }
-
   } else {
-
-    no warnings 'uninitialized';
-    $error .= "$key fails binary comparison; "
-      unless scalar($self->config_binary($key)) eq scalar($compat->config_binary($key));
-
+    warn "Not inserting $key\n" if $DEBUG;
   }
-
-#remove deprecated config on our own terms, not freeside-upgrade's
-#  if ($error =~ /existential comparison/ && $item->section eq 'deprecated') {
-#    my $proto;
-#    for ( @config_items ) { $proto = $_; last if $proto->key eq $key;  }
-#    unless ($proto->key eq $key) { 
-#      warn "removed config item $error\n" if $DEBUG;
-#      $error = '';
-#    }
-#  }
-
-  $error;
 }
 
 #item _orbase_items OPTIONS
@@ -606,7 +527,6 @@ FS::ConfItem objects.  See L<FS::ConfItem>.
 
 sub config_items {
   my $self = shift; 
-  return $self->_usecompat('config_items', @_) if use_confcompat;
 
   ( @config_items, $self->_orbase_items(@_) );
 }
@@ -642,23 +562,11 @@ to conf records in the database.
 sub init_config {
   my $dir = shift;
 
-  {
-    local $FS::UID::use_confcompat = 0;
-    my $conf = new FS::Conf;
-    foreach my $item ( $conf->config_items(dir => $dir) ) {
-      $conf->import_config_item($item, $dir);
-      my $error = $conf->verify_config_item($item, $dir);
-      return $error if $error;
-    }
-  
-    my $compat = new FS::Conf_compat17 $dir;
-    foreach my $item ( $compat->config_items ) {
-      my $error = $conf->verify_config_item($item, $dir);
-      return $error if $error;
-    }
+  my $conf = new FS::Conf;
+  foreach my $item ( $conf->config_items(dir => $dir) ) {
+    $conf->import_config_item($item, $dir);
   }
 
-  $FS::UID::use_confcompat = 0;
   '';  #success
 }
 
@@ -697,10 +605,12 @@ invoice_latexfooter
 invoice_latexsmallfooter
 invoice_latexnotes
 invoice_latexcoupon
+invoice_latexwatermark
 invoice_html
 invoice_htmlreturnaddress
 invoice_htmlfooter
 invoice_htmlnotes
+invoice_htmlwatermark
 logo.png
 logo.eps
 );
@@ -795,6 +705,11 @@ sub reason_type_options {
   }
 }
 
+my $validate_email = sub { $_[0] =~
+                             /^[^@]+\@[[:alnum:]-]+(\.[[:alnum:]-]+)+$/
+                             ? '' : 'Invalid email address';
+                         };
+
 #Billing (81 items)
 #Invoicing (50 items)
 #UI (69 items)
@@ -1286,10 +1201,7 @@ sub reason_type_options {
     'description' => 'Return address on email invoices (address only, see invoice_from_name)',
     'type'        => 'text',
     'per_agent'   => 1,
-    'validate'    => sub { $_[0] =~
-                             /^[^@]+\@[[:alnum:]-]+(\.[[:alnum:]-]+)+$/
-                             ? '' : 'Invalid email address';
-                         }
+    'validate'    => $validate_email,
   },
 
   {
@@ -1396,6 +1308,15 @@ sub reason_type_options {
   },
 
   {
+    'key'         => 'invoice_htmlwatermark',
+    'section'     => 'invoicing',
+    'description' => 'Watermark for HTML invoices. Appears in a semitransparent positioned DIV overlaid on the main invoice container.',
+    'type'        => 'textarea',
+    'per_agent'   => 1,
+    'per_locale'  => 1,
+  },
+
+  {
     'key'         => 'invoice_latex',
     'section'     => 'invoicing',
     'description' => 'Optional LaTeX template for typeset PostScript invoices.  See the <a href="http://www.freeside.biz/mediawiki/index.php/Freeside:2.1:Documentation:Administration#Typeset_.28LaTeX.29_invoice_templates">billing documentation</a> for details.',
@@ -1583,6 +1504,15 @@ and customer address. Include units.',
   },
 
   {
+    'key'         => 'invoice_latexwatermark',
+    'section'     => 'invoicing',
+    'description' => 'Watermark for LaTeX invoices. See "texdoc background" for information on what this can contain. The content itself should be enclosed in braces, optionally followed by a comma and any formatting options.',
+    'type'        => 'textarea',
+    'per_agent'   => 1,
+    'per_locale'  => 1,
+  },
+
+  {
     'key'         => 'invoice_email_pdf',
     'section'     => 'invoicing',
     'description' => 'Send PDF invoice as an attachment to emailed invoices.  By default, includes the HTML invoice as the email body, unless invoice_email_pdf_note is set.',
@@ -2387,6 +2317,7 @@ and customer address. Include units.',
                        'svc_acct'  => 'Account (svc_acct)',
                        'svc_phone' => 'Phone number (svc_phone)',
                        'svc_pbx'   => 'PBX (svc_pbx)',
+                       'none'      => 'None - package only',
                      ],
   },
   
@@ -2543,55 +2474,91 @@ and customer address. Include units.',
 
   {
     'key'         => 'enable_taxclasses',
-    'section'     => 'billing',
+    'section'     => 'taxation',
     'description' => 'Enable per-package tax classes',
     'type'        => 'checkbox',
   },
 
   {
     'key'         => 'require_taxclasses',
-    'section'     => 'billing',
+    'section'     => 'taxation',
     'description' => 'Require a taxclass to be entered for every package',
     'type'        => 'checkbox',
   },
 
   {
-    'key'         => 'enable_taxproducts',
-    'section'     => 'billing',
+    'key'         => 'tax_data_vendor',
+    'section'     => 'taxation',
     'description' => 'Tax data vendor you are using.',
     'type'        => 'select',
-    'select_enum' => [ 'cch', 'billsoft', 'avalara' ],
+    'select_enum' => [ '', 'cch', 'billsoft', 'avalara', 'suretax' ],
   },
 
   {
     'key'         => 'taxdatadirectdownload',
-    'section'     => 'billing',  #well
-    'description' => 'Enable downloading tax data directly from the vendor site. at least three lines: URL, username, and password.j',
+    'section'     => 'taxation',
+    'description' => 'Enable downloading tax data directly from CCH. at least three lines: URL, username, and password.j',
     'type'        => 'textarea',
   },
 
   {
     'key'         => 'ignore_incalculable_taxes',
-    'section'     => 'billing',
+    'section'     => 'taxation',
     'description' => 'Prefer to invoice without tax over not billing at all',
     'type'        => 'checkbox',
   },
 
   {
     'key'         => 'billsoft-company_code',
-    'section'     => 'billing',
+    'section'     => 'taxation',
     'description' => 'Billsoft tax service company code (3 letters)',
     'type'        => 'text',
   },
 
   {
     'key'         => 'avalara-taxconfig',
-    'section'     => 'billing',
+    'section'     => 'taxation',
     'description' => 'Avalara tax service configuration. Four lines: company code, account number, license key, test mode (1 to enable).',
     'type'        => 'textarea',
   },
 
   {
+    'key'         => 'suretax-hostname',
+    'section'     => 'taxation',
+    'description' => 'SureTax server name; defaults to the test server.',
+    'type'        => 'text',
+  },
+
+  {
+    'key'         => 'suretax-client_number',
+    'section'     => 'taxation',
+    'description' => 'SureTax tax service client ID.',
+    'type'        => 'text',
+  },
+  {
+    'key'         => 'suretax-validation_key',
+    'section'     => 'taxation',
+    'description' => 'SureTax validation key (UUID).',
+    'type'        => 'text',
+  },
+  {
+    'key'         => 'suretax-business_unit',
+    'section'     => 'taxation',
+    'description' => 'SureTax client business unit name; optional.',
+    'type'        => 'text',
+    'per_agent'   => 1,
+  },
+  {
+    'key'         => 'suretax-regulatory_code',
+    'section'     => 'taxation',
+    'description' => 'SureTax client regulatory status.',
+    'type'        => 'select',
+    'select_enum' => [ '', 'ILEC', 'IXC', 'CLEC', 'VOIP', 'ISP', 'Wireless' ],
+    'per_agent'   => 1,
+  },
+
+
+  {
     'key'         => 'welcome_msgnum',
     'section'     => 'notification',
     'description' => 'Template to use for welcome messages when a svc_acct record is created.',
@@ -2793,6 +2760,14 @@ and customer address. Include units.',
   },
 
   {
+    'key'         => 'dump-email_to',
+    'section'     => '',
+    'description' => "Optional email address to send success/failure message for database dumps.",
+    'type'        => 'text',
+    'validate'    => $validate_email,
+  },
+
+  {
     'key'         => 'users-allow_comp',
     'section'     => 'deprecated',
     'description' => '<b>DEPRECATED</b>, enable the <i>Complimentary customer</i> access right instead.  Was: Usernames (Freeside users, created with <a href="../docs/man/bin/freeside-adduser.html">freeside-adduser</a>) which can create complimentary customers, one per line.  If no usernames are entered, all users can create complimentary accounts.',
@@ -3770,14 +3745,14 @@ and customer address. Include units.',
 
   {
     'key'         => 'tax-ship_address',
-    'section'     => 'billing',
+    'section'     => 'taxation',
     'description' => 'By default, tax calculations are done based on the billing address.  Enable this switch to calculate tax based on the shipping address instead.',
     'type'        => 'checkbox',
   }
 ,
   {
     'key'         => 'tax-pkg_address',
-    'section'     => 'billing',
+    'section'     => 'taxation',
     'description' => 'By default, tax calculations are done based on the billing address.  Enable this switch to calculate tax based on the package address instead (when present).',
     'type'        => 'checkbox',
   },
@@ -4559,7 +4534,7 @@ and customer address. Include units.',
 
   {
     'key'         => 'tax_district_method',
-    'section'     => 'UI',
+    'section'     => 'taxation',
     'description' => 'The method to use to look up tax district codes.',
     'type'        => 'select',
     #'select_hash' => [ FS::Misc::Geo::get_district_methods() ],
@@ -5320,7 +5295,7 @@ and customer address. Include units.',
 
   {
     'key'         => 'tax-cust_exempt-groups',
-    'section'     => 'billing',
+    'section'     => 'taxation',
     'description' => 'List of grouping possibilities for tax names, for per-customer exemption purposes, one tax name per line.  For example, "GST" would indicate the ability to exempt customers individually from taxes named "GST" (but not other taxes).',
     'type'        => 'textarea',
   },
@@ -5334,7 +5309,7 @@ and customer address. Include units.',
 
   {
     'key'         => 'tax-cust_exempt-groups-num_req',
-    'section'     => 'billing',
+    'section'     => 'taxation',
     'description' => 'When using tax-cust_exempt-groups, control whether individual tax exemption numbers are required for exemption from different taxes.',
     'type'        => 'select',
     'select_hash' => [ ''            => 'Not required',
@@ -5362,7 +5337,7 @@ and customer address. Include units.',
 
   {
     'key'         => 'enable_tax_adjustments',
-    'section'     => 'billing',
+    'section'     => 'taxation',
     'description' => 'Enable the ability to add manual tax adjustments.',
     'type'        => 'checkbox',
   },
@@ -5815,7 +5790,7 @@ and customer address. Include units.',
 
   {
     'key'         => 'cust_class-tax_exempt',
-    'section'     => 'billing',
+    'section'     => 'taxation',
     'description' => 'Control the tax exemption flag per customer class rather than per indivual customer.',
     'type'        => 'checkbox',
   },
@@ -5851,6 +5826,13 @@ and customer address. Include units.',
   },
 
   {
+    'key'         => 'selfservice-enable_payment_without_balance',
+    'section'     => 'self-service',
+    'description' => 'Allow selfservice customers to make payments even if balance is zero or below (resulting in an unapplied payment and negative balance.)',
+    'type'        => 'checkbox',
+  },
+
+  {
     'key'         => 'logout-timeout',
     'section'     => 'UI',
     'description' => 'If set, automatically log users out of the backoffice after this many minutes.',
@@ -6003,6 +5985,13 @@ and customer address. Include units.',
     'type'        => 'checkbox',
   },
 
+  {
+    'key'         => 'default_appointment_length',
+    'section'     => 'UI',
+    'description' => 'Default appointment length, in minutes (30 minute granularity).',
+    'type'        => 'text',
+  },
+
   { key => "apacheroot", section => "deprecated", description => "<b>DEPRECATED</b>", type => "text" },
   { key => "apachemachine", section => "deprecated", description => "<b>DEPRECATED</b>", type => "text" },
   { key => "apachemachines", section => "deprecated", description => "<b>DEPRECATED</b>", type => "text" },