summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--Changes.1.5.85
-rw-r--r--FS/FS/cust_pay.pm60
-rw-r--r--README.1.5.82
-rw-r--r--htetc/global.asa3
-rw-r--r--htetc/handler.pl1
-rw-r--r--httemplate/docs/upgrade10.html1
-rw-r--r--httemplate/index.html2
-rw-r--r--httemplate/misc/batch-cust_pay.html414
-rw-r--r--httemplate/misc/process/batch-cust_pay.cgi40
-rw-r--r--httemplate/misc/xmlhttp-cust_main-search.cgi21
-rwxr-xr-xhttemplate/search/cust_pay.cgi104
11 files changed, 530 insertions, 123 deletions
diff --git a/Changes.1.5.8 b/Changes.1.5.8
index f8066cc99..2b55c5cdf 100644
--- a/Changes.1.5.8
+++ b/Changes.1.5.8
@@ -16,3 +16,8 @@
- moved to XMLHttpRequest instead of hidden iframe transport for progress bar,
should be more efficient and improve compatibility with Konq and maybe other
browsers?
+- also use XMLHttpRequest for retreiving states rather than send a huge page
+ for customer add/edit, much faster
+- redo account view and edit pages, add ability to edit uid/gid if conf options
+ for it are turned on
+- redo quick payment entry page with ajax magic
diff --git a/FS/FS/cust_pay.pm b/FS/FS/cust_pay.pm
index 0f872a4d2..a7d69901f 100644
--- a/FS/FS/cust_pay.pm
+++ b/FS/FS/cust_pay.pm
@@ -122,12 +122,13 @@ sub insert {
$self->custnum($cust_bill->custnum );
}
- my $cust_main = $self->cust_main;
- my $old_balance = $cust_main->balance;
my $error = $self->check;
return $error if $error;
+ my $cust_main = $self->cust_main;
+ my $old_balance = $cust_main->balance;
+
$error = $self->SUPER::insert;
if ( $error ) {
$dbh->rollback if $oldAutoCommit;
@@ -398,6 +399,61 @@ sub check {
$self->SUPER::check;
}
+=item batch_insert CUST_PAY_OBJECT, ...
+
+Class method which inserts multiple payments. Takes a list of FS::cust_pay
+objects. Returns a list, each element representing the status of inserting the
+corresponding payment - empty. If there is an error inserting any payment, the
+entire transaction is rolled back, i.e. all payments are inserted or none are.
+
+For example:
+
+ my @errors = FS::cust_pay->batch_insert(@cust_pay);
+ my $num_errors = scalar(grep $_, @errors);
+ if ( $num_errors == 0 ) {
+ #success; all payments were inserted
+ } else {
+ #failure; no payments were inserted.
+ }
+
+=cut
+
+sub batch_insert {
+ my $self = shift; #class method
+
+ local $SIG{HUP} = 'IGNORE';
+ local $SIG{INT} = 'IGNORE';
+ local $SIG{QUIT} = 'IGNORE';
+ local $SIG{TERM} = 'IGNORE';
+ local $SIG{TSTP} = 'IGNORE';
+ local $SIG{PIPE} = 'IGNORE';
+
+ my $oldAutoCommit = $FS::UID::AutoCommit;
+ local $FS::UID::AutoCommit = 0;
+ my $dbh = dbh;
+
+ my $errors = 0;
+
+ my @errors = map {
+ my $error = $_->insert;
+ if ( $error ) {
+ $errors++;
+ } else {
+ $_->cust_main->apply_payments;
+ }
+ $error;
+ } @_;
+
+ if ( $errors ) {
+ $dbh->rollback if $oldAutoCommit;
+ } else {
+ $dbh->commit or die $dbh->errstr if $oldAutoCommit;
+ }
+
+ @errors;
+
+}
+
=item cust_bill_pay
Returns all applications to invoices (see L<FS::cust_bill_pay>) for this
diff --git a/README.1.5.8 b/README.1.5.8
index fb173650e..209ab3fef 100644
--- a/README.1.5.8
+++ b/README.1.5.8
@@ -1,4 +1,6 @@
+install JSON
+
install DBIx::DBSchema 0.27
install HTML::Widgets:SelectLayers 0.05
install Business::CreditCard 0.28
diff --git a/htetc/global.asa b/htetc/global.asa
index 1a81c4b1c..bb30608a4 100644
--- a/htetc/global.asa
+++ b/htetc/global.asa
@@ -14,6 +14,7 @@ use Time::Duration;
use Tie::IxHash;
use URI::Escape;
use HTML::Entities;
+use JSON;
use IO::Handle;
use IO::File;
use IO::Scalar;
@@ -47,7 +48,7 @@ use FS::cust_bill;
use FS::cust_bill_pay;
use FS::cust_credit;
use FS::cust_credit_bill;
-use FS::cust_main;
+use FS::cust_main qw(smart_search);
use FS::cust_main_county;
use FS::cust_pay;
use FS::cust_pkg;
diff --git a/htetc/handler.pl b/htetc/handler.pl
index 737e5af0b..851b2e7d2 100644
--- a/htetc/handler.pl
+++ b/htetc/handler.pl
@@ -99,6 +99,7 @@ sub handler
use Tie::IxHash;
use URI::Escape;
use HTML::Entities;
+ use JSON;
use IO::Handle;
use IO::File;
use IO::Scalar;
diff --git a/httemplate/docs/upgrade10.html b/httemplate/docs/upgrade10.html
index fb51bfb8e..a4ed27170 100644
--- a/httemplate/docs/upgrade10.html
+++ b/httemplate/docs/upgrade10.html
@@ -9,6 +9,7 @@ If migrating from 1.5.7, see README.1.5.8 instead
If migrating from 1.5.0pre6, see README.1.5.7 instead
+install JSON
install DBD::Pg 1.32, 1.41 or later (not 1.40) (or, if you're using a Perl version before 5.6, you could try installing DBD::Pg 1.22 with <a href="http://420.am/~ivan/DBD-Pg-1.22-fixvercmp.patch">this patch</a> and commenting out the "use DBD::Pg 1.32" at the top of DBIx/DBSchema/DBD/Pg.pm)
install DBIx::DBSchema 0.26
install Net::SSH 0.08
diff --git a/httemplate/index.html b/httemplate/index.html
index 82a028126..b4b85063e 100644
--- a/httemplate/index.html
+++ b/httemplate/index.html
@@ -120,7 +120,7 @@
<TABLE CELLSPACING=2 CELLPADDING=0 BORDER=0 WIDTH="100%" BGCOLOR="#eeeeee">
<TR><TH BGCOLOR="#cccccc">Bookkeeping / Collections</TH></TR>
<TR><TD>
- <BR><A HREF="search/cust_main-quickpay.html">Quick payment entry</A>
+ <BR><A HREF="misc/batch-cust_pay.html">Quick payment entry</A>
<BR>
<BR><FORM ACTION="search/cust_main.cgi" METHOD="GET">Credit card # <INPUT TYPE="hidden" NAME="card_on" VALUE="1"><INPUT TYPE="text" NAME="card"><INPUT TYPE="submit" VALUE="Search"></FORM>
<FORM ACTION="search/cust_bill.html" METHOD="GET">Invoice # <INPUT TYPE="text" NAME="invnum" SIZE="8"><INPUT TYPE="submit" VALUE="Search"></FORM>
diff --git a/httemplate/misc/batch-cust_pay.html b/httemplate/misc/batch-cust_pay.html
index b09876b89..ac7f7ffa3 100644
--- a/httemplate/misc/batch-cust_pay.html
+++ b/httemplate/misc/batch-cust_pay.html
@@ -1,99 +1,234 @@
<%= header( 'Quick payment entry',
menubar(
- 'Main Menu' => popurl(1),
+ 'Main Menu' => $p, #popurl(1),
+ 'Old-style quick payment entry' =>
+ $p. 'search/cust_main-quickpay.html',
),
- 'onLoad="addRow()"',
+ ( $cgi->param('error') ? '' : 'onload="addRow()"' ),
)
%>
<% if ( $cgi->param('error') ) { %>
- <FONT SIZE="+1" COLOR="#ff0000">Error: <%= $cgi->param('error') %></FONT>
+ <FONT SIZE="+1" COLOR="#ff0000"><%= $cgi->param('error') %></FONT><BR><BR>
<% } %>
-<SCRIPT TYPE="text/javascript">
-
- var rownum = 0;
- function addRow() {
+<FORM ACTION="process/batch-cust_pay.cgi" NAME="OneTrueForm" METHOD="POST" onsubmit="document.OneTrueForm.submit.disabled=true;">
- var table = document.getElementById('OneTrueTable');
- var tablebody = table.getElementsByTagName('tbody').item(0);
+<!-- <B>Batch</B> <INPUT TYPE="text" NAME="paybatch"><BR><BR> -->
- var row = document.createElement('TR');
-
- var custnum_cell = document.createElement('TD');
- var custnum_input = document.createElement('INPUT');
- custnum_input.setAttribute('name', 'custnum'+rownum);
- custnum_input.setAttribute('size', 8);
- custnum_input.setAttribute('maxlength', 7);
- custnum_input.setAttribute('rownum', rownum);
- custnum_input.onchange = search_custnum;
-
- custnum_cell.appendChild(custnum_input);
- row.appendChild(custnum_cell);
-
- var customer_cell = document.createElement('TD');
- var customer_input = document.createElement('INPUT');
- custnum_input.setAttribute('name', 'customer'+rownum);
- customer_input.setAttribute('size', 32);
- customer_input.setAttribute('value', '(last name or company)' );
- customer_input.onfocus = clearhint;
- customer_input.setAttribute('rownum', rownum);
- customer_input.onchange = search_customer;
- customer_cell.appendChild(customer_input);
- row.appendChild(customer_cell);
+<SCRIPT TYPE="text/javascript">
- var paid_cell = document.createElement('TD');
- var paid_text = document.createTextNode('$');
- var paid_input = document.createElement('INPUT');
- paid_input.setAttribute('name', 'paid'+rownum);
- paid_input.setAttribute('size', 8);
- paid_input.setAttribute('maxlength', 8);
- paid_cell.appendChild(paid_text);
- paid_cell.appendChild(paid_input);
- row.appendChild(paid_cell);
+ function clearhint_custnum() {
- var payinfo_cell = document.createElement('TD');
- var payinfo_input = document.createElement('INPUT');
- payinfo_input.setAttribute('name', 'payinfo'+rownum);
- payinfo_input.setAttribute('size', 10);
- payinfo_cell.appendChild(payinfo_input);
- row.appendChild(payinfo_cell);
+ //this.style.color = '#000000';
- tablebody.appendChild(row);
-
- rownum++;
+ if ( this.value == 'Not found' || this.value == 'Multiple' ) {
+ this.value = '';
+ this.style.color = '#000000';
+ }
}
- function clearhint() {
+ function clearhint_customer() {
- if ( this.value == '(last name or company)' )
+ this.style.color = '#000000';
+
+ if ( this.value == '(last name or company)' || this.value == 'Not found' )
this.value = '';
}
function search_custnum() {
+ this.style.color = '#000000'
+
+ var custnum_obj = this;
var searchrow = this.getAttribute('rownum');
+ var custnum = this.value;
+
+ if ( custnum == 'searching...' || custnum == 'Not found' || custnum == '' )
+ return;
+
+ if ( this.getAttribute('magic') == 'nosearch' ) {
+ this.setAttribute('magic', '');
+ return;
+ }
+
+ if ( ( rownum - searchrow ) == 1 ) {
+ addRow();
+ }
+ var customer = document.getElementById('customer'+searchrow);
+ customer.value = 'searching...';
+ customer.disabled = true;
+ customer.style.color = '#000000';
+ customer.style.backgroundColor = '#dddddd';
+
+ var customer_select = document.getElementById('cust_select'+searchrow);
+
+ //alert('search for custnum ' + custnum + ', row#' + searchrow );
+
+ customer.style.display = '';
+ customer_select.style.display = 'none';
+
+ function search_custnum_update(name) {
+
+ var name = eval('(' + name + ')' );
+
+ customer.disabled = false;
+ customer.style.backgroundColor = '#ffffff';
+
+ if ( name.length > 0 ) {
+ //alert('custnum found: ' + name);
+ customer.value = name;
+ customer.setAttribute('magic', 'nosearch');
+ } else {
+ customer.value = 'Not found';
+ customer.style.color = '#ff0000';
+ custnum_obj.style.color = '#ff0000';
- alert('search for custnum row#' + searchrow );
+ }
+
+ }
+
+ custnum_search( custnum, search_custnum_update );
}
function search_customer() {
+ var customer_obj = this;
var searchrow = this.getAttribute('rownum');
+ var customer = this.value;
+
+ if ( customer == 'searching...' || customer == 'Not found' || customer == '' )
+ return;
+
+ if ( this.getAttribute('magic') == 'nosearch' ) {
+ this.setAttribute('magic', '');
+ return;
+ }
+
+ if ( ( rownum - searchrow ) == 1 ) {
+ addRow();
+ }
+
+ var custnum_obj = document.getElementById('custnum'+searchrow);
+ custnum_obj.value = 'searching...';
+ custnum_obj.disabled = true;
+ custnum_obj.style.color = '#000000';
+ custnum_obj.style.backgroundColor = '#dddddd';
+
+ var customer_select = document.getElementById('cust_select'+searchrow);
+
+ //alert('search for customer ' + customer + ', row#' + searchrow );
+
+ function search_customer_update(customers) {
+
+ //alert('customers returned: ' + customers);
+
+ var customerArray = eval('(' + customers + ')');
+
+ custnum_obj.disabled = false;
+ custnum_obj.style.backgroundColor = '#ffffff';
+
+ if ( customerArray.length == 0 ) {
+
+ custnum_obj.value = 'Not found';
+ custnum_obj.style.color = '#ff0000';
+ customer_obj.style.color = '#ff0000';
+
+ customer_obj.style.display = '';
+ customer_select.style.display = 'none';
+
+
+ } else if ( customerArray.length == 1 ) {
- alert('search for customer row#' + searchrow );
+ //alert('one customer found: ' + customerArray[0]);
+
+ custnum_obj.value = customerArray[0][0];
+ customer_obj.value = customerArray[0][1];
+
+ customer_obj.style.display = '';
+ customer_select.style.display = 'none';
+
+
+ } else {
+
+ custnum_obj.value = 'Multiple'; // or something
+ custnum_obj.style.color = '#ff0000';
+
+ //alert('multiple customers found, have to create select dropdown');
+
+ //blank the current list
+ for ( var i = customer_select.length; i >= 0; i-- )
+ customer_select.options[i] = null;
+
+ opt(customer_select, '', 'Multiple customers match "' + customer + '" - select one', '#ff0000');
+
+ //add the multiple customers
+ for ( var s = 0; s < customerArray.length; s++ )
+ opt(customer_select, customerArray[s][0], customerArray[s][1], '#000000');
+
+ opt(customer_select, 'cancel', '(Edit search string)', '#000000');
+
+ customer_obj.style.display = 'none';
+
+ customer_select.style.display = '';
+
+ }
+
+ }
+
+ smart_search( customer, search_customer_update );
}
-</SCRIPT>
+ function select_customer() {
-<FORM ACTION="<%= $p %>process/batch-cust_pay.cgi" METHOD=POST>
+ var custnum = this.options[this.selectedIndex].value;
+ var customer = this.options[this.selectedIndex].text;
-<B>Batch <INPUT TYPE="text" NAME="paybatch"><BR><BR>
+ var searchrow = this.getAttribute('rownum');
+ var custnum_obj = document.getElementById('custnum'+searchrow);
+ var customer_obj = document.getElementById('customer'+searchrow);
+
+ if ( custnum == '' ) {
+ //this.style.color = '#ff0000';
+
+ } else if ( custnum == 'cancel' ) {
+
+ custnum_obj.value = '';
+ custnum_obj.style.color = '#000000';
+
+ this.style.display = 'none';
+ customer_obj.style.display = '';
+ customer_obj.focus();
+
+ } else {
+
+
+ custnum_obj.value = custnum;
+ custnum_obj.style.color = '#000000';
+
+ customer_obj.value = customer;
+ customer_obj.style.color = '#000000';
+
+ this.style.display = 'none';
+ customer_obj.style.display = '';
+
+ }
+
+ }
+
+ function opt(what,value,text,color) {
+ var optionName = new Option(text, value, false, false);
+ optionName.style.color = color;
+ var length = what.length;
+ what.options[length] = optionName;
+ }
+
+</SCRIPT>
<TABLE ID="OneTrueTable" BGCOLOR="#cccccc" BORDER=0 CELLSPACING=0>
@@ -102,33 +237,160 @@
<TH>Customer</TH>
<TH>Amount</TH>
<TH>Check #</TH>
+ <TH BGCOLOR="#e8e8e8"></TH>
</TR>
-<!--
-<TR>
- <TD>
- <INPUT TYPE="text" NAME="custnum0" SIZE=8 MAXLENGTH=7 onChange="alert('search for custnum')" >
- </TD>
- <TD>
- <INPUT TYPE="text" NAME="customer0" SIZE=32 VALUE="(last name or company)" onChange="alert('search for customer')" >
- </TD>
- <TD>
- $<INPUT TYPE="text" NAME="paid0" SIZE=8 MAXLENGTH=8>
- </TD>
- <TD>
- <INPUT TYPE="text" NAME="payinfo" SIZE=10>
- </TD>
-</TR>
--->
+<% my $row = 0;
+ if ( $cgi->param('error') ) {
+ my $param = $cgi->Vars;
+%>
+
+ <% for ( $row = 0; exists($param->{"custnum$row"}); $row++ ) { %>
+
+ <TR>
+
+ <TD>
+ <INPUT TYPE="text" NAME="custnum<%= $row %>" ID="custnum<%= $row %>" SIZE=8 MAXLENGTH=12 VALUE="<%= $param->{"custnum$row"} %>" rownum="<%= $row %>">
+ <SCRIPT TYPE="text/javascript">
+ var custnum_input<%= $row %> = document.getElementById("custnum<%= $row %>");
+ custnum_input<%= $row %>.onfocus = clearhint_custnum;
+ custnum_input<%= $row %>.onchange = search_custnum;
+ </SCRIPT>
+ </TD>
+
+ <TD>
+ <INPUT TYPE="text" NAME="customer<%= $row %>" ID="customer<%= $row %>" SIZE=64 VALUE="<%= $param->{"customer$row"} %>" rownum="<%= $row %>">
+ <SCRIPT TYPE="text/javascript">
+ var customer_input<%= $row %> = document.getElementById("customer<%= $row %>");
+ customer_input<%= $row %>.onfocus = clearhint_customer;
+ customer_input<%= $row %>.onclick = clearhint_customer;
+ customer_input<%= $row %>.onchange = search_customer;
+ </SCRIPT>
+ <SELECT NAME="cust_select<%= $row %>" ID="cust_select<%= $row %>" rownum="<%= $row %>" STYLE="color:#ff0000; display:none"">
+ </SELECT>
+ <SCRIPT TYPE="text/javascript">
+ var customer_select<%= $row %> = document.getElementById("cust_select<%= $row %>");
+ customer_select<%= $row %>.onchange = select_customer;
+ </SCRIPT>
+ </TD>
+
+ <TD>
+ $<INPUT TYPE="text" NAME="paid<%= $row %>" SIZE=8 MAXLENGTH=8 VALUE="<%= $param->{"paid$row"} %>" >
+ </TD>
+
+ <TD>
+ <INPUT TYPE="text" NAME="payinfo<%= $row %>" SIZE=10 VALUE="<%= $param->{"payinfo$row"} %>" >
+ </TD>
+
+ <TD BGCOLOR="#e8e8e8">
+ <% if ( $param->{"error$row"} ) { %>
+ <FONT SIZE="-1" COLOR="#ff0000">Error: <%= $param->{"error$row"} %></FONT>
+ <% } %>
+ </TD>
+
+ </TR>
+
+ <% } %>
+
+<% } %>
</TABLE>
-<BR>
-<INPUT TYPE="button" VALUE="TEST addrow" onClick="addRow()">
+<!-- <BR>
+<INPUT TYPE="button" VALUE="TEST addrow" onclick="addRow()"> -->
<BR>
-<INPUT TYPE="submit" VALUE="Post payments">
+<INPUT TYPE="submit" NAME="submit" VALUE="Post payments">
</FORM>
+
+
+<%= include('/elements/xmlhttp.html',
+ 'url' => $p. 'misc/xmlhttp-cust_main-search.cgi',
+ 'subs' => [qw( custnum_search smart_search )],
+ )
+%>
+
+<SCRIPT TYPE="text/javascript">
+
+ var rownum = <%= $row %>;
+
+ function addRow() {
+
+ var table = document.getElementById('OneTrueTable');
+ var tablebody = table.getElementsByTagName('tbody').item(0);
+
+ var row = document.createElement('TR');
+
+ var custnum_cell = document.createElement('TD');
+
+ var custnum_input = document.createElement('INPUT');
+ custnum_input.setAttribute('name', 'custnum'+rownum);
+ custnum_input.setAttribute('id', 'custnum'+rownum);
+ custnum_input.setAttribute('size', 8);
+ custnum_input.setAttribute('maxlength', 12);
+ custnum_input.setAttribute('rownum', rownum);
+ custnum_input.onfocus = clearhint_custnum;
+ custnum_input.onchange = search_custnum;
+ custnum_cell.appendChild(custnum_input);
+
+ row.appendChild(custnum_cell);
+
+ var customer_cell = document.createElement('TD');
+
+ var customer_input = document.createElement('INPUT');
+ customer_input.setAttribute('name', 'customer'+rownum);
+ customer_input.setAttribute('id', 'customer'+rownum);
+ customer_input.setAttribute('size', 64);
+ customer_input.setAttribute('value', '(last name or company)' );
+ customer_input.setAttribute('rownum', rownum);
+ customer_input.onfocus = clearhint_customer;
+ customer_input.onclick = clearhint_customer;
+ customer_input.onchange = search_customer;
+ customer_cell.appendChild(customer_input);
+
+ var customer_select = document.createElement('SELECT');
+ customer_select.setAttribute('name', 'cust_select'+rownum);
+ customer_select.setAttribute('id', 'cust_select'+rownum);
+ customer_select.setAttribute('rownum', rownum);
+ customer_select.style.color = '#ff0000';
+ customer_select.style.display = 'none';
+ customer_select.onchange = select_customer;
+ customer_cell.appendChild(customer_select);
+
+ row.appendChild(customer_cell);
+
+ var paid_cell = document.createElement('TD');
+
+ var paid_text = document.createTextNode('$');
+ paid_cell.appendChild(paid_text);
+
+ var paid_input = document.createElement('INPUT');
+ paid_input.setAttribute('name', 'paid'+rownum);
+ paid_input.setAttribute('size', 8);
+ paid_input.setAttribute('maxlength', 8);
+ paid_cell.appendChild(paid_input);
+
+ row.appendChild(paid_cell);
+
+ var payinfo_cell = document.createElement('TD');
+ var payinfo_input = document.createElement('INPUT');
+ payinfo_input.setAttribute('name', 'payinfo'+rownum);
+ payinfo_input.setAttribute('size', 10);
+ payinfo_cell.appendChild(payinfo_input);
+ row.appendChild(payinfo_cell);
+
+ var error_cell = document.createElement('TD');
+ error_cell.style.backgroundColor = '#e8e8e8';
+ row.appendChild(error_cell);
+
+ tablebody.appendChild(row);
+
+ rownum++;
+
+ }
+
+</SCRIPT>
+
</BODY>
</HTML>
diff --git a/httemplate/misc/process/batch-cust_pay.cgi b/httemplate/misc/process/batch-cust_pay.cgi
new file mode 100644
index 000000000..1cc6c3bad
--- /dev/null
+++ b/httemplate/misc/process/batch-cust_pay.cgi
@@ -0,0 +1,40 @@
+<%
+ my $param = $cgi->Vars;
+
+ #my $paybatch = $param->{'paybatch'};
+ my $paybatch = time2str('webbatch-%Y/%m/%d-%T'. "-$$-". rand() * 2**32, time);
+
+ my @cust_pay = ();
+ #my $row = 0;
+ #while ( exists($param->{"custnum$row"}) ) {
+ for ( my $row = 0; exists($param->{"custnum$row"}); $row++ ) {
+ push @cust_pay, new FS::cust_pay {
+ 'custnum' => $param->{"custnum$row"},
+ 'paid' => $param->{"paid$row"},
+ 'payby' => 'BILL',
+ 'payinfo' => $param->{"payinfo$row"},
+ 'paybatch' => $paybatch,
+ }
+ if $param->{"custnum$row"}
+ || $param->{"paid$row"}
+ || $param->{"payinfo$row"};
+ #$row++;
+ }
+
+ my @errors = FS::cust_pay->batch_insert(@cust_pay);
+ my $num_errors = scalar(grep $_, @errors);
+
+ if ( $num_errors ) {
+
+ $cgi->param('error', "$num_errors error". ($num_errors>1 ? 's' : '') );
+
+ my $erow=0;
+ $cgi->param('error'. $erow++, shift @errors) while @errors;
+
+ %><%= $cgi->redirect($p.'batch-cust_pay.html?'. $cgi->query_string)
+
+ %><% } else {
+
+ %><%= $cgi->redirect(popurl(3). "search/cust_pay.cgi?magic=paybatch;paybatch=$paybatch") %>
+
+ <% } %>
diff --git a/httemplate/misc/xmlhttp-cust_main-search.cgi b/httemplate/misc/xmlhttp-cust_main-search.cgi
new file mode 100644
index 000000000..8dbd5a4f2
--- /dev/null
+++ b/httemplate/misc/xmlhttp-cust_main-search.cgi
@@ -0,0 +1,21 @@
+<%
+ my $sub = $cgi->param('sub');
+
+ if ( $sub eq 'custnum_search' ) {
+
+ my $custnum = $cgi->param('arg');
+ my $cust_main = qsearchs('cust_main', { 'custnum' => $custnum } );
+
+ %>"<%= $cust_main ? $cust_main->name : '' %>"
+
+<% } elsif ( $sub eq 'smart_search' ) {
+
+ my $string = $cgi->param('arg');
+ my @cust_main = smart_search( 'search' => $string );
+ my $return = [ map [ $_->custnum, $_->name ], @cust_main ];
+
+ %><%= objToJson($return) %>
+
+<% } %>
+
+
diff --git a/httemplate/search/cust_pay.cgi b/httemplate/search/cust_pay.cgi
index da3d12523..89da7426a 100755
--- a/httemplate/search/cust_pay.cgi
+++ b/httemplate/search/cust_pay.cgi
@@ -1,60 +1,78 @@
<%
my $title = 'Payment Search Results';
my( $count_query, $sql_query );
- if ( $cgi->param('magic') && $cgi->param('magic') eq '_date' ) {
-
- my @search = ();
+ if ( $cgi->param('magic') ) {
- if ( $cgi->param('agentnum') && $cgi->param('agentnum') =~ /^(\d+)$/ ) {
- push @search, "agentnum = $1"; # $search{'agentnum'} = $1;
- my $agent = qsearchs('agent', { 'agentnum' => $1 } );
- die "unknown agentnum $1" unless $agent;
- $title = $agent->agent. " $title";
- }
+ my @search = ();
+ my $orderby;
+ if ( $cgi->param('magic') eq '_date' ) {
- if ( $cgi->param('payby') ) {
- $cgi->param('payby') =~ /^(CARD|CHEK|BILL)(-(VisaMC|Amex|Discover))?$/
- or die "illegal payby ". $cgi->param('payby');
- push @search, "cust_pay.payby = '$1'";
- if ( $3 ) {
- if ( $3 eq 'VisaMC' ) {
- #avoid posix regexes for portability
- push @search,
- " ( substring(cust_pay.payinfo from 1 for 1) = '4' ".
- " OR substring(cust_pay.payinfo from 1 for 2) = '51' ".
- " OR substring(cust_pay.payinfo from 1 for 2) = '52' ".
- " OR substring(cust_pay.payinfo from 1 for 2) = '53' ".
- " OR substring(cust_pay.payinfo from 1 for 2) = '54' ".
- " OR substring(cust_pay.payinfo from 1 for 2) = '54' ".
- " OR substring(cust_pay.payinfo from 1 for 2) = '55' ".
- " ) ";
- } elsif ( $3 eq 'Amex' ) {
- push @search,
- " ( substring(cust_pay.payinfo from 1 for 2 ) = '34' ".
- " OR substring(cust_pay.payinfo from 1 for 2 ) = '37' ".
- " ) ";
- } elsif ( $3 eq 'Discover' ) {
- push @search,
- " substring(cust_pay.payinfo from 1 for 4 ) = '6011' ";
- } else {
- die "unknown card type $3";
+
+ if ( $cgi->param('agentnum') && $cgi->param('agentnum') =~ /^(\d+)$/ ) {
+ push @search, "agentnum = $1"; # $search{'agentnum'} = $1;
+ my $agent = qsearchs('agent', { 'agentnum' => $1 } );
+ die "unknown agentnum $1" unless $agent;
+ $title = $agent->agent. " $title";
+ }
+
+ if ( $cgi->param('payby') ) {
+ $cgi->param('payby') =~ /^(CARD|CHEK|BILL)(-(VisaMC|Amex|Discover))?$/
+ or die "illegal payby ". $cgi->param('payby');
+ push @search, "cust_pay.payby = '$1'";
+ if ( $3 ) {
+ if ( $3 eq 'VisaMC' ) {
+ #avoid posix regexes for portability
+ push @search,
+ " ( substring(cust_pay.payinfo from 1 for 1) = '4' ".
+ " OR substring(cust_pay.payinfo from 1 for 2) = '51' ".
+ " OR substring(cust_pay.payinfo from 1 for 2) = '52' ".
+ " OR substring(cust_pay.payinfo from 1 for 2) = '53' ".
+ " OR substring(cust_pay.payinfo from 1 for 2) = '54' ".
+ " OR substring(cust_pay.payinfo from 1 for 2) = '54' ".
+ " OR substring(cust_pay.payinfo from 1 for 2) = '55' ".
+ " ) ";
+ } elsif ( $3 eq 'Amex' ) {
+ push @search,
+ " ( substring(cust_pay.payinfo from 1 for 2 ) = '34' ".
+ " OR substring(cust_pay.payinfo from 1 for 2 ) = '37' ".
+ " ) ";
+ } elsif ( $3 eq 'Discover' ) {
+ push @search,
+ " substring(cust_pay.payinfo from 1 for 4 ) = '6011' ";
+ } else {
+ die "unknown card type $3";
+ }
}
}
- }
+
+ my($beginning, $ending) = FS::UI::Web::parse_beginning_ending($cgi);
+ push @search, "_date >= $beginning ",
+ "_date <= $ending";
+
+ $orderby = '_date';
+
+ } elsif ( $cgi->param('magic') eq 'paybatch' ) {
+
+ $cgi->param('paybatch') =~ /^([\w\/\:\-\.]+)$/
+ or die "illegal paybatch: ". $cgi->param('paybatch');
- my($beginning, $ending) = FS::UI::Web::parse_beginning_ending($cgi);
- push @search, "_date >= $beginning ",
- "_date <= $ending";
+ push @search, "paybatch = '$1'";
+
+ $orderby = "LOWER(company || ' ' || last || ' ' || first )";
+
+ } else {
+ die "unknown search magic: ". $cgi->param('magic');
+ }
my $search = '';
if ( @search ) {
$search = ' WHERE '. join(' AND ', @search);
}
-
+
$count_query = "SELECT COUNT(*), SUM(paid) ".
"FROM cust_pay LEFT JOIN cust_main USING ( custnum )".
$search;
-
+
$sql_query = {
'table' => 'cust_pay',
'select' => join(', ',
@@ -63,10 +81,10 @@
FS::UI::Web::cust_sql_fields(),
),
'hashref' => {},
- 'extra_sql' => "$search ORDER BY _date",
+ 'extra_sql' => "$search ORDER BY $orderby",
'addl_from' => 'LEFT JOIN cust_main USING ( custnum )',
};
-
+
} else {
$cgi->param('payinfo') =~ /^\s*(\d+)\s*$/ or die "illegal payinfo";