Update issues_stats: add shelving location limit, replace cgi::scrolling_list, update...
[koha.git] / C4 / Accounts.pm
blobe4a745a131de967249bb58f5c97c1e4a5df75623
1 package C4::Accounts;
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 under the
8 # terms of the GNU General Public License as published by the Free Software
9 # Foundation; either version 2 of the License, or (at your option) any later
10 # version.
12 # Koha is distributed in the hope that it will be useful, but WITHOUT ANY
13 # WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
14 # A PARTICULAR PURPOSE. See the GNU General Public License for more details.
16 # You should have received a copy of the GNU General Public License along with
17 # Koha; if not, write to the Free Software Foundation, Inc., 59 Temple Place,
18 # Suite 330, Boston, MA 02111-1307 USA
21 use strict;
22 use C4::Context;
23 use C4::Stats;
24 use C4::Members;
25 use C4::Items;
26 use C4::Circulation;
28 use vars qw($VERSION @ISA @EXPORT);
30 BEGIN {
31 # set the version for version checking
32 $VERSION = 3.03;
33 require Exporter;
34 @ISA = qw(Exporter);
35 @EXPORT = qw(
36 &recordpayment &makepayment &manualinvoice
37 &getnextacctno &reconcileaccount &getcharges &getcredits
38 &getrefunds
39 ); # removed &fixaccounts
42 =head1 NAME
44 C4::Accounts - Functions for dealing with Koha accounts
46 =head1 SYNOPSIS
48 use C4::Accounts;
50 =head1 DESCRIPTION
52 The functions in this module deal with the monetary aspect of Koha,
53 including looking up and modifying the amount of money owed by a
54 patron.
56 =head1 FUNCTIONS
58 =head2 recordpayment
60 &recordpayment($borrowernumber, $payment);
62 Record payment by a patron. C<$borrowernumber> is the patron's
63 borrower number. C<$payment> is a floating-point number, giving the
64 amount that was paid.
66 Amounts owed are paid off oldest first. That is, if the patron has a
67 $1 fine from Feb. 1, another $1 fine from Mar. 1, and makes a payment
68 of $1.50, then the oldest fine will be paid off in full, and $0.50
69 will be credited to the next one.
71 =cut
74 sub recordpayment {
76 #here we update both the accountoffsets and the account lines
77 my ( $borrowernumber, $data ) = @_;
78 my $dbh = C4::Context->dbh;
79 my $newamtos = 0;
80 my $accdata = "";
81 my $branch = C4::Context->userenv->{'branch'};
82 my $amountleft = $data;
84 # begin transaction
85 my $nextaccntno = getnextacctno($borrowernumber);
87 # get lines with outstanding amounts to offset
88 my $sth = $dbh->prepare(
89 "SELECT * FROM accountlines
90 WHERE (borrowernumber = ?) AND (amountoutstanding<>0)
91 ORDER BY date"
93 $sth->execute($borrowernumber);
95 # offset transactions
96 while ( ( $accdata = $sth->fetchrow_hashref ) and ( $amountleft > 0 ) ) {
97 if ( $accdata->{'amountoutstanding'} < $amountleft ) {
98 $newamtos = 0;
99 $amountleft -= $accdata->{'amountoutstanding'};
101 else {
102 $newamtos = $accdata->{'amountoutstanding'} - $amountleft;
103 $amountleft = 0;
105 my $thisacct = $accdata->{accountno};
106 my $usth = $dbh->prepare(
107 "UPDATE accountlines SET amountoutstanding= ?
108 WHERE (borrowernumber = ?) AND (accountno=?)"
110 $usth->execute( $newamtos, $borrowernumber, $thisacct );
111 $usth->finish;
112 $usth = $dbh->prepare(
113 "INSERT INTO accountoffsets
114 (borrowernumber, accountno, offsetaccount, offsetamount)
115 VALUES (?,?,?,?)"
117 $usth->execute( $borrowernumber, $accdata->{'accountno'},
118 $nextaccntno, $newamtos );
119 $usth->finish;
122 # create new line
123 my $usth = $dbh->prepare(
124 "INSERT INTO accountlines
125 (borrowernumber, accountno,date,amount,description,accounttype,amountoutstanding)
126 VALUES (?,?,now(),?,'Payment,thanks','Pay',?)"
128 $usth->execute( $borrowernumber, $nextaccntno, 0 - $data, 0 - $amountleft );
129 $usth->finish;
130 UpdateStats( $branch, 'payment', $data, '', '', '', $borrowernumber, $nextaccntno );
131 $sth->finish;
134 =head2 makepayment
136 &makepayment($borrowernumber, $acctnumber, $amount, $branchcode);
138 Records the fact that a patron has paid off the entire amount he or
139 she owes.
141 C<$borrowernumber> is the patron's borrower number. C<$acctnumber> is
142 the account that was credited. C<$amount> is the amount paid (this is
143 only used to record the payment. It is assumed to be equal to the
144 amount owed). C<$branchcode> is the code of the branch where payment
145 was made.
147 =cut
150 # FIXME - I'm not at all sure about the above, because I don't
151 # understand what the acct* tables in the Koha database are for.
152 sub makepayment {
154 #here we update both the accountoffsets and the account lines
155 #updated to check, if they are paying off a lost item, we return the item
156 # from their card, and put a note on the item record
157 my ( $borrowernumber, $accountno, $amount, $user, $branch ) = @_;
158 my $dbh = C4::Context->dbh;
160 # begin transaction
161 my $nextaccntno = getnextacctno($borrowernumber);
162 my $newamtos = 0;
163 my $sth =
164 $dbh->prepare(
165 "SELECT * FROM accountlines WHERE borrowernumber=? AND accountno=?");
166 $sth->execute( $borrowernumber, $accountno );
167 my $data = $sth->fetchrow_hashref;
168 $sth->finish;
170 $dbh->do(
171 "UPDATE accountlines
172 SET amountoutstanding = 0
173 WHERE borrowernumber = $borrowernumber
174 AND accountno = $accountno
178 # print $updquery;
179 $dbh->do( "
180 INSERT INTO accountoffsets
181 (borrowernumber, accountno, offsetaccount,
182 offsetamount)
183 VALUES ($borrowernumber, $accountno, $nextaccntno, $newamtos)
184 " );
186 # create new line
187 my $payment = 0 - $amount;
188 $dbh->do( "
189 INSERT INTO accountlines
190 (borrowernumber, accountno, date, amount,
191 description, accounttype, amountoutstanding)
192 VALUES ($borrowernumber, $nextaccntno, now(), $payment,
193 'Payment,thanks - $user', 'Pay', 0)
194 " );
196 # FIXME - The second argument to &UpdateStats is supposed to be the
197 # branch code.
198 # UpdateStats is now being passed $accountno too. MTJ
199 UpdateStats( $user, 'payment', $amount, '', '', '', $borrowernumber,
200 $accountno );
201 $sth->finish;
203 #check to see what accounttype
204 if ( $data->{'accounttype'} eq 'Rep' || $data->{'accounttype'} eq 'L' ) {
205 returnlost( $borrowernumber, $data->{'itemnumber'} );
209 =head2 getnextacctno
211 $nextacct = &getnextacctno($borrowernumber);
213 Returns the next unused account number for the patron with the given
214 borrower number.
216 =cut
219 # FIXME - Okay, so what does the above actually _mean_?
220 sub getnextacctno ($) {
221 my ($borrowernumber) = shift or return undef;
222 my $sth = C4::Context->dbh->prepare(
223 "SELECT accountno+1 FROM accountlines
224 WHERE (borrowernumber = ?)
225 ORDER BY accountno DESC
226 LIMIT 1"
228 $sth->execute($borrowernumber);
229 return ($sth->fetchrow || 1);
232 =head2 fixaccounts (removed)
234 &fixaccounts($borrowernumber, $accountnumber, $amount);
237 # FIXME - I don't understand what this function does.
238 sub fixaccounts {
239 my ( $borrowernumber, $accountno, $amount ) = @_;
240 my $dbh = C4::Context->dbh;
241 my $sth = $dbh->prepare(
242 "SELECT * FROM accountlines WHERE borrowernumber=?
243 AND accountno=?"
245 $sth->execute( $borrowernumber, $accountno );
246 my $data = $sth->fetchrow_hashref;
248 # FIXME - Error-checking
249 my $diff = $amount - $data->{'amount'};
250 my $outstanding = $data->{'amountoutstanding'} + $diff;
251 $sth->finish;
253 $dbh->do(<<EOT);
254 UPDATE accountlines
255 SET amount = '$amount',
256 amountoutstanding = '$outstanding'
257 WHERE borrowernumber = $borrowernumber
258 AND accountno = $accountno
260 # FIXME: exceedingly bad form. Use prepare with placholders ("?") in query and execute args.
263 =cut
265 sub returnlost {
266 my ( $borrowernumber, $itemnum ) = @_;
267 C4::Circulation::MarkIssueReturned( $borrowernumber, $itemnum );
268 my $borrower = C4::Members::GetMember( $borrowernumber, 'borrowernumber' );
269 my @datearr = localtime(time);
270 my $date = ( 1900 + $datearr[5] ) . "-" . ( $datearr[4] + 1 ) . "-" . $datearr[3];
271 my $bor = "$borrower->{'firstname'} $borrower->{'surname'} $borrower->{'cardnumber'}";
272 ModItem({ paidfor => "Paid for by $bor $date" }, undef, $itemnum);
275 =head2 manualinvoice
277 &manualinvoice($borrowernumber, $itemnumber, $description, $type,
278 $amount, $user);
280 C<$borrowernumber> is the patron's borrower number.
281 C<$description> is a description of the transaction.
282 C<$type> may be one of C<CS>, C<CB>, C<CW>, C<CF>, C<CL>, C<N>, C<L>,
283 or C<REF>.
284 C<$itemnumber> is the item involved, if pertinent; otherwise, it
285 should be the empty string.
287 =cut
290 # FIXME - Okay, so what does this function do, really?
291 sub manualinvoice {
292 my ( $borrowernumber, $itemnum, $desc, $type, $amount, $user ) = @_;
293 my $dbh = C4::Context->dbh;
294 my $notifyid = 0;
295 my $insert;
296 $itemnum =~ s/ //g;
297 my $accountno = getnextacctno($borrowernumber);
298 my $amountleft = $amount;
300 if ( $type eq 'CS'
301 || $type eq 'CB'
302 || $type eq 'CW'
303 || $type eq 'CF'
304 || $type eq 'CL' )
306 my $amount2 = $amount * -1; # FIXME - $amount2 = -$amount
307 $amountleft =
308 fixcredit( $borrowernumber, $amount2, $itemnum, $type, $user );
310 if ( $type eq 'N' ) {
311 $desc .= " New Card";
313 if ( $type eq 'F' ) {
314 $desc .= " Fine";
316 if ( $type eq 'A' ) {
317 $desc .= " Account Management fee";
319 if ( $type eq 'M' ) {
320 $desc .= " Sundry";
323 if ( $type eq 'L' && $desc eq '' ) {
325 $desc = " Lost Item";
327 if ( $type eq 'REF' ) {
328 $desc .= " Cash Refund";
329 $amountleft = refund( '', $borrowernumber, $amount );
331 if ( ( $type eq 'L' )
332 or ( $type eq 'F' )
333 or ( $type eq 'A' )
334 or ( $type eq 'N' )
335 or ( $type eq 'M' ) )
337 $notifyid = 1;
340 if ( $itemnum ne '' ) {
341 $desc .= " " . $itemnum;
342 my $sth = $dbh->prepare(
343 "INSERT INTO accountlines
344 (borrowernumber, accountno, date, amount, description, accounttype, amountoutstanding, itemnumber,notify_id)
345 VALUES (?, ?, now(), ?,?, ?,?,?,?)");
346 $sth->execute($borrowernumber, $accountno, $amount, $desc, $type, $amountleft, $itemnum,$notifyid) || return $sth->errstr;
347 } else {
348 my $sth=$dbh->prepare("INSERT INTO accountlines
349 (borrowernumber, accountno, date, amount, description, accounttype, amountoutstanding,notify_id)
350 VALUES (?, ?, now(), ?, ?, ?, ?,?)"
352 $sth->execute( $borrowernumber, $accountno, $amount, $desc, $type,
353 $amountleft, $notifyid );
355 return 0;
358 =head2 fixcredit
360 $amountleft = &fixcredit($borrowernumber, $data, $barcode, $type, $user);
362 This function is only used internally, not exported.
363 FIXME - Figure out what this function does, and write it down.
365 =cut
367 sub fixcredit {
369 #here we update both the accountoffsets and the account lines
370 my ( $borrowernumber, $data, $barcode, $type, $user ) = @_;
371 my $dbh = C4::Context->dbh;
372 my $newamtos = 0;
373 my $accdata = "";
374 my $amountleft = $data;
375 if ( $barcode ne '' ) {
376 my $item = GetBiblioFromItemNumber( '', $barcode );
377 my $nextaccntno = getnextacctno($borrowernumber);
378 my $query = "SELECT * FROM accountlines WHERE (borrowernumber=?
379 AND itemnumber=? AND amountoutstanding > 0)";
380 if ( $type eq 'CL' ) {
381 $query .= " AND (accounttype = 'L' OR accounttype = 'Rep')";
383 elsif ( $type eq 'CF' ) {
384 $query .= " AND (accounttype = 'F' OR accounttype = 'FU' OR
385 accounttype='Res' OR accounttype='Rent')";
387 elsif ( $type eq 'CB' ) {
388 $query .= " and accounttype='A'";
391 # print $query;
392 my $sth = $dbh->prepare($query);
393 $sth->execute( $borrowernumber, $item->{'itemnumber'} );
394 $accdata = $sth->fetchrow_hashref;
395 $sth->finish;
396 if ( $accdata->{'amountoutstanding'} < $amountleft ) {
397 $newamtos = 0;
398 $amountleft -= $accdata->{'amountoutstanding'};
400 else {
401 $newamtos = $accdata->{'amountoutstanding'} - $amountleft;
402 $amountleft = 0;
404 my $thisacct = $accdata->{accountno};
405 my $usth = $dbh->prepare(
406 "UPDATE accountlines SET amountoutstanding= ?
407 WHERE (borrowernumber = ?) AND (accountno=?)"
409 $usth->execute( $newamtos, $borrowernumber, $thisacct );
410 $usth->finish;
411 $usth = $dbh->prepare(
412 "INSERT INTO accountoffsets
413 (borrowernumber, accountno, offsetaccount, offsetamount)
414 VALUES (?,?,?,?)"
416 $usth->execute( $borrowernumber, $accdata->{'accountno'},
417 $nextaccntno, $newamtos );
418 $usth->finish;
421 # begin transaction
422 my $nextaccntno = getnextacctno($borrowernumber);
424 # get lines with outstanding amounts to offset
425 my $sth = $dbh->prepare(
426 "SELECT * FROM accountlines
427 WHERE (borrowernumber = ?) AND (amountoutstanding >0)
428 ORDER BY date"
430 $sth->execute($borrowernumber);
432 # print $query;
433 # offset transactions
434 while ( ( $accdata = $sth->fetchrow_hashref ) and ( $amountleft > 0 ) ) {
435 if ( $accdata->{'amountoutstanding'} < $amountleft ) {
436 $newamtos = 0;
437 $amountleft -= $accdata->{'amountoutstanding'};
439 else {
440 $newamtos = $accdata->{'amountoutstanding'} - $amountleft;
441 $amountleft = 0;
443 my $thisacct = $accdata->{accountno};
444 my $usth = $dbh->prepare(
445 "UPDATE accountlines SET amountoutstanding= ?
446 WHERE (borrowernumber = ?) AND (accountno=?)"
448 $usth->execute( $newamtos, $borrowernumber, $thisacct );
449 $usth->finish;
450 $usth = $dbh->prepare(
451 "INSERT INTO accountoffsets
452 (borrowernumber, accountno, offsetaccount, offsetamount)
453 VALUE (?,?,?,?)"
455 $usth->execute( $borrowernumber, $accdata->{'accountno'},
456 $nextaccntno, $newamtos );
457 $usth->finish;
459 $sth->finish;
460 $type = "Credit " . $type;
461 UpdateStats( $user, $type, $data, $user, '', '', $borrowernumber );
462 $amountleft *= -1;
463 return ($amountleft);
467 =head2 refund
469 # FIXME - Figure out what this function does, and write it down.
471 =cut
473 sub refund {
475 #here we update both the accountoffsets and the account lines
476 my ( $borrowernumber, $data ) = @_;
477 my $dbh = C4::Context->dbh;
478 my $newamtos = 0;
479 my $accdata = "";
480 my $amountleft = $data * -1;
482 # begin transaction
483 my $nextaccntno = getnextacctno($borrowernumber);
485 # get lines with outstanding amounts to offset
486 my $sth = $dbh->prepare(
487 "SELECT * FROM accountlines
488 WHERE (borrowernumber = ?) AND (amountoutstanding<0)
489 ORDER BY date"
491 $sth->execute($borrowernumber);
493 # print $amountleft;
494 # offset transactions
495 while ( ( $accdata = $sth->fetchrow_hashref ) and ( $amountleft < 0 ) ) {
496 if ( $accdata->{'amountoutstanding'} > $amountleft ) {
497 $newamtos = 0;
498 $amountleft -= $accdata->{'amountoutstanding'};
500 else {
501 $newamtos = $accdata->{'amountoutstanding'} - $amountleft;
502 $amountleft = 0;
505 # print $amountleft;
506 my $thisacct = $accdata->{accountno};
507 my $usth = $dbh->prepare(
508 "UPDATE accountlines SET amountoutstanding= ?
509 WHERE (borrowernumber = ?) AND (accountno=?)"
511 $usth->execute( $newamtos, $borrowernumber, $thisacct );
512 $usth->finish;
513 $usth = $dbh->prepare(
514 "INSERT INTO accountoffsets
515 (borrowernumber, accountno, offsetaccount, offsetamount)
516 VALUES (?,?,?,?)"
518 $usth->execute( $borrowernumber, $accdata->{'accountno'},
519 $nextaccntno, $newamtos );
520 $usth->finish;
522 $sth->finish;
523 return ($amountleft);
526 sub getcharges {
527 my ( $borrowerno, $timestamp, $accountno ) = @_;
528 my $dbh = C4::Context->dbh;
529 my $timestamp2 = $timestamp - 1;
530 my $query = "";
531 my $sth = $dbh->prepare(
532 "SELECT * FROM accountlines WHERE borrowernumber=? AND accountno = ?"
534 $sth->execute( $borrowerno, $accountno );
536 my @results;
537 while ( my $data = $sth->fetchrow_hashref ) {
538 push @results,$data;
540 return (@results);
544 sub getcredits {
545 my ( $date, $date2 ) = @_;
546 my $dbh = C4::Context->dbh;
547 my $sth = $dbh->prepare(
548 "SELECT * FROM accountlines,borrowers
549 WHERE amount < 0 AND accounttype <> 'Pay' AND accountlines.borrowernumber = borrowers.borrowernumber
550 AND timestamp >=TIMESTAMP(?) AND timestamp < TIMESTAMP(?)"
553 $sth->execute( $date, $date2 );
554 my @results;
555 while ( my $data = $sth->fetchrow_hashref ) {
556 $data->{'date'} = $data->{'timestamp'};
557 push @results,$data;
559 return (@results);
563 sub getrefunds {
564 my ( $date, $date2 ) = @_;
565 my $dbh = C4::Context->dbh;
567 my $sth = $dbh->prepare(
568 "SELECT *,timestamp AS datetime
569 FROM accountlines,borrowers
570 WHERE (accounttype = 'REF'
571 AND accountlines.borrowernumber = borrowers.borrowernumber
572 AND date >=? AND date <?)"
575 $sth->execute( $date, $date2 );
577 my @results;
578 while ( my $data = $sth->fetchrow_hashref ) {
579 push @results,$data;
582 return (@results);
584 END { } # module clean-up code here (global destructor)
587 __END__
589 =head1 SEE ALSO
591 DBI(3)
593 =cut