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, $timestamp, $accountno ) = @_;
303 my $dbh = C4::Context->dbh;
304 my $timestamp2 = $timestamp - 1;
306 my $sth = $dbh->prepare(
307 "SELECT * FROM accountlines WHERE borrowernumber=? AND accountno = ?"
309 $sth->execute( $borrowerno, $accountno );
312 while ( my $data = $sth->fetchrow_hashref ) {
319 my ( $accountlines_id, $note ) = @_;
320 my $dbh = C4::Context->dbh;
321 my $sth = $dbh->prepare('UPDATE accountlines SET note = ? WHERE accountlines_id = ?');
322 $sth->execute( $note, $accountlines_id );
326 my ( $date, $date2 ) = @_;
327 my $dbh = C4::Context->dbh;
328 my $sth = $dbh->prepare(
329 "SELECT * FROM accountlines,borrowers
330 WHERE amount < 0 AND accounttype not like 'Pay%' AND accountlines.borrowernumber = borrowers.borrowernumber
331 AND timestamp >=TIMESTAMP(?) AND timestamp < TIMESTAMP(?)"
334 $sth->execute( $date, $date2 );
336 while ( my $data = $sth->fetchrow_hashref ) {
337 $data->{'date'} = $data->{'timestamp'};
345 my ( $date, $date2 ) = @_;
346 my $dbh = C4::Context->dbh;
348 my $sth = $dbh->prepare(
349 "SELECT *,timestamp AS datetime
350 FROM accountlines,borrowers
351 WHERE (accounttype = 'REF'
352 AND accountlines.borrowernumber = borrowers.borrowernumber
353 AND date >=? AND date <?)"
356 $sth->execute( $date, $date2 );
359 while ( my $data = $sth->fetchrow_hashref ) {
366 #FIXME: ReversePayment should be replaced with a Void Payment feature
368 my ($accountlines_id) = @_;
369 my $dbh = C4::Context->dbh;
371 my $accountline = Koha::Account::Lines->find($accountlines_id);
372 my $amount_outstanding = $accountline->amountoutstanding;
374 my $new_amountoutstanding =
375 $amount_outstanding <= 0 ? $accountline->amount * -1 : 0;
377 $accountline->description( $accountline->description . " Reversed -" );
378 $accountline->amountoutstanding($new_amountoutstanding);
379 $accountline->store();
381 my $account_offset = Koha::Account::Offset->new(
383 credit_id => $accountline->id,
384 type => 'Reverse Payment',
385 amount => $amount_outstanding - $new_amountoutstanding,
389 if ( C4::Context->preference("FinesLog") ) {
391 $manager_id = C4::Context->userenv->{'number'} if C4::Context->userenv;
395 $accountline->borrowernumber,
398 action => 'reverse_fee_payment',
399 borrowernumber => $accountline->borrowernumber,
400 old_amountoutstanding => $amount_outstanding,
401 new_amountoutstanding => $new_amountoutstanding,
403 accountlines_id => $accountline->id,
404 accountno => $accountline->accountno,
405 manager_id => $manager_id,
412 =head2 purge_zero_balance_fees
414 purge_zero_balance_fees( $days );
416 Delete accountlines entries where amountoutstanding is 0 or NULL which are more than a given number of days old.
418 B<$days> -- Zero balance fees older than B<$days> days old will be deleted.
420 B<Warning:> Because fines and payments are not linked in accountlines, it is
421 possible for a fine to be deleted without the accompanying payment,
422 or vise versa. This won't affect the account balance, but might be
427 sub purge_zero_balance_fees {
431 my $dbh = C4::Context->dbh;
432 my $sth = $dbh->prepare(
434 DELETE FROM accountlines
435 WHERE date < date_sub(curdate(), INTERVAL ? DAY)
436 AND ( amountoutstanding = 0 or amountoutstanding IS NULL );
439 $sth->execute($days) or die $dbh->errstr;
442 END { } # module clean-up code here (global destructor)