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);
43 &purge_zero_balance_fees
49 C4::Accounts - Functions for dealing with Koha accounts
57 The functions in this module deal with the monetary aspect of Koha,
58 including looking up and modifying the amount of money owed by a
65 $nextacct = &getnextacctno($borrowernumber);
67 Returns the next unused account number for the patron with the given
73 # FIXME - Okay, so what does the above actually _mean_?
75 my ($borrowernumber) = shift or return;
76 my $sth = C4
::Context
->dbh->prepare(
77 "SELECT accountno+1 FROM accountlines
78 WHERE (borrowernumber = ?)
79 ORDER BY accountno DESC
82 $sth->execute($borrowernumber);
83 return ($sth->fetchrow || 1);
88 In a default install of Koha the following lost values are set
93 FIXME: itemlost should be set to 3 after payment is made, should be a warning to the interface that a charge has been added
94 FIXME : if no replacement price, borrower just doesn't get charged?
99 my $dbh = C4
::Context
->dbh();
100 my ($borrowernumber, $itemnumber, $amount, $description) = @_;
101 my $itype = Koha
::ItemTypes
->find({ itemtype
=> Koha
::Items
->find($itemnumber)->effective_itemtype() });
102 my $replacementprice = $amount;
103 my $defaultreplacecost = $itype->defaultreplacecost;
104 my $processfee = $itype->processfee;
105 my $usedefaultreplacementcost = C4
::Context
->preference("useDefaultReplacementCost");
106 my $processingfeenote = C4
::Context
->preference("ProcessingFeeNote");
107 if ($usedefaultreplacementcost && $amount == 0 && $defaultreplacecost){
108 $replacementprice = $defaultreplacecost;
110 # first make sure the borrower hasn't already been charged for this item
111 # FIXME this should be more exact
112 # there is no reason a user can't lose an item, find and return it, and lost it again
113 my $existing_charges = Koha
::Account
::Lines
->search(
115 borrowernumber
=> $borrowernumber,
116 itemnumber
=> $itemnumber,
122 unless ($existing_charges) {
124 if ($processfee && $processfee > 0){
125 my $accountline = Koha
::Account
::Line
->new(
127 borrowernumber
=> $borrowernumber,
128 accountno
=> getnextacctno
($borrowernumber),
130 amount => $processfee,
131 description => $description,
133 amountoutstanding => $processfee,
134 itemnumber => $itemnumber,
135 note => $processingfeenote,
136 manager_id => C4::Context->userenv ? C4::Context->userenv->{'number
'} : 0,
140 my $account_offset = Koha::Account::Offset->new(
142 debit_id => $accountline->id,
143 type => 'Processing Fee
',
144 amount => $accountline->amount,
148 if ( C4::Context->preference("FinesLog") ) {
149 logaction("FINES", 'CREATE
',$borrowernumber,Dumper({
150 action => 'create_fee
',
151 borrowernumber => $accountline->borrowernumber,,
152 accountno => $accountline->accountno,
153 amount => $accountline->amount,
154 description => $accountline->description,
155 accounttype => $accountline->accounttype,
156 amountoutstanding => $accountline->amountoutstanding,
157 note => $accountline->note,
158 itemnumber => $accountline->itemnumber,
159 manager_id => $accountline->manager_id,
164 if ($replacementprice > 0){
165 my $accountline = Koha::Account::Line->new(
167 borrowernumber => $borrowernumber,
168 accountno => getnextacctno($borrowernumber),
170 amount
=> $replacementprice,
171 description
=> $description,
173 amountoutstanding
=> $replacementprice,
174 itemnumber
=> $itemnumber,
175 manager_id
=> C4
::Context
->userenv ? C4
::Context
->userenv->{'number'} : 0,
179 my $account_offset = Koha
::Account
::Offset
->new(
181 debit_id
=> $accountline->id,
183 amount
=> $accountline->amount,
187 if ( C4
::Context
->preference("FinesLog") ) {
188 logaction
("FINES", 'CREATE',$borrowernumber,Dumper
({
189 action
=> 'create_fee',
190 borrowernumber
=> $accountline->borrowernumber,,
191 accountno
=> $accountline->accountno,
192 amount
=> $accountline->amount,
193 description
=> $accountline->description,
194 accounttype
=> $accountline->accounttype,
195 amountoutstanding
=> $accountline->amountoutstanding,
196 note
=> $accountline->note,
197 itemnumber
=> $accountline->itemnumber,
198 manager_id
=> $accountline->manager_id,
207 &manualinvoice($borrowernumber, $itemnumber, $description, $type,
210 C<$borrowernumber> is the patron's borrower number.
211 C<$description> is a description of the transaction.
212 C<$type> may be one of C<CS>, C<CB>, C<CW>, C<CF>, C<CL>, C<N>, C<L>,
214 C<$itemnumber> is the item involved, if pertinent; otherwise, it
215 should be the empty string.
220 # FIXME: In Koha 3.0 , the only account adjustment 'types' passed to this function
223 # 'FOR' = FORGIVEN (Formerly 'F', but 'F' is taken to mean 'FINE' elsewhere)
226 # 'A' = Account Management fee
232 my ( $borrowernumber, $itemnum, $desc, $type, $amount, $note ) = @_;
234 $manager_id = C4
::Context
->userenv->{'number'} if C4
::Context
->userenv;
235 my $dbh = C4
::Context
->dbh;
237 my $accountno = getnextacctno
($borrowernumber);
238 my $amountleft = $amount;
240 my $accountline = Koha
::Account
::Line
->new(
242 borrowernumber
=> $borrowernumber,
243 accountno
=> $accountno,
246 description => $desc,
247 accounttype => $type,
248 amountoutstanding => $amountleft,
249 itemnumber => $itemnum || undef,
251 manager_id => $manager_id,
255 my $account_offset = Koha::Account::Offset->new(
257 debit_id => $accountline->id,
258 type => 'Manual Debit
',
263 if ( C4::Context->preference("FinesLog") ) {
264 logaction("FINES", 'CREATE
',$borrowernumber,Dumper({
265 action => 'create_fee
',
266 borrowernumber => $borrowernumber,
267 accountno => $accountno,
269 description => $desc,
270 accounttype => $type,
271 amountoutstanding => $amountleft,
273 itemnumber => $itemnum,
274 manager_id => $manager_id,
282 my ( $borrowerno, $timestamp, $accountno ) = @_;
283 my $dbh = C4::Context->dbh;
284 my $timestamp2 = $timestamp - 1;
286 my $sth = $dbh->prepare(
287 "SELECT * FROM accountlines WHERE borrowernumber=? AND accountno = ?"
289 $sth->execute( $borrowerno, $accountno );
292 while ( my $data = $sth->fetchrow_hashref ) {
298 =head2 purge_zero_balance_fees
300 purge_zero_balance_fees( $days );
302 Delete accountlines entries where amountoutstanding is 0 or NULL which are more than a given number of days old.
304 B<$days> -- Zero balance fees older than B<$days> days old will be deleted.
306 B<Warning:> Because fines and payments are not linked in accountlines, it is
307 possible for a fine to be deleted without the accompanying payment,
308 or vise versa. This won't affect the account balance
, but might be
313 sub purge_zero_balance_fees
{
317 my $dbh = C4
::Context
->dbh;
318 my $sth = $dbh->prepare(
320 DELETE a1 FROM accountlines a1
322 LEFT JOIN account_offsets credit_offset ON ( a1.accountlines_id = credit_offset.credit_id )
323 LEFT JOIN accountlines a2 ON ( credit_offset.debit_id = a2.accountlines_id )
325 LEFT JOIN account_offsets debit_offset ON ( a1.accountlines_id = debit_offset.debit_id )
326 LEFT JOIN accountlines a3 ON ( debit_offset.credit_id = a3.accountlines_id )
328 WHERE a1.date < date_sub(curdate(), INTERVAL ? DAY)
329 AND ( a1.amountoutstanding = 0 OR a1.amountoutstanding IS NULL )
330 AND ( a2.amountoutstanding = 0 OR a2.amountoutstanding IS NULL )
331 AND ( a3.amountoutstanding = 0 OR a3.amountoutstanding IS NULL )
334 $sth->execute($days) or die $dbh->errstr;
337 END { } # module clean-up code here (global destructor)