=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 a count
-of the reserves and an arrayref pointing to the reserves for C<$biblionumber>.
+This function gets the list of reservations for one C<$biblionumber>,
+returning 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
}
push @results, $data;
}
- return ( $#results + 1, \@results );
+ return \@results;
}
=head2 GetReservesFromItemnumber
- ( $reservedate, $borrowernumber, $branchcode, $reserve_id ) = GetReservesFromItemnumber($itemnumber);
+ ( $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.
-TODO :: Description here
+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
+ 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()";
- }
my $sth_res = $dbh->prepare($query);
$sth_res->execute($itemnumber);
- my ( $reservedate, $borrowernumber, $branchcode, $reserve_id ) = $sth_res->fetchrow_array;
- return ( $reservedate, $borrowernumber, $branchcode, $reserve_id );
+ my ( $reservedate, $borrowernumber,$branchcode, $reserve_id, $wait ) = $sth_res->fetchrow_array;
+ return ( $reservedate, $borrowernumber, $branchcode, $reserve_id, $wait );
}
=head2 GetReservesFromBorrowernumber
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
";
- my $itemtype = $item->{$itype};
- my $categorycode = $borrower->{categorycode};
my $branchcode = "";
my $branchfield = "reserves.branchcode";
}
# 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";
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
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
$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);
$sth->execute( $reserve_id );
# now fix the priority on the others....
- _FixPriority({
- reserve_id => $reserve_id,
- biblionumber => $reserve->{biblionumber},
- });
+ _FixPriority({ biblionumber => $reserve->{biblionumber} });
}
=head2 ModReserve
$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 );
}
";
my $sth_upd = $dbh->prepare($query);
$sth_upd->execute( $itemnumber, $reserve_id );
- # second step update all others reservs
+ # second step update all others reserves
_FixPriority({ reserve_id => $reserve_id, rank => '0' });
}
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';
=head2 _FixPriority
- &_FixPriority({ reserve_id => $reserve_id, rank => $rank, ignoreSetLowestRank => $ignoreSetLowestRank });
+ _FixPriority({
+ reserve_id => $reserve_id,
+ [rank => $rank,]
+ [ignoreSetLowestRank => $ignoreSetLowestRank]
+ });
+
+ or
+
+ _FixPriority({ biblionumber => $biblionumber});
+
+This routine adjusts the priority of a hold request and holds
+on the same bib.
-Only used internally (so don't export it)
-Changed how this functions works #
-Now just gets an array of reserves in the rank order and updates them with
-the array index (+1 as array starts from 0)
-and if $rank is supplied will splice item from the array and splice it back in again
-in new priority rank
+In the first form, where a reserve_id is passed, the priority of the
+hold is set to supplied rank, and other holds for that bib are adjusted
+accordingly. If the rank is "del", the hold is cancelled. If no rank
+is supplied, all of the holds on that bib have their priority adjusted
+as if the second form had been used.
-=cut
+In the second form, where a biblionumber is passed, the holds on that
+bib (that are not captured) are sorted in order of increasing priority,
+then have reserves.priority set so that the first non-captured hold
+has its priority set to 1, the second non-captured hold has its priority
+set to 2, and so forth.
+
+In both cases, holds that have the lowestPriority flag on are have their
+priority adjusted to ensure that they remain at the end of the line.
+
+Note that the ignoreSetLowestRank parameter is meant to be used only
+when _FixPriority calls itself.
+
+=cut
sub _FixPriority {
my ( $params ) = @_;
my $reserve_id = $params->{reserve_id};
- my $rank = $params->{rank};
+ my $rank = $params->{rank} // '';
my $ignoreSetLowestRank = $params->{ignoreSetLowestRank};
my $biblionumber = $params->{biblionumber};
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)
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 = ?
# 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 *
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
=head2 RevertWaitingStatus
- $success = RevertWaitingStatus({ itemnumber => $itemnumber });
+ RevertWaitingStatus({ itemnumber => $itemnumber });
Reverts a 'waiting' hold back to a regular hold with a priority of 1.
reserve_id = ?
";
$sth = $dbh->prepare( $query );
- return $sth->execute( $reserve->{'reserve_id'} );
+ $sth->execute( $reserve->{'reserve_id'} );
+ _FixPriority( { biblionumber => $reserve->{biblionumber} } );
}
=head2 GetReserveId
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/>