All necessary date functions for koha using the fast DateTime module
[koha.git] / C4 / Circulation / Circ2.pm
1 # -*- tab-width: 8 -*-
2 # Please use 8-character tabs for this file (indents are every 4 characters)
3
4 package C4::Circulation::Circ2;
5
6 # $Id$
7
8 #package to deal with circulation
9 #written 3/11/99 by olwen@katipo.co.nz
10
11
12 # Copyright 2000-2002 Katipo Communications
13 #
14 # This file is part of Koha.
15 #
16 # Koha is free software; you can redistribute it and/or modify it under the
17 # terms of the GNU General Public License as published by the Free Software
18 # Foundation; either version 2 of the License, or (at your option) any later
19 # version.
20 #
21 # Koha is distributed in the hope that it will be useful, but WITHOUT ANY
22 # WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
23 # A PARTICULAR PURPOSE.  See the GNU General Public License for more details.
24 #
25 # You should have received a copy of the GNU General Public License along with
26 # Koha; if not, write to the Free Software Foundation, Inc., 59 Temple Place,
27 # Suite 330, Boston, MA  02111-1307 USA
28
29 use strict;
30 # use warnings;
31 require Exporter;
32
33 use C4::Context;
34 use C4::Stats;
35 use C4::Reserves2;
36 use C4::Koha;
37 use C4::Accounts2;
38 use C4::Biblio;
39 use C4::Calendar::Calendar;
40 use C4::Search;
41 use C4::Members;
42 use C4::Date;
43 use vars qw($VERSION @ISA @EXPORT @EXPORT_OK %EXPORT_TAGS);
44
45 # set the version for version checking
46 $VERSION = 0.01;
47
48 =head1 NAME
49
50 C4::Circulation::Circ2 - Koha circulation module
51
52 =head1 SYNOPSIS
53
54   use C4::Circulation::Circ2;
55
56 =head1 DESCRIPTION
57
58 The functions in this module deal with circulation, issues, and
59 returns, as well as general information about the library.
60 Also deals with stocktaking.
61
62 =head1 FUNCTIONS
63
64 =over 2
65
66 =cut
67
68 @ISA = qw(Exporter);
69 @EXPORT = qw(
70         &currentissues 
71         &getissues 
72         &getiteminformation 
73         &renewstatus 
74         &renewbook
75         &canbookbeissued 
76         &issuebook 
77         &returnbook 
78         &find_reserves 
79         &transferbook 
80         &decode
81         &calc_charges 
82         &listitemsforinventory 
83         &itemseen 
84         &itemseenbarcode
85         &fixdate 
86         &itemissues 
87         &patronflags
88          &get_current_return_date_of
89                 &get_transfert_infos
90                 &checktransferts
91                 &GetReservesForBranch
92                 &GetReservesToBranch
93                 &GetTransfersFromBib
94                 &getBranchIp);
95
96 # &getbranches &getprinters &getbranch &getprinter => moved to C4::Koha.pm
97 =item itemissues
98
99   @issues = &itemissues($biblionumber, $biblio);
100
101 Looks up information about who has borrowed the bookZ<>(s) with the
102 given biblionumber.
103
104 C<$biblio> is ignored.
105
106 C<&itemissues> returns an array of references-to-hash. The keys
107 include the fields from the C<items> table in the Koha database.
108 Additional keys include:
109
110 =over 4
111
112 =item C<date_due>
113
114 If the item is currently on loan, this gives the due date.
115
116 If the item is not on loan, then this is either "Available" or
117 "Cancelled", if the item has been withdrawn.
118
119 =item C<card>
120
121 If the item is currently on loan, this gives the card number of the
122 patron who currently has the item.
123
124 =item C<timestamp0>, C<timestamp1>, C<timestamp2>
125
126 These give the timestamp for the last three times the item was
127 borrowed.
128
129 =item C<card0>, C<card1>, C<card2>
130
131 The card number of the last three patrons who borrowed this item.
132
133 =item C<borrower0>, C<borrower1>, C<borrower2>
134
135 The borrower number of the last three patrons who borrowed this item.
136
137 =back
138
139 =cut
140 #'
141 sub itemissues {
142     my ($dbh,$data, $itemnumber)=@_;
143     
144       
145     my $i     = 0;
146     my @results;
147
148
149         # Find out who currently has this item.
150         # FIXME - Wouldn't it be better to do this as a left join of
151         # some sort? Currently, this code assumes that if
152         # fetchrow_hashref() fails, then the book is on the shelf.
153         # fetchrow_hashref() can fail for any number of reasons (e.g.,
154         # database server crash), not just because no items match the
155         # search criteria.
156         my $sth2   = $dbh->prepare("select * from issues,borrowers
157 where itemnumber = ?
158 and returndate is NULL
159 and issues.borrowernumber = borrowers.borrowernumber");
160
161         $sth2->execute($itemnumber);
162         if (my $data2 = $sth2->fetchrow_hashref) {
163
164         $data->{'date_due'}=$data2->{'date_due'};
165         $data->{'datelastborrowed'} = $data2->{'issue_date'};
166             $data->{'card'}     = $data2->{'cardnumber'};
167             $data->{'borrower'}     = $data2->{'borrowernumber'};
168         } 
169
170         $sth2->finish;
171
172         # Find the last 2 people who borrowed this item.
173         $sth2 = $dbh->prepare("select * from issues, borrowers
174                                                 where itemnumber = ?
175                                                                         and issues.borrowernumber = borrowers.borrowernumber
176                                                                         and returndate is not NULL
177                                                                         order by returndate desc,timestamp desc limit 2") ;
178         $sth2->execute($itemnumber) ;
179 #        for (my $i2 = 0; $i2 < 2; $i2++) { # FIXME : error if there is less than 3 pple borrowing this item
180 my $i2=0;
181           while (my $data2  = $sth2->fetchrow_hashref) {
182                 $data->{"timestamp$i2"} = $data2->{'timestamp'};
183                 $data->{"card$i2"}      = $data2->{'cardnumber'};
184                 $data->{"borrower$i2"}  = $data2->{'borrowernumber'};
185 $data->{'datelastborrowed'} = $data2->{'issue_date'} unless $data->{'datelastborrowed'};
186         $i2++;
187             } # while
188 #       } # for
189
190         $sth2->finish;
191     return($data);
192 }
193
194
195
196 =head2 itemseen
197
198 &itemseen($dbh,$itemnum)
199 Mark item as seen. Is called when an item is issued, returned or manually marked during inventory/stocktaking
200 C<$itemnum> is the item number
201
202 =cut
203
204 sub itemseen {
205         my ($dbh,$itemnumber) = @_;
206 my $sth=$dbh->prepare("select biblionumber from items where itemnumber=?");
207         $sth->execute($itemnumber);
208 my ($biblionumber)=$sth->fetchrow; 
209 XMLmoditemonefield($dbh,$biblionumber,$itemnumber,'itemlost',"0",1);
210 # find today's date
211 my ($sec,$min,$hour,$mday,$mon,$year) = localtime();
212         $year += 1900;
213         $mon += 1;
214         my $timestamp = sprintf("%4d%02d%02d%02d%02d%02d.0",
215                 $year,$mon,$mday,$hour,$min,$sec);
216 XMLmoditemonefield($dbh,$biblionumber,$itemnumber,'datelastseen', $timestamp);  
217 }
218 sub itemseenbarcode {
219         my ($dbh,$barcode) = @_;
220 my $sth=$dbh->prepare("select biblionumber,itemnumber from items where barcode=$barcode");
221         $sth->execute();
222 my ($biblionumber,$itemnumber)=$sth->fetchrow; 
223 XMLmoditemonefield($dbh,$biblionumber,$itemnumber,'itemlost',"0",1);
224 my ($sec,$min,$hour,$mday,$mon,$year) = localtime();
225         $year += 1900;
226         $mon += 1;
227 my $timestamp = sprintf("%4d%02d%02d%02d%02d%02d.0",$year,$mon,$mday,$hour,$min,$sec);
228 XMLmoditemonefield($dbh,$biblionumber,$itemnumber,'datelastseen', $timestamp);  
229 }
230
231 sub listitemsforinventory {
232         my ($minlocation,$datelastseen,$offset,$size) = @_;
233         my $count=0;
234         my @results;
235         my @kohafields;
236         my @values;
237         my @relations;
238         my $sort;
239         my @and_or;
240         my $facets;
241         if ($datelastseen){
242                 push @kohafields, "classification","datelastseen";
243                 push @values,$minlocation,$datelastseen;
244                 push @relations,"\@attr 5=1  \@attr 6=3 \@attr 4=1 ","\@attr 2=1 ";
245                 push @and_or,"\@and";
246                 $sort="lcsort";
247                 ($count,$facets,@results)=ZEBRAsearch_kohafields(\@kohafields,\@values,\@relations,$sort,\@and_or,0,"",$offset,$size);
248         }else{
249         push @kohafields, "classification";
250                 push @values,$minlocation;
251                 push @relations,"\@attr 5=1  \@attr 6=3 \@attr 4=1 ";
252                 push @and_or,"";
253                 $sort="lcsort";
254                 ($count,$facets,@results)=ZEBRAsearch_kohafields(\@kohafields,\@values,\@relations,$sort,\@and_or,0,"",$offset,$size);
255         }
256         
257         return @results;
258 }
259
260
261
262
263 =head2 decode
264
265 =over 4
266
267 =head3 $str = &decode($chunk);
268
269 =over 4
270
271 Decodes a segment of a string emitted by a CueCat barcode scanner and
272 returns it.
273
274 =back
275
276 =back
277
278 =cut
279
280 # FIXME - At least, I'm pretty sure this is for decoding CueCat stuff.
281 sub decode {
282         my ($encoded) = @_;
283         my $seq = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789+-';
284         my @s = map { index($seq,$_); } split(//,$encoded);
285         my $l = ($#s+1) % 4;
286         if ($l)
287         {
288                 if ($l == 1)
289                 {
290                         print "Error!";
291                         return;
292                 }
293                 $l = 4-$l;
294                 $#s += $l;
295         }
296         my $r = '';
297         while ($#s >= 0)
298         {
299                 my $n = (($s[0] << 6 | $s[1]) << 6 | $s[2]) << 6 | $s[3];
300                 $r .=chr(($n >> 16) ^ 67) .
301                 chr(($n >> 8 & 255) ^ 67) .
302                 chr(($n & 255) ^ 67);
303                 @s = @s[4..$#s];
304         }
305         $r = substr($r,0,length($r)-$l);
306         return $r;
307 }
308
309 =head2 getiteminformation
310
311 =over 4
312
313 $item = &getiteminformation($env, $itemnumber, $barcode);
314
315 Looks up information about an item, given either its item number or
316 its barcode. If C<$itemnumber> is a nonzero value, it is used;
317 otherwise, C<$barcode> is used.
318
319 C<$env> is effectively ignored, but should be a reference-to-hash.
320
321 C<$item> is a reference-to-hash whose keys are fields from the biblio,
322 items, and biblioitems tables of the Koha database. It may also
323 contain the following keys:
324
325 =head3 date_due
326
327 =over 4
328
329 The due date on this item, if it has been borrowed and not returned
330 yet. The date is in YYYY-MM-DD format.
331
332 =back
333
334 =head3 notforloan
335
336 =over 4
337
338 True if the item may not be borrowed.
339
340 =back
341
342 =back
343
344 =cut
345
346
347 sub getiteminformation {
348 # returns a hash of item information together with biblio given either the itemnumber or the barcode
349         my ($env, $itemnumber, $barcode) = @_;
350         my $dbh=C4::Context->dbh;
351         my ($itemrecord)=XMLgetitem($dbh,$itemnumber,$barcode);
352         return undef unless $itemrecord; ## This is to prevent a system crash if barcode does not exist 
353          my $itemhash=XML_xml2hash_onerecord($itemrecord);      
354         my $iteminformation=XMLmarc2koha_onerecord($dbh,$itemhash,"holdings");
355 ##Now get full biblio details from MARC
356         if ($iteminformation) {
357 my ($record)=XMLgetbiblio($dbh,$iteminformation->{'biblionumber'});
358         my $recordhash=XML_xml2hash_onerecord($record);
359 my $biblio=XMLmarc2koha_onerecord($dbh,$recordhash,"biblios");
360                 foreach my $field (keys %$biblio){
361                 $iteminformation->{$field}=$biblio->{$field};
362                 } 
363         $iteminformation->{'date_due'}="" if $iteminformation->{'date_due'} eq "0000-00-00";
364         ($iteminformation->{'dewey'} == 0) && ($iteminformation->{'dewey'}=''); 
365         }
366         return($iteminformation);
367 }
368
369 =head2 transferbook
370
371 =over 4
372
373 ($dotransfer, $messages, $iteminformation) = &transferbook($newbranch, $barcode, $ignore_reserves);
374
375 Transfers an item to a new branch. If the item is currently on loan, it is automatically returned before the actual transfer.
376
377 C<$newbranch> is the code for the branch to which the item should be transferred.
378
379 C<$barcode> is the barcode of the item to be transferred.
380
381 If C<$ignore_reserves> is true, C<&transferbook> ignores reserves.
382 Otherwise, if an item is reserved, the transfer fails.
383
384 Returns three values:
385
386 =head3 $dotransfer 
387
388 is true if the transfer was successful.
389
390 =head3 $messages
391  
392 is a reference-to-hash which may have any of the following keys:
393
394 =over 4
395
396 C<BadBarcode>
397
398 There is no item in the catalog with the given barcode. The value is C<$barcode>.
399
400 C<IsPermanent>
401
402 The item's home branch is permanent. This doesn't prevent the item from being transferred, though. The value is the code of the item's home branch.
403
404 C<DestinationEqualsHolding>
405
406 The item is already at the branch to which it is being transferred. The transfer is nonetheless considered to have failed. The value should be ignored.
407
408 C<WasReturned>
409
410 The item was on loan, and C<&transferbook> automatically returned it before transferring it. The value is the borrower number of the patron who had the item.
411
412 C<ResFound>
413
414 The item was reserved. The value is a reference-to-hash whose keys are fields from the reserves table of the Koha database, and C<biblioitemnumber>. It also has the key C<ResFound>, whose value is either C<Waiting> or C<Reserved>.
415
416 C<WasTransferred>
417
418 The item was eligible to be transferred. Barring problems communicating with the database, the transfer should indeed have succeeded. The value should be ignored.
419
420 =back
421
422 =back
423
424 =back
425
426 =cut
427
428 ##This routine is reverted to origional state
429 ##This routine is used when a book physically arrives at a branch due to user returning it there
430 ## so record the fact that holdingbranch is changed.
431 sub transferbook {
432 # transfer book code....
433         my ($tbr, $barcode, $ignoreRs,$user) = @_;
434         my $messages;
435         my %env;
436         my $dbh=C4::Context->dbh;
437         my $dotransfer = 1;
438         my $branches = GetBranches();
439
440         my $iteminformation = getiteminformation(\%env, 0, $barcode);
441         # bad barcode..
442         if (not $iteminformation) {
443                 $messages->{'BadBarcode'} = $barcode;
444                 $dotransfer = 0;
445         }
446         # get branches of book...
447         my $hbr = $iteminformation->{'homebranch'};
448         my $fbr = $iteminformation->{'holdingbranch'};
449         # if is permanent...
450         if ($hbr && $branches->{$hbr}->{'PE'}) {
451                 $messages->{'IsPermanent'} = $hbr;
452         }
453         # can't transfer book if is already there....
454         # FIXME - Why not? Shouldn't it trivially succeed?
455         if ($fbr eq $tbr) {
456                 $messages->{'DestinationEqualsHolding'} = 1;
457                 $dotransfer = 0;
458         }
459         # check if it is still issued to someone, return it...
460         my ($currentborrower) = currentborrower($iteminformation->{'itemnumber'});
461         if ($currentborrower) {
462                 returnbook($barcode, $fbr);
463                 $messages->{'WasReturned'} = $currentborrower;
464         }
465         # find reserves.....
466         # FIXME - Don't call &CheckReserves unless $ignoreRs is true.
467         # That'll save a database query.
468         my ($resfound, $resrec) = CheckReserves($iteminformation->{'itemnumber'});
469         if ($resfound and not $ignoreRs) {
470                 $resrec->{'ResFound'} = $resfound;
471                 $messages->{'ResFound'} = $resrec;
472                 $dotransfer = 0;
473         }
474         #actually do the transfer....
475         if ($dotransfer) {
476                 dotransfer($iteminformation->{'itemnumber'}, $fbr, $tbr,$user);
477                 $messages->{'WasTransfered'} = 1;
478         }
479         return ($dotransfer, $messages, $iteminformation);
480 }
481
482 # Not exported
483
484 sub dotransfer {
485 ## The book has arrived at this branch because it has been returned there
486 ## So we update the fact the book is in that branch not that we want to send the book to that branch
487
488         my ($itm, $fbr, $tbr,$user) = @_;
489         my $dbh = C4::Context->dbh;
490         
491         #new entry in branchtransfers....
492         my $sth=$dbh->prepare("INSERT INTO branchtransfers (itemnumber, frombranch, datearrived, tobranch,comments) VALUES (?, ?, now(), ?,?)");
493         $sth->execute($itm, $fbr,  $tbr,$user);
494         #update holdingbranch in items .....
495         &domarctransfer($dbh,$itm,$tbr);
496 ## Item seen taken out of this loop to optimize ZEBRA updates
497 #       &itemseen($dbh,$itm);   
498         return;
499 }
500
501 sub domarctransfer{
502 my ($dbh,$itemnumber,$holdingbranch) = @_; 
503 $itemnumber=~s /\'//g;
504 my $sth=$dbh->prepare("select biblionumber from items where itemnumber=$itemnumber");
505         $sth->execute();
506 my ($biblionumber)=$sth->fetchrow; 
507 XMLmoditemonefield($dbh,$biblionumber,$itemnumber,'holdingbranch',$holdingbranch,1);
508         $sth->finish;
509 }
510
511 =head2 canbookbeissued
512
513 Check if a book can be issued.
514
515 my ($issuingimpossible,$needsconfirmation) = canbookbeissued($env,$borrower,$barcode,$year,$month,$day);
516
517 =over 4
518
519 C<$env> Environment variable. Should be empty usually, but used by other subs. Next code cleaning could drop it.
520
521 C<$borrower> hash with borrower informations (from getpatroninformation)
522
523 C<$barcode> is the bar code of the book being issued.
524
525 C<$year> C<$month> C<$day> contains the date of the return (in case it's forced by "stickyduedate".
526
527 =back
528
529 Returns :
530
531 =over 4
532
533 C<$issuingimpossible> a reference to a hash. It contains reasons why issuing is impossible.
534 Possible values are :
535
536 =head3 INVALID_DATE 
537
538 sticky due date is invalid
539
540 =head3 GNA
541
542 borrower gone with no address
543
544 =head3 CARD_LOST
545  
546 borrower declared it's card lost
547
548 =head3 DEBARRED
549
550 borrower debarred
551
552 =head3 UNKNOWN_BARCODE
553
554 barcode unknown
555
556 =head3 NOT_FOR_LOAN
557
558 item is not for loan
559
560 =head3 WTHDRAWN
561
562 item withdrawn.
563
564 =head3 RESTRICTED
565
566 item is restricted (set by ??)
567
568 =back
569
570 C<$issuingimpossible> a reference to a hash. It contains reasons why issuing is impossible.
571 Possible values are :
572
573 =head3 DEBT
574
575 borrower has debts.
576
577 =head3 RENEW_ISSUE
578
579 renewing, not issuing
580
581 =head3 ISSUED_TO_ANOTHER
582
583 issued to someone else.
584
585 =head3 RESERVED
586
587 reserved for someone else.
588
589 =head3 INVALID_DATE
590
591 sticky due date is invalid
592
593 =head3 TOO_MANY
594
595 if the borrower borrows to much things
596
597 =cut
598
599 # check if a book can be issued.
600 # returns an array with errors if any
601
602
603
604
605
606
607
608
609
610
611
612 sub TooMany ($$){
613         my $borrower = shift;
614         my $iteminformation = shift;
615         my $cat_borrower = $borrower->{'categorycode'};
616         my $branch_borrower = $borrower->{'branchcode'};
617         my $dbh = C4::Context->dbh;
618         my $sth = $dbh->prepare('select itemtype from biblio where biblionumber = ?');
619         $sth->execute($iteminformation->{'biblionumber'});
620         my $type = $sth->fetchrow;
621         $sth = $dbh->prepare('select * from issuingrules where categorycode = ? and itemtype = ? and branchcode = ?');
622         my $sth2 = $dbh->prepare("select COUNT(*) from issues i,  items it, biblio b where i.borrowernumber = ? and i.returndate is null and i.itemnumber = it.itemnumber  and b.biblionumber=it.biblionumber and b.itemtype  like ?");
623         my $sth3 = $dbh->prepare('select COUNT(*) from issues where borrowernumber = ? and returndate is null');
624         my $alreadyissued;
625
626         # check the 3 parameters
627         #print "content-type: text/plain \n\n";
628         #print "$cat_borrower, $type, $branch_borrower";
629         $sth->execute($cat_borrower, $type, $branch_borrower);
630         my $result = $sth->fetchrow_hashref;
631         if (defined($result->{maxissueqty})) {
632         #       print "content-type: text/plain \n\n";
633         #print "$cat_borrower, $type, $branch_borrower";
634                 $sth2->execute($borrower->{'borrowernumber'}, $type);
635                 my $alreadyissued = $sth2->fetchrow;    
636         #       print "***" . $alreadyissued;
637         #print "----". $result->{'maxissueqty'};
638           if ($result->{'maxissueqty'} <= $alreadyissued) {
639                         return ("$type  $alreadyissued / max:".($result->{'maxissueqty'}+0));
640           }else {
641                 return;
642           }
643         }
644
645         # check for branch=*
646         $sth->execute($cat_borrower, $type, "");
647          $result = $sth->fetchrow_hashref;
648         if (defined($result->{maxissueqty})) {
649                 $sth2->execute($borrower->{'borrowernumber'}, $type);
650                 my $alreadyissued = $sth2->fetchrow;
651           if ($result->{'maxissueqty'} <= $alreadyissued){
652                 return ("$type  $alreadyissued / max:".($result->{'maxissueqty'}+0));
653              } else {
654                 return;
655              }
656         }
657
658         # check for itemtype=*
659         $sth->execute($cat_borrower, "*", $branch_borrower);
660         $result = $sth->fetchrow_hashref;
661         if (defined($result->{maxissueqty})) {
662                 $sth3->execute($borrower->{'borrowernumber'});
663                 my ($alreadyissued) = $sth3->fetchrow;
664              if ($result->{'maxissueqty'} <= $alreadyissued){
665 #               warn "HERE : $alreadyissued / ($result->{maxissueqty} for $borrower->{'borrowernumber'}";
666                 return ("$type  $alreadyissued / max:".($result->{'maxissueqty'}+0));
667              } else {
668                 return;
669              }
670         }
671
672         #check for borrowertype=*
673         $sth->execute("*", $type, $branch_borrower);
674         $result = $sth->fetchrow_hashref;
675         if (defined($result->{maxissueqty})) {    
676                 $sth2->execute($borrower->{'borrowernumber'}, "%$type%");
677                 my $alreadyissued = $sth2->fetchrow;
678             if ($result->{'maxissueqty'} <= $alreadyissued){        
679                 return ("$type  $alreadyissued / max:".($result->{'maxissueqty'}+0));
680             } else {
681                 return;
682             }
683         }
684
685         #check for borrowertype=*;itemtype=*
686         $sth->execute("*", "*", $branch_borrower);
687         $result = $sth->fetchrow_hashref;
688         if (defined($result->{maxissueqty})) {    
689                 $sth3->execute($borrower->{'borrowernumber'});
690                 my $alreadyissued = $sth3->fetchrow;
691             if ($result->{'maxissueqty'} <= $alreadyissued){
692                 return ("$type  $alreadyissued / max:".($result->{'maxissueqty'}+0));
693             } else {
694                 return;
695             }
696         }
697
698         $sth->execute("*", $type, "");
699         $result = $sth->fetchrow_hashref;
700         if (defined($result->{maxissueqty}) && $result->{maxissueqty}>=0) {
701                 $sth2->execute($borrower->{'borrowernumber'}, "%$type%");
702                 my $alreadyissued = $sth2->fetchrow;
703              if ($result->{'maxissueqty'} <= $alreadyissued){
704                 return ("$type  $alreadyissued / max:".($result->{'maxissueqty'}+0));
705              } else {
706                 return;
707              }
708         }
709
710         $sth->execute($cat_borrower, "*", "");
711         $result = $sth->fetchrow_hashref;
712         if (defined($result->{maxissueqty})) {    
713                 $sth2->execute($borrower->{'borrowernumber'}, "%$type%");
714                 my $alreadyissued = $sth2->fetchrow;
715              if ($result->{'maxissueqty'} <= $alreadyissued){
716                 return ("$type  $alreadyissued / max:".($result->{'maxissueqty'}+0));
717              } else {
718                 return;
719              }
720         }
721
722         $sth->execute("*", "*", "");
723         $result = $sth->fetchrow_hashref;
724         if (defined($result->{maxissueqty})) {    
725                 $sth3->execute($borrower->{'borrowernumber'});
726                 my $alreadyissued = $sth3->fetchrow;
727              if ($result->{'maxissueqty'} <= $alreadyissued){
728                 return ("$type  $alreadyissued / max:".($result->{'maxissueqty'}+0));
729              } else {
730                 return;
731              }
732         }
733         return;
734 }
735
736
737
738
739 sub canbookbeissued {
740         my ($env,$borrower,$barcode,$year,$month,$day,$inprocess) = @_;
741         my %needsconfirmation; # filled with problems that needs confirmations
742         my %issuingimpossible; # filled with problems that causes the issue to be IMPOSSIBLE
743         my $iteminformation = getiteminformation($env, 0, $barcode);
744         my $dbh = C4::Context->dbh;
745 #
746 # DUE DATE is OK ?
747 #
748         my ($duedate, $invalidduedate) = fixdate($year, $month, $day);
749         $issuingimpossible{INVALID_DATE} = 1 if ($invalidduedate);
750
751 #
752 # BORROWER STATUS
753 #
754         if ($borrower->{flags}->{GNA}) {
755                 $issuingimpossible{GNA} = 1;
756         }
757         if ($borrower->{flags}->{'LOST'}) {
758                 $issuingimpossible{CARD_LOST} = 1;
759         }
760         if ($borrower->{flags}->{'DBARRED'}) {
761                 $issuingimpossible{DEBARRED} = 1;
762         }
763         my $today=get_today();
764         if (DATE_diff($borrower->{expiry},$today)<0) {
765                 $issuingimpossible{EXPIRED} = 1;
766         }
767 #
768 # BORROWER STATUS
769 #
770
771 # DEBTS
772         my $amount = checkaccount($env,$borrower->{'borrowernumber'}, $dbh,$duedate);
773         if(C4::Context->preference("IssuingInProcess")){
774             my $amountlimit = C4::Context->preference("noissuescharge");
775                 if ($amount > $amountlimit && !$inprocess) {
776                         $issuingimpossible{DEBT} = sprintf("%.2f",$amount);
777                 } elsif ($amount <= $amountlimit && !$inprocess) {
778                         $needsconfirmation{DEBT} = sprintf("%.2f",$amount);
779                 }
780         } else {
781                          if ($amount >0) {
782                         $needsconfirmation{DEBT} = $amount;
783                 }
784                 }
785
786
787 #
788 # JB34 CHECKS IF BORROWERS DONT HAVE ISSUE TOO MANY BOOKS
789 #
790         my $toomany = TooMany($borrower, $iteminformation);
791         $needsconfirmation{TOO_MANY} =  $toomany if $toomany;
792         $issuingimpossible{TOO_MANY} = $toomany if $toomany;
793 #
794 # ITEM CHECKING
795 #
796         unless ($iteminformation->{barcode}) {
797                 $issuingimpossible{UNKNOWN_BARCODE} = 1;
798         }
799         if ($iteminformation->{'notforloan'} > 0) {
800                 $issuingimpossible{NOT_FOR_LOAN} = 1;
801         }
802         if ($iteminformation->{'itemtype'} eq 'REF') {
803                 $issuingimpossible{NOT_FOR_LOAN} = 1;
804         }
805         if ($iteminformation->{'wthdrawn'} == 1) {
806                 $issuingimpossible{WTHDRAWN} = 1;
807         }
808         if ($iteminformation->{'restricted'} == 1) {
809                 $issuingimpossible{RESTRICTED} = 1;
810         }
811         if ($iteminformation->{'shelf'} eq 'Res') {
812                 $issuingimpossible{IN_RESERVE} = 1;
813         }
814 if (C4::Context->preference("IndependantBranches")){
815                 my $userenv = C4::Context->userenv;
816                 if (($userenv)&&($userenv->{flags} != 1)){
817                         $issuingimpossible{NOTSAMEBRANCH} = 1 if ($iteminformation->{'holdingbranch'} ne $userenv->{branch} ) ;
818                 }
819         }
820
821 #
822 # CHECK IF BOOK ALREADY ISSUED TO THIS BORROWER
823 #
824         my ($currentborrower) = currentborrower($iteminformation->{'itemnumber'});
825         if ($currentborrower eq $borrower->{'borrowernumber'}) {
826 # Already issued to current borrower. Ask whether the loan should
827 # be renewed.
828                 my ($renewstatus) = renewstatus($env, $borrower->{'borrowernumber'}, $iteminformation->{'itemnumber'});
829                 if ($renewstatus == 0) { # no more renewals allowed
830                         $issuingimpossible{NO_MORE_RENEWALS} = 1;
831                 } else {
832                         if (C4::Context->preference("strictrenewals")){
833                         ###if this is set do not allow automatic renewals
834                         ##the new renew script will do same strict checks as issues and return error codes
835                         $needsconfirmation{RENEW_ISSUE} = 1;
836                         }       
837                         
838                 }
839         } elsif ($currentborrower) {
840 # issued to someone else
841                 my $currborinfo = getpatroninformation(0,$currentborrower);
842 #               warn "=>.$currborinfo->{'firstname'} $currborinfo->{'surname'} ($currborinfo->{'cardnumber'})";
843                 $needsconfirmation{ISSUED_TO_ANOTHER} = "$currborinfo->{'reservedate'} : $currborinfo->{'firstname'} $currborinfo->{'surname'} ($currborinfo->{'cardnumber'})";
844         }
845 # See if the item is on RESERVE
846         my ($restype, $res) = CheckReserves($iteminformation->{'itemnumber'});
847         if ($restype) {
848                 my $resbor = $res->{'borrowernumber'};
849                 if ($resbor ne $borrower->{'borrowernumber'} && $restype eq "Waiting") {
850                         # The item is on reserve and waiting, but has been
851                         # reserved by some other patron.
852                         my ($resborrower, $flags)=getpatroninformation($env, $resbor,0);
853                         my $branches = GetBranches();
854                         my $branchname = $branches->{$res->{'branchcode'}}->{'branchname'};
855                         $needsconfirmation{RESERVE_WAITING} = "$resborrower->{'firstname'} $resborrower->{'surname'} ($resborrower->{'cardnumber'}, $branchname)";
856                 #       CancelReserve(0, $res->{'itemnumber'}, $res->{'borrowernumber'});
857                 } elsif ($restype eq "Reserved") {
858                         # The item is on reserve for someone else.
859                         my ($resborrower, $flags)=getpatroninformation($env, $resbor,0);
860                         my $branches = GetBranches();
861                         my $branchname = $branches->{$res->{'branchcode'}}->{'branchname'};
862                         $needsconfirmation{RESERVED} = "$res->{'reservedate'} : $resborrower->{'firstname'} $resborrower->{'surname'} ($resborrower->{'cardnumber'})";
863                 }
864         }
865                 if(C4::Context->preference("LibraryName") eq "Horowhenua Library Trust"){
866                                  if ($borrower->{'categorycode'} eq 'W'){
867                         my %issuingimpossible;
868                                 return(\%issuingimpossible,\%needsconfirmation);
869                         }
870                 }
871               
872         return(\%issuingimpossible,\%needsconfirmation);
873 }
874
875 =head2 issuebook
876
877 Issue a book. Does no check, they are done in canbookbeissued. If we reach this sub, it means the user confirmed if needed.
878
879 &issuebook($env,$borrower,$barcode,$date)
880
881 =over 4
882
883 C<$env> Environment variable. Should be empty usually, but used by other subs. Next code cleaning could drop it.
884
885 C<$borrower> hash with borrower informations (from getpatroninformation)
886
887 C<$barcode> is the bar code of the book being issued.
888
889 C<$date> contains the max date of return. calculated if empty.
890
891 =cut
892
893 #
894 # issuing book. We already have checked it can be issued, so, just issue it !
895 #
896 sub issuebook {
897 ### fix me STOP using koha hashes, change so that XML hash is used
898         my ($env,$borrower,$barcode,$date,$cancelreserve) = @_;
899         my $dbh = C4::Context->dbh;
900         my $itemrecord=XMLgetitemhash($dbh,"",$barcode);
901         my $iteminformation=XMLmarc2koha_onerecord($dbh,$itemrecord,"holdings");
902               $iteminformation->{'itemtype'}=MARCfind_itemtype($dbh,$iteminformation->{biblionumber});
903         my $error;
904 #
905 # check if we just renew the issue.
906 #
907         my ($currentborrower) = currentborrower($iteminformation->{'itemnumber'});
908         if ($currentborrower eq $borrower->{'borrowernumber'}) {
909                 my ($charge,$itemtype) = calc_charges($env, $iteminformation->{'itemnumber'}, $borrower->{'borrowernumber'});
910                 if ($charge > 0) {
911                         createcharge($env, $dbh, $iteminformation->{'itemnumber'}, $borrower->{'borrowernumber'}, $charge);
912                         $iteminformation->{'charge'} = $charge;
913                 }
914                 &UpdateStats($env,$env->{'branchcode'},'renew',$charge,'',$iteminformation->{'itemnumber'},$iteminformation->{'itemtype'},$borrower->{'borrowernumber'});
915                         if (C4::Context->preference("strictrenewals")){
916                         $error=renewstatus($env, $borrower->{'borrowernumber'}, $iteminformation->{'itemnumber'});
917                         renewbook($env, $borrower->{'borrowernumber'}, $iteminformation->{'itemnumber'}) if ($error>1);
918                         }else{
919                  renewbook($env, $borrower->{'borrowernumber'}, $iteminformation->{'itemnumber'});
920                         }
921         } else {
922 #
923 # NOT a renewal
924 #
925                 if ($currentborrower ne '') {
926                         # This book is currently on loan, but not to the person
927                         # who wants to borrow it now. mark it returned before issuing to the new borrower
928                         returnbook($iteminformation->{'barcode'}, $env->{'branchcode'});
929 #warn "return : ".$borrower->{borrowernumber}." / I : ".$iteminformation->{'itemnumber'};
930
931                 }
932                 # See if the item is on reserve.
933                 my ($restype, $res) = CheckReserves($iteminformation->{'itemnumber'});
934 #warn "$restype,$res";
935                 if ($restype) {
936                         my $resbor = $res->{'borrowernumber'};
937                         if ($resbor eq $borrower->{'borrowernumber'}) {
938                                 # The item is on reserve to the current patron
939                                 FillReserve($res);
940 #                               warn "FillReserve";
941                         } elsif ($restype eq "Waiting") {
942 #                               warn "Waiting";
943                                 # The item is on reserve and waiting, but has been
944                                 # reserved by some other patron.
945                                 my ($resborrower, $flags)=getpatroninformation($env, $resbor,0);
946                                 my $branches = GetBranches();
947                                 my $branchname = $branches->{$res->{'branchcode'}}->{'branchname'};
948                  if ($cancelreserve){
949                                     CancelReserve(0, $res->{'itemnumber'}, $res->{'borrowernumber'});
950                   } else {
951                                     # set waiting reserve to first in reserve queue as book isn't waiting now
952                                     UpdateReserve(1, $res->{'biblionumber'}, $res->{'borrowernumber'}, $res->{'branchcode'});
953                                 }
954                         } elsif ($restype eq "Reserved") {
955 #warn "Reserved";
956                                 # The item is on reserve for someone else.
957                                 my ($resborrower, $flags)=getpatroninformation($env, $resbor,0);
958                                 my $branches = GetBranches();
959                                 my $branchname = $branches->{$res->{'branchcode'}}->{'branchname'};
960                                 if ($cancelreserve) {
961                                         # cancel reserves on this item
962                                         CancelReserve(0, $res->{'itemnumber'}, $res->{'borrowernumber'});
963                                         # also cancel reserve on biblio related to this item
964                                 #       my $st_Fbiblio = $dbh->prepare("select biblionumber from items where itemnumber=?");
965                                 #       $st_Fbiblio->execute($res->{'itemnumber'});
966                                 #       my $biblionumber = $st_Fbiblio->fetchrow;
967 #                                       CancelReserve($iteminformation->{'biblionumber'},0,$res->{'borrowernumber'});
968 #                                       warn "CancelReserve $res->{'itemnumber'}, $res->{'borrowernumber'}";
969                                 } else {
970                                         my $tobrcd = ReserveWaiting($res->{'itemnumber'}, $res->{'borrowernumber'});
971                                         transferbook($tobrcd,$barcode, 1);
972                                         warn "transferbook";
973                                 }
974                         }
975                 }
976                 
977                 my $sth=$dbh->prepare("insert into issues (borrowernumber, itemnumber, date_due, branchcode,issue_date) values (?,?,?,?,NOW())");
978                 my $loanlength = getLoanLength($borrower->{'categorycode'},$iteminformation->{'itemtype'},$borrower->{'branchcode'});
979
980                 my $dateduef;
981                  my @datearr = localtime();
982                 $dateduef = (1900+$datearr[5])."-".($datearr[4]+1)."-". $datearr[3];
983
984                 my $calendar = C4::Calendar::Calendar->new(branchcode => $borrower->{'branchcode'});
985                 my ($yeardue, $monthdue, $daydue) = split /-/, $dateduef;
986                 ($daydue, $monthdue, $yeardue) = $calendar->addDate($daydue, $monthdue, $yeardue, $loanlength);
987                 $dateduef = "$yeardue-".sprintf ("%0.2d", $monthdue)."-". sprintf("%0.2d",$daydue);
988         
989 #warn $dateduef;
990                 if ($date) {
991                         $dateduef=$date;
992                 }
993                 # if ReturnBeforeExpiry ON the datedue can't be after borrower expirydate
994                 if (C4::Context->preference('ReturnBeforeExpiry') && $dateduef gt $borrower->{expiry}) {
995                         $dateduef=$borrower->{expiry};
996                 }
997                 $sth->execute($borrower->{'borrowernumber'}, $iteminformation->{'itemnumber'}, $dateduef, $env->{'branchcode'});
998                 $sth->finish;
999                 $iteminformation->{'issues'}++;
1000 ##Record in MARC the new data ,date_due as due date,issue count and the borrowernumber
1001                 $itemrecord=XML_writeline($itemrecord, "issues", $iteminformation->{'issues'},"holdings");
1002                 $itemrecord=XML_writeline($itemrecord, "date_due", $dateduef,"holdings");
1003                 $itemrecord=XML_writeline($itemrecord, "borrowernumber", $borrower->{'borrowernumber'},"holdings");
1004                 $itemrecord=XML_writeline($itemrecord, "itemlost", "0","holdings");
1005                 $itemrecord=XML_writeline($itemrecord, "onloan", "1","holdings");
1006                 # find today's date as timestamp
1007                 my ($sec,$min,$hour,$mday,$mon,$year) = localtime();
1008                 $year += 1900;
1009                 $mon += 1;
1010                 my $timestamp = sprintf("%4d%02d%02d%02d%02d%02d.0",
1011                 $year,$mon,$mday,$hour,$min,$sec);
1012                 $itemrecord=XML_writeline($itemrecord, "datelastseen", $timestamp,"holdings");
1013                 ##Now update the zebradb
1014                 NEWmoditem($dbh,$itemrecord,$iteminformation->{'biblionumber'},$iteminformation->{'itemnumber'});
1015                 # If it costs to borrow this book, charge it to the patron's account.
1016                 my ($charge,$itemtype)=calc_charges($env, $iteminformation->{'itemnumber'}, $borrower->{'borrowernumber'});
1017                 if ($charge > 0) {
1018                         createcharge($env, $dbh, $iteminformation->{'itemnumber'}, $borrower->{'borrowernumber'}, $charge);
1019                         $iteminformation->{'charge'}=$charge;
1020                 }
1021                 # Record the fact that this book was issued in SQL
1022                 &UpdateStats($env,$env->{'branchcode'},'issue',$charge,'',$iteminformation->{'itemnumber'},$iteminformation->{'itemtype'},$borrower->{'borrowernumber'});
1023         }
1024 return($error);
1025 }
1026
1027 =head2 getLoanLength
1028
1029 Get loan length for an itemtype, a borrower type and a branch
1030
1031 my $loanlength = &getLoanLength($borrowertype,$itemtype,branchcode)
1032
1033 =cut
1034
1035 sub getLoanLength {
1036         my ($borrowertype,$itemtype,$branchcode) = @_;
1037         my $dbh = C4::Context->dbh;
1038         my $sth = $dbh->prepare("select issuelength from issuingrules where categorycode=? and itemtype=? and branchcode=?");
1039         # try to find issuelength & return the 1st available.
1040         # check with borrowertype, itemtype and branchcode, then without one of those parameters
1041         $sth->execute($borrowertype,$itemtype,$branchcode);
1042         my $loanlength = $sth->fetchrow_hashref;
1043         return $loanlength->{issuelength} if defined($loanlength);
1044         
1045         $sth->execute($borrowertype,$itemtype,"");
1046         $loanlength = $sth->fetchrow_hashref;
1047         return $loanlength->{issuelength} if defined($loanlength) && $loanlength->{issuelength} ne 'NULL';
1048
1049         $sth->execute($borrowertype,"*",$branchcode);
1050         $loanlength = $sth->fetchrow_hashref;
1051         return $loanlength->{issuelength} if defined($loanlength) && $loanlength->{issuelength} ne 'NULL';
1052
1053         $sth->execute("*",$itemtype,$branchcode);
1054         $loanlength = $sth->fetchrow_hashref;
1055         return $loanlength->{issuelength} if defined($loanlength) && $loanlength->{issuelength} ne 'NULL';
1056
1057         $sth->execute($borrowertype,"*","");
1058         $loanlength = $sth->fetchrow_hashref;
1059         return $loanlength->{issuelength} if defined($loanlength) && $loanlength->{issuelength} ne 'NULL';
1060
1061         $sth->execute("*","*",$branchcode);
1062         $loanlength = $sth->fetchrow_hashref;
1063         return $loanlength->{issuelength} if defined($loanlength) && $loanlength->{issuelength} ne 'NULL';
1064
1065         $sth->execute("*",$itemtype,"");
1066         $loanlength = $sth->fetchrow_hashref;
1067         return $loanlength->{issuelength} if defined($loanlength) && $loanlength->{issuelength} ne 'NULL';
1068
1069         $sth->execute("*","*","");
1070         $loanlength = $sth->fetchrow_hashref;
1071         return $loanlength->{issuelength} if defined($loanlength) && $loanlength->{issuelength} ne 'NULL';
1072
1073         # if no rule is set => 21 days (hardcoded)
1074         return 21;
1075 }
1076 =head2 returnbook
1077
1078   ($doreturn, $messages, $iteminformation, $borrower) =
1079           &returnbook($barcode, $branch);
1080
1081 Returns a book.
1082
1083 C<$barcode> is the bar code of the book being returned. C<$branch> is
1084 the code of the branch where the book is being returned.
1085
1086 C<&returnbook> returns a list of four items:
1087
1088 C<$doreturn> is true iff the return succeeded.
1089
1090 C<$messages> is a reference-to-hash giving the reason for failure:
1091
1092 =over 4
1093
1094 =item C<BadBarcode>
1095
1096 No item with this barcode exists. The value is C<$barcode>.
1097
1098 =item C<NotIssued>
1099
1100 The book is not currently on loan. The value is C<$barcode>.
1101
1102 =item C<IsPermanent>
1103
1104 The book's home branch is a permanent collection. If you have borrowed
1105 this book, you are not allowed to return it. The value is the code for
1106 the book's home branch.
1107
1108 =item C<wthdrawn>
1109
1110 This book has been withdrawn/cancelled. The value should be ignored.
1111
1112 =item C<ResFound>
1113
1114 The item was reserved. The value is a reference-to-hash whose keys are
1115 fields from the reserves table of the Koha database, and
1116 C<biblioitemnumber>. It also has the key C<ResFound>, whose value is
1117 either C<Waiting>, C<Reserved>, or 0.
1118
1119 =back
1120
1121 C<$borrower> is a reference-to-hash, giving information about the
1122 patron who last borrowed the book.
1123
1124 =cut
1125
1126 # FIXME - This API is bogus. There's no need to return $borrower and
1127 # $iteminformation; the caller can ask about those separately, if it
1128 # cares (it'd be inefficient to make two database calls instead of
1129 # one, but &getpatroninformation and &getiteminformation can be
1130 # memoized if this is an issue).
1131 #
1132 # The ($doreturn, $messages) tuple is redundant: if the return
1133 # succeeded, that's all the caller needs to know. So &returnbook can
1134 # return 1 and 0 on success and failure, and set
1135 # $C4::Circulation::Circ2::errmsg to indicate the error. Or it can
1136 # return undef for success, and an error message on error (though this
1137 # is more C-ish than Perl-ish).
1138
1139 sub returnbook {
1140         my ($barcode, $branch) = @_;
1141         my %env;
1142         my $messages;
1143         my $dbh = C4::Context->dbh;
1144         my $doreturn = 1;
1145         die '$branch not defined' unless defined $branch; # just in case (bug 170)
1146         # get information on item
1147         my $itemrecord=XMLgetitemhash($dbh,"",$barcode);
1148         my $iteminformation=XMLmarc2koha_onerecord($dbh,$itemrecord,"holdings");
1149               $iteminformation->{'itemtype'}=MARCfind_itemtype($dbh,$iteminformation->{biblionumber});
1150         if (not $iteminformation) {
1151                 $messages->{'BadBarcode'} = $barcode;
1152                 $doreturn = 0;
1153         }
1154         # find the borrower
1155         my ($currentborrower) = currentborrower($iteminformation->{'itemnumber'});
1156         if ((not $currentborrower) && $doreturn) {
1157                 $messages->{'NotIssued'} = $barcode;
1158         #       $doreturn = 0;
1159         }
1160         # check if the book is in a permanent collection....
1161         my $hbr = $iteminformation->{'homebranch'};
1162         my $branches = GetBranches();
1163         if ($branches->{$hbr}->{'PE'}) {
1164                 $messages->{'IsPermanent'} = $hbr;
1165         }
1166         # check that the book has been cancelled
1167         if ($iteminformation->{'wthdrawn'}) {
1168                 $messages->{'wthdrawn'} = 1;
1169         #       $doreturn = 0;
1170         }
1171         # update issues, thereby returning book (should push this out into another subroutine
1172         my ($borrower) = getpatroninformation(\%env, $currentborrower, 0);
1173         if ($doreturn) {
1174                 my $sth = $dbh->prepare("update issues set returndate = now() where (itemnumber = ?) and (returndate is null)");
1175                 $sth->execute( $iteminformation->{'itemnumber'});
1176                 $messages->{'WasReturned'} = 1; # FIXME is the "= 1" right?
1177         
1178                 $sth->finish;
1179         $itemrecord=XML_writeline($itemrecord, "date_due", "","holdings");
1180         $itemrecord=XML_writeline($itemrecord, "onloan", "0","holdings");
1181         $itemrecord=XML_writeline($itemrecord, "borrowernumber", "","holdings");
1182         }
1183         my ($transfered, $mess, $item) = transferbook($branch, $barcode, 1);
1184         my ($sec,$min,$hour,$mday,$mon,$year) = localtime();
1185                 $year += 1900;
1186                 $mon += 1;
1187                 my $timestamp = sprintf("%4d%02d%02d%02d%02d%02d.0",
1188                 $year,$mon,$mday,$hour,$min,$sec);
1189                 $itemrecord=XML_writeline($itemrecord, "datelastseen", $timestamp,"holdings");
1190                 
1191                 
1192         ($borrower) = getpatroninformation(\%env, $currentborrower, 0);
1193         # transfer book to the current branch
1194         
1195         if ($transfered) {
1196                 $messages->{'WasTransfered'} = 1; # FIXME is the "= 1" right?
1197         }
1198         # fix up the accounts.....
1199         if ($iteminformation->{'itemlost'}) {
1200                 fixaccountforlostandreturned($iteminformation, $borrower);
1201                 $messages->{'WasLost'} = 1; # FIXME is the "= 1" right?
1202                 $itemrecord=XML_writeline($itemrecord, "itemlost", "","holdings");
1203         }
1204 ####WARNING-- FIXME#########    
1205 ### The following new script is commented out
1206 ##      I did not understand what it is supposed to do.
1207 ## If a book is returned at one branch it is automatically recorded being in that branch by
1208 ## transferbook script. This scrip tries to find out whether it was sent thre
1209 ## Well whether sent or not it is physically there and transferbook records this fact in MARCrecord as well
1210 ## If this script is trying to do something else it should be uncommented and also add support for updating MARC record --TG
1211 # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
1212 #       check if we have a transfer for this document
1213 #       my $checktransfer = checktransferts($iteminformation->{'itemnumber'});
1214 #       if we have a return, we update the line of transfers with the datearrived
1215 #       if ($checktransfer){
1216 #               my $sth = $dbh->prepare("update branchtransfers set datearrived = now() where itemnumber= ? AND datearrived IS NULL");
1217 #               $sth->execute($iteminformation->{'itemnumber'});
1218 #               $sth->finish;
1219 #               now we check if there is a reservation with the validate of transfer if we have one, we can             set it with the status 'W'
1220 #               my $updateWaiting = SetWaitingStatus($iteminformation->{'itemnumber'});
1221 #       }
1222 #       if we don't have a transfer on run, we check if the document is not in his homebranch and there is not a reservation, we transfer this one to his home branch directly if system preference Automaticreturn is turn on .
1223 #       else {
1224 #               my $checkreserves = CheckReserves($iteminformation->{'itemnumber'});
1225 #               if (($iteminformation->{'homebranch'} ne $iteminformation->{'holdingbranch'}) and (not $checkreserves) and (C4::Context->preference("AutomaticItemReturn") == 1)){
1226 #                               my $automatictransfer = dotransfer($iteminformation->{'itemnumber'},$iteminformation->{'holdingbranch'},$iteminformation->{'homebranch'});
1227 #                               $messages->{'WasTransfered'} = 1;
1228 #               }
1229 #       }
1230 # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # 
1231         # fix up the overdues in accounts...
1232         fixoverduesonreturn($borrower->{'borrowernumber'}, $iteminformation->{'itemnumber'});
1233         $itemrecord=XML_writeline($itemrecord, "itemoverdue", "","holdings");
1234         # find reserves.....
1235         my ($resfound, $resrec) = CheckReserves($iteminformation->{'itemnumber'});
1236         if ($resfound) {
1237         #       my $tobrcd = ReserveWaiting($resrec->{'itemnumber'}, $resrec->{'borrowernumber'});
1238                 $resrec->{'ResFound'} = $resfound;
1239                 $messages->{'ResFound'} = $resrec;
1240         }
1241         ##Now update the zebradb
1242                 NEWmoditem($dbh,$itemrecord,$iteminformation->{'biblionumber'},$iteminformation->{'itemnumber'});
1243         # update stats?
1244         # Record the fact that this book was returned.
1245         UpdateStats(\%env, $branch ,'return','0','',$iteminformation->{'itemnumber'},$iteminformation->{'itemtype'},$borrower->{'borrowernumber'});
1246         return ($doreturn, $messages, $iteminformation, $borrower);
1247 }
1248
1249 =head2 fixaccountforlostandreturned
1250
1251         &fixaccountforlostandreturned($iteminfo,$borrower);
1252
1253 Calculates the charge for a book lost and returned (Not exported & used only once)
1254
1255 C<$iteminfo> is a hashref to iteminfo. Only {itemnumber} is used.
1256
1257 C<$borrower> is a hashref to borrower. Only {borrowernumber is used.
1258
1259 =cut
1260
1261 sub fixaccountforlostandreturned {
1262         my ($iteminfo, $borrower) = @_;
1263         my %env;
1264         my $dbh = C4::Context->dbh;
1265         my $itm = $iteminfo->{'itemnumber'};
1266         # check for charge made for lost book
1267         my $sth = $dbh->prepare("select * from accountlines where (itemnumber = ?) and (accounttype='L' or accounttype='Rep') order by date desc");
1268         $sth->execute($itm);
1269         if (my $data = $sth->fetchrow_hashref) {
1270         # writeoff this amount
1271                 my $offset;
1272                 my $amount = $data->{'amount'};
1273                 my $acctno = $data->{'accountno'};
1274                 my $amountleft;
1275                 if ($data->{'amountoutstanding'} == $amount) {
1276                 $offset = $data->{'amount'};
1277                 $amountleft = 0;
1278                 } else {
1279                 $offset = $amount - $data->{'amountoutstanding'};
1280                 $amountleft = $data->{'amountoutstanding'} - $amount;
1281                 }
1282                 my $usth = $dbh->prepare("update accountlines set accounttype = 'LR',amountoutstanding='0'
1283                         where (borrowernumber = ?)
1284                         and (itemnumber = ?) and (accountno = ?) ");
1285                 $usth->execute($data->{'borrowernumber'},$itm,$acctno);
1286                 $usth->finish;
1287         #check if any credit is left if so writeoff other accounts
1288                 my $nextaccntno = getnextacctno(\%env,$data->{'borrowernumber'},$dbh);
1289                 if ($amountleft < 0){
1290                 $amountleft*=-1;
1291                 }
1292                 if ($amountleft > 0){
1293                 my $msth = $dbh->prepare("select * from accountlines where (borrowernumber = ?)
1294                                                         and (amountoutstanding >0) order by date");
1295                 $msth->execute($data->{'borrowernumber'});
1296         # offset transactions
1297                 my $newamtos;
1298                 my $accdata;
1299                 while (($accdata=$msth->fetchrow_hashref) and ($amountleft>0)){
1300                         if ($accdata->{'amountoutstanding'} < $amountleft) {
1301                         $newamtos = 0;
1302                         $amountleft -= $accdata->{'amountoutstanding'};
1303                         }  else {
1304                         $newamtos = $accdata->{'amountoutstanding'} - $amountleft;
1305                         $amountleft = 0;
1306                         }
1307                         my $thisacct = $accdata->{'accountno'};
1308                         my $usth = $dbh->prepare("update accountlines set amountoutstanding= ?
1309                                         where (borrowernumber = ?)
1310                                         and (accountno=?)");
1311                         $usth->execute($newamtos,$data->{'borrowernumber'},'$thisacct');
1312                         $usth->finish;
1313                         $usth = $dbh->prepare("insert into accountoffsets
1314                                 (borrowernumber, accountno, offsetaccount,  offsetamount)
1315                                 values
1316                                 (?,?,?,?)");
1317                         $usth->execute($data->{'borrowernumber'},$accdata->{'accountno'},$nextaccntno,$newamtos);
1318                         $usth->finish;
1319                 }
1320                 $msth->finish;
1321                 }
1322                 if ($amountleft > 0){
1323                         $amountleft*=-1;
1324                 }
1325                 my $desc="Book Returned ".$iteminfo->{'barcode'};
1326                 $usth = $dbh->prepare("insert into accountlines
1327                         (borrowernumber,accountno,date,amount,description,accounttype,amountoutstanding)
1328                         values (?,?,now(),?,?,'CR',?)");
1329                 $usth->execute($data->{'borrowernumber'},$nextaccntno,0-$amount,$desc,$amountleft);
1330                 $usth->finish;
1331                 $usth = $dbh->prepare("insert into accountoffsets
1332                         (borrowernumber, accountno, offsetaccount,  offsetamount)
1333                         values (?,?,?,?)");
1334                 $usth->execute($borrower->{'borrowernumber'},$data->{'accountno'},$nextaccntno,$offset);
1335                 $usth->finish;
1336 #               $usth = $dbh->prepare("update items set paidfor='' where itemnumber=?");
1337 #               $usth->execute($itm);
1338 #               $usth->finish;
1339         }
1340         $sth->finish;
1341         return;
1342 }
1343
1344 =head2 fixoverdueonreturn
1345
1346         &fixoverdueonreturn($brn,$itm);
1347
1348 ??
1349
1350 C<$brn> borrowernumber
1351
1352 C<$itm> itemnumber
1353
1354 =cut
1355
1356 sub fixoverduesonreturn {
1357         my ($brn, $itm) = @_;
1358         my $dbh = C4::Context->dbh;
1359         # check for overdue fine
1360         my $sth = $dbh->prepare("select * from accountlines where (borrowernumber = ?) and (itemnumber = ?) and (accounttype='FU' or accounttype='O')");
1361         $sth->execute($brn,$itm);
1362         # alter fine to show that the book has been returned
1363         if (my $data = $sth->fetchrow_hashref) {
1364                 my $usth=$dbh->prepare("update accountlines set accounttype='F' where (borrowernumber = ?) and (itemnumber = ?) and (accountno = ?)");
1365                 $usth->execute($brn,$itm,$data->{'accountno'});
1366                 $usth->finish();
1367         }
1368         $sth->finish();
1369         return;
1370 }
1371
1372
1373 #
1374 # NOTE!: If you change this function, be sure to update the POD for
1375 # &getpatroninformation.
1376 #
1377 # $flags = &patronflags($env, $patron, $dbh);
1378 #
1379 # $flags->{CHARGES}
1380 #               {message}       Message showing patron's credit or debt
1381 #               {noissues}      Set if patron owes >$5.00
1382 #         {GNA}                 Set if patron gone w/o address
1383 #               {message}       "Borrower has no valid address"
1384 #               {noissues}      Set.
1385 #         {LOST}                Set if patron's card reported lost
1386 #               {message}       Message to this effect
1387 #               {noissues}      Set.
1388 #         {DBARRED}             Set is patron is debarred
1389 #               {message}       Message to this effect
1390 #               {noissues}      Set.
1391 #         {NOTES}               Set if patron has notes
1392 #               {message}       Notes about patron
1393 #         {ODUES}               Set if patron has overdue books
1394 #               {message}       "Yes"
1395 #               {itemlist}      ref-to-array: list of overdue books
1396 #               {itemlisttext}  Text list of overdue items
1397 #         {WAITING}             Set if there are items available that the
1398 #                               patron reserved
1399 #               {message}       Message to this effect
1400 #               {itemlist}      ref-to-array: list of available items
1401 sub patronflags {
1402 # Original subroutine for Circ2.pm
1403         my %flags;
1404         my ($env, $patroninformation, $dbh) = @_;
1405         my $amount = C4::Accounts2::checkaccount($env, $patroninformation->{'borrowernumber'}, $dbh);
1406         if ($amount > 0) {
1407                 my %flaginfo;
1408                 my $noissuescharge = C4::Context->preference("noissuescharge");
1409                 $flaginfo{'message'}= sprintf "Patron owes \$%.02f", $amount;
1410                 if ($amount > $noissuescharge) {
1411                 $flaginfo{'noissues'} = 1;
1412                 }
1413                 $flags{'CHARGES'} = \%flaginfo;
1414         } elsif ($amount < 0){
1415         my %flaginfo;
1416         $flaginfo{'message'} = sprintf "Patron has credit of \$%.02f", -$amount;
1417                 $flags{'CHARGES'} = \%flaginfo;
1418         }
1419         if ($patroninformation->{'gonenoaddress'} == 1) {
1420                 my %flaginfo;
1421                 $flaginfo{'message'} = 'Borrower has no valid address.';
1422                 $flaginfo{'noissues'} = 1;
1423                 $flags{'GNA'} = \%flaginfo;
1424         }
1425         if ($patroninformation->{'lost'} == 1) {
1426                 my %flaginfo;
1427                 $flaginfo{'message'} = 'Borrower\'s card reported lost.';
1428                 $flaginfo{'noissues'} = 1;
1429                 $flags{'LOST'} = \%flaginfo;
1430         }
1431         if ($patroninformation->{'debarred'} == 1) {
1432                 my %flaginfo;
1433                 $flaginfo{'message'} = 'Borrower is Debarred.';
1434                 $flaginfo{'noissues'} = 1;
1435                 $flags{'DBARRED'} = \%flaginfo;
1436         }
1437         if ($patroninformation->{'borrowernotes'}) {
1438                 my %flaginfo;
1439                 $flaginfo{'message'} = "$patroninformation->{'borrowernotes'}";
1440                 $flags{'NOTES'} = \%flaginfo;
1441         }
1442         my ($odues, $itemsoverdue)
1443                         = checkoverdues($env, $patroninformation->{'borrowernumber'}, $dbh);
1444         if ($odues > 0) {
1445                 my %flaginfo;
1446                 $flaginfo{'message'} = "Yes";
1447                 $flaginfo{'itemlist'} = $itemsoverdue;
1448                 foreach (sort {$a->{'date_due'} cmp $b->{'date_due'}} @$itemsoverdue) {
1449                 $flaginfo{'itemlisttext'}.="$_->{'date_due'} $_->{'barcode'} $_->{'title'} \n";
1450                 }
1451                 $flags{'ODUES'} = \%flaginfo;
1452         }
1453         my ($nowaiting, $itemswaiting)
1454                         = CheckWaiting($patroninformation->{'borrowernumber'});
1455         if ($nowaiting > 0) {
1456                 my %flaginfo;
1457                 $flaginfo{'message'} = "Reserved items available";
1458                 $flaginfo{'itemlist'} = $itemswaiting;
1459                 $flags{'WAITING'} = \%flaginfo;
1460         }
1461         return(\%flags);
1462 }
1463
1464
1465 # Not exported
1466 sub checkoverdues {
1467 # From Main.pm, modified to return a list of overdueitems, in addition to a count
1468   #checks whether a borrower has overdue items
1469         my ($env, $bornum, $dbh)=@_;
1470         my $today=get_today();
1471         my @overdueitems;
1472         my $count = 0;
1473         my $sth = $dbh->prepare("SELECT issues.* , i.biblionumber as biblionumber,b.* FROM issues, items i,biblio b
1474                         WHERE  i.itemnumber=issues.itemnumber
1475                                 AND i.biblionumber=b.biblionumber
1476                                 AND issues.borrowernumber  = ?
1477                                 AND issues.returndate is NULL
1478                                 AND issues.date_due < ?");
1479         $sth->execute($bornum,$today);
1480         while (my $data = $sth->fetchrow_hashref) {
1481         
1482         push (@overdueitems, $data);
1483         $count++;
1484         }
1485         $sth->finish;
1486         return ($count, \@overdueitems);
1487 }
1488
1489 # Not exported
1490 sub currentborrower {
1491 # Original subroutine for Circ2.pm
1492         my ($itemnumber) = @_;
1493         my $dbh = C4::Context->dbh;
1494         
1495         my $sth=$dbh->prepare("select borrowers.borrowernumber from
1496         issues,borrowers where issues.itemnumber=? and
1497         issues.borrowernumber=borrowers.borrowernumber and issues.returndate is
1498         NULL");
1499         $sth->execute($itemnumber);
1500         my ($borrower) = $sth->fetchrow;
1501         return($borrower);
1502 }
1503
1504 # FIXME - Not exported, but used in 'updateitem.pl' anyway.
1505 sub checkreserve_to_delete {
1506 # Check for reserves for biblio
1507         my ($env,$dbh,$itemnum)=@_;
1508         my $resbor = "";
1509         my $sth = $dbh->prepare("select * from reserves,items
1510         where (items.itemnumber = ?)
1511         and (reserves.cancellationdate is NULL)
1512         and (items.biblionumber = reserves.biblionumber)
1513         and ((reserves.found = 'W')
1514         or (reserves.found is null))
1515         order by priority");
1516         $sth->execute($itemnum);
1517         my $resrec;
1518         my $data=$sth->fetchrow_hashref;
1519         while ($data && $resbor eq '') {
1520         $resrec=$data;
1521         my $const = $data->{'constrainttype'};
1522         if ($const eq "a") {
1523         $resbor = $data->{'borrowernumber'};
1524         } else {
1525         my $found = 0;
1526         my $csth = $dbh->prepare("select * from reserveconstraints,items
1527                 where (borrowernumber=?)
1528                 and reservedate=?
1529                 and reserveconstraints.biblionumber=?
1530                 and (items.itemnumber=? )");
1531         $csth->execute($data->{'borrowernumber'},$data->{'biblionumber'},$data->{'reservedate'},$itemnum);
1532         if (my $cdata=$csth->fetchrow_hashref) {$found = 1;}
1533         if ($const eq 'o') {
1534                 if ($found eq 1) {$resbor = $data->{'borrowernumber'};}
1535         } else {
1536                 if ($found eq 0) {$resbor = $data->{'borrowernumber'};}
1537         }
1538         $csth->finish();
1539         }
1540         $data=$sth->fetchrow_hashref;
1541         }
1542         $sth->finish;
1543         return ($resbor,$resrec);
1544 }
1545
1546 =head2 currentissues
1547
1548   $issues = &currentissues($env, $borrower);
1549
1550 Returns a list of books currently on loan to a patron.
1551
1552 If C<$env-E<gt>{todaysissues}> is set and true, C<&currentissues> only
1553 returns information about books issued today. If
1554 C<$env-E<gt>{nottodaysissues}> is set and true, C<&currentissues> only
1555 returns information about books issued before today. If both are
1556 specified, C<$env-E<gt>{todaysissues}> is ignored. If neither is
1557 specified, C<&currentissues> returns all of the patron's issues.
1558
1559 C<$borrower->{borrowernumber}> is the borrower number of the patron
1560 whose issues we want to list.
1561
1562 C<&currentissues> returns a PHP-style array: C<$issues> is a
1563 reference-to-hash whose keys are integers in the range 1...I<n>, where
1564 I<n> is the number of items on issue (either today or before today).
1565 C<$issues-E<gt>{I<n>}> is a reference-to-hash whose keys are all of
1566 the fields of the biblio, biblioitems, items, and issues fields of the
1567 Koha database for that particular item.
1568
1569 =cut
1570
1571 #'
1572 sub currentissues {
1573 # New subroutine for Circ2.pm
1574         my ($env, $borrower) = @_;
1575         my $dbh = C4::Context->dbh;
1576         my %currentissues;
1577         my $counter=1;
1578         my $borrowernumber = $borrower->{'borrowernumber'};
1579         my $crit='';
1580
1581         # Figure out whether to get the books issued today, or earlier.
1582         # FIXME - $env->{todaysissues} and $env->{nottodaysissues} can
1583         # both be specified, but are mutually-exclusive. This is bogus.
1584         # Make this a flag. Or better yet, return everything in (reverse)
1585         # chronological order and let the caller figure out which books
1586         # were issued today.
1587         my $today=get_today();
1588         if ($env->{'todaysissues'}) {
1589                 
1590                 $crit=" and issues.timestamp like '$today%' ";
1591         }
1592         if ($env->{'nottodaysissues'}) {
1593                 
1594                 $crit=" and !(issues.timestamp like '$today%') ";
1595         }
1596
1597         # FIXME - Does the caller really need every single field from all
1598         # four tables?
1599         my $sth=$dbh->prepare("select * from issues,items where
1600         borrowernumber=? and issues.itemnumber=items.itemnumber and
1601          returndate is null
1602         $crit order by issues.date_due");
1603         $sth->execute($borrowernumber);
1604         while (my $data = $sth->fetchrow_hashref) {
1605
1606                 
1607                 if ($data->{'date_due'} lt $today) {
1608                         $data->{'overdue'}=1;
1609                 }
1610                 my $itemnumber=$data->{'itemnumber'};
1611                 # FIXME - Consecutive integers as hash keys? You have GOT to
1612                 # be kidding me! Use an array, fercrissakes!
1613                 $currentissues{$counter}=$data;
1614                 $counter++;
1615         }
1616         $sth->finish;
1617         return(\%currentissues);
1618 }
1619
1620 =head2 getissues
1621
1622   $issues = &getissues($borrowernumber);
1623
1624 Returns the set of books currently on loan to a patron.
1625
1626 C<$borrowernumber> is the patron's borrower number.
1627
1628 C<&getissues> returns a PHP-style array: C<$issues> is a
1629 reference-to-hash whose keys are integers in the range 0..I<n>-1,
1630 where I<n> is the number of books the patron currently has on loan.
1631
1632 The values of C<$issues> are references-to-hash whose keys are
1633 selected fields from the issues, items, biblio, and biblioitems tables
1634 of the Koha database.
1635
1636 =cut
1637 #'
1638 sub getissues {
1639         my ($borrower) = @_;
1640         my $dbh = C4::Context->dbh;
1641         my $borrowernumber = $borrower->{'borrowernumber'};
1642         my %currentissues;
1643         my $bibliodata;
1644         my @results;
1645         my $todaysdate=get_today();
1646         my $counter = 0;
1647         my $select = "SELECT *
1648                         FROM issues,items,biblio
1649                         WHERE issues.borrowernumber  = ?
1650                         AND issues.itemnumber      = items.itemnumber
1651                         AND items.biblionumber      = biblio.biblionumber
1652                         AND issues.returndate      IS NULL
1653                         ORDER BY issues.date_due";
1654         #    print $select;
1655         my $sth=$dbh->prepare($select);
1656         $sth->execute($borrowernumber);
1657         while (my $data = $sth->fetchrow_hashref) {     
1658                 if ($data->{'date_due'}  lt $todaysdate) {
1659                         $data->{'overdue'} = 1;
1660                 }
1661                 $currentissues{$counter} = $data;
1662                 $counter++;
1663         }
1664         $sth->finish;
1665         
1666         return(\%currentissues);
1667 }
1668
1669 # Not exported
1670 sub checkwaiting {
1671 # check for reserves waiting
1672         my ($env,$dbh,$bornum)=@_;
1673         my @itemswaiting;
1674         my $sth = $dbh->prepare("select * from reserves where (borrowernumber = ?) and (reserves.found='W') and cancellationdate is NULL");
1675         $sth->execute($bornum);
1676         my $cnt=0;
1677         if (my $data=$sth->fetchrow_hashref) {
1678                 $itemswaiting[$cnt] =$data;
1679                 $cnt ++
1680         }
1681         $sth->finish;
1682         return ($cnt,\@itemswaiting);
1683 }
1684
1685 =head2 renewstatus
1686
1687   $ok = &renewstatus($env, $dbh, $borrowernumber, $itemnumber);
1688
1689 Find out whether a borrowed item may be renewed.
1690
1691 C<$env> is ignored.
1692
1693 C<$dbh> is a DBI handle to the Koha database.
1694
1695 C<$borrowernumber> is the borrower number of the patron who currently
1696 has the item on loan.
1697
1698 C<$itemnumber> is the number of the item to renew.
1699
1700 C<$renewstatus> returns a true value iff the item may be renewed. The
1701 item must currently be on loan to the specified borrower; renewals
1702 must be allowed for the item's type; and the borrower must not have
1703 already renewed the loan.
1704
1705 =cut
1706
1707 sub renewstatus {
1708         # check renewal status
1709         ##If system preference "strictrenewals" is used This script will try to return $renewok=2 or $renewok=3 as error messages
1710         ## 
1711         my ($env,$bornum,$itemnumber)=@_;
1712         my $dbh=C4::Context->dbh;
1713         my $renews = 1;
1714         my $resfound;
1715         my $resrec;
1716         my $renewokay; ##
1717         # Look in the issues table for this item, lent to this borrower,
1718         # and not yet returned.
1719 my $borrower=C4::Members::getpatroninformation($dbh,$bornum,undef);
1720         if (C4::Context->preference("LibraryName") eq "NEU Grand Library"){
1721                 ## faculty members and privileged get renewal whatever the case may be
1722                 if ($borrower->{'categorycode'} eq 'F' ||$borrower->{'categorycode'} eq 'P'){
1723                 $renewokay = 1;
1724                 return $renewokay;
1725                 }
1726         }
1727         # FIXME - I think this function could be redone to use only one SQL call.
1728         my $sth1 = $dbh->prepare("select * from issues,items,biblio
1729                                                                 where (borrowernumber = ?)
1730                                                                 and (issues.itemnumber = ?)
1731                                                                 and items.biblionumber=biblio.biblionumber
1732                                                                 and returndate is null
1733                                                                 and items.itemnumber=issues.itemnumber");
1734         $sth1->execute($bornum,$itemnumber);
1735         if (my $data1 = $sth1->fetchrow_hashref) {
1736                 # Found a matching item
1737         
1738                 # See if this item may be renewed. 
1739                 my $sth2 = $dbh->prepare("select renewalsallowed from itemtypes where itemtypes.itemtype=?");
1740                 $sth2->execute($data1->{itemtype});
1741                 if (my $data2=$sth2->fetchrow_hashref) {
1742                 $renews = $data2->{'renewalsallowed'};
1743                 }
1744                 if ($renews > $data1->{'renewals'}) {
1745                         $renewokay= 1;
1746                 }else{
1747                         if (C4::Context->preference("strictrenewals")){
1748                         $renewokay=3 ;
1749                         }
1750                 }
1751                 $sth2->finish;
1752                  ($resfound, $resrec) = CheckReserves($itemnumber);
1753                 if ($resfound) {
1754                         if (C4::Context->preference("strictrenewals")){
1755                         $renewokay=4;
1756                         }else{
1757                                $renewokay = 0;
1758                                  }
1759                 }
1760         }## item found
1761                  ($resfound, $resrec) = CheckReserves($itemnumber);
1762                          if ($resfound) {
1763                                  if (C4::Context->preference("strictrenewals")){
1764                                                 $renewokay=4;
1765                                                 }else{
1766                                                  $renewokay = 0;
1767                                           }
1768                                         }       
1769 #       }
1770         $sth1->finish;
1771 if (C4::Context->preference("strictrenewals")){
1772         ### A new system pref "allowRenewalsBefore" prevents the renewal before a set amount of days left before expiry
1773         ## Try to find whether book can be renewed at this date
1774         my $loanlength;
1775
1776         my $allowRenewalsBefore = C4::Context->preference("allowRenewalsBefore");
1777         my $today=get_today();
1778
1779         # Find the issues record for this book### 
1780         my $sth=$dbh->prepare("select SUBDATE(date_due, $allowRenewalsBefore)  from issues where itemnumber=? and returndate is null");
1781         $sth->execute($itemnumber);
1782         my $startdate=$sth->fetchrow;
1783         $sth->finish;
1784         
1785         my $difference = DATE_diff($today,$startdate);
1786         if  ($difference < 0) {
1787         $renewokay=2 ;
1788         }
1789 }##strictrenewals
1790         return($renewokay);
1791 }
1792
1793 =head2 renewbook
1794
1795   &renewbook($env, $borrowernumber, $itemnumber, $datedue);
1796
1797 Renews a loan.
1798
1799 C<$env-E<gt>{branchcode}> is the code of the branch where the
1800 renewal is taking place.
1801
1802 C<$env-E<gt>{usercode}> is the value to log in C<statistics.usercode>
1803 in the Koha database.
1804
1805 C<$borrowernumber> is the borrower number of the patron who currently
1806 has the item.
1807
1808 C<$itemnumber> is the number of the item to renew.
1809
1810 C<$datedue> can be used to set the due date. If C<$datedue> is the
1811 empty string, C<&renewbook> will calculate the due date automatically
1812 from the book's item type. If you wish to set the due date manually,
1813 C<$datedue> should be in the form YYYY-MM-DD.
1814
1815 =cut
1816
1817 sub renewbook {
1818         my ($env,$bornum,$itemnumber,$datedue)=@_;
1819         # mark book as renewed
1820
1821         my $loanlength;
1822 my $dbh=C4::Context->dbh;
1823 my  $iteminformation = getiteminformation($env, $itemnumber,0);
1824         my $sth=$dbh->prepare("select date_due  from issues where itemnumber=? and returndate is null ");
1825         $sth->execute($itemnumber);
1826         my $issuedata=$sth->fetchrow;
1827         $sth->finish;
1828                 
1829
1830 ## We find a new datedue either from today or from the due_date of the book- if "strictrenewals" is in effect
1831
1832 if ($datedue eq "" ) {
1833
1834                 my  $borrower = getpatroninformation($env,$bornum,0);
1835                  $loanlength = getLoanLength($borrower->{'categorycode'},$iteminformation->{'itemtype'},$borrower->{'branchcode'});
1836         if (C4::Context->preference("strictrenewals")){
1837         my @nowarr = localtime(time);
1838         my $now = (1900+$nowarr[5])."-".($nowarr[4]+1)."-".$nowarr[3]; 
1839                 if ($issuedata<=$now){
1840         
1841                 $datedue=$issuedata;
1842                 my $calendar = C4::Calendar::Calendar->new(branchcode => $borrower->{'branchcode'});
1843                 my ($yeardue, $monthdue, $daydue) = split /-/, $datedue;
1844                 ($daydue, $monthdue, $yeardue) = $calendar->addDate($daydue, $monthdue, $yeardue, $loanlength);
1845                 $datedue = "$yeardue-".sprintf ("%0.2d", $monthdue)."-". sprintf("%0.2d",$daydue);
1846                 }
1847         }## stricrenewals       
1848                 
1849         if ($datedue eq "" ){## incase $datedue chnaged above
1850                 
1851                 my $datedue=get_today();
1852                 my $calendar = C4::Calendar::Calendar->new(branchcode => $borrower->{'branchcode'});
1853                 my ($yeardue, $monthdue, $daydue) = split /-/, $datedue;
1854                 ($daydue, $monthdue, $yeardue) = $calendar->addDate($daydue, $monthdue, $yeardue, $loanlength);
1855                 $datedue = "$yeardue-".sprintf ("%0.2d", $monthdue)."-". sprintf("%0.2d",$daydue);
1856                 
1857         }
1858
1859
1860
1861
1862         # Update the issues record to have the new due date, and a new count
1863         # of how many times it has been renewed.
1864         
1865         $sth=$dbh->prepare("update issues set date_due = ?, renewals = renewals+1
1866                 where borrowernumber=? and itemnumber=? and returndate is null");
1867         $sth->execute($datedue,$bornum,$itemnumber);
1868         $sth->finish;
1869
1870         ## Update items and marc record with new date -T.G
1871         my $iteminformation = getiteminformation($env, $itemnumber,0);
1872         &XMLmoditemonefield($dbh,$iteminformation->{'biblionumber'},$iteminformation->{'itemnumber'},'date_due',$datedue);
1873                 
1874         # Log the renewal
1875         UpdateStats($env,$env->{'branchcode'},'renew','','',$itemnumber,'',$bornum);
1876
1877         # Charge a new rental fee, if applicable?
1878         my ($charge,$type)=calc_charges($env, $itemnumber, $bornum);
1879         if ($charge > 0){
1880                 my $accountno=getnextacctno($env,$bornum,$dbh);
1881                 $sth=$dbh->prepare("Insert into accountlines (borrowernumber,accountno,date,amount,description,accounttype,amountoutstanding,itemnumber)
1882                                                         values (?,?,now(),?,?,?,?,?)");
1883                 $sth->execute($bornum,$accountno,$charge,"Renewal of Rental Item $iteminformation->{'title'} $iteminformation->{'barcode'}",'Rent',$charge,$itemnumber);
1884                 $sth->finish;
1885         #     print $account;
1886         }# end of rental charge
1887         
1888
1889         }
1890
1891  
1892         
1893 }
1894
1895
1896
1897 =item calc_charges
1898
1899   ($charge, $item_type) = &calc_charges($env, $itemnumber, $borrowernumber);
1900
1901 Calculate how much it would cost for a given patron to borrow a given
1902 item, including any applicable discounts.
1903
1904 C<$env> is ignored.
1905
1906 C<$itemnumber> is the item number of item the patron wishes to borrow.
1907
1908 C<$borrowernumber> is the patron's borrower number.
1909
1910 C<&calc_charges> returns two values: C<$charge> is the rental charge,
1911 and C<$item_type> is the code for the item's item type (e.g., C<VID>
1912 if it's a video).
1913
1914 =cut
1915
1916 sub calc_charges {
1917         # calculate charges due
1918         my ($env, $itemnumber, $bornum)=@_;
1919         my $charge=0;
1920         my $dbh = C4::Context->dbh;
1921         my $item_type;
1922         my $sth= $dbh->prepare("select itemtype from biblio,items where items.biblionumber=biblio.biblionumber and itemnumber=?");
1923         $sth->execute($itemnumber);
1924         my $itemtype=$sth->fetchrow;
1925         $sth->finish;
1926         
1927         my $sth1= $dbh->prepare("select rentalcharge from itemtypes where  itemtypes.itemtype=?");
1928         $sth1->execute($itemtype);
1929         
1930         $charge = $sth1->fetchrow;
1931         my $q2 = "select rentaldiscount from issuingrules,borrowers
1932               where (borrowers.borrowernumber = ?)
1933               and (borrowers.categorycode = issuingrules.categorycode)
1934               and (issuingrules.itemtype = ?)";
1935             my $sth2=$dbh->prepare($q2);
1936             $sth2->execute($bornum,$itemtype);
1937     if (my $data2=$sth2->fetchrow_hashref) {
1938                 my $discount = $data2->{'rentaldiscount'};
1939                 if ($discount eq 'NULL') {
1940                     $discount=0;
1941                 }
1942                 $charge = ($charge *(100 - $discount)) / 100;
1943                 #               warn "discount is $discount";
1944          }
1945         $sth2->finish;
1946         
1947         $sth1->finish;
1948         return ($charge,$itemtype);
1949 }
1950
1951
1952
1953 sub createcharge {
1954
1955     my ($env,$dbh,$itemnumber,$bornum,$charge) = @_;
1956     my $nextaccntno = getnextacctno($env,$bornum,$dbh);
1957     my $sth = $dbh->prepare(<<EOT);
1958         INSERT INTO     accountlines
1959                         (borrowernumber, itemnumber, accountno,
1960                          date, amount, description, accounttype,
1961                          amountoutstanding)
1962         VALUES          (?, ?, ?,
1963                          now(), ?, 'Rental', 'Rent',
1964                          ?)
1965 EOT
1966     $sth->execute($bornum, $itemnumber, $nextaccntno, $charge, $charge);
1967     $sth->finish;
1968 }
1969
1970
1971
1972
1973 =item find_reserves
1974
1975   ($status, $record) = &find_reserves($itemnumber);
1976
1977 Looks up an item in the reserves.
1978
1979 C<$itemnumber> is the itemnumber to look up.
1980
1981 C<$status> is true iff the search was successful.
1982
1983 C<$record> is a reference-to-hash describing the reserve. Its keys are
1984 the fields from the reserves table of the Koha database.
1985
1986 =cut
1987 #'
1988 # FIXME - This API is bogus: just return the record, or undef if none
1989 # was found.
1990
1991 sub find_reserves {
1992     my ($itemnumber) = @_;
1993     my $dbh = C4::Context->dbh;
1994     my ($itemdata) = getiteminformation("", $itemnumber,0);
1995     my $sth = $dbh->prepare("select * from reserves where ((found = 'W') or (found is null)) and biblionumber = ? and cancellationdate is NULL order by priority, reservedate");
1996     $sth->execute($itemdata->{'biblionumber'});
1997     my $resfound = 0;
1998     my $resrec;
1999     my $lastrec;
2000
2001     # FIXME - I'm not really sure what's going on here, but since we
2002     # only want one result, wouldn't it be possible (and far more
2003     # efficient) to do something clever in SQL that only returns one
2004     # set of values?
2005 while ($resrec = $sth->fetchrow_hashref) {
2006         $lastrec = $resrec;
2007       if ($resrec->{'found'} eq "W") {
2008             if ($resrec->{'itemnumber'} eq $itemnumber) {
2009                 $resfound = 1;
2010             }
2011         } else {
2012             # FIXME - Use 'elsif' to avoid unnecessary indentation.
2013             if ($resrec->{'constrainttype'} eq "a") {
2014                 $resfound = 1;  
2015             } else {
2016                         my $consth = $dbh->prepare("select * from reserveconstraints where borrowernumber = ? and reservedate = ? and biblionumber = ? ");
2017                         $consth->execute($resrec->{'borrowernumber'},$resrec->{'reservedate'},$resrec->{'biblionumber'});
2018                         if (my $conrec = $consth->fetchrow_hashref) {
2019                                 if ($resrec->{'constrainttype'} eq "o") {
2020                                 $resfound = 1;
2021                                 
2022                                 }
2023                         }
2024                 $consth->finish;
2025                 }
2026         }
2027         if ($resfound) {
2028             my $updsth = $dbh->prepare("update reserves set found = 'W', itemnumber = ? where borrowernumber = ? and reservedate = ? and biblionumber = ?");
2029             $updsth->execute($itemnumber,$resrec->{'borrowernumber'},$resrec->{'reservedate'},$resrec->{'biblionumber'});
2030             $updsth->finish;
2031             last;
2032         }
2033     }
2034     $sth->finish;
2035     return ($resfound,$lastrec);
2036 }
2037
2038 sub fixdate {
2039     my ($year, $month, $day) = @_;
2040     my $invalidduedate;
2041     my $date;
2042     if (($year eq 0) && ($month eq 0) && ($year eq 0)) {
2043 #       $env{'datedue'}='';
2044     } else {
2045         if (($year eq 0) || ($month eq 0) || ($year eq 0)) {
2046             $invalidduedate=1;
2047         } else {
2048             if (($day>30) && (($month==4) || ($month==6) || ($month==9) || ($month==11))) {
2049                 $invalidduedate = 1;
2050             } elsif (($day > 29) && ($month == 2)) {
2051                 $invalidduedate=1;
2052             } elsif (($month == 2) && ($day > 28) && (($year%4) && ((!($year%100) || ($year%400))))) {
2053                 $invalidduedate=1;
2054             } else {
2055                 $date="$year-$month-$day";
2056             }
2057         }
2058     }
2059     return ($date, $invalidduedate);
2060 }
2061
2062 sub get_current_return_date_of {
2063     my (@itemnumbers) = @_;
2064
2065     my $query = '
2066 SELECT date_due,
2067        itemnumber
2068   FROM issues
2069   WHERE itemnumber IN ('.join(',', @itemnumbers).') AND returndate IS NULL
2070 ';
2071     return get_infos_of($query, 'itemnumber', 'date_due');
2072 }
2073
2074 sub get_transfert_infos {
2075     my ($itemnumber) = @_;
2076
2077     my $dbh = C4::Context->dbh;
2078
2079     my $query = '
2080 SELECT datesent,
2081        frombranch,
2082        tobranch
2083   FROM branchtransfers
2084   WHERE itemnumber = ?
2085     AND datearrived IS NULL
2086 ';
2087     my $sth = $dbh->prepare($query);
2088     $sth->execute($itemnumber);
2089
2090     my @row = $sth->fetchrow_array();
2091
2092     $sth->finish;
2093
2094     return @row;
2095 }
2096
2097
2098 sub DeleteTransfer {
2099         my($itemnumber) = @_;
2100         my $dbh = C4::Context->dbh;
2101         my $sth=$dbh->prepare("DELETE FROM branchtransfers
2102         where itemnumber=?
2103         AND datearrived is null ");
2104         $sth->execute($itemnumber);
2105         $sth->finish;
2106 }
2107
2108 sub GetTransfersFromBib {
2109         my($frombranch,$tobranch) = @_;
2110         my $dbh = C4::Context->dbh;
2111         my $sth=$dbh->prepare("SELECT itemnumber,datesent,frombranch FROM
2112          branchtransfers 
2113         where frombranch=?
2114         AND tobranch=? 
2115         AND datearrived is null ");
2116         $sth->execute($frombranch,$tobranch);
2117         my @gettransfers;
2118         my $i=0;
2119         while (my $data=$sth->fetchrow_hashref){
2120                 $gettransfers[$i]=$data;
2121                 $i++;
2122         }
2123         $sth->finish;
2124         return(@gettransfers);  
2125 }
2126
2127 sub GetReservesToBranch {
2128         my($frombranch,$default) = @_;
2129         my $dbh = C4::Context->dbh;
2130         my $sth=$dbh->prepare("SELECT borrowernumber,reservedate,itemnumber,timestamp FROM
2131          reserves 
2132         where priority='0' AND cancellationdate is null  
2133         AND branchcode=?
2134         AND branchcode!=?
2135         AND found is null ");
2136         $sth->execute($frombranch,$default);
2137         my @transreserv;
2138         my $i=0;
2139         while (my $data=$sth->fetchrow_hashref){
2140                 $transreserv[$i]=$data;
2141                 $i++;
2142         }
2143         $sth->finish;
2144         return(@transreserv);   
2145 }
2146
2147 sub GetReservesForBranch {
2148         my($frombranch) = @_;
2149         my $dbh = C4::Context->dbh;
2150         my $sth=$dbh->prepare("SELECT borrowernumber,reservedate,itemnumber,waitingdate FROM
2151          reserves 
2152         where priority='0' AND cancellationdate is null 
2153         AND found='W' 
2154         AND branchcode=? order by reservedate");
2155         $sth->execute($frombranch);
2156         my @transreserv;
2157         my $i=0;
2158         while (my $data=$sth->fetchrow_hashref){
2159                 $transreserv[$i]=$data;
2160                 $i++;
2161         }
2162         $sth->finish;
2163         return(@transreserv);   
2164 }
2165
2166 sub checktransferts{
2167         my($itemnumber) = @_;
2168         my $dbh = C4::Context->dbh;
2169         my $sth=$dbh->prepare("SELECT datesent,frombranch,tobranch FROM branchtransfers
2170         WHERE itemnumber = ? AND datearrived IS NULL");
2171         $sth->execute($itemnumber);
2172         my @tranferts = $sth->fetchrow_array;
2173         $sth->finish;
2174
2175         return (@tranferts);
2176 }
2177
2178
2179 1;
2180 __END__
2181
2182 =back
2183
2184 =head1 AUTHOR
2185
2186 Koha Developement team <info@koha.org>
2187
2188 =cut