[freeside-commits] branch FREESIDE_4_BRANCH updated. 77a4ccff7384368888fe88379f01f9ddc34244d8

Mark Wells mark at 420.am
Fri Sep 18 12:01:35 PDT 2015


The branch, FREESIDE_4_BRANCH has been updated
       via  77a4ccff7384368888fe88379f01f9ddc34244d8 (commit)
      from  8cbf995cfbc851821242f4ab5e03713b3de8ef69 (commit)

Those revisions listed above that are new to this repository have
not appeared on any other notification email; so we list those
revisions in full, below.

- Log -----------------------------------------------------------------
commit 77a4ccff7384368888fe88379f01f9ddc34244d8
Author: Mark Wells <mark at freeside.biz>
Date:   Fri Sep 18 10:18:43 2015 -0700

    send commission reports by email, #33101

diff --git a/FS/FS/AccessRight.pm b/FS/FS/AccessRight.pm
index 0493edd..c26cc2c 100644
--- a/FS/FS/AccessRight.pm
+++ b/FS/FS/AccessRight.pm
@@ -289,6 +289,7 @@ tie my %rights, 'Tie::IxHash',
     'Billing event reports',
     'Receivables report',
     'Financial reports',
+    { rightname=>'Send reports to customers', global=>1 },
     { rightname=> 'List inventory', global=>1 },
     { rightname=>'View email logs', global=>1 },
     { rightname=>'View system logs' },
