From 2f8309127b08490eba625e97464d034e2c704035 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

Signed-off-by: Séverine QUEUNE <severine.queune@bulac.fr>

Signed-off-by: Kyle M Hall <kyle@bywatersolutions.com>
---
 .../prog/en/modules/reserve/request.tt             |  16 +-
 .../opac-tmpl/bootstrap/en/modules/opac-reserve.tt | 192 ++++++++++-----------
 koha-tmpl/opac-tmpl/bootstrap/less/opac.less       |   3 +
 opac/opac-reserve.pl                               | 174 +++++++++----------
 reserve/placerequest.pl                            |  34 ++--
 5 files changed, 202 insertions(+), 217 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 02c50b9..ad60b2f 100644
--- a/koha-tmpl/intranet-tmpl/prog/en/modules/reserve/request.tt
+++ b/koha-tmpl/intranet-tmpl/prog/en/modules/reserve/request.tt
@@ -25,6 +25,8 @@ var override_items = {[% FOREACH bibitemloo IN bibitemloop %][% FOREACH itemloo
 [% END %][% END %][% END %]
 };
 var MSG_NO_ITEMS_AVAILABLE = _("A hold cannot be requested on any of these items.");
+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() {
@@ -149,17 +151,21 @@ function checkMultiHold() {
         }
     });
     $("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 > 0){
             $("#requestany").prop("checked", false);
         } else {
             $("#requestany").prop("checked",true);
         }
+
+        if (remainingHolds - numchecked < 0) {
+            alert(MSG_EXCEEDED_HOLDS_PER_RECORD);
+        }
     });
 
     $(".clear-date").on("click",function(e){
@@ -493,9 +499,9 @@ function checkMultiHold() {
                         Hold must be record level
                     </span>
                 [% ELSIF ( itemloo.available ) %]
-                    <input type="radio" name="checkitem" value="[% itemloo.itemnumber %]" />
+                    <input type="checkbox" name="checkitem" value="[% itemloo.itemnumber %]" />
                 [% ELSIF ( itemloo.override ) %]
-                    <input type="radio" name="checkitem" class="needsoverride" value="[% itemloo.itemnumber %]" />
+                    <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">
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 0f2f4eb..e529885 100644
--- a/koha-tmpl/opac-tmpl/bootstrap/en/modules/opac-reserve.tt
+++ b/koha-tmpl/opac-tmpl/bootstrap/en/modules/opac-reserve.tt
@@ -138,20 +138,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 %]"/>
-                                                <span class="confirmjs_hold" title="[% bibitemloo.biblionumber %]" style="padding:.3em"></span>
-                                                <span class="confirm_nonjs">
-                                                    <input type="radio" class="confirmbox checkitem [% bibitemloo.biblionumber %]" name="[% bibitemloo.biblionumber %]" checked="checked" id="single_[% bibitemloo.biblionumber %]" value="any" />
-                                                    <label class="confirm_label" for="single_[% bibitemloo.biblionumber %]">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 %]
 
@@ -218,7 +216,7 @@
                                                             <li class="branch">
                                                                 <label for="branch_[% bibitemloo.biblionumber %]">Pick up location:</label>
                                                                 [% UNLESS ( bibitemloo.holdable ) %]
-                                                                    <select name="branch" id="branch_[% bibitemloo.biblionumber %]" disabled="disabled">
+                                                                    <select name="branch_[% bibitemloo.biblionumber %]" id="branch_[% bibitemloo.biblionumber %]" disabled="disabled">
                                                                         [% PROCESS options_for_libraries libraries => Branches.all( selected => branch ) %]
                                                                     </select>
                                                                 [% ELSE %]
@@ -292,29 +290,39 @@
                                                             </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 %]">Next available item</label>
-                                                                    <input type="radio" name="reqtype_[% bibitemloo.biblionumber %]"
-                                                                            id="reqany_[% bibitemloo.biblionumber %]"
-                                                                            class="selectany"
-                                                                            value="Any"
-                                                                            checked="checked"
-                                                                    />
-                                                                [% END %]
-                                                                <label class="radio inline" for="reqspecific_[% bibitemloo.biblionumber %]">A specific item</label>
+                                                        <!-- 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="reqspecific_[% bibitemloo.biblionumber %]"
-                                                                       class="selectspecific"
-                                                                       value="Specific"
+                                                                        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 %]
+                                                    [% 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>
@@ -338,9 +346,9 @@
                                                                 <tr class="[% itemLoo.backgroundcolor %]">
                                                                     <td class="copynumber">
                                                                         [% IF ( itemLoo.available ) %]
-                                                                            <input type="radio" class="checkitem checkitem_[% bibitemloo.biblionumber %]" name="checkitem_[% bibitemloo.biblionumber %]" value="[% itemLoo.itemnumber %]" />
+                                                                            <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 %]"
+                                                                            <input disabled="disabled" type="checkbox" class="checkitem" name="checkitem" value="[% itemLoo.itemnumber %]"
                                                                                    style="display:none;" />
                                                                             <img src="[% interface %]/lib/famfamfam/silk/cross.png" alt="Cannot be put on hold" title="Cannot be put on hold" />
                                                                         [% END %]
