Koha/C4/Accounts.pm
Colin Campbell 507c7f4224 Bug 3498 [ENH] Allow Partial Payment of Fines
Allow partial payment of outstanding fines
against either individual fine entries or as a lump payment

Sponsered by East Brunswick Public Library, East Brunswick, NJ, USA

NB: Adds a version of the include circ-menu.inc as circ-menu.tt
this has the same functionality as the old include but does not
require all the borrowers attributes to be passed as global
scoped variables

Signed-off-by: Liz Rea <lrea@nekls.org>

Bug 3498 - Documentation update to explain what every button does.

Help file updated to the following:
Pay and Writeoff Fines

Each line item can be paid in full, partially paid, or written off.
Pay a fine in full

    Click "Pay" next to the fine you want to pay in full
    The full amount of the fine will be populated for you in the "Collect From Patron" box
    Click "Confirm"
    The fine will be removed from outstanding fines, and displayed as fully paid.

Pay a partial fine

    Click "Pay" next to the fine you want to partially pay
    Enter the amount you are collecting from the patron in the "Collect From Patron" box
    Click "Confirm"
    The fine will be updated to show the original Amount, and the current Amount Outstanding

Writeoff a single fine

    Click "Writeoff" next to the fine you wish to writeoff.
    The fine will be removed from outstanding fines, and displayed as fully paid.

Pay an amount towards all fines

    Click the "Pay Amount" button
    Enter the amount you are collecting from the patron in "Collect from Patron." The sum of all fines is shown in "Total Amount Outstanding"
    Click "Confirm"
    The fine totals will be updated with the payment applied to oldest fines first.

Writeoff All fines

    Click the "Writeoff All" button
    All fines will be removed from outstanding fines, and displayed as written off.

Pay Selected fines

    Check the selection boxes next to the fines you wish to pay, click "Pay Selected"
    Enter an amount to pay towards the fines.
    Click "Confirm"
    The fine totals will be updated with the payment applied to the oldest selected fines first.

Bug 3498 - adding parens to TT IF Statements for style

pay.tt

Bug 3498 - Add parens to TT IF statements for style

paycollect.tt

Signed-off-by: Ian Walls <ian.walls@bywatersolutions.com>
Signed-off-by: Chris Cormack <chrisc@catalyst.net.nz>
2011-09-13 08:40:49 +12:00

800 lines
26 KiB
Perl

