Merge remote-tracking branch 'kc/new/enh/bug_6716' into kcmaster
[koha.git] / C4 / Accounts.pm
1 package C4::Accounts;
2
3 # Copyright 2000-2002 Katipo Communications
4 #
5 # This file is part of Koha.
6 #
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.
11 #
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.
15 #
16 # You should have received a copy of the GNU General Public License along
17 # with Koha; if not, write to the Free Software Foundation, Inc.,
18 # 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
19
20
21 use strict;
22 #use warnings; FIXME - Bug 2505
23 use C4::Context;
24 use C4::Stats;
25 use C4::Members;
26 use C4::Items;
27 use C4::Circulation qw(MarkIssueReturned);
28
29 use vars qw($VERSION @ISA @EXPORT);
30
31 BEGIN {
32         # set the version for version checking
33         $VERSION = 3.03;
34         require Exporter;
35         @ISA    = qw(Exporter);
36         @EXPORT = qw(
37                 &recordpayment &makepayment &manualinvoice
38                 &getnextacctno &reconcileaccount &getcharges &ModNote &getcredits
39                 &getrefunds &chargelostitem
40                 &ReversePayment
41         makepartialpayment
42         recordpayment_selectaccts
43         );
44 }
45
46 =head1 NAME
47
48 C4::Accounts - Functions for dealing with Koha accounts
49
50 =head1 SYNOPSIS
51
52 use C4::Accounts;
53
54 =head1 DESCRIPTION
55
56 The functions in this module deal with the monetary aspect of Koha,
57 including looking up and modifying the amount of money owed by a
58 patron.
59
60 =head1 FUNCTIONS
61
62 =head2 recordpayment
63
64   &recordpayment($borrowernumber, $payment);
65
66 Record payment by a patron. C<$borrowernumber> is the patron's
67 borrower number. C<$payment> is a floating-point number, giving the
68 amount that was paid. 
69
70 Amounts owed are paid off oldest first. That is, if the patron has a
71 $1 fine from Feb. 1, another $1 fine from Mar. 1, and makes a payment
72 of $1.50, then the oldest fine will be paid off in full, and $0.50
73 will be credited to the next one.
74
75 =cut
76
77 #'
78 sub recordpayment {
79
80     #here we update the account lines
81     my ( $borrowernumber, $data ) = @_;
82     my $dbh        = C4::Context->dbh;
83     my $newamtos   = 0;
84     my $accdata    = "";
85     my $branch     = C4::Context->userenv->{'branch'};
86     my $amountleft = $data;
87
88     # begin transaction
89     my $nextaccntno = getnextacctno($borrowernumber);
90
91     # get lines with outstanding amounts to offset
92     my $sth = $dbh->prepare(
93         "SELECT * FROM accountlines
94   WHERE (borrowernumber = ?) AND (amountoutstanding<>0)
95   ORDER BY date"
96     );
97     $sth->execute($borrowernumber);
98
99     # offset transactions
100     while ( ( $accdata = $sth->fetchrow_hashref ) and ( $amountleft > 0 ) ) {
101         if ( $accdata->{'amountoutstanding'} < $amountleft ) {
102             $newamtos = 0;
103             $amountleft -= $accdata->{'amountoutstanding'};
104         }
105         else {
106             $newamtos   = $accdata->{'amountoutstanding'} - $amountleft;
107             $amountleft = 0;
108         }
109         my $thisacct = $accdata->{accountno};
110         my $usth     = $dbh->prepare(
111             "UPDATE accountlines SET amountoutstanding= ?
112      WHERE (borrowernumber = ?) AND (accountno=?)"
113         );
114         $usth->execute( $newamtos, $borrowernumber, $thisacct );
115         $usth->finish;
116 #        $usth = $dbh->prepare(
117 #            "INSERT INTO accountoffsets
118 #     (borrowernumber, accountno, offsetaccount,  offsetamount)
119 #     VALUES (?,?,?,?)"
120 #        );
121 #        $usth->execute( $borrowernumber, $accdata->{'accountno'},
122 #            $nextaccntno, $newamtos );
123         $usth->finish;
124     }
125
126     # create new line
127     my $usth = $dbh->prepare(
128         "INSERT INTO accountlines
129   (borrowernumber, accountno,date,amount,description,accounttype,amountoutstanding)
130   VALUES (?,?,now(),?,'Payment,thanks','Pay',?)"
131     );
132     $usth->execute( $borrowernumber, $nextaccntno, 0 - $data, 0 - $amountleft );
133     $usth->finish;
134     UpdateStats( $branch, 'payment', $data, '', '', '', $borrowernumber, $nextaccntno );
135     $sth->finish;
136 }
137
138 =head2 makepayment
139
140   &makepayment($borrowernumber, $acctnumber, $amount, $branchcode);
141
142 Records the fact that a patron has paid off the entire amount he or
143 she owes.
144
145 C<$borrowernumber> is the patron's borrower number. C<$acctnumber> is
146 the account that was credited. C<$amount> is the amount paid (this is
147 only used to record the payment. It is assumed to be equal to the
148 amount owed). C<$branchcode> is the code of the branch where payment
149 was made.
150
151 =cut
152
153 #'
154 # FIXME - I'm not at all sure about the above, because I don't
155 # understand what the acct* tables in the Koha database are for.
156 sub makepayment {
157
158     #here we update both the accountoffsets and the account lines
159     #updated to check, if they are paying off a lost item, we return the item
160     # from their card, and put a note on the item record
161     my ( $borrowernumber, $accountno, $amount, $user, $branch ) = @_;
162     my $dbh = C4::Context->dbh;
163     my $manager_id = 0;
164     $manager_id = C4::Context->userenv->{'number'} if C4::Context->userenv; 
165
166     # begin transaction
167     my $nextaccntno = getnextacctno($borrowernumber);
168     my $newamtos    = 0;
169     my $sth =
170       $dbh->prepare(
171         "SELECT * FROM accountlines WHERE  borrowernumber=? AND accountno=?");
172     $sth->execute( $borrowernumber, $accountno );
173     my $data = $sth->fetchrow_hashref;
174     $sth->finish;
175
176     if($data->{'accounttype'} eq "Pay"){
177         my $udp =               
178             $dbh->prepare(
179                 "UPDATE accountlines
180                     SET amountoutstanding = 0, description = 'Payment,thanks'
181                     WHERE borrowernumber = ?
182                     AND accountno = ?
183                 "
184             );
185         $udp->execute($borrowernumber, $accountno );
186         $udp->finish;
187     }else{
188         my $udp =               
189             $dbh->prepare(
190                 "UPDATE accountlines
191                     SET amountoutstanding = 0
192                     WHERE borrowernumber = ?
193                     AND accountno = ?
194                 "
195             );
196         $udp->execute($borrowernumber, $accountno );
197         $udp->finish;
198
199          # create new line
200         my $payment = 0 - $amount;
201         
202         my $ins = 
203             $dbh->prepare( 
204                 "INSERT 
205                     INTO accountlines (borrowernumber, accountno, date, amount, description, accounttype, amountoutstanding)
206                     VALUES ( ?, ?, now(), ?, 'Payment,thanks', 'Pay', 0)"
207             );
208         $ins->execute($borrowernumber, $nextaccntno, $payment);
209         $ins->finish;
210     }
211
212     # FIXME - The second argument to &UpdateStats is supposed to be the
213     # branch code.
214     # UpdateStats is now being passed $accountno too. MTJ
215     UpdateStats( $user, 'payment', $amount, '', '', '', $borrowernumber,
216         $accountno );
217     #from perldoc: for SELECT only #$sth->finish;
218
219     #check to see what accounttype
220     if ( $data->{'accounttype'} eq 'Rep' || $data->{'accounttype'} eq 'L' ) {
221         returnlost( $borrowernumber, $data->{'itemnumber'} );
222     }
223 }
224
225 =head2 getnextacctno
226
227   $nextacct = &getnextacctno($borrowernumber);
228
229 Returns the next unused account number for the patron with the given
230 borrower number.
231
232 =cut
233
234 #'
235 # FIXME - Okay, so what does the above actually _mean_?
236 sub getnextacctno ($) {
237     my ($borrowernumber) = shift or return undef;
238     my $sth = C4::Context->dbh->prepare(
239         "SELECT accountno+1 FROM accountlines
240          WHERE    (borrowernumber = ?)
241          ORDER BY accountno DESC
242                  LIMIT 1"
243     );
244     $sth->execute($borrowernumber);
245     return ($sth->fetchrow || 1);
246 }
247
248 =head2 fixaccounts (removed)
249
250   &fixaccounts($borrowernumber, $accountnumber, $amount);
251
252 #'
253 # FIXME - I don't understand what this function does.
254 sub fixaccounts {
255     my ( $borrowernumber, $accountno, $amount ) = @_;
256     my $dbh = C4::Context->dbh;
257     my $sth = $dbh->prepare(
258         "SELECT * FROM accountlines WHERE borrowernumber=?
259      AND accountno=?"
260     );
261     $sth->execute( $borrowernumber, $accountno );
262     my $data = $sth->fetchrow_hashref;
263
264     # FIXME - Error-checking
265     my $diff        = $amount - $data->{'amount'};
266     my $outstanding = $data->{'amountoutstanding'} + $diff;
267     $sth->finish;
268
269     $dbh->do(<<EOT);
270         UPDATE  accountlines
271         SET     amount = '$amount',
272                 amountoutstanding = '$outstanding'
273         WHERE   borrowernumber = $borrowernumber
274           AND   accountno = $accountno
275 EOT
276         # FIXME: exceedingly bad form.  Use prepare with placholders ("?") in query and execute args.
277 }
278
279 =cut
280
281 sub returnlost{
282     my ( $borrowernumber, $itemnum ) = @_;
283     C4::Circulation::MarkIssueReturned( $borrowernumber, $itemnum );
284     my $borrower = C4::Members::GetMember( 'borrowernumber'=>$borrowernumber );
285     my @datearr = localtime(time);
286     my $date = ( 1900 + $datearr[5] ) . "-" . ( $datearr[4] + 1 ) . "-" . $datearr[3];
287     my $bor = "$borrower->{'firstname'} $borrower->{'surname'} $borrower->{'cardnumber'}";
288     ModItem({ paidfor =>  "Paid for by $bor $date" }, undef, $itemnum);
289 }
290
291
292 sub chargelostitem{
293 # lost ==1 Lost, lost==2 longoverdue, lost==3 lost and paid for
294 # FIXME: itemlost should be set to 3 after payment is made, should be a warning to the interface that
295 # a charge has been added
296 # FIXME : if no replacement price, borrower just doesn't get charged?
297    
298     my $dbh = C4::Context->dbh();
299     my ($itemnumber) = @_;
300     my $sth=$dbh->prepare("SELECT issues.*,items.*,biblio.title 
301                            FROM issues 
302                            JOIN items USING (itemnumber) 
303                            JOIN biblio USING (biblionumber)
304                            WHERE issues.itemnumber=?");
305     $sth->execute($itemnumber);
306     my $issues=$sth->fetchrow_hashref();
307
308     # if a borrower lost the item, add a replacement cost to the their record
309     if ( $issues->{borrowernumber} ){
310
311         # first make sure the borrower hasn't already been charged for this item
312         my $sth1=$dbh->prepare("SELECT * from accountlines
313         WHERE borrowernumber=? AND itemnumber=? and accounttype='L'");
314         $sth1->execute($issues->{'borrowernumber'},$itemnumber);
315         my $existing_charge_hashref=$sth1->fetchrow_hashref();
316
317         # OK, they haven't
318         unless ($existing_charge_hashref) {
319             # This item is on issue ... add replacement cost to the borrower's record and mark it returned
320             #  Note that we add this to the account even if there's no replacement price, allowing some other
321             #  process (or person) to update it, since we don't handle any defaults for replacement prices.
322             my $accountno = getnextacctno($issues->{'borrowernumber'});
323             my $sth2=$dbh->prepare("INSERT INTO accountlines
324             (borrowernumber,accountno,date,amount,description,accounttype,amountoutstanding,itemnumber)
325             VALUES (?,?,now(),?,?,'L',?,?)");
326             $sth2->execute($issues->{'borrowernumber'},$accountno,$issues->{'replacementprice'},
327             "Lost Item $issues->{'title'} $issues->{'barcode'}",
328             $issues->{'replacementprice'},$itemnumber);
329             $sth2->finish;
330         # FIXME: Log this ?
331         }
332         #FIXME : Should probably have a way to distinguish this from an item that really was returned.
333         #warn " $issues->{'borrowernumber'}  /  $itemnumber ";
334         C4::Circulation::MarkIssueReturned($issues->{borrowernumber},$itemnumber);
335         #  Shouldn't MarkIssueReturned do this?
336         C4::Items::ModItem({ onloan => undef }, undef, $itemnumber);
337     }
338     $sth->finish;
339 }
340
341 =head2 manualinvoice
342
343   &manualinvoice($borrowernumber, $itemnumber, $description, $type,
344                  $amount, $note);
345
346 C<$borrowernumber> is the patron's borrower number.
347 C<$description> is a description of the transaction.
348 C<$type> may be one of C<CS>, C<CB>, C<CW>, C<CF>, C<CL>, C<N>, C<L>,
349 or C<REF>.
350 C<$itemnumber> is the item involved, if pertinent; otherwise, it
351 should be the empty string.
352
353 =cut
354
355 #'
356 # FIXME: In Koha 3.0 , the only account adjustment 'types' passed to this function
357 # are :  
358 #               'C' = CREDIT
359 #               'FOR' = FORGIVEN  (Formerly 'F', but 'F' is taken to mean 'FINE' elsewhere)
360 #               'N' = New Card fee
361 #               'F' = Fine
362 #               'A' = Account Management fee
363 #               'M' = Sundry
364 #               'L' = Lost Item
365 #
366
367 sub manualinvoice {
368     my ( $borrowernumber, $itemnum, $desc, $type, $amount, $note ) = @_;
369     my $manager_id = 0;
370     $manager_id = C4::Context->userenv->{'number'} if C4::Context->userenv;
371     my $dbh      = C4::Context->dbh;
372     my $notifyid = 0;
373     my $insert;
374     my $accountno  = getnextacctno($borrowernumber);
375     my $amountleft = $amount;
376
377 #    if (   $type eq 'CS'
378 #        || $type eq 'CB'
379 #        || $type eq 'CW'
380 #        || $type eq 'CF'
381 #        || $type eq 'CL' )
382 #    {
383 #        my $amount2 = $amount * -1;    # FIXME - $amount2 = -$amount
384 #        $amountleft =
385 #          fixcredit( $borrowernumber, $amount2, $itemnum, $type, $user );
386 #    }
387     if ( $type eq 'N' ) {
388         $desc .= " New Card";
389     }
390     if ( $type eq 'F' ) {
391         $desc .= " Fine";
392     }
393     if ( $type eq 'A' ) {
394         $desc .= " Account Management fee";
395     }
396     if ( $type eq 'M' ) {
397         $desc .= " Sundry";
398     }
399
400     if ( $type eq 'L' && $desc eq '' ) {
401
402         $desc = " Lost Item";
403     }
404 #    if ( $type eq 'REF' ) {
405 #        $desc .= " Cash Refund";
406 #        $amountleft = refund( '', $borrowernumber, $amount );
407 #    }
408     if (   ( $type eq 'L' )
409         or ( $type eq 'F' )
410         or ( $type eq 'A' )
411         or ( $type eq 'N' )
412         or ( $type eq 'M' ) )
413     {
414         $notifyid = 1;
415     }
416
417     if ( $itemnum ) {
418         $desc .= ' ' . $itemnum;
419         my $sth = $dbh->prepare(
420             'INSERT INTO  accountlines
421                         (borrowernumber, accountno, date, amount, description, accounttype, amountoutstanding, itemnumber,notify_id, note, manager_id)
422         VALUES (?, ?, now(), ?,?, ?,?,?,?,?,?)');
423      $sth->execute($borrowernumber, $accountno, $amount, $desc, $type, $amountleft, $itemnum,$notifyid, $note, $manager_id) || return $sth->errstr;
424   } else {
425     my $sth=$dbh->prepare("INSERT INTO  accountlines
426             (borrowernumber, accountno, date, amount, description, accounttype, amountoutstanding,notify_id, note, manager_id)
427             VALUES (?, ?, now(), ?, ?, ?, ?,?,?,?)"
428         );
429         $sth->execute( $borrowernumber, $accountno, $amount, $desc, $type,
430             $amountleft, $notifyid, $note, $manager_id );
431     }
432     return 0;
433 }
434
435 =head2 fixcredit #### DEPRECATED
436
437  $amountleft = &fixcredit($borrowernumber, $data, $barcode, $type, $user);
438
439  This function is only used internally, not exported.
440
441 =cut
442
443 # This function is deprecated in 3.0
444
445 sub fixcredit {
446
447     #here we update both the accountoffsets and the account lines
448     my ( $borrowernumber, $data, $barcode, $type, $user ) = @_;
449     my $dbh        = C4::Context->dbh;
450     my $newamtos   = 0;
451     my $accdata    = "";
452     my $amountleft = $data;
453     if ( $barcode ne '' ) {
454         my $item        = GetBiblioFromItemNumber( '', $barcode );
455         my $nextaccntno = getnextacctno($borrowernumber);
456         my $query       = "SELECT * FROM accountlines WHERE (borrowernumber=?
457     AND itemnumber=? AND amountoutstanding > 0)";
458         if ( $type eq 'CL' ) {
459             $query .= " AND (accounttype = 'L' OR accounttype = 'Rep')";
460         }
461         elsif ( $type eq 'CF' ) {
462             $query .= " AND (accounttype = 'F' OR accounttype = 'FU' OR
463       accounttype='Res' OR accounttype='Rent')";
464         }
465         elsif ( $type eq 'CB' ) {
466             $query .= " and accounttype='A'";
467         }
468
469         #    print $query;
470         my $sth = $dbh->prepare($query);
471         $sth->execute( $borrowernumber, $item->{'itemnumber'} );
472         $accdata = $sth->fetchrow_hashref;
473         $sth->finish;
474         if ( $accdata->{'amountoutstanding'} < $amountleft ) {
475             $newamtos = 0;
476             $amountleft -= $accdata->{'amountoutstanding'};
477         }
478         else {
479             $newamtos   = $accdata->{'amountoutstanding'} - $amountleft;
480             $amountleft = 0;
481         }
482         my $thisacct = $accdata->{accountno};
483         my $usth     = $dbh->prepare(
484             "UPDATE accountlines SET amountoutstanding= ?
485      WHERE (borrowernumber = ?) AND (accountno=?)"
486         );
487         $usth->execute( $newamtos, $borrowernumber, $thisacct );
488         $usth->finish;
489         $usth = $dbh->prepare(
490             "INSERT INTO accountoffsets
491      (borrowernumber, accountno, offsetaccount,  offsetamount)
492      VALUES (?,?,?,?)"
493         );
494         $usth->execute( $borrowernumber, $accdata->{'accountno'},
495             $nextaccntno, $newamtos );
496         $usth->finish;
497     }
498
499     # begin transaction
500     my $nextaccntno = getnextacctno($borrowernumber);
501
502     # get lines with outstanding amounts to offset
503     my $sth = $dbh->prepare(
504         "SELECT * FROM accountlines
505   WHERE (borrowernumber = ?) AND (amountoutstanding >0)
506   ORDER BY date"
507     );
508     $sth->execute($borrowernumber);
509
510     #  print $query;
511     # offset transactions
512     while ( ( $accdata = $sth->fetchrow_hashref ) and ( $amountleft > 0 ) ) {
513         if ( $accdata->{'amountoutstanding'} < $amountleft ) {
514             $newamtos = 0;
515             $amountleft -= $accdata->{'amountoutstanding'};
516         }
517         else {
518             $newamtos   = $accdata->{'amountoutstanding'} - $amountleft;
519             $amountleft = 0;
520         }
521         my $thisacct = $accdata->{accountno};
522         my $usth     = $dbh->prepare(
523             "UPDATE accountlines SET amountoutstanding= ?
524      WHERE (borrowernumber = ?) AND (accountno=?)"
525         );
526         $usth->execute( $newamtos, $borrowernumber, $thisacct );
527         $usth->finish;
528         $usth = $dbh->prepare(
529             "INSERT INTO accountoffsets
530      (borrowernumber, accountno, offsetaccount,  offsetamount)
531      VALUE (?,?,?,?)"
532         );
533         $usth->execute( $borrowernumber, $accdata->{'accountno'},
534             $nextaccntno, $newamtos );
535         $usth->finish;
536     }
537     $sth->finish;
538     $type = "Credit " . $type;
539     UpdateStats( $user, $type, $data, $user, '', '', $borrowernumber );
540     $amountleft *= -1;
541     return ($amountleft);
542
543 }
544
545 =head2 refund
546
547 #FIXME : DEPRECATED SUB
548  This subroutine tracks payments and/or credits against fines/charges
549    using the accountoffsets table, which is not used consistently in
550    Koha's fines management, and so is not used in 3.0 
551
552 =cut 
553
554 sub refund {
555
556     #here we update both the accountoffsets and the account lines
557     my ( $borrowernumber, $data ) = @_;
558     my $dbh        = C4::Context->dbh;
559     my $newamtos   = 0;
560     my $accdata    = "";
561     my $amountleft = $data * -1;
562
563     # begin transaction
564     my $nextaccntno = getnextacctno($borrowernumber);
565
566     # get lines with outstanding amounts to offset
567     my $sth = $dbh->prepare(
568         "SELECT * FROM accountlines
569   WHERE (borrowernumber = ?) AND (amountoutstanding<0)
570   ORDER BY date"
571     );
572     $sth->execute($borrowernumber);
573
574     #  print $amountleft;
575     # offset transactions
576     while ( ( $accdata = $sth->fetchrow_hashref ) and ( $amountleft < 0 ) ) {
577         if ( $accdata->{'amountoutstanding'} > $amountleft ) {
578             $newamtos = 0;
579             $amountleft -= $accdata->{'amountoutstanding'};
580         }
581         else {
582             $newamtos   = $accdata->{'amountoutstanding'} - $amountleft;
583             $amountleft = 0;
584         }
585
586         #     print $amountleft;
587         my $thisacct = $accdata->{accountno};
588         my $usth     = $dbh->prepare(
589             "UPDATE accountlines SET amountoutstanding= ?
590      WHERE (borrowernumber = ?) AND (accountno=?)"
591         );
592         $usth->execute( $newamtos, $borrowernumber, $thisacct );
593         $usth->finish;
594         $usth = $dbh->prepare(
595             "INSERT INTO accountoffsets
596      (borrowernumber, accountno, offsetaccount,  offsetamount)
597      VALUES (?,?,?,?)"
598         );
599         $usth->execute( $borrowernumber, $accdata->{'accountno'},
600             $nextaccntno, $newamtos );
601         $usth->finish;
602     }
603     $sth->finish;
604     return ($amountleft);
605 }
606
607 sub getcharges {
608         my ( $borrowerno, $timestamp, $accountno ) = @_;
609         my $dbh        = C4::Context->dbh;
610         my $timestamp2 = $timestamp - 1;
611         my $query      = "";
612         my $sth = $dbh->prepare(
613                         "SELECT * FROM accountlines WHERE borrowernumber=? AND accountno = ?"
614           );
615         $sth->execute( $borrowerno, $accountno );
616         
617     my @results;
618     while ( my $data = $sth->fetchrow_hashref ) {
619                 push @results,$data;
620         }
621     return (@results);
622 }
623
624 sub ModNote {
625     my ( $borrowernumber, $accountno, $note ) = @_;
626     my $dbh = C4::Context->dbh;
627     my $sth = $dbh->prepare('UPDATE accountlines SET note = ? WHERE borrowernumber = ? AND accountno = ?');
628     $sth->execute( $note, $borrowernumber, $accountno );
629 }
630
631 sub getcredits {
632         my ( $date, $date2 ) = @_;
633         my $dbh = C4::Context->dbh;
634         my $sth = $dbh->prepare(
635                                 "SELECT * FROM accountlines,borrowers
636       WHERE amount < 0 AND accounttype <> 'Pay' AND accountlines.borrowernumber = borrowers.borrowernumber
637           AND timestamp >=TIMESTAMP(?) AND timestamp < TIMESTAMP(?)"
638       );  
639
640     $sth->execute( $date, $date2 );                                                                                                              
641     my @results;          
642     while ( my $data = $sth->fetchrow_hashref ) {
643                 $data->{'date'} = $data->{'timestamp'};
644                 push @results,$data;
645         }
646     return (@results);
647
648
649
650 sub getrefunds {
651         my ( $date, $date2 ) = @_;
652         my $dbh = C4::Context->dbh;
653         
654         my $sth = $dbh->prepare(
655                                 "SELECT *,timestamp AS datetime                                                                                      
656                   FROM accountlines,borrowers
657                   WHERE (accounttype = 'REF'
658                                           AND accountlines.borrowernumber = borrowers.borrowernumber
659                                                           AND date  >=?  AND date  <?)"
660     );
661
662     $sth->execute( $date, $date2 );
663
664     my @results;
665     while ( my $data = $sth->fetchrow_hashref ) {
666                 push @results,$data;
667                 
668         }
669     return (@results);
670 }
671
672 sub ReversePayment {
673   my ( $borrowernumber, $accountno ) = @_;
674   my $dbh = C4::Context->dbh;
675   
676   my $sth = $dbh->prepare('SELECT amountoutstanding FROM accountlines WHERE borrowernumber = ? AND accountno = ?');
677   $sth->execute( $borrowernumber, $accountno );
678   my $row = $sth->fetchrow_hashref();
679   my $amount_outstanding = $row->{'amountoutstanding'};
680   
681   if ( $amount_outstanding <= 0 ) {
682     $sth = $dbh->prepare('UPDATE accountlines SET amountoutstanding = amount * -1, description = CONCAT( description, " Reversed -" ) WHERE borrowernumber = ? AND accountno = ?');
683     $sth->execute( $borrowernumber, $accountno );
684   } else {
685     $sth = $dbh->prepare('UPDATE accountlines SET amountoutstanding = 0, description = CONCAT( description, " Reversed -" ) WHERE borrowernumber = ? AND accountno = ?');
686     $sth->execute( $borrowernumber, $accountno );
687   }
688 }
689
690 =head2 recordpayment_selectaccts
691
692   recordpayment_selectaccts($borrowernumber, $payment,$accts);
693
694 Record payment by a patron. C<$borrowernumber> is the patron's
695 borrower number. C<$payment> is a floating-point number, giving the
696 amount that was paid. C<$accts> is an array ref to a list of
697 accountnos which the payment can be recorded against
698
699 Amounts owed are paid off oldest first. That is, if the patron has a
700 $1 fine from Feb. 1, another $1 fine from Mar. 1, and makes a payment
701 of $1.50, then the oldest fine will be paid off in full, and $0.50
702 will be credited to the next one.
703
704 =cut
705
706 sub recordpayment_selectaccts {
707     my ( $borrowernumber, $amount, $accts ) = @_;
708
709     my $dbh        = C4::Context->dbh;
710     my $newamtos   = 0;
711     my $accdata    = q{};
712     my $branch     = C4::Context->userenv->{branch};
713     my $amountleft = $amount;
714     my $sql = 'SELECT * FROM accountlines WHERE (borrowernumber = ?) ' .
715     'AND (amountoutstanding<>0) ';
716     if (@{$accts} ) {
717         $sql .= ' AND accountno IN ( ' .  join ',', @{$accts};
718         $sql .= ' ) ';
719     }
720     $sql .= ' ORDER BY date';
721     # begin transaction
722     my $nextaccntno = getnextacctno($borrowernumber);
723
724     # get lines with outstanding amounts to offset
725     my $rows = $dbh->selectall_arrayref($sql, { Slice => {} }, $borrowernumber);
726
727     # offset transactions
728     my $sth     = $dbh->prepare('UPDATE accountlines SET amountoutstanding= ? ' .
729         'WHERE (borrowernumber = ?) AND (accountno=?)');
730     for my $accdata ( @{$rows} ) {
731         if ($amountleft == 0) {
732             last;
733         }
734         if ( $accdata->{amountoutstanding} < $amountleft ) {
735             $newamtos = 0;
736             $amountleft -= $accdata->{amountoutstanding};
737         }
738         else {
739             $newamtos   = $accdata->{amountoutstanding} - $amountleft;
740             $amountleft = 0;
741         }
742         my $thisacct = $accdata->{accountno};
743         $sth->execute( $newamtos, $borrowernumber, $thisacct );
744     }
745
746     # create new line
747     $sql = 'INSERT INTO accountlines ' .
748     '(borrowernumber, accountno,date,amount,description,accounttype,amountoutstanding) ' .
749     q|VALUES (?,?,now(),?,'Payment,thanks','Pay',?)|;
750     $dbh->do($sql,{},$borrowernumber, $nextaccntno, 0 - $amount, 0 - $amountleft );
751     UpdateStats( $branch, 'payment', $amount, '', '', '', $borrowernumber, $nextaccntno );
752     return;
753 }
754
755 # makepayment needs to be fixed to handle partials till then this separate subroutine
756 # fills in
757 sub makepartialpayment {
758     my ( $borrowernumber, $accountno, $amount, $user, $branch ) = @_;
759     if (!$amount || $amount < 0) {
760         return;
761     }
762     my $dbh = C4::Context->dbh;
763
764     my $nextaccntno = getnextacctno($borrowernumber);
765     my $newamtos    = 0;
766
767     my $data = $dbh->selectrow_hashref(
768         'SELECT * FROM accountlines WHERE  borrowernumber=? AND accountno=?',undef,$borrowernumber,$accountno);
769     my $new_outstanding = $data->{amountoutstanding} - $amount;
770
771     my $update = 'UPDATE  accountlines SET amountoutstanding = ?  WHERE   borrowernumber = ? '
772     . ' AND   accountno = ?';
773     $dbh->do( $update, undef, $new_outstanding, $borrowernumber, $accountno);
774
775     # create new line
776     my $insert = 'INSERT INTO accountlines (borrowernumber, accountno, date, amount, '
777     .  'description, accounttype, amountoutstanding) '
778     . ' VALUES (?, ?, now(), ?, ?, ?, 0)';
779
780     $dbh->do(  $insert, undef, $borrowernumber, $nextaccntno, $amount,
781         "Payment, thanks - $user", 'Pay');
782
783     UpdateStats( $user, 'payment', $amount, '', '', '', $borrowernumber, $accountno );
784
785     return;
786 }
787
788
789
790 END { }    # module clean-up code here (global destructor)
791
792 1;
793 __END__
794
795 =head1 SEE ALSO
796
797 DBI(3)
798
799 =cut
800