@@ -414,7 +422,6 @@
                                                                 </tr>
                                                             [% END # / FOREACH itemLoo IN bibitemloo.itemLoop%]
                                                         </table> <!-- / #copiesrow_[% bibitemloo.biblionumber %] -->
-                                                    [% END # / IF ( bibitemloo.itemholdable )%]
                                                 </div> <!-- / #hold-options-[% bibitemloo.biblionumber %] -->
                                             </fieldset>
                                         [% END # / IF ( bibitemloo.holdable ) %]
@@ -437,6 +444,7 @@
 <script type="text/javascript">
 // <![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){
@@ -446,8 +454,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() {
@@ -468,14 +476,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){
@@ -490,15 +500,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 %]").click();
-            $("#reqspecific_[% bibitemloo.biblionumber %]").click();
-            $("#copiesrow_[% bibitemloo.biblionumber %]").show();
-          [% END %]
-          [% IF bibitemloo.reqholdnotes %]
-              $("#holdnotes_[% bibitemloo.biblionumber %]").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(){
@@ -515,40 +534,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
@@ -557,8 +573,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") ) {
@@ -580,35 +596,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 %] ) {
-                    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() == '' ) {
@@ -625,18 +628,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;
             });
 
@@ -644,9 +637,6 @@
                 return false;
             }
 
-            $("#selections").val(selections);
-            $("#biblionumbers").val(biblionumbers);
-
             return true;
         });
 
diff --git a/koha-tmpl/opac-tmpl/bootstrap/less/opac.less b/koha-tmpl/opac-tmpl/bootstrap/less/opac.less
index 63961e8..80d49c4 100644
--- a/koha-tmpl/opac-tmpl/bootstrap/less/opac.less
+++ b/koha-tmpl/opac-tmpl/bootstrap/less/opac.less
@@ -2389,6 +2389,9 @@ td img {
     margin: 1em 0;
     padding: .5em;
 }
+.hold-option select {
+    width: auto;
+}
 .copiesrow {
     clear : both;
 }
diff --git a/opac/opac-reserve.pl b/opac/opac-reserve.pl
index 95204b3..e70af52 100755
--- a/opac/opac-reserve.pl
+++ b/opac/opac-reserve.pl
@@ -105,34 +105,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....
@@ -204,39 +189,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 || !$OPACChooseBranch )
@@ -244,7 +212,6 @@ if ( $query->param('place_reserve') ) {
             $branch = $patron->branchcode;
         }
 
-#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;
@@ -266,31 +233,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.
+        # 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 ) eq 'OK';
-            $rank = '0' unless C4::Context->preference('ReservesNeedReturns');
-            my $item = GetItem($itemNum);
-            if ( $item->{'holdingbranch'} eq $branch ) {
-                $found = 'W'
-                  unless C4::Context->preference('ReservesNeedReturns');
-            }
-        }
-        else {
-            $canreserve = 1 if CanBookBeReserved( $borrowernumber, $biblioNum ) 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 });
@@ -306,14 +286,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;
+            }
         }
     }
 
@@ -373,6 +355,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 );
@@ -542,7 +528,7 @@ foreach my $biblioNum (@biblionumbers) {
             if ( my $hold_allowed = OPACItemHoldsAllowed( $itemInfo, $patron_unblessed ) ) {
                 $itemLoopIter->{available} = 1;
                 $numCopiesOPACAvailable++;
-                $biblioLoopIter{force_hold} = 1 if $hold_allowed eq 'F';
+                $biblioLoopIter{force_hold} = 'item' if $hold_allowed eq 'F';
             }
             $numCopiesAvailable++;
 
@@ -602,18 +588,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 403321c..9532a6d 100755
--- a/reserve/placerequest.pl
+++ b/reserve/placerequest.pl
@@ -48,7 +48,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;
 
@@ -59,6 +59,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;
@@ -69,17 +70,15 @@ foreach my $bibnum (@biblionumbers) {
     $bibinfos{$bibnum} = \%bibinfo;
 }
 
-my $found;
+my @found;
 
-# 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 .
-if (defined $checkitem && $checkitem ne ''){
-    $holds_to_place_count = 1;
+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 = $checkitem;
-    $item = GetItem($item);
+    my $item = GetItem($checkitem);
     if ( $item->{'holdingbranch'} eq $branch ){
-        $found = 'W' unless C4::Context->preference('ReservesNeedReturns');
+        push @found, 'W' unless C4::Context->preference('ReservesNeedReturns');
     }
 }
 
@@ -99,23 +98,18 @@ if ( $type eq 'str8' && $borrower ) {
             }
         }
 
-        if ( defined $checkitem && $checkitem ne '' ) {
-            my $item = GetItem($checkitem);
-            if ( $item->{'biblionumber'} ne $biblionumber ) {
-                $biblionumber = $item->{'biblionumber'};
-            }
-        }
-
         if ($multi_hold) {
             my $bibinfo = $bibinfos{$biblionumber};
-            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++ ) {
+                my $item = GetItem($checkitems[$i]);
                 AddReserve( $branch, $borrower->{'borrowernumber'},
-                    $biblionumber, \@realbi, $rank[0], $startdate, $expirationdate, $notes, $title,
-                    $checkitem, $found, $itemtype );
+                    $item->{biblionumber}, \@realbi, $rank[0], $startdate, $expirationdate, $notes, $title,
+                    $checkitems[$i], $found[$i] );
             }
         }
     }
-- 
2.10.2