adding confirmation to basket closing
[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 with
17 # Koha; if not, write to the Free Software Foundation, Inc., 59 Temple Place,
18 # Suite 330, Boston, MA  02111-1307 USA
19
20 # $Id$
21
22 use strict;
23 require Exporter;
24 use C4::Context;
25 use C4::Stats;
26 use C4::Members;
27 #use C4::Circulation;
28 use vars qw($VERSION @ISA @EXPORT);
29
30 # set the version for version checking
31 $VERSION = do { my @v = '$Revision$' =~ /\d+/g; 
32 shift(@v) . "." . join("_", map {sprintf "%03d", $_ } @v); };
33
34 =head1 NAME
35
36 C4::Accounts - Functions for dealing with Koha accounts
37
38 =head1 SYNOPSIS
39
40 use C4::Accounts;
41
42 =head1 DESCRIPTION
43
44 The functions in this module deal with the monetary aspect of Koha,
45 including looking up and modifying the amount of money owed by a
46 patron.
47
48 =head1 FUNCTIONS
49
50 =cut
51
52 @ISA = qw(Exporter);
53 @EXPORT = qw(&recordpayment &fixaccounts &makepayment &manualinvoice
54 &getnextacctno &reconcileaccount);
55
56
57 =head2 recordpayment
58
59   &recordpayment($borrowernumber, $payment);
60
61 Record payment by a patron. C<$borrowernumber> is the patron's
62 borrower number. C<$payment> is a floating-point number, giving the
63 amount that was paid. 
64
65 Amounts owed are paid off oldest first. That is, if the patron has a
66 $1 fine from Feb. 1, another $1 fine from Mar. 1, and makes a payment
67 of $1.50, then the oldest fine will be paid off in full, and $0.50
68 will be credited to the next one.
69
70 =cut
71
72 #'
73 sub recordpayment{
74   #here we update both the accountoffsets and the account lines
75   my ($borrowernumber,$data)=@_;
76   my $dbh = C4::Context->dbh;
77   my $newamtos = 0;
78   my $accdata = "";
79   my $branch=C4::Context->userenv->{'branch'};
80   my $amountleft = $data;
81   # begin transaction
82   my $nextaccntno = getnextacctno($borrowernumber);
83   # get lines with outstanding amounts to offset
84   my $sth = $dbh->prepare("SELECT * FROM accountlines
85   WHERE (borrowernumber = ?) AND (amountoutstanding<>0)
86   ORDER BY date");
87   $sth->execute($borrowernumber);
88   # offset transactions
89   while (($accdata=$sth->fetchrow_hashref) and ($amountleft>0)){
90      if ($accdata->{'amountoutstanding'} < $amountleft) {
91         $newamtos = 0;
92         $amountleft -= $accdata->{'amountoutstanding'};
93      }  else {
94         $newamtos = $accdata->{'amountoutstanding'} - $amountleft;
95         $amountleft = 0;
96      }
97      my $thisacct = $accdata->{accountno};
98      my $usth = $dbh->prepare("UPDATE accountlines SET amountoutstanding= ?
99      WHERE (borrowernumber = ?) AND (accountno=?)");
100      $usth->execute($newamtos,$borrowernumber,$thisacct);
101      $usth->finish;
102      $usth = $dbh->prepare("INSERT INTO accountoffsets
103      (borrowernumber, accountno, offsetaccount,  offsetamount)
104      VALUES (?,?,?,?)");
105      $usth->execute($borrowernumber,$accdata->{'accountno'},$nextaccntno,$newamtos);
106      $usth->finish;
107   }
108   # create new line
109   my $usth = $dbh->prepare("INSERT INTO accountlines
110   (borrowernumber, accountno,date,amount,description,accounttype,amountoutstanding)
111   VALUES (?,?,now(),?,'Payment,thanks','Pay',?)");
112   $usth->execute($borrowernumber,$nextaccntno,0-$data,0-$amountleft);
113   $usth->finish;
114   UpdateStats($branch,'payment',$data,'','','',$borrowernumber);
115   $sth->finish;
116 }
117
118 =head2 makepayment
119
120   &makepayment($borrowernumber, $acctnumber, $amount, $branchcode);
121
122 Records the fact that a patron has paid off the entire amount he or
123 she owes.
124
125 C<$borrowernumber> is the patron's borrower number. C<$acctnumber> is
126 the account that was credited. C<$amount> is the amount paid (this is
127 only used to record the payment. It is assumed to be equal to the
128 amount owed). C<$branchcode> is the code of the branch where payment
129 was made.
130
131 =cut
132
133 #'
134 # FIXME - I'm not at all sure about the above, because I don't
135 # understand what the acct* tables in the Koha database are for.
136 sub makepayment{
137   #here we update both the accountoffsets and the account lines
138   #updated to check, if they are paying off a lost item, we return the item
139   # from their card, and put a note on the item record
140   my ($borrowernumber,$accountno,$amount,$user,$branch)=@_;
141   my $dbh = C4::Context->dbh;
142   # begin transaction
143   my $nextaccntno = getnextacctno($borrowernumber);
144   my $newamtos=0;
145   my $sth=$dbh->prepare("SELECT * FROM accountlines WHERE  borrowernumber=? AND accountno=?");
146   $sth->execute($borrowernumber,$accountno);
147   my $data=$sth->fetchrow_hashref;
148   $sth->finish;
149
150   $dbh->do("UPDATE  accountlines
151         SET     amountoutstanding = 0
152         WHERE   borrowernumber = $borrowernumber
153           AND   accountno = $accountno
154         ");
155
156 #  print $updquery;
157   $dbh->do("
158         INSERT INTO     accountoffsets
159                         (borrowernumber, accountno, offsetaccount,
160                          offsetamount)
161         VALUES          ($borrowernumber, $accountno, $nextaccntno, $newamtos)
162         ");
163
164   # create new line
165   my $payment=0-$amount;
166   $dbh->do("
167         INSERT INTO     accountlines
168                         (borrowernumber, accountno, date, amount,
169                          description, accounttype, amountoutstanding)
170         VALUES          ($borrowernumber, $nextaccntno, now(), $payment,
171                         'Payment,thanks - $user', 'Pay', 0)
172         ");
173
174   # FIXME - The second argument to &UpdateStats is supposed to be the
175   # branch code.
176   # UpdateStats is now being passed $accountno too. MTJ
177   UpdateStats($user,'payment',$amount,'','','',$borrowernumber,$accountno);
178   $sth->finish;
179   #check to see what accounttype
180   if ($data->{'accounttype'} eq 'Rep' || $data->{'accounttype'} eq 'L'){
181     returnlost($borrowernumber,$data->{'itemnumber'});
182   }
183 }
184
185 =head2 getnextacctno
186
187   $nextacct = &getnextacctno($borrowernumber);
188
189 Returns the next unused account number for the patron with the given
190 borrower number.
191
192 =cut
193
194 #'
195 # FIXME - Okay, so what does the above actually _mean_?
196 sub getnextacctno {
197   my ($borrowernumber)=@_;
198   my $nextaccntno = 1;
199   my $dbh = C4::Context->dbh;
200   my $sth = $dbh->prepare("SELECT * FROM accountlines
201                                 WHERE (borrowernumber = ?)
202                                 ORDER BY accountno DESC");
203   $sth->execute($borrowernumber);
204   if (my $accdata=$sth->fetchrow_hashref){
205     $nextaccntno = $accdata->{'accountno'} + 1;
206   }
207   $sth->finish;
208   return($nextaccntno);
209 }
210
211 =head2 fixaccounts
212
213   &fixaccounts($borrowernumber, $accountnumber, $amount);
214
215 =cut
216
217 #'
218 # FIXME - I don't understand what this function does.
219 sub fixaccounts {
220   my ($borrowernumber,$accountno,$amount)=@_;
221   my $dbh = C4::Context->dbh;
222   my $sth=$dbh->prepare("SELECT * FROM accountlines WHERE borrowernumber=?
223      AND accountno=?");
224   $sth->execute($borrowernumber,$accountno);
225   my $data=$sth->fetchrow_hashref;
226         # FIXME - Error-checking
227   my $diff=$amount-$data->{'amount'};
228   my $outstanding=$data->{'amountoutstanding'}+$diff;
229   $sth->finish;
230
231   $dbh->do(<<EOT);
232         UPDATE  accountlines
233         SET     amount = '$amount',
234                 amountoutstanding = '$outstanding'
235         WHERE   borrowernumber = $borrowernumber
236           AND   accountno = $accountno
237 EOT
238  }
239
240 # FIXME - Never used, but not exported, either.
241 sub returnlost{
242   my ($borrowernumber,$itemnum)=@_;
243   my $dbh = C4::Context->dbh;
244   my $borrower=GetMember($borrowernumber,'borrowernumber');
245   my $sth=$dbh->prepare("UPDATE issues SET returndate=now() WHERE
246   borrowernumber=? AND itemnumber=? AND returndate IS NULL");
247   $sth->execute($borrowernumber,$itemnum);
248   $sth->finish;
249   my @datearr = localtime(time);
250   my $date = (1900+$datearr[5])."-".($datearr[4]+1)."-".$datearr[3];
251   my $bor="$borrower->{'firstname'} $borrower->{'surname'} $borrower->{'cardnumber'}";
252   $sth=$dbh->prepare("UPDATE items SET paidfor=? WHERE itemnumber=?");
253   $sth->execute("Paid for by $bor $date",$itemnum);
254   $sth->finish;
255 }
256
257 =head2 manualinvoice
258
259   &manualinvoice($borrowernumber, $itemnumber, $description, $type,
260                  $amount, $user);
261
262 C<$borrowernumber> is the patron's borrower number.
263 C<$description> is a description of the transaction.
264 C<$type> may be one of C<CS>, C<CB>, C<CW>, C<CF>, C<CL>, C<N>, C<L>,
265 or C<REF>.
266 C<$itemnumber> is the item involved, if pertinent; otherwise, it
267 should be the empty string.
268
269 =cut
270
271 #'
272 # FIXME - Okay, so what does this function do, really?
273 sub manualinvoice{
274   my ($borrowernumber,$itemnum,$desc,$type,$amount,$user)=@_;
275   my $dbh = C4::Context->dbh;
276   my $notifyid=0;
277   my $insert;
278   $itemnum=~ s/ //g;
279   my $accountno=getnextacctno($borrowernumber);
280   my $amountleft=$amount;
281
282   if ($type eq 'CS' || $type eq 'CB' || $type eq 'CW'
283   || $type eq 'CF' || $type eq 'CL'){
284     my $amount2=$amount*-1;     # FIXME - $amount2 = -$amount
285     $amountleft=fixcredit($borrowernumber,$amount2,$itemnum,$type,$user);
286   }
287   if ($type eq 'N'){
288     $desc.="New Card";
289   }
290   if ($type eq 'F'){
291     $desc.="Fine";
292   }
293   if ($type eq 'A'){
294     $desc.="Account Management fee";
295   }
296   if ($type eq 'M'){
297     $desc.="Sundry";
298   }     
299         
300   if ($type eq 'L' && $desc eq ''){
301     
302     $desc="Lost Item";
303   }
304   if ($type eq 'REF'){
305     $desc.="Cash Refund";    
306     $amountleft=refund('',$borrowernumber,$amount);
307   }
308   if(($type eq 'L') or ($type eq 'F') or ($type eq 'A') or ($type eq 'N') or ($type eq 'M') ){
309   $notifyid=1;  
310   }
311     
312   if ($itemnum ne ''){
313     $desc.=" ".$itemnum;
314     my $sth=$dbh->prepare("INSERT INTO  accountlines
315                         (borrowernumber, accountno, date, amount, description, accounttype, amountoutstanding, itemnumber,notify_id)
316         VALUES (?, ?, now(), ?,?, ?,?,?,?)");
317      $sth->execute($borrowernumber, $accountno, $amount, $desc, $type, $amountleft, $itemnum,$notifyid) || return $sth->errstr;
318   } else {
319     my $sth=$dbh->prepare("INSERT INTO  accountlines
320             (borrowernumber, accountno, date, amount, description, accounttype, amountoutstanding,notify_id)
321             VALUES (?, ?, now(), ?, ?, ?, ?,?)");
322     $sth->execute($borrowernumber, $accountno, $amount, $desc, $type, $amountleft,$notifyid);
323   }
324 }
325
326 =head2 fixcredit
327
328  $amountleft = &fixcredit($borrowernumber, $data, $barcode, $type, $user);
329
330  This function is only used internally, not exported.
331  FIXME - Figure out what this function does, and write it down.
332
333 =cut
334
335 sub fixcredit{
336   #here we update both the accountoffsets and the account lines
337   my ($borrowernumber,$data,$barcode,$type,$user)=@_;
338   my $dbh = C4::Context->dbh;
339   my $newamtos = 0;
340   my $accdata = "";
341   my $amountleft = $data;
342   if ($barcode ne ''){
343     my $item=GetBiblioFromItemNumber('',$barcode);
344     my $nextaccntno = getnextacctno($borrowernumber);
345     my $query="SELECT * FROM accountlines WHERE (borrowernumber=?
346     AND itemnumber=? AND amountoutstanding > 0)";
347     if ($type eq 'CL'){
348       $query.=" AND (accounttype = 'L' OR accounttype = 'Rep')";
349     } elsif ($type eq 'CF'){
350       $query.=" AND (accounttype = 'F' OR accounttype = 'FU' OR
351       accounttype='Res' OR accounttype='Rent')";
352     } elsif ($type eq 'CB'){
353       $query.=" and accounttype='A'";
354     }
355 #    print $query;
356     my $sth=$dbh->prepare($query);
357     $sth->execute($borrowernumber,$item->{'itemnumber'});
358     $accdata=$sth->fetchrow_hashref;
359     $sth->finish;
360     if ($accdata->{'amountoutstanding'} < $amountleft) {
361         $newamtos = 0;
362         $amountleft -= $accdata->{'amountoutstanding'};
363      }  else {
364         $newamtos = $accdata->{'amountoutstanding'} - $amountleft;
365         $amountleft = 0;
366      }
367           my $thisacct = $accdata->{accountno};
368      my $usth = $dbh->prepare("UPDATE accountlines SET amountoutstanding= ?
369      WHERE (borrowernumber = ?) AND (accountno=?)");
370      $usth->execute($newamtos,$borrowernumber,$thisacct);
371      $usth->finish;
372      $usth = $dbh->prepare("INSERT INTO accountoffsets
373      (borrowernumber, accountno, offsetaccount,  offsetamount)
374      VALUES (?,?,?,?)");
375      $usth->execute($borrowernumber,$accdata->{'accountno'},$nextaccntno,$newamtos);
376      $usth->finish;
377   }
378   # begin transaction
379   my $nextaccntno = getnextacctno($borrowernumber);
380   # get lines with outstanding amounts to offset
381   my $sth = $dbh->prepare("SELECT * FROM accountlines
382   WHERE (borrowernumber = ?) AND (amountoutstanding >0)
383   ORDER BY date");
384   $sth->execute($borrowernumber);
385 #  print $query;
386   # offset transactions
387   while (($accdata=$sth->fetchrow_hashref) and ($amountleft>0)){
388      if ($accdata->{'amountoutstanding'} < $amountleft) {
389         $newamtos = 0;
390         $amountleft -= $accdata->{'amountoutstanding'};
391      }  else {
392         $newamtos = $accdata->{'amountoutstanding'} - $amountleft;
393         $amountleft = 0;
394      }
395      my $thisacct = $accdata->{accountno};
396      my $usth = $dbh->prepare("UPDATE accountlines SET amountoutstanding= ?
397      WHERE (borrowernumber = ?) AND (accountno=?)");
398      $usth->execute($newamtos,$borrowernumber,$thisacct);
399      $usth->finish;
400      $usth = $dbh->prepare("INSERT INTO accountoffsets
401      (borrowernumber, accountno, offsetaccount,  offsetamount)
402      VALUE (?,?,?,?)");
403      $usth->execute($borrowernumber,$accdata->{'accountno'},$nextaccntno,$newamtos);
404      $usth->finish;
405   }
406   $sth->finish;
407   $type="Credit ".$type;
408   UpdateStats($user,$type,$data,$user,'','',$borrowernumber);
409   $amountleft*=-1;
410   return($amountleft);
411
412 }
413
414 =head2 refund
415
416 # FIXME - Figure out what this function does, and write it down.
417
418 =cut 
419
420 sub refund{
421   #here we update both the accountoffsets and the account lines
422   my ($borrowernumber,$data)=@_;
423   my $dbh = C4::Context->dbh;
424   my $newamtos = 0;
425   my $accdata = "";
426   my $amountleft = $data *-1;
427
428   # begin transaction
429   my $nextaccntno = getnextacctno($borrowernumber);
430   # get lines with outstanding amounts to offset
431   my $sth = $dbh->prepare("SELECT * FROM accountlines
432   WHERE (borrowernumber = ?) AND (amountoutstanding<0)
433   ORDER BY date");
434   $sth->execute($borrowernumber);
435 #  print $amountleft;
436   # offset transactions
437   while (($accdata=$sth->fetchrow_hashref) and ($amountleft<0)){
438      if ($accdata->{'amountoutstanding'} > $amountleft) {
439         $newamtos = 0;
440         $amountleft -= $accdata->{'amountoutstanding'};
441      }  else {
442         $newamtos = $accdata->{'amountoutstanding'} - $amountleft;
443         $amountleft = 0;
444      }
445 #     print $amountleft;
446      my $thisacct = $accdata->{accountno};
447      my $usth = $dbh->prepare("UPDATE accountlines SET amountoutstanding= ?
448      WHERE (borrowernumber = ?) AND (accountno=?)");
449      $usth->execute($newamtos,$borrowernumber,$thisacct);
450      $usth->finish;
451      $usth = $dbh->prepare("INSERT INTO accountoffsets
452      (borrowernumber, accountno, offsetaccount,  offsetamount)
453      VALUES (?,?,?,?)");
454      $usth->execute($borrowernumber,$accdata->{'accountno'},$nextaccntno,$newamtos);
455      $usth->finish;
456   }
457   $sth->finish;
458   return($amountleft);
459 }
460
461
462 END { }       # module clean-up code here (global destructor)
463
464 1;
465 __END__
466
467
468 =head1 SEE ALSO
469
470 DBI(3)
471
472 =cut
473