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