summaryrefslogtreecommitdiff
path: root/httemplate
diff options
context:
space:
mode:
authorIvan Kohler <ivan@freeside.biz>2012-07-26 14:05:08 -0700
committerIvan Kohler <ivan@freeside.biz>2012-07-26 14:05:08 -0700
commit8c450aab9bae89373c2c1b35c85597bb52299de3 (patch)
tree62e7400162703ad0990f80a2d71b90fc1167e759 /httemplate
parent2528cc7b182781a82844d8bbb1b555560487abc7 (diff)
parentbd647f16de5352722baed016b45baa4e7c695278 (diff)
Merge branch 'master' of git.freeside.biz:/home/git/freeside
Diffstat (limited to 'httemplate')
-rw-r--r--httemplate/edit/payment_gateway.html7
-rw-r--r--httemplate/edit/prepay_credit.cgi10
-rw-r--r--httemplate/edit/radius_group.html1
-rw-r--r--httemplate/edit/svc_dsl.cgi24
-rw-r--r--httemplate/elements/customer-table.html12
-rw-r--r--httemplate/elements/menu.html1
-rw-r--r--httemplate/elements/select-cust_pkg-status.html2
-rw-r--r--httemplate/elements/tr-amount_fee.html98
-rw-r--r--httemplate/graph/elements/report.html16
-rwxr-xr-xhttemplate/misc/cancel_pkg.html14
-rw-r--r--httemplate/misc/order_pkg.html2
-rw-r--r--httemplate/misc/payment.cgi115
-rwxr-xr-xhttemplate/misc/process/cancel_pkg.html8
-rw-r--r--httemplate/pref/pref-process.html3
-rw-r--r--httemplate/pref/pref.html15
-rwxr-xr-xhttemplate/search/477partV.html8
-rw-r--r--httemplate/search/cust_bill_pkg_referral.html294
-rw-r--r--httemplate/search/cust_main-zip.html2
-rw-r--r--httemplate/search/elements/cust_pay_batch_top.html2
-rwxr-xr-xhttemplate/search/elements/cust_pay_or_refund.html9
-rw-r--r--httemplate/search/elements/search-xls.html38
-rwxr-xr-xhttemplate/search/pay_batch.cgi92
-rw-r--r--httemplate/search/report_cust_bill_pkg_referral.html61
-rwxr-xr-xhttemplate/search/report_tax-xls.cgi29
-rw-r--r--httemplate/view/cust_main/packages/status.html7
-rwxr-xr-xhttemplate/view/cust_main_statement-pdf.cgi6
26 files changed, 708 insertions, 168 deletions
diff --git a/httemplate/edit/payment_gateway.html b/httemplate/edit/payment_gateway.html
index 2840df35b..e5897b035 100644
--- a/httemplate/edit/payment_gateway.html
+++ b/httemplate/edit/payment_gateway.html
@@ -19,8 +19,7 @@
<SCRIPT TYPE="text/javascript">
-% my $json = JSON->new->canonical;
- var modulesForNamespace = <% $json->encode(\%modules_for_namespace) %>;
+ var modulesForNamespace = <% to_json(\%modules_for_namespace, {canonical=>1}) %>;
function changeNamespace(what) {
var ns = what.value;
var select_module = document.getElementById('gateway_module');
@@ -68,7 +67,6 @@ my %modules = (
'OpenECHO' => 'Business::OnlinePayment',
'PayConnect' => 'Business::OnlinePayment',
'PayflowPro' => 'Business::OnlinePayment',
- 'Paymentech' => 'Business::BatchPayment',
'PaymenTech' => 'Business::OnlinePayment',
'PaymentsGateway' => 'Business::OnlinePayment',
'PayPal' => 'Business::OnlinePayment',
@@ -90,6 +88,9 @@ my %modules = (
'VirtualNet' => 'Business::OnlinePayment',
'WesternACH' => 'Business::OnlinePayment',
'WorldPay' => 'Business::OnlinePayment',
+
+ 'KeyBank' => 'Business::BatchPayment',
+ 'Paymentech' => 'Business::BatchPayment',
);
my %modules_for_namespace;
diff --git a/httemplate/edit/prepay_credit.cgi b/httemplate/edit/prepay_credit.cgi
index f7a1b0801..c03bbf990 100644
--- a/httemplate/edit/prepay_credit.cgi
+++ b/httemplate/edit/prepay_credit.cgi
@@ -18,13 +18,11 @@ prepaid cards of
characters each
-<BR>for <SELECT NAME="agentnum"><OPTION>(any agent)
-% foreach my $opt_agent ( qsearch('agent', { 'disabled' => '' } ) ) {
+<BR>for
- <OPTION VALUE="<% $opt_agent->agentnum %>"<% $opt_agent->agentnum == $agentnum ? ' SELECTED' : '' %>><% $opt_agent->agent %>
-% }
-
-</SELECT>
+<& /elements/select-agent.html,
+ 'empty_label' => '(any agent)',
+&>
<TABLE>
<TR><TD>Value:
diff --git a/httemplate/edit/radius_group.html b/httemplate/edit/radius_group.html
index 025561159..0c99b4c4c 100644
--- a/httemplate/edit/radius_group.html
+++ b/httemplate/edit/radius_group.html
@@ -8,6 +8,7 @@
'attrnum' => 'Attribute',
'priority' => 'Priority',
},
+ 'viewall_dir' => 'browse',
'menubar' => \@menubar,
'edit_callback' => $edit_callback,
'error_callback' => $edit_callback,
diff --git a/httemplate/edit/svc_dsl.cgi b/httemplate/edit/svc_dsl.cgi
index 1aeadb376..36345b9c5 100644
--- a/httemplate/edit/svc_dsl.cgi
+++ b/httemplate/edit/svc_dsl.cgi
@@ -52,12 +52,24 @@ my $edit_cb = sub {
elsif($export->exporttype eq 'ikano') {
@fields = ( 'password', 'monitored', );
- foreach my $hf ( keys %$ti_fields ) {
- push @fields, {
- field => $hf,
- type => 'hidden',
- value => $svc_x->$hf,
- } unless ( $hf eq 'password' || $hf eq 'monitored' );
+ if ( $svc_x->vendor_qual_id ) {
+ push @fields, { field => 'vendor_qual_id',
+ type => 'hidden',
+ value => $svc_x->vendor_qual_id,
+ };
+ } else {
+ push @fields, 'vendor_qual_id';
+ }
+
+ foreach my $hf (
+ grep { $_ !~ /^(password|monitored|vendor_qual_id)$/ }
+ keys %$ti_fields
+ ) {
+ push @fields, {
+ field => $hf,
+ type => 'hidden',
+ value => $svc_x->$hf,
+ };
}
}
# else add any other export-specific stuff here
diff --git a/httemplate/elements/customer-table.html b/httemplate/elements/customer-table.html
index 79443dc8b..75e682d92 100644
--- a/httemplate/elements/customer-table.html
+++ b/httemplate/elements/customer-table.html
@@ -203,7 +203,11 @@ Example:
var customerArrayArray = eval('(' + customers + ')') || [];
- if ( customerArrayArray.length == 1 ) {
+ if ( customerArrayArray.length == 0 ) {
+
+ update_customer(searchrow, []);
+
+ } else if ( customerArrayArray.length == 1 ) {
update_customer(searchrow, customerArrayArray[0]);
% if ( $opt{custnum_update_callback} ) {
@@ -277,7 +281,11 @@ Example:
custnum_obj.disabled = false;
custnum_obj.style.backgroundColor = '#ffffff';
- if ( customerArrayArray.length == 1 ) {
+ if ( customerArrayArray.length == 0 ) {
+
+ update_customer(searchrow, []);
+
+ } else if ( customerArrayArray.length == 1 ) {
update_customer(searchrow, customerArrayArray[0]);
% if ( $opt{custnum_update_callback} ) {
diff --git a/httemplate/elements/menu.html b/httemplate/elements/menu.html
index 938303521..019afe94e 100644
--- a/httemplate/elements/menu.html
+++ b/httemplate/elements/menu.html
@@ -314,6 +314,7 @@ if($curuser->access_right('Financial reports')) {
'Daily Sales, Credits and Receipts' => [ $fsurl.'graph/report_money_time_daily.html', 'Sales, credits and receipts (broken down by day) summary graph' ],
'Sales Report' => [ $fsurl.'graph/report_cust_bill_pkg.html', 'Sales report and graph (by agent, package class and/or date range)' ],
'Rated Call Sales Report' => [ $fsurl.'graph/report_cust_bill_pkg_detail.html', 'Sales report and graph (by agent, package class, usage class and/or date range)' ],
+ 'Sales With Advertising Source' => [ $fsurl.'search/report_cust_bill_pkg_referral.html' ],
'Employee Commission Report' => [ $fsurl.'search/report_employee_commission.html', '' ],
'Credit Report' => [ $fsurl.'search/report_cust_credit.html', 'Credit report (by employee and/or date range)' ],
'Unapplied Credits' => [ $fsurl.'search/report_cust_credit.html?unapplied=1', 'Unapplied credit report (by type and/or date range)' ],
diff --git a/httemplate/elements/select-cust_pkg-status.html b/httemplate/elements/select-cust_pkg-status.html
index ec37eaf67..2114c07a1 100644
--- a/httemplate/elements/select-cust_pkg-status.html
+++ b/httemplate/elements/select-cust_pkg-status.html
@@ -3,7 +3,9 @@
<% $onchange %>
>
+% if ( !$opt{'disable_empty'} ) {
<OPTION VALUE="">all
+% }
% foreach my $option ( @{ $opt{'statuses'} } ) {
diff --git a/httemplate/elements/tr-amount_fee.html b/httemplate/elements/tr-amount_fee.html
new file mode 100644
index 000000000..a1a9e3433
--- /dev/null
+++ b/httemplate/elements/tr-amount_fee.html
@@ -0,0 +1,98 @@
+ <TR>
+ <TH ALIGN="right"><% mt('Payment amount') |h %></TH>
+ <TD COLSPAN=7>
+ <TABLE><TR><TD BGCOLOR="#ffffff">
+ <% $money_char %><INPUT NAME = "amount"
+ ID = "amount"
+ TYPE = "text"
+ VALUE = "<% $amount %>"
+ SIZE = 8
+ STYLE = "text-align:right;"
+% if ( $fee ) {
+ onChange = "amount_changed(this)"
+ onKeyDown = "amount_changed(this)"
+ onKeyUp = "amount_changed(this)"
+ onKeyPress = "amount_changed(this)"
+% }
+ >
+ </TD><TD BGCOLOR="#cccccc">
+% if ( $fee ) {
+ <INPUT TYPE="hidden" NAME="fee_pkgpart" VALUE="<% $fee_pkg->pkgpart %>">
+ <INPUT TYPE="hidden" NAME="fee" VALUE="<% $fee_display eq 'add' ? $fee : '' %>">
+ <B><FONT SIZE='+1'><% $fee_op %></FONT>
+ <% $money_char . $fee %>
+ </B>
+ <% $fee_pkg->pkg |h %>
+ <B><FONT SIZE='+1'>=</FONT></B>
+ </TD><TD ID="ajax_total_cell" BGCOLOR="#dddddd" STYLE="border:1px solid blue">
+ <FONT SIZE="+1"><% length($amount) ? $money_char. sprintf('%.2f', ($fee_display eq 'add') ? $amount + $fee : $amount - $fee ) : '' %> <% $fee_display eq 'add' ? 'TOTAL' : 'AVAILABLE' %></FONT>
+
+% }
+ </TD></TR></TABLE>
+ </TD>
+ </TR>
+
+% if ( $fee ) {
+
+ <SCRIPT TYPE="text/javascript">
+
+ function amount_changed(what) {
+
+
+ var total = '';
+ if ( what.value.length ) {
+ total = parseFloat(what.value) <% $fee_op %> <% $fee %>;
+ /* total = Math.round(total*100)/100; */
+ total = '<% $money_char %>' + total.toFixed(2);
+ }
+
+ var total_cell = document.getElementById('ajax_total_cell');
+ total_cell.innerHTML = '<FONT SIZE="+1">' + total + ' <% $fee_display eq 'add' ? 'TOTAL' : 'AVAILABLE' %></FONT>';
+
+ }
+
+ </SCRIPT>
+
+% }
+
+<%init>
+
+my %opt = @_;
+
+my $conf = new FS::Conf;
+my $money_char = $conf->config('money_char') || '$';
+
+my $fee = '';
+my $fee_pkg = '';
+my $fee_display = '';
+my $fee_op = '';
+
+if ( $opt{'process-pkgpart'}
+ and ! $opt{'process-skip_first'} || $opt{'num_payments'}
+ )
+{
+
+ $fee_display = $opt{'process-display'} || 'add';
+ $fee_op = $fee_display eq 'add' ? '+' : '-';
+
+ $fee_pkg =
+ qsearchs('part_pkg', { pkgpart=>$opt{'process-pkgpart'} } );
+
+ #well ->unit_setup or ->calc_setup both call for a $cust_pkg
+ # (though ->unit_setup doesn't use it...)
+ $fee = $fee_pkg->option('setup_fee')
+ if $fee_pkg; #in case.. better than dying with a perl traceback
+
+}
+
+my $amount = $opt{'amount'};
+if ( $amount > 0 ) {
+ $amount += $fee
+ if $fee && $fee_display eq 'subtract';
+
+ &{ $opt{post_fee_callback} }( \$amount ) if $opt{post_fee_callback};
+
+ $amount = sprintf("%.2f", $amount);
+}
+
+</%init>
diff --git a/httemplate/graph/elements/report.html b/httemplate/graph/elements/report.html
index 3600f2c66..f7746165a 100644
--- a/httemplate/graph/elements/report.html
+++ b/httemplate/graph/elements/report.html
@@ -77,15 +77,16 @@ any delimiter and linked from the elements in @data.
% }
%
% } elsif ( $cgi->param('_type') =~ /(xls)$/ ) {
-%
-% #http_header('Content-Type' => 'application/excel' ); #eww
-% http_header('Content-Type' => 'application/vnd.ms-excel' );
-% #http_header('Content-Type' => 'application/msexcel' ); #alas
-% http_header('Content-Disposition' => "attachment;filename=$filename.xls");
+% #false laziness w/ search/elements/search-xls
+% my $format = $FS::CurrentUser::CurrentUser->spreadsheet_format;
+% $filename .= $format->{extension};
+%
+% http_header('Content-Type' => $format->{mime_type} );
+% http_header('Content-Disposition' => qq!attachment;filename="$filename"! );
%
% my $output = '';
% my $XLS = new IO::Scalar \$output;
-% my $workbook = Spreadsheet::WriteExcel->new($XLS)
+% my $workbook = $format->{class}->new($XLS)
% or die "Error opening .xls file: $!";
%
% my $worksheet = $workbook->add_worksheet(substr($opt{'title'},0,31));
@@ -304,9 +305,6 @@ td.cell {
<% include('/elements/footer.html') %>
% }
-<%once>
-
-</%once>
<%init>
my(%opt) = @_;
diff --git a/httemplate/misc/cancel_pkg.html b/httemplate/misc/cancel_pkg.html
index 348f0a6cb..f9a46a898 100755
--- a/httemplate/misc/cancel_pkg.html
+++ b/httemplate/misc/cancel_pkg.html
@@ -62,7 +62,19 @@
&>
% }
-% if ( ( $method eq 'adjourn' or $method eq 'suspend' ) and
+% if ( $method eq 'adjourn' || $method eq 'suspend' ) {
+ <TR><TD COLSPAN=2>
+% if ( $part_pkg->option('suspend_bill', 1) ) {
+ <& /elements/checkbox.html, name=>'no_suspend_bill', value=>'Y' &>
+ Disable recurring billing while suspended
+% } else {
+ <& /elements/checkbox.html, name=>'suspend_bill', value=>'Y' &>
+ Continue recurring billing while suspended
+% }
+ </TD></TR>
+% }
+
+% if ( ( $method eq 'adjourn' || $method eq 'suspend' ) and
% $curuser->access_right('Unsuspend customer package') ) { #later?
% my $resume_date = $cgi->param('error')
% ? str2time($cgi->param('resume_date'))
diff --git a/httemplate/misc/order_pkg.html b/httemplate/misc/order_pkg.html
index 57fdd64ee..c5f4509ab 100644
--- a/httemplate/misc/order_pkg.html
+++ b/httemplate/misc/order_pkg.html
@@ -44,6 +44,8 @@
<INPUT TYPE="text" NAME="quantity" SIZE=4 VALUE="<% $quantity %>">
</TD>
</TR>
+% } else {
+ <INPUT TYPE="hidden" NAME="quantity" VALUE="1">
% }
<TR>
diff --git a/httemplate/misc/payment.cgi b/httemplate/misc/payment.cgi
index 093494a06..1ae15b930 100644
--- a/httemplate/misc/payment.cgi
+++ b/httemplate/misc/payment.cgi
@@ -9,67 +9,20 @@
<& /elements/init_overlib.html &>
<% ntable('#cccccc') %>
- <TR>
- <TH ALIGN="right"><% mt('Payment amount') |h %></TH>
- <TD COLSPAN=7>
- <TABLE><TR><TD BGCOLOR="#ffffff">
- <% $money_char %><INPUT NAME = "amount"
- ID = "amount"
- TYPE = "text"
- VALUE = "<% $amount %>"
- SIZE = 8
- STYLE = "text-align:right;"
-% if ( $fee ) {
- onChange = "amount_changed(this)"
- onKeyDown = "amount_changed(this)"
- onKeyUp = "amount_changed(this)"
- onKeyPress = "amount_changed(this)"
-% }
- >
- </TD><TD BGCOLOR="#cccccc">
-% if ( $fee ) {
- <INPUT TYPE="hidden" NAME="fee_pkgpart" VALUE="<% $fee_pkg->pkgpart %>">
- <INPUT TYPE="hidden" NAME="fee" VALUE="<% $fee_display eq 'add' ? $fee : '' %>">
- <B><FONT SIZE='+1'><% $fee_op %></FONT>
- <% $money_char . $fee %>
- </B>
- <% $fee_pkg->pkg |h %>
- <B><FONT SIZE='+1'>=</FONT></B>
- </TD><TD ID="ajax_total_cell" BGCOLOR="#dddddd" STYLE="border:1px solid blue">
- <FONT SIZE="+1"><% length($amount) ? $money_char. sprintf('%.2f', ($fee_display eq 'add') ? $amount + $fee : $amount - $fee ) : '' %> <% $fee_display eq 'add' ? 'TOTAL' : 'AVAILABLE' %></FONT>
-
-% }
- </TD></TR></TABLE>
- </TD>
- </TR>
-
-% if ( $fee ) {
-
- <SCRIPT TYPE="text/javascript">
-
- function amount_changed(what) {
-
-
- var total = '';
- if ( what.value.length ) {
- total = parseFloat(what.value) <% $fee_op %> <% $fee %>;
- /* total = Math.round(total*100)/100; */
- total = '<% $money_char %>' + total.toFixed(2);
- }
-
- var total_cell = document.getElementById('ajax_total_cell');
- total_cell.innerHTML = '<FONT SIZE="+1">' + total + ' <% $fee_display eq 'add' ? 'TOTAL' : 'AVAILABLE' %></FONT>';
-
- }
-
- </SCRIPT>
-% }
+ <& /elements/tr-amount_fee.html,
+ 'amount' => $amount,
+ 'process-pkgpart' => scalar($conf->config('manual_process-pkgpart')),
+ 'process-display' => scalar($conf->config('manual_process-display')),
+ 'process-skip-first' => $conf->exists('manual_process-skip_first'),
+ 'num_payments' => scalar($cust_main->cust_pay),
+ 'post_fee_callback' => $post_fee_callback,
+ &>
-<& /elements/tr-select-discount_term.html,
- 'custnum' => $custnum,
- 'amount_id' => 'amount',
-&>
+ <& /elements/tr-select-discount_term.html,
+ 'custnum' => $custnum,
+ 'amount_id' => 'amount',
+ &>
% if ( $payby eq 'CARD' ) {
%
@@ -304,8 +257,6 @@ my $payinfo = '';
my $conf = new FS::Conf;
-my $money_char = $conf->config('money_char') || '$';
-
#false laziness w/selfservice make_payment.html shortcut for one-country
my %states = map { $_->state => 1 }
qsearch('cust_main_county', {
@@ -313,43 +264,23 @@ my %states = map { $_->state => 1 }
} );
my @states = sort { $a cmp $b } keys %states;
-my $fee = '';
-my $fee_pkg = '';
-my $fee_display = '';
-my $fee_op = '';
-my $num_payments = scalar($cust_main->cust_pay);
-#handle old cust_main.pm (remove...)
-$num_payments = scalar( @{ [ $cust_main->cust_pay ] } )
- unless defined $num_payments;
-if ( $conf->config('manual_process-pkgpart')
- and ! $conf->exists('manual_process-skip_first') || $num_payments
- )
-{
-
- $fee_display = $conf->config('manual_process-display') || 'add';
- $fee_op = $fee_display eq 'add' ? '+' : '-';
-
- $fee_pkg =
- qsearchs('part_pkg', { pkgpart=>$conf->config('manual_process-pkgpart') } );
-
- #well ->unit_setup or ->calc_setup both call for a $cust_pkg
- # (though ->unit_setup doesn't use it...)
- $fee = $fee_pkg->option('setup_fee')
- if $fee_pkg; #in case.. better than dying with a perl traceback
-
-}
-
my $amount = '';
if ( $balance > 0 ) {
$amount = $balance;
- $amount += $fee
- if $fee && $fee_display eq 'subtract';
+}
+
+my $post_fee_callback = sub {
+ my( $amountref ) = @_;
+
+ return unless $$amountref > 0;
+
+ my $conf = new FS::Conf;
my $cc_surcharge_pct = $conf->config('credit-card-surcharge-percentage');
- $amount += $amount * $cc_surcharge_pct/100 if $cc_surcharge_pct > 0;
+ $$amountref += $$amountref * $cc_surcharge_pct/100 if $cc_surcharge_pct > 0;
- $amount = sprintf("%.2f", $amount);
-}
+ $$amountref = sprintf("%.2f", $$amountref);
+};
my $payunique = "webui-payment-". time. "-$$-". rand() * 2**32;
diff --git a/httemplate/misc/process/cancel_pkg.html b/httemplate/misc/process/cancel_pkg.html
index b2d7bfaa4..a106b845a 100755
--- a/httemplate/misc/process/cancel_pkg.html
+++ b/httemplate/misc/process/cancel_pkg.html
@@ -52,10 +52,15 @@ if ($method eq 'expire' || $method eq 'adjourn' || $method eq 'resume') {
$method = 'unsuspend' if $method eq 'resume';
}
-my $resume_date;
+my $resume_date = '';
+my $options = '';
if ( $method eq 'suspend' ) { #or 'adjourn'
$resume_date = parse_datetime($cgi->param('resume_date'))
if $cgi->param('resume_date');
+
+ $options = { map { $_ => scalar($cgi->param($_)) }
+ qw( suspend_bill no_suspend_bill )
+ };
}
my $cust_pkg = qsearchs( 'cust_pkg', {'pkgnum'=>$pkgnum} );
@@ -88,6 +93,7 @@ my $error = $cust_pkg->$method( 'reason' => $reasonnum,
'last_bill' => $last_bill,
'bill' => $bill,
'svc_fatal' => $svc_fatal,
+ 'options' => $options,
);
if ($error) {
diff --git a/httemplate/pref/pref-process.html b/httemplate/pref/pref-process.html
index bd6bb860a..932cf1a0a 100644
--- a/httemplate/pref/pref-process.html
+++ b/httemplate/pref/pref-process.html
@@ -48,7 +48,8 @@ unless ( $error ) { # if ($access_user) {
my %param = $access_user->options;
#XXX autogen
- my @paramlist = qw( locale menu_position default_customer_view mobile_menu
+ my @paramlist = qw( locale menu_position default_customer_view
+ spreadsheet_format mobile_menu
disable_html_editor disable_enter_submit_onetimecharge
email_address
snom-ip snom-username snom-password
diff --git a/httemplate/pref/pref.html b/httemplate/pref/pref.html
index 8e56355db..9ebf2f1ba 100644
--- a/httemplate/pref/pref.html
+++ b/httemplate/pref/pref.html
@@ -75,6 +75,21 @@ Interface
</SELECT>
</TD>
</TR>
+
+ <TR>
+ <TH ALIGN="right">Spreadsheet download format: </TH>
+ <TD COLSPAN=2>
+ <SELECT NAME="spreadsheet_format">
+% my $xls = $curuser->option('spreadsheet_format') eq 'XLS';
+% my $xlsx = $curuser->option('spreadsheet_format') eq 'XLSX';
+ <OPTION VALUE=""></OPTION>
+ <OPTION VALUE="XLS"<% $xls ? 'SELECTED' : '' %>>XLS (Excel 97/2000/XP)
+ </OPTION>
+ <OPTION VALUE="XLSX"<% $xlsx ? 'SELECTED' : ''%>>XLSX (Excel 2007+)
+ </OPTION>
+ </SELECT>
+ </TD>
+ </TR>
<TR>
<TH ALIGN="right" COLSPAN=1>Disable HTML editor for customer notes: </TH>
diff --git a/httemplate/search/477partV.html b/httemplate/search/477partV.html
index 55ebc0be2..0987fea44 100755
--- a/httemplate/search/477partV.html
+++ b/httemplate/search/477partV.html
@@ -34,9 +34,11 @@ $search_hash{'classnum'} = [ $cgi->param('classnum') ];
$search_hash{report_option} = $cgi->param('partv_report_option')
if $cgi->param('partv_report_option');
-my $sql_query = FS::cust_pkg->search( { %search_hash, 'fcc_line' => 1 });
-$sql_query->{select} = 'DISTINCT substr(zip,1,5) as zip';
-$sql_query->{order_by} = 'ORDER BY substr(zip,1,5)';
+my $sql_query = FS::cust_pkg->search( { %search_hash,
+ 'fcc_line' => 1,
+ 'select_zip5' => 1,
+ }
+ );
my $count_query = delete($sql_query->{'count_query'});
$count_query =~ s/COUNT\(\*\)/count(DISTINCT substr(zip,1,5))/;
$count_query =~ s/ORDER BY [.\w]+//;
diff --git a/httemplate/search/cust_bill_pkg_referral.html b/httemplate/search/cust_bill_pkg_referral.html
new file mode 100644
index 000000000..3cb434caa
--- /dev/null
+++ b/httemplate/search/cust_bill_pkg_referral.html
@@ -0,0 +1,294 @@
+<& elements/search.html,
+ 'title' => emt('Sales with advertising source'),
+ 'name' => emt('line items'),
+ 'query' => $query,
+ 'count_query' => $count_query,
+ 'count_addl' => [
+ ($setup ? $money_char. '%.2f setup' : ()),
+ ($recur ? $money_char. '%.2f recurring' : ()),
+ ($usage ? $money_char. '%.2f usage' : ()),
+ ],
+ 'header' => [
+ emt('Description'),
+ ($setup ? emt('Setup') : ()),
+ ($recur ? emt('Recurring') : ()),
+ ($usage ? emt('Usage') : ()),
+ emt('Invoice'),
+ emt('Invoice date'),
+ emt('Paid'),
+ emt('Payment date'),
+ emt('Pkg. status'),
+ emt('Pkg. class'),
+ '', #report class
+ emt('Cust#'),
+ emt('Customer'),
+ emt('Ad source'),
+ emt('Agent'),
+ ],
+ 'fields' => [
+ 'pkg',
+ ($setup ? money_sub('setup') : ()),
+ ($recur ? money_sub('recur_no_usage') : ()),
+ ($usage ? money_sub('recur_usage') : ()),
+ 'invnum',
+ date_sub('_date'),
+ money_sub('paid'),
+ date_sub('last_pay'),
+ sub {
+ my $cust_pkg = shift->cust_pkg;
+ $cust_pkg ? ucfirst($cust_pkg->status) : '';
+ },
+ 'classname',
+ sub { # report_option
+ my $cust_bill_pkg = shift;
+ my $pkgpart = $cust_bill_pkg->pkgpart_override
+ || $cust_bill_pkg->cust_pkg->pkgpart;
+ if ( !exists($report_classes{$pkgpart}) ) {
+ my $part_pkg = FS::part_pkg->by_key($pkgpart);
+ my %opts = $part_pkg->options;
+ $report_classes{$pkgpart} = [
+ map { /^report_option_(\d+)/ ?
+ $report_option_name{$1} :
+ () }
+ keys %opts
+ ];
+ }
+ join( '<BR>', @{ $report_classes{$pkgpart} });
+ },
+ 'custnum',
+ 'name',
+ 'referral', # from query
+ 'agent',
+ ],
+ 'sort_fields' => [
+ '',
+ ($setup ? 'setup' : ()),
+ ($recur ? 'recur_no_usage' : ()),
+ ($usage ? 'recur_usage' : ()),
+ 'invnum',
+ '_date',
+ 'paid',
+ 'last_pay',
+ '', #package status
+ 'classname',
+ '', #report_option
+ 'custnum',
+ '',
+ 'referral',
+ 'agent',
+ ],
+ 'links' => [
+ '', #package/item desc
+ ('') x $x, #setup/recur/usage
+ $ilink, #invnum
+ $ilink, #invoice date
+ '', #paid amt
+ '', #payment date
+ '', #pkg status
+ '', #classnum
+ '', #report class
+ $clink, #custnum
+ $clink, #customer name
+ '', #referral
+ '', #agent
+ ],
+ #'align' => 'rlrrrc'.FS::UI::Web::cust_aligns(),
+ 'align' => 'l' . ('r' x $x) . 'rcrccccrlll',
+ 'color' => [ ('') x (5 + $x),
+ sub {
+ my $cust_pkg = shift->cust_pkg;
+ $cust_pkg ? ucfirst($cust_pkg->statuscolor) : '';
+ },
+ ('') x 6,
+ ],
+ 'style' => [
+ ('') x (5 + $x),
+ 'b',
+ ('') x 6
+ ],
+&>
+<%init>
+
+die "access denied"
+ unless $FS::CurrentUser::CurrentUser->access_right('Financial reports');
+
+my $conf = new FS::Conf;
+
+my $setup = $cgi->param('setup') ? 1 : 0;
+my $recur = $cgi->param('recur') ? 1 : 0;
+my $usage = $cgi->param('usage') ? 1 : 0;
+
+my $x = $setup + $recur + $usage;
+
+my @select = ( 'cust_bill_pkg.*', 'cust_bill._date' );
+my ($join_cust, $join_pkg ) = ('', '');
+
+#here is the agent virtualization
+my $agentnums_sql =
+ $FS::CurrentUser::CurrentUser->agentnums_sql( 'table' => 'cust_main' );
+
+my($beginning, $ending) = FS::UI::Web::parse_beginning_ending($cgi);
+
+my @where = ( $agentnums_sql,
+ 'cust_bill_pkg.pkgnum != 0', # exclude taxes
+ "cust_bill._date >= $beginning",
+ "cust_bill._date <= $ending",
+ );
+
+my @status_where;
+foreach my $status ($cgi->param('status')) {
+ if ( $status =~ /^([- a-z]+)$/ ) { #"one-time charge"
+ push @status_where, "'$status'";
+ }
+}
+if ( @status_where ) {
+ push @where, '('. FS::cust_pkg->status_sql.
+ ') IN (' . join(',', @status_where) .')';
+}
+
+if ( $cgi->param('agentnum') =~ /^(\d+)$/ ) {
+ push @where, "cust_main.agentnum = $1";
+}
+
+#classnum
+# not specified: all classes
+# 0: empty class
+# N: classnum
+my $use_override = 1; #$cgi->param('use_override');
+if ( $cgi->param('classnum') =~ /^(\d+)$/ ) {
+ my $comparison = '';
+ if ( $1 == 0 ) {
+ $comparison = "IS NULL";
+ } else {
+ $comparison = "= $1";
+ }
+
+ if ( $use_override ) {
+ push @where, "(
+ part_pkg.classnum $comparison AND pkgpart_override IS NULL OR
+ override.classnum $comparison AND pkgpart_override IS NOT NULL
+ )";
+ } else {
+ push @where, "part_pkg.classnum $comparison";
+ }
+}
+
+# report option
+my @report_option = grep /^\d+$/, ( $cgi->param('report_option') );
+if ( @report_option ) {
+ @report_option = map { "'report_option_$_'" } @report_option;
+ push @where, "EXISTS(
+ SELECT 1 FROM part_pkg_option WHERE optionname IN (".
+ join(',', @report_option).") AND (
+ part_pkg_option.pkgpart = cust_pkg.pkgpart AND pkgpart_override IS NULL
+ OR part_pkg_option.pkgpart = pkgpart_override
+ )
+ )";
+}
+
+my $setup_sql =
+ FS::cust_bill_pkg->charged_sql('', '', setuprecur => 'setup');
+my $recur_sql =
+ FS::cust_bill_pkg->charged_sql('', '', setuprecur => 'recur', no_usage => 1);
+my $usage_sql = FS::cust_bill_pkg->usage_sql;
+
+# exclude zero-amount items
+my @orwhere;
+push @orwhere, "(cust_bill_pkg.setup > 0)" if $setup;
+push @orwhere, "($recur_sql > 0)" if $recur;
+push @orwhere, "($usage_sql > 0)" if $usage;
+push @where, '('.join(' OR ', @orwhere).')' if @orwhere;
+
+$join_cust = ' JOIN cust_bill USING ( invnum )
+ LEFT JOIN cust_main USING ( custnum )
+ LEFT JOIN part_referral USING ( refnum )
+ LEFT JOIN agent ON cust_main.agentnum = agent.agentnum
+ ';
+
+$join_pkg .= ' LEFT JOIN cust_pkg USING ( pkgnum )
+ LEFT JOIN part_pkg USING ( pkgpart )
+ LEFT JOIN part_pkg AS override
+ ON pkgpart_override = override.pkgpart
+ LEFT JOIN pkg_class ON '; #...
+
+if ( $use_override ) {
+ # join to whichever pkgpart is appropriate
+ $join_pkg .= '
+ ( pkgpart_override IS NULL AND part_pkg.classnum = pkg_class.classnum )
+ OR ( pkgpart_override IS NOT NULL AND override.classnum = pkg_class.classnum )';
+} else {
+ $join_pkg .= 'part_pkg.classnum = pkg_class.classnum';
+}
+
+my $where = ' WHERE '. join(' AND ', @where);
+
+# setup and recurring only
+my $count_query = "SELECT
+ COUNT(billpkgnum)".
+ ($setup ? ", SUM($setup_sql)" : '').
+ ($recur ? ", SUM($recur_sql)" : '').
+ ($usage ? ", SUM($usage_sql)" : '').
+ " FROM cust_bill_pkg
+ $join_cust
+ $join_pkg
+ $where
+ ";
+
+my $paid_sql = FS::cust_bill_pkg->paid_sql('', '');
+my $last_pay_sql = "SELECT MAX(_date)
+ FROM cust_bill_pay JOIN cust_bill_pay_pkg USING (billpaynum)
+ WHERE cust_bill_pay_pkg.billpkgnum = cust_bill_pkg.billpkgnum";
+
+push @select, 'part_pkg.pkg',
+ 'part_pkg.freq',
+ 'cust_main.custnum',
+ 'cust_main.first',
+ 'cust_main.last',
+ 'cust_main.company',
+ 'part_referral.referral',
+ "($paid_sql) AS paid",
+ "($last_pay_sql) AS last_pay",
+ "($recur_sql) AS recur_no_usage",
+ "($usage_sql) AS recur_usage",
+ 'pkg_class.classname',
+ 'agent.agent',
+ ;
+
+my $query = {
+ 'table' => 'cust_bill_pkg',
+ 'addl_from' => "$join_cust $join_pkg",
+ 'hashref' => {},
+ 'select' => join(",\n", @select ),
+ 'extra_sql' => $where,
+ 'order_by' => 'ORDER BY cust_bill._date, billpkgnum',
+};
+
+my $ilink = [ "${p}view/cust_bill.cgi?", 'invnum' ];
+my $clink = [ "${p}view/cust_main.cgi?", 'custnum' ];
+
+my $conf = new FS::Conf;
+my $money_char = $conf->config('money_char') || '$';
+
+my %report_classes; #cache
+my %report_option_name =
+ map { $_->num => $_->name } qsearch('part_pkg_report_option', {});
+
+# should this be in Mason.pm or something?
+sub money_sub {
+ $conf ||= new FS::Conf;
+ $money_char ||= $conf->config('money_char') || '$';
+ my $field = shift;
+ sub {
+ $money_char . sprintf('%.2f', $_[0]->get($field));
+ };
+}
+
+sub date_sub {
+ my $field = shift;
+ sub {
+ my $value = $_[0]->get($field);
+ $value ? time2str('%b %d %Y', $value) : '';
+ };
+}
+
+</%init>
diff --git a/httemplate/search/cust_main-zip.html b/httemplate/search/cust_main-zip.html
index e87b21474..c317dc36f 100644
--- a/httemplate/search/cust_main-zip.html
+++ b/httemplate/search/cust_main-zip.html
@@ -5,7 +5,7 @@
'count_query' => $count_sql,
'header' => [ 'Zip code', 'Customers', ],
#'fields' => [ 'zip', 'num_cust', ],
- 'links' => [ '', sub { 'somewhere'; } ],
+ #'links' => [ '', sub { 'somewhere'; } ],
)
%>
<%init>
diff --git a/httemplate/search/elements/cust_pay_batch_top.html b/httemplate/search/elements/cust_pay_batch_top.html
index 005b76182..739e65b50 100644
--- a/httemplate/search/elements/cust_pay_batch_top.html
+++ b/httemplate/search/elements/cust_pay_batch_top.html
@@ -103,7 +103,7 @@ Batch is <% $statustext{$status} %><BR>
% }
</%def>
<%shared>
-my $show_gateways = FS::payment_gateway->count("gateway_namespace = 'Business::BatchPayment'");
+my $show_gateways = FS::payment_gateway->count("gateway_namespace = 'Business::BatchPayment' AND disabled IS NULL");
</%shared>
<%init>
my %opt = @_;
diff --git a/httemplate/search/elements/cust_pay_or_refund.html b/httemplate/search/elements/cust_pay_or_refund.html
index dc3cb2a99..c60411107 100755
--- a/httemplate/search/elements/cust_pay_or_refund.html
+++ b/httemplate/search/elements/cust_pay_or_refund.html
@@ -357,6 +357,15 @@ if ( $cgi->param('magic') ) {
$orderby = "LOWER(company || ' ' || last || ' ' || first )";
+ } elsif ( $cgi->param('magic') eq 'batchnum' ) {
+
+ $cgi->param('batchnum') =~ /^(\d+)$/
+ or die "illegal batchnum: ".$cgi->param('batchnum');
+
+ push @search, "batchnum = $1";
+
+ $orderby = "LOWER(company || ' ' || last || ' ' || first )";
+
} else {
die "unknown search magic: ". $cgi->param('magic');
}
diff --git a/httemplate/search/elements/search-xls.html b/httemplate/search/elements/search-xls.html
index 0b5636c0e..09dbe46e0 100644
--- a/httemplate/search/elements/search-xls.html
+++ b/httemplate/search/elements/search-xls.html
@@ -7,14 +7,20 @@ my $header = $args{'header'};
my $rows = $args{'rows'};
my %opt = %{ $args{'opt'} };
+my $override = scalar(@$rows) >= 65536 ? 'XLSX' : '';
+
+my $format = $FS::CurrentUser::CurrentUser->spreadsheet_format($override);
+
+my $filename = $opt{'name'} || PL($opt{'name_singular'});
+$filename .= $format->{extension};
+
#http_header('Content-Type' => 'application/excel' ); #eww
#http_header('Content-Type' => 'application/msexcel' ); #alas
#http_header('Content-Type' => 'application/x-msexcel' ); #?
#http://support.microsoft.com/kb/199841
-http_header('Content-Type' => 'application/vnd.ms-excel' );
-http_header('Content-Disposition' =>
- 'attachment;filename="'.($opt{'name'} || PL($opt{'name_singular'}) ).'.xls"');
+http_header('Content-Type' => $format->{mime_type} );
+http_header('Content-Disposition' => qq!attachment;filename="$filename"! );
#http://support.microsoft.com/kb/812935
#http://support.microsoft.com/kb/323308
@@ -22,8 +28,8 @@ $HTML::Mason::Commands::r->headers_out->{'Cache-control'} = 'max-age=0';
my $data = '';
my $XLS = new IO::Scalar \$data;
-my $workbook = Spreadsheet::WriteExcel->new($XLS)
- or die "Error opening .xls file: $!";
+my $workbook = $format->{class}->new($XLS)
+ or die "Error opening Excel file: $!";
my $worksheet = $workbook->add_worksheet(substr($opt{'title'},0,31));
@@ -42,14 +48,18 @@ my $default_format = $workbook->add_format(locked => 0);
my %money_format;
my $money_char = FS::Conf->new->config('money_char') || '$';
+my %date_format;
+xl_parse_date_init();
+
my $writer = sub {
# Wrapper for $worksheet->write.
# Do any massaging of the value/format here.
my ($r, $c, $value, $format) = @_;
- if ( $value =~ /^\Q$money_char\E(\d+\.?\d*)$/ ) {
+ if ( $value =~ /^\Q$money_char\E(-?\d+\.?\d*)$/ ) {
# Currency: strip the symbol, clone the requested format,
# and format it for currency
$value = $1;
+# warn "formatting $value as money\n";
if ( !exists($money_format{$format}) ) {
$money_format{$format} = $workbook->add_format();
$money_format{$format}->copy($format);
@@ -57,6 +67,22 @@ my $writer = sub {
}
$format = $money_format{$format};
}
+ elsif ( $value =~ /^([A-Z][a-z]{2}) (\d{2}) (\d{4})$/ ) {
+ # Date: convert the value to an Excel date number and set
+ # the format
+ $value = xl_parse_date($value);
+# warn "formatting $value as date\n";
+ if ( !exists($date_format{$format}) ) {
+ $date_format{$format} = $workbook->add_format();
+ $date_format{$format}->copy($format);
+ $date_format{$format}->set_num_format('mmm dd yyyy');
+ }
+ $format = $date_format{$format};
+ }
+ else {
+ # String: replace line breaks with newlines
+ $value =~ s/<BR>/\n/gi;
+ }
$worksheet->write($r, $c, $value, $format);
};
diff --git a/httemplate/search/pay_batch.cgi b/httemplate/search/pay_batch.cgi
index 05415f36e..aeaa012f4 100755
--- a/httemplate/search/pay_batch.cgi
+++ b/httemplate/search/pay_batch.cgi
@@ -14,12 +14,13 @@
'Type',
'First Download',
'Last Upload',
- 'Items',
- 'Unresolved',
- 'Amount',
+ '', # requests
+ '', # req amt
+ '', # payments
+ '', # pay amt
'Status',
],
- 'align' => 'rcllrrc',
+ 'align' => 'rcllrrrrc',
'fields' => [ 'batchnum',
sub {
FS::payby->shortname(shift->payby);
@@ -47,33 +48,44 @@
}
},
sub {
- FS::cust_pay_batch->count(
- 'batchnum = '.$_[0]->batchnum
- )
+ my $c = FS::cust_pay_batch->count('batchnum = '.$_[0]->batchnum);
+ $c ? "$c requested" : ''
},
sub {
- FS::cust_pay_batch->count(
- 'status is null and batchnum = '.
- $_[0]->batchnum
- )
- },
- sub {
my $st = "SELECT SUM(amount) from cust_pay_batch WHERE batchnum=" . shift->batchnum;
my $sth = dbh->prepare($st)
- or die dbh->errstr. "doing $st";
+ or die dbh->errstr. "doing $st";
$sth->execute
- or die "Error executing \"$st\": ". $sth->errstr;
- $sth->fetchrow_arrayref->[0];
- },
+ or die "Error executing \"$st\": ". $sth->errstr;
+ my $total = $sth->fetchrow_arrayref->[0];
+ $total ? $money_char.sprintf('%.2f',$total) : '';
+ },
+ sub {
+ my $c = FS::cust_pay->count('batchnum = '.$_[0]->batchnum);
+ $c ? "$c paid" : ''
+ },
+ sub {
+ my $st = "SELECT SUM(paid) from cust_pay WHERE batchnum=" . shift->batchnum;
+ my $sth = dbh->prepare($st)
+ or die dbh->errstr. "doing $st";
+ $sth->execute
+ or die "Error executing \"$st\": ". $sth->errstr;
+ my $total = $sth->fetchrow_arrayref->[0];
+ $total ? $money_char.sprintf('%.2f',$total) : '';
+ },
sub {
$statusmap{shift->status};
},
],
'links' => [
- $link,
+ '',
'',
- sub { shift->status eq 'O' ? $link : '' },
- sub { shift->status eq 'I' ? $link : '' },
+ sub { shift->status eq 'O' ? $cpb_link : '' },
+ sub { shift->status eq 'I' ? $cpb_link : '' },
+ $cpb_link,
+ $cpb_link,
+ $pay_link,
+ $pay_link,
],
'size' => [
'',
@@ -88,9 +100,42 @@
sub { shift->status eq 'I' ? "b" : '' },
],
'html_init' => $html_init,
+ 'html_foot' => include('.upload_incoming'),
)
-
%>
+<%def .upload_incoming>
+% if ( FS::payment_gateway->count("gateway_namespace = 'Business::BatchPayment' AND disabled IS NULL") > 0 ) {
+<& /elements/form-file_upload.html,
+ name => 'FileUpload',
+ action => $p.'misc/upload-batch.cgi',
+ num_files => 1,
+ fields => [ 'gatewaynum' ],
+ message => 'Incoming batch uploaded.',
+&>
+<BR>
+<BR>
+Upload incoming batch from gateway
+<& /elements/select-table.html,
+ table => 'payment_gateway',
+ field => 'gatewaynum',
+ name_col => 'label',
+ value_col => 'gatewaynum',
+ order_by => 'ORDER BY gatewaynum',
+ empty_label => ' ',
+ hashref =>
+ { 'gateway_namespace' => 'Business::BatchPayment',
+ 'disabled' => '' },
+&>
+<BR>
+<& '/elements/file-upload.html',
+ field => 'file',
+ label => 'Filename',
+ no_table => 1,
+&>
+<INPUT TYPE="submit" VALUE="Upload">
+</FORM>
+% }
+</%def>
<%init>
die "access denied"
@@ -134,11 +179,14 @@ push @where,
my $extra_sql = scalar(@where) ? 'WHERE ' . join(' AND ', @where) : '';
-my $link = [ "${p}search/cust_pay_batch.cgi?dcln=1;batchnum=", 'batchnum' ];
+my $cpb_link = [ "${p}search/cust_pay_batch.cgi?dcln=1;batchnum=", 'batchnum' ];
+my $pay_link = [ "${p}search/cust_pay.html?magic=batchnum;batchnum=", 'batchnum' ];
my $resolved = $cgi->param('resolved') || 0;
$cgi->param('resolved' => !$resolved);
my $html_init = '<A HREF="' . $cgi->self_url . '"><I>'.
($resolved ? 'Hide' : 'Show') . ' resolved batches</I></A><BR>';
+my $money_char = FS::Conf->new->config('money_char') || '$';
+
</%init>
diff --git a/httemplate/search/report_cust_bill_pkg_referral.html b/httemplate/search/report_cust_bill_pkg_referral.html
new file mode 100644
index 000000000..ff2caa1fa
--- /dev/null
+++ b/httemplate/search/report_cust_bill_pkg_referral.html
@@ -0,0 +1,61 @@
+<% include('/elements/header.html', 'Sales Report with Advertising Source' ) %>
+
+<FORM ACTION="cust_bill_pkg_referral.html" METHOD="GET">
+
+<TABLE>
+
+<& /elements/tr-input-beginning_ending.html &>
+
+<& /elements/tr-select-agent.html,
+ 'label' => 'For agent: ',
+ 'disable_empty' => 0,
+ 'empty_label' => 'all',
+&>
+
+<& /elements/tr-select-cust_pkg-status.html,
+ 'label' => 'Package status',
+ 'multiple' => 1,
+ 'disable_empty' => 1,
+&>
+
+<& /elements/tr-select-pkg_class.html,
+ 'pre_options' => [ '' => 'all', '0' => '(empty class)' ],
+ 'disable_empty' => 1,
+&>
+
+<& /elements/tr-select-table.html,
+ 'label' => 'Report classes',
+ 'table' => 'part_pkg_report_option',
+ 'name_col' => 'name',
+ 'hashref' => { disabled => '' },
+ 'element_name' => 'report_option',
+ 'multiple' => 1,
+&>
+
+<TR>
+ <TD ALIGN="right"><INPUT TYPE="checkbox" NAME="setup" VALUE="1" CHECKED></TD>
+ <TD>Show setup/one-time fees</TD>
+</TR>
+
+<TR>
+ <TD ALIGN="right"><INPUT TYPE="checkbox" NAME="recur" VALUE="1" CHECKED></TD>
+ <TD>Show recurring fees</TD>
+</TR>
+
+<TR>
+ <TD ALIGN="right"><INPUT TYPE="checkbox" NAME="usage" VALUE="1" CHECKED></TD>
+ <TD>Show usage charges</TD>
+</TR>
+
+</TABLE>
+
+<BR><INPUT TYPE="submit" VALUE="Display">
+</FORM>
+
+<% include('/elements/footer.html') %>
+<%init>
+
+die "access denied"
+ unless $FS::CurrentUser::CurrentUser->access_right('Financial reports');
+
+</%init>
diff --git a/httemplate/search/report_tax-xls.cgi b/httemplate/search/report_tax-xls.cgi
index 1c278dfd1..f19f85aaa 100755
--- a/httemplate/search/report_tax-xls.cgi
+++ b/httemplate/search/report_tax-xls.cgi
@@ -1,9 +1,25 @@
<% $data %>
<%init>
+my $htmldoc = include('report_tax.cgi');
+
+my ($title) = ($htmldoc =~ /<title>\s*(.*)\s*<\/title>/i);
+
+# do this first so we can override the format if it's too many rows
+# attribs option: how to locate the table? It's the only one with class="grid".
+my $te = HTML::TableExtract->new(attribs => {class => 'grid'});
+$te->parse($htmldoc);
+my $table = $te->first_table_found;
+
+my $override = ($table->row_count >= 65536 ? 'XLSX' : '');
+my $format = $FS::CurrentUser::CurrentUser->spreadsheet_format($override);
+my $filename = 'report_tax'.$format->{extension};
+
+http_header('Content-Type' => $format->{mime_type});
+http_header('Content-Disposition' => qq!attachment;filename="$filename"! );
my $data = '';
my $XLS = new IO::Scalar \$data;
-my $workbook = Spreadsheet::WriteExcel->new($XLS)
+my $workbook = $format->{class}->new($XLS)
or die "Error opening .xls file: $!";
# hardcoded formats, this could be handled better
@@ -66,15 +82,6 @@ foreach (keys(%format)) {
}
my $ws = $workbook->add_worksheet('taxreport');
-my $htmldoc = include('report_tax.cgi');
-
-my ($title) = ($htmldoc =~ /<title>\s*(.*)\s*<\/title>/i);
-
-# attribs option: how to locate the table? It's the only one with class="grid".
-my $te = HTML::TableExtract->new(attribs => {class => 'grid'});
-$te->parse($htmldoc);
-my $table = $te->first_table_found;
-
my @sheet;
$sheet[0][0] = {
text => $title,
@@ -148,6 +155,4 @@ for my $x (0..scalar(@widths)-1) {
$workbook->close;
-http_header('Content-Type' => 'application/vnd.ms-excel');
-http_header('Content-Disposition' => 'attachment;filename="report_tax.xls"');
</%init>
diff --git a/httemplate/view/cust_main/packages/status.html b/httemplate/view/cust_main/packages/status.html
index 4aec90efb..e9017745b 100644
--- a/httemplate/view/cust_main/packages/status.html
+++ b/httemplate/view/cust_main/packages/status.html
@@ -73,7 +73,12 @@
<% pkg_status_row_changed( $cust_pkg, %opt, 'colspan'=>$colspan ) %>
<% pkg_status_row_if( $cust_pkg, $last_bill_or_renewed, 'last_bill', %opt, curuser=>$curuser ) %>
-% if ( $part_pkg->option('suspend_bill', 1) ) {
+% if ( $cust_pkg->option('suspend_bill', 1)
+% || ( $part_pkg->option('suspend_bill', 1)
+% && ! $cust_pkg->option('no_suspend_bill',1)
+% )
+% )
+% {
<% pkg_status_row_if( $cust_pkg, emt('Next bill'), 'bill', %opt, curuser=>$curuser ) %>
% }
<% pkg_status_row_if( $cust_pkg, emt('Will resume'), 'resume', %opt, curuser=>$curuser ) %>
diff --git a/httemplate/view/cust_main_statement-pdf.cgi b/httemplate/view/cust_main_statement-pdf.cgi
index 7a0e19838..7c2c20799 100755
--- a/httemplate/view/cust_main_statement-pdf.cgi
+++ b/httemplate/view/cust_main_statement-pdf.cgi
@@ -23,13 +23,17 @@ my $cust_main = qsearchs({
'extra_sql' => ' AND '. $FS::CurrentUser::CurrentUser->agentnums_sql,
});
die "Customer #$custnum not found!" unless $cust_main;
+my $cust_bill = ($cust_main->cust_bill)[-1]
+ or die "Customer #$custnum has no invoices!";
my $cust_statement = FS::cust_statement->new({
'custnum' => $custnum,
- 'statementnum' => 'ALL', #magic
+# 'statementnum' => 'ALL', #magic
+ 'invnum' => $cust_bill->invnum,
'_date' => time,
});
+
my $pdf = $cust_statement->print_pdf( '', $templatename );
http_header('Content-Type' => 'application/pdf' );