Circulation.pm - cleanup, clarification of HLT-specific behavior. Please confirm...
[koha.git] / C4 / Circulation.pm
1 package C4::Circulation;
2
3 # Copyright 2000-2002 Katipo Communications
4 #
5 # This file is part of Koha.
6 #
7 # Koha is free software; you can redistribute it and/or modify it under the
8 # terms of the GNU General Public License as published by the Free Software
9 # Foundation; either version 2 of the License, or (at your option) any later
10 # version.
11 #
12 # Koha is distributed in the hope that it will be useful, but WITHOUT ANY
13 # WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
14 # A PARTICULAR PURPOSE.  See the GNU General Public License for more details.
15 #
16 # You should have received a copy of the GNU General Public License along with
17 # Koha; if not, write to the Free Software Foundation, Inc., 59 Temple Place,
18 # Suite 330, Boston, MA  02111-1307 USA
19
20
21 use strict;
22 require Exporter;
23 use C4::Context;
24 use C4::Stats;
25 use C4::Reserves;
26 use C4::Koha;
27 use C4::Biblio;
28 use C4::Items;
29 use C4::Members;
30 use C4::Dates;
31 use Date::Calc qw(
32   Today
33   Today_and_Now
34   Add_Delta_YM
35   Add_Delta_DHMS
36   Date_to_Days
37   Day_of_Week
38   Add_Delta_Days        
39 );
40 use POSIX qw(strftime);
41 use C4::Branch; # GetBranches
42 use C4::Log; # logaction
43
44 use Data::Dumper;
45
46 use vars qw($VERSION @ISA @EXPORT @EXPORT_OK %EXPORT_TAGS);
47
48 BEGIN {
49         # set the version for version checking
50         $VERSION = 3.01;
51         @ISA    = qw(Exporter);
52
53         # FIXME subs that should probably be elsewhere
54         push @EXPORT, qw(
55                 &FixOverduesOnReturn
56                 &cuecatbarcodedecode
57         );
58
59         # subs to deal with issuing a book
60         push @EXPORT, qw(
61                 &CanBookBeIssued
62                 &CanBookBeRenewed
63                 &AddIssue
64                 &AddRenewal
65                 &GetRenewCount
66                 &GetItemIssue
67                 &GetItemIssues
68                 &GetBorrowerIssues
69                 &GetIssuingCharges
70                 &GetBiblioIssues
71                 &AnonymiseIssueHistory
72         );
73
74         # subs to deal with returns
75         push @EXPORT, qw(
76                 &AddReturn
77         );
78
79         # subs to deal with transfers
80         push @EXPORT, qw(
81                 &transferbook
82                 &GetTransfers
83                 &GetTransfersFromTo
84                 &updateWrongTransfer
85                 &DeleteTransfer
86         );
87 }
88
89 =head1 NAME
90
91 C4::Circulation - Koha circulation module
92
93 =head1 SYNOPSIS
94
95 use C4::Circulation;
96
97 =head1 DESCRIPTION
98
99 The functions in this module deal with circulation, issues, and
100 returns, as well as general information about the library.
101 Also deals with stocktaking.
102
103 =head1 FUNCTIONS
104
105 =head2 decode
106
107 =head3 $str = &decode($chunk);
108
109 =over 4
110
111 =item Decodes a segment of a string emitted by a CueCat barcode scanner and
112 returns it.
113
114 =back
115
116 =cut
117
118 # FIXME - At least, I'm pretty sure this is for decoding CueCat stuff.
119 # FIXME From Paul : i don't understand what this sub does & why it has to be called on every circ. Speak of this with chris maybe ?
120
121 sub cuecatbarcodedecode {
122     my ($barcode) = @_;
123     chomp($barcode);
124     my @fields = split( /\./, $barcode );
125     my @results = map( decode($_), @fields[ 1 .. $#fields ] );
126     if ( $#results == 2 ) {
127         return $results[2];
128     }
129     else {
130         return $barcode;
131     }
132 }
133
134 =head2 decode
135
136 =head3 $str = &decode($chunk);
137
138 =over 4
139
140 =item Decodes a segment of a string emitted by a CueCat barcode scanner and
141 returns it.
142
143 =back
144
145 =cut
146
147 sub decode {
148     my ($encoded) = @_;
149     my $seq =
150       'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789+-';
151     my @s = map { index( $seq, $_ ); } split( //, $encoded );
152     my $l = ( $#s + 1 ) % 4;
153     if ($l) {
154         if ( $l == 1 ) {
155             warn "Error!";
156             return;
157         }
158         $l = 4 - $l;
159         $#s += $l;
160     }
161     my $r = '';
162     while ( $#s >= 0 ) {
163         my $n = ( ( $s[0] << 6 | $s[1] ) << 6 | $s[2] ) << 6 | $s[3];
164         $r .=
165             chr( ( $n >> 16 ) ^ 67 )
166          .chr( ( $n >> 8 & 255 ) ^ 67 )
167          .chr( ( $n & 255 ) ^ 67 );
168         @s = @s[ 4 .. $#s ];
169     }
170     $r = substr( $r, 0, length($r) - $l );
171     return $r;
172 }
173
174 =head2 transferbook
175
176 ($dotransfer, $messages, $iteminformation) = &transferbook($newbranch, $barcode, $ignore_reserves);
177
178 Transfers an item to a new branch. If the item is currently on loan, it is automatically returned before the actual transfer.
179
180 C<$newbranch> is the code for the branch to which the item should be transferred.
181
182 C<$barcode> is the barcode of the item to be transferred.
183
184 If C<$ignore_reserves> is true, C<&transferbook> ignores reserves.
185 Otherwise, if an item is reserved, the transfer fails.
186
187 Returns three values:
188
189 =head3 $dotransfer 
190
191 is true if the transfer was successful.
192
193 =head3 $messages
194
195 is a reference-to-hash which may have any of the following keys:
196
197 =over 4
198
199 =item C<BadBarcode>
200
201 There is no item in the catalog with the given barcode. The value is C<$barcode>.
202
203 =item C<IsPermanent>
204
205 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.
206
207 =item C<DestinationEqualsHolding>
208
209 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.
210
211 =item C<WasReturned>
212
213 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.
214
215 =item C<ResFound>
216
217 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>.
218
219 =item C<WasTransferred>
220
221 The item was eligible to be transferred. Barring problems communicating with the database, the transfer should indeed have succeeded. The value should be ignored.
222
223 =back
224
225 =cut
226
227 sub transferbook {
228     my ( $tbr, $barcode, $ignoreRs ) = @_;
229     my $messages;
230     my $dotransfer      = 1;
231     my $branches        = GetBranches();
232     my $itemnumber = GetItemnumberFromBarcode( $barcode );
233     my $issue      = GetItemIssue($itemnumber);
234     my $biblio = GetBiblioFromItemNumber($itemnumber);
235
236     # bad barcode..
237     if ( not $itemnumber ) {
238         $messages->{'BadBarcode'} = $barcode;
239         $dotransfer = 0;
240     }
241
242     # get branches of book...
243     my $hbr = $biblio->{'homebranch'};
244     my $fbr = $biblio->{'holdingbranch'};
245
246     # if is permanent...
247     if ( $hbr && $branches->{$hbr}->{'PE'} ) {
248         $messages->{'IsPermanent'} = $hbr;
249     }
250
251     # can't transfer book if is already there....
252     if ( $fbr eq $tbr ) {
253         $messages->{'DestinationEqualsHolding'} = 1;
254         $dotransfer = 0;
255     }
256
257     # check if it is still issued to someone, return it...
258     if ($issue->{borrowernumber}) {
259         AddReturn( $barcode, $fbr );
260         $messages->{'WasReturned'} = $issue->{borrowernumber};
261     }
262
263     # find reserves.....
264     # That'll save a database query.
265     my ( $resfound, $resrec ) =
266       CheckReserves( $itemnumber );
267     if ( $resfound and not $ignoreRs ) {
268         $resrec->{'ResFound'} = $resfound;
269
270         #         $messages->{'ResFound'} = $resrec;
271         $dotransfer = 1;
272     }
273
274     #actually do the transfer....
275     if ($dotransfer) {
276         ModItemTransfer( $itemnumber, $fbr, $tbr );
277
278         # don't need to update MARC anymore, we do it in batch now
279         $messages->{'WasTransfered'} = 1;
280                 ModDateLastSeen( $itemnumber );
281     }
282     return ( $dotransfer, $messages, $biblio );
283 }
284
285 =head2 CanBookBeIssued
286
287 Check if a book can be issued.
288
289 my ($issuingimpossible,$needsconfirmation) = CanBookBeIssued($borrower,$barcode,$year,$month,$day);
290
291 =over 4
292
293 =item C<$borrower> hash with borrower informations (from GetMemberDetails)
294
295 =item C<$barcode> is the bar code of the book being issued.
296
297 =item C<$year> C<$month> C<$day> contains the date of the return (in case it's forced by "stickyduedate".
298
299 =back
300
301 Returns :
302
303 =over 4
304
305 =item C<$issuingimpossible> a reference to a hash. It contains reasons why issuing is impossible.
306 Possible values are :
307
308 =back
309
310 =head3 INVALID_DATE 
311
312 sticky due date is invalid
313
314 =head3 GNA
315
316 borrower gone with no address
317
318 =head3 CARD_LOST
319
320 borrower declared it's card lost
321
322 =head3 DEBARRED
323
324 borrower debarred
325
326 =head3 UNKNOWN_BARCODE
327
328 barcode unknown
329
330 =head3 NOT_FOR_LOAN
331
332 item is not for loan
333
334 =head3 WTHDRAWN
335
336 item withdrawn.
337
338 =head3 RESTRICTED
339
340 item is restricted (set by ??)
341
342 C<$issuingimpossible> a reference to a hash. It contains reasons why issuing is impossible.
343 Possible values are :
344
345 =head3 DEBT
346
347 borrower has debts.
348
349 =head3 RENEW_ISSUE
350
351 renewing, not issuing
352
353 =head3 ISSUED_TO_ANOTHER
354
355 issued to someone else.
356
357 =head3 RESERVED
358
359 reserved for someone else.
360
361 =head3 INVALID_DATE
362
363 sticky due date is invalid
364
365 =head3 TOO_MANY
366
367 if the borrower borrows to much things
368
369 =cut
370
371 # check if a book can be issued.
372
373
374 sub TooMany {
375     my $borrower        = shift;
376     my $biblionumber = shift;
377         my $item                = shift;
378     my $cat_borrower    = $borrower->{'categorycode'};
379     my $dbh             = C4::Context->dbh;
380         my $branch;
381         # Get which branchcode we need
382         if (C4::Context->preference('CircControl') eq 'PickupLibary'){
383                 $branch = C4::Context->userenv->{'branchcode'}; 
384         }
385         elsif (C4::Context->preference('CircControl') eq 'PatronLibary'){
386         $branch = $borrower->{'branchcode'}; 
387         }
388         else {
389                 # items home library
390                 $branch = $item->{'homebranch'};
391         }
392         my $type = (C4::Context->preference('item-level_itypes')) 
393                         ? $item->{'itype'}         # item-level
394                         : $item->{'itemtype'};     # biblio-level
395   
396         my $sth =
397       $dbh->prepare(
398                 'SELECT * FROM issuingrules 
399                         WHERE categorycode = ? 
400                             AND itemtype = ? 
401                             AND branchcode = ?'
402       );
403
404     my $query2 = "SELECT  COUNT(*) FROM issues i, biblioitems s1, items s2 
405                 WHERE i.borrowernumber = ? 
406                     AND i.returndate IS NULL 
407                     AND i.itemnumber = s2.itemnumber 
408                     AND s1.biblioitemnumber = s2.biblioitemnumber";
409     if (C4::Context->preference('item-level_itypes')){
410            $query2.=" AND s2.itype=? ";
411     } else { 
412            $query2.=" AND s1.itemtype= ? ";
413     }
414     my $sth2=  $dbh->prepare($query2);
415     my $sth3 =
416       $dbh->prepare(
417             'SELECT COUNT(*) FROM issues
418                 WHERE borrowernumber = ?
419                     AND returndate IS NULL'
420             );
421     my $alreadyissued;
422
423     # check the 3 parameters (branch / itemtype / category code
424     $sth->execute( $cat_borrower, $type, $branch );
425     my $result = $sth->fetchrow_hashref;
426 #     warn "$cat_borrower, $type, $branch = ".Data::Dumper::Dumper($result);
427
428     if ( $result->{maxissueqty} ne '' ) {
429 #         warn "checking on everything set";
430         $sth2->execute( $borrower->{'borrowernumber'}, $type );
431         my $alreadyissued = $sth2->fetchrow;
432         if ( $result->{'maxissueqty'} <= $alreadyissued ) {
433             return ( "$alreadyissued / ".( $result->{maxissueqty} + 0 )." (rule on branch/category/itemtype failed)" );
434         }
435         # now checking for total
436         $sth->execute( $cat_borrower, '', $branch );
437         my $result = $sth->fetchrow_hashref;
438         if ( $result->{maxissueqty} ne '*' ) {
439             $sth2->execute( $borrower->{'borrowernumber'}, $type );
440             my $alreadyissued = $sth2->fetchrow;
441             if ( $result->{'maxissueqty'} <= $alreadyissued ) {
442                 return ( "$alreadyissued / ".( $result->{maxissueqty} + 0 )." (rule on branch/category/total failed)"  );
443             }
444         }
445     }
446
447     # check the 2 parameters (branch / itemtype / default categorycode
448     $sth->execute( '*', $type, $branch );
449     $result = $sth->fetchrow_hashref;
450 #     warn "*, $type, $branch = ".Data::Dumper::Dumper($result);
451
452     if ( $result->{maxissueqty} ne '' ) {
453 #         warn "checking on 2 parameters (default categorycode)";
454         $sth2->execute( $borrower->{'borrowernumber'}, $type );
455         my $alreadyissued = $sth2->fetchrow;
456         if ( $result->{'maxissueqty'} <= $alreadyissued ) {
457             return ( "$alreadyissued / ".( $result->{maxissueqty} + 0 )." (rule on branch / default category / itemtype failed)"  );
458         }
459         # now checking for total
460         $sth->execute( '*', '*', $branch );
461         my $result = $sth->fetchrow_hashref;
462         if ( $result->{maxissueqty} ne '' ) {
463             $sth2->execute( $borrower->{'borrowernumber'}, $type );
464             my $alreadyissued = $sth2->fetchrow;
465             if ( $result->{'maxissueqty'} <= $alreadyissued ) {
466                 return ( "$alreadyissued / ".( $result->{maxissueqty} + 0 )." (rule on branch / default category / total failed)" );
467             }
468         }
469     }
470     
471     # check the 1 parameters (default branch / itemtype / categorycode
472     $sth->execute( $cat_borrower, $type, '*' );
473     $result = $sth->fetchrow_hashref;
474 #     warn "$cat_borrower, $type, * = ".Data::Dumper::Dumper($result);
475     
476     if ( $result->{maxissueqty} ne '' ) {
477 #         warn "checking on 1 parameter (default branch + categorycode)";
478         $sth2->execute( $borrower->{'borrowernumber'}, $type );
479         my $alreadyissued = $sth2->fetchrow;
480         if ( $result->{'maxissueqty'} <= $alreadyissued ) {
481             return ( "$alreadyissued / ".( $result->{maxissueqty} + 0 )." (rule on default branch/category/itemtype failed)"  );
482         }
483         # now checking for total
484         $sth->execute( $cat_borrower, '*', '*' );
485         my $result = $sth->fetchrow_hashref;
486         if ( $result->{maxissueqty} ne '' ) {
487             $sth2->execute( $borrower->{'borrowernumber'}, $type );
488             my $alreadyissued = $sth2->fetchrow;
489             if ( $result->{'maxissueqty'} <= $alreadyissued ) {
490                 return ( "$alreadyissued / ".( $result->{maxissueqty} + 0 )." (rule on default branch / category / total failed)"  );
491             }
492         }
493     }
494
495     # check the 0 parameters (default branch / itemtype / default categorycode
496     $sth->execute( '*', $type, '*' );
497     $result = $sth->fetchrow_hashref;
498 #     warn "*, $type, * = ".Data::Dumper::Dumper($result);
499
500     if ( $result->{maxissueqty} ne '' ) {
501 #         warn "checking on default branch and default categorycode";
502         $sth2->execute( $borrower->{'borrowernumber'}, $type );
503         my $alreadyissued = $sth2->fetchrow;
504         if ( $result->{'maxissueqty'} <= $alreadyissued ) {
505             return ( "$alreadyissued / ".( $result->{maxissueqty} + 0 )." (rule on default branch / default category / itemtype failed)"  );
506         }
507         }
508     # now checking for total
509     $sth->execute( '*', '*', '*' );
510     $result = $sth->fetchrow_hashref;
511     if ( $result->{maxissueqty} ne '' ) {
512                 warn "checking total";
513                 $sth2->execute( $borrower->{'borrowernumber'}, $type );
514                 my $alreadyissued = $sth2->fetchrow;
515                 if ( $result->{'maxissueqty'} <= $alreadyissued ) {
516                         return ( "$alreadyissued / ".( $result->{maxissueqty} + 0 )." (rule on default branch / default category / total failed)"  );
517                 }
518         }
519
520     # OK, the patron can issue !!!
521     return;
522 }
523
524 =head2 itemissues
525
526   @issues = &itemissues($biblioitemnumber, $biblio);
527
528 Looks up information about who has borrowed the bookZ<>(s) with the
529 given biblioitemnumber.
530
531 C<$biblio> is ignored.
532
533 C<&itemissues> returns an array of references-to-hash. The keys
534 include the fields from the C<items> table in the Koha database.
535 Additional keys include:
536
537 =over 4
538
539 =item C<date_due>
540
541 If the item is currently on loan, this gives the due date.
542
543 If the item is not on loan, then this is either "Available" or
544 "Cancelled", if the item has been withdrawn.
545
546 =item C<card>
547
548 If the item is currently on loan, this gives the card number of the
549 patron who currently has the item.
550
551 =item C<timestamp0>, C<timestamp1>, C<timestamp2>
552
553 These give the timestamp for the last three times the item was
554 borrowed.
555
556 =item C<card0>, C<card1>, C<card2>
557
558 The card number of the last three patrons who borrowed this item.
559
560 =item C<borrower0>, C<borrower1>, C<borrower2>
561
562 The borrower number of the last three patrons who borrowed this item.
563
564 =back
565
566 =cut
567
568 #'
569 sub itemissues {
570     my ( $bibitem, $biblio ) = @_;
571     my $dbh = C4::Context->dbh;
572     my $sth =
573       $dbh->prepare("Select * from items where items.biblioitemnumber = ?")
574       || die $dbh->errstr;
575     my $i = 0;
576     my @results;
577
578     $sth->execute($bibitem) || die $sth->errstr;
579
580     while ( my $data = $sth->fetchrow_hashref ) {
581
582         # Find out who currently has this item.
583         # FIXME - Wouldn't it be better to do this as a left join of
584         # some sort? Currently, this code assumes that if
585         # fetchrow_hashref() fails, then the book is on the shelf.
586         # fetchrow_hashref() can fail for any number of reasons (e.g.,
587         # database server crash), not just because no items match the
588         # search criteria.
589         my $sth2 = $dbh->prepare(
590             "SELECT * FROM issues
591                 LEFT JOIN borrowers ON issues.borrowernumber = borrowers.borrowernumber
592                 WHERE itemnumber = ?
593                     AND returndate IS NULL
594             "
595         );
596
597         $sth2->execute( $data->{'itemnumber'} );
598         if ( my $data2 = $sth2->fetchrow_hashref ) {
599             $data->{'date_due'} = $data2->{'date_due'};
600             $data->{'card'}     = $data2->{'cardnumber'};
601             $data->{'borrower'} = $data2->{'borrowernumber'};
602         }
603         else {
604             $data->{'date_due'} = ($data->{'wthdrawn'} eq '1') ? 'Cancelled' : 'Available';
605         }
606
607         $sth2->finish;
608
609         # Find the last 3 people who borrowed this item.
610         $sth2 = $dbh->prepare(
611             "SELECT * FROM issues
612                 LEFT JOIN borrowers ON  issues.borrowernumber = borrowers.borrowernumber
613                 WHERE itemnumber = ?
614                 AND returndate IS NOT NULL
615                 ORDER BY returndate DESC,timestamp DESC"
616         );
617
618         $sth2->execute( $data->{'itemnumber'} );
619         for ( my $i2 = 0 ; $i2 < 2 ; $i2++ )
620         {    # FIXME : error if there is less than 3 pple borrowing this item
621             if ( my $data2 = $sth2->fetchrow_hashref ) {
622                 $data->{"timestamp$i2"} = $data2->{'timestamp'};
623                 $data->{"card$i2"}      = $data2->{'cardnumber'};
624                 $data->{"borrower$i2"}  = $data2->{'borrowernumber'};
625             }    # if
626         }    # for
627
628         $sth2->finish;
629         $results[$i] = $data;
630         $i++;
631     }
632
633     $sth->finish;
634     return (@results);
635 }
636
637 =head2 CanBookBeIssued
638
639 ( $issuingimpossible, $needsconfirmation ) = 
640         CanBookBeIssued( $borrower, $barcode, $duedatespec, $inprocess );
641 C<$duedatespec> is a C4::Dates object.
642 C<$issuingimpossible> and C<$needsconfirmation> are some hashref.
643
644 =cut
645
646 sub CanBookBeIssued {
647     my ( $borrower, $barcode, $duedate, $inprocess ) = @_;
648     my %needsconfirmation;    # filled with problems that needs confirmations
649     my %issuingimpossible;    # filled with problems that causes the issue to be IMPOSSIBLE
650     my $item = GetItem(GetItemnumberFromBarcode( $barcode ));
651     my $issue = GetItemIssue($item->{itemnumber});
652         my $biblioitem = GetBiblioItemData($item->{biblioitemnumber});
653         $item->{'itemtype'}=$biblioitem->{'itemtype'};
654     my $dbh             = C4::Context->dbh;
655
656     #
657     # DUE DATE is OK ? -- should already have checked.
658     #
659     #$issuingimpossible{INVALID_DATE} = 1 unless ($duedate);
660
661     #
662     # BORROWER STATUS
663     #
664     if ( $borrower->{flags}->{GNA} ) {
665         $issuingimpossible{GNA} = 1;
666     }
667     if ( $borrower->{flags}->{'LOST'} ) {
668         $issuingimpossible{CARD_LOST} = 1;
669     }
670     if ( $borrower->{flags}->{'DBARRED'} ) {
671         $issuingimpossible{DEBARRED} = 1;
672     }
673     if ( $borrower->{'dateexpiry'} eq '0000-00-00') {
674         $issuingimpossible{EXPIRED} = 1;
675     } else {
676         my @expirydate=  split /-/,$borrower->{'dateexpiry'};
677         if($expirydate[0]==0 || $expirydate[1]==0|| $expirydate[2]==0 ||
678             Date_to_Days(Today) > Date_to_Days( @expirydate )) {
679             $issuingimpossible{EXPIRED} = 1;                                   
680         }
681     }
682     #
683     # BORROWER STATUS
684     #
685
686     # DEBTS
687     my ($amount) =
688       C4::Members::GetMemberAccountRecords( $borrower->{'borrowernumber'}, '' && $duedate->output('iso') );
689     if ( C4::Context->preference("IssuingInProcess") ) {
690         my $amountlimit = C4::Context->preference("noissuescharge");
691         if ( $amount > $amountlimit && !$inprocess ) {
692             $issuingimpossible{DEBT} = sprintf( "%.2f", $amount );
693         }
694         elsif ( $amount <= $amountlimit && !$inprocess ) {
695             $needsconfirmation{DEBT} = sprintf( "%.2f", $amount );
696         }
697     }
698     else {
699         if ( $amount > 0 ) {
700             $needsconfirmation{DEBT} = $amount;
701         }
702     }
703
704     #
705     # JB34 CHECKS IF BORROWERS DONT HAVE ISSUE TOO MANY BOOKS
706     #
707         my $toomany = TooMany( $borrower, $item->{biblionumber}, $item );
708     $needsconfirmation{TOO_MANY} = $toomany if $toomany;
709
710     #
711     # ITEM CHECKING
712     #
713     unless ( $item->{barcode} ) {
714         $issuingimpossible{UNKNOWN_BARCODE} = 1;
715     }
716     if (   $item->{'notforloan'}
717         && $item->{'notforloan'} > 0 )
718     {
719         $issuingimpossible{NOT_FOR_LOAN} = 1;
720     }
721         elsif ( !$item->{'notforloan'} ){
722                 # we have to check itemtypes.notforloan also
723                 if (C4::Context->preference('item-level_itypes')){
724                         # this should probably be a subroutine
725                         my $sth = $dbh->prepare("SELECT notforloan FROM itemtypes WHERE itemtype = ?");
726                         $sth->execute($item->{'itemtype'});
727                         my $notforloan=$sth->fetchrow_hashref();
728                         $sth->finish();
729                         if ($notforloan->{'notforloan'} == 1){
730                                 $issuingimpossible{NOT_FOR_LOAN} = 1;                           
731                         }
732                 }
733                 elsif ($biblioitem->{'notforloan'} == 1){
734                         $issuingimpossible{NOT_FOR_LOAN} = 1;
735                 }
736         }
737     if ( $item->{'wthdrawn'} && $item->{'wthdrawn'} == 1 )
738     {
739         $issuingimpossible{WTHDRAWN} = 1;
740     }
741     if (   $item->{'restricted'}
742         && $item->{'restricted'} == 1 )
743     {
744         $issuingimpossible{RESTRICTED} = 1;
745     }
746     if ( C4::Context->preference("IndependantBranches") ) {
747         my $userenv = C4::Context->userenv;
748         if ( ($userenv) && ( $userenv->{flags} != 1 ) ) {
749             $issuingimpossible{NOTSAMEBRANCH} = 1
750               if ( $item->{C4::Context->preference("HomeOrHoldingbranch")} ne $userenv->{branch} );
751         }
752     }
753
754     #
755     # CHECK IF BOOK ALREADY ISSUED TO THIS BORROWER
756     #
757     if ( $issue->{borrowernumber} && $issue->{borrowernumber} eq $borrower->{'borrowernumber'} )
758     {
759
760         # Already issued to current borrower. Ask whether the loan should
761         # be renewed.
762         my ($CanBookBeRenewed,$renewerror) = CanBookBeRenewed(
763             $borrower->{'borrowernumber'},
764             $item->{'itemnumber'}
765         );
766         if ( $CanBookBeRenewed == 0 ) {    # no more renewals allowed
767             $issuingimpossible{NO_MORE_RENEWALS} = 1;
768         }
769         else {
770             $needsconfirmation{RENEW_ISSUE} = 1;
771         }
772     }
773     elsif ($issue->{borrowernumber}) {
774
775         # issued to someone else
776         my $currborinfo = GetMemberDetails( $issue->{borrowernumber} );
777
778 #        warn "=>.$currborinfo->{'firstname'} $currborinfo->{'surname'} ($currborinfo->{'cardnumber'})";
779         $needsconfirmation{ISSUED_TO_ANOTHER} =
780 "$currborinfo->{'reservedate'} : $currborinfo->{'firstname'} $currborinfo->{'surname'} ($currborinfo->{'cardnumber'})";
781     }
782
783     # See if the item is on reserve.
784     my ( $restype, $res ) = C4::Reserves::CheckReserves( $item->{'itemnumber'} );
785     if ($restype) {
786                 my $resbor = $res->{'borrowernumber'};
787                 my ( $resborrower, $flags ) = GetMemberDetails( $resbor, 0 );
788                 my $branches  = GetBranches();
789                 my $branchname = $branches->{ $res->{'branchcode'} }->{'branchname'};
790         if ( $resbor ne $borrower->{'borrowernumber'} && $restype eq "Waiting" )
791         {
792             # The item is on reserve and waiting, but has been
793             # reserved by some other patron.
794             $needsconfirmation{RESERVE_WAITING} =
795 "$resborrower->{'firstname'} $resborrower->{'surname'} ($resborrower->{'cardnumber'}, $branchname)";
796         }
797         elsif ( $restype eq "Reserved" ) {
798             # The item is on reserve for someone else.
799             $needsconfirmation{RESERVED} =
800 "$res->{'reservedate'} : $resborrower->{'firstname'} $resborrower->{'surname'} ($resborrower->{'cardnumber'})";
801         }
802     }
803     if ( C4::Context->preference("LibraryName") eq "Horowhenua Library Trust" ) {
804         if ( $borrower->{'categorycode'} eq 'W' ) {
805             my %emptyhash;
806             return ( \%emptyhash, \%needsconfirmation );
807         }
808         }
809         return ( \%issuingimpossible, \%needsconfirmation );
810 }
811
812 =head2 AddIssue
813
814 Issue a book. Does no check, they are done in CanBookBeIssued. If we reach this sub, it means the user confirmed if needed.
815
816 &AddIssue($borrower,$barcode,$date)
817
818 =over 4
819
820 =item C<$borrower> hash with borrower informations (from GetMemberDetails)
821
822 =item C<$barcode> is the bar code of the book being issued.
823
824 =item C<$date> contains the max date of return. calculated if empty.
825
826 AddIssue does the following things :
827 - step 01: check that there is a borrowernumber & a barcode provided
828 - check for RENEWAL (book issued & being issued to the same patron)
829     - renewal YES = Calculate Charge & renew
830     - renewal NO  = 
831         * BOOK ACTUALLY ISSUED ? do a return if book is actually issued (but to someone else)
832         * RESERVE PLACED ?
833             - fill reserve if reserve to this patron
834             - cancel reserve or not, otherwise
835         * TRANSFERT PENDING ?
836             - complete the transfert
837         * ISSUE THE BOOK
838
839 =back
840
841 =cut
842
843 sub AddIssue {
844     my ( $borrower, $barcode, $date, $cancelreserve ) = @_;
845     my $dbh = C4::Context->dbh;
846         my $barcodecheck=CheckValidBarcode($barcode);
847         if ($borrower and $barcode and $barcodecheck ne '0'){
848                 # find which item we issue
849                 my $item = GetItem('', $barcode);
850                 my $datedue; 
851                 
852                 my $branch;
853                 # Get which branchcode we need
854                 if (C4::Context->preference('CircControl') eq 'PickupLibary'){
855                         $branch = C4::Context->userenv->{'branchcode'}; 
856                 }
857                 elsif (C4::Context->preference('CircControl') eq 'PatronLibary'){
858                         $branch = $borrower->{'branchcode'}; 
859                 }
860                 else {
861                         # items home library
862                         $branch = $item->{'homebranch'};
863                 }
864                 
865                 # get actual issuing if there is one
866                 my $actualissue = GetItemIssue( $item->{itemnumber});
867                 
868                 # get biblioinformation for this item
869                 my $biblio = GetBiblioFromItemNumber($item->{itemnumber});
870                 
871                 #
872                 # check if we just renew the issue.
873                 #
874                 if ( $actualissue->{borrowernumber} eq $borrower->{'borrowernumber'} ) {
875                         AddRenewal(
876                                 $borrower->{'borrowernumber'},
877                                 $item->{'itemnumber'},
878                                 $branch,
879                                 $date
880                         );
881
882                 }
883                 else {
884         # it's NOT a renewal
885                         if ( $actualissue->{borrowernumber}) {
886                                 # This book is currently on loan, but not to the person
887                                 # who wants to borrow it now. mark it returned before issuing to the new borrower
888                                 AddReturn(
889                                         $item->{'barcode'},
890                                         C4::Context->userenv->{'branch'}
891                                 );
892                         }
893
894                         # See if the item is on reserve.
895                         my ( $restype, $res ) =
896                           C4::Reserves::CheckReserves( $item->{'itemnumber'} );
897                         if ($restype) {
898                                 my $resbor = $res->{'borrowernumber'};
899                                 if ( $resbor eq $borrower->{'borrowernumber'} ) {
900
901                                         # The item is reserved by the current patron
902                                         ModReserveFill($res);
903                                 }
904                                 elsif ( $restype eq "Waiting" ) {
905
906                                         # warn "Waiting";
907                                         # The item is on reserve and waiting, but has been
908                                         # reserved by some other patron.
909                                         my ( $resborrower, $flags ) = GetMemberDetails( $resbor, 0 );
910                                         my $branches   = GetBranches();
911                                         my $branchname =
912                                           $branches->{ $res->{'branchcode'} }->{'branchname'};
913                                 }
914                                 elsif ( $restype eq "Reserved" ) {
915
916                                         # warn "Reserved";
917                                         # The item is reserved by someone else.
918                                         my ( $resborrower, $flags ) =
919                                           GetMemberDetails( $resbor, 0 );
920                                         my $branches   = GetBranches();
921                                         my $branchname =  $branches->{ $res->{'branchcode'} }->{'branchname'};
922                                         if ($cancelreserve) { # cancel reserves on this item
923                                                 CancelReserve( 0, $res->{'itemnumber'},
924                                                         $res->{'borrowernumber'} );
925                                         }
926                                 }
927                                 if ($cancelreserve) {
928                                         CancelReserve( $res->{'biblionumber'}, 0,
929                     $res->{'borrowernumber'} );
930                                 }
931                                 else {
932                                         # set waiting reserve to first in reserve queue as book isn't waiting now
933                                         ModReserve(1,
934                                                 $res->{'biblionumber'},
935                                                 $res->{'borrowernumber'},
936                                                 $res->{'branchcode'}
937                                         );
938                                 }
939                         }
940
941                         # Starting process for transfer job (checking transfert and validate it if we have one)
942             my ($datesent) = GetTransfers($item->{'itemnumber'});
943             if ($datesent) {
944         #       updating line of branchtranfert to finish it, and changing the to branch value, implement a comment for lisibility of this case (maybe for stats ....)
945             my $sth =
946                     $dbh->prepare(
947                     "UPDATE branchtransfers 
948                         SET datearrived = now(),
949                         tobranch = ?,
950                         comments = 'Forced branchtransfert'
951                     WHERE itemnumber= ? AND datearrived IS NULL"
952                     );
953                     $sth->execute(C4::Context->userenv->{'branch'},$item->{'itemnumber'});
954                     $sth->finish;
955             }
956
957         # Record in the database the fact that the book was issued.
958         my $sth =
959           $dbh->prepare(
960                 "INSERT INTO issues 
961                     (borrowernumber, itemnumber,issuedate, date_due, branchcode)
962                 VALUES (?,?,?,?,?)"
963           );
964                 my $dateduef;
965         if ($date) {
966             $dateduef = $date;
967         } else {
968                         my $itype=(C4::Context->preference('item-level_itypes')) ?  $biblio->{'itype'} : $biblio->{'itemtype'} ;
969                 my $loanlength = GetLoanLength(
970                     $borrower->{'categorycode'},
971                     $itype,
972                 $branch
973                 );
974                 $datedue  = time + ($loanlength) * 86400;
975                 my @datearr  = localtime($datedue);
976                         $dateduef = C4::Dates->new( sprintf("%04d-%02d-%02d", 1900 + $datearr[5], $datearr[4] + 1, $datearr[3]), 'iso');
977                         $dateduef=CheckValidDatedue($dateduef,$item->{'itemnumber'},C4::Context->userenv->{'branch'});
978                 
979                 # if ReturnBeforeExpiry ON the datedue can't be after borrower expirydate
980                 if ( C4::Context->preference('ReturnBeforeExpiry') && $dateduef->output('iso') gt $borrower->{dateexpiry} ) {
981                     $dateduef = C4::Dates->new($borrower->{dateexpiry},'iso');
982                 }
983         };
984                 $sth->execute(
985             $borrower->{'borrowernumber'},
986             $item->{'itemnumber'},
987             strftime( "%Y-%m-%d", localtime ),$dateduef->output('iso'), C4::Context->userenv->{'branch'}
988         );
989         $sth->finish;
990         $item->{'issues'}++;
991         ModItem({ issues           => $item->{'issues'},
992                   holdingbranch    => C4::Context->userenv->{'branch'},
993                   itemlost         => 0,
994                   datelastborrowed => C4::Dates->new()->output('iso'),
995                   onloan           => $dateduef->output('iso'),
996                 }, $item->{'biblionumber'}, $item->{'itemnumber'});
997         ModDateLastSeen( $item->{'itemnumber'} );
998         
999         # If it costs to borrow this book, charge it to the patron's account.
1000         my ( $charge, $itemtype ) = GetIssuingCharges(
1001             $item->{'itemnumber'},
1002             $borrower->{'borrowernumber'}
1003         );
1004         if ( $charge > 0 ) {
1005             AddIssuingCharge(
1006                 $item->{'itemnumber'},
1007                 $borrower->{'borrowernumber'}, $charge
1008             );
1009             $item->{'charge'} = $charge;
1010         }
1011
1012         # Record the fact that this book was issued.
1013         &UpdateStats(
1014             C4::Context->userenv->{'branch'},
1015             'issue',                        $charge,
1016             '',                             $item->{'itemnumber'},
1017             $item->{'itemtype'}, $borrower->{'borrowernumber'}
1018         );
1019     }
1020     
1021     &logaction(C4::Context->userenv->{'number'},"CIRCULATION","ISSUE",$borrower->{'borrowernumber'},$biblio->{'biblionumber'}) 
1022         if C4::Context->preference("IssueLog");
1023     return ($datedue);
1024   }
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 =
1039       $dbh->prepare(
1040 "select issuelength from issuingrules where categorycode=? and itemtype=? and branchcode=? and issuelength is not null"
1041       );
1042 # warn "in get loan lenght $borrowertype $itemtype $branchcode ";
1043 # try to find issuelength & return the 1st available.
1044 # check with borrowertype, itemtype and branchcode, then without one of those parameters
1045     $sth->execute( $borrowertype, $itemtype, $branchcode );
1046     my $loanlength = $sth->fetchrow_hashref;
1047     return $loanlength->{issuelength}
1048       if defined($loanlength) && $loanlength->{issuelength} ne 'NULL';
1049
1050     $sth->execute( $borrowertype, $itemtype, "*" );
1051     $loanlength = $sth->fetchrow_hashref;
1052     return $loanlength->{issuelength}
1053       if defined($loanlength) && $loanlength->{issuelength} ne 'NULL';
1054
1055     $sth->execute( $borrowertype, "*", $branchcode );
1056     $loanlength = $sth->fetchrow_hashref;
1057     return $loanlength->{issuelength}
1058       if defined($loanlength) && $loanlength->{issuelength} ne 'NULL';
1059
1060     $sth->execute( "*", $itemtype, $branchcode );
1061     $loanlength = $sth->fetchrow_hashref;
1062     return $loanlength->{issuelength}
1063       if defined($loanlength) && $loanlength->{issuelength} ne 'NULL';
1064
1065     $sth->execute( $borrowertype, "*", "*" );
1066     $loanlength = $sth->fetchrow_hashref;
1067     return $loanlength->{issuelength}
1068       if defined($loanlength) && $loanlength->{issuelength} ne 'NULL';
1069
1070     $sth->execute( "*", "*", $branchcode );
1071     $loanlength = $sth->fetchrow_hashref;
1072     return $loanlength->{issuelength}
1073       if defined($loanlength) && $loanlength->{issuelength} ne 'NULL';
1074
1075     $sth->execute( "*", $itemtype, "*" );
1076     $loanlength = $sth->fetchrow_hashref;
1077     return $loanlength->{issuelength}
1078       if defined($loanlength) && $loanlength->{issuelength} ne 'NULL';
1079
1080     $sth->execute( "*", "*", "*" );
1081     $loanlength = $sth->fetchrow_hashref;
1082     return $loanlength->{issuelength}
1083       if defined($loanlength) && $loanlength->{issuelength} ne 'NULL';
1084
1085     # if no rule is set => 21 days (hardcoded)
1086     return 21;
1087 }
1088
1089 =head2 AddReturn
1090
1091 ($doreturn, $messages, $iteminformation, $borrower) =
1092     &AddReturn($barcode, $branch, $exemptfine);
1093
1094 Returns a book.
1095
1096 C<$barcode> is the bar code of the book being returned. C<$branch> is
1097 the code of the branch where the book is being returned.  C<$exemptfine>
1098 indicates that overdue charges for the item will not be applied.
1099
1100 C<&AddReturn> returns a list of four items:
1101
1102 C<$doreturn> is true iff the return succeeded.
1103
1104 C<$messages> is a reference-to-hash giving the reason for failure:
1105
1106 =over 4
1107
1108 =item C<BadBarcode>
1109
1110 No item with this barcode exists. The value is C<$barcode>.
1111
1112 =item C<NotIssued>
1113
1114 The book is not currently on loan. The value is C<$barcode>.
1115
1116 =item C<IsPermanent>
1117
1118 The book's home branch is a permanent collection. If you have borrowed
1119 this book, you are not allowed to return it. The value is the code for
1120 the book's home branch.
1121
1122 =item C<wthdrawn>
1123
1124 This book has been withdrawn/cancelled. The value should be ignored.
1125
1126 =item C<ResFound>
1127
1128 The item was reserved. The value is a reference-to-hash whose keys are
1129 fields from the reserves table of the Koha database, and
1130 C<biblioitemnumber>. It also has the key C<ResFound>, whose value is
1131 either C<Waiting>, C<Reserved>, or 0.
1132
1133 =back
1134
1135 C<$borrower> is a reference-to-hash, giving information about the
1136 patron who last borrowed the book.
1137
1138 =cut
1139
1140 sub AddReturn {
1141     my ( $barcode, $branch, $exemptfine ) = @_;
1142     my $dbh      = C4::Context->dbh;
1143     my $messages;
1144     my $doreturn = 1;
1145     my $borrower;
1146     my $validTransfert = 0;
1147     my $reserveDone = 0;
1148     
1149     # get information on item
1150     my $iteminformation = GetItemIssue( GetItemnumberFromBarcode($barcode));
1151     my $biblio = GetBiblioItemData($iteminformation->{'biblioitemnumber'});
1152 #     use Data::Dumper;warn Data::Dumper::Dumper($iteminformation);  
1153     unless ($iteminformation->{'itemnumber'} ) {
1154         $messages->{'BadBarcode'} = $barcode;
1155         $doreturn = 0;
1156     } else {
1157         # find the borrower
1158         if ( ( not $iteminformation->{borrowernumber} ) && $doreturn ) {
1159             $messages->{'NotIssued'} = $barcode;
1160             $doreturn = 0;
1161         }
1162     
1163         # check if the book is in a permanent collection....
1164         my $hbr      = $iteminformation->{'homebranch'};
1165         my $branches = GetBranches();
1166         if ( $hbr && $branches->{$hbr}->{'PE'} ) {
1167             $messages->{'IsPermanent'} = $hbr;
1168         }
1169                 
1170                     # if independent branches are on and returning to different branch, refuse the return
1171         if ($hbr ne C4::Context->userenv->{'branch'} && C4::Context->preference("IndependantBranches")){
1172                           $messages->{'Wrongbranch'} = 1;
1173                           $doreturn=0;
1174                     }
1175                         
1176         # check that the book has been cancelled
1177         if ( $iteminformation->{'wthdrawn'} ) {
1178             $messages->{'wthdrawn'} = 1;
1179             $doreturn = 0;
1180         }
1181     
1182     #     new op dev : if the book returned in an other branch update the holding branch
1183     
1184     # update issues, thereby returning book (should push this out into another subroutine
1185         $borrower = C4::Members::GetMemberDetails( $iteminformation->{borrowernumber}, 0 );
1186     
1187     # case of a return of document (deal with issues and holdingbranch)
1188     
1189         if ($doreturn) {
1190             my $sth =
1191             $dbh->prepare(
1192     "UPDATE issues SET returndate = now() WHERE (borrowernumber = ?) AND (itemnumber = ?) AND (returndate IS NULL)"
1193             );
1194             $sth->execute( $borrower->{'borrowernumber'},
1195                 $iteminformation->{'itemnumber'} );
1196             $messages->{'WasReturned'} = 1;    # FIXME is the "= 1" right?
1197         }
1198     
1199     # continue to deal with returns cases, but not only if we have an issue
1200     
1201         # the holdingbranch is updated if the document is returned in an other location .
1202         if ( $iteminformation->{'holdingbranch'} ne C4::Context->userenv->{'branch'} ) {
1203                         UpdateHoldingbranch(C4::Context->userenv->{'branch'},$iteminformation->{'itemnumber'}); 
1204                         #               reload iteminformation holdingbranch with the userenv value
1205                         $iteminformation->{'holdingbranch'} = C4::Context->userenv->{'branch'};
1206         }
1207         ModDateLastSeen( $iteminformation->{'itemnumber'} );
1208         ModItem({ onloan => undef }, $biblio->{'biblionumber'}, $iteminformation->{'itemnumber'});
1209                     
1210                     if ($iteminformation->{borrowernumber}){
1211                           ($borrower) = C4::Members::GetMemberDetails( $iteminformation->{borrowernumber}, 0 );
1212         }       
1213         # fix up the accounts.....
1214         if ( $iteminformation->{'itemlost'} ) {
1215             $messages->{'WasLost'} = 1;
1216         }
1217     
1218     # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
1219     #     check if we have a transfer for this document
1220         my ($datesent,$frombranch,$tobranch) = GetTransfers( $iteminformation->{'itemnumber'} );
1221     
1222     #     if we have a transfer to do, we update the line of transfers with the datearrived
1223         if ($datesent) {
1224             if ( $tobranch eq C4::Context->userenv->{'branch'} ) {
1225                     my $sth =
1226                     $dbh->prepare(
1227                             "UPDATE branchtransfers SET datearrived = now() WHERE itemnumber= ? AND datearrived IS NULL"
1228                     );
1229                     $sth->execute( $iteminformation->{'itemnumber'} );
1230                     $sth->finish;
1231     #         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'
1232             C4::Reserves::ModReserveStatus( $iteminformation->{'itemnumber'},'W' );
1233             }
1234         else {
1235             $messages->{'WrongTransfer'} = $tobranch;
1236             $messages->{'WrongTransferItem'} = $iteminformation->{'itemnumber'};
1237         }
1238         $validTransfert = 1;
1239         }
1240     
1241     # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # 
1242         # fix up the accounts.....
1243         if ($iteminformation->{'itemlost'}) {
1244                 FixAccountForLostAndReturned($iteminformation, $borrower);
1245                 $messages->{'WasLost'} = 1;
1246         }
1247         # fix up the overdues in accounts...
1248         FixOverduesOnReturn( $borrower->{'borrowernumber'},
1249             $iteminformation->{'itemnumber'}, $exemptfine );
1250     
1251     # find reserves.....
1252     #     if we don't have a reserve with the status W, we launch the Checkreserves routine
1253         my ( $resfound, $resrec ) =
1254         C4::Reserves::CheckReserves( $iteminformation->{'itemnumber'} );
1255         if ($resfound) {
1256             $resrec->{'ResFound'}   = $resfound;
1257             $messages->{'ResFound'} = $resrec;
1258             $reserveDone = 1;
1259         }
1260     
1261         # update stats?
1262         # Record the fact that this book was returned.
1263         UpdateStats(
1264             $branch, 'return', '0', '',
1265             $iteminformation->{'itemnumber'},
1266             $biblio->{'itemtype'},
1267             $borrower->{'borrowernumber'}
1268         );
1269         
1270         &logaction(C4::Context->userenv->{'number'},"CIRCULATION","RETURN",$iteminformation->{borrowernumber},$iteminformation->{'biblionumber'}) 
1271             if C4::Context->preference("ReturnLog");
1272         
1273         #adding message if holdingbranch is non equal a userenv branch to return the document to homebranch
1274         #we check, if we don't have reserv or transfert for this document, if not, return it to homebranch .
1275         
1276         if ( ($iteminformation->{'holdingbranch'} ne $iteminformation->{'homebranch'}) and not $messages->{'WrongTransfer'} and ($validTransfert ne 1) and ($reserveDone ne 1) ){
1277                         if (C4::Context->preference("AutomaticItemReturn") == 1) {
1278                                 ModItemTransfer($iteminformation->{'itemnumber'}, C4::Context->userenv->{'branch'}, $iteminformation->{'homebranch'});
1279                                 $messages->{'WasTransfered'} = 1;
1280                         }
1281                         else {
1282                                 $messages->{'NeedsTransfer'} = 1;
1283                         }
1284         }
1285     }
1286     return ( $doreturn, $messages, $iteminformation, $borrower );
1287 }
1288
1289 =head2 FixOverduesOnReturn
1290
1291     &FixOverduesOnReturn($brn,$itm, $exemptfine);
1292
1293 C<$brn> borrowernumber
1294
1295 C<$itm> itemnumber
1296
1297 internal function, called only by AddReturn
1298
1299 =cut
1300
1301 sub FixOverduesOnReturn {
1302     my ( $borrowernumber, $item, $exemptfine ) = @_;
1303     my $dbh = C4::Context->dbh;
1304
1305     # check for overdue fine
1306     my $sth =
1307       $dbh->prepare(
1308 "SELECT * FROM accountlines WHERE (borrowernumber = ?) AND (itemnumber = ?) AND (accounttype='FU' OR accounttype='O')"
1309       );
1310     $sth->execute( $borrowernumber, $item );
1311
1312     # alter fine to show that the book has been returned
1313    my $data; 
1314         if ($data = $sth->fetchrow_hashref) {
1315         my $uquery =($exemptfine)? "update accountlines set accounttype='FFOR', amountoutstanding=0":"update accountlines set accounttype='F' ";
1316                 $uquery .= " where (borrowernumber = ?) and (itemnumber = ?) and (accountno = ?)";
1317         my $usth = $dbh->prepare($uquery);
1318         $usth->execute($borrowernumber,$item ,$data->{'accountno'});
1319         $usth->finish();
1320     }
1321
1322     $sth->finish();
1323     return;
1324 }
1325
1326 =head2 FixAccountForLostAndReturned
1327
1328         &FixAccountForLostAndReturned($iteminfo,$borrower);
1329
1330 Calculates the charge for a book lost and returned (Not exported & used only once)
1331
1332 C<$iteminfo> is a hashref to iteminfo. Only {itemnumber} is used.
1333
1334 C<$borrower> is a hashref to borrower. Only {borrowernumber is used.
1335
1336 Internal function, called by AddReturn
1337
1338 =cut
1339
1340 sub FixAccountForLostAndReturned {
1341         my ($iteminfo, $borrower) = @_;
1342         my %env;
1343         my $dbh = C4::Context->dbh;
1344         my $itm = $iteminfo->{'itemnumber'};
1345         # check for charge made for lost book
1346         my $sth = $dbh->prepare("SELECT * FROM accountlines WHERE (itemnumber = ?) AND (accounttype='L' OR accounttype='Rep') ORDER BY date DESC");
1347         $sth->execute($itm);
1348         if (my $data = $sth->fetchrow_hashref) {
1349         # writeoff this amount
1350                 my $offset;
1351                 my $amount = $data->{'amount'};
1352                 my $acctno = $data->{'accountno'};
1353                 my $amountleft;
1354                 if ($data->{'amountoutstanding'} == $amount) {
1355                 $offset = $data->{'amount'};
1356                 $amountleft = 0;
1357                 } else {
1358                 $offset = $amount - $data->{'amountoutstanding'};
1359                 $amountleft = $data->{'amountoutstanding'} - $amount;
1360                 }
1361                 my $usth = $dbh->prepare("UPDATE accountlines SET accounttype = 'LR',amountoutstanding='0'
1362                         WHERE (borrowernumber = ?)
1363                         AND (itemnumber = ?) AND (accountno = ?) ");
1364                 $usth->execute($data->{'borrowernumber'},$itm,$acctno);
1365                 $usth->finish;
1366         #check if any credit is left if so writeoff other accounts
1367                 my $nextaccntno = getnextacctno(\%env,$data->{'borrowernumber'},$dbh);
1368                 if ($amountleft < 0){
1369                 $amountleft*=-1;
1370                 }
1371                 if ($amountleft > 0){
1372                 my $msth = $dbh->prepare("SELECT * FROM accountlines WHERE (borrowernumber = ?)
1373                                                         AND (amountoutstanding >0) ORDER BY date");
1374                 $msth->execute($data->{'borrowernumber'});
1375         # offset transactions
1376                 my $newamtos;
1377                 my $accdata;
1378                 while (($accdata=$msth->fetchrow_hashref) and ($amountleft>0)){
1379                         if ($accdata->{'amountoutstanding'} < $amountleft) {
1380                         $newamtos = 0;
1381                         $amountleft -= $accdata->{'amountoutstanding'};
1382                         }  else {
1383                         $newamtos = $accdata->{'amountoutstanding'} - $amountleft;
1384                         $amountleft = 0;
1385                         }
1386                         my $thisacct = $accdata->{'accountno'};
1387                         my $usth = $dbh->prepare("UPDATE accountlines SET amountoutstanding= ?
1388                                         WHERE (borrowernumber = ?)
1389                                         AND (accountno=?)");
1390                         $usth->execute($newamtos,$data->{'borrowernumber'},'$thisacct');
1391                         $usth->finish;
1392                         $usth = $dbh->prepare("INSERT INTO accountoffsets
1393                                 (borrowernumber, accountno, offsetaccount,  offsetamount)
1394                                 VALUES
1395                                 (?,?,?,?)");
1396                         $usth->execute($data->{'borrowernumber'},$accdata->{'accountno'},$nextaccntno,$newamtos);
1397                         $usth->finish;
1398                 }
1399                 $msth->finish;
1400                 }
1401                 if ($amountleft > 0){
1402                         $amountleft*=-1;
1403                 }
1404                 my $desc="Item Returned ".$iteminfo->{'barcode'};
1405                 $usth = $dbh->prepare("INSERT INTO accountlines
1406                         (borrowernumber,accountno,date,amount,description,accounttype,amountoutstanding)
1407                         VALUES (?,?,now(),?,?,'CR',?)");
1408                 $usth->execute($data->{'borrowernumber'},$nextaccntno,0-$amount,$desc,$amountleft);
1409                 $usth->finish;
1410                 $usth = $dbh->prepare("INSERT INTO accountoffsets
1411                         (borrowernumber, accountno, offsetaccount,  offsetamount)
1412                         VALUES (?,?,?,?)");
1413                 $usth->execute($borrower->{'borrowernumber'},$data->{'accountno'},$nextaccntno,$offset);
1414                 $usth->finish;
1415         ModItem({ paidfor => '' }, undef, $itm);
1416         }
1417         $sth->finish;
1418         return;
1419 }
1420
1421 =head2 GetItemIssue
1422
1423 $issues = &GetItemIssue($itemnumber);
1424
1425 Returns patrons currently having a book. nothing if item is not issued atm
1426
1427 C<$itemnumber> is the itemnumber
1428
1429 Returns an array of hashes
1430
1431 =cut
1432
1433 sub GetItemIssue {
1434     my ( $itemnumber) = @_;
1435     return unless $itemnumber;
1436     my $dbh = C4::Context->dbh;
1437     my @GetItemIssues;
1438     
1439     # get today date
1440     my $today = POSIX::strftime("%Y%m%d", localtime);
1441
1442     my $sth = $dbh->prepare(
1443         "SELECT * FROM issues 
1444         LEFT JOIN items ON issues.itemnumber=items.itemnumber
1445     WHERE
1446     issues.itemnumber=?  AND returndate IS NULL ");
1447     $sth->execute($itemnumber);
1448     my $data = $sth->fetchrow_hashref;
1449     my $datedue = $data->{'date_due'};
1450     $datedue =~ s/-//g;
1451     if ( $datedue < $today ) {
1452         $data->{'overdue'} = 1;
1453     }
1454     $data->{'itemnumber'} = $itemnumber; # fill itemnumber, in case item is not on issue
1455     $sth->finish;
1456     return ($data);
1457 }
1458
1459 =head2 GetItemIssues
1460
1461 $issues = &GetItemIssues($itemnumber, $history);
1462
1463 Returns patrons that have issued a book
1464
1465 C<$itemnumber> is the itemnumber
1466 C<$history> is 0 if you want actuel "issuer" (if it exist) and 1 if you want issues history
1467
1468 Returns an array of hashes
1469
1470 =cut
1471
1472 sub GetItemIssues {
1473     my ( $itemnumber,$history ) = @_;
1474     my $dbh = C4::Context->dbh;
1475     my @GetItemIssues;
1476     
1477     # get today date
1478     my $today = POSIX::strftime("%Y%m%d", localtime);
1479
1480     my $sth = $dbh->prepare(
1481         "SELECT * FROM issues 
1482         LEFT JOIN borrowers ON borrowers.borrowernumber 
1483         LEFT JOIN items ON items.itemnumber=issues.itemnumber 
1484     WHERE
1485     issues.itemnumber=?".($history?"":" AND returndate IS NULL ").
1486     "ORDER BY issues.date_due DESC"
1487     );
1488     $sth->execute($itemnumber);
1489     while ( my $data = $sth->fetchrow_hashref ) {
1490         my $datedue = $data->{'date_due'};
1491         $datedue =~ s/-//g;
1492         if ( $datedue < $today ) {
1493             $data->{'overdue'} = 1;
1494         }
1495         my $itemnumber = $data->{'itemnumber'};
1496         push @GetItemIssues, $data;
1497     }
1498     $sth->finish;
1499     return ( \@GetItemIssues );
1500 }
1501
1502 =head2 GetBiblioIssues
1503
1504 $issues = GetBiblioIssues($biblionumber);
1505
1506 this function get all issues from a biblionumber.
1507
1508 Return:
1509 C<$issues> is a reference to array which each value is ref-to-hash. This ref-to-hash containts all column from
1510 tables issues and the firstname,surname & cardnumber from borrowers.
1511
1512 =cut
1513
1514 sub GetBiblioIssues {
1515     my $biblionumber = shift;
1516     return undef unless $biblionumber;
1517     my $dbh   = C4::Context->dbh;
1518     my $query = "
1519         SELECT issues.*,items.barcode,biblio.biblionumber,biblio.title, biblio.author,borrowers.cardnumber,borrowers.surname,borrowers.firstname
1520         FROM issues
1521             LEFT JOIN borrowers ON borrowers.borrowernumber = issues.borrowernumber
1522             LEFT JOIN items ON issues.itemnumber = items.itemnumber
1523             LEFT JOIN biblioitems ON items.itemnumber = biblioitems.biblioitemnumber
1524             LEFT JOIN biblio ON biblio.biblionumber = items.biblioitemnumber
1525         WHERE biblio.biblionumber = ?
1526         ORDER BY issues.timestamp
1527     ";
1528     my $sth = $dbh->prepare($query);
1529     $sth->execute($biblionumber);
1530
1531     my @issues;
1532     while ( my $data = $sth->fetchrow_hashref ) {
1533         push @issues, $data;
1534     }
1535     return \@issues;
1536 }
1537
1538 =head2 CanBookBeRenewed
1539
1540 ($ok,$error) = &CanBookBeRenewed($borrowernumber, $itemnumber);
1541
1542 Find out whether a borrowed item may be renewed.
1543
1544 C<$dbh> is a DBI handle to the Koha database.
1545
1546 C<$borrowernumber> is the borrower number of the patron who currently
1547 has the item on loan.
1548
1549 C<$itemnumber> is the number of the item to renew.
1550
1551 C<$CanBookBeRenewed> returns a true value iff the item may be renewed. The
1552 item must currently be on loan to the specified borrower; renewals
1553 must be allowed for the item's type; and the borrower must not have
1554 already renewed the loan. $error will contain the reason the renewal can not proceed
1555
1556 =cut
1557
1558 sub CanBookBeRenewed {
1559
1560     # check renewal status
1561     my ( $borrowernumber, $itemnumber ) = @_;
1562     my $dbh       = C4::Context->dbh;
1563     my $renews    = 1;
1564     my $renewokay = 0;
1565         my $error;
1566
1567     # Look in the issues table for this item, lent to this borrower,
1568     # and not yet returned.
1569
1570     # FIXME - I think this function could be redone to use only one SQL call.
1571     my $sth1 = $dbh->prepare(
1572         "SELECT * FROM issues
1573             WHERE borrowernumber = ?
1574             AND itemnumber = ?
1575             AND returndate IS NULL"
1576     );
1577     $sth1->execute( $borrowernumber, $itemnumber );
1578     if ( my $data1 = $sth1->fetchrow_hashref ) {
1579
1580         # Found a matching item
1581
1582         # See if this item may be renewed. This query is convoluted
1583         # because it's a bit messy: given the item number, we need to find
1584         # the biblioitem, which gives us the itemtype, which tells us
1585         # whether it may be renewed.
1586         my $sth2 = $dbh->prepare(
1587             "SELECT renewalsallowed FROM items
1588                 LEFT JOIN biblioitems on items.biblioitemnumber = biblioitems.biblioitemnumber
1589                 LEFT JOIN itemtypes ON biblioitems.itemtype = itemtypes.itemtype
1590                 WHERE items.itemnumber = ?
1591                 "
1592         );
1593         $sth2->execute($itemnumber);
1594         if ( my $data2 = $sth2->fetchrow_hashref ) {
1595             $renews = $data2->{'renewalsallowed'};
1596         }
1597         if ( $renews && $renews >= $data1->{'renewals'} ) {
1598             $renewokay = 1;
1599         }
1600         else {
1601                         $error="too_many";
1602                 }
1603         $sth2->finish;
1604         my ( $resfound, $resrec ) = C4::Reserves::CheckReserves($itemnumber);
1605         if ($resfound) {
1606             $renewokay = 0;
1607                         $error="on_reserve"
1608         }
1609
1610     }
1611     $sth1->finish;
1612     return ($renewokay,$error);
1613 }
1614
1615 =head2 AddRenewal
1616
1617 &AddRenewal($borrowernumber, $itemnumber, $datedue);
1618
1619 Renews a loan.
1620
1621 C<$borrowernumber> is the borrower number of the patron who currently
1622 has the item.
1623
1624 C<$itemnumber> is the number of the item to renew.
1625
1626 C<$datedue> can be used to set the due date. If C<$datedue> is the
1627 empty string, C<&AddRenewal> will calculate the due date automatically
1628 from the book's item type. If you wish to set the due date manually,
1629 C<$datedue> should be in the form YYYY-MM-DD.
1630
1631 =cut
1632
1633 sub AddRenewal {
1634
1635     my ( $borrowernumber, $itemnumber, $branch ,$datedue ) = @_;
1636     my $dbh = C4::Context->dbh;
1637         
1638         my $biblio = GetBiblioFromItemNumber($itemnumber);
1639     # If the due date wasn't specified, calculate it by adding the
1640     # book's loan length to today's date.
1641     unless ( $datedue ) {
1642
1643
1644         my $borrower = C4::Members::GetMemberDetails( $borrowernumber, 0 );
1645         my $loanlength = GetLoanLength(
1646             $borrower->{'categorycode'},
1647              (C4::Context->preference('item-level_itypes')) ? $biblio->{'itype'} : $biblio->{'itemtype'} ,
1648                         $borrower->{'branchcode'}
1649         );
1650                 #FIXME --  choose issuer or borrower branch.
1651                 #FIXME -- where's the calendar ?
1652                 #FIXME -- $debug-ify the (0)
1653         my @darray = Add_Delta_DHMS( Today_and_Now(), $loanlength, 0, 0, 0 );
1654         $datedue = C4::Dates->new( sprintf("%04d-%02d-%02d",@darray[0..2]), 'iso');
1655                 (0) and print STDERR  "C4::Dates->new->output = " . C4::Dates->new()->output()
1656                                 . "\ndatedue->output = " . $datedue->output()
1657                                 . "\n(Y,M,D) = " . join ',', @darray;
1658                 $datedue=CheckValidDatedue($datedue,$itemnumber,$branch);
1659     }
1660
1661     # Find the issues record for this book
1662     my $sth =
1663       $dbh->prepare("SELECT * FROM issues
1664                         WHERE borrowernumber=? 
1665                         AND itemnumber=? 
1666                         AND returndate IS NULL"
1667       );
1668     $sth->execute( $borrowernumber, $itemnumber );
1669     my $issuedata = $sth->fetchrow_hashref;
1670     $sth->finish;
1671
1672     # Update the issues record to have the new due date, and a new count
1673     # of how many times it has been renewed.
1674     my $renews = $issuedata->{'renewals'} + 1;
1675     $sth = $dbh->prepare("UPDATE issues SET date_due = ?, renewals = ?
1676                             WHERE borrowernumber=? 
1677                             AND itemnumber=? 
1678                             AND returndate IS NULL"
1679     );
1680     $sth->execute( $datedue->output('iso'), $renews, $borrowernumber, $itemnumber );
1681     $sth->finish;
1682
1683     # Update the renewal count on the item, and tell zebra to reindex
1684     $renews = $biblio->{'renewals'} + 1;
1685     ModItem({ renewals => $renews }, $biblio->{'biblionumber'}, $itemnumber);
1686
1687     # Charge a new rental fee, if applicable?
1688     my ( $charge, $type ) = GetIssuingCharges( $itemnumber, $borrowernumber );
1689     if ( $charge > 0 ) {
1690         my $accountno = getnextacctno( $borrowernumber );
1691         my $item = GetBiblioFromItemNumber($itemnumber);
1692         $sth = $dbh->prepare(
1693                 "INSERT INTO accountlines
1694                     (borrowernumber,accountno,date,amount,
1695                         description,accounttype,amountoutstanding,
1696                     itemnumber)
1697                     VALUES (?,?,now(),?,?,?,?,?)"
1698         );
1699         $sth->execute( $borrowernumber, $accountno, $charge,
1700             "Renewal of Rental Item $item->{'title'} $item->{'barcode'}",
1701             'Rent', $charge, $itemnumber );
1702         $sth->finish;
1703     }
1704     # Log the renewal
1705     UpdateStats( $branch, 'renew', $charge, '', $itemnumber );
1706 }
1707
1708 sub GetRenewCount {
1709     # check renewal status
1710     my ($bornum,$itemno)=@_;
1711     my $dbh = C4::Context->dbh;
1712     my $renewcount = 0;
1713         my $renewsallowed = 0;
1714         my $renewsleft = 0;
1715     # Look in the issues table for this item, lent to this borrower,
1716     # and not yet returned.
1717
1718     # FIXME - I think this function could be redone to use only one SQL call.
1719     my $sth = $dbh->prepare("select * from issues
1720                                 where (borrowernumber = ?)
1721                                 and (itemnumber = ?)
1722                                 and returndate is null");
1723     $sth->execute($bornum,$itemno);
1724         my $data = $sth->fetchrow_hashref;
1725         $renewcount = $data->{'renewals'} if $data->{'renewals'};
1726     my $sth2 = $dbh->prepare("select renewalsallowed from items,biblioitems,itemtypes
1727         where (items.itemnumber = ?)
1728                 and (items.biblioitemnumber = biblioitems.biblioitemnumber)
1729         and (biblioitems.itemtype = itemtypes.itemtype)");
1730     $sth2->execute($itemno);
1731         my $data2 = $sth2->fetchrow_hashref();
1732         $renewsallowed = $data2->{'renewalsallowed'};
1733         $renewsleft = $renewsallowed - $renewcount;
1734 #         warn "Renewcount:$renewcount RenewsAll:$renewsallowed RenewLeft:$renewsleft";
1735         return ($renewcount,$renewsallowed,$renewsleft);
1736 }
1737 =head2 GetIssuingCharges
1738
1739 ($charge, $item_type) = &GetIssuingCharges($itemnumber, $borrowernumber);
1740
1741 Calculate how much it would cost for a given patron to borrow a given
1742 item, including any applicable discounts.
1743
1744 C<$itemnumber> is the item number of item the patron wishes to borrow.
1745
1746 C<$borrowernumber> is the patron's borrower number.
1747
1748 C<&GetIssuingCharges> returns two values: C<$charge> is the rental charge,
1749 and C<$item_type> is the code for the item's item type (e.g., C<VID>
1750 if it's a video).
1751
1752 =cut
1753
1754 sub GetIssuingCharges {
1755
1756     # calculate charges due
1757     my ( $itemnumber, $borrowernumber ) = @_;
1758     my $charge = 0;
1759     my $dbh    = C4::Context->dbh;
1760     my $item_type;
1761
1762     # Get the book's item type and rental charge (via its biblioitem).
1763     my $qcharge =     "SELECT itemtypes.itemtype,rentalcharge FROM items
1764             LEFT JOIN biblioitems ON biblioitems.biblioitemnumber = items.biblioitemnumber";
1765         $qcharge .= (C4::Context->preference('item-level_itypes'))
1766                 ? " LEFT JOIN itemtypes ON items.itype = itemtypes.itemtype "
1767                 : " LEFT JOIN itemtypes ON biblioitems.itemtype = itemtypes.itemtype ";
1768         
1769     $qcharge .=      "WHERE items.itemnumber =?";
1770    
1771     my $sth1 = $dbh->prepare($qcharge);
1772     $sth1->execute($itemnumber);
1773     if ( my $data1 = $sth1->fetchrow_hashref ) {
1774         $item_type = $data1->{'itemtype'};
1775         $charge    = $data1->{'rentalcharge'};
1776         my $q2 = "SELECT rentaldiscount FROM borrowers
1777             LEFT JOIN issuingrules ON borrowers.categorycode = issuingrules.categorycode
1778             WHERE borrowers.borrowernumber = ?
1779             AND issuingrules.itemtype = ?";
1780         my $sth2 = $dbh->prepare($q2);
1781         $sth2->execute( $borrowernumber, $item_type );
1782         if ( my $data2 = $sth2->fetchrow_hashref ) {
1783             my $discount = $data2->{'rentaldiscount'};
1784             if ( $discount eq 'NULL' ) {
1785                 $discount = 0;
1786             }
1787             $charge = ( $charge * ( 100 - $discount ) ) / 100;
1788         }
1789         $sth2->finish;
1790     }
1791
1792     $sth1->finish;
1793     return ( $charge, $item_type );
1794 }
1795
1796 =head2 AddIssuingCharge
1797
1798 &AddIssuingCharge( $itemno, $borrowernumber, $charge )
1799
1800 =cut
1801
1802 sub AddIssuingCharge {
1803     my ( $itemnumber, $borrowernumber, $charge ) = @_;
1804     my $dbh = C4::Context->dbh;
1805     my $nextaccntno = getnextacctno( $borrowernumber );
1806     my $query ="
1807         INSERT INTO accountlines
1808             (borrowernumber, itemnumber, accountno,
1809             date, amount, description, accounttype,
1810             amountoutstanding)
1811         VALUES (?, ?, ?,now(), ?, 'Rental', 'Rent',?)
1812     ";
1813     my $sth = $dbh->prepare($query);
1814     $sth->execute( $borrowernumber, $itemnumber, $nextaccntno, $charge, $charge );
1815     $sth->finish;
1816 }
1817
1818 =head2 GetTransfers
1819
1820 GetTransfers($itemnumber);
1821
1822 =cut
1823
1824 sub GetTransfers {
1825     my ($itemnumber) = @_;
1826
1827     my $dbh = C4::Context->dbh;
1828
1829     my $query = '
1830         SELECT datesent,
1831                frombranch,
1832                tobranch
1833         FROM branchtransfers
1834         WHERE itemnumber = ?
1835           AND datearrived IS NULL
1836         ';
1837     my $sth = $dbh->prepare($query);
1838     $sth->execute($itemnumber);
1839     my @row = $sth->fetchrow_array();
1840     $sth->finish;
1841     return @row;
1842 }
1843
1844
1845 =head2 GetTransfersFromTo
1846
1847 @results = GetTransfersFromTo($frombranch,$tobranch);
1848
1849 Returns the list of pending transfers between $from and $to branch
1850
1851 =cut
1852
1853 sub GetTransfersFromTo {
1854     my ( $frombranch, $tobranch ) = @_;
1855     return unless ( $frombranch && $tobranch );
1856     my $dbh   = C4::Context->dbh;
1857     my $query = "
1858         SELECT itemnumber,datesent,frombranch
1859         FROM   branchtransfers
1860         WHERE  frombranch=?
1861           AND  tobranch=?
1862           AND datearrived IS NULL
1863     ";
1864     my $sth = $dbh->prepare($query);
1865     $sth->execute( $frombranch, $tobranch );
1866     my @gettransfers;
1867
1868     while ( my $data = $sth->fetchrow_hashref ) {
1869         push @gettransfers, $data;
1870     }
1871     $sth->finish;
1872     return (@gettransfers);
1873 }
1874
1875 =head2 DeleteTransfer
1876
1877 &DeleteTransfer($itemnumber);
1878
1879 =cut
1880
1881 sub DeleteTransfer {
1882     my ($itemnumber) = @_;
1883     my $dbh          = C4::Context->dbh;
1884     my $sth          = $dbh->prepare(
1885         "DELETE FROM branchtransfers
1886          WHERE itemnumber=?
1887          AND datearrived IS NULL "
1888     );
1889     $sth->execute($itemnumber);
1890     $sth->finish;
1891 }
1892
1893 =head2 AnonymiseIssueHistory
1894
1895 $rows = AnonymiseIssueHistory($borrowernumber,$date)
1896
1897 This function write NULL instead of C<$borrowernumber> given on input arg into the table issues.
1898 if C<$borrowernumber> is not set, it will delete the issue history for all borrower older than C<$date>.
1899
1900 return the number of affected rows.
1901
1902 =cut
1903
1904 sub AnonymiseIssueHistory {
1905     my $date           = shift;
1906     my $borrowernumber = shift;
1907     my $dbh            = C4::Context->dbh;
1908     my $query          = "
1909         UPDATE issues
1910         SET    borrowernumber = NULL
1911         WHERE  returndate < '".$date."'
1912           AND borrowernumber IS NOT NULL
1913     ";
1914     $query .= " AND borrowernumber = '".$borrowernumber."'" if defined $borrowernumber;
1915     my $rows_affected = $dbh->do($query);
1916     return $rows_affected;
1917 }
1918
1919 =head2 updateWrongTransfer
1920
1921 $items = updateWrongTransfer($itemNumber,$borrowernumber,$waitingAtLibrary,$FromLibrary);
1922
1923 This function validate the line of brachtransfer but with the wrong destination (mistake from a librarian ...), and create a new line in branchtransfer from the actual library to the original library of reservation 
1924
1925 =cut
1926
1927 sub updateWrongTransfer {
1928         my ( $itemNumber,$waitingAtLibrary,$FromLibrary ) = @_;
1929         my $dbh = C4::Context->dbh;     
1930 # first step validate the actual line of transfert .
1931         my $sth =
1932                 $dbh->prepare(
1933                         "update branchtransfers set datearrived = now(),tobranch=?,comments='wrongtransfer' where itemnumber= ? AND datearrived IS NULL"
1934                 );
1935                 $sth->execute($FromLibrary,$itemNumber);
1936                 $sth->finish;
1937
1938 # second step create a new line of branchtransfer to the right location .
1939         ModItemTransfer($itemNumber, $FromLibrary, $waitingAtLibrary);
1940
1941 #third step changing holdingbranch of item
1942         UpdateHoldingbranch($FromLibrary,$itemNumber);
1943 }
1944
1945 =head2 UpdateHoldingbranch
1946
1947 $items = UpdateHoldingbranch($branch,$itmenumber);
1948 Simple methode for updating hodlingbranch in items BDD line
1949
1950 =cut
1951
1952 sub UpdateHoldingbranch {
1953         my ( $branch,$itemnumber ) = @_;
1954     ModItem({ holdingbranch => $branch }, undef, $itemnumber);
1955 }
1956
1957 =head2 CheckValidDatedue
1958
1959 $newdatedue = CheckValidDatedue($date_due,$itemnumber,$branchcode);
1960 this function return a new date due after checked if it's a repeatable or special holiday
1961 C<$date_due>   = returndate calculate with no day check
1962 C<$itemnumber>  = itemnumber
1963 C<$branchcode>  = localisation of issue 
1964
1965 =cut
1966
1967 # Why not create calendar object?  - 
1968 # TODO add 'duedate' option to useDaysMode .
1969 sub CheckValidDatedue { 
1970 my ($date_due,$itemnumber,$branchcode)=@_;
1971 my @datedue=split('-',$date_due->output('iso'));
1972 my $years=$datedue[0];
1973 my $month=$datedue[1];
1974 my $day=$datedue[2];
1975 # die "Item# $itemnumber ($branchcode) due: " . ${date_due}->output() . "\n(Y,M,D) = ($years,$month,$day)":
1976 my $dow;
1977 for (my $i=0;$i<2;$i++){
1978         $dow=Day_of_Week($years,$month,$day);
1979         ($dow=0) if ($dow>6);
1980         my $result=CheckRepeatableHolidays($itemnumber,$dow,$branchcode);
1981         my $countspecial=CheckSpecialHolidays($years,$month,$day,$itemnumber,$branchcode);
1982         my $countspecialrepeatable=CheckRepeatableSpecialHolidays($month,$day,$itemnumber,$branchcode);
1983                 if (($result ne '0') or ($countspecial ne '0') or ($countspecialrepeatable ne '0') ){
1984                 $i=0;
1985                 (($years,$month,$day) = Add_Delta_Days($years,$month,$day, 1))if ($i ne '1');
1986                 }
1987         }
1988         my $newdatedue=C4::Dates->new(sprintf("%04d-%02d-%02d",$years,$month,$day),'iso');
1989 return $newdatedue;
1990 }
1991
1992 =head2 CheckRepeatableHolidays
1993
1994 $countrepeatable = CheckRepeatableHoliday($itemnumber,$week_day,$branchcode);
1995 this function check if the date due is a repeatable holiday
1996 C<$date_due>   = returndate calculate with no day check
1997 C<$itemnumber>  = itemnumber
1998 C<$branchcode>  = localisation of issue 
1999
2000 =cut
2001
2002 sub CheckRepeatableHolidays{
2003 my($itemnumber,$week_day,$branchcode)=@_;
2004 my $dbh = C4::Context->dbh;
2005 my $query = qq|SELECT count(*)  
2006         FROM repeatable_holidays 
2007         WHERE branchcode=?
2008         AND weekday=?|;
2009 my $sth = $dbh->prepare($query);
2010 $sth->execute($branchcode,$week_day);
2011 my $result=$sth->fetchrow;
2012 $sth->finish;
2013 return $result;
2014 }
2015
2016
2017 =head2 CheckSpecialHolidays
2018
2019 $countspecial = CheckSpecialHolidays($years,$month,$day,$itemnumber,$branchcode);
2020 this function check if the date is a special holiday
2021 C<$years>   = the years of datedue
2022 C<$month>   = the month of datedue
2023 C<$day>     = the day of datedue
2024 C<$itemnumber>  = itemnumber
2025 C<$branchcode>  = localisation of issue 
2026
2027 =cut
2028
2029 sub CheckSpecialHolidays{
2030 my ($years,$month,$day,$itemnumber,$branchcode) = @_;
2031 my $dbh = C4::Context->dbh;
2032 my $query=qq|SELECT count(*) 
2033              FROM `special_holidays`
2034              WHERE year=?
2035              AND month=?
2036              AND day=?
2037              AND branchcode=?
2038             |;
2039 my $sth = $dbh->prepare($query);
2040 $sth->execute($years,$month,$day,$branchcode);
2041 my $countspecial=$sth->fetchrow ;
2042 $sth->finish;
2043 return $countspecial;
2044 }
2045
2046 =head2 CheckRepeatableSpecialHolidays
2047
2048 $countspecial = CheckRepeatableSpecialHolidays($month,$day,$itemnumber,$branchcode);
2049 this function check if the date is a repeatble special holidays
2050 C<$month>   = the month of datedue
2051 C<$day>     = the day of datedue
2052 C<$itemnumber>  = itemnumber
2053 C<$branchcode>  = localisation of issue 
2054
2055 =cut
2056
2057 sub CheckRepeatableSpecialHolidays{
2058 my ($month,$day,$itemnumber,$branchcode) = @_;
2059 my $dbh = C4::Context->dbh;
2060 my $query=qq|SELECT count(*) 
2061              FROM `repeatable_holidays`
2062              WHERE month=?
2063              AND day=?
2064              AND branchcode=?
2065             |;
2066 my $sth = $dbh->prepare($query);
2067 $sth->execute($month,$day,$branchcode);
2068 my $countspecial=$sth->fetchrow ;
2069 $sth->finish;
2070 return $countspecial;
2071 }
2072
2073
2074
2075 sub CheckValidBarcode{
2076 my ($barcode) = @_;
2077 my $dbh = C4::Context->dbh;
2078 my $query=qq|SELECT count(*) 
2079              FROM items 
2080              WHERE barcode=?
2081             |;
2082 my $sth = $dbh->prepare($query);
2083 $sth->execute($barcode);
2084 my $exist=$sth->fetchrow ;
2085 $sth->finish;
2086 return $exist;
2087 }
2088
2089 1;
2090
2091 __END__
2092
2093 =head1 AUTHOR
2094
2095 Koha Developement team <info@koha.org>
2096
2097 =cut
2098