package C4::Accounts;
# Copyright 2000-2002 Katipo Communications
#
# This file is part of Koha.
#
# Koha is free software; you can redistribute it and/or modify it under the
# terms of the GNU General Public License as published by the Free Software
# Foundation; either version 2 of the License, or (at your option) any later
# version.
#
# Koha is distributed in the hope that it will be useful, but WITHOUT ANY
# WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
# A PARTICULAR PURPOSE. See the GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License along
# with Koha; if not, write to the Free Software Foundation, Inc.,
# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
use strict;
#use warnings; FIXME - Bug 2505
use C4::Context;
use C4::Stats;
use C4::Members;
use C4::Items;
use C4::Circulation qw(MarkIssueReturned);
use vars qw($VERSION @ISA @EXPORT);
BEGIN {
# set the version for version checking
$VERSION = 3.03;
require Exporter;
@ISA = qw(Exporter);
@EXPORT = qw(
&recordpayment &makepayment &manualinvoice
&getnextacctno &reconcileaccount &getcharges &ModNote &getcredits
&getrefunds &chargelostitem
&ReversePayment
makepartialpayment
recordpayment_selectaccts
);
}
=head1 NAME
C4::Accounts - Functions for dealing with Koha accounts
=head1 SYNOPSIS
use C4::Accounts;
=head1 DESCRIPTION
The functions in this module deal with the monetary aspect of Koha,
including looking up and modifying the amount of money owed by a
patron.
=head1 FUNCTIONS
=head2 recordpayment
&recordpayment($borrowernumber, $payment);
Record payment by a patron. C<$borrowernumber> is the patron's
borrower number. C<$payment> is a floating-point number, giving the
amount that was paid.
Amounts owed are paid off oldest first. That is, if the patron has a
$1 fine from Feb. 1, another $1 fine from Mar. 1, and makes a payment
of $1.50, then the oldest fine will be paid off in full, and $0.50
will be credited to the next one.
=cut
#'
sub recordpayment {
#here we update the account lines
my ( $borrowernumber, $data ) = @_;
my $dbh = C4::Context->dbh;
my $newamtos = 0;
my $accdata = "";
my $branch = C4::Context->userenv->{'branch'};
my $amountleft = $data;
# begin transaction
my $nextaccntno = getnextacctno($borrowernumber);
# get lines with outstanding amounts to offset
my $sth = $dbh->prepare(
"SELECT * FROM accountlines
WHERE (borrowernumber = ?) AND (amountoutstanding<>0)
ORDER BY date"
);
$sth->execute($borrowernumber);
# offset transactions
while ( ( $accdata = $sth->fetchrow_hashref ) and ( $amountleft > 0 ) ) {
if ( $accdata->{'amountoutstanding'} < $amountleft ) {
$newamtos = 0;
$amountleft -= $accdata->{'amountoutstanding'};
}
else {
$newamtos = $accdata->{'amountoutstanding'} - $amountleft;
$amountleft = 0;
}
my $thisacct = $accdata->{accountno};
my $usth = $dbh->prepare(
"UPDATE accountlines SET amountoutstanding= ?
WHERE (borrowernumber = ?) AND (accountno=?)"
);
$usth->execute( $newamtos, $borrowernumber, $thisacct );
$usth->finish;
# $usth = $dbh->prepare(
# "INSERT INTO accountoffsets
# (borrowernumber, accountno, offsetaccount, offsetamount)
# VALUES (?,?,?,?)"
# );
# $usth->execute( $borrowernumber, $accdata->{'accountno'},
# $nextaccntno, $newamtos );
$usth->finish;
}
# create new line
my $usth = $dbh->prepare(
"INSERT INTO accountlines
(borrowernumber, accountno,date,amount,description,accounttype,amountoutstanding)
VALUES (?,?,now(),?,'Payment,thanks','Pay',?)"
);
$usth->execute( $borrowernumber, $nextaccntno, 0 - $data, 0 - $amountleft );
$usth->finish;
UpdateStats( $branch, 'payment', $data, '', '', '', $borrowernumber, $nextaccntno );
$sth->finish;
}
=head2 makepayment
&makepayment($borrowernumber, $acctnumber, $amount, $branchcode);
Records the fact that a patron has paid off the entire amount he or
she owes.
C<$borrowernumber> is the patron's borrower number. C<$acctnumber> is
the account that was credited. C<$amount> is the amount paid (this is
only used to record the payment. It is assumed to be equal to the
amount owed). C<$branchcode> is the code of the branch where payment
was made.
=cut
#'
# FIXME - I'm not at all sure about the above, because I don't
# understand what the acct* tables in the Koha database are for.
sub makepayment {
#here we update both the accountoffsets and the account lines
#updated to check, if they are paying off a lost item, we return the item
# from their card, and put a note on the item record
my ( $borrowernumber, $accountno, $amount, $user, $branch ) = @_;
my $dbh = C4::Context->dbh;
my $manager_id = 0;
$manager_id = C4::Context->userenv->{'number'} if C4::Context->userenv;
# begin transaction
my $nextaccntno = getnextacctno($borrowernumber);
my $newamtos = 0;
my $sth =
$dbh->prepare(
"SELECT * FROM accountlines WHERE borrowernumber=? AND accountno=?");
$sth->execute( $borrowernumber, $accountno );
my $data = $sth->fetchrow_hashref;
$sth->finish;
if($data->{'accounttype'} eq "Pay"){
my $udp =
$dbh->prepare(
"UPDATE accountlines
SET amountoutstanding = 0, description = 'Payment,thanks'
WHERE borrowernumber = ?
AND accountno = ?
"
);
$udp->execute($borrowernumber, $accountno );
$udp->finish;
}else{
my $udp =
$dbh->prepare(
"UPDATE accountlines
SET amountoutstanding = 0
WHERE borrowernumber = ?
AND accountno = ?
"
);
$udp->execute($borrowernumber, $accountno );
$udp->finish;
# create new line
my $payment = 0 - $amount;
my $ins =
$dbh->prepare(
"INSERT
INTO accountlines (borrowernumber, accountno, date, amount, description, accounttype, amountoutstanding)
VALUES ( ?, ?, now(), ?, 'Payment,thanks', 'Pay', 0)"
);
$ins->execute($borrowernumber, $nextaccntno, $payment);
$ins->finish;
}
# FIXME - The second argument to &UpdateStats is supposed to be the
# branch code.
# UpdateStats is now being passed $accountno too. MTJ
UpdateStats( $user, 'payment', $amount, '', '', '', $borrowernumber,
$accountno );
#from perldoc: for SELECT only #$sth->finish;
#check to see what accounttype
if ( $data->{'accounttype'} eq 'Rep' || $data->{'accounttype'} eq 'L' ) {
returnlost( $borrowernumber, $data->{'itemnumber'} );
}
}
=head2 getnextacctno
$nextacct = &getnextacctno($borrowernumber);
Returns the next unused account number for the patron with the given
borrower number.
=cut
#'
# FIXME - Okay, so what does the above actually _mean_?
sub getnextacctno ($) {
my ($borrowernumber) = shift or return undef;
my $sth = C4::Context->dbh->prepare(
"SELECT accountno+1 FROM accountlines
WHERE (borrowernumber = ?)
ORDER BY accountno DESC
LIMIT 1"
);
$sth->execute($borrowernumber);
return ($sth->fetchrow || 1);
}
=head2 fixaccounts (removed)
&fixaccounts($borrowernumber, $accountnumber, $amount);
#'
# FIXME - I don't understand what this function does.
sub fixaccounts {
my ( $borrowernumber, $accountno, $amount ) = @_;
my $dbh = C4::Context->dbh;
my $sth = $dbh->prepare(
"SELECT * FROM accountlines WHERE borrowernumber=?
AND accountno=?"
);
$sth->execute( $borrowernumber, $accountno );
my $data = $sth->fetchrow_hashref;
# FIXME - Error-checking
my $diff = $amount - $data->{'amount'};
my $outstanding = $data->{'amountoutstanding'} + $diff;
$sth->finish;
$dbh->do(<<EOT);
UPDATE accountlines
SET amount = '$amount',
amountoutstanding = '$outstanding'
WHERE borrowernumber = $borrowernumber
AND accountno = $accountno
EOT
# FIXME: exceedingly bad form. Use prepare with placholders ("?") in query and execute args.
}
=cut
sub returnlost{
my ( $borrowernumber, $itemnum ) = @_;
C4::Circulation::MarkIssueReturned( $borrowernumber, $itemnum );
my $borrower = C4::Members::GetMember( 'borrowernumber'=>$borrowernumber );
my @datearr = localtime(time);
my $date = ( 1900 + $datearr[5] ) . "-" . ( $datearr[4] + 1 ) . "-" . $datearr[3];
my $bor = "$borrower->{'firstname'} $borrower->{'surname'} $borrower->{'cardnumber'}";
ModItem({ paidfor => "Paid for by $bor $date" }, undef, $itemnum);
}
sub chargelostitem{
# lost ==1 Lost, lost==2 longoverdue, lost==3 lost and paid for
# FIXME: itemlost should be set to 3 after payment is made, should be a warning to the interface that
# a charge has been added
# FIXME : if no replacement price, borrower just doesn't get charged?
my $dbh = C4::Context->dbh();
my ($itemnumber) = @_;
my $sth=$dbh->prepare("SELECT issues.*,items.*,biblio.title
FROM issues
JOIN items USING (itemnumber)
JOIN biblio USING (biblionumber)
WHERE issues.itemnumber=?");
$sth->execute($itemnumber);
my $issues=$sth->fetchrow_hashref();
# if a borrower lost the item, add a replacement cost to the their record
if ( $issues->{borrowernumber} ){
# first make sure the borrower hasn't already been charged for this item
my $sth1=$dbh->prepare("SELECT * from accountlines
WHERE borrowernumber=? AND itemnumber=? and accounttype='L'");
$sth1->execute($issues->{'borrowernumber'},$itemnumber);
my $existing_charge_hashref=$sth1->fetchrow_hashref();
# OK, they haven't
unless ($existing_charge_hashref) {
# This item is on issue ... add replacement cost to the borrower's record and mark it returned
# Note that we add this to the account even if there's no replacement price, allowing some other
# process (or person) to update it, since we don't handle any defaults for replacement prices.
my $accountno = getnextacctno($issues->{'borrowernumber'});
my $sth2=$dbh->prepare("INSERT INTO accountlines
(borrowernumber,accountno,date,amount,description,accounttype,amountoutstanding,itemnumber)
VALUES (?,?,now(),?,?,'L',?,?)");
$sth2->execute($issues->{'borrowernumber'},$accountno,$issues->{'replacementprice'},
"Lost Item $issues->{'title'} $issues->{'barcode'}",
$issues->{'replacementprice'},$itemnumber);
$sth2->finish;
# FIXME: Log this ?
}
#FIXME : Should probably have a way to distinguish this from an item that really was returned.
#warn " $issues->{'borrowernumber'} / $itemnumber ";
C4::Circulation::MarkIssueReturned($issues->{borrowernumber},$itemnumber);
# Shouldn't MarkIssueReturned do this?
C4::Items::ModItem({ onloan => undef }, undef, $itemnumber);
}
$sth->finish;
}
=head2 manualinvoice
&manualinvoice($borrowernumber, $itemnumber, $description, $type,
$amount, $note);
C<$borrowernumber> is the patron's borrower number.
C<$description> is a description of the transaction.
C<$type> may be one of C<CS>, C<CB>, C<CW>, C<CF>, C<CL>, C<N>, C<L>,
or C<REF>.
C<$itemnumber> is the item involved, if pertinent; otherwise, it
should be the empty string.
=cut
#'
# FIXME: In Koha 3.0 , the only account adjustment 'types' passed to this function
# are :
# 'C' = CREDIT
# 'FOR' = FORGIVEN (Formerly 'F', but 'F' is taken to mean 'FINE' elsewhere)
# 'N' = New Card fee
# 'F' = Fine
# 'A' = Account Management fee
# 'M' = Sundry
# 'L' = Lost Item
#
sub manualinvoice {
my ( $borrowernumber, $itemnum, $desc, $type, $amount, $note ) = @_;
my $manager_id = 0;
$manager_id = C4::Context->userenv->{'number'} if C4::Context->userenv;
my $dbh = C4::Context->dbh;
my $notifyid = 0;
my $insert;
my $accountno = getnextacctno($borrowernumber);
my $amountleft = $amount;
# if ( $type eq 'CS'
# || $type eq 'CB'
# || $type eq 'CW'
# || $type eq 'CF'
# || $type eq 'CL' )
# {
# my $amount2 = $amount * -1; # FIXME - $amount2 = -$amount
# $amountleft =
# fixcredit( $borrowernumber, $amount2, $itemnum, $type, $user );
# }
if ( $type eq 'N' ) {
$desc .= " New Card";
}
if ( $type eq 'F' ) {
$desc .= " Fine";
}
if ( $type eq 'A' ) {
$desc .= " Account Management fee";
}
if ( $type eq 'M' ) {
$desc .= " Sundry";
}
if ( $type eq 'L' && $desc eq '' ) {
$desc = " Lost Item";
}
# if ( $type eq 'REF' ) {
# $desc .= " Cash Refund";
# $amountleft = refund( '', $borrowernumber, $amount );
# }
if ( ( $type eq 'L' )
or ( $type eq 'F' )
or ( $type eq 'A' )
or ( $type eq 'N' )
or ( $type eq 'M' ) )
{
$notifyid = 1;
}
if ( $itemnum ) {
$desc .= ' ' . $itemnum;
my $sth = $dbh->prepare(
'INSERT INTO accountlines
(borrowernumber, accountno, date, amount, description, accounttype, amountoutstanding, itemnumber,notify_id, note, manager_id)
VALUES (?, ?, now(), ?,?, ?,?,?,?,?,?)');
$sth->execute($borrowernumber, $accountno, $amount, $desc, $type, $amountleft, $itemnum,$notifyid, $note, $manager_id) || return $sth->errstr;
} else {
my $sth=$dbh->prepare("INSERT INTO accountlines
(borrowernumber, accountno, date, amount, description, accounttype, amountoutstanding,notify_id, note, manager_id)
VALUES (?, ?, now(), ?, ?, ?, ?,?,?,?)"
);
$sth->execute( $borrowernumber, $accountno, $amount, $desc, $type,
$amountleft, $notifyid, $note, $manager_id );
}
return 0;
}
=head2 fixcredit #### DEPRECATED
$amountleft = &fixcredit($borrowernumber, $data, $barcode, $type, $user);
This function is only used internally, not exported.
=cut
# This function is deprecated in 3.0
sub fixcredit {
#here we update both the accountoffsets and the account lines
my ( $borrowernumber, $data, $barcode, $type, $user ) = @_;
my $dbh = C4::Context->dbh;
my $newamtos = 0;
my $accdata = "";
my $amountleft = $data;
if ( $barcode ne '' ) {
my $item = GetBiblioFromItemNumber( '', $barcode );
my $nextaccntno = getnextacctno($borrowernumber);
my $query = "SELECT * FROM accountlines WHERE (borrowernumber=?
AND itemnumber=? AND amountoutstanding > 0)";
if ( $type eq 'CL' ) {
$query .= " AND (accounttype = 'L' OR accounttype = 'Rep')";
}
elsif ( $type eq 'CF' ) {
$query .= " AND (accounttype = 'F' OR accounttype = 'FU' OR
accounttype='Res' OR accounttype='Rent')";
}
elsif ( $type eq 'CB' ) {
$query .= " and accounttype='A'";
}
# print $query;
my $sth = $dbh->prepare($query);
$sth->execute( $borrowernumber, $item->{'itemnumber'} );
$accdata = $sth->fetchrow_hashref;
$sth->finish;
if ( $accdata->{'amountoutstanding'} < $amountleft ) {
$newamtos = 0;
$amountleft -= $accdata->{'amountoutstanding'};
}
else {
$newamtos = $accdata->{'amountoutstanding'} - $amountleft;
$amountleft = 0;
}
my $thisacct = $accdata->{accountno};
my $usth = $dbh->prepare(
"UPDATE accountlines SET amountoutstanding= ?
WHERE (borrowernumber = ?) AND (accountno=?)"
);
$usth->execute( $newamtos, $borrowernumber, $thisacct );
$usth->finish;
$usth = $dbh->prepare(
"INSERT INTO accountoffsets
(borrowernumber, accountno, offsetaccount, offsetamount)
VALUES (?,?,?,?)"
);
$usth->execute( $borrowernumber, $accdata->{'accountno'},
$nextaccntno, $newamtos );
$usth->finish;
}
# begin transaction
my $nextaccntno = getnextacctno($borrowernumber);
# get lines with outstanding amounts to offset
my $sth = $dbh->prepare(
"SELECT * FROM accountlines
WHERE (borrowernumber = ?) AND (amountoutstanding >0)
ORDER BY date"
);
$sth->execute($borrowernumber);
# print $query;
# offset transactions
while ( ( $accdata = $sth->fetchrow_hashref ) and ( $amountleft > 0 ) ) {
if ( $accdata->{'amountoutstanding'} < $amountleft ) {
$newamtos = 0;
$amountleft -= $accdata->{'amountoutstanding'};
}
else {
$newamtos = $accdata->{'amountoutstanding'} - $amountleft;
$amountleft = 0;
}
my $thisacct = $accdata->{accountno};
my $usth = $dbh->prepare(
"UPDATE accountlines SET amountoutstanding= ?
WHERE (borrowernumber = ?) AND (accountno=?)"
);
$usth->execute( $newamtos, $borrowernumber, $thisacct );
$usth->finish;
$usth = $dbh->prepare(
"INSERT INTO accountoffsets
(borrowernumber, accountno, offsetaccount, offsetamount)
VALUE (?,?,?,?)"
);
$usth->execute( $borrowernumber, $accdata->{'accountno'},
$nextaccntno, $newamtos );
$usth->finish;
}
$sth->finish;
$type = "Credit " . $type;
UpdateStats( $user, $type, $data, $user, '', '', $borrowernumber );
$amountleft *= -1;
return ($amountleft);
}
=head2 refund
#FIXME : DEPRECATED SUB
This subroutine tracks payments and/or credits against fines/charges
using the accountoffsets table, which is not used consistently in
Koha's fines management, and so is not used in 3.0
=cut
sub refund {
#here we update both the accountoffsets and the account lines
my ( $borrowernumber, $data ) = @_;
my $dbh = C4::Context->dbh;
my $newamtos = 0;
my $accdata = "";
my $amountleft = $data * -1;
# begin transaction
my $nextaccntno = getnextacctno($borrowernumber);
# get lines with outstanding amounts to offset
my $sth = $dbh->prepare(
"SELECT * FROM accountlines
WHERE (borrowernumber = ?) AND (amountoutstanding<0)
ORDER BY date"
);
$sth->execute($borrowernumber);
# print $amountleft;
# offset transactions
while ( ( $accdata = $sth->fetchrow_hashref ) and ( $amountleft < 0 ) ) {
if ( $accdata->{'amountoutstanding'} > $amountleft ) {
$newamtos = 0;
$amountleft -= $accdata->{'amountoutstanding'};
}
else {
$newamtos = $accdata->{'amountoutstanding'} - $amountleft;
$amountleft = 0;
}
# print $amountleft;
my $thisacct = $accdata->{accountno};
my $usth = $dbh->prepare(
"UPDATE accountlines SET amountoutstanding= ?
WHERE (borrowernumber = ?) AND (accountno=?)"
);
$usth->execute( $newamtos, $borrowernumber, $thisacct );
$usth->finish;
$usth = $dbh->prepare(
"INSERT INTO accountoffsets
(borrowernumber, accountno, offsetaccount, offsetamount)
VALUES (?,?,?,?)"
);
$usth->execute( $borrowernumber, $accdata->{'accountno'},
$nextaccntno, $newamtos );
$usth->finish;
}
$sth->finish;
return ($amountleft);
}
sub getcharges {
my ( $borrowerno, $timestamp, $accountno ) = @_;
my $dbh = C4::Context->dbh;
my $timestamp2 = $timestamp - 1;
my $query = "";
my $sth = $dbh->prepare(
"SELECT * FROM accountlines WHERE borrowernumber=? AND accountno = ?"
);
$sth->execute( $borrowerno, $accountno );
my @results;
while ( my $data = $sth->fetchrow_hashref ) {
push @results,$data;
}
return (@results);
}
sub ModNote {
my ( $borrowernumber, $accountno, $note ) = @_;
my $dbh = C4::Context->dbh;
my $sth = $dbh->prepare('UPDATE accountlines SET note = ? WHERE borrowernumber = ? AND accountno = ?');
$sth->execute( $note, $borrowernumber, $accountno );
}
sub getcredits {
my ( $date, $date2 ) = @_;
my $dbh = C4::Context->dbh;
my $sth = $dbh->prepare(
"SELECT * FROM accountlines,borrowers
WHERE amount < 0 AND accounttype <> 'Pay' AND accountlines.borrowernumber = borrowers.borrowernumber
AND timestamp >=TIMESTAMP(?) AND timestamp < TIMESTAMP(?)"
);
$sth->execute( $date, $date2 );
my @results;
while ( my $data = $sth->fetchrow_hashref ) {
$data->{'date'} = $data->{'timestamp'};
push @results,$data;
}
return (@results);
}
sub getrefunds {
my ( $date, $date2 ) = @_;
my $dbh = C4::Context->dbh;
my $sth = $dbh->prepare(
"SELECT *,timestamp AS datetime
FROM accountlines,borrowers
WHERE (accounttype = 'REF'
AND accountlines.borrowernumber = borrowers.borrowernumber
AND date >=? AND date <?)"
);
$sth->execute( $date, $date2 );
my @results;
while ( my $data = $sth->fetchrow_hashref ) {
push @results,$data;
}
return (@results);
}
sub ReversePayment {
my ( $borrowernumber, $accountno ) = @_;
my $dbh = C4::Context->dbh;
my $sth = $dbh->prepare('SELECT amountoutstanding FROM accountlines WHERE borrowernumber = ? AND accountno = ?');
$sth->execute( $borrowernumber, $accountno );
my $row = $sth->fetchrow_hashref();
my $amount_outstanding = $row->{'amountoutstanding'};
if ( $amount_outstanding <= 0 ) {
$sth = $dbh->prepare('UPDATE accountlines SET amountoutstanding = amount * -1, description = CONCAT( description, " Reversed -" ) WHERE borrowernumber = ? AND accountno = ?');
$sth->execute( $borrowernumber, $accountno );
} else {
$sth = $dbh->prepare('UPDATE accountlines SET amountoutstanding = 0, description = CONCAT( description, " Reversed -" ) WHERE borrowernumber = ? AND accountno = ?');
$sth->execute( $borrowernumber, $accountno );
}
}
=head2 recordpayment_selectaccts
recordpayment_selectaccts($borrowernumber, $payment,$accts);
Record payment by a patron. C<$borrowernumber> is the patron's
borrower number. C<$payment> is a floating-point number, giving the
amount that was paid. C<$accts> is an array ref to a list of
accountnos which the payment can be recorded against
Amounts owed are paid off oldest first. That is, if the patron has a
$1 fine from Feb. 1, another $1 fine from Mar. 1, and makes a payment
of $1.50, then the oldest fine will be paid off in full, and $0.50
will be credited to the next one.
=cut
sub recordpayment_selectaccts {
my ( $borrowernumber, $amount, $accts ) = @_;
my $dbh = C4::Context->dbh;
my $newamtos = 0;
my $accdata = q{};
my $branch = C4::Context->userenv->{branch};
my $amountleft = $amount;
my $sql = 'SELECT * FROM accountlines WHERE (borrowernumber = ?) ' .
'AND (amountoutstanding<>0) ';
if (@{$accts} ) {
$sql .= ' AND accountno IN ( ' . join ',', @{$accts};
$sql .= ' ) ';
}
$sql .= ' ORDER BY date';
# begin transaction
my $nextaccntno = getnextacctno($borrowernumber);
# get lines with outstanding amounts to offset
my $rows = $dbh->selectall_arrayref($sql, { Slice => {} }, $borrowernumber);
# offset transactions
my $sth = $dbh->prepare('UPDATE accountlines SET amountoutstanding= ? ' .
'WHERE (borrowernumber = ?) AND (accountno=?)');
for my $accdata ( @{$rows} ) {
if ($amountleft == 0) {
last;
}
if ( $accdata->{amountoutstanding} < $amountleft ) {
$newamtos = 0;
$amountleft -= $accdata->{amountoutstanding};
}
else {
$newamtos = $accdata->{amountoutstanding} - $amountleft;
$amountleft = 0;
}
my $thisacct = $accdata->{accountno};
$sth->execute( $newamtos, $borrowernumber, $thisacct );
}
# create new line
$sql = 'INSERT INTO accountlines ' .
'(borrowernumber, accountno,date,amount,description,accounttype,amountoutstanding) ' .
q|VALUES (?,?,now(),?,'Payment,thanks','Pay',?)|;
$dbh->do($sql,{},$borrowernumber, $nextaccntno, 0 - $amount, 0 - $amountleft );
UpdateStats( $branch, 'payment', $amount, '', '', '', $borrowernumber, $nextaccntno );
return;
}
# makepayment needs to be fixed to handle partials till then this separate subroutine
# fills in
sub makepartialpayment {
my ( $borrowernumber, $accountno, $amount, $user, $branch ) = @_;
if (!$amount || $amount < 0) {
return;
}
my $dbh = C4::Context->dbh;
my $nextaccntno = getnextacctno($borrowernumber);
my $newamtos = 0;
my $data = $dbh->selectrow_hashref(
'SELECT * FROM accountlines WHERE borrowernumber=? AND accountno=?',undef,$borrowernumber,$accountno);
my $new_outstanding = $data->{amountoutstanding} - $amount;
my $update = 'UPDATE accountlines SET amountoutstanding = ? WHERE borrowernumber = ? '
. ' AND accountno = ?';
$dbh->do( $update, undef, $new_outstanding, $borrowernumber, $accountno);
# create new line
my $insert = 'INSERT INTO accountlines (borrowernumber, accountno, date, amount, '
. 'description, accounttype, amountoutstanding) '
. ' VALUES (?, ?, now(), ?, ?, ?, 0)';
$dbh->do( $insert, undef, $borrowernumber, $nextaccntno, $amount,
"Payment, thanks - $user", 'Pay');
UpdateStats( $user, 'payment', $amount, '', '', '', $borrowernumber, $accountno );
return;
}
END { } # module clean-up code here (global destructor)
1;
__END__
=head1 SEE ALSO
DBI(3)
=cut