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