From 41967a7cac39ce2156b9b86436ade82f9a99104e Mon Sep 17 00:00:00 2001 From: Mark Wells Date: Tue, 12 Jun 2012 16:34:18 -0700 Subject: [PATCH] suppress all fractions of days in unearned revenue calculation, #13289 --- FS/FS/Mason.pm | 1 + FS/FS/Record.pm | 21 ++++++++++++++++++++- httemplate/search/prepaid_income.html | 20 +++++++++++--------- httemplate/search/unearned_detail.html | 25 ++++++++++++++++++------- 4 files changed, 50 insertions(+), 17 deletions(-) diff --git a/FS/FS/Mason.pm b/FS/FS/Mason.pm index de97a1915..b0f20ec65 100644 --- a/FS/FS/Mason.pm +++ b/FS/FS/Mason.pm @@ -122,6 +122,7 @@ if ( -e $addl_handler_use_file ) { use FS::UID qw( getotaker dbh datasrc driver_name ); use FS::Record qw( qsearch qsearchs fields dbdef str2time_sql str2time_sql_closing + midnight_sql ); use FS::Conf; use FS::CGI qw(header menubar table itable ntable idiot diff --git a/FS/FS/Record.pm b/FS/FS/Record.pm index dfc2abfc4..a93a10ac6 100644 --- a/FS/FS/Record.pm +++ b/FS/FS/Record.pm @@ -39,6 +39,7 @@ use Tie::IxHash; @EXPORT_OK = qw( dbh fields hfields qsearch qsearchs dbdef jsearch str2time_sql str2time_sql_closing regexp_sql not_regexp_sql concat_sql + midnight_sql ); $DEBUG = 0; @@ -3030,7 +3031,7 @@ sub not_regexp_sql { =item concat_sql [ DRIVER_NAME ] ITEMS_ARRAYREF -Returns the items concatendated based on database type, using "CONCAT()" for +Returns the items concatenated based on database type, using "CONCAT()" for mysql and " || " for Pg and other databases. You can pass an optional driver name such as "Pg", "mysql" or @@ -3051,6 +3052,24 @@ sub concat_sql { } +=item midnight_sql DATE + +Returns an SQL expression to convert DATE (a unix timestamp) to midnight +on that day in the system timezone, using the default driver name. + +=cut + +sub midnight_sql { + my $driver = driver_name; + my $expr = shift; + if ( $driver =~ /^mysql/i ) { + "UNIX_TIMESTAMP(DATE(FROM_UNIXTIME($expr)))"; + } + else { + "EXTRACT( EPOCH FROM DATE(TO_TIMESTAMP($expr)) )"; + } +} + =back =head1 BUGS diff --git a/httemplate/search/prepaid_income.html b/httemplate/search/prepaid_income.html index d02345edc..ebac5a2a9 100644 --- a/httemplate/search/prepaid_income.html +++ b/httemplate/search/prepaid_income.html @@ -56,7 +56,7 @@
<% $actual_label %><% $actual_label ? 'u' : 'U' %>nearned revenue -is the amount of unearned revenue +is the as-yet-unearned portion of revenue <% $actual_label ? 'Freeside has actually' : '' %> invoiced for packages with <% $cgi->param('include_monthly') ? 'terms extending into the future.' @@ -148,17 +148,20 @@ foreach my $agentnum (@agentnums) { } elsif ( $mode eq 'paid' ) { $unearned_base = FS::cust_bill_pkg->paid_sql(@opt); } - my $period = "CAST(cust_bill_pkg.edate - cust_bill_pkg.sdate AS REAL)"; - my $elapsed = "GREATEST( $now - cust_bill_pkg.sdate, 0 )"; - my $remaining = "(1 - $elapsed/$period)"; - + + my $edate_zero = midnight_sql('edate'); + my $sdate_zero = midnight_sql('sdate'); + my $period = "CAST( ($edate_zero - $sdate_zero) / 86400.0 AS DECIMAL(10,0) )"; + my $remaining = "GREATEST( + CAST( ($edate_zero - $now) / 86400.0 AS DECIMAL(10,0) ), + 0)"; + my $fraction = "$remaining / $period"; + my $unearned_sql = "CAST( - GREATEST( $unearned_base * $remaining, 0 ) + GREATEST( $unearned_base * $fraction, 0 ) AS DECIMAL(10,2) )"; - my $float = 'REAL'; #'DOUBLE PRECISION'; - my $select = "SUM( $unearned_sql )"; if ( !$cgi->param('include_monthly') ) { @@ -186,7 +189,6 @@ foreach my $agentnum (@agentnums) { $where "; - warn $sql; my $sth = dbh->prepare($sql) or die dbh->errstr; $sth->execute or die $sth->errstr; my $total = $sth->fetchrow_arrayref->[0]; diff --git a/httemplate/search/unearned_detail.html b/httemplate/search/unearned_detail.html index 831625e7f..02d514cbe 100644 --- a/httemplate/search/unearned_detail.html +++ b/httemplate/search/unearned_detail.html @@ -1,5 +1,6 @@ <& elements/search.html, - 'title' => emt("Unearned revenue - ".ucfirst($unearned_mode)), + 'title' => emt("Unearned revenue - ".ucfirst($unearned_mode)) . ' (' . + time2str('%b %d %Y', $unearned) . ')', 'name' => emt('line items'), 'query' => $query, 'count_query' => $count_query, @@ -41,8 +42,8 @@ date_sub('last_credit'), #Charge start date_sub('sdate'), - #Charge end - date_sub('edate'), + #Charge end, minus most of a day + date_sub('before_edate'), #Invoice 'invnum', #Invoice date @@ -167,11 +168,19 @@ else { # whatever we're using as the base, only show rows where it's positive push @where, "$unearned_base > 0"; -my $period = "CAST(cust_bill_pkg.edate - cust_bill_pkg.sdate AS REAL)"; -my $elapsed = "GREATEST( $unearned - cust_bill_pkg.sdate, 0 )"; -my $remaining = "(1 - $elapsed/$period)"; +my $edate_zero = midnight_sql('edate'); +my $sdate_zero = midnight_sql('sdate'); +# $unearned is one second before midnight on the date requested for the report. -$unearned_sql = "CAST( $unearned_base * $remaining AS DECIMAL(10,2) )"; +# suppress partial days for more accounting-like behavior +my $period = "CAST( ($edate_zero - $sdate_zero) / 86400.0 AS DECIMAL(10,0) )"; + +my $remaining = "GREATEST( + CAST( ($edate_zero - $unearned) / 86400.0 AS DECIMAL(10,0) ), + 0)"; +my $fraction = "$remaining / $period"; + +$unearned_sql = "CAST( $unearned_base * $fraction AS DECIMAL(10,2) )"; push @select, "$unearned_sql AS unearned_revenue"; # last payment/credit date @@ -187,6 +196,8 @@ foreach my $x (qw(pay credit)) { push @select, "($last_date_sql) AS last_$x"; } +push @select, '(edate - 82799) AS before_edate'; + #no itemdesc #no tax report group kludge #no tax exemption -- 2.11.0