Bugzilla – Attachment 177982 Details for
Bug 36135
Add tool to batch modify holds
Home
|
New
|
Browse
|
Search
|
[?]
|
Reports
|
Help
|
New Account
|
Log In
[x]
|
Forgot Password
Login:
[x]
[patch]
Bug 36135: Add more API functionality to display holds to modify
0002-Bug-36135-Add-more-API-functionality-to-display-hold.patch (text/plain), 53.00 KB, created by
Emmi Takkinen
on 2025-02-13 07:34:49 UTC
(
hide
)
Description:
Bug 36135: Add more API functionality to display holds to modify
Filename:
MIME Type:
Creator:
Emmi Takkinen
Created:
2025-02-13 07:34:49 UTC
Size:
53.00 KB
patch
obsolete
>From c8f00b0d5188e1d1c5a4f3ca6847f5297ca0471d Mon Sep 17 00:00:00 2001 >From: Emmi Takkinen <emmi.takkinen@koha-suomi.fi> >Date: Wed, 22 Jan 2025 13:50:26 +0200 >Subject: [PATCH 2/2] Bug 36135: Add more API functionality to display holds to > modify > >--- > admin/columns_settings.yml | 47 +- > .../en/modules/tools/batch_modify_holds.tt | 788 ++++++++++++------ > tools/batch_modify_holds.pl | 102 +-- > 3 files changed, 568 insertions(+), 369 deletions(-) > mode change 100644 => 100755 tools/batch_modify_holds.pl > >diff --git a/admin/columns_settings.yml b/admin/columns_settings.yml >index 2a632acc25..4a9bcbc70a 100644 >--- a/admin/columns_settings.yml >+++ b/admin/columns_settings.yml >@@ -1955,7 +1955,52 @@ modules: > columnname: suspend > - > columnname: print_slip >- >+ holds_to_modify: >+ default_display_length: 20 >+ columns: >+ - >+ columnname: checkbox >+ cannot_be_toggled: 1 >+ cannot_be_modified: 1 >+ - >+ columnname: expiration_date >+ - >+ columnname: title >+ - >+ columnname: barcode >+ - >+ columnname: patron >+ - >+ columnname: status >+ - >+ columnname: pickup_library >+ - >+ columnname: suspended >+ - >+ columnname: suspended_until >+ - >+ columnname: notes >+ modified_holds: >+ default_display_length: 20 >+ columns: >+ - >+ columnname: expiration_date >+ - >+ columnname: title >+ - >+ columnname: barcode >+ - >+ columnname: patron >+ - >+ columnname: status >+ - >+ columnname: pickup_library >+ - >+ columnname: suspended >+ - >+ columnname: suspended_until >+ - >+ columnname: notes > holdsratios: > holds-ratios: > default_display_length: 20 >diff --git a/koha-tmpl/intranet-tmpl/prog/en/modules/tools/batch_modify_holds.tt b/koha-tmpl/intranet-tmpl/prog/en/modules/tools/batch_modify_holds.tt >index 7a926bfb93..380b94685b 100644 >--- a/koha-tmpl/intranet-tmpl/prog/en/modules/tools/batch_modify_holds.tt >+++ b/koha-tmpl/intranet-tmpl/prog/en/modules/tools/batch_modify_holds.tt >@@ -5,6 +5,8 @@ > [% USE Categories %] > [% USE KohaDates %] > [% USE ItemTypes %] >+[% USE TablesSettings %] >+[% USE To %] > [% PROCESS 'html_helpers.inc' %] > > [% INCLUDE 'doc-head-open.inc' %] >@@ -37,194 +39,143 @@ > > <div class="main container-fluid"> > <div class="row"> >- <div class="col-sm-10 order-md-2 order-sm-1"> >+ <div class="col-sm-10 col-sm-push-2 order-sm-1"> > <main> > <h1>Batch modify holds</h1> >- >- [% IF view == 'form' %] >- <form method="get" action="/cgi-bin/koha/tools/batch_modify_holds.pl" id="modify_holds_form"> >- <fieldset class="rows"> >- <legend>Hold search criteria:</legend> >- <span class="hint">NOTE! Column patron_expiration_date is used as a main filter for expiration date field. Otherwise column expirationdate is used.</span> >- <ol> >- <li> >- <label for="expirationdate_from">Expiration date from:</label> >- <input type="text" size="10" id="expirationdate_from" name="expirationdate_from" class="flatpickr" data-date_to="to" /> >- </li> >- <li> >- <label for="expirationdate_to">Expiration date to:</label> >- <input type="text" size="10" id="expirationdate_to" name="expirationdate_to" class="flatpickr" /> >- </li> >- <li> >- <label for="branchcodes">Libraries:</label> >- <select name="branchcodes" id="branchcodes" multiple="multiple"> >- [% PROCESS options_for_libraries libraries => Branches.all(selected => "") %] >- </select> >- </li> >- <li> >- <label for="found_status">Found status:</label> >- <select name="found_status" id="found_status" multiple="multiple"> >- <option value="NULL">No status</option> >- <option value="T">In transit</option> >- <option value="P">In processing</option> >- <option value="W">Waiting</option> >- </select> >- </li> >- <li> >- <label for="suspend_status">Suspended:</label> >- <select name="suspend_status" id="suspend_status"> >- <option value="none" selected="selected"></option> >- <option value="0">Not suspended</option> >- <option value="1">Suspended</option> >- </select> >- </li> >- <li> >- <label for="suspend_until_from">Suspended until from:</label> >- <input type="text" size="10" id="suspend_until_from" name="suspend_until_from" class="flatpickr" data-date_to="to" /> >- </li> >- <li> >- <label for="suspend_until_to">Suspended until to:</label> >- <input type="text" size="10" id="suspend_until_to" name="suspend_until_to" class="flatpickr" /> >- </li> >- <li> >- <label for="reservenotes">Hold note:</label> >- <input type="text" id="reservenotes" name="reservenotes" /> >- </li> >- </ol> >- </fieldset> >+ [% IF view == "form" %] >+ <div id="modify_holds_search"> >+ <form method="get" action="/cgi-bin/koha/tools/batch_modify_holds.pl" id="modify_holds_form"> >+ <fieldset class="rows"> >+ <legend>Hold search criteria:</legend> >+ <ol> >+ <li> >+ <label for="expirationdate_from">Expiration date from:</label> >+ <input type="text" size="10" id="expirationdate_from" name="expirationdate_from" class="flatpickr" data-date_to="expirationdate_to" /> >+ </li> >+ <li> >+ <label for="expirationdate_to">Expiration date to:</label> >+ <input type="text" size="10" id="expirationdate_to" name="expirationdate_to" class="flatpickr"/> >+ </li> >+ <li> >+ <label for="branchcodes">Libraries:</label> >+ <select name="branchcodes" id="branchcodes" multiple="multiple"> >+ [% PROCESS options_for_libraries libraries => Branches.all(selected => "") %] >+ </select> >+ </li> >+ <li> >+ <label for="found_status">Found status:</label> >+ <select name="found_status" id="found_status" multiple="multiple"> >+ <option value="NULL">No status</option> >+ <option value="T">In transit</option> >+ <option value="P">In processing</option> >+ <option value="W">Waiting</option> >+ </select> >+ </li> >+ <li> >+ <label for="suspend_status">Suspended:</label> >+ <select name="suspend_status" id="suspend_status"> >+ <option value="none" selected="selected"></option> >+ <option value="0">Not suspended</option> >+ <option value="1">Suspended</option> >+ </select> >+ </li> >+ <li> >+ <label for="suspend_until_from">Suspended until from:</label> >+ <input type="text" size="10" id="suspend_until_from" name="suspend_until_from" class="flatpickr" data-date_to="suspend_until_to" /> >+ </li> >+ <li> >+ <label for="suspend_until_to">Suspended until to:</label> >+ <input type="text" size="10" id="suspend_until_to" name="suspend_until_to" class="flatpickr"/> >+ </li> >+ <li> >+ <label for="holdnotes">Hold note:</label> >+ <input type="text" id="holdnotes" name="holdnotes"/> >+ </li> >+ </ol> >+ </fieldset> >+ <fieldset class="action"> >+ <input type="submit" class="btn btn-primary" value="Search"/> >+ </fieldset> >+ </form> <!-- /#modify_holds_form --> >+ </div> <!-- /#modify_holds_search --> >+ [% ELSIF view == "report" %] >+ <div id="modified_holds_results-wrapper"> >+ <div id="modified_holds_results" class="page-section"> >+ <table id="modified_holds"></table> >+ </div> <!-- /#modified_holds_results --> > <fieldset class="action"> >- <input type="hidden" name="op" value="list" /> >- <input type="submit" class="btn btn-primary" value="Continue" /> >- <a class="cancel" href="/cgi-bin/koha/tools/tools-home.pl">Cancel</a> >- </fieldset> >- </form> >- <!-- /#modify_holds_form --> >- [% ELSIF view == 'list' %] >- [% IF holds.count %] >- <form action="/cgi-bin/koha/tools/batch_modify_holds.pl" method="post" id="process"> >- [% INCLUDE 'csrf-token.inc' %] >- <div><p>Found [% holds.count | html %][% IF holds.count > 1 %]holds[% ELSE %]hold[% END %]to modify.</p></div> >- <div class="page-section"> >- <div class="btn-toolbar selections-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> >- <table id="found_holds"> >- <thead> >- <tr> >- <th>Hold ID</th> >- <th>Expiration date</th> >- <th>Title</th> >- <th>Barcode</th> >- <th>Status</th> >- <th>Hold pickup library</th> >- <th>Suspended</th> >- <th>Suspended until</th> >- <th>Note</th> >- </tr> >- </thead> >- </table> >- <!-- /#holds --> >+ <a href="/cgi-bin/koha/tools/batch_modify_holds.pl">Return to batch hold modification</a> >+ </fieldset > >+ </div> <!-- /#modified_holds_results-wrapper --> >+ [% END %] >+ <div id="modify_holds_results-wrapper" style="display:none;"> >+ <div id="toolbar" class="btn-toolbar"> >+ <a href="#" id="edit_search" class="btn btn-default"><i class="fa-solid fa-pencil" aria-hidden="true"></i> Edit search</a> >+ </div> <!-- /#toolbar --> >+ <form action="/cgi-bin/koha/tools/batch_modify_holds.pl" method="post" id="process_mods"> >+ [% INCLUDE 'csrf-token.inc' %] >+ <div id="modify_holds_results" class="page-section"> >+ <div id="searchheader" class="searchheader"> >+ <a id="select_all" href="#" class="btn btn-link"><i class="fa fa-check"></i> Select all visible rows</a> >+ | <a id="clear_all" href="#" class="btn btn-link"><i class="fa fa-times"></i> Clear selections</a> > </div> >- <!-- /.page-section --> >+ <h3>Holds found for: <span class="searchpattern"></span></h3> >+ <table id="holds_to_modify"></table> >+ </div> <!-- /#modify_holds_results --> >+ <div id="modify_holds_form_options" class="page-section"> > <h2>Modify holds</h2> >- <div class="page-section"> >- <table id="hold_modifies"> >- <thead> >- <tr> >- <th>New expiration date</th> >- <th>New pickup library</th> >- <th>Suspend holds</th> >- <th>Suspend until</th> >- <th>New reserve note</th> >- </tr> >- </thead> >- <tbody> >- <tr> >- <td> >- <input type="text" id="new_expiration_date" name="new_expiration_date" class="flatpickr" data-flatpickr-futuredate="true" /> >- </td> >- <td> >- <select id="new_pickup_loc" name="new_pickup_loc"> >- <option value="" selected="selected"></option> >- [% PROCESS options_for_libraries libraries => Branches.pickup_locations(selected = "") %] >- </select> >- </td> >- <td> >- <select id="new_suspend_status" name="new_suspend_status"> >- <option value="" selected="selected"></option> >- <option value="not_suspended">Not suspended</option> >- <option value="suspend">Suspend</option> >- </select> >- </td> >- <td> >- <input type="text" id="new_suspend_date" name="new_suspend_date" class="flatpickr" data-flatpickr-futuredate="true" /> >- </td> >- <td> >- <input type="text" id="new_reserve_note" name="new_reserve_note" /> >- </td> >- </tr> >- </tbody> >- </table> >- <!-- /#hold_modifies --> >- </div> >- <fieldset class="action"> >- <input type="hidden" name="op" value="cud-modify" /> >- <input type="submit" class="btn btn-primary" value="Modify holds" /> >- <a class="cancel" href="/cgi-bin/koha/tools/batch_modify_holds.pl">Cancel</a> >- </fieldset> >- </form> >- <!-- /#process --> >- [% ELSE %] >- <div class="dialog message"> No holds were found for the selected filters. </div> >- <a class="cancel" href="/cgi-bin/koha/tools/batch_modify_holds.pl">Return</a> >- [% END %] >- [% ELSIF view == 'report' %] >- <div class="dialog message"> [% updated_holds.count | html %] holds have been modified! </div> >- <div class="page-section"> >- <table id="holds"> >- <thead> >- <tr> >- <th>Expiration date</th> >- <th>Title</th> >- <th>Barcode</th> >- <th>Hold pickup library</th> >- <th>Suspended</th> >- <th>Suspended until</th> >- <th>Note</th> >- </tr> >- </thead> >- <tbody> >- [% FOREACH hold IN updated_holds %] >+ <table id="modify_holds_options"> >+ <thead> > <tr> >- <td>[% hold.expirationdate | $KohaDates %]</td> >- <td><a href="/cgi-bin/koha/catalogue/detail.pl?biblionumber=[% hold.biblionumber | uri %]">[% hold.biblio.title | html %]</a></td> >- <td >- ><a href="/cgi-bin/koha/catalogue/moredetail.pl?itemnumber=[% hold.itemnumber | uri %]&biblionumber=[% hold.biblionumber | uri %]&bi=[% hold.biblionumber | uri %]#item[% hold.itemnumber | uri %]" >- >[% hold.item.barcode | html %]</a >- ></td >- > >- <td>[% Branches.GetName( hold.branchcode ) | html %]</td> >- <td>[% IF hold.suspend == 0 %]No[% ELSE %]Yes[% END %]</td> >- <td>[% hold.suspend_until | $KohaDates %]</td> >- <td>[% hold.reservenotes | html %]</td> >+ <th>New expiration date</th> >+ <th>New pickup library</th> >+ <th>Suspend holds</th> >+ <th>Suspend until</th> >+ <th>New hold note</th> > </tr> >- [% END %] >- </tbody> >- </table> >- <!-- /#holds --> >- </div> >- <!-- /.page-section --> >- <a class="cancel" href="/cgi-bin/koha/tools/batch_modify_holds.pl">Return to batch hold modification</a> >- [% END %] >+ </thead> >+ <tbody> >+ <tr> >+ <td> >+ <input type="text" id="new_expiration_date" name="new_expiration_date" class="flatpickr" data-flatpickr-futuredate="true" /> >+ </td> >+ <td> >+ <select name="new_pickup_loc" id="new_pickup_loc"> >+ <option value="" selected="selected"></option> >+ [% PROCESS options_for_libraries libraries => Branches.pickup_locations({ selected => "" }) %] >+ </select> >+ </td> >+ <td> >+ <select id="new_suspend_status" name="new_suspend_status"> >+ <option value="" selected="selected"></option> >+ <option value="0">Not suspended</option> >+ <option value="1">Suspend</option> >+ </select> >+ </td> >+ <td> >+ <input type="text" id="new_suspend_date" name="new_suspend_date" class="flatpickr" data-flatpickr-futuredate="true" /> >+ </td> >+ <td> >+ <input type="text" id="new_hold_note" name="new_hold_note"/> >+ </td> >+ </tr> >+ </tbody> >+ </table> <!-- /#modify_holds_options --> >+ </div> <!-- /#modify_holds_options --> >+ <fieldset class="action"> >+ <input type="hidden" name="op" value="cud-modify" /> >+ <input type="submit" class="btn btn-primary" value="Modify holds" /> >+ <a class="cancel" href="/cgi-bin/koha/tools/batch_modify_holds.pl">Cancel</a> >+ </fieldset> >+ </form> <!-- /#process_mods --> >+ </div> <!-- /#modify_holds_results-wrapper --> > </main> >- </div> >- <!-- /.col-sm-10 col-sm-push-2 --> >- >+ </div> <!-- /.col-sm-10 col-sm-push-2 --> > <div class="col-sm-2 col-sm-pull-10"> >- <aside> [% INCLUDE 'tools-menu.inc' %] </aside> >- </div> >- <!-- /.col-sm-2 col-sm-pull-10 --> >+ <aside> >+ [% INCLUDE 'tools-menu.inc' %] >+ </aside> >+ </div> <!-- /.col-sm-2 col-sm-pull-10 --> > </div> > <!-- /.row --> > </div> >@@ -232,132 +183,419 @@ > [% Asset.js("js/tools-menu.js") | $raw %] > [% INCLUDE 'calendar.inc' %] > [% INCLUDE 'datatables.inc' %] >+ [% INCLUDE 'js-patron-format.inc' %] > [% Asset.js("lib/jquery/plugins/humanmsg.js") | $raw %] > <script> >- $(document).ready(function () { >- var holds_table_url = "/api/v1/holds"; >- var table = $("#found_holds").kohaTable({ >- ajax: { >- url: holds_table_url, >- }, >- autoWidth: false, >- embed: ["biblio", "pickup_library", "item"], >- columns: [ >- { >- data: "hold_id", >- searchable: true, >- orderable: true, >- }, >- { >- data: "expiration_date", >- title: "Expiration date", >- searchable: true, >- orderable: true, >- render: function (data, type, row, meta) { >- return $date(row.expiration_date); >- }, >+ let hide_patron_name = [% IF Koha.Preference('HidePatronName') %]true[% ELSE %]false[% END %]; >+ let hold_ids = [% IF updated_holds %] [% updated_holds | $raw %] [% ELSE %] [] [% END %]; >+ >+ $(document).ready(function() { >+ >+ $("#select_all").click(function(e){ >+ e.preventDefault(); >+ $("#holds_to_modify input[type='checkbox']").each(function(){ >+ $(this).prop("checked", true); >+ }); >+ }); >+ >+ $("#clear_all").click(function(e){ >+ e.preventDefault(); >+ $("#holds_to_modify input[type='checkbox']").each(function(){ >+ $(this).prop("checked", false); >+ }); >+ }); >+ >+ $('#modify_holds_form').submit(function() { >+ var modify_holds_form = $(this); >+ search_holds(modify_holds_form); >+ $("#modify_holds_search").hide(); >+ $('#edit_search').show(); >+ return false; >+ }); >+ >+ $("#edit_search").on("click", function(e){ >+ e.preventDefault(); >+ $('#modify_holds_search').show(); >+ $('#edit_search').hide(); >+ }); >+ >+ function search_holds(modify_holds_form){ >+ var table_settings = [% TablesSettings.GetTableSettings( 'circ', 'holds', 'holds_to_modify', 'json' ) | $raw %]; >+ var searchpattern = ""; >+ var filters = { >+ "me.expirationdate": function(){ >+ var expirationdate_from = modify_holds_form.find("#expirationdate_from").val(); >+ var expirationdate_to = modify_holds_form.find("#expirationdate_to").val(); >+ >+ searchpattern += expirationdate_from && expirationdate_to ? _("expiration date between ")+ expirationdate_from + " and " + expirationdate_to + " ": ""; >+ >+ return expirationdate_from && expirationdate_to ? {"-between": [expirationdate_from, expirationdate_to]} : ""; > }, >- { >- data: "", >- title: "Title", >- searchable: true, >- orderable: true, >- render: function (data, type, row, meta) { >- return row.biblio.title; >- }, >+ "me.branchcode": function(){ >+ var branchcodes = modify_holds_form.find("#branchcodes").val(); >+ if( branchcodes.length > 0 ){ >+ searchpattern += _("from libraries "); >+ var selected_libraries = modify_holds_form.find("#branchcodes option:selected"); >+ $.each(selected_libraries, function(key, value) { >+ searchpattern += value.text + " "; >+ }); >+ return {"-in": branchcodes }; >+ } > }, >- { >- data: "", >- title: "Barcode", >- searchable: true, >- orderable: true, >- render: function (data, type, row, meta) { >- if (row.item_level) { >- return row.item.external_id; >- } else { >- return "Bibliographic level hold"; >+ "me.status": function(){ >+ var found_status = modify_holds_form.find("#found_status").val(); >+ if( found_status.length > 0 ){ >+ var filters = [{"-in": found_status}]; >+ if(jQuery.inArray("NULL", found_status) !== -1){ >+ filters.push({"=": null}); > } >- }, >+ searchpattern += _("found status in "); >+ $.each(found_status, function(key, value) { >+ let status = _("No status "); >+ if ( value == "T" ) { >+ status = _("In transit "); >+ } else if( value == "P" ) { >+ status = _("In processing "); >+ } else if( value == "W" ) { >+ status = _("Waiting "); >+ } >+ searchpattern += status; >+ }); >+ return filters; >+ } > }, >- { >- data: "status", >- title: "Status", >- searchable: true, >- orderable: true, >+ "me.suspended": function(){ >+ var suspend_status = modify_holds_form.find("#suspend_status").val(); >+ >+ if(suspend_status != "none"){ >+ var suspended_text = suspend_status == 0 ? _("Not suspended") : _("Suspended"); >+ searchpattern += _("with suspend status ") + suspended_text; >+ >+ return {"=": suspend_status}; >+ } > }, >- { >- data: "pickup_library_id", >- title: "Pickup library", >- searchable: true, >- orderable: true, >- render: function (data, type, row, meta) { >- return row.pickup_library.name; >- }, >+ "me.suspend_until": function(){ >+ var suspend_until_from = modify_holds_form.find("#suspend_until_from").val(); >+ var suspend_until_to = modify_holds_form.find("#suspend_until_to").val(); >+ >+ searchpattern += suspend_until_from && suspend_until_to ? _("suspend date between ")+ suspend_until_from +"-"+suspend_until_to : ""; >+ >+ return suspend_until_from && suspend_until_to ? {"-between": [suspend_until_from, suspend_until_to]} : ""; > }, >- { >- data: "suspended", >- title: "Suspended", >- searchable: true, >- orderable: true, >- render: function (data) { >- return data ? "Yes" : "No"; >- }, >+ "me.notes": function(){ >+ var holdnotes = modify_holds_form.find("#holdnotes").val(); >+ >+ searchpattern += holdnotes ? _("hold notes like ") + holdnotes : ""; >+ >+ return holdnotes ? {"-like": "%"+holdnotes+"%"} : ""; >+ } >+ } >+ >+ var holds_modify_table = $("#holds_to_modify").kohaTable({ >+ "ajax": { >+ "url": "/api/v1/holds" > }, >- { >- data: "suspended_until", >- title: "Suspended until", >- searchable: true, >- orderable: true, >- render: function (data, type, row, meta) { >- return $date(row.suspended_until); >+ "embed": [ >+ "biblio", >+ "item", >+ "pickup_library", >+ "patron" >+ ], >+ "destroy": true, >+ "autoWidth": false, >+ "processing": true, >+ "columns": [ >+ { >+ "data": "hold_id", >+ "name": "checkbox", >+ "orderable": false, >+ "render": function(data, type, row, meta) { >+ return '<input type="checkbox" name="hold_id" value="'+ encodeURIComponent(data) +'"/>' >+ } > }, >- }, >- { >- data: "notes", >- title: "Notes", >- searchable: true, >- orderable: true, >- }, >- ], >- }); >+ { >+ "data": "expiration_date", >+ "name": "expiration_date", >+ "type": "date", >+ "title": _("Expiration date"), >+ "orderable": true, >+ "render": function( data, type, row, meta ) { >+ return $date(data); >+ } >+ }, >+ { >+ "data": "biblio.title", >+ "name": "title", >+ "title": _("Title"), >+ "orderable": true, >+ "render": function( data, type, row, meta ) { >+ return '<a href="/cgi-bin/koha/catalogue/detail.pl?' + >+ 'biblionumber=' + encodeURIComponent( row.biblio_id ) + '">' + escape_str( data ) + '</a>'; >+ } >+ }, >+ { >+ "data": "item.external_id", >+ "name": "barcode", >+ "title": _("Barcode"), >+ "defaultContent": _(""), >+ "orderable": true, >+ "render": function( data, type, row, meta ) { >+ if( row.item ){ >+ let item_id = encodeURIComponent( row.item_id ); >+ let biblio_id = encodeURIComponent( row.biblio_id ); >+ return '<a href="/cgi-bin/koha/catalogue/moredetail.pl?' + >+ 'itemnumber='+ item_id +'&biblionumber='+ biblio_id +'&' + >+ 'bi='+ biblio_id +'#item'+ item_id +'">' + escape_str( data ) + '</a>' >+ } >+ } >+ }, >+ { >+ "data": "patron_id", >+ "name": "patron", >+ "title": _("Patron"), >+ "orderable": true, >+ "render": function( data, type, row, meta ) { >+ let patron_to_html = $patron_to_html(row.patron, { url: true, display_cardnumber: true, hide_patron_name }); >+ return patron_to_html; >+ } >+ }, >+ { >+ "data": "status", >+ "name": "status", >+ "className": "found_status", >+ "title": _("Status"), >+ "orderable": true, >+ "render": function( data, type, row, meta ) { >+ let status = _("No status"); >+ if ( data == "T" ) { >+ status = _("In transit"); >+ } else if( data == "P" ) { >+ status = _("In processing"); >+ } else if( data == "W" ) { >+ status = _("Waiting"); >+ } >+ return status; >+ } >+ }, >+ { >+ "data": "pickup_library_id", >+ "name": "pickup_library", >+ "title": _("Hold pickup library"), >+ "orderable": true, >+ "render": function( data, type, row, meta ) { >+ return escape_str( row.pickup_library.name ); >+ } >+ }, >+ { >+ "data": "suspended", >+ "name": "suspended", >+ "title": _("Suspended"), >+ "orderable": true, >+ "render": function( data, type, row, meta ) { >+ return data == 0 ? _("No") : _("Yes"); >+ } >+ }, >+ { >+ "data": "suspended_until", >+ "name": "suspended_until", >+ "title": _("Suspended until"), >+ "orderable": true, >+ "render": function( data, type, row, meta ) { >+ return $date( data ); >+ } >+ }, >+ { >+ "data": "notes", >+ "name": "notes", >+ "title": _("Notes"), >+ "orderable": true, >+ "render": function( data, type, row, meta ) { >+ return escape_str( data ); >+ } >+ } >+ ] >+ }, table_settings, 0, filters); >+ >+ $(".searchpattern").text(searchpattern); >+ >+ $("#modify_holds_results-wrapper").show(); >+ } > >- $("#process").on("submit", function (e) { >+ $("#process_mods").on('submit', function(e) { >+ >+ var hold_ids = []; > var errors = []; >- var reserve_ids = $("input[type=checkbox][name='hold_id']:checked"); >+ var holds_checked = $("input[type=checkbox][name='hold_id']:checked"); >+ >+ var new_expiration_date = $("#new_expiration_date").val(); >+ var new_pickup_loc = $("#new_pickup_loc").val(); >+ var new_suspend_status = $("#new_suspend_status").val(); >+ var new_suspend_date = $("#new_suspend_date").val(); >+ var new_hold_note = $("#new_hold_note").val(); > >- var new_pickup_loc = $("#new_pickup_loc").val(); >- var new_suspend_status = $("#new_suspend_status").val(); >- var new_suspend_date = $("#new_suspend_date").val(); >+ if ( holds_checked.length == 0 ) { >+ e.preventDefault(); >+ errors.push(_("Please select at least one hold to process.")); >+ } > >- if (new_pickup_loc || new_suspend_status) { >- reserve_ids.each(function () { >- if ($(this).parents("tr").children(".found_status").data("found") != "") { >+ if( new_pickup_loc || new_suspend_status ){ >+ var found = false; >+ holds_checked.each(function(){ >+ if($(this).parents("tr").children(".found_status").html() !== "No status"){ > e.preventDefault(); >- if (new_suspend_status) { >- errors.push(_("One or more holds have found status and can't be suspended.")); >- } >- if (new_pickup_loc) { >- errors.push(_("One or more holds have found status and their pick up location can't be changed.")); >- } >- return false; >+ found = true; > } > }); >+ if( new_suspend_status && found ){ >+ errors.push(_("One or more holds have found status and can't be suspended.")); >+ } >+ if( new_pickup_loc && found ){ >+ errors.push(_("One or more holds have found status and their pick up location can't be changed.")); >+ } > } >- if ((!new_suspend_status || new_suspend_status == "not_suspended") && new_suspend_date) { >+ if( ( !new_suspend_status || new_suspend_status == "0") && new_suspend_date ){ > e.preventDefault(); > errors.push(_("You have to suspend holds if new suspend until date is set.")); >- return false; > } > > if (errors.length > 0) { > e.preventDefault(); > $("#errorList").html(errors.map(error => `<li>${error}</li>`).join("")); >- $("#errorModal").modal("show"); >+ $("#errorModal").modal('show'); > return false; > } > > return true; > }); >+ >+ //Modified holds table >+ var table_settings = [% TablesSettings.GetTableSettings( 'circ', 'holds', 'modified_holds', 'json' ) | $raw %]; >+ var filters = { >+ "me.hold_id": function(){ >+ return {"-in": hold_ids}; >+ } >+ } >+ >+ var holds_modified_table = $("#modified_holds").kohaTable({ >+ "ajax": { >+ "url": "/api/v1/holds" >+ }, >+ "embed": [ >+ "biblio", >+ "item", >+ "pickup_library", >+ "patron" >+ ], >+ "destroy": true, >+ "autoWidth": false, >+ "processing": true, >+ "columns": [ >+ { >+ "data": "expiration_date", >+ "name": "expiration_date", >+ "type": "date", >+ "title": _("Expiration date"), >+ "orderable": true, >+ "render": function( data, type, row, meta ) { >+ return $date(data); >+ } >+ }, >+ { >+ "data": "biblio.title", >+ "name": "title", >+ "title": _("Title"), >+ "orderable": true, >+ "render": function( data, type, row, meta ) { >+ return '<a href="/cgi-bin/koha/catalogue/detail.pl?' + >+ 'biblionumber=' + encodeURIComponent( row.biblio_id ) + '">' + escape_str( data ) + '</a>'; >+ } >+ }, >+ { >+ "data": "item.external_id", >+ "name": "barcode", >+ "title": _("Barcode"), >+ "defaultContent": _(""), >+ "orderable": true, >+ "render": function( data, type, row, meta ) { >+ if( row.item ){ >+ let item_id = encodeURIComponent( row.item_id ); >+ let biblio_id = encodeURIComponent( row.biblio_id ); >+ return '<a href="/cgi-bin/koha/catalogue/moredetail.pl?' + >+ 'itemnumber='+ item_id +'&biblionumber='+ biblio_id +'&' + >+ 'bi='+ biblio_id +'#item'+ item_id +'">' + escape_str( data ) + '</a>' >+ } >+ } >+ }, >+ { >+ "data": "patron_id", >+ "name": "patron", >+ "title": _("Patron"), >+ "orderable": true, >+ "render": function( data, type, row, meta ) { >+ let patron_to_html = $patron_to_html(row.patron, { url: true, display_cardnumber: true, hide_patron_name }); >+ return patron_to_html; >+ } >+ }, >+ { >+ "data": "status", >+ "name": "status", >+ "className": "found_status", >+ "title": _("Status"), >+ "orderable": true, >+ "render": function( data, type, row, meta ) { >+ let status = _("No status"); >+ if ( data == "T" ) { >+ status = _("In transit"); >+ } else if( data == "P" ) { >+ status = _("In processing"); >+ } else if( data == "W" ) { >+ status = _("Waiting"); >+ } >+ return status; >+ } >+ }, >+ { >+ "data": "pickup_library_id", >+ "name": "pickup_library", >+ "title": _("Hold pickup library"), >+ "orderable": true, >+ "render": function( data, type, row, meta ) { >+ return escape_str( row.pickup_library.name ); >+ } >+ }, >+ { >+ "data": "suspended", >+ "name": "suspended", >+ "title": _("Suspended"), >+ "orderable": true, >+ "render": function( data, type, row, meta ) { >+ return data == 0 ? _("No") : _("Yes"); >+ } >+ }, >+ { >+ "data": "suspended_until", >+ "name": "suspended_until", >+ "title": _("Suspended until"), >+ "orderable": true, >+ "render": function( data, type, row, meta ) { >+ return $date( data ); >+ } >+ }, >+ { >+ "data": "notes", >+ "name": "notes", >+ "title": _("Notes"), >+ "orderable": true, >+ "render": function( data, type, row, meta ) { >+ return escape_str( data ); >+ } >+ } >+ ] >+ }, table_settings, 0, filters); >+ >+ var modified_message = '<div class="alert alert-info">'+hold_ids.length+' hold(s) have been modified!</div>'; >+ $("#modified_holds_results-wrapper").prepend(modified_message); >+ > }); > </script> > [% END %] >diff --git a/tools/batch_modify_holds.pl b/tools/batch_modify_holds.pl >old mode 100644 >new mode 100755 >index 8d3f29a59a..e174b133a5 >--- a/tools/batch_modify_holds.pl >+++ b/tools/batch_modify_holds.pl >@@ -19,10 +19,11 @@ > # <http://www.gnu.org/licenses> > > use Modern::Perl; >- > use CGI; > >-use C4::Auth qw( get_template_and_user ); >+use JSON qw( to_json ); >+ >+use C4::Auth qw( get_template_and_user ); > use C4::Output qw( output_html_with_http_headers ); > > use Koha::DateUtils qw( dt_from_string ); >@@ -44,98 +45,13 @@ my @hold_ids; > > if ( $op eq 'form' ) { > $template->param( view => 'form', ); >-} elsif ( $op eq 'list' ) { >- >- my @reserve_ids = $input->multi_param('reserve_ids'); >- my $expirationdate_from = $input->param('expirationdate_from'); >- my $expirationdate_to = $input->param('expirationdate_to'); >- my @branchcodes = $input->multi_param('branchcodes'); >- my @found_status = $input->multi_param('found_status'); >- my $suspend_status = $input->param('suspend_status'); >- my $suspend_until_from = $input->param('suspend_until_from'); >- my $suspend_until_to = $input->param('suspend_until_to'); >- my $reservenotes = $input->param('reservenotes'); >- >- my $search_params; >- >- if (@reserve_ids) { >- $search_params->{'me.reserve_id'} = { -in => \@reserve_ids }; >- } else { >- if ( $expirationdate_from && $expirationdate_to ) { >- my $expirationdate_params = { >- -or => { >- patron_expiration_date => { >- -between => [ >- $expirationdate_from, >- $expirationdate_to >- ] >- }, >- expirationdate => { >- -between => [ >- $expirationdate_from, >- $expirationdate_to >- ] >- } >- } >- }; >- >- $search_params = $expirationdate_params; >- } >- >- if (@branchcodes) { >- $search_params->{'me.branchcode'} = { -in => \@branchcodes }; >- } >- >- if (@found_status) { >- my $found_params = {}; >- >- # if NULL is used as filter, use it as "is" param >- if ( grep { $_ eq "NULL" } @found_status ) { >- $found_params->{'-or'}{'-is'} = undef; >- $found_params->{'-or'}{'-in'} = \@found_status; >- } else { >- $found_params = { -in => \@found_status }; >- } >- >- $search_params->{'me.found'} = $found_params; >- } >- >- if ( $suspend_status ne "none" ) { >- $search_params->{'me.suspend'} = $suspend_status; >- } >- >- if ( $suspend_until_from && $suspend_until_to ) { >- $search_params->{'me.suspend_until'} = { >- -between => [ >- $suspend_until_from, >- $suspend_until_to, >- ] >- }; >- } >- >- if ($reservenotes) { >- $search_params->{'me.reservenotes'} = { -like => "%" . $reservenotes . "%" }; >- } >- } >- >- my $holds = Koha::Holds->search( >- $search_params, >- { join => [ "item", "biblio" ] } >- ); >- >- $template->param( >- holds => $holds, >- view => 'list', >- ); > } >- >-if ( $op eq 'cud-modify' ) { >- >+elsif ( $op eq 'cud-modify' ) { > my $new_expiration_date = $input->param('new_expiration_date'); > my $new_pickup_loc = $input->param('new_pickup_loc'); > my $new_suspend_status = $input->param('new_suspend_status'); > my $new_suspend_date = $input->param('new_suspend_date'); >- my $new_reserve_note = $input->param('new_reserve_note'); >+ my $new_hold_note = $input->param('new_hold_note'); > > @hold_ids = $input->multi_param('hold_id'); > >@@ -153,7 +69,7 @@ if ( $op eq 'cud-modify' ) { > } > > if ( $new_suspend_status && !$hold->is_found ) { >- if ( $new_suspend_status eq "suspend" ) { >+ if ( $new_suspend_status ) { > $hold->suspend(1)->store; > if ($new_suspend_date) { > $hold->suspend_until($new_suspend_date)->store; >@@ -166,13 +82,13 @@ if ( $op eq 'cud-modify' ) { > } > } > >- if ($new_reserve_note) { >- $hold->reservenotes($new_reserve_note)->store; >+ if ($new_hold_note) { >+ $hold->reservenotes($new_hold_note)->store; > } > } > > $template->param( >- updated_holds => $holds_to_update, >+ updated_holds => to_json(\@hold_ids), > view => 'report', > ); > >-- >2.34.1 >
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 36135
:
163021
|
163022
|
163023
|
163024
|
163529
|
163530
|
163540
|
163541
|
163542
|
163543
|
163544
|
163796
|
164037
|
164051
|
164057
|
164058
|
164059
|
164060
|
164061
|
164062
|
164063
|
177518
|
177519
|
177959
|
177960
|
177961
|
177962
|
177963
|
177964
|
177965
|
177966
|
177967
|
177968
|
177969
|
177975
|
177976
|
177981
|
177982
|
178154
|
178155
|
178156
|
178157
|
178158
|
178159
|
178160
|
178161
|
178162
|
178163
|
178164
|
178165
|
178166
|
178167