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);
46 &purge_zero_balance_fees
52 C4::Accounts - Functions for dealing with Koha accounts
60 The functions in this module deal with the monetary aspect of Koha,
61 including looking up and modifying the amount of money owed by a
68 $nextacct = &getnextacctno($borrowernumber);
70 Returns the next unused account number for the patron with the given
76 # FIXME - Okay, so what does the above actually _mean_?
78 my ($borrowernumber) = shift or return;
79 my $sth = C4
::Context
->dbh->prepare(
80 "SELECT accountno+1 FROM accountlines
81 WHERE (borrowernumber = ?)
82 ORDER BY accountno DESC
85 $sth->execute($borrowernumber);
86 return ($sth->fetchrow || 1);
89 =head2 fixaccounts (removed)
91 &fixaccounts($accountlines_id, $borrowernumber, $accountnumber, $amount);
94 # FIXME - I don't understand what this function does.
96 my ( $accountlines_id, $borrowernumber, $accountno, $amount ) = @_;
97 my $dbh = C4::Context->dbh;
98 my $sth = $dbh->prepare(
99 "SELECT * FROM accountlines WHERE accountlines_id=?"
101 $sth->execute( $accountlines_id );
102 my $data = $sth->fetchrow_hashref;
104 # FIXME - Error-checking
105 my $diff = $amount - $data->{'amount'};
106 my $outstanding = $data->{'amountoutstanding'} + $diff;
111 SET amount = '$amount',
112 amountoutstanding = '$outstanding'
113 WHERE accountlines_id = $accountlines_id
115 # FIXME: exceedingly bad form. Use prepare with placholders ("?") in query and execute args.
120 =head2 chargelostitem
122 In a default install of Koha the following lost values are set
125 3 = Lost and paid for
127 FIXME: itemlost should be set to 3 after payment is made, should be a warning to the interface that a charge has been added
128 FIXME : if no replacement price, borrower just doesn't get charged?
133 my $dbh = C4
::Context
->dbh();
134 my ($borrowernumber, $itemnumber, $amount, $description) = @_;
135 my $itype = Koha
::ItemTypes
->find({ itemtype
=> Koha
::Items
->find($itemnumber)->effective_itemtype() });
136 my $replacementprice = $amount;
137 my $defaultreplacecost = $itype->defaultreplacecost;
138 my $processfee = $itype->processfee;
139 my $usedefaultreplacementcost = C4
::Context
->preference("useDefaultReplacementCost");
140 my $processingfeenote = C4
::Context
->preference("ProcessingFeeNote");
141 if ($usedefaultreplacementcost && $amount == 0 && $defaultreplacecost){
142 $replacementprice = $defaultreplacecost;
144 # first make sure the borrower hasn't already been charged for this item
145 # FIXME this should be more exact
146 # there is no reason a user can't lose an item, find and return it, and lost it again
147 my $existing_charges = Koha
::Account
::Lines
->search(
149 borrowernumber
=> $borrowernumber,
150 itemnumber
=> $itemnumber,
156 unless ($existing_charges) {
158 if ($processfee && $processfee > 0){
159 my $accountline = Koha
::Account
::Line
->new(
161 borrowernumber
=> $borrowernumber,
162 accountno
=> getnextacctno
($borrowernumber),
164 amount => $processfee,
165 description => $description,
167 amountoutstanding => $processfee,
168 itemnumber => $itemnumber,
169 note => $processingfeenote,
170 manager_id => C4::Context->userenv ? C4::Context->userenv->{'number
'} : 0,
174 my $account_offset = Koha::Account::Offset->new(
176 debit_id => $accountline->id,
177 type => 'Processing Fee
',
178 amount => $accountline->amount,
182 if ( C4::Context->preference("FinesLog") ) {
183 logaction("FINES", 'CREATE
',$borrowernumber,Dumper({
184 action => 'create_fee
',
185 borrowernumber => $accountline->borrowernumber,,
186 accountno => $accountline->accountno,
187 amount => $accountline->amount,
188 description => $accountline->description,
189 accounttype => $accountline->accounttype,
190 amountoutstanding => $accountline->amountoutstanding,
191 note => $accountline->note,
192 itemnumber => $accountline->itemnumber,
193 manager_id => $accountline->manager_id,
198 if ($replacementprice > 0){
199 my $accountline = Koha::Account::Line->new(
201 borrowernumber => $borrowernumber,
202 accountno => getnextacctno($borrowernumber),
204 amount
=> $replacementprice,
205 description
=> $description,
207 amountoutstanding
=> $replacementprice,
208 itemnumber
=> $itemnumber,
209 manager_id
=> C4
::Context
->userenv ? C4
::Context
->userenv->{'number'} : 0,
213 my $account_offset = Koha
::Account
::Offset
->new(
215 debit_id
=> $accountline->id,
217 amount
=> $accountline->amount,
221 if ( C4
::Context
->preference("FinesLog") ) {
222 logaction
("FINES", 'CREATE',$borrowernumber,Dumper
({
223 action
=> 'create_fee',
224 borrowernumber
=> $accountline->borrowernumber,,
225 accountno
=> $accountline->accountno,
226 amount
=> $accountline->amount,
227 description
=> $accountline->description,
228 accounttype
=> $accountline->accounttype,
229 amountoutstanding
=> $accountline->amountoutstanding,
230 note
=> $accountline->note,
231 itemnumber
=> $accountline->itemnumber,
232 manager_id
=> $accountline->manager_id,
241 &manualinvoice($borrowernumber, $itemnumber, $description, $type,
244 C<$borrowernumber> is the patron's borrower number.
245 C<$description> is a description of the transaction.
246 C<$type> may be one of C<CS>, C<CB>, C<CW>, C<CF>, C<CL>, C<N>, C<L>,
248 C<$itemnumber> is the item involved, if pertinent; otherwise, it
249 should be the empty string.
254 # FIXME: In Koha 3.0 , the only account adjustment 'types' passed to this function
257 # 'FOR' = FORGIVEN (Formerly 'F', but 'F' is taken to mean 'FINE' elsewhere)
260 # 'A' = Account Management fee
266 my ( $borrowernumber, $itemnum, $desc, $type, $amount, $note ) = @_;
268 $manager_id = C4
::Context
->userenv->{'number'} if C4
::Context
->userenv;
269 my $dbh = C4
::Context
->dbh;
271 my $accountno = getnextacctno
($borrowernumber);
272 my $amountleft = $amount;
274 my $accountline = Koha
::Account
::Line
->new(
276 borrowernumber
=> $borrowernumber,
277 accountno
=> $accountno,
280 description => $desc,
281 accounttype => $type,
282 amountoutstanding => $amountleft,
283 itemnumber => $itemnum || undef,
285 manager_id => $manager_id,
289 my $account_offset = Koha::Account::Offset->new(
291 debit_id => $accountline->id,
292 type => 'Manual Debit
',
297 if ( C4::Context->preference("FinesLog") ) {
298 logaction("FINES", 'CREATE
',$borrowernumber,Dumper({
299 action => 'create_fee
',
300 borrowernumber => $borrowernumber,
301 accountno => $accountno,
303 description => $desc,
304 accounttype => $type,
305 amountoutstanding => $amountleft,
307 itemnumber => $itemnum,
308 manager_id => $manager_id,
316 my ( $borrowerno, $timestamp, $accountno ) = @_;
317 my $dbh = C4::Context->dbh;
318 my $timestamp2 = $timestamp - 1;
320 my $sth = $dbh->prepare(
321 "SELECT * FROM accountlines WHERE borrowernumber=? AND accountno = ?"
323 $sth->execute( $borrowerno, $accountno );
326 while ( my $data = $sth->fetchrow_hashref ) {
332 #FIXME: ReversePayment should be replaced with a Void Payment feature
334 my ($accountlines_id) = @_;
335 my $dbh = C4::Context->dbh;
337 my $accountline = Koha::Account::Lines->find($accountlines_id);
338 my $amount_outstanding = $accountline->amountoutstanding;
340 my $new_amountoutstanding =
341 $amount_outstanding <= 0 ? $accountline->amount * -1 : 0;
343 $accountline->description( $accountline->description . " Reversed -" );
344 $accountline->amountoutstanding($new_amountoutstanding);
345 $accountline->store();
347 my $account_offset = Koha::Account::Offset->new(
349 credit_id => $accountline->id,
350 type => 'Reverse Payment
',
351 amount => $amount_outstanding - $new_amountoutstanding,
355 if ( C4::Context->preference("FinesLog") ) {
357 $manager_id = C4::Context->userenv->{'number
'} if C4::Context->userenv;
361 $accountline->borrowernumber,
364 action => 'reverse_fee_payment
',
365 borrowernumber => $accountline->borrowernumber,
366 old_amountoutstanding => $amount_outstanding,
367 new_amountoutstanding => $new_amountoutstanding,
369 accountlines_id => $accountline->id,
370 accountno => $accountline->accountno,
371 manager_id => $manager_id,
378 =head2 purge_zero_balance_fees
380 purge_zero_balance_fees( $days );
382 Delete accountlines entries where amountoutstanding is 0 or NULL which are more than a given number of days old.
384 B<$days> -- Zero balance fees older than B<$days> days old will be deleted.
386 B<Warning:> Because fines and payments are not linked in accountlines, it is
387 possible for a fine to be deleted without the accompanying payment,
388 or vise versa. This won't affect the account balance
, but might be
393 sub purge_zero_balance_fees
{
397 my $dbh = C4
::Context
->dbh;
398 my $sth = $dbh->prepare(
400 DELETE a1 FROM accountlines a1
402 LEFT JOIN account_offsets credit_offset ON ( a1.accountlines_id = credit_offset.credit_id )
403 LEFT JOIN accountlines a2 ON ( credit_offset.debit_id = a2.accountlines_id )
405 LEFT JOIN account_offsets debit_offset ON ( a1.accountlines_id = debit_offset.debit_id )
406 LEFT JOIN accountlines a3 ON ( debit_offset.credit_id = a3.accountlines_id )
408 WHERE a1.date < date_sub(curdate(), INTERVAL ? DAY)
409 AND ( a1.amountoutstanding = 0 OR a1.amountoutstanding IS NULL )
410 AND ( a2.amountoutstanding = 0 OR a2.amountoutstanding IS NULL )
411 AND ( a3.amountoutstanding = 0 OR a3.amountoutstanding IS NULL )
414 $sth->execute($days) or die $dbh->errstr;
417 END { } # module clean-up code here (global destructor)