Bug 28290: Don't send subfields to 'as_string' if none to send
[koha-ffzg.git] / C4 / HoldsQueue.pm
index b4d2b02..8173efa 100644 (file)
@@ -23,8 +23,7 @@ use strict;
 use warnings;
 
 use C4::Context;
-use C4::Search;
-use C4::Circulation qw( GetTransfers GetBranchItemRule );
+use C4::Circulation qw( GetBranchItemRule );
 use Koha::DateUtils qw( dt_from_string );
 use Koha::Items;
 use Koha::Patrons;
@@ -44,6 +43,8 @@ BEGIN {
         TransportCostMatrix
         UpdateTransportCostMatrix
         GetPendingHoldRequestsForBib
+        load_branches_to_pull_from
+        update_queue_for_biblio
      );
 }
 
@@ -114,14 +115,14 @@ sub UpdateTransportCostMatrix {
 
 =head2 GetHoldsQueueItems
 
-  GetHoldsQueueItems($branch);
+  GetHoldsQueueItems({ branchlimit => $branch, itemtypeslimit =>  $itype, ccodeslimit => $ccode, locationslimit => $location );
 
 Returns hold queue for a holding branch. If branch is omitted, then whole queue is returned
 
 =cut
 
 sub GetHoldsQueueItems {
-    my ($branchlimit) = @_;
+    my $params = shift;
     my $dbh   = C4::Context->dbh;
 
     my @bind_params = ();
@@ -135,10 +136,23 @@ sub GetHoldsQueueItems {
                        JOIN biblio      USING (biblionumber)
                   LEFT JOIN biblioitems USING (biblionumber)
                   LEFT JOIN items       USING (  itemnumber)
+                  WHERE 1=1
                 /;
-    if ($branchlimit) {
-        $query .=" WHERE tmp_holdsqueue.holdingbranch = ?";
-        push @bind_params, $branchlimit;
+    if ($params->{branchlimit}) {
+        $query .="AND tmp_holdsqueue.holdingbranch = ? ";
+        push @bind_params, $params->{branchlimit};
+    }
+    if( $params->{itemtypeslimit} ) {
+        $query .=" AND items.itype = ? ";
+        push @bind_params, $params->{itemtypeslimit};
+    }
+    if( $params->{ccodeslimit} ) {
+        $query .=" AND items.ccode = ? ";
+        push @bind_params, $params->{ccodeslimit};
+    }
+    if( $params->{locationslimit} ) {
+        $query .=" AND items.location = ? ";
+        push @bind_params, $params->{locationslimit};
     }
     $query .= " ORDER BY ccode, location, cn_sort, author, title, pickbranch, reservedate";
     my $sth = $dbh->prepare($query);
@@ -191,27 +205,19 @@ sub CreateQueue {
     my $bibs_with_pending_requests = GetBibsWithPendingHoldRequests();
 
     foreach my $biblionumber (@$bibs_with_pending_requests) {
+
         $total_bibs++;
-        my $hold_requests   = GetPendingHoldRequestsForBib($biblionumber);
-        my $available_items = GetItemsAvailableToFillHoldRequestsForBib($biblionumber, $branches_to_use);
-        $total_requests        += scalar(@$hold_requests);
-        $total_available_items += scalar(@$available_items);
 
-        my $item_map = MapItemsToHoldRequests($hold_requests, $available_items, $branches_to_use, $transport_cost_matrix);
-        $item_map  or next;
-        my $item_map_size = scalar(keys %$item_map)
-          or next;
+        my $result = update_queue_for_biblio(
+            {   biblio_id             => $biblionumber,
+                branches_to_use       => $branches_to_use,
+                transport_cost_matrix => $transport_cost_matrix,
+            }
+        );
 
-        $num_items_mapped += $item_map_size;
-        CreatePicklistFromItemMap($item_map);
-        AddToHoldTargetMap($item_map);
-        if (($item_map_size < scalar(@$hold_requests  )) and
-            ($item_map_size < scalar(@$available_items))) {
-            # DOUBLE CHECK, but this is probably OK - unfilled item-level requests
-            # FIXME
-            #warn "unfilled requests for $biblionumber";
-            #warn Dumper($hold_requests), Dumper($available_items), Dumper($item_map);
-        }
+        $total_requests        += $result->{requests};
+        $total_available_items += $result->{available_items};
+        $num_items_mapped      += $result->{mapped_items};
     }
 }
 
@@ -308,7 +314,7 @@ sub GetItemsAvailableToFillHoldRequestsForBib {
     my ($biblionumber, $branches_to_use) = @_;
 
     my $dbh = C4::Context->dbh;
-    my $items_query = "SELECT itemnumber, homebranch, holdingbranch, itemtypes.itemtype AS itype
+    my $items_query = "SELECT items.itemnumber, homebranch, holdingbranch, itemtypes.itemtype AS itype
                        FROM items ";
 
     if (C4::Context->preference('item-level_itypes')) {
@@ -317,14 +323,17 @@ sub GetItemsAvailableToFillHoldRequestsForBib {
         $items_query .=   "JOIN biblioitems USING (biblioitemnumber)
                            LEFT JOIN itemtypes USING (itemtype) ";
     }
-    $items_query .=   "WHERE items.notforloan = 0
+    $items_query .=  " LEFT JOIN branchtransfers ON (items.itemnumber = branchtransfers.itemnumber)";
+    $items_query .=  " WHERE items.notforloan = 0
                        AND holdingbranch IS NOT NULL
                        AND itemlost = 0
                        AND withdrawn = 0";
+    $items_query .= "  AND branchtransfers.datearrived IS NULL
+                       AND branchtransfers.datecancelled IS NULL";
     $items_query .= "  AND damaged = 0" unless C4::Context->preference('AllowHoldsOnDamagedItems');
     $items_query .= "  AND items.onloan IS NULL
                        AND (itemtypes.notforloan IS NULL OR itemtypes.notforloan = 0)
-                       AND itemnumber NOT IN (
+                       AND items.itemnumber NOT IN (
                            SELECT itemnumber
                            FROM reserves
                            WHERE biblionumber = ?
@@ -342,12 +351,11 @@ sub GetItemsAvailableToFillHoldRequestsForBib {
     $sth->execute(@params);
 
     my $itm = $sth->fetchall_arrayref({});
-    my @items = grep { ! scalar GetTransfers($_->{itemnumber}) } @$itm;
     return [ grep {
-        my $rule = GetBranchItemRule($_->{homebranch}, $_->{itype});
+        my $rule = C4::Circulation::GetBranchItemRule($_->{homebranch}, $_->{itype});
         $_->{holdallowed} = $rule->{holdallowed};
         $_->{hold_fulfillment_policy} = $rule->{hold_fulfillment_policy};
-    } @items ];
+    } @{$itm} ];
 }
 
 =head2 _checkHoldPolicy
@@ -416,7 +424,7 @@ sub MapItemsToHoldRequests {
 
     map { $_->{_object} = Koha::Items->find( $_->{itemnumber} ) } @$available_items;
     my $libraries = {};
-    map { $libraries->{$_->id} = $_ } Koha::Libraries->search();
+    map { $libraries->{$_->id} = $_ } Koha::Libraries->search->as_list;
 
     # group available items by itemnumber
     my %items_by_itemnumber = map { $_->{itemnumber} => $_ } @$available_items;
@@ -842,5 +850,90 @@ sub least_cost_branch {
     # return $branch[0] if @branch == 1;
 }
 
+=head3 update_queue_for_biblio
+
+    my $result = update_queue_for_biblio(
+        {
+            biblio_id             => $biblio_id,
+          [ branches_to_use       => $branches_to_use,
+            transport_cost_matrix => $transport_cost_matrix,
+            delete                => $delete, ]
+        }
+    );
+
+Given a I<biblio_id>, this method calculates and sets the holds queue entries
+for the biblio's holds, and the hold fill targets (items).
+
+=head4 Return value
+
+It return a hashref containing:
+
+=over
+
+=item I<requests>: the pending holds count for the biblio.
+
+=item I<available_items> the count of items that are available to fill holds for the biblio.
+
+=item I<mapped_items> the total items that got mapped.
+
+=back
+
+=head4 Optional parameters
+
+=over
+
+=item I<branches_to_use> a list of branchcodes to be used to restrict which items can be used.
+
+=item I<transport_cost_matrix> is the output of C<TransportCostMatrix>.
+
+=item I<delete> tells the method to delete prior entries on the related tables for the biblio_id.
+
+=back
+
+Note: All the optional parameters will be calculated in the method if omitted. They
+are allowed to be passed to avoid calculating them many times inside loops.
+
+=cut
+
+sub update_queue_for_biblio {
+    my ($args) = @_;
+
+    my $biblio_id = $args->{biblio_id};
+
+    my $branches_to_use = $args->{branches_to_use} // load_branches_to_pull_from( C4::Context->preference('UseTransportCostMatrix') );
+    my $transport_cost_matrix;
+
+    if ( !exists $args->{transport_cost_matrix}
+        && C4::Context->preference('UseTransportCostMatrix') ) {
+        $transport_cost_matrix = TransportCostMatrix();
+    } else {
+        $transport_cost_matrix = $args->{transport_cost_matrix};
+    }
+
+    if ( $args->{delete} ) {
+        my $dbh = C4::Context->dbh;
+
+        $dbh->do("DELETE FROM tmp_holdsqueue WHERE biblionumber=$biblio_id");
+        $dbh->do("DELETE FROM hold_fill_targets WHERE biblionumber=$biblio_id");
+    }
+
+    my $hold_requests   = GetPendingHoldRequestsForBib($biblio_id);
+    my $available_items = GetItemsAvailableToFillHoldRequestsForBib( $biblio_id, $branches_to_use );
+
+    my $result = {
+        requests        => scalar( @{$hold_requests} ),
+        available_items => scalar( @{$available_items} ),
+    };
+
+    my $item_map = MapItemsToHoldRequests( $hold_requests, $available_items, $branches_to_use, $transport_cost_matrix );
+    $result->{mapped_items} = scalar( keys %{$item_map} );
+
+    if ($item_map) {
+        CreatePicklistFromItemMap($item_map);
+        AddToHoldTargetMap($item_map);
+    }
+
+    return $result;
+}
 
 1;