[Koha-patches] [PATCH] Fines System Updates - Code Cleanup & Additional Features

Kyle M Hall kyle.m.hall at gmail.com
Wed Mar 17 13:51:24 CET 2010


  * Major code cleanup for pay.pl
  * Added ability to negotiate fines ( i.e. Change the fine amount )
  * Added ability to pay, writeoff, and negotiate partial amounts.
  * Moved additional functions from pay.pl to Accounts.pm
---
 C4/Accounts.pm                                     |   57 ++++++-
 .../intranet-tmpl/prog/en/modules/members/pay.tmpl |  147 ++++++++++++++-
 members/pay.pl                                     |  198 +++++++-------------
 3 files changed, 263 insertions(+), 139 deletions(-)

diff --git a/C4/Accounts.pm b/C4/Accounts.pm
index bbc6c00..599c67f 100644
--- a/C4/Accounts.pm
+++ b/C4/Accounts.pm
@@ -35,7 +35,7 @@ BEGIN {
 	@EXPORT = qw(
 		&recordpayment &makepayment &manualinvoice
 		&getnextacctno &reconcileaccount &getcharges &getcredits
-		&getrefunds &chargelostitem
+		&getrefunds &chargelostitem &writeoff &negotiate
 		&ReversePayment
 	); # removed &fixaccounts
 }
