From 2731c8c68ba508d8502adf3db770df90f03b9fc9 Mon Sep 17 00:00:00 2001 From: Alex Arnaud <alex.arnaud@biblibre.com> Date: Wed, 12 Oct 2016 07:02:19 +0000 Subject: [PATCH] Bug 15565: Place multiple holds at once for the same record on staff and OPAC. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Test plan: 1. Create an circulation rule to use for testing the patches on this bug report. Ensure it has a value greater than 1 for 'Holds allowed', 'Holds per record (count)', and 'Item level holds' is enabled. 2. Also ensure the 'RequestOnOpac' syspref is set to 'Allow'. 3. Go to the reservation page (request.pl) of a biblio in the staff client with more than one item. 4. Notice beside each item a radio button is displayed preventing you from being able to select multiple items to place a hold on. 5. Go to the reservation page (opac-reserve.pl) of the same biblio in the OPAC also notice a radio button is displayed beside every item. 6. Apply patches 7. Repeat step 3 and 5 and notice in both the staff client and OPAC checkboxes rather than radio buttons are displayed beside each item so you can select multiple items for reservation at a time. 8. From staff interface, place several item-level holds for the same user on the same record at once: - check that you are warned if you exceed "Holds per record (count)" (circulation rules) for this record. - check also maxReserves system preference. 9. From opac, place several item-level and record level holds for the same user on the same record at once: - check that can't exceed "Holds per record (count)" for this record. This is enforced by all unticked item checkboxes being disabled after the number of ticked checkboxes equals the 'Holds per record (count)' (circulation rules) value. - check also maxReserves system preference. Do the same in OPAC and staff by selecting several record (multi-hold). Signed-off-by: Séverine QUEUNE <severine.queune@bulac.fr> Signed-off-by: Kyle M Hall <kyle@bywatersolutions.com> Signed-off-by: Benjamin Rokseth <benjamin.rokseth@deichman.no> Signed-off-by: Katrin Fischer <katrin.fischer.83@web.de> --- .../prog/en/modules/reserve/request.tt | 16 +- .../opac-tmpl/bootstrap/en/modules/opac-reserve.tt | 200 ++++++++++----------- opac/opac-reserve.pl | 167 ++++++++--------- reserve/placerequest.pl | 38 ++-- 4 files changed, 210 insertions(+), 211 deletions(-) diff --git a/koha-tmpl/intranet-tmpl/prog/en/modules/reserve/request.tt b/koha-tmpl/intranet-tmpl/prog/en/modules/reserve/request.tt index 36d6a30..aa55c67 100644 --- a/koha-tmpl/intranet-tmpl/prog/en/modules/reserve/request.tt +++ b/koha-tmpl/intranet-tmpl/prog/en/modules/reserve/request.tt @@ -15,6 +15,7 @@ <title>Koha › Circulation › Holds › Confirm holds</title> [% END %] [% INCLUDE 'doc-head-close.inc' %] +[% Asset.css("css/datatables.css") | $raw %] </head> <body id="circ_request" class="catalog"> @@ -316,9 +317,9 @@ Hold must be record level </span> [% ELSIF ( itemloo.available ) %] - <input type="radio" name="checkitem" value="[% itemloo.itemnumber | html %]" /> + <input type="checkbox" name="checkitem" value="[% itemloo.itemnumber %]" /> [% ELSIF ( itemloo.override ) %] - <input type="radio" name="checkitem" class="needsoverride" value="[% itemloo.itemnumber | html %]" /> + <input type="checkbox" name="checkitem" class="needsoverride" value="[% itemloo.itemnumber %]" /> <i class="fa fa-exclamation-triangle fa-lg" style="color:gold" alt="Requires override of hold policy"/></i> [% ELSE %] <span class="error"> @@ -676,6 +677,8 @@ }; var MSG_NO_ITEMS_AVAILABLE = _("A hold cannot be requested on any of these items."); columns_settings_borrowers_table = [% ColumnsSettings.GetColumns( 'circ', 'circulation', 'table_borrowers', 'json' ) | $raw %] + var MSG_EXCEEDED_HOLDS_PER_RECORD = _("Patron has exceeded the number of holds for this record."); + var remainingHolds = "[% remaining_holds_for_record %]"; $(document).ready(function() { function ToggleHoldsToPlace() { @@ -810,17 +813,20 @@ } }); $("input[name=checkitem]").click(function() { - onechecked = 0; + numchecked = 0; $("input[name=checkitem]").each(function() { if(this.checked){ - onechecked = 1; + numchecked = 1; } }); - if(onechecked == 1){ + if(numchecked == 1){ $("#requestany").prop("checked", false); } else { $("#requestany").prop("checked",true); } + if (remainingHolds - numchecked < 0) { + alert(MSG_EXCEEDED_HOLDS_PER_RECORD); + } }); var prev_rank_request; $("select[name=rank-request]").on("focus", function() { diff --git a/koha-tmpl/opac-tmpl/bootstrap/en/modules/opac-reserve.tt b/koha-tmpl/opac-tmpl/bootstrap/en/modules/opac-reserve.tt index d643c80..491415e 100644 --- a/koha-tmpl/opac-tmpl/bootstrap/en/modules/opac-reserve.tt +++ b/koha-tmpl/opac-tmpl/bootstrap/en/modules/opac-reserve.tt @@ -151,20 +151,18 @@ <form action="/cgi-bin/koha/opac-reserve.pl" method="post" id="hold-request-form"> <input type="hidden" name="place_reserve" value="1"/> <!-- These values are set dynamically by js --> - <input type="hidden" name="biblionumbers" id="biblionumbers"/> - <input type="hidden" name="selecteditems" id="selections"/> <div id="bigloop"> [% FOREACH bibitemloo IN bibitemloop %] <div class="holdrow"> + <input id="remaining_holds_[% bibitemloo.biblionumber %]" type="hidden" value="[% bibitemloo.remaining_holds_for_record %]"/> <p> [% IF ( bibitemloo.holdable ) %] - <input class="reserve_mode" name="reserve_mode" type="hidden" value="single"/> - <input class="single_bib" name="single_bib" type="hidden" value="[% bibitemloo.biblionumber | html %]"/> - <span class="confirmjs_hold" title="[% bibitemloo.biblionumber | html %]" style="padding:.3em"></span> - <span class="confirm_nonjs"> - <input type="radio" class="confirmbox checkitem [% bibitemloo.biblionumber | html %]" name="[% bibitemloo.biblionumber | html %]" checked="checked" id="single_[% bibitemloo.biblionumber | html %]" value="any" /> - <label class="confirm_label" for="single_[% bibitemloo.biblionumber | html %]">Place a hold on </label> + <span title="[% bibitemloo.biblionumber %]" style="padding:.3em"> + <label> + <input class="biblionumbers" name="biblionumbers" type="checkbox" value="[% bibitemloo.biblionumber %]" checked="checked"> + Place a hold on + </label> </span> [% END # / bibitemloo.holdable %] @@ -230,7 +228,7 @@ <li class="branch"> <label for="branch_[% bibitemloo.biblionumber | html %]">Pick up location:</label> [% UNLESS ( bibitemloo.holdable ) %] - <select name="branch" id="branch_[% bibitemloo.biblionumber | html %]" disabled="disabled"> + <select name="branch_[% bibitemloo.biblionumber %]" id="branch_[% bibitemloo.biblionumber | html %]" disabled="disabled"> [% PROCESS options_for_libraries libraries => Branches.pickup_locations( biblio => bibitemloo.biblionumber, selected => branch ) %] </select> [% ELSE %] @@ -304,30 +302,40 @@ </li> [% END # / IF OpacHoldNotes %] - [% IF bibitemloo.itemholdable %] - <!-- ITEM HOLDS --> - <li class="lradio place_on_type" style="display:none;"> - [% IF NOT bibitemloo.force_hold %] - <label class="radio inline" for="reqany_[% bibitemloo.biblionumber | html %]">Next available item</label> - <input type="radio" name="reqtype_[% bibitemloo.biblionumber | html %]" - id="reqany_[% bibitemloo.biblionumber | html %]" - class="selectany" - value="Any" - checked="checked" - /> - [% END %] - <label class="radio inline" for="reqspecific_[% bibitemloo.biblionumber | html %]">A specific item</label> - <input type="radio" name="reqtype_[% bibitemloo.biblionumber | html %]" - id="reqspecific_[% bibitemloo.biblionumber | html %]" - class="selectspecific" - value="Specific" + <!-- ITEM HOLDS --> + <li class="lradio place_on_type" style="display:none;"> + <label class="radio inline" for="reqany_[% bibitemloo.biblionumber %]">Next available item</label> + <input type="radio" name="reqtype_[% bibitemloo.biblionumber %]" + id="reqany_[% bibitemloo.biblionumber %]" + class="selectany" + value="Any" + checked="checked" /> - </li> - [% END # / IF bibitemloo.itemholdable %] + <label class="radio inline" for="reqspecific_[% bibitemloo.biblionumber %]">A specific item</label> + <input type="radio" name="reqtype_[% bibitemloo.biblionumber %]" + id="reqspecific_[% bibitemloo.biblionumber %]" + class="selectspecific" + value="Specific" + /> + </li> </ul> - [% IF bibitemloo.itemholdable %] - <table class="copiesrow table table-bordered table-striped" id="copiesrow_[% bibitemloo.biblionumber | html %]"> + [% IF bibitemloo.remaining_holds_for_record > 1 %] + [% SET count = 1 %] + <div id="holds_to_place_[% bibitemloo.biblionumber %]" class="hold-options holds-to-place"> + <label>Holds to place (count)</label> + <select name="holds_to_place_count_[% bibitemloo.biblionumber %]"> + [% WHILE count <= bibitemloo.remaining_holds_for_record %] + <option value="[% count %]">[% count %]</option> + [% SET count = count + 1 %] + [% END %] + </select> + </div> + [% ELSE %] + <input type="hidden" name="holds_to_place_count_[% bibitemloo.biblionumber %]" value="1" /> + [% END %] + + <table class="copiesrow table table-bordered table-striped" id="copiesrow_[% bibitemloo.biblionumber %]"> <caption>Select a specific item:</caption> <tr> <th>Copy number</th> @@ -353,9 +361,9 @@ <tr class="[% itemLoo.backgroundcolor | html %]"> <td class="copynumber"> [% IF ( itemLoo.available ) %] - <input type="radio" class="checkitem checkitem_[% bibitemloo.biblionumber | html %]" name="checkitem_[% bibitemloo.biblionumber | html %]" value="[% itemLoo.itemnumber | html %]" /> + <input type="checkbox" class="checkitem checkitem_[% bibitemloo.biblionumber %]" name="checkitem_[% bibitemloo.biblionumber %]" value="[% itemLoo.itemnumber %]" /> [% ELSE %] - <input disabled="disabled" type="radio" class="checkitem" name="checkitem" value="[% itemLoo.itemnumber | html %]" + <input disabled="disabled" type="checkbox" class="checkitem" name="checkitem" value="[% itemLoo.itemnumber %]" style="display:none;" /> <img src="[% interface | html %]/lib/famfamfam/cross.png" alt="Cannot be put on hold" title="Cannot be put on hold" /> [% END %] @@ -431,9 +439,8 @@ </td> </tr> [% END # / FOREACH itemLoo IN bibitemloo.itemLoop%] - </table> <!-- / #copiesrow_[% bibitemloo.biblionumber | html %] --> - [% END # / IF ( bibitemloo.itemholdable )%] - </div> <!-- / #hold-options-[% bibitemloo.biblionumber | html %] --> + </table> <!-- / #copiesrow_[% bibitemloo.biblionumber %] --> + </div> <!-- / #hold-options-[% bibitemloo.biblionumber %] --> </fieldset> [% END # / IF ( bibitemloo.holdable ) %] </div> <!-- / .holdrow --> @@ -455,6 +462,7 @@ <script> // <![CDATA[ var MSG_NO_ITEM_SELECTED = _("Expecting a specific item selection."); + var MSG_MAX_HOLDS_EXCEEDED = _("Maximum number of reserve exceded."); // Clear the contents of an input field $(".clearfield").on("click",function(e){ @@ -464,8 +472,8 @@ // Select the first item available function select_first_available(id){ - var radios = $("input:radio[name='checkitem_" + id + "']"); - $(radios).first().attr("checked", "checked"); + var checkboxes = $("input:checkbox[name='checkitem_" + id + "']"); + $(checkboxes).first().attr("checked", "checked"); } $(document).ready(function() { @@ -486,14 +494,16 @@ } }); - // click on a first td check the confirmjs checkbox - $("td.hold").click(function(e){ - if(e.target.tagName.toLowerCase() == 'td'){ - $(this).find("input.confirmjs").each( function() { - $(this).attr('checked', !$(this).attr('checked')); - $(this).change(); - }); - } + $('.checkitem').click(function(e) { + var bibNum = suffixOf($(this).attr("name"), "_"); + var nbCheked = $("input[name='checkitem_"+bibNum+"']:checked").length; + var remaining = $("input[id='remaining_holds_"+bibNum+"']").val(); + if (nbCheked >= remaining) { + $("input[name='checkitem_"+bibNum+"']:not(:checked)").attr('disabled', 'disabled'); + } + else { + $('.checkitem_'+bibNum).attr('disabled', false); + } }); $(".toggle-hold-options").on("click",function(e){ @@ -508,15 +518,24 @@ // Hides all 'specific copy' table rows on load. $(".copiesrow").hide(); + $(".holds-to-place").show(); [% FOREACH bibitemloo IN bibitemloop %] - [% IF bibitemloo.force_hold %] - $("#toggle-hold-options-[% bibitemloo.biblionumber | html %]").click(); - $("#reqspecific_[% bibitemloo.biblionumber | html %]").click(); - $("#copiesrow_[% bibitemloo.biblionumber | html %]").show(); - [% END %] - [% IF bibitemloo.reqholdnotes %] - $("#holdnotes_[% bibitemloo.biblionumber | html %]").attr( 'required', true ); - [% END %] + [% IF bibitemloo.force_hold %] + [% IF bibitemloo.force_hold == 'item' %] + $("#toggle-hold-options-[% bibitemloo.biblionumber %]").click(); + $("#reqspecific_[% bibitemloo.biblionumber %]").attr('checked', true); + $("#holds_to_place_[% bibitemloo.biblionumber %]").hide(); + $("#copiesrow_[% bibitemloo.biblionumber %]").show(); + [% ELSIF bibitemloo.force_hold == 'record' %] + $("#toggle-hold-options-[% bibitemloo.biblionumber %]").click(); + $("#reqany_[% bibitemloo.biblionumber %]").attr('checked', true); + [% END %] + $("#reqany_[% bibitemloo.biblionumber %]").attr('disabled', 'disabled'); + $("#reqspecific_[% bibitemloo.biblionumber %]").attr('disabled', 'disabled'); + [% END %] + [% IF bibitemloo.reqholdnotes %] + $("#holdnotes_[% bibitemloo.biblionumber %]").attr( 'required', true ); + [% END %] [% END %] $(".date-format").each(function(){ @@ -533,40 +552,37 @@ $("#" + op + fieldID).val(""); }); - // Replace non-JS single-selection with multi-selection capability. - $(".reserve_mode").val("multi"); - $(".confirm_nonjs").remove(); - $(".confirmjs_hold").each(function(){ - var bib = $(this).attr("title"); - var html = "<label><input type =\"checkbox\" class=\"confirmjs\" checked=\"checked\""; - html += "value=\"" + bib + "\" id=\"" + bib + "\" /> " + _("Place a hold on") + " </label> "; - $(this).html(html); - }); - $(".confirmjs_nohold").each(function(){ - var bib = $(this).attr("title"); - var html = "<label><input type =\"checkbox\" class=\"confirmjs\" disabled=\"disabled\""; - html += "value=\"" + bib + "\" id=\"" + bib + "\" />" + _("Place a hold on: ") + "</label>"; - $(this).html(html); - }); - // expand or collapse the copiesrow tr function toggle_copiesrow(biblioNum) { var checkbox = $("input:checkbox[value='"+biblioNum+"']"); newCopiesRowId = "#copiesrow_" + biblioNum; + holdsToPlaceCountId = "#holds_to_place_" + biblioNum; var select_specific = $("#reqspecific_"+biblioNum).is(":checked"); // If the checkbox is checked AND we want a specific item, we display the items block if ( $(checkbox).is(":checked") && select_specific ) { $(newCopiesRowId).show(); + $(holdsToPlaceCountId).hide(); } else { $(newCopiesRowId).hide(); + $(holdsToPlaceCountId).show(); } }; - $("#place_on_hdr").show(); + function total_requested() { + var total = 0; + $("input[name='biblionumbers']:checked").each(function() { + var biblioNum = $(this).val(); + if ($("#reqspecific_" + biblioNum + ":checked").size() > 0) { + total += parseInt($("input[name='checkitem_'"+biblioNum+"]:checked").length); + } else { + total += parseInt($("select[name='holds_to_place_count_"+biblioNum+"']").val()); + } + }); + return total; + } $(".place_on_type").show(); // onload, selectany is checked - $(".selectany").attr("checked", "checked"); // If the user is *allowed* to choose a specific item // The first one is preselected @@ -575,8 +591,8 @@ select_first_available(id); }); - // On confirmsjs change - $(".confirmjs").change(function(){ + // On biblionumbers change + $(".biblionumbers").change(function(){ var id = suffixOf($(this).attr("id"), "_"); // If I m checked, I enable radio buttons if ( $(this).is(":checked") ) { @@ -598,35 +614,22 @@ toggle_copiesrow(id); }); - // Show or hide holds notes - $(".shownotes").click(function(){ - biblioNum = suffixOf($(this).attr("id"), "_"); - $("#notesrow_"+biblioNum).toggle(); - }); - // When 'Place Hold' button is clicked $(".placehold").click(function(){ - var biblionumbers = ""; - var selections = ""; - - [% IF new_reserves_allowed %] - if ($(".confirmjs:checked").size() > [% new_reserves_allowed | html %] ) { - alert(MSG_MAX_HOLDS_EXCEEDED); - return false; - } - [% END %] + if (total_requested() + [% reserves_count %] > [% maxreserves %]) { + alert(MSG_MAX_HOLDS_EXCEEDED); + return false; + } - if ($(".confirmjs:checked").size() == 0) { + if ($("input[name='biblionumbers']:checked").size() == 0) { alert(MSG_NO_RECORD_SELECTED); return false; } // Find the items with the 'Hold' box checked var badBib = null; - $(".confirmjs:checked").each(function() { + $("input[name='biblionumbers']:checked").each(function() { var biblioNum = $(this).val(); - biblionumbers += biblioNum + "/"; - selections += biblioNum + "/"; // If required hold note is empty, make it visible if( $("#holdnotes_"+biblioNum).attr( 'required' ) && $("#holdnotes_"+biblioNum).val() == '' ) { @@ -643,18 +646,8 @@ alert(MSG_NO_ITEM_SELECTED); badBib = biblioNum; return false; - } else { - selections += $(item).val(); } } - selections += "/"; - - // Add the pickup location - var branchSel = $("#branch_" + biblioNum); - if (branchSel.size() > 0) { - selections += $(branchSel).val(); - } - selections += "/"; return true; }); @@ -662,9 +655,6 @@ return false; } - $("#selections").val(selections); - $("#biblionumbers").val(biblionumbers); - return true; }); diff --git a/opac/opac-reserve.pl b/opac/opac-reserve.pl index 725b31d..4b332e8 100755 --- a/opac/opac-reserve.pl +++ b/opac/opac-reserve.pl @@ -106,34 +106,19 @@ if ( $reservefee > 0){ my $itemtypes = { map { $_->{itemtype} => $_ } @{ Koha::ItemTypes->search_with_localization->unblessed } }; -# There are two ways of calling this script, with a single biblio num -# or multiple biblio nums. -my $biblionumbers = $query->param('biblionumbers'); -my $reserveMode = $query->param('reserve_mode'); -if ($reserveMode && ($reserveMode eq 'single')) { - my $bib = $query->param('single_bib'); - $biblionumbers = "$bib/"; -} -if (! $biblionumbers) { - $biblionumbers = $query->param('biblionumber'); -} - -if ((! $biblionumbers) && (! $query->param('place_reserve'))) { - $template->param(message=>1, no_biblionumber=>1); - &get_out($query, $cookie, $template->output); +# Coming from opac-search biblionumbers param is a string, coming from +# opac-reserve, it's an array. +my @biblionumbers = ref($query->param('biblionumbers')) eq 'Array' + ? $query->param('biblionumbers') + : split(/\//, $query->param('biblionumbers')); + +unless (@biblionumbers) { + push(@biblionumbers, $query->param('biblionumber')); } # Pass the numbers to the page so they can be fed back # when the hold is confirmed. TODO: Not necessary? -$template->param( biblionumbers => $biblionumbers ); - -# Each biblio number is suffixed with '/', e.g. "1/2/3/" -my @biblionumbers = split /\//, $biblionumbers; -if (($#biblionumbers < 0) && (! $query->param('place_reserve'))) { - # TODO: New message? - $template->param(message=>1, no_biblionumber=>1); - &get_out($query, $cookie, $template->output); -} +$template->param( biblionumbers => @biblionumbers ); # pass the pickup branch along.... @@ -201,39 +186,22 @@ if ( $query->param('place_reserve') ) { $reserve_cnt = $patron->holds->count; } - # List is composed of alternating biblio/item/branch - my $selectedItems = $query->param('selecteditems'); - - if ($query->param('reserve_mode') eq 'single') { - # This indicates non-JavaScript mode, so there was - # only a single biblio number selected. - my $bib = $query->param('single_bib'); - my $item = $query->param("checkitem_$bib"); - if ($item eq 'any') { - $item = ''; - } - my $branch = $query->param('branch'); - $selectedItems = "$bib/$item/$branch/"; - } - - $selectedItems =~ s!/$!!; - my @selectedItems = split /\//, $selectedItems, -1; - - # Make sure there is a biblionum/itemnum/branch triplet for each item. - # The itemnum can be 'any', meaning next available. - my $selectionCount = @selectedItems; - if (($selectionCount == 0) || (($selectionCount % 3) != 0)) { - $template->param(message=>1, bad_data=>1); - &get_out($query, $cookie, $template->output); - } - my $failed_holds = 0; - while (@selectedItems) { - my $biblioNum = shift(@selectedItems); - my $itemNum = shift(@selectedItems); - my $branch = shift(@selectedItems); # i.e., branch code, not name - - my $canreserve = 0; + foreach my $biblioNum (@biblionumbers) { + my $itemNum = $query->param("checkitem_$biblioNum"); + my @itemnumbers = $query->param("checkitem_$biblioNum"); + my $branch = $query->param("branch_$biblioNum"); + my $reqtype = $query->param("reqtype_$biblioNum"); + my $holds_to_place_count = $query->param("holds_to_place_count_$biblioNum") || 1; + my $notes = $query->param('notes_'.$biblioNum) || ''; + my $nbRequested = $reqtype eq 'Specific' ? @itemnumbers : $holds_to_place_count; + + my $canreserve = 1; + if ( $maxreserves + && $reserve_cnt + $nbRequested >= $maxreserves ) + { + $canreserve = 0; + } my $singleBranchMode = Koha::Libraries->search->count == 1; if ( $singleBranchMode || ! C4::Context->preference("OPACAllowUserToChooseBranch") ) @@ -255,7 +223,7 @@ if ( $query->param('place_reserve') ) { } } -#item may belong to a host biblio, if yes change biblioNum to hosts bilbionumber + # item may belong to a host biblio, if yes change biblioNum to hosts bilbionumber if ( $itemNum ne '' ) { my $item = Koha::Items->find( $itemNum ); my $hostbiblioNum = $item->biblio->biblionumber; @@ -277,23 +245,44 @@ if ( $query->param('place_reserve') ) { my $expiration_date = $query->param("expiration_date_$biblioNum"); + # If a specific item was selected and the pickup branch is the same as the + # holdingbranch, force the value $rank and $found. my $rank = $biblioData->{rank}; - if ( $itemNum ne '' ) { - $canreserve = 1 if CanItemBeReserved( $borrowernumber, $itemNum )->{status} eq 'OK'; - } - else { - $canreserve = 1 if CanBookBeReserved( $borrowernumber, $biblioNum )->{status} eq 'OK'; + if ( $reqtype eq 'Specific') { + foreach my $itemnumber (@itemnumbers) { + my $biblionumber = $biblioNum; + + #item may belong to a host biblio, if yes change biblioNum to hosts bilbionumber + my $hostbiblioNum = GetBiblionumberFromItemnumber($itemnumber); + if ( $hostbiblioNum ne $biblioNum ) { + $biblionumber = $hostbiblioNum; + } - # Inserts a null into the 'itemnumber' field of 'reserves' table. - $itemNum = undef; - } - my $notes = $query->param('notes_'.$biblioNum)||''; + $canreserve = 0 unless CanItemBeReserved( $borrowernumber, $itemnumber ) eq 'OK'; + $rank = '0' unless C4::Context->preference('ReservesNeedReturns'); + my $item = GetItem($itemnumber); + if ( $item->{'holdingbranch'} eq $branch ) { + $found = 'W' + unless C4::Context->preference('ReservesNeedReturns'); + } - if ( $maxreserves - && $reserve_cnt >= $maxreserves ) - { - $canreserve = 0; + if ($canreserve) { + my $reserve_id = AddReserve( + $branch, $borrowernumber, + $biblionumber, + [$biblionumber], $rank, + $startdate, $expiration_date, + $notes, $biblioData->{title}, + $itemnumber, $found + ); + $failed_holds++ unless $reserve_id; + ++$reserve_cnt; + } + } + print $query->redirect("/cgi-bin/koha/opac-user.pl?" . ( $failed_holds ? "failed_holds=$failed_holds" : q|| ) . "#opac-user-holds"); + exit; } + $canreserve = 0 unless CanBookBeReserved( $borrowernumber, $biblioNum ) eq 'OK'; unless ( $can_place_hold_if_available_at_pickup ) { my $items_in_this_library = Koha::Items->search({ biblionumber => $biblioNum, holdingbranch => $branch }); @@ -309,14 +298,16 @@ if ( $query->param('place_reserve') ) { # Here we actually do the reserveration. Stage 3. if ($canreserve) { - my $reserve_id = AddReserve( - $branch, $borrowernumber, $biblioNum, - [$biblioNum], $rank, $startdate, - $expiration_date, $notes, $biblioData->{title}, - $itemNum, $found, $itemtype, - ); - $failed_holds++ unless $reserve_id; - ++$reserve_cnt; + for ( my $i = 0 ; $i < $holds_to_place_count ; $i++ ) { + my $reserve_id = AddReserve( + $branch, $borrowernumber, $biblioNum, + [$biblioNum], $rank, $startdate, + $expiration_date, $notes, $biblioData->{title}, + $itemNum, $found, $itemtype, + ); + $failed_holds++ unless $reserve_id; + ++$reserve_cnt; + } } } @@ -376,6 +367,10 @@ if ( $maxreserves && ( $reserves_count >= $maxreserves ) ) { } unless ( $noreserves ) { + $template->param( + maxreserves => $maxreserves, + reserves_count => $reserves_count + ); my $requested_reserves_count = scalar( @biblionumbers ); if ( $maxreserves && ( $reserves_count + $requested_reserves_count > $maxreserves ) ) { $template->param( new_reserves_allowed => $maxreserves - $reserves_count ); @@ -550,7 +545,7 @@ foreach my $biblioNum (@biblionumbers) { if ( $opac_hold_policy ne 'N' ) { # If Y or F $itemLoopIter->{available} = 1; $numCopiesOPACAvailable++; - $biblioLoopIter{force_hold} = 1 if $opac_hold_policy eq 'F'; + $biblioLoopIter{force_hold} = 'item' if $opac_hold_policy eq 'F'; } $numCopiesAvailable++; @@ -617,18 +612,24 @@ foreach my $biblioNum (@biblionumbers) { # patron placed a record level hold, all the holds the patron places must # be record level. If the patron placed an item level hold, all holds # the patron places must be item level - my $forced_hold_level = Koha::Holds->search( + my $holds = Koha::Holds->search( { borrowernumber => $borrowernumber, biblionumber => $biblioNum, found => undef, } - )->forced_hold_level(); + ); + my $forced_hold_level = $holds->forced_hold_level(); if ($forced_hold_level) { - $biblioLoopIter{force_hold} = 1 if $forced_hold_level eq 'item'; - $biblioLoopIter{itemholdable} = 0 if $forced_hold_level eq 'record'; + #$biblioLoopIter{force_hold} = 1 if $forced_hold_level eq 'item'; + #$biblioLoopIter{itemholdable} = 0 if $forced_hold_level eq 'record'; + $biblioLoopIter{force_hold} = $forced_hold_level; } + my $max_holds_for_record = GetMaxPatronHoldsForRecord( $borrowernumber, $biblioNum ); + my $remaining_holds_for_record = $max_holds_for_record - $holds->count(); + $biblioLoopIter{remaining_holds_for_record} = $remaining_holds_for_record; + push @$biblioLoop, \%biblioLoopIter; diff --git a/reserve/placerequest.pl b/reserve/placerequest.pl index 1e49a4b..f06c6d3 100755 --- a/reserve/placerequest.pl +++ b/reserve/placerequest.pl @@ -49,7 +49,7 @@ my $startdate = $input->param('reserve_date') || ''; my @rank = $input->multi_param('rank-request'); my $type = $input->param('type'); my $title = $input->param('title'); -my $checkitem = $input->param('checkitem'); +my @checkitems = $input->param('checkitem'); my $expirationdate = $input->param('expiration_date'); my $itemtype = $input->param('itemtype') || undef; @@ -60,6 +60,7 @@ my $multi_hold = $input->param('multi_hold'); my $biblionumbers = $multi_hold ? $input->param('biblionumbers') : ($biblionumber . '/'); my $bad_bibs = $input->param('bad_bibs'); my $holds_to_place_count = $input->param('holds_to_place_count') || 1; +$holds_to_place_count = scalar(@checkitems) if scalar(@checkitems); my %bibinfos = (); my @biblionumbers = split '/', $biblionumbers; @@ -70,7 +71,17 @@ foreach my $bibnum (@biblionumbers) { $bibinfos{$bibnum} = \%bibinfo; } -my $found; +my @found; + +foreach my $checkitem (@checkitems) { + # if we have an item selectionned, and the pickup branch is the same as the holdingbranch + # of the document, we force the value $rank and $found . + $rank[0] = '0' unless C4::Context->preference('ReservesNeedReturns'); + my $item = GetItem($checkitem); + if ( $item->{'holdingbranch'} eq $branch ){ + push @found, 'W' unless C4::Context->preference('ReservesNeedReturns'); + } +} if ( $type eq 'str8' && $borrower ) { @@ -88,27 +99,18 @@ if ( $type eq 'str8' && $borrower ) { } } - if ( defined $checkitem && $checkitem ne '' ) { - my $item = Koha::Items->find($checkitem); - if ( $item->biblionumber ne $biblionumber ) { - $biblionumber = $item->biblionumber; - } - } - if ($multi_hold) { my $bibinfo = $bibinfos{$biblionumber}; - if ( CanBookBeReserved($borrower->{'borrowernumber'}, $biblionumber)->{status} eq 'OK' ) { - AddReserve($branch,$borrower->{'borrowernumber'},$biblionumber,[$biblionumber], - $bibinfo->{rank},$startdate,$expirationdate,$notes,$bibinfo->{title},$checkitem,$found); - } + AddReserve( $branch, $borrower->{'borrowernumber'}, + $biblionumber, [$biblionumber], + $bibinfo->{rank}, $startdate, $expirationdate, $notes,$bibinfo->{title}); } else { # place a request on 1st available for ( my $i = 0 ; $i < $holds_to_place_count ; $i++ ) { - if ( CanBookBeReserved($borrower->{'borrowernumber'}, $biblionumber)->{status} eq 'OK' ) { - AddReserve( $branch, $borrower->{'borrowernumber'}, - $biblionumber, \@realbi, $rank[0], $startdate, $expirationdate, $notes, $title, - $checkitem, $found, $itemtype ); - } + my $item = GetItem($checkitems[$i]); + AddReserve( $branch, $borrower->{'borrowernumber'}, + $item->{biblionumber}, \@realbi, $rank[0], $startdate, $expirationdate, $notes, $title, + $checkitems[$i], $found[$i] ); } } } -- 2.1.4