summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMitch Jackson <mitch@freeside.biz>2018-09-17 21:30:15 -0400
committerMitch Jackson <mitch@freeside.biz>2018-09-19 12:06:06 -0400
commitfa32644857a444c489ff0d7389b0d6d61790b229 (patch)
tree024521c0304b8f5ec7be4e560e00f17df7a596be
parentd727ecee34916db4c21ae09d258faa243ab7faa9 (diff)
RT# 78547 Future autobill report - report runs in job queue
-rw-r--r--FS/FS/Report/Queued/FutureAutobill.pm132
-rw-r--r--FS/FS/UI/Web.pm1
-rw-r--r--httemplate/search/elements/grid-report.html14
-rw-r--r--httemplate/search/future_autobill.html47
-rw-r--r--httemplate/search/report_future_autobill-queued_job.html11
-rw-r--r--httemplate/search/report_future_autobill.html60
6 files changed, 238 insertions, 27 deletions
diff --git a/FS/FS/Report/Queued/FutureAutobill.pm b/FS/FS/Report/Queued/FutureAutobill.pm
new file mode 100644
index 0000000..82c9021
--- /dev/null
+++ b/FS/FS/Report/Queued/FutureAutobill.pm
@@ -0,0 +1,132 @@
+package FS::Report::Queued::FutureAutobill;
+use strict;
+use warnings;
+use vars qw( $job );
+
+use FS::Conf;
+use FS::cust_main;
+use FS::cust_main::Location;
+use FS::cust_payby;
+use FS::CurrentUser;
+use FS::Log;
+use FS::Mason qw(mason_interps);
+use FS::Record qw( qsearch );
+use FS::UI::Web;
+use FS::UID qw( dbh );
+
+use DateTime;
+use File::Temp;
+use Data::Dumper;
+use HTML::Entities qw( encode_entities );
+
+=head1 NAME
+
+FS::Report::Queued::FutureAutobill - Future Auto-Bill Transactions Report
+
+=head1 DESCRIPTION
+
+Future Autobill report generated within the job queue.
+
+Report results are saved to temp storage as a Mason fragment
+that is rendered by the queued report viewer.
+
+For every customer with a valid auto-bill payment method,
+report runs bill_and_collect() for each day, from today through
+the report target date. After recording the results, all
+operations are rolled back.
+
+This report relies on the ability to safely run bill_and_collect(),
+with all exports and messaging disabled, and then to roll back the
+results.
+
+=head1 PARAMETERS
+
+C<agentnum>, C<target_date>
+
+=cut
+
+sub make_report {
+ $job = shift;
+ my $param = shift;
+ my $outbuf;
+ my $DEBUG = 0;
+
+ my $time_begin = time();
+
+ my $report_fh = File::Temp->new(
+ TEMPLATE => 'report.future_autobill.XXXXXXXX',
+ DIR => sprintf( '%s/cache.%s', $FS::Conf::base_dir, $FS::UID::datasrc ),
+ UNLINK => 0
+ ) or die "Cannot create report file: $!";
+
+ if ( $DEBUG ) {
+ warn Dumper( $job );
+ warn Dumper( $param );
+ warn $report_fh;
+ warn $report_fh->filename;
+ }
+
+ my $curuser = FS::CurrentUser->load_user( $param->{CurrentUser} )
+ or die 'Unable to set report user';
+
+ my ( $fs_interp ) = FS::Mason::mason_interps(
+ 'standalone',
+ outbuf => \$outbuf,
+ );
+ $fs_interp->error_mode('fatal');
+ $fs_interp->error_format('text');
+
+ $FS::Mason::Request::QUERY_STRING = sprintf(
+ 'target_date=%s&agentnum=%s',
+ encode_entities( $param->{target_date} ),
+ encode_entities( $param->{agentnum} || '' ),
+ );
+ $FS::Mason::Request::FSURL = $param->{RootURL};
+
+ my $mason_request = $fs_interp->make_request(
+ comp => '/search/future_autobill.html'
+ );
+
+ {
+ local $@;
+ eval{ $mason_request->exec() };
+ if ( $@ ) {
+ my $error = ref $@ eq 'HTML::Mason::Exception' ? $@->error : $@;
+
+ my $log = FS::Log->new('FS::Report::Queued::FutureAutobill');
+ $log->error(
+ "Error generating report: $FS::Mason::Request::QUERY_STRING $error"
+ );
+ die $error;
+ }
+ }
+
+ my $report_fn;
+ if ( $report_fh->filename =~ /report\.(future_autobill.+)$/ ) {
+ $report_fn = $1
+ } else {
+ die 'Error parsing report filename '.$report_fh->filename;
+ }
+
+ my $report_title = FS::cust_payby->future_autobill_report_title();
+ my $time_rendered = time() - $time_begin;
+
+ if ( $DEBUG ) {
+ warn "Generated content:\n";
+ warn $outbuf;
+ warn $report_fn;
+ warn $report_title;
+ }
+
+ print $report_fh qq{<% include("/elements/header.html", '$report_title') %>\n};
+ print $report_fh $outbuf;
+ print $report_fh qq{<!-- Time to render report $time_rendered seconds -->};
+ print $report_fh qq{<% include("/elements/footer.html") %>\n};
+
+ die sprintf
+ "<a href=%s/misc/queued_report.html?report=%s>view</a>\n",
+ $param->{RootURL},
+ $report_fn;
+}
+
+1;
diff --git a/FS/FS/UI/Web.pm b/FS/FS/UI/Web.pm
index 6cc04b9..5412868 100644
--- a/FS/FS/UI/Web.pm
+++ b/FS/FS/UI/Web.pm
@@ -743,6 +743,7 @@ use FS::CurrentUser;
use FS::Record qw(qsearchs);
use FS::queue;
use FS::CGI qw(rooturl);
+use FS::Report::Queued::FutureAutobill;
$DEBUG = 0;
diff --git a/httemplate/search/elements/grid-report.html b/httemplate/search/elements/grid-report.html
index b1e5430..efc0097 100644
--- a/httemplate/search/elements/grid-report.html
+++ b/httemplate/search/elements/grid-report.html
@@ -141,13 +141,17 @@ Usage:
$m->print($output);
</%perl>
% } else {
+% unless ( $suppress_header ) {
<& /elements/header.html, $title &>
+% }
<% $head %>
% my $myself = $cgi->self_url;
+% unless ( $suppress_header ) {
<P ALIGN="right" CLASS="noprint">
Download full reports<BR>
as <A HREF="<% "$myself;_type=xls" %>">Excel spreadsheet</A><BR>
</P>
+% }
<style type="text/css">
.report * {
background-color: #f8f8f8;
@@ -169,8 +173,10 @@ as <A HREF="<% "$myself;_type=xls" %>">Excel spreadsheet</A><BR>
% next if !ref($cell); # placeholders
% my $td = $cell->{header} ? 'th' : 'td';
% my $style = '';
-% $style .= " rowspan=".$cell->{rowspan} if $cell->{rowspan} > 1;
-% $style .= " colspan=".$cell->{colspan} if $cell->{colspan} > 1;
+% $style .= " rowspan=".$cell->{rowspan}
+% if exists $cell->{rowspan} && $cell->{rowspan} > 1;
+% $style .= " colspan=".$cell->{colspan}
+% if exists $cell->{colspan} && $cell->{colspan} > 1;
% $style .= ' class="' . $cell->{class} . '"' if $cell->{class};
% if ($cell->{bypass_filter}) {
<<%$td%><%$style%>><% $cell->{value} %></<%$td%>>
@@ -182,8 +188,10 @@ as <A HREF="<% "$myself;_type=xls" %>">Excel spreadsheet</A><BR>
% }
</table>
<% $foot %>
+% unless ( $suppress_footer ) {
<& /elements/footer.html &>
% }
+% }
<%args>
$title
@rows
@@ -192,4 +200,6 @@ $head => ''
$foot => ''
$table_width => "100%"
$table_class => "report"
+$suppress_header => undef
+$suppress_footer => undef
</%args>
diff --git a/httemplate/search/future_autobill.html b/httemplate/search/future_autobill.html
index d4ad8e5..1f3862f 100644
--- a/httemplate/search/future_autobill.html
+++ b/httemplate/search/future_autobill.html
@@ -11,7 +11,12 @@ This report relies on the ability to safely run bill_and_collect(),
with all exports and messaging disabled, and then to roll back the
results.
+This report takes time. If 200 customers have automatic
+payment methods, and requester is looking one week ahead,
+there will be 1,400 billing and payment cycles simulated
+
</%doc>
+<h4><% $report_subtitle %></h4>
<& elements/grid-report.html,
title => $report_title,
rows => \@rows,
@@ -27,17 +32,25 @@ results.
td.gridreport { margin: 0 .2em; padding: 0 .4em; }
</style>
',
+ suppress_header => $job ? 1 : 0,
+ suppress_footer => $job ? 1 : 0,
&>
<%init>
+ use DateTime;
+ use FS::Misc::Savepoint;
+ use FS::Report::Queued::FutureAutobill;
use FS::UID qw( dbh );
die "access denied"
unless $FS::CurrentUser::CurrentUser->access_right('Financial reports');
+ my $job = $FS::Report::Queued::FutureAutobill::job;
+
+ $job->update_statustext('0,Finding customers') if $job;
+
my $DEBUG = $cgi->param('DEBUG') || 0;
- my $report_title = FS::cust_payby->future_autobill_report_title;
my $agentnum = $cgi->param('agentnum')
if $cgi->param('agentnum') =~ /^\d+/;
@@ -60,16 +73,20 @@ results.
# Get target date from form
if ($cgi->param('target_date')) {
+ # DateTime::Format::DateParse would be better
my ($mm, $dd, $yy) = split /[\-\/]/,$cgi->param('target_date');
+ ( $yy, $mm, $dd ) = ( $mm, $dd, $yy ) if $mm > 1900;
+
$target_dt = DateTime->new(
month => $mm,
day => $dd,
year => $yy,
%noon,
- ) if $mm && $dd & $yy;
+ ) if $mm && $dd && $yy;
# Catch a date from the past: time only travels in one direction
- $target_dt = undef if $target_dt->epoch < $now_dt->epoch;
+ $target_dt = undef
+ unless $target_dt && $now_dt && $now_dt <= $target_dt;
}
# without a target date, default to tomorrow
@@ -77,6 +94,13 @@ results.
$target_dt = $now_dt->clone->add( days => 1 );
}
+ my $report_title = FS::cust_payby->future_autobill_report_title;
+ my $report_subtitle = sprintf(
+ '(%s through %s)',
+ $now_dt->mdy('/'),
+ $target_dt->mdy('/'),
+ );
+
# Create a range of dates from today until the given report date
# (leaving the probably useless 'quick-report' mode, but disabled)
if ( 1 || $cgi->param('multiple_billing_dates')) {
@@ -104,6 +128,9 @@ results.
. ($agentnum ? "AND cust_main.agentnum = $agentnum" : ''),
});
+ my $completion_target = scalar(keys %cust_payby) * scalar( @target_dates );
+ my $completion_progress = 0;
+
my $fakebill_time = time();
my %abreport;
my @rows;
@@ -125,6 +152,9 @@ results.
local $FS::cust_main::Billing_Realtime::BOP_TESTING = 1;
local $FS::cust_main::Billing_Realtime::BOP_TESTING_SUCCESS = 1;
+ my $savepoint_label = 'future_autobill';
+ savepoint_create( $savepoint_label );
+
warn sprintf "Report involves %s customers", scalar keys %cust_payby
if $DEBUG;
@@ -153,8 +183,18 @@ results.
);
warn "!!! $error (simulating future billing)\n" if $error;
+
+ my $statustext = sprintf(
+ '%s,Simulating upcoming invoices and payments',
+ int( ( ++$completion_progress / $completion_target ) * 100 )
+ );
+ $job->update_statustext( $statustext ) if $job;
+ warn "[ $completion_progress / $completion_target ] $statustext\n"
+ if $DEBUG;
+
}
+
# Generate report rows from recorded payments in cust_pay
for my $cust_pay (
qsearch( cust_pay => {
@@ -206,6 +246,7 @@ results.
# locked at a time
warn "-- custnum $custnum -- rollback()\n" if $DEBUG;
+ savepoint_rollback( $savepoint_label );
dbh->rollback if $oldAutoCommit;
} # /foreach $custnum
diff --git a/httemplate/search/report_future_autobill-queued_job.html b/httemplate/search/report_future_autobill-queued_job.html
new file mode 100644
index 0000000..d23efb5
--- /dev/null
+++ b/httemplate/search/report_future_autobill-queued_job.html
@@ -0,0 +1,11 @@
+<% $server->process %>
+<%init>
+
+die "access denied"
+ unless $FS::CurrentUser::CurrentUser->access_right('Financial reports');
+
+my $server = new FS::UI::Web::JSRPC
+ 'FS::Report::Queued::FutureAutobill::make_report',
+ $cgi;
+
+</%init>
diff --git a/httemplate/search/report_future_autobill.html b/httemplate/search/report_future_autobill.html
index ccde299..28f589e 100644
--- a/httemplate/search/report_future_autobill.html
+++ b/httemplate/search/report_future_autobill.html
@@ -1,6 +1,9 @@
<%doc>
-Display date selector for the future_autobill.html report
+Display pre-report page for the Future Auto Bill Transactions report
+
+Report runs in the queue. Once the report is generated, user is
+redirected to the report results.
</%doc>
<% include('/elements/header.html', $report_title ) %>
@@ -14,30 +17,43 @@ Display date selector for the future_autobill.html report
% } else {
- <FORM ACTION="future_autobill.html" METHOD="GET">
- <TABLE>
- <& /elements/tr-input-date-field.html,
- {
- name => 'target_date',
- value => $target_date,
- label => emt('Target billing date').': ',
- required => 1
- }
- &>
-
- <% include('/elements/tr-select-agent.html',
- 'label' => 'For agent: ',
- 'disable_empty' => 0,
+ <FORM NAME="future_autobill" ID="future_autobill">
+ <TABLE>
+ <& /elements/tr-input-date-field.html,
+ {
+ name => 'target_date',
+ value => $target_date,
+ label => emt('Target billing date').': ',
+ required => 1
+ }
+ &>
+
+ <% include('/elements/tr-select-agent.html',
+ 'label' => 'For agent: ',
+ 'disable_empty' => 0,
+ )
+ %>
+ </TABLE>
+ <BR>
+
+ <INPUT ID="future_autobill_submit" TYPE="submit" VALUE="<% mt('Get Report') |h %>">
+ </FORM>
+
+ <% include( '/elements/progress-init.html',
+ 'future_autobill',
+ [ qw( agentnum target_date ) ],
+ 'report_future_autobill-queued_job.html',
)
%>
- </TABLE>
-
- <BR>
-
- <INPUT TYPE="submit" VALUE="<% mt('Get Report') |h %>">
-
- </FORM>
+ <script type="text/javascript">
+ $('#future_autobill').submit( function( event ) {
+ $('#future_autobill').prop( 'disabled', true );
+ $('#future_autobill_submit').prop( 'disabled', true );
+ event.preventDefault();
+ process();
+ });
+ </script>
% }