Bug 17680: C4::Circulation - Remove GetItemIssue, simple calls
[koha.git] / C4 / Circulation.pm
index e71600d..6725d4b 100644 (file)
@@ -307,7 +307,7 @@ sub transferbook {
     my $messages;
     my $dotransfer      = 1;
     my $itemnumber = GetItemnumberFromBarcode( $barcode );
-    my $issue      = GetItemIssue($itemnumber);
+    my $issue      = Koha::Checkouts->find({ itemnumber => $itemnumber });
     my $biblio = GetBiblioFromItemNumber($itemnumber);
 
     # bad barcode..
@@ -349,9 +349,9 @@ sub transferbook {
     }
 
     # check if it is still issued to someone, return it...
-    if ($issue->{borrowernumber}) {
+    if ( $issue ) {
         AddReturn( $barcode, $fbr );
-        $messages->{'WasReturned'} = $issue->{borrowernumber};
+        $messages->{'WasReturned'} = $issue->borrowernumber;
     }
 
     # find reserves.....
@@ -674,7 +674,7 @@ sub CanBookBeIssued {
     my $override_high_holds = $params->{override_high_holds} || 0;
 
     my $item = GetItem(GetItemnumberFromBarcode( $barcode ));
-    my $issue = GetItemIssue($item->{itemnumber});
+    my $issue = Koha::Checkouts->find( { itemnumber => $item->{itemnumber} } );
        my $biblioitem = GetBiblioItemData($item->{biblioitemnumber});
        $item->{'itemtype'}=$item->{'itype'}; 
     my $dbh             = C4::Context->dbh;
@@ -832,9 +832,9 @@ sub CanBookBeIssued {
     #
     my $switch_onsite_checkout = (
           C4::Context->preference('SwitchOnSiteCheckouts')
-      and $issue->{onsite_checkout}
       and $issue
-      and $issue->{borrowernumber} == $borrower->{'borrowernumber'} ? 1 : 0 );
+      and $issue->onsite_checkout
+      and $issue->borrowernumber == $borrower->{'borrowernumber'} ? 1 : 0 );
     my $toomany = TooMany( $borrower, $item->{biblionumber}, $item, { onsite_checkout => $onsite_checkout, switch_onsite_checkout => $switch_onsite_checkout, } );
     # if TooMany max_allowed returns 0 the user doesn't have permission to check out this book
     if ( $toomany ) {
@@ -941,13 +941,13 @@ sub CanBookBeIssued {
     #
     # CHECK IF BOOK ALREADY ISSUED TO THIS BORROWER
     #
-    if ( $issue->{borrowernumber} && $issue->{borrowernumber} eq $borrower->{'borrowernumber'} ){
+    if ( $issue && $issue->borrowernumber eq $borrower->{'borrowernumber'} ){
 
         # Already issued to current borrower.
         # If it is an on-site checkout if it can be switched to a normal checkout
         # or ask whether the loan should be renewed
 
-        if ( $issue->{onsite_checkout}
+        if ( $issue->onsite_checkout
                 and C4::Context->preference('SwitchOnSiteCheckouts') ) {
             $messages{ONSITE_CHECKOUT_WILL_BE_SWITCHED} = 1;
         } else {
@@ -968,10 +968,10 @@ sub CanBookBeIssued {
             }
         }
     }
-    elsif ($issue->{borrowernumber}) {
+    elsif ( $issue ) {
 
         # issued to someone else
-        my $currborinfo =    C4::Members::GetMember( borrowernumber => $issue->{borrowernumber} );
+        my $currborinfo =    C4::Members::GetMember( borrowernumber => $issue->borrowernumber );
 
 
         my ( $can_be_returned, $message ) = CanBookBeReturned( $item, C4::Context->userenv->{branch} );
@@ -1304,13 +1304,13 @@ sub AddIssue {
         my $branch = _GetCircControlBranch( $item, $borrower );
 
         # get actual issuing if there is one
-        my $actualissue = GetItemIssue( $item->{itemnumber} );
+        my $actualissue = Koha::Checkouts->find( { itemnumber => $item->{itemnumber} } );
 
         # get biblioinformation for this item
         my $biblio = GetBiblioFromItemNumber( $item->{itemnumber} );
 
         # check if we just renew the issue.
-        if ( $actualissue->{borrowernumber} eq $borrower->{'borrowernumber'}
+        if ( $actualissue and $actualissue->borrowernumber eq $borrower->{'borrowernumber'}
                 and not $switch_onsite_checkout ) {
             $datedue = AddRenewal(
                 $borrower->{'borrowernumber'},
@@ -1322,8 +1322,7 @@ sub AddIssue {
         }
         else {
             # it's NOT a renewal
-            if ( $actualissue->{borrowernumber}
-                    and not $switch_onsite_checkout ) {
+            if ( $actualissue and not $switch_onsite_checkout ) {
                 # This book is currently on loan, but not to the person
                 # who wants to borrow it now. mark it returned before issuing to the new borrower
                 my ( $allowed, $message ) = CanBookBeReturned( $item, C4::Context->userenv->{branch} );
@@ -1934,15 +1933,16 @@ sub AddReturn {
         }
 
         if ($borrowernumber) {
-            if ( ( C4::Context->preference('CalculateFinesOnReturn') && $issue->{'overdue'} ) || $return_date ) {
-                _CalculateAndUpdateFine( { issue => $issue, item => $item, borrower => $borrower, return_date => $return_date } );
-            }
-
             eval {
-                MarkIssueReturned( $borrowernumber, $item->{'itemnumber'},
+                my $issue_id = MarkIssueReturned( $borrowernumber, $item->{'itemnumber'},
                     $circControlBranch, $return_date, $borrower->{'privacy'} );
+                $issue->{issue_id} = $issue_id;
             };
-            if ( $@ ) {
+            unless ( $@ ) {
+                if ( ( C4::Context->preference('CalculateFinesOnReturn') && $issue->{'overdue'} ) || $return_date ) {
+                    _CalculateAndUpdateFine( { issue => $issue, item => $item, borrower => $borrower, return_date => $return_date } );
+                }
+            } else {
                 $messages->{'Wrongbranch'} = {
                     Wrongbranch => $branch,
                     Rightbranch => $message
@@ -2172,30 +2172,35 @@ sub MarkIssueReturned {
 
     # FIXME Improve the return value and handle it from callers
     $schema->txn_do(sub {
+
         $dbh->do( $query, undef, @bind );
 
-        my $id_already_exists = $dbh->selectrow_array(
-            q|SELECT COUNT(*) FROM old_issues WHERE issue_id = ?|,
-            undef, $issue_id
-        );
+        my $issue = Koha::Checkouts->find( $issue_id ); # FIXME should be fetched earlier
 
-        if ( $id_already_exists ) {
-            my $new_issue_id = $dbh->selectrow_array(q|SELECT MAX(issue_id)+1 FROM old_issues|);
-            $dbh->do(
-                q|UPDATE issues SET issue_id = ? WHERE issue_id = ?|,
-                undef, $new_issue_id, $issue_id
-            );
+        # Create the old_issues entry
+        my $old_checkout_data = $issue->unblessed;
+
+        if ( Koha::Old::Checkouts->find( $issue_id ) ) {
+            my $new_issue_id = ( Koha::Old::Checkouts->search(
+                {},
+                { columns => [ { max_issue_id => { max => 'issue_id' } } ] }
+            )->get_column('max_issue_id') )[0];
+            $new_issue_id++;
             $issue_id = $new_issue_id;
         }
+        $old_checkout_data->{issue_id} = $issue_id;
+        my $old_checkout = Koha::Old::Checkout->new($old_checkout_data)->store;
 
-        $dbh->do(q|INSERT INTO old_issues SELECT * FROM issues WHERE issue_id = ?|, undef, $issue_id);
+        # Update the fines
+        $dbh->do(q|UPDATE accountlines SET issue_id = ? WHERE issue_id = ?|, undef, $old_checkout->issue_id, $issue->issue_id);
 
         # anonymise patron checkout immediately if $privacy set to 2 and AnonymousPatron is set to a valid borrowernumber
         if ( $privacy == 2) {
-            $dbh->do(q|UPDATE old_issues SET borrowernumber=? WHERE issue_id = ?|, undef, $anonymouspatron, $issue_id);
+            $dbh->do(q|UPDATE old_issues SET borrowernumber=? WHERE issue_id = ?|, undef, $anonymouspatron, $old_checkout->issue_id);
         }
 
-        $dbh->do(q|DELETE FROM issues WHERE issue_id = ?|, undef, $issue_id);
+        # Delete the issue
+        $issue->delete;
 
         ModItem( { 'onloan' => undef }, undef, $itemnumber );
 
@@ -2205,6 +2210,8 @@ sub MarkIssueReturned {
             $item->last_returned_by( $patron );
         }
     });
+
+    return $issue_id;
 }
 
 =head2 _debar_user_on_return
@@ -2220,7 +2227,7 @@ C<$datedue> date due DateTime object
 C<$return_date> DateTime object representing the return time
 
 Internal function, called only by AddReturn that calculates and updates
- the user fine days, and debars him if necessary.
+ the user fine days, and debars them if necessary.
 
 Should only be called for overdue returns
 
@@ -2753,19 +2760,34 @@ sub CanBookBeRenewed {
         return ( 0, 'overdue');
     }
 
-    if ( $itemissue->{auto_renew}
-        and defined $issuing_rule->no_auto_renewal_after
+    if ( $itemissue->{auto_renew} ) {
+        if ( defined $issuing_rule->no_auto_renewal_after
                 and $issuing_rule->no_auto_renewal_after ne "" ) {
+            # Get issue_date and add no_auto_renewal_after
+            # If this is greater than today, it's too late for renewal.
+            my $maximum_renewal_date = dt_from_string($itemissue->{issuedate});
+            $maximum_renewal_date->add(
+                $issuing_rule->lengthunit => $issuing_rule->no_auto_renewal_after
+            );
+            my $now = dt_from_string;
+            if ( $now >= $maximum_renewal_date ) {
+                return ( 0, "auto_too_late" );
+            }
+        }
+        if ( defined $issuing_rule->no_auto_renewal_after_hard_limit
+                      and $issuing_rule->no_auto_renewal_after_hard_limit ne "" ) {
+            # If no_auto_renewal_after_hard_limit is >= today, it's also too late for renewal
+            if ( dt_from_string >= dt_from_string( $issuing_rule->no_auto_renewal_after_hard_limit ) ) {
+                return ( 0, "auto_too_late" );
+            }
+        }
 
-        # Get issue_date and add no_auto_renewal_after
-        # If this is greater than today, it's too late for renewal.
-        my $maximum_renewal_date = dt_from_string($itemissue->{issuedate});
-        $maximum_renewal_date->add(
-            $issuing_rule->lengthunit => $issuing_rule->no_auto_renewal_after
-        );
-        my $now = dt_from_string;
-        if ( $now >= $maximum_renewal_date ) {
-            return ( 0, "auto_too_late" );
+        if ( C4::Context->preference('OPACFineNoRenewalsBlockAutoRenew') ) {
+            my $fine_no_renewals = C4::Context->preference("OPACFineNoRenewals");
+            my ( $amountoutstanding ) = C4::Members::GetMemberAccountRecords($borrower->{borrowernumber});
+            if ( $amountoutstanding and $amountoutstanding > $fine_no_renewals ) {
+                return ( 0, "auto_too_much_oweing" );
+            }
         }
     }
 
@@ -3022,9 +3044,9 @@ sub GetSoonestRenewDate {
     my $dbh = C4::Context->dbh;
 
     my $item      = GetItem($itemnumber)      or return;
-    my $itemissue = GetItemIssue($itemnumber) or return;
+    my $itemissue = Koha::Checkouts->find( { itemnumber => $itemnumber } ) or return;
 
-    $borrowernumber ||= $itemissue->{borrowernumber};
+    $borrowernumber ||= $itemissue->borrowernumber;
     my $borrower = C4::Members::GetMember( borrowernumber => $borrowernumber )
       or return;
 
@@ -3043,8 +3065,7 @@ sub GetSoonestRenewDate {
         and $issuing_rule->norenewalbefore ne "" )
     {
         my $soonestrenewal =
-          $itemissue->{date_due}->clone()
-          ->subtract(
+          dt_from_string( $itemissue->date_due )->subtract(
             $issuing_rule->lengthunit => $issuing_rule->norenewalbefore );
 
         if ( C4::Context->preference('NoRenewalBeforePrecision') eq 'date'
@@ -3069,8 +3090,8 @@ has the item on loan.
 C<$itemnumber> is the number of the item to renew.
 
 C<$GetLatestAutoRenewDate> returns the DateTime of the latest possible
-auto renew date, based on the value "No auto renewal after" of the applicable
-issuing rule.
+auto renew date, based on the value "No auto renewal after" and the "No auto
+renewal after (hard limit) of the applicable issuing rule.
 Returns undef if there is no date specify in the circ rules or if the patron, loan,
 or item cannot be found.
 
@@ -3082,9 +3103,9 @@ sub GetLatestAutoRenewDate {
     my $dbh = C4::Context->dbh;
 
     my $item      = GetItem($itemnumber)      or return;
-    my $itemissue = GetItemIssue($itemnumber) or return;
+    my $itemissue = Koha::Checkouts->find( { itemnumber => $itemnumber } ) or return;
 
-    $borrowernumber ||= $itemissue->{borrowernumber};
+    $borrowernumber ||= $itemissue->borrowernumber;
     my $borrower = C4::Members::GetMember( borrowernumber => $borrowernumber )
       or return;
 
@@ -3097,14 +3118,24 @@ sub GetLatestAutoRenewDate {
     );
 
     return unless $issuing_rule;
-    return if not $issuing_rule->no_auto_renewal_after
-               or $issuing_rule->no_auto_renewal_after eq '';
-
-    my $maximum_renewal_date = dt_from_string($itemissue->{issuedate});
-    $maximum_renewal_date->add(
-        $issuing_rule->lengthunit => $issuing_rule->no_auto_renewal_after
-    );
+    return
+      if ( not $issuing_rule->no_auto_renewal_after
+            or $issuing_rule->no_auto_renewal_after eq '' )
+      and ( not $issuing_rule->no_auto_renewal_after_hard_limit
+             or $issuing_rule->no_auto_renewal_after_hard_limit eq '' );
+
+    my $maximum_renewal_date;
+    if ( $issuing_rule->no_auto_renewal_after ) {
+        $maximum_renewal_date = dt_from_string($itemissue->issuedate);
+        $maximum_renewal_date->add(
+            $issuing_rule->lengthunit => $issuing_rule->no_auto_renewal_after
+        );
+    }
 
+    if ( $issuing_rule->no_auto_renewal_after_hard_limit ) {
+        my $dt = dt_from_string( $issuing_rule->no_auto_renewal_after_hard_limit );
+        $maximum_renewal_date = $dt if not $maximum_renewal_date or $maximum_renewal_date > $dt;
+    }
     return $maximum_renewal_date;
 }
 
@@ -3359,12 +3390,20 @@ sub SendCirculationAlert {
 
     my $schema = Koha::Database->new->schema;
     my @transports = keys %{ $borrower_preferences->{transports} };
+
+    # From the MySQL doc:
+    # LOCK TABLES is not transaction-safe and implicitly commits any active transaction before attempting to lock the tables.
+    # If the LOCK/UNLOCK statements are executed from tests, the current transaction will be committed.
+    # To avoid that we need to guess if this code is execute from tests or not (yes it is a bit hacky)
+    my $do_not_lock = ( exists $ENV{_} && $ENV{_} =~ m|prove| ) || $ENV{KOHA_NO_TABLE_LOCKS};
+
     for my $mtt (@transports) {
         my $letter =  C4::Letters::GetPreparedLetter (
             module => 'circulation',
             letter_code => $type,
             branchcode => $branch,
             message_transport_type => $mtt,
+            lang => $borrower->{lang},
             tables => {
                 $issues_table => $item->{itemnumber},
                 'items'       => $item->{itemnumber},
@@ -3376,17 +3415,17 @@ sub SendCirculationAlert {
         ) or next;
 
         $schema->storage->txn_begin;
-        C4::Context->dbh->do(q|LOCK TABLE message_queue READ|);
-        C4::Context->dbh->do(q|LOCK TABLE message_queue WRITE|);
+        C4::Context->dbh->do(q|LOCK TABLE message_queue READ|) unless $do_not_lock;
+        C4::Context->dbh->do(q|LOCK TABLE message_queue WRITE|) unless $do_not_lock;
         my $message = C4::Message->find_last_message($borrower, $type, $mtt);
         unless ( $message ) {
-            C4::Context->dbh->do(q|UNLOCK TABLES|);
+            C4::Context->dbh->do(q|UNLOCK TABLES|) unless $do_not_lock;
             C4::Message->enqueue($letter, $borrower, $mtt);
         } else {
             $message->append($letter);
             $message->update;
         }
-        C4::Context->dbh->do(q|UNLOCK TABLES|);
+        C4::Context->dbh->do(q|UNLOCK TABLES|) unless $do_not_lock;
         $schema->storage->txn_commit;
     }
 
@@ -3853,7 +3892,7 @@ sub IsItemIssued {
   my ($ageRestriction, $daysToAgeRestriction) = GetAgeRestriction($record_restrictions, $borrower);
   my ($ageRestriction, $daysToAgeRestriction) = GetAgeRestriction($record_restrictions);
 
-  if($daysToAgeRestriction <= 0) { #Borrower is allowed to access this material, as he is older or as old as the agerestriction }
+  if($daysToAgeRestriction <= 0) { #Borrower is allowed to access this material, as they are older or as old as the agerestriction }
   if($daysToAgeRestriction > 0) { #Borrower is this many days from meeting the agerestriction }
 
 @PARAM1 the koha.biblioitems.agerestriction value, like K18, PEGI 13, ...