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::Log qw(logaction);
28 use Koha::Account::Lines;
29 use Koha::Account::Offsets;
32 use Data::Dumper qw(Dumper);
34 use vars qw(@ISA @EXPORT);
45 &purge_zero_balance_fees
51 C4::Accounts - Functions for dealing with Koha accounts
59 The functions in this module deal with the monetary aspect of Koha,
60 including looking up and modifying the amount of money owed by a
67 $nextacct = &getnextacctno($borrowernumber);
69 Returns the next unused account number for the patron with the given
75 # FIXME - Okay, so what does the above actually _mean_?
77 my ($borrowernumber) = shift or return;
78 my $sth = C4::Context->dbh->prepare(
79 "SELECT accountno+1 FROM accountlines
80 WHERE (borrowernumber = ?)
81 ORDER BY accountno DESC
84 $sth->execute($borrowernumber);
85 return ($sth->fetchrow || 1);
90 In a default install of Koha the following lost values are set
95 FIXME: itemlost should be set to 3 after payment is made, should be a warning to the interface that a charge has been added
96 FIXME : if no replacement price, borrower just doesn't get charged?
101 my $dbh = C4::Context->dbh();
102 my ($borrowernumber, $itemnumber, $amount, $description) = @_;
103 my $itype = Koha::ItemTypes->find({ itemtype => Koha::Items->find($itemnumber)->effective_itemtype() });
104 my $replacementprice = $amount;
105 my $defaultreplacecost = $itype->defaultreplacecost;
106 my $processfee = $itype->processfee;
107 my $usedefaultreplacementcost = C4::Context->preference("useDefaultReplacementCost");
108 my $processingfeenote = C4::Context->preference("ProcessingFeeNote");
109 if ($usedefaultreplacementcost && $amount == 0 && $defaultreplacecost){
110 $replacementprice = $defaultreplacecost;
112 # first make sure the borrower hasn't already been charged for this item
113 # FIXME this should be more exact
114 # there is no reason a user can't lose an item, find and return it, and lost it again
115 my $existing_charges = Koha::Account::Lines->search(
117 borrowernumber => $borrowernumber,
118 itemnumber => $itemnumber,
124 unless ($existing_charges) {
125 my $checkout = Koha::Checkouts->find({ itemnumber => $itemnumber });
126 my $issue_id = $checkout ? $checkout->issue_id : undef;
128 if ($processfee && $processfee > 0){
129 my $accountline = Koha::Account::Line->new(
131 borrowernumber => $borrowernumber,
132 issue_id => $issue_id,
133 accountno => getnextacctno($borrowernumber),
135 amount => $processfee,
136 description => $description,
138 amountoutstanding => $processfee,
139 itemnumber => $itemnumber,
140 note => $processingfeenote,
141 manager_id => C4::Context->userenv ? C4::Context->userenv->{'number'} : 0,
145 my $account_offset = Koha::Account::Offset->new(
147 debit_id => $accountline->id,
148 type => 'Processing Fee',
149 amount => $accountline->amount,
153 if ( C4::Context->preference("FinesLog") ) {
154 logaction("FINES", 'CREATE',$borrowernumber,Dumper({
155 action => 'create_fee',
156 borrowernumber => $accountline->borrowernumber,,
157 accountno => $accountline->accountno,
158 amount => $accountline->amount,
159 description => $accountline->description,
160 accounttype => $accountline->accounttype,
161 amountoutstanding => $accountline->amountoutstanding,
162 note => $accountline->note,
163 itemnumber => $accountline->itemnumber,
164 manager_id => $accountline->manager_id,
169 if ($replacementprice > 0){
170 my $accountline = Koha::Account::Line->new(
172 borrowernumber => $borrowernumber,
173 issue_id => $issue_id,
174 accountno => getnextacctno($borrowernumber),
176 amount => $replacementprice,
177 description => $description,
179 amountoutstanding => $replacementprice,
180 itemnumber => $itemnumber,
181 manager_id => C4::Context->userenv ? C4::Context->userenv->{'number'} : 0,
185 my $account_offset = Koha::Account::Offset->new(
187 debit_id => $accountline->id,
189 amount => $accountline->amount,
193 if ( C4::Context->preference("FinesLog") ) {
194 logaction("FINES", 'CREATE',$borrowernumber,Dumper({
195 action => 'create_fee',
196 borrowernumber => $accountline->borrowernumber,,
197 accountno => $accountline->accountno,
198 amount => $accountline->amount,
199 description => $accountline->description,
200 accounttype => $accountline->accounttype,
201 amountoutstanding => $accountline->amountoutstanding,
202 note => $accountline->note,
203 itemnumber => $accountline->itemnumber,
204 manager_id => $accountline->manager_id,
213 &manualinvoice($borrowernumber, $itemnumber, $description, $type,
216 C<$borrowernumber> is the patron's borrower number.
217 C<$description> is a description of the transaction.
218 C<$type> may be one of C<CS>, C<CB>, C<CW>, C<CF>, C<CL>, C<N>, C<L>,
220 C<$itemnumber> is the item involved, if pertinent; otherwise, it
221 should be the empty string.
226 # FIXME: In Koha 3.0 , the only account adjustment 'types' passed to this function
229 # 'FOR' = FORGIVEN (Formerly 'F', but 'F' is taken to mean 'FINE' elsewhere)
232 # 'A' = Account Management fee
238 my ( $borrowernumber, $itemnum, $desc, $type, $amount, $note ) = @_;
240 $manager_id = C4::Context->userenv->{'number'} if C4::Context->userenv;
241 my $dbh = C4::Context->dbh;
243 my $accountno = getnextacctno($borrowernumber);
244 my $amountleft = $amount;
246 my $accountline = Koha::Account::Line->new(
248 borrowernumber => $borrowernumber,
249 accountno => $accountno,
252 description => $desc,
253 accounttype => $type,
254 amountoutstanding => $amountleft,
255 itemnumber => $itemnum || undef,
257 manager_id => $manager_id,
261 my $account_offset = Koha::Account::Offset->new(
263 debit_id => $accountline->id,
264 type => 'Manual Debit',
269 if ( C4::Context->preference("FinesLog") ) {
270 logaction("FINES", 'CREATE',$borrowernumber,Dumper({
271 action => 'create_fee',
272 borrowernumber => $borrowernumber,
273 accountno => $accountno,
275 description => $desc,
276 accounttype => $type,
277 amountoutstanding => $amountleft,
279 itemnumber => $itemnum,
280 manager_id => $manager_id,
288 my ( $borrowerno, $timestamp, $accountno ) = @_;
289 my $dbh = C4::Context->dbh;
290 my $timestamp2 = $timestamp - 1;
292 my $sth = $dbh->prepare(
293 "SELECT * FROM accountlines WHERE borrowernumber=? AND accountno = ?"
295 $sth->execute( $borrowerno, $accountno );
298 while ( my $data = $sth->fetchrow_hashref ) {
304 #FIXME: ReversePayment should be replaced with a Void Payment feature
306 my ($accountlines_id) = @_;
307 my $dbh = C4::Context->dbh;
309 my $accountline = Koha::Account::Lines->find($accountlines_id);
310 my $amount_outstanding = $accountline->amountoutstanding;
312 my $new_amountoutstanding =
313 $amount_outstanding <= 0 ? $accountline->amount * -1 : 0;
315 $accountline->description( $accountline->description . " Reversed -" );
316 $accountline->amountoutstanding($new_amountoutstanding);
317 $accountline->store();
319 my $account_offset = Koha::Account::Offset->new(
321 credit_id => $accountline->id,
322 type => 'Reverse Payment',
323 amount => $amount_outstanding - $new_amountoutstanding,
327 if ( C4::Context->preference("FinesLog") ) {
329 $manager_id = C4::Context->userenv->{'number'} if C4::Context->userenv;
333 $accountline->borrowernumber,
336 action => 'reverse_fee_payment',
337 borrowernumber => $accountline->borrowernumber,
338 old_amountoutstanding => $amount_outstanding,
339 new_amountoutstanding => $new_amountoutstanding,
341 accountlines_id => $accountline->id,
342 accountno => $accountline->accountno,
343 manager_id => $manager_id,
350 =head2 purge_zero_balance_fees
352 purge_zero_balance_fees( $days );
354 Delete accountlines entries where amountoutstanding is 0 or NULL which are more than a given number of days old.
356 B<$days> -- Zero balance fees older than B<$days> days old will be deleted.
358 B<Warning:> Because fines and payments are not linked in accountlines, it is
359 possible for a fine to be deleted without the accompanying payment,
360 or vise versa. This won't affect the account balance, but might be
365 sub purge_zero_balance_fees {
369 my $dbh = C4::Context->dbh;
370 my $sth = $dbh->prepare(
372 DELETE a1 FROM accountlines a1
374 LEFT JOIN account_offsets credit_offset ON ( a1.accountlines_id = credit_offset.credit_id )
375 LEFT JOIN accountlines a2 ON ( credit_offset.debit_id = a2.accountlines_id )
377 LEFT JOIN account_offsets debit_offset ON ( a1.accountlines_id = debit_offset.debit_id )
378 LEFT JOIN accountlines a3 ON ( debit_offset.credit_id = a3.accountlines_id )
380 WHERE a1.date < date_sub(curdate(), INTERVAL ? DAY)
381 AND ( a1.amountoutstanding = 0 OR a1.amountoutstanding IS NULL )
382 AND ( a2.amountoutstanding = 0 OR a2.amountoutstanding IS NULL )
383 AND ( a3.amountoutstanding = 0 OR a3.amountoutstanding IS NULL )
386 $sth->execute($days) or die $dbh->errstr;
389 END { } # module clean-up code here (global destructor)