diff options
| author | Ivan Kohler <ivan@freeside.biz> | 2013-09-03 22:52:26 -0700 | 
|---|---|---|
| committer | Ivan Kohler <ivan@freeside.biz> | 2013-09-03 22:52:26 -0700 | 
| commit | 36ad5e538cb56de33c779e34baf9abdf63c4312e (patch) | |
| tree | de42d922496c881b69643656f055b68e64e96d72 | |
| parent | 04c1a98c9bcf2b6f0da8f18b627eec703dbafd49 (diff) | |
| parent | aae697565c071b2880d0106b00a4a01d0ddab7bf (diff) | |
Merge branch 'master' of git.freeside.biz:/home/git/freeside
| -rw-r--r-- | FS/FS/Conf.pm | 6 | ||||
| -rw-r--r-- | FS/FS/Cron/alert_expiration.pm | 190 | ||||
| -rw-r--r-- | FS/FS/cdr.pm | 8 | ||||
| -rw-r--r-- | FS/FS/msg_template.pm | 58 | ||||
| -rw-r--r-- | FS/FS/part_event/Condition/cust_paydate_within.pm | 11 | ||||
| -rwxr-xr-x | FS/bin/freeside-daily | 9 | ||||
| -rw-r--r-- | httemplate/edit/elements/edit.html | 2 | ||||
| -rw-r--r-- | httemplate/edit/msg_template.html | 7 | ||||
| -rw-r--r-- | httemplate/elements/ckeditor/plugins/blockprotect/plugin.js | 128 | ||||
| -rw-r--r-- | httemplate/elements/htmlarea.html | 24 | ||||
| -rw-r--r-- | httemplate/elements/onload.js | 13 | ||||
| -rwxr-xr-x | httemplate/search/477partIA.html | 6 | ||||
| -rwxr-xr-x | httemplate/search/477partIIA.html | 2 | ||||
| -rwxr-xr-x | httemplate/search/477partIIB.html | 2 | ||||
| -rwxr-xr-x | httemplate/search/477partVI_census.html | 2 | ||||
| -rw-r--r-- | rt/lib/RT/Interface/Web_Vendor.pm | 14 | 
16 files changed, 253 insertions, 229 deletions
| diff --git a/FS/FS/Conf.pm b/FS/FS/Conf.pm index 065fd8c4e..476d490d2 100644 --- a/FS/FS/Conf.pm +++ b/FS/FS/Conf.pm @@ -779,8 +779,8 @@ sub reason_type_options {    {      'key'         => 'alert_expiration', -    'section'     => 'notification', -    'description' => 'Enable alerts about billing method expiration (i.e. expiring credit cards).', +    'section'     => 'deprecated', +    'description' => 'Enable alerts about credit card expiration.  This is obsolete and no longer works.',      'type'        => 'checkbox',      'per_agent'   => 1,    }, @@ -795,7 +795,7 @@ sub reason_type_options {    {      'key'         => 'alerter_msgnum', -    'section'     => 'notification', +    'section'     => 'deprecated',      'description' => 'Template to use for credit card expiration alerts.',      %msg_template_options,    }, diff --git a/FS/FS/Cron/alert_expiration.pm b/FS/FS/Cron/alert_expiration.pm deleted file mode 100644 index 5961e6155..000000000 --- a/FS/FS/Cron/alert_expiration.pm +++ /dev/null @@ -1,190 +0,0 @@ -package FS::Cron::alert_expiration; - -use vars qw( @ISA @EXPORT_OK); -use Exporter; -use FS::Record qw(qsearch qsearchs); -use FS::Conf; -use FS::cust_main; -use FS::Misc; -use Time::Local; -use Date::Parse qw(str2time); - - -@ISA = qw( Exporter ); -@EXPORT_OK = qw( alert_expiration ); - -my $warning_time = 30 * 24 * 60 * 60; -my $urgent_time = 15 * 24 * 60 * 60; -my $panic_time = 5 * 24 * 60 * 60; -my $window_time = 24 * 60 * 60; - -sub alert_expiration { -  my $conf = new FS::Conf; -  my $smtpmachine = $conf->config('smtpmachine'); -   -  my %opt = @_; -  my ($_date) = $opt{'d'} ? str2time($opt{'d'}) : $^T; -  $_date += $opt{'y'} * 86400 if $opt{'y'}; -  my ($sec, $min, $hour, $mday, $mon, $year) = (localtime($_date)) [0..5]; -  $mon++; - -  my $debug = 0; -  $debug = 1 if $opt{'v'}; -  $debug = $opt{'l'} if $opt{'l'}; - -  $FS::cust_main::DEBUG = $debug; - -  # Get a list of customers. -  -  my %limit; -  $limit{'agentnum'} = $opt{'a'} if $opt{'a'}; -  $limit{'payby'}    = $opt{'p'} if $opt{'p'}; - -  my @customers; - -  if(my @custnums = @ARGV) { -    # We're given an explicit list of custnums, so select those.  Then check against  -    # -a and -p to avoid doing anything unexpected. -    foreach (@custnums) { -      my $customer = FS::cust_main->by_key($_); -      if($customer and (!$opt{'a'} or $customer->agentnum == $opt{'a'}) -                   and (!$opt{'p'} or $customer->payby eq $opt{'p'}) ) { -        push @customers, $customer; -      } -    } -  } -  else { # no @ARGV -    @customers = qsearch('cust_main', \%limit); -  } -  return if(!@customers); -  foreach my $customer (@customers) { -    next if !($customer->ncancelled_pkgs); # skip inactive customers -    my $paydate = $customer->paydate; -    next if $paydate =~ /^\s*$/; # skip empty expiration dates -     -    my $custnum = $customer->custnum; -    my $first   = $customer->first; -    my $last    = $customer->last; -    my $company = $customer->company; -    my $payby   = $customer->payby; -    my $payinfo = $customer->payinfo; -    my $daytime = $customer->daytime; -    my $night   = $customer->night; - -    my ($paymonth, $payyear) = $customer->paydate_monthyear; -    $paymonth--; # localtime() convention -    $payday = 1; # This is enforced by FS::cust_main::check. -    my $expire_time; -    if($payby eq 'CARD' || $payby eq 'DCRD') { -      # Credit cards expire at the end of the month/year. -      if($paymonth == 11) { -        $payyear++; -        $paymonth = 0; -      } else { -        $paymonth++; -      } -      $expire_time = timelocal(0,0,0,$payday,$paymonth,$payyear) - 1; -    } -    else { -      $expire_time = timelocal(0,0,0,$payday,$paymonth,$payyear); -    } -     -    if (grep { $expire_time < $_date + $_ && -               $expire_time > $_date + $_ - $window_time }  -               ($warning_time, $urgent_time, $panic_time) ) { -      # Send an expiration notice. -      my $agentnum = $customer->agentnum; -      my $error = ''; - -      my $msgnum = $conf->config('alerter_msgnum', $agentnum); -      if ( $msgnum ) { # new hotness -        my $msg_template = qsearchs('msg_template', { msgnum => $msgnum } ); -        $customer->setfield('expdate', $expire_time); -        $error = $msg_template->send('cust_main' => $customer, -                                     'object'    => $customer); -      } -      else { #!$msgnum, the hard way -        $mail_sender = $conf->config('invoice_from', $agentnum); -        $failure_recipient = $conf->config('invoice_from', $agentnum)  -          || 'postmaster'; -        -        my @alerter_template = $conf->config('alerter_template', $agentnum) -          or die 'cannot load config file alerter_template'; - -        my $alerter = new Text::Template(TYPE   => 'ARRAY', -                                         SOURCE => [  -                                           map "$_\n", @alerter_template -                                           ]) -          or die "can't create Text::Template object: $Text::Template::ERROR"; - -        $alerter->compile() -          or die "can't compile template: $Text::Template::ERROR"; -         -        my @invoicing_list = $customer->invoicing_list; -        my @to_addrs = grep { $_ ne 'POST' } @invoicing_list; -        if(@to_addrs) { -          # Set up template fields. -          my %fill_in; -          $fill_in{$_} = $customer->getfield($_)  -            foreach(qw(first last company)); -          $fill_in{'expdate'} = $expire_time; -          $fill_in{'company_name'} = $conf->config('company_name', $agentnum); -          $fill_in{'company_address'} = -            join("\n",$conf->config('company_address',$agentnum))."\n"; -          if($payby eq 'CARD' || $payby eq 'DCRD') { -            $fill_in{'payby'} = "credit card (". -              substr($customer->payinfo, 0, 2) . "xxxxxxxxxx" . -              substr($payinfo, -4) . ")"; -          } -          elsif($payby eq 'COMP') { -            $fill_in{'payby'} = 'complimentary account'; -          } -          else { -            $fill_in{'payby'} = 'current method'; -          } -          # Send it already! -          $error = FS::Misc::send_email (  -            from    =>  $mail_sender, -            to      =>  [ @to_addrs ], -            subject =>  'Billing Arrangement Expiration', -            body    =>  [ $alerter->fill_in( HASH => \%fill_in ) ], -          ); -      }  -      else { # if(@to_addrs) -        push @{$agent_failure_body{$customer->agentnum}}, -          sprintf(qq{%5d %-32.32s %4s %10s %12s %12s}, -            $custnum, -            $first . " " . $last . "   " . $company, -            $payby, -            $paydate, -            $daytime, -            $night ); -      } -    } # if($msgnum) -     -# should we die here rather than report failure as below? -    die "can't send expiration alert: $error" -      if $error; -     -    } # if(expired) -  } # foreach(@customers) - -  # Failure notification -  foreach my $agentnum (keys %agent_failure_body) { -    $mail_sender = $conf->config('invoice_from', $agentnum) -      if($conf->exists('invoice_from', $agentnum)); -    $failure_recipient = $conf->config('invoice_from', $agentnum) -      if($conf->exists('invoice_from', $agentnum)); -    my $error = FS::Misc::send_email ( -      from    =>  $mail_sender, -      to      =>  $failure_recipient, -      subject =>  'Unnotified Billing Arrangement Expirations', -      body    =>  [ @{$agent_failure_body{$agentnum}} ], -      ); -    die "can't send alerter failure email to $failure_recipient: $error" -      if $error; -  } - -} - -1; diff --git a/FS/FS/cdr.pm b/FS/FS/cdr.pm index e127e8bc5..9d72c39e4 100644 --- a/FS/FS/cdr.pm +++ b/FS/FS/cdr.pm @@ -932,8 +932,12 @@ sub rate_prefix {        }                             #should preserve (display?) this -      my $charge_min = ( $charge_sec - $conn_seconds ) / 60; -      $charge += ($rate_detail->min_charge * $charge_min) if $charge_min > 0; #still not rounded +      if ( $granularity == 0 ) { # per call rate +        $charge += $rate_detail->min_charge; +      } else { +        my $charge_min = ( $charge_sec - $conn_seconds ) / 60; +        $charge += ($rate_detail->min_charge * $charge_min) if $charge_min > 0; #still not rounded +      }      } diff --git a/FS/FS/msg_template.pm b/FS/FS/msg_template.pm index bef2b4b55..4e1f4da24 100644 --- a/FS/FS/msg_template.pm +++ b/FS/FS/msg_template.pm @@ -732,6 +732,64 @@ sub _upgrade_data {          $conf->delete($subject, $agentnum) if $subject;        }      } + +    if ( $conf->exists('alert_expiration', $agentnum) ) { +      my $msgnum = $conf->exists('alerter_msgnum', $agentnum); +      my $template = FS::msg_template->by_key($msgnum) if $msgnum; +      if (!$template) { +        warn "template for alerter_msgnum $msgnum not found\n"; +        next; +      } +      # this is now a set of billing events +      foreach my $days (30, 15, 5) { +        my $event = FS::part_event->new({ +            'agentnum'    => $agentnum, +            'event'       => "Card expiration warning - $days days", +            'eventtable'  => 'cust_main', +            'check_freq'  => '1d', +            'action'      => 'notice', +            'disabled'    => 'Y', #initialize first +        }); +        my $error = $event->insert( 'msgnum' => $msgnum ); +        if ($error) { +          warn "error creating expiration alert event:\n$error\n\n"; +          next; +        } +        # make it work like before: +        # only send each warning once before the card expires, +        # only warn active customers, +        # only warn customers with CARD/DCRD, +        # only warn customers who get email invoices +        my %conds = ( +          'once_every'          => { 'run_delay' => '30d' }, +          'cust_paydate_within' => { 'within' => $days.'d' }, +          'cust_status'         => { 'status' => { 'active' => 1 } }, +          'payby'               => { 'payby'  => { 'CARD' => 1, +                                                   'DCRD' => 1, } +                                   }, +          'message_email'       => {}, +        ); +        foreach (keys %conds) { +          my $condition = FS::part_event_condition->new({ +              'conditionname' => $_, +              'eventpart'     => $event->eventpart, +          }); +          $error = $condition->insert( %{ $conds{$_} }); +          if ( $error ) { +            warn "error creating expiration alert event:\n$error\n\n"; +            next; +          } +        } +        $error = $event->initialize; +        if ( $error ) { +          warn "expiration alert event was created, but not initialized:\n$error\n\n"; +        } +      } # foreach $days +      $conf->delete('alerter_msgnum', $agentnum); +      $conf->delete('alert_expiration', $agentnum); + +    } # if alerter_msgnum +    }    foreach my $msg_template ( qsearch('msg_template', {}) ) {      if ( $msg_template->subject || $msg_template->body ) { diff --git a/FS/FS/part_event/Condition/cust_paydate_within.pm b/FS/FS/part_event/Condition/cust_paydate_within.pm index 4808e9083..bfe3e84d2 100644 --- a/FS/FS/part_event/Condition/cust_paydate_within.pm +++ b/FS/FS/part_event/Condition/cust_paydate_within.pm @@ -23,7 +23,7 @@ sub eventtable_hashref {  sub option_fields {    ( -    'within'  =>  { 'label'   => 'Expiration date within', +    'within'  =>  { 'label'   => 'Credit card will expire within',                      'type'    => 'freq',                    },    ); @@ -32,14 +32,17 @@ sub option_fields {  sub condition {    my( $self, $cust_main, %opt ) = @_;    my $expire_time = $cust_main->paydate_epoch or return 0; -  $opt{'time'} >= $self->option_age_from('within', $expire_time); +  $opt{'time'} >= $self->option_age_from('within', $expire_time) and +  $opt{'time'} <  $expire_time;  }  sub condition_sql {    my ($self, $table, %opt) = @_;    my $expire_time = FS::cust_main->paydate_epoch_sql or return 'true'; -  $opt{'time'} . ' >= ' .   -    $self->condition_sql_option_age_from('within', $expire_time); +  ' ( '. $opt{'time'} . ' >= ' .   +    $self->condition_sql_option_age_from('within', $expire_time) . +    ' AND ' . $opt{'time'} . ' < ' . $expire_time . ' ) '; +  }  1; diff --git a/FS/bin/freeside-daily b/FS/bin/freeside-daily index b6ee5188e..14d797f49 100755 --- a/FS/bin/freeside-daily +++ b/FS/bin/freeside-daily @@ -38,10 +38,13 @@ upload(%opt);  use FS::Cron::set_lata_have_usage qw(set_lata_have_usage);  set_lata_have_usage(%opt); -# Send alerts about upcoming credit card expiration. -use FS::Cron::alert_expiration qw(alert_expiration); +# we used to send alerts about upcoming credit card expiration here  my $conf = new FS::Conf; -alert_expiration(%opt) if($conf->exists('alert_expiration')); +if($conf->exists('alert_expiration')) { +  warn "WARNING: the alert_expiration option is obsolete.  If you ran  +  freeside-upgrade, it should have configured credit card expiration alerts  +  as billing events.\n"; +}  #what to do about the below when using -m?  that is the question. diff --git a/httemplate/edit/elements/edit.html b/httemplate/edit/elements/edit.html index 08408297b..ed677d7ab 100644 --- a/httemplate/edit/elements/edit.html +++ b/httemplate/edit/elements/edit.html @@ -328,7 +328,7 @@ Example:  %     qw( hashref agent_virt agent_null agent_null_right ),#*-table  %     qw( formatted_value ),                               #fixed  %     qw( country ),                                       #select-country -%     qw( width height ),                                  #htmlarea +%     qw( width height config ),                           #htmlarea  %     qw( alt_format ),                                    #select-cust_location  %     qw( classnum ),                                   # select-inventory_item  %   ; diff --git a/httemplate/edit/msg_template.html b/httemplate/edit/msg_template.html index 7205ba844..06cac440e 100644 --- a/httemplate/edit/msg_template.html +++ b/httemplate/edit/msg_template.html @@ -67,7 +67,8 @@ if ( $curuser->access_right('Edit global templates')        { field => 'subject',   size=>60, },        { field => 'body',          type  => 'htmlarea', -        width => 763 +        width => 763, +        config=> { extraPlugins => 'blockprotect' },        },    ;  } else { #readonly @@ -328,8 +329,8 @@ my $widget = new HTML::Widgets::SelectLayers(  my $sidebar = '  <SCRIPT TYPE="text/javascript">  function insertHtml(what) { -  var oEditor = FCKeditorAPI.GetInstance("body"); -  oEditor.InsertHtml(what); +  var oEditor = CKEDITOR.instances["body"]; +  oEditor.insertHtml(what);  };  function areyousure(url, message) { diff --git a/httemplate/elements/ckeditor/plugins/blockprotect/plugin.js b/httemplate/elements/ckeditor/plugins/blockprotect/plugin.js new file mode 100644 index 000000000..61c993a9f --- /dev/null +++ b/httemplate/elements/ckeditor/plugins/blockprotect/plugin.js @@ -0,0 +1,128 @@ +/** + * The "blockprotect" plugin.  Adapted from the "placeholder" plugin. + */ + +(function() { +        var delim_o = '{'; +        var delim_c = '}'; + +        var create_block = function(content) { +          // fix nbsp's +          content = content.replace(/ /gi, ' '); +          // escape the content +          var el = document.createElement('SPAN'); +          // IE8 compat +          if( typeof(el.textContent) != 'undefined' ) { +            el.textContent = content; +          } else if( typeof(el.innerText) != 'undefined' ) { +            el.innerText = content; +          } +          el.setAttribute('class', 'cke_blockprotect'); +          return el.outerHTML; +        }; +        var block_writeHtml = function( element ) { +          // to unescape the element contents, write it out as HTML, +          // stick that into a SPAN element, and then extract the text +          // content of that. +          var inner_writer = new CKEDITOR.htmlParser.basicWriter; +          element.writeChildrenHtml(inner_writer); + +          var el = document.createElement('SPAN'); +          el.innerHTML = inner_writer.getHtml(); +          if( typeof(el.textContent) != 'undefined' ) { +            return el.textContent; +          } else if( typeof(el.innerText) != 'undefined' ) { +            return el.innerText; +          } +        }; +        var to_protected_html = function(data) { +          var depth = 0; +          var chunk = ''; +          var out = ''; +          var p = 0; // position in the string +          while( 1 ) { +            // find the next delimiter of either kind +            var i = data.indexOf(delim_o, p); +            var j = data.indexOf(delim_c, p); +            if (i == -1 && j == -1) { +              // then there are no more delimiters +              break; +            } else if ((i < j || j == -1) && i != -1) { +              // next delimiter is an open +              // push everything from current position to  +              // the delimiter +              if ( i > p ) chunk += data.substr(p, i - p); +              p = i + 1; +              if ( depth == 0 ) { +                // start of a protected block +                out += chunk; +                chunk = ''; +              } +              chunk += delim_o; +              depth++; +            } else if ((j < i || i == -1) && j != -1) { +              // next delimiter is a close +              if ( j > p ) chunk += data.substr(p, j - p); +              p = j + 1; +              depth--; +              chunk += delim_c +                if ( depth == 0 ) { +                  // end of a protected block +                  out += create_block(chunk); +                  chunk = ''; +                } else if ( depth < 0 ) { +                  depth = 0; +                } +            } else { +              // can't happen +            } +          } +          // append any text after the last delimiter +          if ( depth ) { +            out += create_block(data.substr(p)); +          } else { +            out += data.substr(p); +          } +          return out; +        }; + +	CKEDITOR.plugins.add( 'blockprotect', { +		afterInit: function( editor ) { +			CKEDITOR.addCss( '.cke_blockprotect' + +			'{' + +					'background-color: #ffff88;' + +					( CKEDITOR.env.gecko ? 'cursor: default;' : '' ) + +				'}' +                        ); +                       +                        // keep these from getting stripped out  +                        editor.filter.allow('span(cke_blockprotect)', +                                            'blockprotect', true); + +                        // add filter at the front of toHtml +                        editor.on( 'toHtml', +                          function( evt ) { +                            evt.data.dataValue = +                              to_protected_html(evt.data.dataValue); +                            return evt; +                          }, +                          this, null, 0 +                        ); + +                        editor.dataProcessor.htmlFilter.addRules({ +                          elements: { +                            span: function( element ) { +                              if ( element.className = 'cke_blockprotect' ) { +                                // defeat HTML escaping +                                var content = block_writeHtml(element); +                                element.writeHtml = function(writer, filter) { +                                  writer.text(content); +                                } +                              } +                            } // span function +                          } // elements +                        }); +                } +        }); // plugins.add +}) (); + diff --git a/httemplate/elements/htmlarea.html b/httemplate/elements/htmlarea.html index f9dcffd3f..c98993d40 100644 --- a/httemplate/elements/htmlarea.html +++ b/httemplate/elements/htmlarea.html @@ -6,6 +6,7 @@ Example:              'field'      => 'fieldname',              'curr_value' => $curr_value,              'height'     => 800, +            'config'     => { extraPlugins => 'blockprotect' },           );  </%doc> @@ -19,21 +20,24 @@ Example:  <SCRIPT TYPE="text/javascript"> -  CKEDITOR.replace('<% $opt{'field'} %>', { -%   if ( $opt{'width'} ) { -      width: <% $opt{'width'} %>, -%   } -    height: <% $opt{'height'} || 420 %>, -    startupFocus: true, -    toolbarCanCollapse: true, -    basePath: '<% $p %>elements/ckeditor/', -    enterMode: 2 -  }); +  CKEDITOR.replace('<% $opt{'field'} %>', +                   <% encode_json($config) %> +  );  </SCRIPT>  <%init>  my %opt = @_; +my $config = { +  'height'              => ($opt{height} || 420), +  'startupFocus'        => JSON::true, +  'skin'                => 'kama', +  'toolbarCanCollapse'  => JSON::true, +  'basePath'            => $p.'elements/ckeditor/', +  'enterMode'           => 2, +  %{ $opt{config} || {} }, +}; +$config->{width} = $opt{width} if defined($opt{width});  </%init> diff --git a/httemplate/elements/onload.js b/httemplate/elements/onload.js index bfa7eef94..c7bbbb283 100644 --- a/httemplate/elements/onload.js +++ b/httemplate/elements/onload.js @@ -12,11 +12,12 @@ Usage:  </%doc>  (function() { -  var tmp = window.onload; -  window.onload = function() { -    if (typeof(tmp)== 'function') { -      tmp(); -    } +  var myonload = function() {  <% $m->content %> -  }; +  } +  if ( window.addEventListener ) { +    window.addEventListener('load', myonload); +  } else if ( window.attachEvent ) { +    window.attachEvent('onload', myonload); +  }  })(); diff --git a/httemplate/search/477partIA.html b/httemplate/search/477partIA.html index 1cd0b70e0..5ee44dad5 100755 --- a/httemplate/search/477partIA.html +++ b/httemplate/search/477partIA.html @@ -139,7 +139,7 @@ for ( my $row = 0; $row < scalar @upload_option; $row++ ) {      my $count = FS::Record->scalar_sql($this_count_query);      my $residential = FS::Record->scalar_sql($this_count_query . $is_residential); -    my $percent = sprintf('%.2f', $count ? 100 * $residential / $count : 0); +    my $percent = sprintf('%.3f', $count ? 100 * $residential / $count : 0);      $data[$col][$row] = [ $count, $percent ];      $total_count += $count; @@ -149,10 +149,10 @@ for ( my $row = 0; $row < scalar @upload_option; $row++ ) {  }  my $total_percentage = -  sprintf("%.2f", $total_count ? 100*$total_residential/$total_count : 0); +  sprintf("%.3f", $total_count ? 100*$total_residential/$total_count : 0);  my $above_200_percentage = -  sprintf("%.2f", $total_count ? 100*$above_200/$total_count : 0); +  sprintf("%.3f", $total_count ? 100*$above_200/$total_count : 0);  my @summary_row = (    $total_count, diff --git a/httemplate/search/477partIIA.html b/httemplate/search/477partIIA.html index 95c00a3e0..907a176e5 100755 --- a/httemplate/search/477partIIA.html +++ b/httemplate/search/477partIIA.html @@ -104,7 +104,7 @@ if ( $total_lines > 0 ) {    foreach (@row_conds) {      my $sql = $query_ds0 . $_;      my $lines = FS::Record->scalar_sql($sql); -    my $percent = sprintf('%.2f', 100 * $lines / $total_lines); +    my $percent = sprintf('%.3f', 100 * $lines / $total_lines);      push @{ $data[0] }, $percent;    }  } diff --git a/httemplate/search/477partIIB.html b/httemplate/search/477partIIB.html index 5b9b30769..cb181f4fd 100755 --- a/httemplate/search/477partIIB.html +++ b/httemplate/search/477partIIB.html @@ -120,7 +120,7 @@ foreach (@col_conds) {        if ( $col_data[0] == 0 ) {          $col_data[$row] = ''; # show nothing in this row, then        } else { -        $col_data[$row] = sprintf('%.2f', 100 * $count / $col_data[0]) . '%'; +        $col_data[$row] = sprintf('%.3f', 100 * $count / $col_data[0]) . '%';        }      } #if $row == 0      $row++; diff --git a/httemplate/search/477partVI_census.html b/httemplate/search/477partVI_census.html index 59a6fb50d..0dafc6b21 100755 --- a/httemplate/search/477partVI_census.html +++ b/httemplate/search/477partVI_census.html @@ -75,7 +75,7 @@ push @fields,          $state_pkgcount{$state} += $row->quantity;          $row->quantity;        }, -  sub { my $row = shift; sprintf "%.2f", $row->residential }, +  sub { my $row = shift; sprintf "%.3f", $row->residential },  ;  my %search_hash = (); diff --git a/rt/lib/RT/Interface/Web_Vendor.pm b/rt/lib/RT/Interface/Web_Vendor.pm index 0c061e2de..3aad3fee2 100644 --- a/rt/lib/RT/Interface/Web_Vendor.pm +++ b/rt/lib/RT/Interface/Web_Vendor.pm @@ -264,7 +264,12 @@ sub ProcessTicketBasics {        my $DateObj = RT::Date->new($session{'CurrentUser'});        if ( $to_date ) {            $DateObj->Set(Format => 'unknown', Value => $to_date); -          $ARGSRef->{'WillResolve'} = $DateObj->ISO; +          if ( $DateObj->Unix > time ) { +            $ARGSRef->{'WillResolve'} = $DateObj->ISO; +          } else { +            warn "Ticket ".$TicketObj->Id.": WillResolve date '$to_date' not accepted.\n"; +            # and then don't set it in ARGSRef +          }        } elsif ( $TicketObj and $TicketObj->WillResolveObj->Unix > 0 ) {            $DateObj->Set(Value => 0);            $ARGSRef->{'WillResolve'} = $DateObj->ISO; @@ -343,6 +348,13 @@ sub ProcessTicketDates {              Value  => $ARGSRef->{ $field . '_Date' }          ); +        if ( $field eq 'WillResolve' +              and $DateObj->Unix > 0  +              and $DateObj->Unix <= time ) { +            push @results, "Can't set WillResolve date in the past."; +            next; +        } +          my $obj = $field . "Obj";          if (    ( defined $DateObj->Unix )              and ( $DateObj->Unix != $Ticket->$obj()->Unix() ) ) | 
