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,
td.gridreport { margin: 0 .2em; padding: 0 .4em; }
</style>
',
+ suppress_header => $job ? 1 : 0,
+ suppress_footer => $job ? 1 : 0,
&>
+% if ( %pmt_type_subtotal ) {
+ <table class="gridreport" style="margin-left: 2em;">
+ <tr>
+ <th class="gridreport" colspan="2">
+ Summary
+ </th>
+ </tr>
+% for my $pmt_type ( sort keys %pmt_type_subtotal ) {
+ <tr class="gridreport">
+ <td class="gridreport" style="text-align: right; margin-right: 1em;">
+ <% sprintf '$%.2f', $pmt_type_subtotal{ $pmt_type } %>
+ </td>
+ <td class="gridreport">
+ <% $pmt_type |h %>
+ </td>
+ </tr>
+% }
+% if ( keys %pmt_type_subtotal > 1 ) {
+% $pmt_type_subtotal{Total} += $_ for values %pmt_type_subtotal;
+ <tr class="gridreport" style="border-top: solid 1px #999;">
+ <td class="gridreport" style="text-align: right; margin-right: 1em; border-top: solid 1px #666;">
+ <% sprintf( '$%.2f', $pmt_type_subtotal{Total} ) %>
+ </td>
+ <td class="gridreport" style="border-top: solid 1px #666;">
+ Total
+ </td>
+ </tr>
+ </table>
+% }
+% }
<%init>
- use FS::UID qw( dbh myconnect );
+ 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 $agentnum = $cgi->param('agentnum')
+ if $cgi->param('agentnum') =~ /^\d+/;
+
my $target_dt;
my @target_dates;
# 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
$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')) {
# List all customers with an auto-bill method that's not expired
my %cust_payby = map {$_->custnum => $_} qsearch({
- table => 'cust_payby',
- hashref => {
- weight => { op => '>', value => '0' },
- },
- order_by => " ORDER BY weight DESC ",
- extra_sql => "
- AND (
- payby IN ('CHEK','DCHK')
- OR ( paydate > '".$target_dt->ymd."')
+ table => 'cust_payby',
+ addl_from => 'JOIN cust_main USING (custnum)',
+ hashref => { weight => { op => '>', value => '0' }},
+ order_by => " ORDER BY weight DESC ",
+ extra_sql =>
+ "AND (
+ cust_payby.payby IN ('CHEK','DCHK','DCHEK')
+ OR ( cust_payby.paydate > '".$target_dt->ymd."')
)
- ",
+ AND " . $FS::CurrentUser::CurrentUser->agentnums_sql
+ . ($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;
+ my %pmt_type_subtotal;
local $@;
local $SIG{__DIE__};
eval { # Sandbox
- # Create new database handle and supress all COMMIT statements
+ # Supress COMMIT statements
my $oldAutoCommit = $FS::UID::AutoCommit;
local $FS::UID::AutoCommit = 0;
local $FS::UID::ForceObeyAutoCommit = 1;
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;
);
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 => {
]
};
+ $pmt_type_subtotal{ $cust_pay->paycardtype || $cust_pay-> payby }
+ += $cust_pay->paid;
+
} # /foreach payment
# Roll back database at the end of each customer
# Makes the report slighly slower, but ensures only one customer row
# locked at a time
- warn "-- custnum $custnum -- rollback()\n";
+ warn "-- custnum $custnum -- rollback()\n" if $DEBUG;
+ savepoint_rollback( $savepoint_label );
dbh->rollback if $oldAutoCommit;
} # /foreach $custnum
# grid-report.html requires a parallel @rows parameter to accompany @cells
@rows = map { {class => 'gridreport'} } 1..scalar(@cells);
- # Dynamic report title
- my $title_types = '';
- my $card_count = FS::cust_payby->count_autobill_cards;
- my $check_count = FS::cust_payby->count_autobill_checks;
- if ( $card_count && $check_count ) {
- $title_types = 'Card and Check';
- } elsif ( $card_count ) {
- $title_types = 'Card';
- } elsif ( $check_count ) {
- $title_types = 'Check';
- }
-
- my $report_title = sprintf(
- 'Upcoming Auto Bill %s Transactions',
- $title_types,
- );
-
</%init>