summaryrefslogtreecommitdiff
path: root/httemplate
diff options
context:
space:
mode:
Diffstat (limited to 'httemplate')
-rw-r--r--httemplate/edit/cust_main-contacts.html27
-rw-r--r--httemplate/edit/elements/edit.html2
-rw-r--r--httemplate/edit/process/cust_main-contacts.html2
-rw-r--r--httemplate/elements/change_password.html15
-rw-r--r--httemplate/elements/contact.html43
-rw-r--r--httemplate/elements/header.html1
-rw-r--r--httemplate/elements/menu.html4
-rw-r--r--httemplate/elements/validate_password.html55
-rw-r--r--httemplate/elements/validate_password_js.html71
-rw-r--r--httemplate/misc/edge_browser_check-fail_notice.html25
-rw-r--r--httemplate/misc/edge_browser_check-header.html36
-rw-r--r--httemplate/misc/edge_browser_check-iframe.html34
-rw-r--r--httemplate/misc/process/change-password.html6
-rw-r--r--httemplate/search/future_autobill.html210
-rw-r--r--httemplate/search/report_future_autobill.html57
-rw-r--r--httemplate/view/cust_main/contacts_new.html14
16 files changed, 402 insertions, 200 deletions
diff --git a/httemplate/edit/cust_main-contacts.html b/httemplate/edit/cust_main-contacts.html
index 3783cb9e9..abef7505d 100644
--- a/httemplate/edit/cust_main-contacts.html
+++ b/httemplate/edit/cust_main-contacts.html
@@ -5,18 +5,20 @@ this one isn't being maintained well. :/
</%doc>
- <SCRIPT>
-function checkPasswordValidation(fieldid) {
- var validationResult = document.getElementById(fieldid+'_result').innerHTML;
- if (validationResult.match(/Password valid!/)) {
- return true;
+<SCRIPT>
+ function checkPasswordValidation(fieldid) {
+ var validationResult = document.getElementById(fieldid+'_result').innerHTML;
+ if (validationResult.match(/Password valid!/)) {
+ return true;
+ }
+ else {
+ return false;
+ }
}
- else {
- return false;
- }
-}
</SCRIPT>
+<& '/elements/validate_password_js.html', &>
+
<& elements/edit.html,
'name_singular' => 'customer contacts', #yes, we're editing all of them
'table' => 'cust_main',
@@ -58,6 +60,13 @@ function checkPasswordValidation(fieldid) {
my $curuser = $FS::CurrentUser::CurrentUser;
my $conf = new FS::Conf;
+if ( $cgi->param('redirect') ) {
+ my $session = $cgi->param('redirect');
+ my $pref = $curuser->option("redirect$session");
+ die "unknown redirect session $session\n" unless length($pref);
+ $cgi = new CGI($pref);
+}
+
my $custnum;
if ( $cgi->param('error') ) {
$custnum = scalar($cgi->param('custnum'));
diff --git a/httemplate/edit/elements/edit.html b/httemplate/edit/elements/edit.html
index 8ba703a2f..b7f2e7adb 100644
--- a/httemplate/edit/elements/edit.html
+++ b/httemplate/edit/elements/edit.html
@@ -669,7 +669,7 @@ Example:
var newrow = <% include(@layer_opt, html_only=>1) |js_string %>;
% #until the rest have html/js_only
-% if ( ($type eq 'selectlayers') || ($type eq 'selectlayersx') || ($type =~ /^select-cgp_rule_/) ) {
+% if ( ($type eq 'selectlayers') || ($type eq 'selectlayersx') || ($type =~ /^select-cgp_rule_/) || ($type eq 'contact') ) {
var newfunc = <% include(@layer_opt, js_only=>1) |js_string %>;
% } else {
var newfunc = '';
diff --git a/httemplate/edit/process/cust_main-contacts.html b/httemplate/edit/process/cust_main-contacts.html
index 5b8319f5a..6b7f1c2db 100644
--- a/httemplate/edit/process/cust_main-contacts.html
+++ b/httemplate/edit/process/cust_main-contacts.html
@@ -8,7 +8,7 @@
</%doc>
<% include('elements/process.html',
'table' => 'cust_main',
- 'error_redirect' => popurl(3). 'edit/cust_main-contacts.html?',
+ 'error_redirect' => popurl(3). 'edit/cust_main-contacts.html',
'agent_virt' => 1,
'skip_process' => 1, #we don't want to make any changes to cust_main
'precheck_callback' => $precheck_callback,
diff --git a/httemplate/elements/change_password.html b/httemplate/elements/change_password.html
index 7d95e19dc..65b7d8502 100644
--- a/httemplate/elements/change_password.html
+++ b/httemplate/elements/change_password.html
@@ -11,9 +11,9 @@
% if (!$opt{'no_label_display'}) {
<A ID="<%$pre%>link" HREF="javascript:void(0)" onclick="<%$pre%>toggle(true)">(<% emt( $change_title ) %>)</A>
% }
-<DIV ID="<%$pre%>form" CLASS="passwordbox">
+<DIV ID="<%$pre%>div" CLASS="passwordbox">
% if (!$opt{'noformtag'}) {
- <FORM METHOD="POST" ACTION="<%$fsurl%>misc/process/change-password.html" onsubmit="return checkPasswordValidation()">
+ <FORM ID="<%$pre%>form" METHOD="POST" ACTION="<%$fsurl%>misc/process/change-password.html" onsubmit="return <%$pre%>checkPasswordValidation()">
% }
<% $change_id_input %>
@@ -44,11 +44,8 @@ function <%$pre%>toggle(toggle, clear) {
if (clear) {
document.getElementById('<%$pre%>password').value = '';
document.getElementById('<%$pre%>password_result').innerHTML = '';
-% if ($opt{'contact_num'}) {
- document.getElementById('<% $opt{'pre_pwd_field_label'} %>selfservice_access').value = 'Y';
-% }
}
- document.getElementById('<%$pre%>form').style.display =
+ document.getElementById('<%$pre%>div').style.display =
toggle ? 'inline-block' : 'none';
% if (!$opt{'no_label_display'}) {
document.getElementById('<%$pre%>link').style.display =
@@ -56,7 +53,7 @@ function <%$pre%>toggle(toggle, clear) {
% }
}
-function checkPasswordValidation() {
+function <%$pre%>checkPasswordValidation(resultId) {
var validationResult = document.getElementById('<%$pre%>password_result').innerHTML;
if (validationResult.match(/Password valid!/)) {
return true;
@@ -83,8 +80,8 @@ if ($opt{'svc_acct'}) {
}
elsif ($opt{'contact_num'}) {
$change_id_input = '
- <INPUT TYPE="hidden" NAME="'.$opt{'pre_pwd_field_label'}.'contactnum" VALUE="' . $opt{'contact_num'} . '">
- <INPUT TYPE="hidden" NAME="'.$opt{'pre_pwd_field_label'}.'custnum" VALUE="' . $opt{'custnum'} . '">
+ <INPUT TYPE="hidden" NAME="contactnum" VALUE="' . $opt{'contact_num'} . '">
+ <INPUT TYPE="hidden" NAME="custnum" VALUE="' . $opt{'custnum'} . '">
';
$pre .= $opt{'pre_pwd_field_label'};
}
diff --git a/httemplate/elements/contact.html b/httemplate/elements/contact.html
index 7b6c853d4..909ff7893 100644
--- a/httemplate/elements/contact.html
+++ b/httemplate/elements/contact.html
@@ -1,4 +1,6 @@
-% unless ( $opt{'js_only'} ) {
+% if ( $opt{'js_only'} ) {
+<% $js %>
+% } else {
<INPUT TYPE="hidden" NAME="<%$name%>" ID="<%$id%>" VALUE="<% $curr_value %>">
@@ -75,25 +77,8 @@
VALUE = ""
placeholder = "<% $value |h %>"
>
-% my $contactnum = $curr_value ? $curr_value : '0';
- <& '/elements/validate_password.html',
- 'fieldid' => "changepw".$id."_password",
- 'svcnum' => '',
- 'contactnum' => $contactnum,
- 'submitid' => "submit",
- &>
-
- <SCRIPT TYPE="text/javascript">
- var selfService = document.getElementById("<%$id%>_selfservice_access").value;
-
- if (selfService !== "Y") { document.getElementById("changepw<%$id%>_password").disabled = 'true'; }
- document.getElementById("<%$id%>_selfservice_access").onchange = function() {
- if (this.value == "P" || this.value == "E" || this.value =="Y") {
- document.getElementById("changepw<%$id%>_password").disabled = '';
- }
- else { document.getElementById("changepw<%$id%>_password").disabled = 'true'; }
- return false;
- }
+ <SCRIPT>
+ <% $js %>
</SCRIPT>
% } elsif ( $field eq 'invoice_dest' || $field eq 'message_dest' ) {
% my $curr_value = $cgi->param($name . '_' . $field);
@@ -118,7 +103,7 @@
<BR>
<FONT SIZE="-1"><% $label{$field} %></FONT>
% if ( $field eq 'password' ) {
- <div id="changepw<%$id%>_<%$field%>_result"></div>
+ <DIV ID="changepw<%$id%>_<%$field%>_result" STYLE="font-size: smaller"></DIV>
% }
</TD>
% }
@@ -138,6 +123,7 @@ my $name = $opt{'element_name'} || $opt{'field'} || 'contactnum';
my $id = $opt{'id'} || 'contactnum';
my $curr_value = $opt{'curr_value'} || $opt{'value'};
+my $contactnum = $curr_value ? $curr_value : '0';
my $onchange = '';
if ( $opt{'onchange'} ) {
@@ -205,4 +191,19 @@ $label{'comment'} = 'Comment';
my @fields = $opt{'name_only'} ? qw( first last ) : keys %label;
+my $js = qq(
+ add_password_validation('changepw$id\_password', 'submit', '', '$contactnum');
+
+ var selfService = document.getElementById("$id\_selfservice_access").value;
+
+ if (selfService !== "Y") { document.getElementById("changepw$id\_password").disabled = 'true'; }
+ document.getElementById("$id\_selfservice_access").onchange = function() {
+ if (this.value == "P" || this.value == "E" || this.value =="Y") {
+ document.getElementById("changepw$id\_password").disabled = '';
+ }
+ else { document.getElementById("changepw$id\_password").disabled = 'true'; }
+ return false;
+ }
+);
+
</%init>
diff --git a/httemplate/elements/header.html b/httemplate/elements/header.html
index c6b10e301..6df45fb07 100644
--- a/httemplate/elements/header.html
+++ b/httemplate/elements/header.html
@@ -4,3 +4,4 @@
% } else {
<& header-full.html, @_ &>
% }
+<& /misc/edge_browser_check-header.html &>
diff --git a/httemplate/elements/menu.html b/httemplate/elements/menu.html
index eb065b668..cae0cdbfb 100644
--- a/httemplate/elements/menu.html
+++ b/httemplate/elements/menu.html
@@ -418,7 +418,9 @@ if( $curuser->access_right('Financial reports') ) {
$report_financial{'Customer Accounting Summary'} = [ $fsurl.'search/report_customer_accounting_summary.html', 'Customer accounting summary report' ];
- $report_financial{'Upcoming Auto-Bill Transactions'} = [ $fsurl.'search/report_future_autobill.html', 'Upcoming auto-bill transactions' ];
+ if ( my $report_title = FS::cust_payby->future_autobill_report_title ) {
+ $report_financial{$report_title} = [ $fsurl.'search/report_future_autobill.html', "$report_title for customers with automatic payment methods (by date)" ];
+ }
} elsif($curuser->access_right('Receivables report')) {
diff --git a/httemplate/elements/validate_password.html b/httemplate/elements/validate_password.html
index 73c0db281..6aada2fee 100644
--- a/httemplate/elements/validate_password.html
+++ b/httemplate/elements/validate_password.html
@@ -14,59 +14,10 @@ should be the input id plus '_result'.
</%doc>
-<& '/elements/xmlhttp.html',
- 'url' => $p.'misc/xmlhttp-validate_password.html',
- 'subs' => [ 'validate_password' ],
- 'method' => 'POST', # important not to put passwords in url
-&>
-<SCRIPT>
-function add_password_validation (fieldid, submitid) {
- var inputfield = document.getElementById(fieldid);
- inputfield.onkeydown = function(e) {
- var key;
- if (window.event) { key = window.event.keyCode; }
- else { key = e.which; } // for ff browsers
- // some browsers allow the enter key to submit a form even if the submit button is disabled
- // below prevents enter key from submiting form if password has not been validated.
- if (key == '13') {
- var check = checkPasswordValidation(fieldid);
- return check;
- }
- }
- inputfield.onkeyup = function () {
- var fieldid = this.id+'_result';
- var resultfield = document.getElementById(fieldid);
- if (this.value) {
- resultfield.innerHTML = '<SPAN STYLE="color: blue;">Validating password...</SPAN>';
- validate_password('fieldid',fieldid,'svcnum','<% $opt{'svcnum'} %>','contactnum','<% $opt{'contactnum'} %>','password',this.value,
- function (result) {
- result = JSON.parse(result);
- var resultfield = document.getElementById(result.fieldid);
- if (resultfield) {
- var errorimg = '<IMG SRC="<% $p %>images/error.png" style="width: 1em; display: inline-block; padding-right: .5em">';
- var validimg = '<IMG SRC="<% $p %>images/tick.png" style="width: 1em; display: inline-block; padding-right: .5em">';
- if (result.valid) {
- resultfield.innerHTML = validimg+'<SPAN STYLE="color: green;">Password valid!</SPAN>';
- if (submitid){ document.getElementById(submitid).disabled = false; }
- } else if (result.error) {
- resultfield.innerHTML = errorimg+'<SPAN STYLE="color: red;">'+result.error+'</SPAN>';
- if (submitid){ document.getElementById(submitid).disabled = true; }
- } else {
- result.syserror = result.syserror || 'Server error';
- resultfield.innerHTML = errorimg+'<SPAN STYLE="color: red;">'+result.syserror+'</SPAN>';
- if (submitid){ document.getElementById(submitid).disabled = true; }
- }
- }
- }
- );
- } else {
- resultfield.innerHTML = '';
- if (submitid){ document.getElementById(submitid).disabled = false; }
- }
- };
-}
+<& '/elements/validate_password_js.html', %opt &>
-add_password_validation('<% $opt{'fieldid'} %>', '<% $opt{'submitid'} %>');
+<SCRIPT>
+ add_password_validation('<% $opt{'fieldid'} %>', '<% $opt{'submitid'} %>', '<% $opt{'svcnum'} %>', '<% $opt{'contactnum'} %>');
</SCRIPT>
<%init>
diff --git a/httemplate/elements/validate_password_js.html b/httemplate/elements/validate_password_js.html
new file mode 100644
index 000000000..64db0a97b
--- /dev/null
+++ b/httemplate/elements/validate_password_js.html
@@ -0,0 +1,71 @@
+<%doc>
+
+JavaScript to perform password validation
+
+ <& '/elements/validate_password_js.html',
+ contactnum => $contactnum,
+ svcnum => $svcnum
+ &>
+
+The ID of the input field can be anything; the ID of the DIV in which to display results
+should be the input id plus '_result'.
+
+</%doc>
+
+<& '/elements/xmlhttp.html',
+ 'url' => $p.'misc/xmlhttp-validate_password.html',
+ 'subs' => [ 'validate_password' ],
+ 'method' => 'POST', # important not to put passwords in url
+&>
+<SCRIPT>
+function add_password_validation (fieldid, submitid, svcnum, contactnum) {
+ var inputfield = document.getElementById(fieldid);
+ inputfield.onkeydown = function(e) {
+ var key;
+ if (window.event) { key = window.event.keyCode; }
+ else { key = e.which; } // for ff browsers
+ // some browsers allow the enter key to submit a form even if the submit button is disabled
+ // below prevents enter key from submiting form if password has not been validated.
+ if (key == '13') {
+ var check = checkPasswordValidation(fieldid);
+ return check;
+ }
+ }
+ inputfield.onkeyup = function () {
+ var fieldid = this.id+'_result';
+ var resultfield = document.getElementById(fieldid);
+ if (this.value) {
+ resultfield.innerHTML = '<SPAN STYLE="color: blue;">Validating password...</SPAN>';
+ validate_password('fieldid',fieldid,'svcnum','<% $opt{'svcnum'} %>','contactnum', contactnum,'password',this.value,
+ function (result) {
+ result = JSON.parse(result);
+ var resultfield = document.getElementById(result.fieldid);
+ if (resultfield) {
+ var errorimg = '<IMG SRC="<% $p %>images/error.png" style="width: 1em; display: inline-block; padding-right: .5em">';
+ var validimg = '<IMG SRC="<% $p %>images/tick.png" style="width: 1em; display: inline-block; padding-right: .5em">';
+ if (result.valid) {
+ resultfield.innerHTML = validimg+'<SPAN STYLE="color: green;">Password valid!</SPAN>';
+ if (submitid){ document.getElementById(submitid).disabled = false; }
+ } else if (result.error) {
+ resultfield.innerHTML = errorimg+'<SPAN STYLE="color: red;">'+result.error+'</SPAN>';
+ if (submitid){ document.getElementById(submitid).disabled = true; }
+ } else {
+ result.syserror = result.syserror || 'Server error';
+ resultfield.innerHTML = errorimg+'<SPAN STYLE="color: red;">'+result.syserror+'</SPAN>';
+ if (submitid){ document.getElementById(submitid).disabled = true; }
+ }
+ }
+ }
+ );
+ } else {
+ resultfield.innerHTML = '';
+ if (submitid){ document.getElementById(submitid).disabled = false; }
+ }
+ };
+}
+
+</SCRIPT>
+
+<%init>
+my %opt = @_;
+</%init> \ No newline at end of file
diff --git a/httemplate/misc/edge_browser_check-fail_notice.html b/httemplate/misc/edge_browser_check-fail_notice.html
new file mode 100644
index 000000000..fb42ffe8e
--- /dev/null
+++ b/httemplate/misc/edge_browser_check-fail_notice.html
@@ -0,0 +1,25 @@
+<& /elements/header.html, "Edge browser bug" &>
+
+<div id="edgebug" style="border: solid 1px #888; border-radius: 4px; margin: 5em; max-width: 400px; text-align: left; padding: 0 1em; background-color: #ffe; box-shadow: 2px 2px 4px">
+ <div style="text-align: center; font-size: 3em; color: #933; text-shadow: 1px 1px 2px black;">
+ &#9888;
+ </div>
+ <h4 style="border-bottom: solid 1px #888; margin: 1em 0; text-align: center;">
+ Edge Browser Bug
+ </h4>
+ <p>
+ Your copy of Microsoft Edge has a data corrupting bug.
+ </p>
+ <p>
+ Microsoft fixed this bug with the <b>July RS4 Windows 10 Update</b>.
+ Please update your copy of Windows.
+ </p>
+ <p>
+ Alternatively, you may choose to use
+ <a href="https://mozilla.org/en-US/firefox/new/">Mozilla Firefox</a>
+ or <a href="https://chrome.google.com">Google Chrome</a>. They
+ are not affected by this bug.
+ </p>
+</div>
+
+<& /elements/footer.html &> \ No newline at end of file
diff --git a/httemplate/misc/edge_browser_check-header.html b/httemplate/misc/edge_browser_check-header.html
new file mode 100644
index 000000000..a88962be9
--- /dev/null
+++ b/httemplate/misc/edge_browser_check-header.html
@@ -0,0 +1,36 @@
+% if ( $force_redirect ) {
+ <script type="text/javascript">
+ if ( <% $DEBUG %> || /Edge\/17\.17134/.test( navigator.userAgent )) {
+ if ( window.location.href.indexOf("fail_notice") == -1 ) {
+ window.location.href = "<% $fsurl %>misc/edge_browser_check-fail_notice.html";
+ }
+ }
+ </script>
+% } elsif ( $do_check ) {
+ <iframe id="edge_browser_check_iframe" style="display:none;"></iframe>
+ <script type="text/javascript">
+ if ( <% $DEBUG %> || /Edge\/17\.17134/.test( navigator.userAgent )) {
+ $("#edge_browser_check_iframe").attr(
+ 'src',
+ '<% $fsurl %>misc/edge_browser_check-iframe.html?edge_browser_check=1'
+ );
+ }
+ </script>
+% }
+<%init>
+my $curuser = $FS::CurrentUser::CurrentUser;
+my $session = $FS::CurrentUser::CurrentSession;
+my $sessionkey = $session->sessionkey if $session;
+
+my $cgi = FS::UID::cgi();
+my $DEBUG = 0;
+
+my $do_check = 0;
+$do_check = 1
+ if $curuser
+ && !$cgi->param('edge_browser_check')
+ && $sessionkey
+ && $curuser->get_pref('edge_bug_vulnerable') ne $sessionkey;
+
+my $force_redirect = $curuser->get_pref('edge_bug_vulnerable') eq 'Y' ? 1 : 0;
+</%init>
diff --git a/httemplate/misc/edge_browser_check-iframe.html b/httemplate/misc/edge_browser_check-iframe.html
new file mode 100644
index 000000000..61ae9a0bd
--- /dev/null
+++ b/httemplate/misc/edge_browser_check-iframe.html
@@ -0,0 +1,34 @@
+<form id="canary-form" action="<% $fsurl %>misc/edge_browser_check-iframe.html" method="POST">
+<input type="text" id="canary-result" value="<% scalar $cgi->param('edge_browser_canary') %>">
+<select name="edge_browser_canary">
+ <option>test
+ <option>test
+</select>
+<input id="canary-submit" type="submit">
+</form>
+
+<script type="text/javascript" src="<% $fsurl %>elements/jquery.js"></script>
+<script type="text/javascript">
+ $( function() {
+ if ( ! $("#canary-result").val() ) {
+ $("#canary-form").submit();
+ }
+ });
+</script>
+
+<%init>
+my $cgi = FS::UID::cgi();
+my $curuser = $FS::CurrentUser::CurrentUser;
+my $session = $FS::CurrentUser::CurrentSession;
+my $sessionkey = $session->sessionkey if $session;
+
+if ( $curuser ) {
+ my $canary = $cgi->param('edge_browser_canary');
+ $curuser->set_pref(
+ 'edge_bug_vulnerable',
+
+ $canary eq 'test' ? $sessionkey : 'Y',
+ );
+}
+
+</%init> \ No newline at end of file
diff --git a/httemplate/misc/process/change-password.html b/httemplate/misc/process/change-password.html
index a3e060168..37ad6d915 100644
--- a/httemplate/misc/process/change-password.html
+++ b/httemplate/misc/process/change-password.html
@@ -18,7 +18,7 @@
<% $cgi->redirect($fsurl.'view/svc_acct.cgi?'.$cgi->query_string) %>
% }
% elsif ($contactnum) {
- <% $cgi->redirect($fsurl.'edit/cust_main-contacts.html?'.$cgi->param('custnum')) %>
+ <% $cgi->redirect($fsurl.'view/cust_main.cgi?'.$cgi->param('custnum')) %>
% }
% }
@@ -34,6 +34,10 @@ my $curuser = $FS::CurrentUser::CurrentUser;
$cgi->param('svcnum') =~ /^(\d+)$/ or die "illegal svcnum" if $cgi->param('svcnum');
my $svcnum = $1;
+foreach my $prefix (grep /^(.*)(password)$/, $cgi->param) {
+ $cgi->param('password' => $cgi->param($prefix));
+}
+
$cgi->param('contactnum') =~ /^(\d+)$/ or die "illegal contactnum" if $cgi->param('contactnum');
my $contactnum = $1;
diff --git a/httemplate/search/future_autobill.html b/httemplate/search/future_autobill.html
index 711a25f82..d4ad8e524 100644
--- a/httemplate/search/future_autobill.html
+++ b/httemplate/search/future_autobill.html
@@ -2,20 +2,18 @@
Report listing upcoming auto-bill transactions
-Spec requested the ability to run this report with a longer date range,
-and see which charges will process on which day. Checkbox multiple_billing_dates
-enables this functionality.
+For every customer with a valid auto-bill payment method,
+report runs bill_and_collect() for each customer, for each
+day, from today through the report target date. After
+recording the results, all operations are rolled back.
-Performance:
-This is a dynamically generated report. The time this report takes to run
-will depends on the number of customers. Installations with a high number
-of auto-bill customers may find themselves unable to run this report
-because of browser timeout. Report could be implemented as a queued job if
-necessary, to solve the performance problem.
+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.
</%doc>
<& elements/grid-report.html,
- title => 'Upcoming auto-bill transactions',
+ title => $report_title,
rows => \@rows,
cells => \@cells,
table_width => "",
@@ -32,11 +30,16 @@ necessary, to solve the performance problem.
&>
<%init>
+ use FS::UID qw( dbh );
-use FS::UID qw( dbh myconnect );
+ die "access denied"
+ unless $FS::CurrentUser::CurrentUser->access_right('Financial reports');
-die "access denied"
- unless $FS::CurrentUser::CurrentUser->access_right('Financial reports');
+ 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+/;
my $target_dt;
my @target_dates;
@@ -45,14 +48,13 @@ die "access denied"
my %noon = (
hour => 12,
minute => 0,
- second => 0
+ second => 0,
);
-
my $now_dt = DateTime->now;
$now_dt = DateTime->new(
- month => $now_dt->month,
- day => $now_dt->day,
- year => $now_dt->year,
+ month => $now_dt->month,
+ day => $now_dt->day,
+ year => $now_dt->year,
%noon,
);
@@ -60,9 +62,9 @@ die "access denied"
if ($cgi->param('target_date')) {
my ($mm, $dd, $yy) = split /[\-\/]/,$cgi->param('target_date');
$target_dt = DateTime->new(
- month => $mm,
- day => $dd,
- year => $yy,
+ month => $mm,
+ day => $dd,
+ year => $yy,
%noon,
) if $mm && $dd & $yy;
@@ -72,18 +74,12 @@ die "access denied"
# without a target date, default to tomorrow
unless ($target_dt) {
- $target_dt = DateTime->from_epoch( epoch => time() + 86400) ;
- $target_dt = DateTime->new(
- month => $target_dt->month,
- day => $target_dt->day,
- year => $target_dt->year,
- %noon
- );
+ $target_dt = $now_dt->clone->add( days => 1 );
}
- # If multiple_billing_dates checkbox selected, create a range of dates
- # from today until the given report date. Otherwise, use target date only.
- if ($cgi->param('multiple_billing_dates')) {
+ # 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')) {
my $walking_dt = DateTime->from_epoch(epoch => $now_dt->epoch);
until ($walking_dt->epoch > $target_dt->epoch) {
push @target_dates, $walking_dt->epoch;
@@ -93,80 +89,128 @@ die "access denied"
push @target_dates, $target_dt->epoch;
}
- # List all customers with an auto-bill method
- #
- # my %cust_payby = map {$_->custnum => $_} qsearch({
- # table => 'cust_payby',
- # hashref => {
- # weight => { op => '>', value => '0' },
- # paydate => { op => '>', value => $target_dt->ymd },
- # },
- # order_by => " ORDER BY weight DESC ",
- # });
-
# 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 = 'CHEK' 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 (
+ payby IN ('CHEK','DCHK','DCHEK')
+ OR ( paydate > '".$target_dt->ymd."')
+ )
+ AND " . $FS::CurrentUser::CurrentUser->agentnums_sql
+ . ($agentnum ? "AND cust_main.agentnum = $agentnum" : ''),
});
+ my $fakebill_time = time();
my %abreport;
my @rows;
local $@;
local $SIG{__DIE__};
- my $temp_dbh = myconnect();
- eval { # Creating sandbox dbh where all connections are to be rolled back
- local $FS::UID::dbh = $temp_dbh;
+
+ eval { # Sandbox
+
+ # Supress COMMIT statements
+ my $oldAutoCommit = $FS::UID::AutoCommit;
local $FS::UID::AutoCommit = 0;
+ local $FS::UID::ForceObeyAutoCommit = 1;
+
+ # Suppress notices generated by billing events
+ local $FS::Misc::DISABLE_ALL_NOTICES = 1;
+
+ # Bypass payment processing, recording a fake payment
+ local $FS::cust_main::Billing_Realtime::BOP_TESTING = 1;
+ local $FS::cust_main::Billing_Realtime::BOP_TESTING_SUCCESS = 1;
- # Generate report data into @rows
+ warn sprintf "Report involves %s customers", scalar keys %cust_payby
+ if $DEBUG;
+
+ # Run bill_and_collect(), for each customer with an autobill payment method,
+ # for each day represented in the report
for my $custnum (keys %cust_payby) {
my $cust_main = qsearchs('cust_main', {custnum => $custnum});
+ warn "-- Processing custnum $custnum\n"
+ if $DEBUG;
+
# walk forward through billing dates
for my $query_epoch (@target_dates) {
+ $FS::cust_main::Billing_Realtime::BOP_TESTING_TIMESTAMP = $query_epoch;
my $return_bill = [];
- eval { # Don't let an error on one customer crash the report
- my $error = $cust_main->bill(
- time => $query_epoch,
- return_bill => $return_bill,
- no_usage_reset => 1,
- );
- die "$error (simulating future billing)" if $error;
- };
- warn ("$@: (future_autobill custnum:$custnum)");
-
- if (@{$return_bill}) {
- my $inv = $return_bill->[0];
- push @rows,{
- name => $cust_main->name,
- _date => $inv->_date,
- cells => [
- { class => 'gridreport', value => $custnum },
- { class => 'gridreport',
- value => '<a href="/view/cust_main.cgi?"'.$custnum.'">'.$cust_main->name.'</a>',
- bypass_filter => 1,
- },
- { class => 'gridreport', value => $inv->charged, format => 'money' },
- { class => 'gridreport', value => DateTime->from_epoch(epoch=>$inv->_date)->ymd },
- { class => 'gridreport', value => ($cust_payby{$custnum}->payby || $cust_payby{$custnum}->paytype) },
- { class => 'gridreport', value => $cust_payby{$custnum}->paymask },
- ]
- };
- }
+ warn "---- Set billtime to ".
+ DateTime->from_epoch( epoch => $query_epoch )."\n"
+ if $DEBUG;
+
+ my $error = $cust_main->bill_and_collect(
+ time => $query_epoch,
+ return_bill => $return_bill,
+ no_usage_reset => 1,
+ fake => 1,
+ );
+ warn "!!! $error (simulating future billing)\n" if $error;
}
- $temp_dbh->rollback;
- } # /foreach $custnum
+ # Generate report rows from recorded payments in cust_pay
+ for my $cust_pay (
+ qsearch( cust_pay => {
+ custnum => $custnum,
+ _date => { op => '>=', value => $fakebill_time },
+ })
+ ) {
+ push @rows,{
+ name => $cust_main->name,
+ _date => $cust_pay->_date,
+ cells => [
+
+ # Customer number
+ { class => 'gridreport', value => $custnum },
+
+ # Customer name / customer link
+ { class => 'gridreport',
+ value => qq{<a href="${fsurl}view/cust_main.cgi?${custnum}">} . encode_entities( $cust_main->name ). '</a>',
+ bypass_filter => 1
+ },
+
+ # Amount
+ { class => 'gridreport',
+ value => $cust_pay->paid,
+ format => 'money'
+ },
+
+ # Transaction Date
+ { class => 'gridreport',
+ value => DateTime->from_epoch( epoch => $cust_pay->_date )->ymd
+ },
+
+ # Payment Method
+ { class => 'gridreport',
+ value => encode_entities( $cust_pay->paycardtype || $cust_pay->payby ),
+ },
+
+ # Masked Payment Instrument
+ { class => 'gridreport',
+ value => encode_entities( $cust_pay->paymask ),
+ },
+ ]
+ };
+
+ } # /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" if $DEBUG;
+ dbh->rollback if $oldAutoCommit;
+
+ } # /foreach $custnum
}; # /eval
- warn("$@") if $@;
+ warn("future_autobill.html report generated error $@") if $@;
# Sort output by date, and format for output to grid-report.html
my @cells = [
diff --git a/httemplate/search/report_future_autobill.html b/httemplate/search/report_future_autobill.html
index 1a0c9f48a..ccde299e9 100644
--- a/httemplate/search/report_future_autobill.html
+++ b/httemplate/search/report_future_autobill.html
@@ -3,40 +3,55 @@
Display date selector for the future_autobill.html report
</%doc>
-<% include('/elements/header.html', 'Future Auto-Bill Transactions' ) %>
+<% include('/elements/header.html', $report_title ) %>
-<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
- }
-&>
+% if ( FS::TaxEngine->new->info->{batch} ) {
-<& /elements/tr-checkbox.html,
- 'label' => emt('Multiple billing dates (slow)').': ',
- 'field' => 'multiple_billing_dates',
- 'value' => '1',
-&>
+ <div style="font-color: red">
+ NOTE: This report is disabled due to tax engine configuration
+ </div>
-</TABLE>
+% } else {
-<BR>
-<INPUT TYPE="submit" VALUE="<% mt('Get Report') |h %>">
+ <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
+ }
+ &>
-</FORM>
+ <% include('/elements/tr-select-agent.html',
+ 'label' => 'For agent: ',
+ 'disable_empty' => 0,
+ )
+ %>
+
+ </TABLE>
+
+ <BR>
+
+ <INPUT TYPE="submit" VALUE="<% mt('Get Report') |h %>">
+
+ </FORM>
+
+% }
<% include('/elements/footer.html') %>
<%init>
+use FS::cust_payby;
+use FS::CurrentUser;
die "access denied"
unless $FS::CurrentUser::CurrentUser->access_right('Financial reports');
-my $target_date = DateTime->from_epoch(epoch=>(time()+86400))->mdy('/');
+my $target_date = DateTime->now->add(days => 1)->mdy('/');
+my $report_title = FS::cust_payby->future_autobill_report_title;
</%init>
+
diff --git a/httemplate/view/cust_main/contacts_new.html b/httemplate/view/cust_main/contacts_new.html
index fe412cc00..9252b2197 100644
--- a/httemplate/view/cust_main/contacts_new.html
+++ b/httemplate/view/cust_main/contacts_new.html
@@ -22,6 +22,7 @@
% my $bgcolor1 = '#ffffff';
% my $bgcolor2 = '#eeeeee';
% my $bgcolor = $bgcolor2;
+% my $count = 0;
% foreach my $cust_contact ( @cust_contacts ) {
% my $contact = $cust_contact->contact;
% my $td = qq(<TD CLASS="grid" BGCOLOR="$bgcolor">);
@@ -39,6 +40,16 @@
Enabled
%# <FONT SIZE="-1"><A HREF="XXX">disable</A>
%# <A HREF="XXX">re-email</A></FONT>
+ <FONT SIZE="-1">
+ <& /elements/change_password.html,
+ 'contact_num' => $cust_contact->contactnum,
+ 'custnum' => $cust_contact->custnum,
+ 'no_label_display' => '',
+ 'label' => 'change password',
+ 'curr_value' => '',
+ 'pre_pwd_field_label' => 'contact'.$count.'_',
+ &>
+ </FONT>
% } else {
Disabled
%# <FONT SIZE="-1"><A HREF="XXX">enable</A></FONT>
@@ -63,6 +74,7 @@
% } else {
% $bgcolor = $bgcolor1;
% }
+% $count++;
% }
</TABLE>
%}
@@ -80,6 +92,6 @@ my @cust_contacts = $cust_main->cust_contact;
# residential customers have a default "invisible" contact, but if they
# somehow get more than one contact, show them
-my $display = scalar(@cust_contacts) > 1;
+my $display = scalar(@cust_contacts) > 0;
</%init>