Bugzilla – Attachment 108504 Details for
Bug 23258
Batch tool for placing and cancelling holds
Home
|
New
|
Browse
|
Search
|
[?]
|
Reports
|
Help
|
New Account
|
Log In
[x]
|
Forgot Password
Login:
[x]
[patch]
Bug 23258: Batch holding
Bug-23258-Batch-holding.patch (text/plain), 22.73 KB, created by
Matthias Meusburger
on 2020-08-18 13:36:04 UTC
(
hide
)
Description:
Bug 23258: Batch holding
Filename:
MIME Type:
Creator:
Matthias Meusburger
Created:
2020-08-18 13:36:04 UTC
Size:
22.73 KB
patch
obsolete
>From acd8dc3cc3b8cb85bfd6876f17b1f97dcbe26012 Mon Sep 17 00:00:00 2001 >From: Matthias Meusburger <matthias.meusburger@biblibre.com> >Date: Tue, 6 Sep 2016 16:43:04 +0200 >Subject: [PATCH] Bug 23258: Batch holding > >This patch allows to batch place or cancel holds. > >Test plan: > > 1) Apply the patch > 2) Go to Circulation -> Batch holding > 3) Check that you can either upload a barcode file or use the textarea > 4) Check that you can place holds on items > 5) Check that you can cancel holds on items >--- > circ/batch_holding.pl | 181 +++++++++++++ > .../prog/en/modules/circ/batch_holding.tt | 293 +++++++++++++++++++++ > .../prog/en/modules/circ/circulation-home.tt | 6 +- > 3 files changed, 479 insertions(+), 1 deletion(-) > create mode 100755 circ/batch_holding.pl > create mode 100644 koha-tmpl/intranet-tmpl/prog/en/modules/circ/batch_holding.tt > >diff --git a/circ/batch_holding.pl b/circ/batch_holding.pl >new file mode 100755 >index 0000000..1e273f8 >--- /dev/null >+++ b/circ/batch_holding.pl >@@ -0,0 +1,181 @@ >+#!/usr/bin/perl >+ >+# Copyright 2019 BibLibre >+# >+# 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::Output; >+use C4::Auth; >+use Koha::Items; >+use C4::Reserves; >+use List::MoreUtils qw/uniq/; >+ >+my $input = new CGI; >+my $op = $input->param('op') // ''; >+my @itemnumbers; >+ >+my ( $template, $loggedinuser, $cookie ) = get_template_and_user( >+ { >+ template_name => "circ/batch_holding.tt", >+ query => $input, >+ type => "intranet", >+ authnotrequired => 0, >+ } >+); >+ >+if ( $op eq "show" ) { >+ my $filefh = $input->upload('uploadfile'); >+ my $filecontent = $input->param('filecontent'); >+ my $borrowernumber = $input->param('borrowernumber'); >+ my $branch = $input->param('branch'); >+ my $action = $input->param('action'); >+ my @notfoundbarcodes; >+ >+ my @contentlist; >+ if ($filefh) { >+ while ( my $content = <$filefh> ) { >+ $content =~ s/[\r\n]*$//; >+ push @contentlist, $content if $content; >+ } >+ >+ foreach my $barcode (@contentlist) { >+ my $item = Koha::Items->find( { barcode => $barcode } ); >+ if ($item) { >+ push @itemnumbers, $item->itemnumber; >+ } >+ else { >+ push @notfoundbarcodes, $barcode; >+ } >+ } >+ } >+ else { >+ if ( my $list = $input->param('barcodelist') ) { >+ push my @barcodelist, uniq( split( /\s\n/, $list ) ); >+ >+ foreach my $barcode (@barcodelist) { >+ >+ my $item = Koha::Items->find( { barcode => $barcode } ); >+ if ($item) { >+ push @itemnumbers, $item->itemnumber; >+ } >+ else { >+ push @notfoundbarcodes, $barcode; >+ } >+ } >+ >+ } >+ } >+ if (@notfoundbarcodes) { >+ my @notfoundbarcodesloop = map { { barcode => $_ } } @notfoundbarcodes; >+ $template->param( notfoundbarcodes => \@notfoundbarcodesloop ); >+ } >+ my @holdloop; >+ if (@itemnumbers) { >+ foreach my $itemnumber (@itemnumbers) { >+ my $item = Koha::Items->find( { itemnumber => $itemnumber } ); >+ my $biblio = >+ Koha::Biblios->find( { biblionumber => $item->biblionumber } ); >+ my $holdable = >+ CanItemBeReserved( $borrowernumber, $item->itemnumber )->{status}; >+ push @holdloop, >+ { >+ 'biblionumber' => $biblio->biblionumber, >+ 'title' => $biblio->title, >+ 'itemnumber' => $item->itemnumber, >+ 'barcode' => $item->barcode, >+ 'holdable' => ( $action eq "cancel" || $holdable eq 'OK' ) >+ ? 1 >+ : 0, >+ }; >+ } >+ >+ } >+ my $borrower = Koha::Patrons->find( { borrowernumber => $borrowernumber } ); >+ $template->param( >+ show => 1, >+ holdloop => \@holdloop, >+ borrowernumber => $borrowernumber, >+ borrowersurname => $borrower->surname, >+ borrowerfirstname => $borrower->firstname, >+ branch => $branch, >+ action => $action, >+ op => $op >+ ); >+} >+ >+elsif ( $op eq "result" ) { >+ my $branch = $input->param('branch'); >+ my $borrowernumber = $input->param('borrowernumber'); >+ my $action = $input->param('action'); >+ my @itemnumbers = $input->multi_param('holdable'); >+ my @holdloop; >+ foreach my $itemnumber (@itemnumbers) { >+ if ( $action eq "place" ) { >+ my $item = Koha::Items->find( { itemnumber => $itemnumber } ); >+ my $biblionumber = $item->biblionumber; >+ my $biblio = >+ Koha::Biblios->find( { biblionumber => $item->biblionumber } ); >+ my $reserve_id = AddReserve( >+ { >+ branchcode => $branch, >+ borrowernumber => $borrowernumber, >+ biblionumber => $biblionumber, >+ itemnumber => $itemnumber >+ } >+ ); >+ push @holdloop, >+ { >+ 'biblionumber' => $biblio->biblionumber, >+ 'title' => $biblio->title, >+ 'itemnumber' => $itemnumber, >+ 'barcode' => $item->barcode, >+ 'reserve_id' => $reserve_id, >+ }; >+ } >+ else { >+ my $holds = Koha::Holds->search( >+ { >+ itemnumber => $itemnumber, >+ borrowernumber => $borrowernumber >+ } >+ ); >+ if ($holds) { >+ while ( my $hold = $holds->next ) { >+ $hold->cancel; >+ } >+ >+ push @holdloop, { 'itemnumber' => $itemnumber, }; >+ } >+ } >+ } >+ my $borrower = Koha::Patrons->find( { borrowernumber => $borrowernumber } ); >+ $template->param( >+ result => 1, >+ holdloop => \@holdloop, >+ borrowernumber => $borrowernumber, >+ borrowersurname => $borrower->surname, >+ borrowerfirstname => $borrower->firstname, >+ branch => $branch, >+ cancel => $action eq "cancel" ? 1 : 0, >+ place => $action eq "place" ? 1 : 0, >+ op => $op >+ ); >+ >+} >+output_html_with_http_headers $input, $cookie, $template->output; >diff --git a/koha-tmpl/intranet-tmpl/prog/en/modules/circ/batch_holding.tt b/koha-tmpl/intranet-tmpl/prog/en/modules/circ/batch_holding.tt >new file mode 100644 >index 0000000..0c72910 >--- /dev/null >+++ b/koha-tmpl/intranet-tmpl/prog/en/modules/circ/batch_holding.tt >@@ -0,0 +1,293 @@ >+[% USE Koha %] >+[% USE Branches %] >+[% SET footerjs = 1 %] >+[% INCLUDE 'doc-head-open.inc' %] >+<title> >+ Koha › >+ Circulation › >+ [% IF ( op == "show" ) %] >+ Batch place or cancel holds › >+ Confirm selection >+ [% ELSIF ( op == "result") %] >+ Batch place or cancel holds › >+ Results >+ [% ELSE %] >+ Batch place or cancel holds >+ [% END %] >+</title> >+[% INCLUDE 'doc-head-close.inc' %] >+</head> >+<body id="batch_holding"> >+ >+[% INCLUDE 'header.inc' %] >+[% INCLUDE 'cataloging-search.inc' %] >+<div id="breadcrumbs"> >+ <a href="/cgi-bin/koha/mainpage.pl">Home</a> › >+ <a href="/cgi-bin/koha/circ/circulation-home.pl">Circulation</a> › >+ [% IF ( op == "show" ) %] >+ <a href="/cgi-bin/koha/circ/batch_holding.pl">Batch place or cancel holds</a> › >+ Confirm selection >+ [% ELSIF ( op == "result") %] >+ <a href="/cgi-bin/koha/circ/batch_holding.pl">Batch place or cancel holds</a> › >+ Results >+ [% ELSE %] >+ Batch place or cancel holds >+ [% END %] >+</div> >+ >+<div class="main container-fluid"> >+ <div class="row"> >+ [% IF Koha.Preference('CircSidebar') %] >+ <div class="col-sm-10 col-sm-push-2"> >+ [% ELSE %] >+ <div class="col-md-10 col-md-offset-1 col-lg-8 col-lg-offset-2"> >+ [% END %] >+ <main> >+ >+ [% IF show %] >+ [% IF ( notfoundbarcodes ) %] >+ <div class="dialog alert"><p>Warning, the following barcodes were not found:</p></div> >+ >+ <table> >+ <thead> >+ <tr><th>Barcodes not found</th></tr> >+ </thead> >+ <tbody> >+ [% FOREACH notfoundbarcode IN notfoundbarcodes %] >+ <tr><td>[% notfoundbarcode.barcode | html %]</td></td> >+ [% END %] >+ </tbody> >+ </table> >+ [% END #/IF notfoundbarcodes %] >+ >+ [% IF (holdloop) %] >+ [% IF ( action == "place" ) %] >+ <h1>Place holds on the following barcodes</h1> >+ [% ELSE %] >+ <h1>Cancel holds on the following barcodes</h1> >+ [% END %] >+ <ul> >+ <li>Patron: <a href="/cgi-bin/koha/members/moremember.pl?borrowernumber=[% borrowernumber | uri %]">[% borrowersurname | html %][% IF ( borrowerfirstname ) %], [% borrowerfirstname | html %][% END %]</a></li> >+ <li>Pickup location: [% Branches.GetName( branch ) | html %]</li> >+ </ul> >+ <div id="toolbar"> >+ <a id="SelectAll" href="#"><i class="fa fa-check"></i> Select all</a> | >+ <a id="ClearAll" href="#"><i class="fa fa-remove"></i> Clear all</a> >+ </div> >+ >+ <form action="batch_holding.pl" id="hold_selections" method="POST"> >+ <table> >+ <thead><tr><th>Hold</th><th>Title</th><th>Item</th><th>Comment</th></tr></thead> >+ <tbody> >+ [% FOREACH hold IN holdloop %] >+ <tr> >+ <td> >+ <input type="checkbox" name="holdable" [% IF !hold.holdable %]disabled="disabled"[% END %] checked="checked" value="[% hold.itemnumber | html %]" /> >+ </td> >+ <td> >+ <a href="/cgi-bin/koha/catalogue/detail.pl?biblionumber=[% hold.biblionumber | uri %]">[% hold.title | html %]</a> >+ </td> >+ <td> >+ <a href="/cgi-bin/koha/catalogue/moredetail.pl?biblionumber=[% hold.biblionumber | uri %]&itemnumber=[% hold.itemnumber | uri %]">[% hold.barcode | html %]</a> >+ </td> >+ <td> >+ [% IF !hold.holdable %] >+ <i class="fa fa-warning"></i> >+ This item cannot be placed on hold >+ [% END %] >+ </td> >+ </tr> >+ [% END %] >+ </tbody> >+ </table> >+ <input type="hidden" name="op" value="result" /> >+ <input type="hidden" name="action" value="[% action | html %]" /> >+ <input type="hidden" name="branch" value="[% branch | html %]" /> >+ <input type="hidden" name="borrowernumber" value="[% borrowernumber | html %]" /> >+ <fieldset class="action"> >+ <input type="submit" class="submit" value="Submit" /> >+ <a href="/cgi-bin/koha/circ/circulation-home.pl" class="cancel">Cancel</a> >+ </fieldset> >+ </form> >+ [% ELSE %] >+ <p>No holds can be placed or cancelled.</p> >+ <a href="batch_holding.pl">Batch place or cancel other holds</a> >+ [% END # /IF holdloop %] >+ >+ [% ELSIF result %] >+ >+ [% IF (holdloop) %] >+ <h1>Results</h1> >+ [% IF place %] >+ <p>The following items have been placed on hold:</p> >+ [% END %] >+ [% IF cancel %] >+ <p>The following holds were cancelled:</p> >+ [% END %] >+ <ul> >+ <li>Patron: <a href="/cgi-bin/koha/members/moremember.pl?borrowernumber=[% borrowernumber | uri %]">[% borrowersurname | html %][% IF ( borrowerfirstname ) %], [% borrowerfirstname | html %][% END %]</a></li> >+ <li>Pickup location: [% Branches.GetName( branch ) | html %]</li> >+ </ul> >+ <table> >+ <thead><tr><th>Title</th><th>Item</th><th>Holds</th></tr></thead> >+ <tbody> >+ [% FOREACH hold IN holdloop %] >+ <tr> >+ <td> >+ <a href="/cgi-bin/koha/catalogue/detail.pl?biblionumber=[% hold.biblionumber | uri %]">[% hold.title | html %]</a> >+ </td> >+ <td> >+ <a href="/cgi-bin/koha/catalogue/moredetail.pl?biblionumber=[% hold.biblionumber | uri %]&itemnumber=[% hold.itemnumber | uri %]">[% hold.barcode | html %]</a> >+ </td> >+ <td> >+ <a href="/cgi-bin/koha/reserve/request.pl?biblionumber=[% hold.biblionumber | uri %]">Show holds</a> >+ </td> >+ </tr> >+ [% END %] >+ </tbody> >+ </table> >+ >+ [% ELSE %] >+ <p>No holds could be placed or cancelled.</p> >+ [% END %] >+ >+ <p><a href="batch_holding.pl">Batch place or cancel other holds</a></p> >+ >+ [% ELSE %] >+ >+ <h1>Batch place or cancel holds</h1> >+ <form method="post" enctype="multipart/form-data" action="/cgi-bin/koha/circ/batch_holding.pl" id="batchholdform"> >+ <fieldset class="rows"> >+ <legend>Use a barcode file</legend> >+ <label for="uploadfile">File: </label> >+ <input type="file" id="uploadfile" name="uploadfile" /> >+ </fieldset> >+ >+ <fieldset class="rows"> >+ <legend>Or scan items one by one</legend> >+ <ol> >+ <li> >+ <label for="barcodelist">Barcode list (one barcode per line): </label> >+ <textarea rows="10" cols="30" id="barcodelist" name="barcodelist"></textarea> >+ </li> >+ </ol> >+ </fieldset> >+ >+ <fieldset class="rows"> >+ <legend>Place hold for</legend> >+ <ol> >+ <li> >+ <label class="required" for="findborrowerhold">Patron:</label> >+ <input type="hidden" id="borrowernumber" name="borrowernumber" /> >+ <div class="autocomplete"> >+ <input autocomplete="on" id="findborrowerhold" name="findborrowerhold" size="40" class="required" type="text" required="required" /> >+ <span class="required">Required</span> >+ </div> >+ <div class="hint"> >+ Enter patron card number or partial name >+ </div> >+ </li> >+ <li> >+ <label for="branch">Pickup at:</label> >+ <select name="branch" id="branch"> >+ [% PROCESS options_for_libraries libraries => Branches.all( unfiltered => 1 ) %] >+ </select> >+ </li> >+ </ol> >+ </fieldset> >+ >+ <fieldset class="rows"> >+ <legend>Action</legend> >+ <ol> >+ <li> >+ <input type="radio" name="action" id="action_place_holds" value="place" checked="checked"> >+ <label for="action_place_holds">Place holds</label> >+ </li> >+ <li> >+ <input type="radio" name="action" id="action_cancel_holds" value="cancel"> >+ <label for="action_cancel_holds">Cancel holds</label> >+ </li> >+ </ol> >+ </fieldset> >+ >+ <fieldset class="action"> >+ <input type="hidden" name="op" value="show" /> >+ <input type="submit" value="Continue" class="submit" /> >+ </fieldset> >+ </form> <!-- /#batchholdform --> >+ [% END # /IF show %] >+ >+ </main> >+ </div> <!-- /.col-sm-10.col-sm-push-2 --> >+ [% IF Koha.Preference('CircSidebar') %] >+ <div class="col-sm-2 col-sm-pull-10"> >+ <aside> >+ [% INCLUDE 'circ-nav.inc' %] >+ </aside> >+ </div> <!-- /.col-sm-2.col-sm-pull-10 --> >+ [% END %] >+ </div> <!-- /.row --> >+ >+[% MACRO jsinclude BLOCK %] >+ <script> >+ $(document).ready(function() { >+ if( $("#findborrowerhold").length > 0 ){ >+ $("#findborrowerhold").autocomplete({ >+ source: "/cgi-bin/koha/circ/ysearch.pl", >+ minLength: 3, >+ select: function( event, ui ) { >+ $( "#findborrowerhold" ).val( ui.item.surname + ', ' + ui.item.firstname + ' (' + ui.item.cardnumber + ')' ); >+ $( "#borrowernumber").val( ui.item.borrowernumber ); >+ return false; >+ } >+ }) >+ .data( "ui-autocomplete" )._renderItem = function( ul, item ) { >+ return $( "<li></li>" ) >+ .data( "ui-autocomplete-item", item ) >+ .append( "<a>" + item.surname + ", " + item.firstname + " (" + item.cardnumber + ") <small>" + item.address + " " + item.city + " " + item.zipcode + " " + item.country + "</small></a>" ) >+ .appendTo( ul ); >+ }; >+ } >+ >+ $("#batchholdform").validate({ >+ rules: { >+ uploadfile: { >+ required: function(element){ >+ return $("#barcodelist").val() === ""; >+ } >+ }, >+ barcodelist: { >+ required: function(element){ >+ return $("#uploadfile").val() === ""; >+ } >+ }, >+ }, >+ messages: { >+ uploadfile: { >+ required: _("Submit barcodes in a file or by entering them below") >+ }, >+ barcodelist: { >+ required: _("Submit barcodes by entering them here or by submitting a file above") >+ } >+ }, >+ findborrowerhold: { >+ required: true >+ } >+ >+ }); >+ >+ $("#SelectAll").on("click",function(e){ >+ e.preventDefault(); >+ $("#hold_selections input[type='checkbox']").prop('checked', true); >+ }); >+ >+ $("#ClearAll").on("click",function(e){ >+ e.preventDefault(); >+ $("#hold_selections input[type='checkbox']").prop('checked', false); >+ }); >+ >+ }); >+ </script> >+[% END %] >+ >+[% INCLUDE 'intranet-bottom.inc' %] >diff --git a/koha-tmpl/intranet-tmpl/prog/en/modules/circ/circulation-home.tt b/koha-tmpl/intranet-tmpl/prog/en/modules/circ/circulation-home.tt >index 2c5a6c6..937ac87 100644 >--- a/koha-tmpl/intranet-tmpl/prog/en/modules/circ/circulation-home.tt >+++ b/koha-tmpl/intranet-tmpl/prog/en/modules/circ/circulation-home.tt >@@ -9,7 +9,8 @@ > [% INCLUDE 'header.inc' %] > [% INCLUDE 'circ-search.inc' %] > >-<div id="breadcrumbs"><a href="/cgi-bin/koha/mainpage.pl">Home</a> › Circulation</div> >+<div id="breadcrumbs"> >+<a href="/cgi-bin/koha/mainpage.pl">Home</a> › Circulation</div> > > <div class="main container-fluid"> > >@@ -73,6 +74,9 @@ > <li> > <a class="circ-button" href="/cgi-bin/koha/circ/reserveratios.pl"><i class="fa fa-line-chart"></i> Hold ratios</a> > </li> >+ <li> >+ <a class="circ-button" href="/cgi-bin/koha/circ/batch_holding.pl"><i class="fa fa-bookmark"></i> Batch place or cancel holds</a> >+ </li> > </ul> > </div> > >-- >2.7.4
You cannot view the attachment while viewing its details because your browser does not support IFRAMEs.
View the attachment on a separate page
.
View Attachment As Diff
View Attachment As Raw
Actions:
View
|
Diff
|
Splinter Review
Attachments on
bug 23258
:
91230
|
106055
|
106077
|
106091
|
106598
|
107408
|
108498
| 108504 |
108628
|
108630