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;
31 use Data::Dumper qw(Dumper);
33 use vars qw(@ISA @EXPORT);
47 &purge_zero_balance_fees
53 C4::Accounts - Functions for dealing with Koha accounts
61 The functions in this module deal with the monetary aspect of Koha,
62 including looking up and modifying the amount of money owed by a
69 $nextacct = &getnextacctno($borrowernumber);
71 Returns the next unused account number for the patron with the given
77 # FIXME - Okay, so what does the above actually _mean_?
79 my ($borrowernumber) = shift or return;
80 my $sth = C4::Context->dbh->prepare(
81 "SELECT accountno+1 FROM accountlines
82 WHERE (borrowernumber = ?)
83 ORDER BY accountno DESC
86 $sth->execute($borrowernumber);
87 return ($sth->fetchrow || 1);
90 =head2 fixaccounts (removed)
92 &fixaccounts($accountlines_id, $borrowernumber, $accountnumber, $amount);
95 # FIXME - I don't understand what this function does.
97 my ( $accountlines_id, $borrowernumber, $accountno, $amount ) = @_;
98 my $dbh = C4::Context->dbh;
99 my $sth = $dbh->prepare(
100 "SELECT * FROM accountlines WHERE accountlines_id=?"
102 $sth->execute( $accountlines_id );
103 my $data = $sth->fetchrow_hashref;
105 # FIXME - Error-checking
106 my $diff = $amount - $data->{'amount'};
107 my $outstanding = $data->{'amountoutstanding'} + $diff;
112 SET amount = '$amount',
113 amountoutstanding = '$outstanding'
114 WHERE accountlines_id = $accountlines_id
116 # FIXME: exceedingly bad form. Use prepare with placholders ("?") in query and execute args.
122 # lost ==1 Lost, lost==2 longoverdue, lost==3 lost and paid for
123 # FIXME: itemlost should be set to 3 after payment is made, should be a warning to the interface that
124 # a charge has been added
125 # FIXME : if no replacement price, borrower just doesn't get charged?
126 my $dbh = C4::Context->dbh();
127 my ($borrowernumber, $itemnumber, $amount, $description) = @_;
129 # first make sure the borrower hasn't already been charged for this item
130 my $sth1=$dbh->prepare("SELECT * from accountlines
131 WHERE borrowernumber=? AND itemnumber=? and accounttype='L'");
132 $sth1->execute($borrowernumber,$itemnumber);
133 my $existing_charge_hashref=$sth1->fetchrow_hashref();
136 unless ($existing_charge_hashref) {
138 $manager_id = C4::Context->userenv->{'number'} if C4::Context->userenv;
139 # This item is on issue ... add replacement cost to the borrower's record and mark it returned
140 # Note that we add this to the account even if there's no replacement price, allowing some other
141 # process (or person) to update it, since we don't handle any defaults for replacement prices.
142 my $accountno = getnextacctno($borrowernumber);
143 my $sth2=$dbh->prepare("INSERT INTO accountlines
144 (borrowernumber,accountno,date,amount,description,accounttype,amountoutstanding,itemnumber,manager_id)
145 VALUES (?,?,now(),?,?,'L',?,?,?)");
146 $sth2->execute($borrowernumber,$accountno,$amount,
147 $description,$amount,$itemnumber,$manager_id);
149 if ( C4::Context->preference("FinesLog") ) {
150 logaction("FINES", 'CREATE', $borrowernumber, Dumper({
151 action => 'create_fee',
152 borrowernumber => $borrowernumber,
153 accountno => $accountno,
155 amountoutstanding => $amount,
156 description => $description,
158 itemnumber => $itemnumber,
159 manager_id => $manager_id,
168 &manualinvoice($borrowernumber, $itemnumber, $description, $type,
171 C<$borrowernumber> is the patron's borrower number.
172 C<$description> is a description of the transaction.
173 C<$type> may be one of C<CS>, C<CB>, C<CW>, C<CF>, C<CL>, C<N>, C<L>,
175 C<$itemnumber> is the item involved, if pertinent; otherwise, it
176 should be the empty string.
181 # FIXME: In Koha 3.0 , the only account adjustment 'types' passed to this function
184 # 'FOR' = FORGIVEN (Formerly 'F', but 'F' is taken to mean 'FINE' elsewhere)
187 # 'A' = Account Management fee
193 my ( $borrowernumber, $itemnum, $desc, $type, $amount, $note ) = @_;
195 $manager_id = C4::Context->userenv->{'number'} if C4::Context->userenv;
196 my $dbh = C4::Context->dbh;
199 my $accountno = getnextacctno($borrowernumber);
200 my $amountleft = $amount;
202 if ( ( $type eq 'L' )
206 or ( $type eq 'M' ) )
212 $desc .= ' ' . $itemnum;
213 my $sth = $dbh->prepare(
214 'INSERT INTO accountlines
215 (borrowernumber, accountno, date, amount, description, accounttype, amountoutstanding, itemnumber,notify_id, note, manager_id)
216 VALUES (?, ?, now(), ?,?, ?,?,?,?,?,?)');
217 $sth->execute($borrowernumber, $accountno, $amount, $desc, $type, $amountleft, $itemnum,$notifyid, $note, $manager_id) || return $sth->errstr;
219 my $sth=$dbh->prepare("INSERT INTO accountlines
220 (borrowernumber, accountno, date, amount, description, accounttype, amountoutstanding,notify_id, note, manager_id)
221 VALUES (?, ?, now(), ?, ?, ?, ?,?,?,?)"
223 $sth->execute( $borrowernumber, $accountno, $amount, $desc, $type,
224 $amountleft, $notifyid, $note, $manager_id );
227 if ( C4::Context->preference("FinesLog") ) {
228 logaction("FINES", 'CREATE',$borrowernumber,Dumper({
229 action => 'create_fee',
230 borrowernumber => $borrowernumber,
231 accountno => $accountno,
233 description => $desc,
234 accounttype => $type,
235 amountoutstanding => $amountleft,
236 notify_id => $notifyid,
238 itemnumber => $itemnum,
239 manager_id => $manager_id,
247 my ( $borrowerno, $timestamp, $accountno ) = @_;
248 my $dbh = C4::Context->dbh;
249 my $timestamp2 = $timestamp - 1;
251 my $sth = $dbh->prepare(
252 "SELECT * FROM accountlines WHERE borrowernumber=? AND accountno = ?"
254 $sth->execute( $borrowerno, $accountno );
257 while ( my $data = $sth->fetchrow_hashref ) {
264 my ( $accountlines_id, $note ) = @_;
265 my $dbh = C4::Context->dbh;
266 my $sth = $dbh->prepare('UPDATE accountlines SET note = ? WHERE accountlines_id = ?');
267 $sth->execute( $note, $accountlines_id );
271 my ( $date, $date2 ) = @_;
272 my $dbh = C4::Context->dbh;
273 my $sth = $dbh->prepare(
274 "SELECT * FROM accountlines,borrowers
275 WHERE amount < 0 AND accounttype not like 'Pay%' AND accountlines.borrowernumber = borrowers.borrowernumber
276 AND timestamp >=TIMESTAMP(?) AND timestamp < TIMESTAMP(?)"
279 $sth->execute( $date, $date2 );
281 while ( my $data = $sth->fetchrow_hashref ) {
282 $data->{'date'} = $data->{'timestamp'};
290 my ( $date, $date2 ) = @_;
291 my $dbh = C4::Context->dbh;
293 my $sth = $dbh->prepare(
294 "SELECT *,timestamp AS datetime
295 FROM accountlines,borrowers
296 WHERE (accounttype = 'REF'
297 AND accountlines.borrowernumber = borrowers.borrowernumber
298 AND date >=? AND date <?)"
301 $sth->execute( $date, $date2 );
304 while ( my $data = $sth->fetchrow_hashref ) {
312 my ( $accountlines_id ) = @_;
313 my $dbh = C4::Context->dbh;
315 my $sth = $dbh->prepare('SELECT * FROM accountlines WHERE accountlines_id = ?');
316 $sth->execute( $accountlines_id );
317 my $row = $sth->fetchrow_hashref();
318 my $amount_outstanding = $row->{'amountoutstanding'};
320 if ( $amount_outstanding <= 0 ) {
321 $sth = $dbh->prepare('UPDATE accountlines SET amountoutstanding = amount * -1, description = CONCAT( description, " Reversed -" ) WHERE accountlines_id = ?');
322 $sth->execute( $accountlines_id );
324 $sth = $dbh->prepare('UPDATE accountlines SET amountoutstanding = 0, description = CONCAT( description, " Reversed -" ) WHERE accountlines_id = ?');
325 $sth->execute( $accountlines_id );
328 if ( C4::Context->preference("FinesLog") ) {
330 $manager_id = C4::Context->userenv->{'number'} if C4::Context->userenv;
332 if ( $amount_outstanding <= 0 ) {
333 $row->{'amountoutstanding'} *= -1;
335 $row->{'amountoutstanding'} = '0';
337 $row->{'description'} .= ' Reversed -';
338 logaction("FINES", 'MODIFY', $row->{'borrowernumber'}, Dumper({
339 action => 'reverse_fee_payment',
340 borrowernumber => $row->{'borrowernumber'},
341 old_amountoutstanding => $row->{'amountoutstanding'},
342 new_amountoutstanding => 0 - $amount_outstanding,,
343 accountlines_id => $row->{'accountlines_id'},
344 accountno => $row->{'accountno'},
345 manager_id => $manager_id,
352 =head2 purge_zero_balance_fees
354 purge_zero_balance_fees( $days );
356 Delete accountlines entries where amountoutstanding is 0 or NULL which are more than a given number of days old.
358 B<$days> -- Zero balance fees older than B<$days> days old will be deleted.
360 B<Warning:> Because fines and payments are not linked in accountlines, it is
361 possible for a fine to be deleted without the accompanying payment,
362 or vise versa. This won't affect the account balance, but might be
367 sub purge_zero_balance_fees {
371 my $dbh = C4::Context->dbh;
372 my $sth = $dbh->prepare(
374 DELETE FROM accountlines
375 WHERE date < date_sub(curdate(), INTERVAL ? DAY)
376 AND ( amountoutstanding = 0 or amountoutstanding IS NULL );
379 $sth->execute($days) or die $dbh->errstr;
382 END { } # module clean-up code here (global destructor)