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