3 # Copyright 2000-2002 Katipo Communications
5 # This file is part of Koha.
7 # Koha is free software; you can redistribute it and/or modify it
8 # under the terms of the GNU General Public License as published by
9 # the Free Software Foundation; either version 3 of the License, or
10 # (at your option) any later version.
12 # Koha is distributed in the hope that it will be useful, but
13 # WITHOUT ANY WARRANTY; without even the implied warranty of
14 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 # GNU General Public License for more details.
17 # You should have received a copy of the GNU General Public License
18 # along with Koha; if not, see <http://www.gnu.org/licenses>.
22 #use warnings; FIXME - Bug 2505
26 use C4::Circulation qw(ReturnLostItem);
27 use C4::Log qw(logaction);
29 use Koha::Account::Lines;
30 use Koha::Account::Offsets;
33 use Data::Dumper qw(Dumper);
35 use vars qw(@ISA @EXPORT);
49 &purge_zero_balance_fees
55 C4::Accounts - Functions for dealing with Koha accounts
63 The functions in this module deal with the monetary aspect of Koha,
64 including looking up and modifying the amount of money owed by a
71 $nextacct = &getnextacctno($borrowernumber);
73 Returns the next unused account number for the patron with the given
79 # FIXME - Okay, so what does the above actually _mean_?
81 my ($borrowernumber) = shift or return;
82 my $sth = C4::Context->dbh->prepare(
83 "SELECT accountno+1 FROM accountlines
84 WHERE (borrowernumber = ?)
85 ORDER BY accountno DESC
88 $sth->execute($borrowernumber);
89 return ($sth->fetchrow || 1);
92 =head2 fixaccounts (removed)
94 &fixaccounts($accountlines_id, $borrowernumber, $accountnumber, $amount);
97 # FIXME - I don't understand what this function does.
99 my ( $accountlines_id, $borrowernumber, $accountno, $amount ) = @_;
100 my $dbh = C4::Context->dbh;
101 my $sth = $dbh->prepare(
102 "SELECT * FROM accountlines WHERE accountlines_id=?"
104 $sth->execute( $accountlines_id );
105 my $data = $sth->fetchrow_hashref;
107 # FIXME - Error-checking
108 my $diff = $amount - $data->{'amount'};
109 my $outstanding = $data->{'amountoutstanding'} + $diff;
114 SET amount = '$amount',
115 amountoutstanding = '$outstanding'
116 WHERE accountlines_id = $accountlines_id
118 # FIXME: exceedingly bad form. Use prepare with placholders ("?") in query and execute args.
123 =head2 chargelostitem
125 In a default install of Koha the following lost values are set
128 3 = Lost and paid for
130 FIXME: itemlost should be set to 3 after payment is made, should be a warning to the interface that a charge has been added
131 FIXME : if no replacement price, borrower just doesn't get charged?
136 my $dbh = C4::Context->dbh();
137 my ($borrowernumber, $itemnumber, $amount, $description) = @_;
138 my $itype = Koha::ItemTypes->find({ itemtype => Koha::Items->find($itemnumber)->effective_itemtype() });
139 my $replacementprice = $amount;
140 my $defaultreplacecost = $itype->defaultreplacecost;
141 my $processfee = $itype->processfee;
142 my $usedefaultreplacementcost = C4::Context->preference("useDefaultReplacementCost");
143 my $processingfeenote = C4::Context->preference("ProcessingFeeNote");
144 if ($usedefaultreplacementcost && $amount == 0 && $defaultreplacecost){
145 $replacementprice = $defaultreplacecost;
147 # first make sure the borrower hasn't already been charged for this item
148 my $existing_charges = Koha::Account::Lines->search(
150 borrowernumber => $borrowernumber,
151 itemnumber => $itemnumber,
157 unless ($existing_charges) {
159 $manager_id = C4::Context->userenv->{'number'} if C4::Context->userenv;
160 # This item is on issue ... add replacement cost to the borrower's record and mark it returned
161 # Note that we add this to the account even if there's no replacement price, allowing some other
162 # process (or person) to update it, since we don't handle any defaults for replacement prices.
163 my $accountno = getnextacctno($borrowernumber);
165 my $accountline = Koha::Account::Line->new(
167 borrowernumber => $borrowernumber,
168 accountno => $accountno,
171 description => $description,
173 amountoutstanding => $amount,
174 itemnumber => $itemnumber,
175 manager_id => $manager_id,
179 my $account_offset = Koha::Account::Offset->new(
181 debit_id => $accountline->id,
187 if ( C4::Context->preference("FinesLog") ) {
188 logaction("FINES", 'CREATE', $borrowernumber, Dumper({
189 action => 'create_fee',
190 borrowernumber => $borrowernumber,
191 accountno => $accountno,
193 amountoutstanding => $amount,
194 description => $description,
196 itemnumber => $itemnumber,
197 manager_id => $manager_id,
202 if ($processfee && $processfee > 0){
203 manualinvoice($borrowernumber, $itemnumber, $description, 'PF', $processfee, $processingfeenote, 1);
206 if ($replacementprice > 0){
207 manualinvoice($borrowernumber, $itemnumber, $description, 'L', $replacementprice, undef, 1);
214 &manualinvoice($borrowernumber, $itemnumber, $description, $type,
217 C<$borrowernumber> is the patron's borrower number.
218 C<$description> is a description of the transaction.
219 C<$type> may be one of C<CS>, C<CB>, C<CW>, C<CF>, C<CL>, C<N>, C<L>,
221 C<$itemnumber> is the item involved, if pertinent; otherwise, it
222 should be the empty string.
227 # FIXME: In Koha 3.0 , the only account adjustment 'types' passed to this function
230 # 'FOR' = FORGIVEN (Formerly 'F', but 'F' is taken to mean 'FINE' elsewhere)
233 # 'A' = Account Management fee
239 my ( $borrowernumber, $itemnum, $desc, $type, $amount, $note, $skip_notify ) = @_;
241 $manager_id = C4::Context->userenv->{'number'} if C4::Context->userenv;
242 my $dbh = C4::Context->dbh;
245 my $accountno = getnextacctno($borrowernumber);
246 my $amountleft = $amount;
249 if ( ( $type eq 'L' )
253 or ( $type eq 'M' ) )
255 $notifyid = 1 unless $skip_notify;
258 my $accountline = Koha::Account::Line->new(
260 borrowernumber => $borrowernumber,
261 accountno => $accountno,
264 description => $desc,
265 accounttype => $type,
266 amountoutstanding => $amountleft,
267 itemnumber => $itemnum || undef,
268 notify_id => $notifyid,
270 manager_id => $manager_id,
274 my $account_offset = Koha::Account::Offset->new(
276 debit_id => $accountline->id,
277 type => 'Manual Debit',
282 if ( C4::Context->preference("FinesLog") ) {
283 logaction("FINES", 'CREATE',$borrowernumber,Dumper({
284 action => 'create_fee',
285 borrowernumber => $borrowernumber,
286 accountno => $accountno,
288 description => $desc,
289 accounttype => $type,
290 amountoutstanding => $amountleft,
291 notify_id => $notifyid,
293 itemnumber => $itemnum,
294 manager_id => $manager_id,
302 my ( $borrowerno, $accountno ) = @_;
303 my $dbh = C4::Context->dbh;
307 my $query = "SELECT * FROM accountlines WHERE borrowernumber = ?";
308 push( @params, $borrowerno );
311 $query .= " AND accountno = ?";
312 push( @params, $accountno );
315 my $sth = $dbh->prepare( $query );
316 $sth->execute( @params );
319 while ( my $data = $sth->fetchrow_hashref ) {
326 my ( $accountlines_id, $note ) = @_;
327 my $dbh = C4::Context->dbh;
328 my $sth = $dbh->prepare('UPDATE accountlines SET note = ? WHERE accountlines_id = ?');
329 $sth->execute( $note, $accountlines_id );
333 my ( $date, $date2 ) = @_;
334 my $dbh = C4::Context->dbh;
335 my $sth = $dbh->prepare(
336 "SELECT * FROM accountlines,borrowers
337 WHERE amount < 0 AND accounttype not like 'Pay%' AND accountlines.borrowernumber = borrowers.borrowernumber
338 AND timestamp >=TIMESTAMP(?) AND timestamp < TIMESTAMP(?)"
341 $sth->execute( $date, $date2 );
343 while ( my $data = $sth->fetchrow_hashref ) {
344 $data->{'date'} = $data->{'timestamp'};
352 my ( $date, $date2 ) = @_;
353 my $dbh = C4::Context->dbh;
355 my $sth = $dbh->prepare(
356 "SELECT *,timestamp AS datetime
357 FROM accountlines,borrowers
358 WHERE (accounttype = 'REF'
359 AND accountlines.borrowernumber = borrowers.borrowernumber
360 AND date >=? AND date <?)"
363 $sth->execute( $date, $date2 );
366 while ( my $data = $sth->fetchrow_hashref ) {
373 #FIXME: ReversePayment should be replaced with a Void Payment feature
375 my ($accountlines_id) = @_;
376 my $dbh = C4::Context->dbh;
378 my $accountline = Koha::Account::Lines->find($accountlines_id);
379 my $amount_outstanding = $accountline->amountoutstanding;
381 my $new_amountoutstanding =
382 $amount_outstanding <= 0 ? $accountline->amount * -1 : 0;
384 $accountline->description( $accountline->description . " Reversed -" );
385 $accountline->amountoutstanding($new_amountoutstanding);
386 $accountline->store();
388 my $account_offset = Koha::Account::Offset->new(
390 credit_id => $accountline->id,
391 type => 'Reverse Payment',
392 amount => $amount_outstanding - $new_amountoutstanding,
396 if ( C4::Context->preference("FinesLog") ) {
398 $manager_id = C4::Context->userenv->{'number'} if C4::Context->userenv;
402 $accountline->borrowernumber,
405 action => 'reverse_fee_payment',
406 borrowernumber => $accountline->borrowernumber,
407 old_amountoutstanding => $amount_outstanding,
408 new_amountoutstanding => $new_amountoutstanding,
410 accountlines_id => $accountline->id,
411 accountno => $accountline->accountno,
412 manager_id => $manager_id,
419 =head2 purge_zero_balance_fees
421 purge_zero_balance_fees( $days );
423 Delete accountlines entries where amountoutstanding is 0 or NULL which are more than a given number of days old.
425 B<$days> -- Zero balance fees older than B<$days> days old will be deleted.
427 B<Warning:> Because fines and payments are not linked in accountlines, it is
428 possible for a fine to be deleted without the accompanying payment,
429 or vise versa. This won't affect the account balance, but might be
434 sub purge_zero_balance_fees {
438 my $dbh = C4::Context->dbh;
439 my $sth = $dbh->prepare(
441 DELETE FROM accountlines
442 WHERE date < date_sub(curdate(), INTERVAL ? DAY)
443 AND ( amountoutstanding = 0 or amountoutstanding IS NULL );
446 $sth->execute($days) or die $dbh->errstr;
449 END { } # module clean-up code here (global destructor)