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() ) ) |