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