diff --git a/FS/FS/Mason.pm b/FS/FS/Mason.pm
index ae4f07c..98a75c8 100644
--- a/FS/FS/Mason.pm
+++ b/FS/FS/Mason.pm
@@ -405,6 +405,10 @@ if ( -e $addl_handler_use_file ) {
   use FS::cust_pkg_reason_fee;
   use FS::part_svc_link;
   use FS::access_user_log;
+  use FS::report_batch;
+  use FS::report_batch;
+  use FS::report_batch;
+  use FS::report_batch;
   # Sammath Naur
 
   if ( $FS::Mason::addl_handler_use ) {
diff --git a/FS/FS/Schema.pm b/FS/FS/Schema.pm
index 12211d1..85fbbeb 100644
--- a/FS/FS/Schema.pm
+++ b/FS/FS/Schema.pm
@@ -7124,6 +7124,37 @@ sub tables_hashref {
                         ],
     },
 
+    'report_batch' => {
+      'columns' => [
+        'reportbatchnum', 'serial',      '',  '', '', '',
+        'reportname',     'varchar',     '', 255, '', '',
+        'agentnum',           'int', 'NULL',  '', '', '',
+        'send_date',     @date_type,              '', '',
+        'sdate',         @date_type,              '', '',
+        'edate',         @date_type,              '', '',
+        'usernum',            'int', 'NULL',  '', '', '',
+        'msgnum',             'int', 'NULL',  '', '', '',
+        # add report params here as necessary
+      ],
+      'primary_key' => 'reportbatchnum',
+      'unique' => [],
+      'index'  => [],
+      'foreign_keys' => [
+                          { columns    => [ 'agentnum' ],
+                            table      => 'agent',
+                            references => [ 'agentnum' ],
+                          },
+                          { columns    => [ 'usernum' ],
+                            table      => 'access_user',
+                            references => [ 'usernum' ],
+                          },
+                          { columns    => [ 'msgnum' ],
+                            table      => 'msg_template',
+                            references => [ 'msgnum' ],
+                          },
+                        ],
+    },
+
     # name type nullability length default local
 
     #'new_table' => {
diff --git a/FS/FS/cust_bill_pkg.pm b/FS/FS/cust_bill_pkg.pm
index 8233ce0..1780426 100644
--- a/FS/FS/cust_bill_pkg.pm
+++ b/FS/FS/cust_bill_pkg.pm
@@ -1124,7 +1124,10 @@ sub tax_locationnum {
   if ( $self->pkgnum ) { # normal sales
     return $self->cust_pkg->tax_locationnum;
   } elsif ( $self->feepart ) { # fees
-    return $self->cust_bill->cust_main->ship_locationnum;
+    my $custnum = $self->fee_origin->custnum;
+    if ( $custnum ) {
+      return FS::cust_main->by_key($custnum)->ship_locationnum;
+    }
   } else { # taxes
     return '';
   }
@@ -1135,7 +1138,10 @@ sub tax_location {
   if ( $self->pkgnum ) { # normal sales
     return $self->cust_pkg->tax_location;
   } elsif ( $self->feepart ) { # fees
-    return $self->cust_bill->cust_main->ship_location;
+    my $custnum = $self->fee_origin->custnum;
+    if ( $custnum ) {
+      return FS::cust_main->by_key($custnum)->ship_location;
+    }
   } else { # taxes
     return;
   }
diff --git a/FS/FS/cust_msg.pm b/FS/FS/cust_msg.pm
index db02680..27272b8 100644
--- a/FS/FS/cust_msg.pm
+++ b/FS/FS/cust_msg.pm
@@ -148,6 +148,7 @@ sub check {
                                     'invoice',
                                     'receipt',
                                     'admin',
+                                    'report',
                                  ])
   ;
   return $error if $error;
diff --git a/FS/FS/msg_template/email.pm b/FS/FS/msg_template/email.pm
index e69183f..5f27370 100644
--- a/FS/FS/msg_template/email.pm
+++ b/FS/FS/msg_template/email.pm
@@ -200,6 +200,12 @@ A hash reference of additional substitutions
 A string identifying the kind of message this is. Currently can be "invoice", 
 "receipt", "admin", or null. Expand this list as necessary.
 
+=item override_content
+
+A string to use as the HTML body; if specified, replaces the entire
+body of the message. This should be used ONLY by L<FS::report_batch> and may
+go away in the future.
+
 =back
 
 =cut
@@ -265,6 +271,12 @@ sub prepare {
   warn "$me filling in body template\n" if $DEBUG;
   $body = $body_tmpl->fill_in( HASH => $hashref );
 
+  # override $body if requested
+  if ( $opt{'override_content'} ) {
+    warn "$me overriding template body with requested content" if $DEBUG;
+    $body = $opt{'override_content'};
+  }
+
   ###
   # and email
   ###
diff --git a/FS/FS/msg_template/http.pm b/FS/FS/msg_template/http.pm
index 51dfcff..a2b0986 100644
--- a/FS/FS/msg_template/http.pm
+++ b/FS/FS/msg_template/http.pm
@@ -59,6 +59,10 @@ sub prepare {
     %$document,
     %$hashref
   };
+  # put override content _somewhere_ so it can be used
+  if ( $opt{'override_content'} ) {
+    $document{'content'} = $opt{'override_content'};
+  }
 
   my $request_content = $json->encode($document);
   warn "$me ".$self->prepare_url."\n" if $DEBUG;
diff --git a/FS/FS/report_batch.pm b/FS/FS/report_batch.pm
new file mode 100644
index 0000000..64412df
--- /dev/null
+++ b/FS/FS/report_batch.pm
@@ -0,0 +1,321 @@
+package FS::report_batch;
+use base qw( FS::Record );
+
+use strict;
+use FS::Record qw( qsearch qsearchs dbdef );
+use FS::msg_template;
+use FS::cust_main;
+use FS::Misc::DateTime qw(parse_datetime);
+use FS::Mason qw(mason_interps);
+use URI::Escape;
+use HTML::Defang;
+
+our $DEBUG = 0;
+
+=head1 NAME
+
+FS::report_batch - Object methods for report_batch records
+
+=head1 SYNOPSIS
+
+  use FS::report_batch;
+
+  $record = new FS::report_batch \%hash;
+  $record = new FS::report_batch { 'column' => 'value' };
+
+  $error = $record->insert;
+
+  $error = $new_record->replace($old_record);
+
+  $error = $record->delete;
+
+  $error = $record->check;
+
+=head1 DESCRIPTION
+
+An FS::report_batch object represents an order to send a batch of reports to
+their respective customers or other contacts.  FS::report_batch inherits from
+FS::Record.  The following fields are currently supported:
+
+=over 4
+
+=item reportbatchnum
+
+primary key
+
+=item reportname
+
+The name of the report, which will be the same as the file name (minus any
+directory names). There's an enumerated set of these; you can't use just any
+report.
+
+=item send_date
+
+The date the report was sent.
+
+=item agentnum
+
+The agentnum to limit the report to, if any.
+
+=item sdate
+
+The start date of the report period.
+
+=item edate
+
+The end date of the report period.
+
+=item usernum
+
+The user who ordered the report.
+
+=back
+
+=head1 METHODS
+
+=over 4
+
+=item new HASHREF
+
+Creates a new report batch.  To add the record to the database, see L<"insert">.
+
+=cut
+
+sub table { 'report_batch'; }
+
+=item insert
+
+Adds this record to the database.  If there is an error, returns the error,
+otherwise returns false.
+
+=item delete
+
+Deletes this record from the database.
+
+=item replace OLD_RECORD
+
+Replaces the OLD_RECORD with this one in the database.  If there is an error,
+returns the error, otherwise returns false.
+
+=item check
+
+Checks all fields to make sure this is a valid record.  If there is
+an error, returns the error, otherwise returns false.  Called by the insert
+and replace methods.
+
+=cut
+
+sub check {
+  my $self = shift;
+
+  my $error = 
+    $self->ut_numbern('reportbatchnum')
+    || $self->ut_text('reportname')
+    || $self->ut_numbern('agentnum')
+    || $self->ut_numbern('sdate')
+    || $self->ut_numbern('edate')
+    || $self->ut_numbern('usernum')
+  ;
+  return $error if $error;
+
+  $self->set('send_date', time);
+
+  $self->SUPER::check;
+}
+
+=back
+
+=head1 SUBROUTINES
+
+=over 4
+
+=item process_send_report JOB, PARAMS
+
+Takes a hash of PARAMS, determines all contacts who need to receive a report,
+and sends it to them. On completion, creates and stores a report_batch record.
+JOB is a queue job to receive status messages.
+
+PARAMS can include:
+
+- reportname: the name of the report (listed in the C<%sendable_reports> hash).
+Required.
+- msgnum: the L<FS::msg_template> to use for this report. Currently the
+content of the template is ignored, but the subject line and From/Bcc addresses
+are still used. Required.
+- agentnum: the agent to limit the report to.
+- beginning, ending: the date range to run the report, as human-readable 
+dates (I<not> unix timestamps).
+
+=cut
+
+# trying to keep this data-driven, with parameters that tell how the report is
+# to be handled rather than callbacks.
+# - path: where under the document root the report is located
+# - domain: which table to query for objects on which the report is run.
+#   Each record in that table produces one report.
+# - cust_main: the method on that object that returns its linked customer (to
+#   which the report will be sent). If the table has a 'custnum' field, this
+#   can be omitted.
+our %sendable_reports = (
+  'sales_commission_pkg' => {
+    'name'      => 'Sales commission per package',
+    'path'      => '/search/sales_commission_pkg.html',
+    'domain'    => 'sales',
+    'cust_main' => 'sales_cust_main',
+  },
+);
+
+sub process_send_report {
+  my $job = shift;
+  my $param = shift;
+
+  my $msgnum = $param->{'msgnum'};
+  my $template = FS::msg_template->by_key($msgnum)
+    or die "msg_template $msgnum not found\n";
+
+  my $reportname = $param->{'reportname'};
+  my $info = $sendable_reports{$reportname}
+    or die "don't know how to send report '$reportname'\n";
+
+  # the most important thing: which report is it?
+  my $path = $info->{'path'};
+
+  # find all targets for the report:
+  # - those matching the agentnum if there is one.
+  # - those that aren't disabled.
+  my $domain = $info->{domain};
+  my $dbt = dbdef->table($domain);
+  my $hashref = {};
+  if ( $param->{'agentnum'} and $dbt->column('agentnum') ) {
+    $hashref->{'agentnum'} = $param->{'agentnum'};
+  }
+  if ( $dbt->column('disabled') ) {
+    $hashref->{'disabled'} = '';
+  }
+  my @records = qsearch($domain, $hashref);
+  my $num_targets = scalar(@records);
+  return if $num_targets == 0;
+  my $sent = 0;
+
+  my $outbuf;
+  my ($fs_interp) = mason_interps('standalone', 'outbuf' => \$outbuf);
+  # if generating the report fails, we want to capture the error and exit,
+  # not send it.
+  $fs_interp->error_mode('fatal');
+  $fs_interp->error_format('brief');
+
+  # we have to at least have an RT::Handle
+  require RT;
+  RT::LoadConfig();
+  RT::Init();
+
+  # hold onto all the reports until we're sure they generated correctly.
+  my %cust_main;
+  my %report_content;
+
+  # grab the stylesheet
+  ### note: if we need the ability to support different stylesheets, this
+  ### is the place to put it in
+  eval { $fs_interp->exec('/elements/freeside.css') };
+  die "couldn't load stylesheet via Mason: $@\n" if $@;
+  my $stylesheet = $outbuf;
+
+  my $pkey = $dbt->primary_key;
+  foreach my $rec (@records) {
+
+    $job->update_statustext(int( 100 * $sent / $num_targets ));
+    my $pkey_val = $rec->get($pkey); # e.g. sales.salesnum
+
+    # find the customer we're sending to, and their email
+    my $cust_main;
+    if ( $info->{'cust_main'} ) {
+      my $cust_method = $info->{'cust_main'};
+      $cust_main = $rec->$cust_method;
+    } elsif ( $rec->custnum ) {
+      $cust_main = FS::cust_main->by_key($rec->custnum);
+    } else {
+      warn "$pkey = $pkey_val has no custnum; not sending report\n";
+      next;
+    }
+    my @email = $cust_main->invoicing_list_emailonly;
+    if (!@email) {
+      warn "$pkey = $pkey_val has no email destinations\n" if $DEBUG;
+      next;
+    }
+
+    # params to send to the report (as if from the user's browser)
+    my @report_param = ( # maybe list these in $info
+      agentnum  => $param->{'agentnum'},
+      beginning => $param->{'beginning'},
+      ending    => $param->{'ending'},
+      $pkey     => $pkey_val,
+      _type     => 'html-print',
+    );
+
+    # build a query string
+    my $query_string = '';
+    while (@report_param) {
+      $query_string .= uri_escape(shift @report_param)
+                    .  '='
+                    .  uri_escape(shift @report_param);
+      $query_string .= ';' if @report_param;
+    }
+    warn "$path?$query_string\n\n" if $DEBUG;
+
+    # run the report!
+    $FS::Mason::Request::QUERY_STRING = $query_string;
+    $FS::Mason::Request::FSURL = '';
+    $outbuf = '';
+    eval { $fs_interp->exec($path) };
+    die "creating report for $pkey = $pkey_val: $@" if $@;
+
+    # make some adjustments to the report
+    my $html_defang;
+    $html_defang = HTML::Defang->new(
+      url_callback      => sub { 1 }, # strip all URLs (they're not accessible)
+      tags_to_callback  => [ 'body' ], # and after the BODY tag...
+      tags_callback     => sub {
+        my $isEndTag = $_[4];
+        $html_defang->add_to_output("\n<style>\n$stylesheet\n</style>\n")
+          unless $isEndTag;
+      },
+    );
+    $outbuf = $html_defang->defang($outbuf);
+
+    $cust_main{ $cust_main->custnum } = $cust_main;
+    $report_content{ $cust_main->custnum } = $outbuf;
+  } # foreach $rec
+
+  $job->update_statustext('Sending reports...');
+  foreach my $custnum (keys %cust_main) {
+    # create an email message with the report as body
+    # change this when backporting to 3.x
+    $template->send(
+      cust_main         => $cust_main{$custnum},
+      object            => $cust_main{$custnum},
+      msgtype           => 'report',
+      override_content  => $report_content{$custnum},
+    );
+  }
+
+  my $self = FS::report_batch->new({
+    reportname  => $param->{'reportname'},
+    agentnum    => $param->{'agentnum'},
+    sdate       => parse_datetime($param->{'beginning'}),
+    edate       => parse_datetime($param->{'ending'}),
+    usernum     => $job->usernum,
+    msgnum      => $param->{'msgnum'},
+  });
+  my $error = $self->insert;
+  warn "error recording completion of report: $error\n" if $error;
+
+}
+
+=head1 SEE ALSO
+
+L<FS::Record>
+
+=cut
+
+1;
+
diff --git a/FS/MANIFEST b/FS/MANIFEST
index 5b73b72..5041ccd 100644
--- a/FS/MANIFEST
+++ b/FS/MANIFEST
@@ -850,3 +850,11 @@ FS/part_svc_link.pm
 t/part_svc_link.t
 FS/access_user_log.pm
 t/access_user_log.t
+FS/report_batch.pm
+t/report_batch.t
+FS/report_batch.pm
+t/report_batch.t
+FS/report_batch.pm
+t/report_batch.t
+FS/report_batch.pm
+t/report_batch.t
diff --git a/FS/t/report_batch.t b/FS/t/report_batch.t
new file mode 100644
index 0000000..42fc893
--- /dev/null
+++ b/FS/t/report_batch.t
@@ -0,0 +1,5 @@
+BEGIN { $| = 1; print "1..1\n" }
+END {print "not ok 1\n" unless $loaded;}
+use FS::report_batch;
+$loaded=1;
+print "ok 1\n";
diff --git a/httemplate/elements/popup_link-send_report_batch.html b/httemplate/elements/popup_link-send_report_batch.html
new file mode 100644
index 0000000..5f44710
--- /dev/null
+++ b/httemplate/elements/popup_link-send_report_batch.html
@@ -0,0 +1,28 @@
+<%doc>
+
+Example:
+
+<& /elements/popup_link-send_report_batch.html,
+  reportname => 'sales_commission_pkg',
+  label => 'Click here to send reports by email',
+&>
+</%doc>
+<& /elements/popup_link.html, $params &>\
+<%init>
+
+my $params = { 'closetext' => emt('Close') };
+
+if (ref($_[0]) eq 'HASH') {
+  $params = { %$params, %{ $_[0] } };
+} else {
+  $params = { %$params, @_ };
+}
+
+$params->{'label'} ||= emt('Send reports by email');
+$params->{'actionlabel'} ||= emt('Send reports');
+#$params->{'width'} ||= 350;
+$params->{'height'} ||= 650;
+
+$params->{'action'} = $fsurl. 'misc/send-report.html?reportname='. $params->{'reportname'};
+
+</%init>
diff --git a/httemplate/misc/process/send-report.html b/httemplate/misc/process/send-report.html
new file mode 100644
index 0000000..3bceebc
--- /dev/null
+++ b/httemplate/misc/process/send-report.html
@@ -0,0 +1,7 @@
+<%init>
+die "access denied"
+  unless $FS::CurrentUser::CurrentUser->access_right('Send reports to customers');
+
+my $server = FS::UI::Web::JSRPC->new('FS::report_batch::process_send_report', $cgi);
+</%init>
+<% $server->process %>
diff --git a/httemplate/misc/send-report.html b/httemplate/misc/send-report.html
new file mode 100644
index 0000000..557767a
--- /dev/null
+++ b/httemplate/misc/send-report.html
@@ -0,0 +1,166 @@
+<%doc>
+
+Parameters:
+
+- reportname: the report name (per FS::report_batch)
+
+</%doc>
+<& /elements/header-popup.html, { title => $report_info->{name} } &>
+<script type="text/javascript">
+
+$().ready(function() {
+  var agent_info = <% encode_json(\%agent) %>;
+
+  $('#agentnum').on('change', function() {
+    var agentnum = this.value;
+    if ( agent_info[agentnum] ) {
+      $('#msgnum').prop('value',         agent_info[agentnum]['msgnum']);
+      $('#beginning_text').prop('value', agent_info[agentnum]['beginning']);
+      $('#ending_text').prop('value',    agent_info[agentnum]['ending']);
+    } else {
+      $('#msgnum').prop('value',         '');
+      $('#beginning_text').prop('value', '');
+      $('#ending_text').prop('value',    '');
+    }
+  });
+
+  $('#agentnum').trigger('change');
+
+});
+
+</script>
+<FORM NAME="OneTrueForm" ACTION="process/send-report.html" METHOD="POST">
+
+<table class="inv">
+  <input type="hidden" name="reportname" value="<% $cgi->param('reportname') |h %>">
+
+  <& /elements/tr-select-agent.html &>
+
+  <& /elements/tr-td-label.html, label => emt('Message template') &>
+    <TD>
+      <& /elements/select-msg_template.html, field => 'msgnum' &>
+    </TD>
+  </TR>
+
+  <& /elements/tr-input-beginning_ending.html &>
+
+  <& /elements/progress-init.html,
+    'OneTrueForm',
+    [ qw( reportname msgnum agentnum beginning ending ) ],
+    $p.'misc/process/send-report.html',
+    { message => 'Reports sent',
+      url => $cgi->referer }
+  &>
+
+</table>
+
+<INPUT TYPE="button" onclick="process()" VALUE="<% emt('Send reports') %>" />
+</FORM>
+
+<style>
+table.grid {
+  border-collapse: collapse;
+  margin-top: 1ex;
+  margin-left: auto;
+  margin-right: auto;
+}
+.grid caption {
+  font-weight: bold;
+  margin-bottom: 0.5ex;
+}
+.grid th,td {
+  padding-left: 3px;
+  padding-right: 3px;
+  padding-bottom: 2px;
+  border: none;
+  empty-cells: show;
+}
+.grid th {
+  border-bottom: 1px solid #999999;
+  font-size: 90%;
+  vertical-align: bottom;
+}
+</style>
+
+% if ( @report_history ) {
+<hr>
+<table class="grid">
+<caption><% emt('Report history') %></caption>
+<thead>
+  <th>Agent</th>
+  <th>Sent on</th>
+  <th colspan=2>Date range</th>
+  <th>User</th>
+</thead>
+<tbody>
+%   my $row = 0;
+%   foreach my $report (@report_history) {
+%   my $agent = ($report->agentnum ?
+%                 $report->agent->agent : 'All agents');
+  <tr class="row<% $row % 2 %>">
+    <td><% $agent %></td>
+    <td><% time2str($date_format, $report->send_date) %></td>
+    <td><% time2str($date_format, $report->sdate) %></td>
+    <td><% time2str($date_format, $report->edate) %></td>
+    <td><% $report->access_user->username %></td>
+  </tr>
+%   $row++;
+%   }
+</tbody>
+</table>
+% }
+
+<& /elements/footer.html &>
+
+<%init>
+
+die "access denied"
+  unless $FS::CurrentUser::CurrentUser->access_right('Send reports to customers');
+
+$cgi->param('reportname') =~ /^(\w+)$/
+  or die "bad reportname";
+my $reportname = $1;
+my $report_info = $FS::report_batch::sendable_reports{$reportname}
+  or die "bad reportname";
+
+my $date_format = FS::Conf->new->config('date_format') || '%x';
+
+my @report_history = qsearch({
+  table     => 'report_batch',
+  hashref   => { reportname => $reportname },
+  order_by  => ' ORDER BY send_date DESC',
+});
+
+# defaults per agent that could be selected for the report
+my %agent;
+
+foreach my $report ( @report_history ) {
+  my $agentnum = $report->agentnum;
+  next if $agent{$agentnum};
+
+  # estimate the width of the report period, in months
+  my $last_sdate = DateTime->from_epoch( epoch => $report->sdate );
+  my $last_edate = DateTime->from_epoch( epoch => $report->edate );
+
+  my $days = $last_sdate->delta_days( $last_edate )->delta_days;
+  my $months = sprintf('%.0f', $days / 6) / 5;
+
+  my $next_sdate = $last_edate->clone->add(days => 1);
+  my $next_edate = $next_sdate->clone;
+  if ( $months >= 1 ) { # then treat as an interval in months
+    $next_edate->add( months => sprintf('%.0f', $months) );
+    $next_edate->subtract(days => 1);
+  } else { # treat as a number of days
+    $next_edate->add( days => $days );
+  }
+
+  my $name = $agentnum ? FS::agent->by_key($agentnum)->agent : 'All agents';
+  $agent{$agentnum} = {
+    name      => $name,
+    beginning => $next_sdate->strftime($date_format),
+    ending    => $next_edate->strftime($date_format),
+    msgnum    => $report->msgnum,
+  };
+}
+
+</%init>
diff --git a/httemplate/search/cust_msg.html b/httemplate/search/cust_msg.html
index 401f52e..e9aece2 100644
--- a/httemplate/search/cust_msg.html
+++ b/httemplate/search/cust_msg.html
@@ -144,11 +144,12 @@ include('/elements/select.html',
 include('/elements/select.html',
   'field' => 'msgtype',
   'curr_value' => $cgi->param('msgtype') || '',
-  'options' => [ '', 'invoice', 'receipt', 'admin' ],
+  'options' => [ '', 'invoice', 'receipt', 'admin', 'report' ],
   'labels'  => { ''         => '(any)',
                  'invoice'  => 'Invoices',
                  'receipt'  => 'Receipts',
                  'admin'    => 'Admin notices',
+                 'report'   => 'Reports',
                },
 ) .
 '</TD>
diff --git a/httemplate/search/report_sales_commission_pkg.html b/httemplate/search/report_sales_commission_pkg.html
index 6adf090..27906e0 100644
--- a/httemplate/search/report_sales_commission_pkg.html
+++ b/httemplate/search/report_sales_commission_pkg.html
@@ -1,5 +1,15 @@
 <& /elements/header.html, 'Sales commission report per package' &>
 
+% if ($FS::CurrentUser::CurrentUser->access_right('Send reports to customers'))
+%   {
+<P>
+<& /elements/popup_link-send_report_batch.html,
+    reportname => 'sales_commission_pkg',
+    label => emt('Send these reports by email'),
+&>
+</P>
+% } 
+
 <FORM ACTION="sales_commission_pkg.html">
 
 <TABLE BGCOLOR="#cccccc" CELLSPACING=0>
diff --git a/httemplate/search/sales_commission_pkg.html b/httemplate/search/sales_commission_pkg.html
index 2b5f2bb..9fbe22e 100644
--- a/httemplate/search/sales_commission_pkg.html
+++ b/httemplate/search/sales_commission_pkg.html
@@ -1,12 +1,17 @@
 %# still not a good way to do rows grouped by some field in a search.html 
 %# report
+%# (there is now, but we're not yet sponsored to switch this over to it)
 % if ( $type eq 'xls' ) {
 <% $data %>\
 % } else {
+%   if ( $type eq 'html-print' ) {
+<& /elements/header-popup.html, $title &>
+%   } else {
 <& /elements/header.html, $title &>
 <P ALIGN="right" CLASS="noprint">
 Download full results<BR>
 as <A HREF="<% $cgi->self_url %>;_type=xls">Excel spreadsheet</A></P>
+%   }
 <BR>
 <STYLE TYPE="text/css">
 td.cust_head {
@@ -22,12 +27,14 @@ td.money:before { content: '<% $money_char %>'; }
 .row1 { background-color: #ffffff; }
 </STYLE>
 <& /elements/table-grid.html &>
+<THEAD>
   <TR STYLE="background-color: #cccccc">
     <TH CLASS="grid">Package</TH>
     <TH CLASS="grid">Sales</TH>
     <TH CLASS="grid">Percentage</TH>
     <TH CLASS="grid">Commission</TH>
   </TR>
+</THEAD>
 % my ($custnum, $sales, $commission, $row, $bgcolor) = (0, 0, 0, 0);
 % foreach my $cust_pkg ( @cust_pkg ) {
 %   if ( $custnum ne $cust_pkg->custnum ) {

-----------------------------------------------------------------------

Summary of changes:
 FS/FS/AccessRight.pm                               |    1 +
 FS/FS/Mason.pm                                     |    4 +
 FS/FS/Schema.pm                                    |   31 ++
 FS/FS/cust_bill_pkg.pm                             |   10 +-
 FS/FS/cust_msg.pm                                  |    1 +
 FS/FS/msg_template/email.pm                        |   12 +
 FS/FS/msg_template/http.pm                         |    4 +
 FS/FS/report_batch.pm                              |  321 ++++++++++++++++++++
 FS/MANIFEST                                        |    8 +
 FS/t/{AccessRight.t => report_batch.t}             |    2 +-
 .../elements/popup_link-send_report_batch.html     |   28 ++
 httemplate/misc/process/send-report.html           |    7 +
 httemplate/misc/send-report.html                   |  166 ++++++++++
 httemplate/search/cust_msg.html                    |    3 +-
 httemplate/search/report_sales_commission_pkg.html |   10 +
 httemplate/search/sales_commission_pkg.html        |    7 +
 16 files changed, 611 insertions(+), 4 deletions(-)
 create mode 100644 FS/FS/report_batch.pm
 copy FS/t/{AccessRight.t => report_batch.t} (82%)
 create mode 100644 httemplate/elements/popup_link-send_report_batch.html
 create mode 100644 httemplate/misc/process/send-report.html
 create mode 100644 httemplate/misc/send-report.html




More information about the freeside-commits mailing list