Added magic RCS comment.
[koha.git] / C4 / Circulation / Issues.pm
1 package C4::Circulation::Issues;
2
3 # $Id$
4
5 #package to deal with Issues
6 #written 3/11/99 by chris@katipo.co.nz
7
8 # Copyright 2000-2002 Katipo Communications
9 #
10 # This file is part of Koha.
11 #
12 # Koha is free software; you can redistribute it and/or modify it under the
13 # terms of the GNU General Public License as published by the Free Software
14 # Foundation; either version 2 of the License, or (at your option) any later
15 # version.
16 #
17 # Koha is distributed in the hope that it will be useful, but WITHOUT ANY
18 # WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
19 # A PARTICULAR PURPOSE.  See the GNU General Public License for more details.
20 #
21 # You should have received a copy of the GNU General Public License along with
22 # Koha; if not, write to the Free Software Foundation, Inc., 59 Temple Place,
23 # Suite 330, Boston, MA  02111-1307 USA
24
25 # FIXME - AFAICT the only function here that's still being used is
26 # &formatitem, and I'm not convinced that it's really being used.
27
28 use strict;
29 require Exporter;
30 use DBI;
31 use C4::Context;
32 use C4::Accounts;
33 use C4::InterfaceCDK;
34 use C4::Circulation::Main;
35         # FIXME - C4::Circulation::Main and C4::Circulation::Issues
36         # use each other, so functions get redefined.
37 use C4::Circulation::Borrower;
38         # FIXME - C4::Circulation::Issues and C4::Circulation::Borrower
39         # use each other, so functions get redefined.
40 use C4::Scan;
41 use C4::Stats;
42 use C4::Print;
43 use C4::Format;
44 use C4::Input;
45 use vars qw($VERSION @ISA @EXPORT);
46   
47 # set the version for version checking
48 $VERSION = 0.01;
49
50 =head1 NAME
51
52 C4::Circulation::Issues - Miscellaneous functions related to Koha issues
53
54 =head1 SYNOPSIS
55
56   use C4::Circulation::Issues;
57
58 =head1 DESCRIPTION
59
60 This module provides a function for pretty-printing an item being
61 issued.
62
63 =head1 FUNCTIONS
64
65 =over 2
66
67 =cut
68 #'
69
70 @ISA = qw(Exporter);
71 @EXPORT = qw(&Issue &formatitem);
72
73 # FIXME - This is only used in C4::Circmain and C4::Circulation, both
74 # of which look obsolete. Is this function obsolete as well?
75 # If not, this needs a POD.
76 sub Issue  {
77    my ($env) = @_;
78    my $dbh = C4::Context->dbh;
79    #clear help
80    helptext('');
81    #clearscreen();
82    my $done;
83    my ($items,$items2,$amountdue);
84    my $itemsdet;
85    $env->{'sysarea'} = "Issues";
86    $done = "Issues";
87    while ($done eq "Issues") {
88      my ($bornum,$issuesallowed,$borrower,$reason,$amountdue) = &findborrower($env,$dbh);      
89      #C4::Circulation::Borrowers
90      $env->{'loanlength'}="";
91      if ($reason ne "") {
92        $done = $reason;
93      } elsif ($env->{'IssuesAllowed'} eq '0') {
94        error_msg($env,"No Issues Allowed =$env->{'IssuesAllowed'}");
95      } else {
96        $env->{'bornum'} = $bornum;
97        $env->{'bcard'}  = $borrower->{'cardnumber'};
98        #deal with alternative loans
99        #now check items 
100        ($items,$items2)=
101        C4::Circulation::Main::pastitems($env,$bornum,$dbh); #from Circulation.pm
102        $done = "No";
103        my $it2p=0;
104        while ($done eq 'No'){
105          ($done,$items2,$it2p,$amountdue,$itemsdet) =
106             &processitems($env,$bornum,$borrower,$items,
107             $items2,$it2p,$amountdue,$itemsdet);
108        }
109      #&endint($env);
110      }
111    }   
112    Cdk::refreshCdkScreen();
113    return ($done);
114 }    
115
116 # FIXME - Not exported, but called by "telnet/borrwraper.pl".
117 # Presumably this function is obsolete.
118 sub processitems {
119   #process a users items
120    my ($env,$bornum,$borrower,$items,$items2,$it2p,$amountdue,$itemsdet,$odues)=@_;
121    my $dbh = C4::Context->dbh;
122    $env->{'newborrower'} = "";
123    my ($itemnum,$reason) = 
124      issuewindow($env,'Issues',$dbh,$items,$items2,$borrower,fmtdec($env,$amountdue,"32"));
125    if ($itemnum eq ""){
126      $reason = "Finished user";
127    } else {
128      my ($item,$charge,$datedue) = &issueitem($env,$dbh,$itemnum,$bornum,$items);
129      if ($datedue ne "") {
130        my $line = formatitem($env,$item,$datedue,$charge);
131        unshift @$items2,$line;
132        #$items2->[$it2p] = $line;
133        $item->{'date_due'} = $datedue;
134        $item->{'charge'} = $charge;
135        $itemsdet->[$it2p] = $item;
136        $it2p++;
137        $amountdue += $charge;
138      }
139    }   
140    #check to see if more books to process for this user
141    my @done;
142    if ($env->{'newborrower'} ne "") {$reason = "Finished user";} 
143    if ($reason eq 'Finished user'){
144      if (@$items2[0] ne "") {
145        remoteprint($env,$itemsdet,$borrower);
146        if ($amountdue > 0) {
147          &reconcileaccount($env,$dbh,$borrower->{'borrowernumber'},$amountdue);
148        }
149      }  
150      @done = ("Issues");
151    } elsif ($reason eq "Print"){
152      remoteprint($env,$itemsdet,$borrower);
153      @done = ("No",$items2,$it2p);
154    } else {
155      if ($reason ne 'Finished issues'){
156        #return No to let them know that we wish to 
157        # process more Items for borrower
158        @done = ("No",$items2,$it2p,$amountdue,$itemsdet);
159      } else  {
160        @done = ("Circ");
161      }
162    }
163    #debug_msg($env, "return from issues $done[0]"); 
164    return @done;
165 }
166
167 =item formatitem
168
169   $line = &formatitem($env, $item, $datedue, $charge);
170
171 Pretty-prints a description of an item being issued, and returns the
172 pretty-printed string.
173
174 C<$env> is effectively ignored.
175
176 C<$item> is a reference-to-hash whose keys are fields from the items
177 table in the Koha database.
178
179 C<$datedue> is a string that will be prepended to the output.
180
181 C<$charge> is a number that will be appended to the output.
182
183 The return value C<$line> is a string of the form
184
185 I<$datedue $barcode $title: $author $type$dewey$subclass $charge>
186
187 where those values that are not passed in as arguments are obtained
188 from C<$item>.
189
190 =cut
191 #'
192 sub formatitem {
193    my ($env,$item,$datedue,$charge) = @_;
194    my $line = $datedue." ".$item->{'barcode'}." ".$item->{'title'}.": ".$item->{'author'};
195         # FIXME - Use string interpolation or sprintf()
196    my $iclass =  $item->{'itemtype'};
197    # FIXME - The Dewey code is a string, not a number.
198    if ($item->{'dewey'} > 0) {
199      my $dewey = $item->{'dewey'};
200      $dewey =~ s/0*$//;
201      $dewey =~ s/\.$//;
202      $iclass = $iclass.$dewey.$item->{'subclass'};      # FIXME - .=
203    };
204    my $llen = 65 - length($iclass);
205    my $line = fmtstr($env,$line,"L".$llen);
206                 # FIXME - Use sprintf() instead of &fmtstr.
207    my $line = $line." $iclass ";                # FIXME - .=
208    my $line = $line.fmtdec($env,$charge,"22");  # FIXME - .=
209    return $line;
210 }   
211
212 # Only used internally
213 # FIXME - Only used by &processitems, which appears to be obsolete.
214 sub issueitem{
215    my ($env,$dbh,$itemnum,$bornum,$items)=@_;
216    $itemnum=uc $itemnum;
217    my $canissue = 1;
218    ##  my ($itemnum,$reason)=&scanbook();
219    my $query="Select * from items,biblio,biblioitems where (barcode='$itemnum') and
220       (items.biblionumber=biblio.biblionumber) and
221       (items.biblioitemnumber=biblioitems.biblioitemnumber) ";
222    my $item;
223    my $charge;
224    my $datedue = $env->{'loanlength'};
225    my $sth=$dbh->prepare($query);  
226    $sth->execute;
227    if ($item=$sth->fetchrow_hashref) {
228      $sth->finish;
229      #check if item is restricted
230      if ($item->{'notforloan'} == 1) {
231        error_msg($env,"Item Not for Loan");
232        $canissue = 0;
233      } elsif ($item->{'wthdrawn'} == 1) {
234        error_msg($env,"Item Withdrawn");
235        $canissue = 0;
236 #     } elsif ($item->{'itemlost'} == 1) {
237 #       error_msg($env,"Item Lost");      
238 #       $canissue = 0;
239      } elsif ($item->{'restricted'} == 1 ){
240        error_msg($env,"Restricted Item");
241        #check borrowers status to take out restricted items
242        # if borrower allowed {
243        #  $canissue = 1
244        # } else {
245        $canissue = 0;
246        # }
247      } elsif ($item->{'itemtype'} eq 'REF'){
248        error_msg($env,"Item Not for Loan");
249        $canissue=0;
250      }
251      #check if item is on issue already
252      if ($canissue == 1) {
253        my ($currbor,$issuestat,$newdate) = 
254          &C4::Circulation::Main::previousissue($env,$item->{'itemnumber'},$dbh,$bornum);
255        if ($issuestat eq "N") { 
256          $canissue = 0;
257        } elsif ($issuestat eq "R") {
258          $canissue = -1;
259          $datedue = $newdate;
260          $charge = calc_charges($env,$dbh,$item->{'itemnumber'},$bornum);
261          if ($charge > 0) {
262            createcharge($env,$dbh,$item->{'itemnumber'},$bornum,$charge);
263          }
264          &UpdateStats($env,$env->{'branchcode'},'renew',$charge,'',$item->{'itemnumber'},$item->{'itemtype'});
265        }  
266      } 
267      if ($canissue == 1) {
268        #check reserve
269        my ($resbor,$resrec) =  &C4::Circulation::Main::checkreserve($env,$dbh,$item->{'itemnumber'});    
270        #debug_msg($env,$resbor);
271        if ($resbor eq $bornum) { 
272          my $rquery = "update reserves 
273            set found = 'F'
274            where reservedate = '$resrec->{'reservedate'}'
275            and borrowernumber = '$resrec->{'borrowernumber'}'
276            and biblionumber = '$resrec->{'biblionumber'}'";
277          my $rsth = $dbh->prepare($rquery);
278          $rsth->execute;
279          $rsth->finish;
280        } elsif ($resbor ne "") {
281          my $bquery = "select * from borrowers 
282             where borrowernumber = '$resbor'";
283          my $btsh = $dbh->prepare($bquery);
284          $btsh->execute;
285          my $resborrower = $btsh->fetchrow_hashref;
286          my $msgtxt = chr(7)."Res for $resborrower->{'cardnumber'},";
287          $msgtxt = $msgtxt." $resborrower->{'initials'} $resborrower->{'surname'}";
288                 # FIXME - .=
289          my $ans = msg_ny($env,$msgtxt,"Allow issue?");
290          if ($ans eq "N") {
291             # print a docket;
292             printreserve($env,$resrec,$resborrower,$item);
293             $canissue = 0;
294          } else {
295            my $ans = msg_ny($env,"Cancel reserve?");
296            if ($ans eq "Y") {
297              my $rquery = "update reserves 
298                set found = 'F'
299                where reservedate = '$resrec->{'reservedate'}'
300                and borrowernumber = '$resrec->{'borrowernumber'}'
301                and biblionumber = '$resrec->{'biblionumber'}'";
302              my $rsth = $dbh->prepare($rquery);
303              $rsth->execute;
304              $rsth->finish;
305            }
306          }
307          $btsh->finish();
308        };
309      }
310      #if charge deal with it
311         
312      if ($canissue == 1) {
313        $charge = calc_charges($env,$dbh,$item->{'itemnumber'},$bornum);
314      }
315      if ($canissue == 1) {
316        #now mark as issued
317        $datedue=&updateissues($env,$item->{'itemnumber'},$item->{'biblioitemnumber'},$dbh,$bornum);
318        #debug_msg("","date $datedue");
319        &UpdateStats($env,$env->{'branchcode'},'issue',$charge,'',$item->{'itemnumber'},$item->{'itemtype'});
320        if ($charge > 0) {
321          createcharge($env,$dbh,$item->{'itemnumber'},$bornum,$charge);
322        }          
323      } elsif ($canissue == 0) {
324        info_msg($env,"Can't issue $item->{'cardnumber'}");
325      }  
326    } else {
327      my $valid = checkdigit($env,$itemnum);
328      if ($valid ==1) {
329        if (substr($itemnum,0,1) = "V") {
330          #this is a borrower
331          $env->{'newborrower'} = $itemnum;
332        } else {   
333          error_msg($env,"$itemnum not found - rescan");
334        }
335      } else {
336        error_msg($env,"Invalid Number");
337      }  
338    }
339    $sth->finish;
340    #debug_msg($env,"date $datedue");
341    return($item,$charge,$datedue);
342 }
343
344 # FIXME - A virtually identical function appears in
345 # C4::Circulation::Circ2. Pick one and stick with it.
346 sub createcharge {
347   my ($env,$dbh,$itemno,$bornum,$charge) = @_;
348   my $nextaccntno = getnextacctno($env,$bornum,$dbh);
349   my $query = "insert into accountlines
350      (borrowernumber,itemnumber,accountno,date,amount,
351      description,accounttype,amountoutstanding)
352      values ($bornum,$itemno,$nextaccntno,now(),$charge,'Rental','Rent',$charge)";
353   my $sth = $dbh->prepare($query);
354   $sth->execute;
355   $sth->finish;
356 }
357
358
359 # Only used internally
360 sub updateissues{
361   # issue the book
362   my ($env,$itemno,$bitno,$dbh,$bornum)=@_;
363   my $loanlength=21;
364   my $query="Select *  from biblioitems,itemtypes
365   where (biblioitems.biblioitemnumber='$bitno') 
366   and (biblioitems.itemtype = itemtypes.itemtype)";
367   my $sth=$dbh->prepare($query);
368   $sth->execute;
369   if (my $data=$sth->fetchrow_hashref) {
370     $loanlength = $data->{'loanlength'}
371   }
372   $sth->finish;         
373   my $dateduef;
374   if ($env->{'loanlength'} eq "") {
375     my $ti = time;
376     my $datedue = time + ($loanlength * 86400);
377     my @datearr = localtime($datedue);
378     $dateduef = (1900+$datearr[5])."-".($datearr[4]+1)."-".$datearr[3];
379   } else {
380     $dateduef = $env->{'loanlength'};
381   }  
382   $query = "Insert into issues (borrowernumber,itemnumber, date_due,branchcode)
383   values ($bornum,$itemno,'$dateduef','$env->{'branchcode'}')";
384   my $sth=$dbh->prepare($query);
385   $sth->execute;
386   $sth->finish;
387   $query = "Select * from items where itemnumber=$itemno";
388   $sth=$dbh->prepare($query);
389   $sth->execute;
390   my $item=$sth->fetchrow_hashref;
391   $sth->finish;
392   $item->{'issues'}++;
393   $query="Update items set issues=$item->{'issues'} where itemnumber=$itemno";
394   $sth=$dbh->prepare($query);
395   $sth->execute;
396   $sth->finish;
397   #my ($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst) = localtime($datedue);
398   my @datearr = split('-',$dateduef);
399   my $dateret = join('-',$datearr[2],$datearr[1],$datearr[0]);
400 #  debug_msg($env,"query $query");
401   return($dateret);
402 }
403
404 # FIXME - This is very similar to
405 # &C4::Circulation::Renewals2::calc_charges and
406 # &C4::Circulation::Circ2::calc_charges.
407 # Pick one and stick with it.
408
409 # Only used internally
410 sub calc_charges {
411   # calculate charges due
412   my ($env, $dbh, $itemno, $bornum)=@_;
413   my $charge=0;
414   my $item_type;
415   my $q1 = "select itemtypes.itemtype,rentalcharge from items,biblioitems,itemtypes
416     where (items.itemnumber ='$itemno')
417     and (biblioitems.biblioitemnumber = items.biblioitemnumber)
418     and (biblioitems.itemtype = itemtypes.itemtype)";
419   my $sth1= $dbh->prepare($q1);
420   $sth1->execute;
421   if (my $data1=$sth1->fetchrow_hashref) {
422      $item_type = $data1->{'itemtype'};
423      $charge = $data1->{'rentalcharge'};
424      my $q2 = "select rentaldiscount from borrowers,categoryitem 
425         where (borrowers.borrowernumber = '$bornum') 
426         and (borrowers.categorycode = categoryitem.categorycode)
427         and (categoryitem.itemtype = '$item_type')";
428      my $sth2=$dbh->prepare($q2);
429      $sth2->execute;
430      if (my $data2=$sth2->fetchrow_hashref) {
431         my $discount = $data2->{'rentaldiscount'};
432         $charge = ($charge *(100 - $discount)) / 100;
433      }
434      $sth2->{'finish'}; # FIXME - Was this supposed to be $sth2->finish ?
435   }   
436   $sth1->finish;
437   return ($charge);
438 }
439
440 1;
441 __END__
442
443 =back
444
445 =head1 AUTHOR
446
447 Koha Developement team <info@koha.org>
448
449 =cut