Bug 12495 - Include streetnumber in hold alert address
[koha_fer] / C4 / Reserves.pm
index afef863..a98eed4 100644 (file)
@@ -268,17 +268,30 @@ sub GetReserve {
 
 =head2 GetReservesFromBiblionumber
 
-  ($count, $title_reserves) = GetReservesFromBiblionumber($biblionumber);
+  my $reserves = GetReservesFromBiblionumber({
+    biblionumber => $biblionumber,
+    [ itemnumber => $itemnumber, ]
+    [ all_dates => 1|0 ]
+  });
+
+This function gets the list of reservations for one C<$biblionumber>,
+returning an arrayref pointing to the reserves for C<$biblionumber>.
 
-This function gets the list of reservations for one C<$biblionumber>, returning a count
-of the reserves and an arrayref pointing to the reserves for C<$biblionumber>.
+By default, only reserves whose start date falls before the current
+time are returned.  To return all reserves, including future ones,
+the C<all_dates> parameter can be included and set to a true value.
+
+If the C<itemnumber> parameter is supplied, reserves must be targeted
+to that item or not targeted to any item at all; otherwise, they
+are excluded from the list.
 
 =cut
 
 sub GetReservesFromBiblionumber {
-    my ($biblionumber) = shift or return (0, []);
-    my ($all_dates) = shift;
-    my ($itemnumber) = shift;
+    my ( $params ) = @_;
+    my $biblionumber = $params->{biblionumber} or return [];
+    my $itemnumber = $params->{itemnumber};
+    my $all_dates = $params->{all_dates} // 0;
     my $dbh   = C4::Context->dbh;
 
     # Find the desired items in the reserves
@@ -353,31 +366,29 @@ sub GetReservesFromBiblionumber {
         }
         push @results, $data;
     }
-    return ( $#results + 1, \@results );
+    return \@results;
 }
 
 =head2 GetReservesFromItemnumber
 
- ( $reservedate, $borrowernumber, $branchcode, $reserve_id, $waitingdate ) = GetReservesFromItemnumber($itemnumber, $all_dates);
+ ( $reservedate, $borrowernumber, $branchcode, $reserve_id, $waitingdate ) = GetReservesFromItemnumber($itemnumber);
 
 Get the first reserve for a specific item number (based on priority). Returns the abovementioned values for that reserve.
 
-all_dates is an optional parameter, telling Koha to include or exclude future holds
+The routine does not look at future reserves (read: item level holds), but DOES include future waits (a confirmed future hold).
 
 =cut
 
 sub GetReservesFromItemnumber {
-    my ( $itemnumber, $all_dates ) = @_;
+    my ( $itemnumber ) = @_;
     my $dbh   = C4::Context->dbh;
     my $query = "
     SELECT reservedate,borrowernumber,branchcode,reserve_id,waitingdate
     FROM   reserves
-    WHERE  itemnumber=?
+    WHERE  itemnumber=? AND ( reservedate <= CAST(now() AS date) OR
+           waitingdate IS NOT NULL )
+    ORDER BY priority
     ";
-    unless ( $all_dates ) {
-       $query .= " AND reservedate <= CURRENT_DATE()";
-    }
-    $query .= ' ORDER BY priority';
     my $sth_res = $dbh->prepare($query);
     $sth_res->execute($itemnumber);
     my ( $reservedate, $borrowernumber,$branchcode, $reserve_id, $wait ) = $sth_res->fetchrow_array;
@@ -452,15 +463,21 @@ sub CanItemBeReserved{
     my ($borrowernumber, $itemnumber) = @_;
     
     my $dbh             = C4::Context->dbh;
+    my $ruleitemtype; # itemtype of the matching issuing rule
     my $allowedreserves = 0;
             
-    my $controlbranch = C4::Context->preference('ReservesControlBranch');
-    my $itype         = C4::Context->preference('item-level_itypes') ? "itype" : "itemtype";
-
     # we retrieve borrowers and items informations #
-    my $item     = GetItem($itemnumber);
+    # item->{itype} will come for biblioitems if necessery
+    my $item = GetItem($itemnumber);
+
+    # If an item is damaged and we don't allow holds on damaged items, we can stop right here
+    return 0 if ( $item->{damaged} && !C4::Context->preference('AllowHoldsOnDamagedItems') );
+
     my $borrower = C4::Members::GetMember('borrowernumber'=>$borrowernumber);     
     
+    my $controlbranch = C4::Context->preference('ReservesControlBranch');
+    my $itemtypefield = C4::Context->preference('item-level_itypes') ? "itype" : "itemtype";
+
     # we retrieve user rights on this itemtype and branchcode
     my $sth = $dbh->prepare("SELECT categorycode, itemtype, branchcode, reservesallowed 
                              FROM issuingrules 
@@ -483,8 +500,6 @@ sub CanItemBeReserved{
                                 ";
     
     
-    my $itemtype     = $item->{$itype};
-    my $categorycode = $borrower->{categorycode};
     my $branchcode   = "";
     my $branchfield  = "reserves.branchcode";
     
@@ -497,25 +512,25 @@ sub CanItemBeReserved{
     }
     
     # we retrieve rights 
-    $sth->execute($categorycode, $itemtype, $branchcode);
+    $sth->execute($borrower->{'categorycode'}, $item->{'itype'}, $branchcode);
     if(my $rights = $sth->fetchrow_hashref()){
-        $itemtype        = $rights->{itemtype};
+        $ruleitemtype    = $rights->{itemtype};
         $allowedreserves = $rights->{reservesallowed}; 
     }else{
-        $itemtype = '*';
+        $ruleitemtype = '*';
     }
     
     # we retrieve count
     
     $querycount .= "AND $branchfield = ?";
     
-    $querycount .= " AND $itype = ?" if ($itemtype ne "*");
+    $querycount .= " AND $itemtypefield = ?" if ($ruleitemtype ne "*");
     my $sthcount = $dbh->prepare($querycount);
     
-    if($itemtype eq "*"){
+    if($ruleitemtype eq "*"){
         $sthcount->execute($borrowernumber, $branchcode);
     }else{
-        $sthcount->execute($borrowernumber, $branchcode, $itemtype);
+        $sthcount->execute($borrowernumber, $branchcode, $ruleitemtype);
     }
     
     my $reservecount = "0";
@@ -858,7 +873,8 @@ sub CheckReserves {
            items.biblioitemnumber,
            itemtypes.notforloan,
            items.notforloan AS itemnotforloan,
-           items.itemnumber
+           items.itemnumber,
+           items.damaged
            FROM   items
            LEFT JOIN biblioitems ON items.biblioitemnumber = biblioitems.biblioitemnumber
            LEFT JOIN itemtypes   ON items.itype   = itemtypes.itemtype
@@ -870,7 +886,8 @@ sub CheckReserves {
            items.biblioitemnumber,
            itemtypes.notforloan,
            items.notforloan AS itemnotforloan,
-           items.itemnumber
+           items.itemnumber,
+           items.damaged
            FROM   items
            LEFT JOIN biblioitems ON items.biblioitemnumber = biblioitems.biblioitemnumber
            LEFT JOIN itemtypes   ON biblioitems.itemtype   = itemtypes.itemtype
@@ -886,13 +903,15 @@ sub CheckReserves {
         $sth->execute($barcode);
     }
     # note: we get the itemnumber because we might have started w/ just the barcode.  Now we know for sure we have it.
-    my ( $biblio, $bibitem, $notforloan_per_itemtype, $notforloan_per_item, $itemnumber ) = $sth->fetchrow_array;
+    my ( $biblio, $bibitem, $notforloan_per_itemtype, $notforloan_per_item, $itemnumber, $damaged ) = $sth->fetchrow_array;
+
+    return if ( $damaged && !C4::Context->preference('AllowHoldsOnDamagedItems') );
 
-    return ( '' ) unless $itemnumber; # bail if we got nothing.
+    return unless $itemnumber; # bail if we got nothing.
 
     # if item is not for loan it cannot be reserved either.....
     #    execpt where items.notforloan < 0 :  This indicates the item is holdable. 
-    return ( '' ) if  ( $notforloan_per_item > 0 ) or $notforloan_per_itemtype;
+    return if  ( $notforloan_per_item > 0 ) or $notforloan_per_itemtype;
 
     # Find this item in the reserves
     my @reserves = _Findgroupreserve( $bibitem, $biblio, $itemnumber, $lookahead_days);
@@ -1277,7 +1296,7 @@ sub ModReserveAffect {
     $sth = $dbh->prepare($query);
     $sth->execute( $itemnumber, $borrowernumber,$biblionumber);
     _koha_notify_reserve( $itemnumber, $borrowernumber, $biblionumber ) if ( !$transferToDo && !$already_on_shelf );
-
+    _FixPriority( { biblionumber => $biblionumber } );
     if ( C4::Context->preference("ReturnToShelvingCart") ) {
       CartToShelf( $itemnumber );
     }
@@ -1526,7 +1545,13 @@ be cleared when it is unsuspended.
 sub ToggleSuspend {
     my ( $reserve_id, $suspend_until ) = @_;
 
-    $suspend_until = output_pref({ dt => dt_from_string( $suspend_until ), dateformat => 'iso' }) if ( $suspend_until );
+    $suspend_until = output_pref(
+        {
+            dt         => dt_from_string($suspend_until),
+            dateformat => 'iso',
+            dateonly   => 1
+        }
+    ) if ($suspend_until);
 
     my $do_until = ( $suspend_until ) ? '?' : 'NULL';
 
@@ -1794,7 +1819,8 @@ sub _Findgroupreserve {
                reserves.priority            AS priority,
                reserves.timestamp           AS timestamp,
                biblioitems.biblioitemnumber AS biblioitemnumber,
-               reserves.itemnumber          AS itemnumber
+               reserves.itemnumber          AS itemnumber,
+               reserves.reserve_id          AS reserve_id
         FROM reserves
         JOIN biblioitems USING (biblionumber)
         JOIN hold_fill_targets USING (biblionumber, borrowernumber)
@@ -1825,7 +1851,8 @@ sub _Findgroupreserve {
                reserves.priority                   AS priority,
                reserves.timestamp                  AS timestamp,
                reserveconstraints.biblioitemnumber AS biblioitemnumber,
-               reserves.itemnumber                 AS itemnumber
+               reserves.itemnumber                 AS itemnumber,
+               reserves.reserve_id                 AS reserve_id
         FROM reserves
           LEFT JOIN reserveconstraints ON reserves.biblionumber = reserveconstraints.biblionumber
         WHERE reserves.biblionumber = ?
@@ -1863,15 +1890,11 @@ sub _koha_notify_reserve {
     
     # Try to get the borrower's email address
     my $to_address = C4::Members::GetNoticeEmailAddress($borrowernumber);
-    
-    my $letter_code;
-    my $print_mode = 0;
-    my $messagingprefs;
-    if ( $to_address || $borrower->{'smsalertnumber'} ) {
-        $messagingprefs = C4::Members::Messaging::GetMessagingPreferences( { borrowernumber => $borrowernumber, message_name => 'Hold_Filled' } );
-    } else {
-        $print_mode = 1;
-    }
+
+    my $messagingprefs = C4::Members::Messaging::GetMessagingPreferences( {
+            borrowernumber => $borrowernumber,
+            message_name => 'Hold_Filled'
+    } );
 
     my $sth = $dbh->prepare("
         SELECT *
@@ -1898,44 +1921,39 @@ sub _koha_notify_reserve {
         substitute => { today => C4::Dates->new()->output() },
     );
 
-
-    if ( $print_mode ) {
-        $letter_params{ 'letter_code' } = 'HOLD_PRINT';
-        my $letter =  C4::Letters::GetPreparedLetter ( %letter_params ) or die "Could not find a letter called '$letter_params{'letter_code'}' in the 'reserves' module";
+    my $notification_sent = 0; #Keeping track if a Hold_filled message is sent. If no message can be sent, then default to a print message.
+    my $send_notification = sub {
+        my ( $mtt, $letter_code ) = (@_);
+        return unless defined $letter_code;
+        $letter_params{letter_code} = $letter_code;
+        $letter_params{message_transport_type} = $mtt;
+        my $letter =  C4::Letters::GetPreparedLetter ( %letter_params );
+        unless ($letter) {
+            warn "Could not find a letter called '$letter_params{'letter_code'}' for $mtt in the 'reserves' module";
+            return;
+        }
 
         C4::Letters::EnqueueLetter( {
             letter => $letter,
             borrowernumber => $borrowernumber,
-            message_transport_type => 'print',
+            from_address => $admin_email_address,
+            message_transport_type => $mtt,
         } );
-        
-        return;
-    }
-
-    if ( $to_address && defined $messagingprefs->{transports}->{'email'} ) {
-        $letter_params{ 'letter_code' } = $messagingprefs->{transports}->{'email'};
-        my $letter =  C4::Letters::GetPreparedLetter ( %letter_params ) or die "Could not find a letter called '$letter_params{'letter_code'}' in the 'reserves' module";
+    };
 
-        C4::Letters::EnqueueLetter(
-            {   letter                 => $letter,
-                borrowernumber         => $borrowernumber,
-                message_transport_type => 'email',
-                from_address           => $admin_email_address,
-            }
-        );
+    while ( my ( $mtt, $letter_code ) = each %{ $messagingprefs->{transports} } ) {
+        if ( ($mtt eq 'email' and not $to_address) or ($mtt eq 'sms' and not $borrower->{smsalertnumber}) ) {
+            # email or sms is requested but not exist
+            next;
+        }
+        &$send_notification($mtt, $letter_code);
+        $notification_sent++;
     }
-
-    if ( $borrower->{'smsalertnumber'} && defined $messagingprefs->{transports}->{'sms'} ) {
-        $letter_params{ 'letter_code' } = $messagingprefs->{transports}->{'sms'};
-        my $letter =  C4::Letters::GetPreparedLetter ( %letter_params ) or die "Could not find a letter called '$letter_params{'letter_code'}' in the 'reserves' module";
-
-        C4::Letters::EnqueueLetter(
-            {   letter                 => $letter,
-                borrowernumber         => $borrowernumber,
-                message_transport_type => 'sms',
-            }
-        );
+    #Making sure that a print notification is sent if no other transport types can be utilized.
+    if (! $notification_sent) {
+        &$send_notification('print', 'HOLD');
     }
+    
 }
 
 =head2 _ShiftPriorityByDateAndPriority
@@ -2086,7 +2104,7 @@ sub MergeHolds {
 
 =head2 RevertWaitingStatus
 
-  $success = RevertWaitingStatus({ itemnumber => $itemnumber });
+  RevertWaitingStatus({ itemnumber => $itemnumber });
 
   Reverts a 'waiting' hold back to a regular hold with a priority of 1.
 
@@ -2141,7 +2159,8 @@ sub RevertWaitingStatus {
       reserve_id = ?
     ";
     $sth = $dbh->prepare( $query );
-    return $sth->execute( $reserve->{'reserve_id'} );
+    $sth->execute( $reserve->{'reserve_id'} );
+    _FixPriority( { biblionumber => $reserve->{biblionumber} } );
 }
 
 =head2 GetReserveId
@@ -2238,6 +2257,51 @@ sub GetReservesControlBranch {
     return $branchcode;
 }
 
+=head2 CalculatePriority
+
+    my $p = CalculatePriority($biblionumber, $resdate);
+
+Calculate priority for a new reserve on biblionumber, placing it at
+the end of the line of all holds whose start date falls before
+the current system time and that are neither on the hold shelf
+or in transit.
+
+The reserve date parameter is optional; if it is supplied, the
+priority is based on the set of holds whose start date falls before
+the parameter value.
+
+After calculation of this priority, it is recommended to call
+_ShiftPriorityByDateAndPriority. Note that this is currently done in
+AddReserves.
+
+=cut
+
+sub CalculatePriority {
+    my ( $biblionumber, $resdate ) = @_;
+
+    my $sql = q{
+        SELECT COUNT(*) FROM reserves
+        WHERE biblionumber = ?
+        AND   priority > 0
+        AND   (found IS NULL OR found = '')
+    };
+    #skip found==W or found==T (waiting or transit holds)
+    if( $resdate ) {
+        $sql.= ' AND ( reservedate <= ? )';
+    }
+    else {
+        $sql.= ' AND ( reservedate < NOW() )';
+    }
+    my $dbh = C4::Context->dbh();
+    my @row = $dbh->selectrow_array(
+        $sql,
+        undef,
+        $resdate ? ($biblionumber, $resdate) : ($biblionumber)
+    );
+
+    return @row ? $row[0]+1 : 1;
+}
+
 =head1 AUTHOR
 
 Koha Development Team <http://koha-community.org/>