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