From 2e62595312f6f076eb63c04a3f7e329ce7517407 Mon Sep 17 00:00:00 2001
From: Julian Maurice <julian.maurice@biblibre.com>
Date: Thu, 7 Jan 2016 14:14:18 +0100
Subject: [PATCH] Bug 15516: Allow to reserve first available item from a group
 of titles

It can be useful, for instance, if a library have the same title from
different publishers (so 1 title but several biblio records) but the
user only wants a copy of the book, regardless of the publisher.

This patch is only for staff interface.

Test plan:
0. Run updatedatabase.pl and misc/devel/update_dbix_class_files.pl
1. Go to intranet search, display some results, click on some checkboxes
   and click on 'Place hold' button at the top of the results.
2. Enter a patron cardnumber and click Search
3. Check the box for 'Place a hold on the next available item' (it's
   usually hidden when placing a hold on multiple biblios)
4. Click on 'Place hold'
5. In the next screen you should see all the holds you placed with the
   additional text '(part of a reserve group)' in Details column.
6. Click on the "reserve group" link. A modal window should appear with
   the content of the reserve group (a list of holds)
7. Check in an item of one of the reserved biblios and confirm the hold
8. The hold status is changed to Waiting, and all other holds are
   deleted.

Note: the "reserve group" link is displayed in the following pages:
- reserve/request.pl
- circ/circulation.pl
- members/moremember.pl
- circ/pendingreserves.pl

Signed-off-by: Josef Moravec <josef.moravec@gmail.com>
---
 C4/Reserves.pm                                     | 75 +++++++++++++++++++++-
 Koha/Template/Plugin/Biblio.pm                     |  8 +++
 circ/pendingreserves.pl                            |  4 +-
 installer/data/mysql/atomicupdate/bug_15516.sql    |  8 +++
 installer/data/mysql/kohastructure.sql             | 12 ++++
 .../prog/en/includes/reserve-group-modal.inc       | 27 ++++++++
 .../intranet-tmpl/prog/en/includes/strings.inc     |  2 +
 koha-tmpl/intranet-tmpl/prog/en/js/holds.js        |  6 ++
 .../prog/en/modules/circ/circulation.tt            |  1 +
 .../prog/en/modules/circ/pendingreserves.tt        | 15 +++--
 .../prog/en/modules/members/moremember.tt          |  1 +
 .../prog/en/modules/reserve/request.tt             | 14 +++-
 .../prog/en/modules/reserve/reserve-group.tt       | 30 +++++++++
 reserve/placerequest.pl                            | 12 +++-
 reserve/request.pl                                 |  1 +
 reserve/reserve-group.pl                           | 42 ++++++++++++
 svc/holds                                          |  1 +
 17 files changed, 247 insertions(+), 12 deletions(-)
 create mode 100644 installer/data/mysql/atomicupdate/bug_15516.sql
 create mode 100644 koha-tmpl/intranet-tmpl/prog/en/includes/reserve-group-modal.inc
 create mode 100644 koha-tmpl/intranet-tmpl/prog/en/modules/reserve/reserve-group.tt
 create mode 100755 reserve/reserve-group.pl

diff --git a/C4/Reserves.pm b/C4/Reserves.pm
index f981be1..5784997 100644
--- a/C4/Reserves.pm
+++ b/C4/Reserves.pm
@@ -141,6 +141,10 @@ BEGIN {
         &GetReservesControlBranch
 
         IsItemOnHoldAndFound
+
+        AddReserveGroup
+        DeleteReserveGroup
+        GetReserveGroupReserves
     );
     @EXPORT_OK = qw( MergeHolds );
 }
