From 550685eff557af23e242c545d6a9e27a7ef44f23 Mon Sep 17 00:00:00 2001 From: ivan Date: Mon, 10 Oct 2005 12:20:57 +0000 Subject: [PATCH] updated quick payment entry --- Changes.1.5.8 | 5 + FS/FS/cust_pay.pm | 60 +++- README.1.5.8 | 2 + htetc/global.asa | 3 +- htetc/handler.pl | 1 + httemplate/docs/upgrade10.html | 1 + httemplate/index.html | 2 +- httemplate/misc/batch-cust_pay.html | 414 ++++++++++++++++++++++----- httemplate/misc/process/batch-cust_pay.cgi | 40 +++ httemplate/misc/xmlhttp-cust_main-search.cgi | 21 ++ httemplate/search/cust_pay.cgi | 104 ++++--- 11 files changed, 530 insertions(+), 123 deletions(-) create mode 100644 httemplate/misc/process/batch-cust_pay.cgi create mode 100644 httemplate/misc/xmlhttp-cust_main-search.cgi 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) 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 this patch 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 @@
Bookkeeping / Collections
-
Quick payment entry +
Quick payment entry

Credit card #
Invoice #
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') ) { %> - Error: <%= $cgi->param('error') %> + <%= $cgi->param('error') %>

<% } %> - + function select_customer() { -
+ var custnum = this.options[this.selectedIndex].value; + var customer = this.options[this.selectedIndex].text; -Batch

+ 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; + } + + @@ -102,33 +237,160 @@ + - +<% my $row = 0; + if ( $cgi->param('error') ) { + my $param = $cgi->Vars; +%> + + <% for ( $row = 0; exists($param->{"custnum$row"}); $row++ ) { %> + + + + + + + + + + + + + + + + <% } %> + +<% } %>
Customer Amount Check #
+ " rownum="<%= $row %>"> + + + " rownum="<%= $row %>"> + + + + + $" > + + " > + + <% if ( $param->{"error$row"} ) { %> + Error: <%= $param->{"error$row"} %> + <% } %> +
-
- +
- + + + +<%= include('/elements/xmlhttp.html', + 'url' => $p. 'misc/xmlhttp-cust_main-search.cgi', + 'subs' => [qw( custnum_search smart_search )], + ) +%> + + + 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"; -- 2.11.0