writing absolute path.
[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;
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, $data->{'itemnumber'});
318      $sth->execute($borrowernumber, $accountno, $amount, $desc, $type, $amountleft, $itemnum,$notifyid);
319   } else {
320     my $sth=$dbh->prepare("INSERT INTO  accountlines
321             (borrowernumber, accountno, date, amount, description, accounttype, amountoutstanding,notify_id)
322             VALUES (?, ?, now(), ?, ?, ?, ?,?)");
323     $sth->execute($borrowernumber, $accountno, $amount, $desc, $type, $amountleft,$notifyid);
324   }
325 }
326
327 =head2 fixcredit
328
329  $amountleft = &fixcredit($borrowernumber, $data, $barcode, $type, $user);
330
331  This function is only used internally, not exported.
332  FIXME - Figure out what this function does, and write it down.
333
334 =cut
335
336 sub fixcredit{
337   #here we update both the accountoffsets and the account lines
338   my ($borrowernumber,$data,$barcode,$type,$user)=@_;
339   my $dbh = C4::Context->dbh;
340   my $newamtos = 0;
341   my $accdata = "";
342   my $amountleft = $data;
343   if ($barcode ne ''){
344     my $item=GetBiblioFromItemNumber('',$barcode);
345     my $nextaccntno = getnextacctno($borrowernumber);
346     my $query="SELECT * FROM accountlines WHERE (borrowernumber=?
347     AND itemnumber=? AND amountoutstanding > 0)";
348     if ($type eq 'CL'){
349       $query.=" AND (accounttype = 'L' OR accounttype = 'Rep')";
350     } elsif ($type eq 'CF'){
351       $query.=" AND (accounttype = 'F' OR accounttype = 'FU' OR
352       accounttype='Res' OR accounttype='Rent')";
353     } elsif ($type eq 'CB'){
354       $query.=" and accounttype='A'";
355     }
356 #    print $query;
357     my $sth=$dbh->prepare($query);
358     $sth->execute($borrowernumber,$item->{'itemnumber'});
359     $accdata=$sth->fetchrow_hashref;
360     $sth->finish;
361     if ($accdata->{'amountoutstanding'} < $amountleft) {
362         $newamtos = 0;
363         $amountleft -= $accdata->{'amountoutstanding'};
364      }  else {
365         $newamtos = $accdata->{'amountoutstanding'} - $amountleft;
366         $amountleft = 0;
367      }
368           my $thisacct = $accdata->{accountno};
369      my $usth = $dbh->prepare("UPDATE accountlines SET amountoutstanding= ?
370      WHERE (borrowernumber = ?) AND (accountno=?)");
371      $usth->execute($newamtos,$borrowernumber,$thisacct);
372      $usth->finish;
373      $usth = $dbh->prepare("INSERT INTO accountoffsets
374      (borrowernumber, accountno, offsetaccount,  offsetamount)
375      VALUES (?,?,?,?)");
376      $usth->execute($borrowernumber,$accdata->{'accountno'},$nextaccntno,$newamtos);
377      $usth->finish;
378   }
379   # begin transaction
380   my $nextaccntno = getnextacctno($borrowernumber);
381   # get lines with outstanding amounts to offset
382   my $sth = $dbh->prepare("SELECT * FROM accountlines
383   WHERE (borrowernumber = ?) AND (amountoutstanding >0)
384   ORDER BY date");
385   $sth->execute($borrowernumber);
386 #  print $query;
387   # offset transactions
388   while (($accdata=$sth->fetchrow_hashref) and ($amountleft>0)){
389      if ($accdata->{'amountoutstanding'} < $amountleft) {
390         $newamtos = 0;
391         $amountleft -= $accdata->{'amountoutstanding'};
392      }  else {
393         $newamtos = $accdata->{'amountoutstanding'} - $amountleft;
394         $amountleft = 0;
395      }
396      my $thisacct = $accdata->{accountno};
397      my $usth = $dbh->prepare("UPDATE accountlines SET amountoutstanding= ?
398      WHERE (borrowernumber = ?) AND (accountno=?)");
399      $usth->execute($newamtos,$borrowernumber,$thisacct);
400      $usth->finish;
401      $usth = $dbh->prepare("INSERT INTO accountoffsets
402      (borrowernumber, accountno, offsetaccount,  offsetamount)
403      VALUE (?,?,?,?)");
404      $usth->execute($borrowernumber,$accdata->{'accountno'},$nextaccntno,$newamtos);
405      $usth->finish;
406   }
407   $sth->finish;
408   $type="Credit ".$type;
409   UpdateStats($user,$type,$data,$user,'','',$borrowernumber);
410   $amountleft*=-1;
411   return($amountleft);
412
413 }
414
415 =head2 refund
416
417 # FIXME - Figure out what this function does, and write it down.
418
419 =cut 
420
421 sub refund{
422   #here we update both the accountoffsets and the account lines
423   my ($borrowernumber,$data)=@_;
424   my $dbh = C4::Context->dbh;
425   my $newamtos = 0;
426   my $accdata = "";
427   my $amountleft = $data *-1;
428
429   # begin transaction
430   my $nextaccntno = getnextacctno($borrowernumber);
431   # get lines with outstanding amounts to offset
432   my $sth = $dbh->prepare("SELECT * FROM accountlines
433   WHERE (borrowernumber = ?) AND (amountoutstanding<0)
434   ORDER BY date");
435   $sth->execute($borrowernumber);
436 #  print $amountleft;
437   # offset transactions
438   while (($accdata=$sth->fetchrow_hashref) and ($amountleft<0)){
439      if ($accdata->{'amountoutstanding'} > $amountleft) {
440         $newamtos = 0;
441         $amountleft -= $accdata->{'amountoutstanding'};
442      }  else {
443         $newamtos = $accdata->{'amountoutstanding'} - $amountleft;
444         $amountleft = 0;
445      }
446 #     print $amountleft;
447      my $thisacct = $accdata->{accountno};
448      my $usth = $dbh->prepare("UPDATE accountlines SET amountoutstanding= ?
449      WHERE (borrowernumber = ?) AND (accountno=?)");
450      $usth->execute($newamtos,$borrowernumber,$thisacct);
451      $usth->finish;
452      $usth = $dbh->prepare("INSERT INTO accountoffsets
453      (borrowernumber, accountno, offsetaccount,  offsetamount)
454      VALUES (?,?,?,?)");
455      $usth->execute($borrowernumber,$accdata->{'accountno'},$nextaccntno,$newamtos);
456      $usth->finish;
457   }
458   $sth->finish;
459   return($amountleft);
460 }
461
462
463 END { }       # module clean-up code here (global destructor)
464
465 1;
466 __END__
467
468
469 =head1 SEE ALSO
470
471 DBI(3)
472
473 =cut
474