@@ -165,7 +169,7 @@ sub AddReserve {
     my (
         $branch,    $borrowernumber, $biblionumber,
         $bibitems,  $priority, $resdate, $expdate, $notes,
-        $title,      $checkitem, $found
+        $title,      $checkitem, $found, $reserve_group_id
     ) = @_;
 
     if ( Koha::Holds->search( { borrowernumber => $borrowernumber, biblionumber => $biblionumber } )->count() > 0 ) {
@@ -205,7 +209,8 @@ sub AddReserve {
             itemnumber     => $checkitem,
             found          => $found,
             waitingdate    => $waitingdate,
-            expirationdate => $expdate
+            expirationdate => $expdate,
+            reserve_group_id => $reserve_group_id,
         }
     )->store();
     my $reserve_id = $hold->id();
@@ -1341,6 +1346,17 @@ sub ModReserveAffect {
     }
     $sth = $dbh->prepare($query);
     $sth->execute( $itemnumber, $borrowernumber,$biblionumber);
+
+    if ($request->{reserve_group_id}) {
+        # Delete other reserves from same group
+        $dbh->do(q{
+            DELETE FROM reserves
+            WHERE reserve_group_id = ?
+              AND reserve_id != ?
+        }, undef, $request->{reserve_group_id}, $request->{reserve_id});
+        DeleteReserveGroup($request->{reserve_group_id});
+    }
+
     _koha_notify_reserve( $itemnumber, $borrowernumber, $biblionumber ) if ( !$transferToDo && !$already_on_shelf );
     _FixPriority( { biblionumber => $biblionumber } );
     if ( C4::Context->preference("ReturnToShelvingCart") ) {
@@ -1420,6 +1436,7 @@ sub GetReserveInfo {
                    reminderdate,
                    priority,
                    found,
+                   reserve_group_id,
                    firstname,
                    surname,
                    phone,
@@ -2450,6 +2467,60 @@ sub IsItemOnHoldAndFound {
     return $found;
 }
 
+=head2 AddReserveGroup
+
+    my $reserve_group_id = AddReserveGroup();
+
+Creates a new reserve group and returns its ID
+
+=cut
+
+sub AddReserveGroup {
+    my $dbh = C4::Context->dbh;
+
+    $dbh->do('INSERT INTO reserve_group VALUES ()');
+    return $dbh->last_insert_id(undef, undef, 'reserve_group', undef);
+}
+
+=head2 DeleteReserveGroup
+
+    DeleteReserveGroup($reserve_group_id);
+
+Delete a reserve group. Reserves that belong to this group are not removed.
+
+=cut
+
+sub DeleteReserveGroup {
+    my ($reserve_group_id) = @_;
+
+    my $dbh = C4::Context->dbh;
+    my $query = 'DELETE FROM reserve_group WHERE reserve_group_id = ?';
+    $dbh->do($query, undef, $reserve_group_id);
+}
+
+=head2 GetReserveGroupReserves
+
+    my @reserves = GetReserveGroupReserves($reserve_group_id);
+
+Fetch all reserve from a reserve group.
+
+=cut
+
+sub GetReserveGroupReserves {
+    my ($reserve_group_id) = @_;
+
+    my $dbh = C4::Context->dbh;
+
+    my $reserves = $dbh->selectall_arrayref(q{
+        SELECT * FROM reserves
+        WHERE reserve_group_id = ?
+    }, { Slice => {} }, $reserve_group_id);
+
+    return () unless defined $reserves;
+
+    return @$reserves;
+}
+
 =head1 AUTHOR
 
 Koha Development Team <http://koha-community.org/>
diff --git a/Koha/Template/Plugin/Biblio.pm b/Koha/Template/Plugin/Biblio.pm
index 3f9cc4f..d0b955f 100644
--- a/Koha/Template/Plugin/Biblio.pm
+++ b/Koha/Template/Plugin/Biblio.pm
@@ -32,4 +32,12 @@ sub HoldsCount {
     return $holds->count();
 }
 
+sub Get {
+    my ($self, $biblionumber) = @_;
+
+    my $biblio = Koha::Biblios->find($biblionumber);
+
+    return $biblio;
+}
+
 1;
diff --git a/circ/pendingreserves.pl b/circ/pendingreserves.pl
index 932b035..ea1de90 100755
--- a/circ/pendingreserves.pl
+++ b/circ/pendingreserves.pl
@@ -118,6 +118,7 @@ if ( $run_report ) {
             reminderdate,
             max(priority) as priority,
             reserves.found,
+            reserves.reserve_group_id,
             biblio.title,
             biblio.author,
             count(DISTINCT items.itemnumber) as icount,
@@ -167,7 +168,8 @@ if ( $run_report ) {
                 biblionumber    => $data->{biblionumber},
                 statusw         => ( $data->{found} eq "W" ),
                 statusf         => ( $data->{found} eq "F" ),
-                holdingbranches => [split('\|', $data->{l_holdingbranch})],,
+                reserve_group_id => $data->{reserve_group_id},
+                holdingbranches => [split('\|', $data->{l_holdingbranch})],
                 branch          => $data->{l_branch},
                 itemcallnumber  => $data->{l_itemcallnumber},
                 enumchron       => $data->{l_enumchron},
diff --git a/installer/data/mysql/atomicupdate/bug_15516.sql b/installer/data/mysql/atomicupdate/bug_15516.sql
new file mode 100644
index 0000000..efd761e
--- /dev/null
+++ b/installer/data/mysql/atomicupdate/bug_15516.sql
@@ -0,0 +1,8 @@
+DROP TABLE IF EXISTS reserve_group;
+CREATE TABLE reserve_group (
+  reserve_group_id int unsigned NOT NULL AUTO_INCREMENT,
+  PRIMARY KEY (reserve_group_id)
+) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci;
+
+ALTER TABLE reserves ADD COLUMN reserve_group_id int unsigned NULL DEFAULT NULL;
+ALTER TABLE reserves ADD CONSTRAINT reserves_ibfk_5 FOREIGN KEY (reserve_group_id) REFERENCES reserve_group (reserve_group_id) ON DELETE SET NULL ON UPDATE CASCADE;
diff --git a/installer/data/mysql/kohastructure.sql b/installer/data/mysql/kohastructure.sql
index b98b017..6f741d7 100644
--- a/installer/data/mysql/kohastructure.sql
+++ b/installer/data/mysql/kohastructure.sql
@@ -1872,6 +1872,7 @@ CREATE TABLE `reserves` ( -- information related to holds/reserves in Koha
   `lowestPriority` tinyint(1) NOT NULL,
   `suspend` BOOLEAN NOT NULL DEFAULT 0,
   `suspend_until` DATETIME NULL DEFAULT NULL,
+  reserve_group_id int unsigned NULL DEFAULT NULL,
   PRIMARY KEY (`reserve_id`),
   KEY priorityfoundidx (priority,found),
   KEY `borrowernumber` (`borrowernumber`),
@@ -1882,6 +1883,17 @@ CREATE TABLE `reserves` ( -- information related to holds/reserves in Koha
   CONSTRAINT `reserves_ibfk_2` FOREIGN KEY (`biblionumber`) REFERENCES `biblio` (`biblionumber`) ON DELETE CASCADE ON UPDATE CASCADE,
   CONSTRAINT `reserves_ibfk_3` FOREIGN KEY (`itemnumber`) REFERENCES `items` (`itemnumber`) ON DELETE CASCADE ON UPDATE CASCADE,
   CONSTRAINT `reserves_ibfk_4` FOREIGN KEY (`branchcode`) REFERENCES `branches` (`branchcode`) ON DELETE CASCADE ON UPDATE CASCADE
+  CONSTRAINT `reserves_ibfk_5` FOREIGN KEY (reserve_group_id) REFERENCES reserve_group (reserve_group_id) ON DELETE SET NULL ON UPDATE CASCADE
+) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci;
+
+--
+-- Table structure for table reserve_group
+--
+
+DROP TABLE IF EXISTS reserve_group;
+CREATE TABLE reserve_group (
+  reserve_group_id int unsigned NOT NULL AUTO_INCREMENT,
+  PRIMARY KEY(reserve_group_id)
 ) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci;
 
 --
diff --git a/koha-tmpl/intranet-tmpl/prog/en/includes/reserve-group-modal.inc b/koha-tmpl/intranet-tmpl/prog/en/includes/reserve-group-modal.inc
new file mode 100644
index 0000000..2a3e68b
--- /dev/null
+++ b/koha-tmpl/intranet-tmpl/prog/en/includes/reserve-group-modal.inc
@@ -0,0 +1,27 @@
+<div id="reserve-group-modal" class="modal hide" tabindex="-1" role="dialog" aria-labelledby="reserve-group-modal-label" aria-hidden="true">
+    <div class="modal-header">
+        <button type="button" class="closebtn" data-dismiss="modal" aria-hidden="true">×</button>
+        <h3 id="reserve-group-modal-label">Reserve group</h3>
+    </div>
+    <div class="modal-body">
+        <div id="loading"> <img src="[% interface %]/[% theme %]/img/loading-small.gif" alt="" /> Loading </div>
+    </div>
+    <div class="modal-footer">
+        <button class="btn" data-dismiss="modal" aria-hidden="true">Close</button>
+    </div>
+</div>
+
+<script>
+  $(document).ready(function() {
+    $('body').on('click', 'a.reserve-group', function() {
+      var href = $(this).attr('href');
+      $('#reserve-group-modal .modal-body').load(href + " #main");
+      $('#reserve-group-modal').modal('show');
+      return false;
+    })
+
+    $('#reserve-group-modal').on('hidden', function(){
+        $('#reserve-group-modal .modal-body').html('<div id="loading"><img src="[% interface %]/[% theme %]/img/loading-small.gif" alt="" /> ' + _('Loading') + '</div>');
+    });
+  });
+</script>
diff --git a/koha-tmpl/intranet-tmpl/prog/en/includes/strings.inc b/koha-tmpl/intranet-tmpl/prog/en/includes/strings.inc
index 62af2a3..bc765e9 100644
--- a/koha-tmpl/intranet-tmpl/prog/en/includes/strings.inc
+++ b/koha-tmpl/intranet-tmpl/prog/en/includes/strings.inc
@@ -38,5 +38,7 @@
     var SUSPEND_HOLD_ERROR_DATE = _("Unable to suspend hold, invalid date");
     var SUSPEND_HOLD_ERROR_NOT_FOUND = _("Unable to suspend hold, hold not found");
     var RESUME_HOLD_ERROR_NOT_FOUND = _("Unable to resume, hold not found");
+    var PART_OF_A = _("(part of a %s)");
+    var RESERVE_GROUP = _("reserve group");
 //]]>
 </script>
diff --git a/koha-tmpl/intranet-tmpl/prog/en/js/holds.js b/koha-tmpl/intranet-tmpl/prog/en/js/holds.js
index c009ba7..e0b8cf6 100644
--- a/koha-tmpl/intranet-tmpl/prog/en/js/holds.js
+++ b/koha-tmpl/intranet-tmpl/prog/en/js/holds.js
@@ -42,6 +42,12 @@ $(document).ready(function() {
                                 title += " - <span class='" + span_class + "'>" + oObj.itemnotes + "</span>"
                             }
 
+                            if (oObj.reserve_group_id) {
+                              title += '<br>';
+                              var link = '<a class="reserve-group" href="/cgi-bin/koha/reserve/reserve-group.pl?reserve_group_id=' + oObj.reserve_group_id + '">' + RESERVE_GROUP +'</a>'
+                              title += '<span>' + PART_OF_A.format(link) + '</span>';
+                            }
+
                             return title;
                         }
                     },
diff --git a/koha-tmpl/intranet-tmpl/prog/en/modules/circ/circulation.tt b/koha-tmpl/intranet-tmpl/prog/en/modules/circ/circulation.tt
index 6fd722c..7c63f5c 100644
--- a/koha-tmpl/intranet-tmpl/prog/en/modules/circ/circulation.tt
+++ b/koha-tmpl/intranet-tmpl/prog/en/modules/circ/circulation.tt
@@ -1002,4 +1002,5 @@ No patron matched <span class="ex">[% message %]</span>
         <p>You have already submitted a barcode, please wait for the checkout to process...</p>
     </div>
 </div>
+[% INCLUDE 'reserve-group-modal.inc' %]
 [% INCLUDE 'intranet-bottom.inc' %]
diff --git a/koha-tmpl/intranet-tmpl/prog/en/modules/circ/pendingreserves.tt b/koha-tmpl/intranet-tmpl/prog/en/modules/circ/pendingreserves.tt
index 919f0c3..1c24426 100644
--- a/koha-tmpl/intranet-tmpl/prog/en/modules/circ/pendingreserves.tt
+++ b/koha-tmpl/intranet-tmpl/prog/en/modules/circ/pendingreserves.tt
@@ -115,10 +115,16 @@ $(document).ready(function() {
             <td>[% reserveloo.count %]</td>
             <td>[% reserveloo.rcount %]</td>
             <td>
-            <p>
-                [% INCLUDE 'biblio-default-view.inc' biblionumber = reserveloo.biblionumber %]
-                [% reserveloo.title |html %]</a></p>
-                [% IF ( reserveloo.author ) %]<p> by [% reserveloo.author %]</p>[% END %]
+                <p>
+                    [% INCLUDE 'biblio-default-view.inc' biblionumber = reserveloo.biblionumber %]
+                    [% reserveloo.title |html %]</a>
+                </p>
+                [% IF ( reserveloo.author ) %]
+                    <p> by [% reserveloo.author %]</p>
+                [% END %]
+                [% IF reserveloo.reserve_group_id %]
+                    <p>(part of a <a class="reserve-group" href="/cgi-bin/koha/reserve/reserve-group.pl?reserve_group_id=[% reserveloo.reserve_group_id %]">reserve group</a>)</p>
+                [% END %]
             </td>
         [% ELSE %]
             <td>"</td>
@@ -202,4 +208,5 @@ $(document).ready(function() {
 </div>
 </div>
 </div>
+[% INCLUDE 'reserve-group-modal.inc' %]
 [% INCLUDE 'intranet-bottom.inc' %]
diff --git a/koha-tmpl/intranet-tmpl/prog/en/modules/members/moremember.tt b/koha-tmpl/intranet-tmpl/prog/en/modules/members/moremember.tt
index 30cda46..3b5b55f 100644
--- a/koha-tmpl/intranet-tmpl/prog/en/modules/members/moremember.tt
+++ b/koha-tmpl/intranet-tmpl/prog/en/modules/members/moremember.tt
@@ -543,4 +543,5 @@ function validate1(date) {
 [% INCLUDE 'circ-menu.inc' %]
 </div>
 </div>
+[% INCLUDE 'reserve-group-modal.inc' %]
 [% INCLUDE 'intranet-bottom.inc' %]
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 85f687a..8ba3714 100644
--- a/koha-tmpl/intranet-tmpl/prog/en/modules/reserve/request.tt
+++ b/koha-tmpl/intranet-tmpl/prog/en/modules/reserve/request.tt
@@ -424,13 +424,15 @@ function checkMultiHold() {
         <a href="#" id="clear-date-to" class="clear-date">Clear date</a>
 	</li>
 
-        [% UNLESS ( multi_hold ) %]
           <li> <label for="requestany">Place a hold on the next available item </label>
-               <input type="checkbox" id="requestany" name="request" checked="checked" value="Any" />
+              [% IF multi_hold %]
+                  <input type="checkbox" id="requestany" name="requestany" value="1" />
+              [% ELSE %]
+                  <input type="checkbox" id="requestany" name="requestany" checked="checked" value="1" />
+              [% END %]
                <input type="hidden" name="biblioitem" value="[% biblioitemnumber %]" />
                <input type="hidden" name="alreadyreserved" value="[% alreadyreserved %]" />
           </li>
-        [% END %]
 
 </ol>
    [% UNLESS ( multi_hold ) %]
@@ -802,6 +804,9 @@ function checkMultiHold() {
                  <input type="hidden" name="itemnumber" value="" />
             [% END %]
     [% END %]
+            [% IF reserveloo.reserve_group_id %]
+                <div>(part of a <a href="/cgi-bin/koha/reserve/reserve-group.pl?reserve_group_id=[% reserveloo.reserve_group_id %]" class="reserve-group">reserve group</a>)</div>
+            [% END %]
         </td>
 
     [% IF ( CAN_user_reserveforothers_modify_holds_priority ) %]
@@ -862,4 +867,7 @@ function checkMultiHold() {
 
 </div>
 </div>
+
+[% INCLUDE 'reserve-group-modal.inc' %]
+
 [% INCLUDE 'intranet-bottom.inc' %]
diff --git a/koha-tmpl/intranet-tmpl/prog/en/modules/reserve/reserve-group.tt b/koha-tmpl/intranet-tmpl/prog/en/modules/reserve/reserve-group.tt
new file mode 100644
index 0000000..8926c0a
--- /dev/null
+++ b/koha-tmpl/intranet-tmpl/prog/en/modules/reserve/reserve-group.tt
@@ -0,0 +1,30 @@
+[% USE Biblio %]
+[% INCLUDE 'doc-head-open.inc' %]
+  <title>Koha &rsaquo; Circulation &rsaquo; Holds &rsaquo; Holds group ([% reserve_group_id %])</title>
+  [% INCLUDE 'doc-head-close.inc' %]
+</head>
+<body id="reserve-reserve-group">
+  <h1>Holds group ([% reserve_group_id %])</h1>
+  <div id="main">
+    [% IF reserves.size %]
+      <table>
+        <thead>
+          <tr>
+            <th>Title</th>
+            <th>Priority</th>
+            <th>Notes</th>
+          </tr>
+        </thead>
+        <tbody>
+          [% FOREACH reserve IN reserves %]
+            [% biblio = Biblio.Get(reserve.biblionumber) %]
+            <tr>
+              <td><a href="/cgi-bin/koha/reserve/request.pl?biblionumber=[% biblio.biblionumber %]">[% biblio.title %]</a></td>
+              <td>[% reserve.priority %]</td>
+              <td>[% reserve.reservenotes %]</td>
+            </tr>
+          [% END %]
+        </tbody>
+      <table>
+    [% END %]
+  [% INCLUDE 'intranet-bottom.inc' %]
diff --git a/reserve/placerequest.pl b/reserve/placerequest.pl
index 28e8868..1a8fd3c 100755
--- a/reserve/placerequest.pl
+++ b/reserve/placerequest.pl
@@ -50,6 +50,7 @@ my $title=$input->param('title');
 my $borrower=GetMember('borrowernumber'=>$borrowernumber);
 my $checkitem=$input->param('checkitem');
 my $expirationdate = $input->param('expiration_date');
+my $requestany = $input->param('requestany');
 
 my $multi_hold = $input->param('multi_hold');
 my $biblionumbers = $multi_hold ? $input->param('biblionumbers') : ($biblionumber . '/');
@@ -79,6 +80,11 @@ if (defined $checkitem && $checkitem ne ''){
 
 if ($type eq 'str8' && $borrower){
 
+    my $reserve_group_id;
+    if ($multi_hold && $requestany) {
+        $reserve_group_id = AddReserveGroup();
+    }
+
     foreach my $biblionumber (keys %bibinfos) {
         my $count=@bibitems;
         @bibitems=sort @bibitems;
@@ -104,8 +110,10 @@ if ($type eq 'str8' && $borrower){
 
         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}, $checkitem, $found,
+                $reserve_group_id);
         } else {
             # place a request on 1st available
             AddReserve($branch,$borrower->{'borrowernumber'},$biblionumber,\@realbi,$rank[0],$startdate,$expirationdate,$notes,$title,$checkitem,$found);
diff --git a/reserve/request.pl b/reserve/request.pl
index dfdd6e1..8f713d5 100755
--- a/reserve/request.pl
+++ b/reserve/request.pl
@@ -562,6 +562,7 @@ foreach my $biblionumber (@biblionumbers) {
         $reserve{'suspend'}        = $res->suspend();
         $reserve{'suspend_until'}  = $res->suspend_until();
         $reserve{'reserve_id'}     = $res->reserve_id();
+        $reserve{'reserve_group_id'} = $res->reserve_group_id;
 
         if ( C4::Context->preference('IndependentBranches') && $flags->{'superlibrarian'} != 1 ) {
             $reserve{'branchloop'} = [ GetBranchDetail( $res->branchcode() ) ];
diff --git a/reserve/reserve-group.pl b/reserve/reserve-group.pl
new file mode 100755
index 0000000..91ed958
--- /dev/null
+++ b/reserve/reserve-group.pl
@@ -0,0 +1,42 @@
+#!/usr/bin/env perl
+
+# This file is part of Koha.
+#
+# Koha is free software; you can redistribute it and/or modify it
+# under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 3 of the License, or
+# (at your option) any later version.
+#
+# Koha is distributed in the hope that it will be useful, but
+# WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with Koha; if not, see <http://www.gnu.org/licenses>.
+
+use Modern::Perl;
+use CGI qw( -utf8 );
+
+use C4::Auth;
+use C4::Output;
+use C4::Reserves;
+
+my $cgi = new CGI;
+my ($template, $loggedinuser, $cookie) = get_template_and_user({
+    template_name   => "reserve/reserve-group.tt",
+    query           => $cgi,
+    type            => "intranet",
+    authnotrequired => 0,
+    flagsrequired   => { circulate => 'circulate_remaining_permissions' },
+});
+
+my $reserve_group_id = $cgi->param('reserve_group_id');
+my @reserves = GetReserveGroupReserves($reserve_group_id);
+
+$template->param(
+    reserve_group_id => $reserve_group_id,
+    reserves => \@reserves,
+);
+
+output_html_with_http_headers $cgi, $cookie, $template->output;
diff --git a/svc/holds b/svc/holds
index 9c90f8c..fa5ccaa 100755
--- a/svc/holds
+++ b/svc/holds
@@ -89,6 +89,7 @@ while ( my $h = $holds_rs->next() ) {
         waiting_at     => $h->branch()->branchname(),
         waiting_here   => $h->branch()->branchcode() eq $branch,
         priority       => $h->priority(),
+        reserve_group_id => $h->reserve_group_id,
         subtitle       => GetRecordValue(
             'subtitle', GetMarcBiblio($biblionumber),
             GetFrameworkCode($biblionumber)
-- 
2.1.4