Bug Fixing : Suggestions.pm warnings on line 132
[koha.git] / C4 / Circulation.pm
index 200bdd2..467fb57 100644 (file)
@@ -68,7 +68,7 @@ BEGIN {
                &AddRenewal
                &GetRenewCount
                &GetItemIssue
-                &GetOpenIssue
+        &GetOpenIssue
                &GetItemIssues
                &GetBorrowerIssues
                &GetIssuingCharges
@@ -92,9 +92,9 @@ BEGIN {
                &GetTransfersFromTo
                &updateWrongTransfer
                &DeleteTransfer
-                &IsBranchTransferAllowed
-                &CreateBranchTransferLimit
-                &DeleteBranchTransferLimits
+        &IsBranchTransferAllowed
+        &CreateBranchTransferLimit
+        &DeleteBranchTransferLimits
        );
 }
 
@@ -669,6 +669,12 @@ sub CanBookBeIssued {
        $item->{'itemtype'}=$item->{'itype'}; 
     my $dbh             = C4::Context->dbh;
 
+    # MANDATORY CHECKS - unless item exists, nothing else matters
+    unless ( $item->{barcode} ) {
+        $issuingimpossible{UNKNOWN_BARCODE} = 1;
+    }
+       return ( \%issuingimpossible, \%needsconfirmation ) if %issuingimpossible;
+
     #
     # DUE DATE is OK ? -- should already have checked.
     #
@@ -706,7 +712,7 @@ sub CanBookBeIssued {
     if ( $borrower->{'dateexpiry'} eq '0000-00-00') {
         $issuingimpossible{EXPIRED} = 1;
     } else {
-        my @expirydate=  split /-/,$borrower->{'dateexpiry'};
+        my @expirydate=  split (/-/,$borrower->{'dateexpiry'});
         if($expirydate[0]==0 || $expirydate[1]==0|| $expirydate[2]==0 ||
             Date_to_Days(Today) > Date_to_Days( @expirydate )) {
             $issuingimpossible{EXPIRED} = 1;                                   
@@ -734,6 +740,15 @@ sub CanBookBeIssued {
         }
     }
 
+    my ($blocktype, $count) = C4::Members::IsMemberBlocked($borrower->{'borrowernumber'});
+    if($blocktype == -1){
+        ## remaining overdue documents
+        $needsconfirmation{USERBLOCKEDREMAINING} = $count;
+    }elsif($blocktype == 1){
+        ## blocked because of overdue return
+        $issuingimpossible{USERBLOCKEDOVERDUE} = $count;
+    }
+
     #
     # JB34 CHECKS IF BORROWERS DONT HAVE ISSUE TOO MANY BOOKS
     #
@@ -751,7 +766,6 @@ sub CanBookBeIssued {
     unless ( $item->{barcode} ) {
         $issuingimpossible{UNKNOWN_BARCODE} = 1;
     }
-
     if (   $item->{'notforloan'}
         && $item->{'notforloan'} > 0 )
     {
@@ -769,12 +783,20 @@ sub CanBookBeIssued {
             $sth->execute($item->{'itemtype'});
             my $notforloan=$sth->fetchrow_hashref();
             $sth->finish();
-            if ($notforloan->{'notforloan'} == 1){
-                $issuingimpossible{NOT_FOR_LOAN} = 1;
+            if ($notforloan->{'notforloan'}) {
+                if (!C4::Context->preference("AllowNotForLoanOverride")) {
+                    $issuingimpossible{NOT_FOR_LOAN} = 1;
+                } else {
+                    $needsconfirmation{NOT_FOR_LOAN_FORCING} = 1;
+                }
             }
         }
         elsif ($biblioitem->{'notforloan'} == 1){
-            $issuingimpossible{NOT_FOR_LOAN} = 1;
+            if (!C4::Context->preference("AllowNotForLoanOverride")) {
+                $issuingimpossible{NOT_FOR_LOAN} = 1;
+            } else {
+                $needsconfirmation{NOT_FOR_LOAN_FORCING} = 1;
+            }
         }
     }
     if ( $item->{'wthdrawn'} && $item->{'wthdrawn'} == 1 )
@@ -788,7 +810,7 @@ sub CanBookBeIssued {
     }
     if ( C4::Context->preference("IndependantBranches") ) {
         my $userenv = C4::Context->userenv;
-        if ( ($userenv) && ( $userenv->{flags} != 1 ) ) {
+        if ( ($userenv) && ( $userenv->{flags} % 2 != 1 ) ) {
             $issuingimpossible{NOTSAMEBRANCH} = 1
               if ( $item->{C4::Context->preference("HomeOrHoldingBranch")} ne $userenv->{branch} );
         }
@@ -843,12 +865,6 @@ sub CanBookBeIssued {
 "$res->{'reservedate'} : $resborrower->{'firstname'} $resborrower->{'surname'} ($resborrower->{'cardnumber'})";
         }
     }
-    if ( C4::Context->preference("LibraryName") eq "Horowhenua Library Trust" ) {
-        if ( $borrower->{'categorycode'} eq 'W' ) {
-            my %emptyhash;
-            return ( \%emptyhash, \%needsconfirmation );
-        }
-       }
        return ( \%issuingimpossible, \%needsconfirmation );
 }
 
@@ -998,6 +1014,17 @@ sub AddIssue {
             my $loanlength = GetLoanLength( $borrower->{'categorycode'}, $itype, $branch );
             $datedue = CalcDateDue( C4::Dates->new( $issuedate, 'iso' ), $loanlength, $branch, $borrower );
 
+            # if ReturnBeforeExpiry ON the datedue can't be after borrower expirydate
+            if ( C4::Context->preference('ReturnBeforeExpiry') && $datedue->output('iso') gt $borrower->{dateexpiry} ) {
+                $datedue = C4::Dates->new( $borrower->{dateexpiry}, 'iso' );
+            }
+
+                       # if ceilingDueDate ON the datedue can't be after the ceiling date
+                       if ( C4::Context->preference('ceilingDueDate')
+                        && ( C4::Context->preference('ceilingDueDate') =~ C4::Dates->regexp('syspref') )
+                        && $datedue->output gt C4::Context->preference('ceilingDueDate') ) {
+                               $datedue = C4::Dates->new( C4::Context->preference('ceilingDueDate') );
+                       }
         }
         $sth->execute(
             $borrower->{'borrowernumber'},      # borrowernumber
@@ -1388,6 +1415,9 @@ either C<Waiting>, C<Reserved>, or 0.
 
 =back
 
+C<$iteminformation> is a reference-to-hash, giving information about the
+returned item from the issues table.
+
 C<$borrower> is a reference-to-hash, giving information about the
 patron who last borrowed the book.
 
@@ -1395,63 +1425,66 @@ patron who last borrowed the book.
 
 sub AddReturn {
     my ( $barcode, $branch, $exemptfine, $dropbox ) = @_;
-    my $dbh      = C4::Context->dbh;
+    if ($branch and not GetBranchDetail($branch)) {
+        warn "AddReturn error: branch '$branch' not found.  Reverting to " . C4::Context->userenv->{'branch'};
+        undef $branch;
+    }
+    $branch = C4::Context->userenv->{'branch'} unless $branch;  # we trust userenv to be a safe fallback/default
     my $messages;
-    my $doreturn = 1;
     my $borrower;
+    my $doreturn       = 1;
     my $validTransfert = 0;
-    my $reserveDone = 0;
+    my $reserveDone    = 0;
     
     # get information on item
-    my $iteminformation = GetItemIssue( GetItemnumberFromBarcode($barcode));
-    my $biblio = GetBiblioItemData($iteminformation->{'biblioitemnumber'});
+    my $itemnumber      = GetItemnumberFromBarcode( $barcode );
+    my $iteminformation = GetItemIssue($itemnumber);
+    my $biblio          = GetBiblioItemData($iteminformation->{'biblioitemnumber'});
 #     use Data::Dumper;warn Data::Dumper::Dumper($iteminformation);  
-    unless ($iteminformation->{'itemnumber'} ) {
+    unless ($itemnumber) {
         $messages->{'BadBarcode'} = $barcode;
         $doreturn = 0;
     } else {
-        # find the borrower
-        if ( ( not $iteminformation->{borrowernumber} ) && $doreturn ) {
+        if ( not $iteminformation ) {
             $messages->{'NotIssued'} = $barcode;
             # even though item is not on loan, it may still
             # be transferred; therefore, get current branch information
             my $curr_iteminfo = GetItem($iteminformation->{'itemnumber'});
-            $iteminformation->{'homebranch'} = $curr_iteminfo->{'homebranch'};
+            $iteminformation->{'homebranch'}    = $curr_iteminfo->{'homebranch'};
             $iteminformation->{'holdingbranch'} = $curr_iteminfo->{'holdingbranch'};
-            $iteminformation->{'itemlost'} = $curr_iteminfo->{'itemlost'};
+            $iteminformation->{'itemlost'}      = $curr_iteminfo->{'itemlost'};
+            # These lines patch up $iteminformation enough so it can be used below for other messages
             $doreturn = 0;
         }
-    
+
+        my $hbr = $iteminformation->{C4::Context->preference("HomeOrHoldingBranch")} || '';
         # check if the book is in a permanent collection....
-        my $hbr      = $iteminformation->{C4::Context->preference("HomeOrHoldingBranch")};
-        my $branches = GetBranches();
-               # FIXME -- This 'PE' attribute is largely undocumented.  afaict, there's no user interface that reflects this functionality.
-        if ( $hbr && $branches->{$hbr}->{'PE'} ) {
-            $messages->{'IsPermanent'} = $hbr;
+        # FIXME -- This 'PE' attribute is largely undocumented.  afaict, there's no user interface that reflects this functionality.
+        if ( $hbr ) {
+            my $branches = GetBranches();    # a potentially expensive call for a non-feature.
+            $branches->{$hbr}->{PE} and $messages->{'IsPermanent'} = $hbr;
         }
-               
-                   # if independent branches are on and returning to different branch, refuse the return
-        if ($hbr ne C4::Context->userenv->{'branch'} && C4::Context->preference("IndependantBranches")){
-                         $messages->{'Wrongbranch'} = 1;
-                         $doreturn=0;
-                   }
-                       
-        # check that the book has been cancelled
-        if ( $iteminformation->{'wthdrawn'} ) {
+
+        # if indy branches and returning to different branch, refuse the return
+        if ($hbr ne $branch && C4::Context->preference("IndependantBranches")){
+            $messages->{'Wrongbranch'} = 1;
+            $doreturn = 0;
+        }
+
+        if ( $iteminformation->{'wthdrawn'} ) { # book has been cancelled
             $messages->{'wthdrawn'} = 1;
             $doreturn = 0;
         }
-    
-    #     new op dev : if the book returned in an other branch update the holding branch
-    
-    # update issues, thereby returning book (should push this out into another subroutine
+
+        # if the book returned in an other branch, update the holding branch
+        # update issues, thereby returning book (should push this out into another subroutine
         $borrower = C4::Members::GetMemberDetails( $iteminformation->{borrowernumber}, 0 );
-    
-    # case of a return of document (deal with issues and holdingbranch)
+
+        # case of a return of document (deal with issues and holdingbranch)
     
         if ($doreturn) {
                        my $circControlBranch;
-                       if($dropbox) {
+                       if ($dropbox) {
                                # don't allow dropbox mode to create an invalid entry in issues (issuedate > returndate) FIXME: actually checks eq, not gt
                                undef($dropbox) if ( $iteminformation->{'issuedate'} eq C4::Dates->today('iso') );
                                if (C4::Context->preference('CircControl') eq 'ItemHomeBranch' ) {
@@ -1466,20 +1499,18 @@ sub AddReturn {
             MarkIssueReturned($borrower->{'borrowernumber'}, $iteminformation->{'itemnumber'},$circControlBranch);
             $messages->{'WasReturned'} = 1;    # FIXME is the "= 1" right?
 
-    
             # continue to deal with returns cases, but not only if we have an issue
         
             # the holdingbranch is updated if the document is returned in an other location .
-            if ( $iteminformation->{'holdingbranch'} ne C4::Context->userenv->{'branch'} ) {
-                           UpdateHoldingbranch(C4::Context->userenv->{'branch'},$iteminformation->{'itemnumber'});
-                           #           reload iteminformation holdingbranch with the userenv value
-                           $iteminformation->{'holdingbranch'} = C4::Context->userenv->{'branch'};
+            if ( $iteminformation->{'holdingbranch'} ne $branch ) {
+                UpdateHoldingbranch($branch, $iteminformation->{'itemnumber'});
+                $iteminformation->{'holdingbranch'} = $branch; # update iteminformation holdingbranch too
             }
             ModDateLastSeen( $iteminformation->{'itemnumber'} );
             ModItem({ onloan => undef }, $biblio->{'biblionumber'}, $iteminformation->{'itemnumber'});
           
-                       if ($iteminformation->{borrowernumber}){
-                           ($borrower) = C4::Members::GetMemberDetails( $iteminformation->{borrowernumber}, 0 );
+            if ($iteminformation->{borrowernumber}){
+                $borrower = C4::Members::GetMemberDetails( $iteminformation->{borrowernumber}, 0 ); # FIXME: we shouldn't need to make the same call twice
             }
         }
         # fix up the accounts.....
@@ -1487,45 +1518,40 @@ sub AddReturn {
             $messages->{'WasLost'} = 1;
         }
     
-    # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
-    #     check if we have a transfer for this document
+        # check if we have a transfer for this document
         my ($datesent,$frombranch,$tobranch) = GetTransfers( $iteminformation->{'itemnumber'} );
     
-    #     if we have a transfer to do, we update the line of transfers with the datearrived
+        # if we have a transfer to do, we update the line of transfers with the datearrived
         if ($datesent) {
-            if ( $tobranch eq C4::Context->userenv->{'branch'} ) {
-                    my $sth =
-                    $dbh->prepare(
-                            "UPDATE branchtransfers SET datearrived = now() WHERE itemnumber= ? AND datearrived IS NULL"
-                    );
-                    $sth->execute( $iteminformation->{'itemnumber'} );
-                    $sth->finish;
-    #         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'
-            C4::Reserves::ModReserveStatus( $iteminformation->{'itemnumber'},'W' );
+            if ( $tobranch eq $branch ) {
+                my $sth = C4::Context->dbh->prepare(
+                    "UPDATE branchtransfers SET datearrived = now() WHERE itemnumber= ? AND datearrived IS NULL"
+                );
+                $sth->execute( $iteminformation->{'itemnumber'} );
+                # if we have a reservation with the validate of transfer, we can set it's status to 'W'
+                C4::Reserves::ModReserveStatus($iteminformation->{'itemnumber'}, 'W');
+            } else {
+                $messages->{'WrongTransfer'}     = $tobranch;
+                $messages->{'WrongTransferItem'} = $iteminformation->{'itemnumber'};
             }
-        else {
-            $messages->{'WrongTransfer'} = $tobranch;
-            $messages->{'WrongTransferItem'} = $iteminformation->{'itemnumber'};
-        }
-        $validTransfert = 1;
+            $validTransfert = 1;
         }
     
-    # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # 
         # fix up the accounts.....
         if ($iteminformation->{'itemlost'}) {
-                FixAccountForLostAndReturned($iteminformation, $borrower);
-                $messages->{'WasLost'} = 1;
+            FixAccountForLostAndReturned($iteminformation, $borrower);
+            $messages->{'WasLost'} = 1;
         }
+
         # fix up the overdues in accounts...
         FixOverduesOnReturn( $borrower->{'borrowernumber'},
             $iteminformation->{'itemnumber'}, $exemptfine, $dropbox );
     
-    # find reserves.....
-    #     if we don't have a reserve with the status W, we launch the Checkreserves routine
-        my ( $resfound, $resrec ) =
-        C4::Reserves::CheckReserves( $iteminformation->{'itemnumber'} );
+        # find reserves.....
+        # if we don't have a reserve with the status W, we launch the Checkreserves routine
+        my ( $resfound, $resrec ) = C4::Reserves::CheckReserves( $iteminformation->{'itemnumber'} );
         if ($resfound) {
-            $resrec->{'ResFound'}   = $resfound;
+              $resrec->{'ResFound'} = $resfound;
             $messages->{'ResFound'} = $resrec;
             $reserveDone = 1;
         }
@@ -1571,8 +1597,7 @@ sub AddReturn {
                                ) {
                                ModItemTransfer($iteminformation->{'itemnumber'}, C4::Context->userenv->{'branch'}, $iteminformation->{'homebranch'});
                                 $messages->{'WasTransfered'} = 1;
-                       }
-                       else {
+                       } else {
                                $messages->{'NeedsTransfer'} = 1;
                        }
         }
@@ -2023,40 +2048,50 @@ sub CanBookBeRenewed {
 
     # Look in the issues table for this item, lent to this borrower,
     # and not yet returned.
-
-    # FIXME - I think this function could be redone to use only one SQL call.
-    my $sth1 = $dbh->prepare(
-        "SELECT * FROM issues
-            WHERE borrowernumber = ?
-            AND itemnumber = ?"
-    );
-    $sth1->execute( $borrowernumber, $itemnumber );
-    if ( my $data1 = $sth1->fetchrow_hashref ) {
-
-        # Found a matching item
-
-        # See if this item may be renewed. This query is convoluted
-        # because it's a bit messy: given the item number, we need to find
-        # the biblioitem, which gives us the itemtype, which tells us
-        # whether it may be renewed.
-        my $query = "SELECT renewalsallowed FROM items ";
-        $query .= (C4::Context->preference('item-level_itypes'))
-                    ? "LEFT JOIN itemtypes ON items.itype = itemtypes.itemtype "
-                    : "LEFT JOIN biblioitems on items.biblioitemnumber = biblioitems.biblioitemnumber
-                       LEFT JOIN itemtypes ON biblioitems.itemtype = itemtypes.itemtype ";
-        $query .= "WHERE items.itemnumber = ?";
-        my $sth2 = $dbh->prepare($query);
-        $sth2->execute($itemnumber);
-        if ( my $data2 = $sth2->fetchrow_hashref ) {
-            $renews = $data2->{'renewalsallowed'};
-        }
-        if ( ( $renews && $renews > $data1->{'renewals'} ) || $override_limit ) {
+    my %branch = (
+            'ItemHomeLibrary' => 'items.homebranch',
+            'PickupLibrary'   => 'items.holdingbranch',
+            'PatronLibrary'   => 'borrowers.branchcode'
+            );
+    my $controlbranch = $branch{C4::Context->preference('CircControl')};
+    my $itype         = C4::Context->preference('item-level_itypes') ? 'items.itype' : 'biblioitems.itemtype';
+    
+    my $sthcount = $dbh->prepare("
+                   SELECT 
+                    borrowers.categorycode, biblioitems.itemtype, issues.renewals, renewalsallowed, $controlbranch
+                   FROM  issuingrules, 
+                   issues 
+                   LEFT JOIN items USING (itemnumber) 
+                   LEFT JOIN borrowers USING (borrowernumber) 
+                   LEFT JOIN biblioitems USING (biblioitemnumber)
+                   
+                   WHERE
+                    issuingrules.categorycode = borrowers.categorycode
+                   AND
+                    issuingrules.itemtype = $itype
+                   AND
+                    (issuingrules.branchcode = $controlbranch OR issuingrules.branchcode = '*') 
+                   AND 
+                    borrowernumber = ? 
+                   AND
+                    itemnumber = ?
+                   ORDER BY
+                    issuingrules.categorycode desc,
+                    issuingrules.itemtype desc,
+                    issuingrules.branchcode desc
+                   LIMIT 1;
+                  ");
+
+    $sthcount->execute( $borrowernumber, $itemnumber );
+    if ( my $data1 = $sthcount->fetchrow_hashref ) {
+        
+        if ( ( $data1->{renewalsallowed} && $data1->{renewalsallowed} > $data1->{renewals} ) || $override_limit ) {
             $renewokay = 1;
         }
         else {
                        $error="too_many";
                }
-        $sth2->finish;
+               
         my ( $resfound, $resrec ) = C4::Reserves::CheckReserves($itemnumber);
         if ($resfound) {
             $renewokay = 0;
@@ -2064,7 +2099,7 @@ sub CanBookBeRenewed {
         }
 
     }
-    $sth1->finish;
+    $sthcount->finish;
     return ($renewokay,$error);
 }
 
@@ -2099,22 +2134,6 @@ sub AddRenewal {
     my $branch  = (@_) ? shift : $item->{homebranch};  # opac-renew doesn't send branch
     my $datedue = shift;
     my $lastreneweddate = shift;
-
-    # If the due date wasn't specified, calculate it by adding the
-    # book's loan length to today's date.
-    unless ($datedue && $datedue->output('iso')) {
-
-        my $borrower = C4::Members::GetMemberDetails( $borrowernumber, 0 ) or return undef;
-        my $loanlength = GetLoanLength(
-            $borrower->{'categorycode'},
-             (C4::Context->preference('item-level_itypes')) ? $biblio->{'itype'} : $biblio->{'itemtype'} ,
-                       $item->{homebranch}                     # item's homebranch determines loanlength OR do we want the branch specified by the AddRenewal argument?
-        );
-               #FIXME -- use circControl?
-               $datedue =  CalcDateDue(C4::Dates->new(),$loanlength,$branch,$borrower);        # this branch is the transactional branch.
-                                                               # The question of whether to use item's homebranch calendar is open.
-    }
-
     # $lastreneweddate defaults to today.
     unless (defined $lastreneweddate) {
         $lastreneweddate = strftime( "%Y-%m-%d", localtime );
@@ -2131,6 +2150,26 @@ sub AddRenewal {
     my $issuedata = $sth->fetchrow_hashref;
     $sth->finish;
 
+    # If the due date wasn't specified, calculate it by adding the
+    # book's loan length to today's date or the current due date
+    # based on the value of the RenewalPeriodBase syspref.
+    unless ($datedue && $datedue->output('iso')) {
+
+        my $borrower = C4::Members::GetMemberDetails( $borrowernumber, 0 ) or return undef;
+        my $loanlength = GetLoanLength(
+            $borrower->{'categorycode'},
+             (C4::Context->preference('item-level_itypes')) ? $biblio->{'itype'} : $biblio->{'itemtype'} ,
+                       $item->{homebranch}     # item's homebranch determines loanlength OR do we want the branch specified by the AddRenewal argument?
+        );
+
+        $datedue = (C4::Context->preference('RenewalPeriodBase') eq 'date_due') ?
+                                        C4::Dates->new($issuedata->{date_due}, 'iso') :
+                                        C4::Dates->new();
+        #FIXME -- use circControl?
+        $datedue =  CalcDateDue($datedue,$loanlength,$branch,$borrower);    # this branch is the transactional branch.
+        # The question of whether to use item's homebranch calendar is open.
+    }
+
     # Update the issues record to have the new due date, and a new count
     # of how many times it has been renewed.
     my $renews = $issuedata->{'renewals'} + 1;
@@ -2733,3 +2772,12 @@ Koha Developement team <info@koha.org>
 
 =cut
 
+
+__END__
+
+=head1 AUTHOR
+
+Koha Developement team <info@koha.org>
+
+=cut
+