@@ -170,7 +170,7 @@ sub makepayment {
 
     $dbh->do(
         "UPDATE  accountlines
-        SET     amountoutstanding = 0
+        SET     amountoutstanding = amountoutstanding - $amount
         WHERE   borrowernumber = $borrowernumber
           AND   accountno = $accountno
         "
@@ -648,6 +648,59 @@ sub getrefunds {
     return (@results);
 }
 
+sub writeoff {
+    my ( $borrowernumber, $accountnum, $itemnum, $accounttype, $amount ) = @_;
+    my $dbh  = C4::Context->dbh;
+    undef $itemnum unless $itemnum; # if no item is attached to fine, make sure to store it as a NULL
+    
+    my $query = "UPDATE accountlines SET amountoutstanding = amountoutstanding - $amount WHERE accountno = ? AND borrowernumber = ?";
+    my $sth = $dbh->prepare( $query );
+    $sth->execute( $accountnum, $borrowernumber );
+
+    my $next_accountno = getnextacctno( $borrowernumber );
+
+    $query = "INSERT INTO accountlines
+                ( borrowernumber, accountno, itemnumber, date, amount, description, accounttype )
+              VALUES 
+                ( ?, ?, ?, NOW(), ?, 'Writeoff', 'W' )
+             ";
+    $sth = $dbh->prepare( $query );
+    $sth->execute( $borrowernumber, $next_accountno, $itemnum, $amount );
+
+    UpdateStats( C4::Context->userenv->{branch}, 'writeoff', $amount, '', '', '', $borrowernumber );
+}
+
+sub negotiate {
+    my ( $borrowernumber, $accountno, $amount, $itemnumber ) = @_;
+    my $dbh  = C4::Context->dbh;
+    
+    my $query = "
+      UPDATE
+        accountlines
+      SET 
+        amount = amount - ?,
+        description = CONCAT( description, '*' ),
+        amountoutstanding = amountoutstanding - ?,
+        timestamp = NOW()
+      WHERE
+        accountno = ?
+      AND
+        borrowernumber = ?
+    ";    
+    my $sth = $dbh->prepare( $query );
+    $sth->execute( $amount, $amount, $accountno, $borrowernumber );
+
+    my $next_accountno = getnextacctno( $borrowernumber );
+    
+    $query = "INSERT INTO accountlines
+                ( borrowernumber, accountno, itemnumber, date, amount, description, accounttype )
+              VALUES 
+                ( ?, ?, ?, NOW(), ?, 'Negotiated', 'N' )
+             ";
+    $sth = $dbh->prepare( $query );
+    $sth->execute( $borrowernumber, $next_accountno, $itemnumber, $amount );
+}
+
 sub ReversePayment {
   my ( $borrowernumber, $accountno ) = @_;
   my $dbh = C4::Context->dbh;
diff --git a/koha-tmpl/intranet-tmpl/prog/en/modules/members/pay.tmpl b/koha-tmpl/intranet-tmpl/prog/en/modules/members/pay.tmpl
index 1177ba2..79fab31 100644
--- a/koha-tmpl/intranet-tmpl/prog/en/modules/members/pay.tmpl
+++ b/koha-tmpl/intranet-tmpl/prog/en/modules/members/pay.tmpl
@@ -1,6 +1,104 @@
 <!-- TMPL_INCLUDE NAME="doc-head-open.inc" -->
 <title>Koha &rsaquo; Patrons &rsaquo; Pay Fines for  <!-- TMPL_VAR NAME="firstname" --> <!-- TMPL_VAR NAME="surname" --></title>
 <!-- TMPL_INCLUDE NAME="doc-head-close.inc" -->
+
+<script type='text/javascript'>
+	function payfineOnChange( id, amount ) {
+		var selectName = "payfine" + id;
+		var inputName = "amount_to_pay" + id;
+
+		var mySelect = document.getElementsByName( selectName )[0];
+		var myInput = document.getElementsByName( inputName )[0];
+
+		if ( mySelect.value == "no" ) {
+			myInput.value = '';
+		} else {
+			if ( myInput.value == '' ) {
+				myInput.value = amount;
+				myInput.select().focus();
+			}
+		}
+	}
+
+	function amountOnChange( id ) {
+                var selectName = "payfine" + id;
+                var inputName = "amount_to_pay" + id;
+
+                var mySelect = document.getElementsByName( selectName )[0];
+                var myInput = document.getElementsByName( inputName )[0];         
+
+		if ( myInput.value == '' ) {
+			mySelect.selectedIndex = 0;
+		} else {
+			if ( mySelect.selectedIndex == 0 ) {
+				mySelect.selectedIndex = 1;
+			}
+		}
+	}
+
+	function checkField( fieldname, total ){
+
+        	var myInput = document.getElementsByName( fieldname )[0];
+		total = parseFloat( total );
+
+		if ( myInput.value != '' ) {
+		        var regExp = /^\d*(\.\d{2})?$/; // This regular express only accepts floating point numbers ex. 5, 5.30, .80, etc...
+		        if ( ! regExp.test( myInput.value ) ) {
+		                alert("Invalid Amount: " + myInput.value );
+				myInput.focus();
+				myInput.select();
+		                return false;
+		        }
+
+
+		        if ( myInput.value > total ) {
+		                if ( confirm("You are trying to make a payment larger then the Amount Outstanding.\n\nAre you sure you want to do this?") ) {
+		                        return true;
+		                } else {
+		                        myInput.focus();   
+		                        myInput.select();  
+		                        return false;
+		                }
+		        }
+
+		        if ( myInput.value.indexOf('.') < 0 ) {
+		                if ( confirm("The amount you've entered has no '.'\n\nAre you sure you want to do this?") ) {
+		                        return true;
+		                } else {
+		                        myInput.focus();   
+		                        myInput.select();  
+		
+		                        return false;
+		                }
+		        }
+
+		}
+
+	        return true;
+	}
+
+	function formatField( myInput ) {
+		if ( myInput.value && !isNaN( myInput.value ) ) {
+			myInput.value = formatAsCurrency( myInput.value );
+		}
+	}
+
+	function formatAsCurrency(number) {
+		number = number.toString().replace(/\$|\,/g,'');
+		if ( isNaN( number ) ) number = "0";
+		number = Math.floor( number * 100 + 0.50000000001 );
+		cents = number % 100;
+		number = Math.floor(number/100).toString();
+		
+		if( cents < 10 ) cents = "0" + cents;
+
+		for (var i = 0; i < Math.floor((number.length-(1+i))/3); i++) {
+			number = number.substring( 0, number.length - ( 4 * i + 3 ) ) + ',' + number.substring( number.length - ( 4 * i + 3 ) );
+		}
+		return number + '.' + cents;
+	}
+</script>
+
 </head>
 <body>
 <!-- TMPL_INCLUDE NAME="header.inc" -->
@@ -30,12 +128,13 @@
 
 <table>
 <tr>
-	<th>Fines &amp; Charges</th>
+	<th>Action</th>
+	<th>Amount</th>
 	<th>Description</th>
 	<th>Account Type</th>
 	<th>Notify id</th>
 	<th>Level</th>
-	<th>Amount</th>
+	<th>Fine Amount</th>
 	<th>Amount Outstanding</th>
 </tr>
 	
@@ -43,13 +142,14 @@
 	<!-- TMPL_LOOP name="loop_pay" -->
 <tr>
 	<td>
-	<!-- TMPL_IF NAME="net_balance" -->
-	<select name="payfine<!-- TMPL_VAR name="i" -->">
-	<option value="no">Unpaid</option>
-	<option value="yes">Paid</option>
-	<option value="wo">Writeoff</option>
+
+	<select name="payfine<!-- TMPL_VAR name="i" -->" onchange="payfineOnChange('<!-- TMPL_VAR name="i" -->', '<!-- TMPL_VAR name="amountoutstanding" -->')" >
+		<option value="no">Don't Pay</option>
+		<option value="yes">Pay</option>
+		<option value="wo">Writeoff</option>
+		<option value="ng">Negotiate</option>
 	</select>
-	<!-- /TMPL_IF -->
+
 	<input type="hidden" name="itemnumber<!-- TMPL_VAR name="i" -->" value="<!-- TMPL_VAR name="itemnumber" -->" />
 	<input type="hidden" name="accounttype<!-- TMPL_VAR name="i" -->" value="<!-- TMPL_VAR name="accounttype" -->" />
 	<input type="hidden" name="amount<!-- TMPL_VAR name="i" -->" value="<!-- TMPL_VAR name="amount" -->" />
@@ -60,6 +160,16 @@
 	<input type="hidden" name="notify_level<!-- TMPL_VAR name="i" -->" value="<!-- TMPL_VAR name="notify_level" -->" />
 	<input type="hidden" name="totals<!-- TMPL_VAR name="i" -->" value="<!-- TMPL_VAR name="totals" -->" />
 	</td>
+	<td>
+		<input 
+			type="text" 
+			name="amount_to_pay<!-- TMPL_VAR name="i" -->" 
+			size="6" 
+			onkeyup="amountOnChange('<!-- TMPL_VAR name="i" -->')"
+			onchange="formatField( this )";
+			onblur="return checkField( 'amount_to_pay<!-- TMPL_VAR name="i" -->', '<!-- TMPL_VAR name="amountoutstanding" -->')"
+		/>
+	</td>
 	<td><!-- TMPL_VAR name="description" --> <!-- TMPL_VAR name="title" escape="html" --></td>
 	<td><!-- TMPL_VAR name="accounttype" --></td>
 	<td><!-- TMPL_VAR name="notify_id" --></td>
@@ -71,19 +181,34 @@
 <!-- TMPL_IF  NAME="total"-->
 <tr>
 
-	<td colspan="6">Sub Total</td>
+	<td colspan="7">Sub Total</td>
 	<td><!-- TMPL_VAR name="total" --></td>
 </tr>
 <!--/TMPL_IF-->
 <!-- /TMPL_LOOP  -->
 <tr>
-	<td colspan="6">Total Due</td>
+	<td colspan="7">Total Due</td>
 	<td><!-- TMPL_VAR name="total" --></td>
 </tr>
 </table>
-<fieldset class="action"><input type="submit" name="submit"  value="Make Payment" class="submit" /> <a class="cancel" href="/cgi-bin/koha/members/boraccount.pl?borrowernumber=<!-- TMPL_VAR NAME="borrowernumber" -->">Cancel</a></fieldset></form><!-- TMPL_ELSE --><p><!-- TMPL_VAR NAME="firstname" --> <!-- TMPL_VAR NAME="surname" --> has no outstanding fines.</p><!-- /TMPL_IF -->
+<fieldset class="action"><input type="submit" name="submit"  value="Update Fines & Charges" class="submit" /> <a class="cancel" href="/cgi-bin/koha/members/boraccount.pl?borrowernumber=<!-- TMPL_VAR NAME="borrowernumber" -->">Cancel</a></fieldset></form>
+
+<fieldset>
+  <legend>Pay Fines by Amount</legend>
+  <form class="inline" action="pay.pl" method="post" onsubmit="return checkField( 'pay_by_amount', '<!-- TMPL_VAR name="total" -->')">
+    <input type="hidden" name="borrowernumber" value="<!-- TMPL_VAR name="borrowernumber" -->" />
+    <label for="pay_by_amount">Amount to be paid:</label>
+    <input id="pay_by_amount" name="pay_by_amount" type="text" />
+    <input type="submit" name="submit"  value="Make Partial Payment" class="submit"/>
+  </form>
+</fieldset>
 </div></div>
 
+<!-- TMPL_ELSE -->
+	<p><!-- TMPL_VAR NAME="firstname" --> <!-- TMPL_VAR NAME="surname" --> has no outstanding fines.</p>
+<!-- /TMPL_IF -->
+
+
 </div>
 </div>
 
diff --git a/members/pay.pl b/members/pay.pl
index 5a36bd7..edbe9e8 100755
--- a/members/pay.pl
+++ b/members/pay.pl
@@ -52,86 +52,86 @@ my ( $template, $loggedinuser, $cookie ) = get_template_and_user(
     }
 );
 
-my $borrowernumber = $input->param('borrowernumber');
-if ( $borrowernumber eq '' ) {
-    $borrowernumber = $input->param('borrowernumber0');
-}
+my $borrowernumber = $input->param('borrowernumber') || $input->param('borrowernumber00');
 
 # get borrower details
 my $data = GetMember( borrowernumber => $borrowernumber );
-my $user = $input->remote_user;
 
 # get account details
 my $branches = GetBranches();
-my $branch   = GetBranch( $input, $branches );
-
-my @names = $input->param;
-my %inp;
-my $check = 0;
-for ( my $i = 0 ; $i < @names ; $i++ ) {
-    my $temp = $input->param( $names[$i] );
-    if ( $temp eq 'wo' ) {
-        $inp{ $names[$i] } = $temp;
-        $check = 1;
-    }
-    if ( $temp eq 'yes' ) {
-
-# FIXME : using array +4, +5, +6 is dirty. Should use arrays for each accountline
-        my $amount         = $input->param( $names[ $i + 4 ] );
-        my $borrowernumber = $input->param( $names[ $i + 5 ] );
-        my $accountno      = $input->param( $names[ $i + 6 ] );
-        makepayment( $borrowernumber, $accountno, $amount, $user, $branch );
-        $check = 2;
+my $branch = GetBranch( $input, $branches );
+
+my $i = 0;
+my $index = '00';
+while ( $input->param( "payfine$index") ) {
+warn "Payfine$index: " .  $input->param( "payfine$index" );   
+    my $payfine = $input->param( "payfine$index" );
+    my $amount         = $input->param( "amount$index" );
+    my $accountno      = $input->param( "accountno$index" );
+    my $amount_to_pay  = $input->param( "amount_to_pay$index" );
+    my $itemnumber     = $input->param( "itemnumber$index" );
+    my $accounttype    = $input->param( "accounttype$index" );
+    $amount = $amount_to_pay if ( $amount_to_pay );
+
+    if ( $payfine eq 'yes' ) { ## Standard Fine Payment        
+        makepayment( $borrowernumber, $accountno, $amount, '', $branch );
+    } elsif ( $payfine eq 'wo' ) { ## Writeoff
+      writeoff( $borrowernumber, $accountno, $itemnumber, $accounttype, $amount );
+    } elsif ( $payfine eq 'ng' ) { ## Negotiate Fine
+      negotiate( $borrowernumber, $accountno, $amount, $itemnumber );
     }
+    
+    $i++;
+    $index = $i;
+    $index = "0$i" if ( $i < 10 );
 }
-my $total = $input->param('total') || '';
-if ( $check == 0 ) {
-    if ( $total ne '' ) {
-        recordpayment( $borrowernumber, $total );
-    }
 
-    my ( $total, $accts, $numaccts) = GetMemberAccountRecords( $borrowernumber );
-
-    my @allfile;
-    my @notify = NumberNotifyId($borrowernumber);
-
-    my $numberofnotify = scalar(@notify);
-    for ( my $j = 0 ; $j < scalar(@notify) ; $j++ ) {
-        my @loop_pay;
-        my ( $total , $accts, $numaccts) =
-          GetBorNotifyAcctRecord( $borrowernumber, $notify[$j] );
-        for ( my $i = 0 ; $i < $numaccts ; $i++ ) {
-            my %line;
-            if ( $accts->[$i]{'amountoutstanding'} != 0 ) {
-                $accts->[$i]{'amount'}            += 0.00;
-                $accts->[$i]{'amountoutstanding'} += 0.00;
-                $line{i}           = $j . "" . $i;
-                $line{itemnumber}  = $accts->[$i]{'itemnumber'};
-                $line{accounttype} = $accts->[$i]{'accounttype'};
-                $line{amount}      = sprintf( "%.2f", $accts->[$i]{'amount'} );
-                $line{amountoutstanding} =
-                  sprintf( "%.2f", $accts->[$i]{'amountoutstanding'} );
-                $line{borrowernumber} = $borrowernumber;
-                $line{accountno}      = $accts->[$i]{'accountno'};
-                $line{description}    = $accts->[$i]{'description'};
-                $line{title}          = $accts->[$i]{'title'};
-                $line{notify_id}      = $accts->[$i]{'notify_id'};
-                $line{notify_level}   = $accts->[$i]{'notify_level'};
-                $line{net_balance} = 1 if($accts->[$i]{'amountoutstanding'} > 0); # you can't pay a credit.
-                push( @loop_pay, \%line );
-            }
-        }
+## Pay by Amount
+my $total_to_pay = $input->param('pay_by_amount');
+if ( $total_to_pay ) {
+    recordpayment( $borrowernumber, $total_to_pay );
+}
 
-        my $totalnotify = AmountNotify( $notify[$j], $borrowernumber );
-        ( $totalnotify = '0' ) if ( $totalnotify =~ /^0.00/ );
-        push @allfile,
-          {
-            'loop_pay' => \@loop_pay,
-            'notify'   => $notify[$j],
-            'total'    =>  sprintf( "%.2f",$totalnotify),
-			
-          };
+## Get Fine Details
+my ( $total, $accts, $numaccts) = GetMemberAccountRecords( $borrowernumber );
+
+my @allfile;
+my @notify = NumberNotifyId($borrowernumber);
+
+my $numberofnotify = scalar(@notify);
+for ( my $j = 0 ; $j < scalar(@notify) ; $j++ ) {
+    my @loop_pay;
+    my ( $total , $accts, $numaccts) = GetBorNotifyAcctRecord( $borrowernumber, $notify[$j] );
+    for ( my $i = 0 ; $i < $numaccts ; $i++ ) {
+        my %line;
+        if ( $accts->[$i]{'amountoutstanding'} != 0 ) {
+            $accts->[$i]{'amount'}            += 0.00;
+            $accts->[$i]{'amountoutstanding'} += 0.00;
+            $line{i}           = $j . "" . $i;
+            $line{itemnumber}  = $accts->[$i]{'itemnumber'};
+            $line{accounttype} = $accts->[$i]{'accounttype'};
+            $line{amount}      = sprintf( "%.2f", $accts->[$i]{'amount'} );
+            $line{amountoutstanding} = sprintf( "%.2f", $accts->[$i]{'amountoutstanding'} );
+            $line{borrowernumber} = $borrowernumber;
+            $line{accountno}      = $accts->[$i]{'accountno'};
+            $line{description}    = $accts->[$i]{'description'};
+            $line{title}          = $accts->[$i]{'title'};
+            $line{notify_id}      = $accts->[$i]{'notify_id'};
+            $line{notify_level}   = $accts->[$i]{'notify_level'};
+            $line{net_balance} = 1 if($accts->[$i]{'amountoutstanding'} > 0); # you can't pay a credit.
+            push( @loop_pay, \%line );
+        }
     }
+ 
+    my $totalnotify = AmountNotify( $notify[$j], $borrowernumber );
+    $totalnotify = '0' if ( $totalnotify =~ /^0.00/ );
+    push @allfile,
+      {
+        'loop_pay' => \@loop_pay,
+        'notify'   => $notify[$j],
+        'total'    =>  sprintf( "%.2f",$totalnotify),
+      };
+}
 	
 if ( $data->{'category_type'} eq 'C') {
    my  ( $catcodes, $labels ) =  GetborCatFromCatType( 'A', 'WHERE category_type = ?' );
@@ -144,7 +144,7 @@ $template->param( adultborrower => 1 ) if ( $data->{'category_type'} eq 'A' );
 my ($picture, $dberror) = GetPatronImage($data->{'cardnumber'});
 $template->param( picture => 1 ) if $picture;
 	
-    $template->param(
+$template->param(
         allfile        => \@allfile,
         firstname      => $data->{'firstname'},
         surname        => $data->{'surname'},
@@ -164,60 +164,6 @@ $template->param( picture => 1 ) if $picture;
 	branchname => GetBranchName($data->{'branchcode'}),
 	is_child        => ($data->{'category_type'} eq 'C'),
         total          => sprintf( "%.2f", $total )
-    );
-    output_html_with_http_headers $input, $cookie, $template->output;
-
-}
-else {
-
-    my %inp;
-    my @name = $input->param;
-    for ( my $i = 0 ; $i < @name ; $i++ ) {
-        my $test = $input->param( $name[$i] );
-        if ( $test eq 'wo' ) {
-            my $temp = $name[$i];
-            $temp =~ s/payfine//;
-            $inp{ $name[$i] } = $temp;
-        }
-    }
-    my $borrowernumber;
-    while ( my ( $key, $value ) = each %inp ) {
-
-        my $accounttype = $input->param("accounttype$value");
-        $borrowernumber = $input->param("borrowernumber$value");
-        my $itemno    = $input->param("itemnumber$value");
-        my $amount    = $input->param("amount$value");
-        my $accountno = $input->param("accountno$value");
-        writeoff( $borrowernumber, $accountno, $itemno, $accounttype, $amount );
-    }
-    $borrowernumber = $input->param('borrowernumber');
-    print $input->redirect(
-        "/cgi-bin/koha/members/boraccount.pl?borrowernumber=$borrowernumber");
-}
+);
 
-sub writeoff {
-    my ( $borrowernumber, $accountnum, $itemnum, $accounttype, $amount ) = @_;
-    my $user = $input->remote_user;
-    my $dbh  = C4::Context->dbh;
-    undef $itemnum unless $itemnum; # if no item is attached to fine, make sure to store it as a NULL
-    my $sth =
-      $dbh->prepare(
-"Update accountlines set amountoutstanding=0 where accountno=? and borrowernumber=?"
-      );
-    $sth->execute( $accountnum, $borrowernumber );
-    $sth->finish;
-    $sth = $dbh->prepare("select max(accountno) from accountlines");
-    $sth->execute;
-    my $account = $sth->fetchrow_hashref;
-    $sth->finish;
-    $account->{'max(accountno)'}++;
-    $sth = $dbh->prepare(
-"insert into accountlines (borrowernumber,accountno,itemnumber,date,amount,description,accounttype)
-						values (?,?,?,now(),?,'Writeoff','W')"
-    );
-    $sth->execute( $borrowernumber, $account->{'max(accountno)'},
-        $itemnum, $amount );
-    $sth->finish;
-    UpdateStats( $branch, 'writeoff', $amount, '', '', '',
-        $borrowernumber );
-}
+output_html_with_http_headers $input, $cookie, $template->output;
-- 
1.5.6.5




More information about the Koha-patches mailing list