Koha/koha-tmpl/intranet-tmpl/prog/en/modules/circ/waitingreserves.tt
Agustin Moyano 954d2606a8 Bug 23678: Allow cancel holds in bulk
This patch allows staff patrons to cancel multiple holds in bulk.

To test:
1. Apply this patch
2. restart_all
3. In cataloge go to a book and place many holds
CHECK => Holds table shows a column of checkboxes
4. Play with checkboxes (have some fun ;-P)
CHECK => When you manually check all checkboxes, the checkbox in the
header also gets checked.
      => When you uncheck one of the checkboxes, the one in the header also gets unchecked.
      => If no checkbox is checked and you check the one in the header,
all checkboxes get checked.
      => If there are some checkboxes that are checked and others are
not, when you click on the checkbox in the header all checkboxes get
unchecked.
      => If all checkboxes are checked, when you uncheck the one in the
header, all checkboxes get unchecked.
      => Every time you play with checkboxes, the number in the button
"Cancel selected" changes.
5. Check some of the checkboxes and click on cancel selected.
SUCCESS => A background job gets fired to cancel all selected holds.
        => A message should appear with a link to the job.
6. Wait a few seconds and click on the link
SUCCESS => A message appears with the report of the execution of the
background job.
7. Grab a patron and search to hold
8. Select multiple biblios and click on "place hold for <patron>"
CHECK => After holds are confirmed, multiple holds table are shown.. one for
   each record. Checkboxes work exactly the same as before, but scoped
for each individual table. Checkboxes from one table will not affect
checkboxes from other tables.
9. Repeat steps 4 to 6.
10. Check In some of the items so the get in Waiting state.
11. Update expirationdate os some of those holds and set it to
    ReservesMaxPickUpDelay + 1 days earlier
NOTE => ReservesMaxPickUpDelay = 7 days by default, so sql syntax to update would be
     => update reserves set expirationdate = date_sub(expirationdate, interval 8 day) where reserve_id in (...)
12. Repeat steps 4 to 6 but in waitingreserves.pl, in both tabs.

Signed-off-by: Tomas Cohen Arazi <tomascohen@theke.io>
Signed-off-by: Martin Renvoize <martin.renvoize@ptfs-europe.com>

Bug 23678: (QA follow-up) Add missing template filter

Signed-off-by: Tomas Cohen Arazi <tomascohen@theke.io>
Signed-off-by: Martin Renvoize <martin.renvoize@ptfs-europe.com>

Bug 23678: (QA follow-up) Add missing filters

Signed-off-by: Tomas Cohen Arazi <tomascohen@theke.io>
Signed-off-by: Martin Renvoize <martin.renvoize@ptfs-europe.com>

Bug 23678: (QA follow-up) Use correct indentation

Signed-off-by: Tomas Cohen Arazi <tomascohen@theke.io>

JD amended patch: also Koha/BackgroundJob/BatchCancelHold.pm

JD Amended patch: Full rebase and adjustements made on top of bug 26080.

Signed-off-by: Jonathan Druart <jonathan.druart@bugs.koha-community.org>
2021-10-01 16:02:58 +02:00

269 lines
12 KiB
Text
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

[% USE raw %]
[% USE Asset %]
[% USE Koha %]
[% USE KohaDates %]
[% USE Branches %]
[% USE TablesSettings %]
[% USE AuthorisedValues %]
[% SET footerjs = 1 %]
[% INCLUDE 'doc-head-open.inc' %]
<title>Holds awaiting pickup &rsaquo; Circulation &rsaquo; Koha</title>
[% INCLUDE 'doc-head-close.inc' %]
</head>
<body id="circ_waitingreserves" class="circ">
[% INCLUDE 'header.inc' %]
[% INCLUDE 'circ-search.inc' %]
<nav id="breadcrumbs" aria-label="Breadcrumb" class="breadcrumb">
<ol>
<li>
<a href="/cgi-bin/koha/mainpage.pl">Home</a>
</li>
<li>
<a href="/cgi-bin/koha/circ/circulation-home.pl">Circulation</a>
</li>
<li>
<a href="#" aria-current="page">Holds awaiting pickup</a>
</li>
</ol>
</nav>
<div class="main container-fluid">
<div class="row">
<div class="col-sm-12">
<main>
<div class="row">
[% IF Koha.Preference('CircSidebar') %]
<div class="col-sm-10 col-sm-push-2">
[% ELSE %]
<div class="col-sm-12">
[% END %]
<h2>Holds awaiting pickup for your library on: [% show_date | $KohaDates %]
[% IF ( all_branches_link ) %]
<span style="margin-left:20px"><a href="[% all_branches_link | url %]">
View all libraries</a></span>
[% END %]
</h2>
[% IF ( cancel_result ) %]
[% FOREACH cancel_result %]
[% IF ( messagetransfert ) %]
<div class="dialog message">
<h2>This item is on hold for pick-up at [% Branches.GetName( branchname ) | html %]</h2>
<p><strong>[% nextreservtitle | html %]</strong> is on hold for <strong> [% nextreservsurname | html %], [% nextreservfirstname | html %]</strong>.
Please retain this item and check it in to process the hold.
</p>
<form name="cancelReservewithtransfert" action="waitingreserves.pl#[% tab | html %]" method="post">
<button type="submit" class="approve"><i class="fa fa-fw fa-check"></i> OK</button>
</form>
</div>
[% END %]
[% IF ( waiting ) %]
<div class="dialog message">
<h2>This item is on hold for pick-up at your library</h2>
<p><strong>[% nextreservtitle | html %]</strong> is on hold for <strong>[% nextreservsurname | html %], [% nextreservfirstname | html %]</strong>.
Please retain this item and check it in to process the hold.
</p>
<form name="cancelReservewithwaiting" action="waitingreserves.pl#[% tab | html %]" method="post">
<button type="submit" class="approve"><i class="fa fa-fw fa-check"></i> OK</button>
</form>
</div>
[% END %]
[% END %]
[% ELSE %]
[% IF enqueued %]
<div class="dialog message">
<p>The job has been enqueued! It will be processed as soon as possible.</p>
<p><a href="/cgi-bin/koha/admin/background_jobs.pl?op=view&id=[% job_id | uri %]" title="View detail of the enqueued job">View detail of the enqueued job</a></p>
</div>
[% END %]
<div id="resultlist" class="toptabs">
<ul>
<li><a href="#holdswaiting">Holds waiting: [% reservecount | html %]</a></li>
<li>
<a href="#holdsover">
Holds waiting over [% Koha.Preference('ReservesMaxPickUpDelay') | html %] days: [% overcount | html %]
</a>
</li>
</ul>
<div id="holdswaiting">
[% IF ( reserveloop ) %]
<div id="toolbar" class="btn-toolbar">
<button class="cancel_selected_holds" data-bulk="true"></button>
</div>
[% INCLUDE waiting_holds.inc select_column='1' table_name='holdst' reserveloop=reserveloop tab='holdwaiting' %]
[% ELSE %]
<div class="dialog message">No holds found.</div>
[% END %]
</div>
<div id="holdsover">
[% IF ( ReservesMaxPickUpDelay ) %]<p>Holds listed here have been awaiting pickup for more than [% ReservesMaxPickUpDelay | html %] days.</p>[% END %]
[% IF ( overloop ) %]
<span id="holdsover-cancel-all">
<button class="cancel_selected_holds" data-bulk="true"></button>
<form name="cancelAllReserve" action="waitingreserves.pl" method="post">
<input type="hidden" name="cancelall" value="1" />
<input type="hidden" name="allbranches" value="[% allbranches | html %]" />
<input type="hidden" name="tab" value="holdsover">
[% IF TransferWhenCancelAllWaitingHolds %]
<input type="submit" value="Cancel and Transfer all" />
[% ELSE %]
<input type="submit" value="Cancel all" />
[% END %]
</form>
[% UNLESS TransferWhenCancelAllWaitingHolds %]
Only items that need not be transferred will be cancelled (TransferWhenCancelAllWaitingHolds syspref)
[% END %]
</span>
[% INCLUDE waiting_holds.inc select_column='1' table_name='holdso' reserveloop=overloop tab='holdsover' %]
[% ELSE %]
<div class="dialog message">No holds found.</div>
[% END %]
</div>
</div>
[% END %]
[% IF Koha.Preference('CircSidebar') %]
</div> <!-- /.col-sm-10.col-sm-push-2 -->
<div class="col-sm-2 col-sm-pull-10">
<aside>
[% INCLUDE 'circ-nav.inc' %]
</aside>
</div> <!-- /.col-sm-2.col-sm-pull-10 -->
</div> <!-- /.row -->
[% END %]
</main>
</div> <!-- /.col-sm-12 -->
</div> <!-- /.row -->
<div id="cancelModal" class="modal" tabindex="-1" role="dialog" aria-hidden="true">
<div class="modal-dialog" role="document">
<div class="modal-content">
<div class="modal-header">
<button type="button" class="closebtn" data-dismiss="modal" aria-hidden="true">×</button>
<h3>Confirm deletion</h3>
</div>
<div class="modal-body">
<p>Are you sure you want to cancel this hold?</p>
<fieldset class="action">
[% SET hold_cancellation = AuthorisedValues.GetAuthValueDropbox('HOLD_CANCELLATION') %]
[% IF hold_cancellation %]
<label for="cancellation-reason">Cancellation reason: </label>
<select class="cancellation-reason" name="modal-cancellation-reason" id="modal-cancellation-reason">
<option value="">No reason given</option>
[% FOREACH reason IN hold_cancellation %]
<option value="[% reason.authorised_value | html %]">[% reason.lib | html %]</option>
[% END %]
</select>
[% END %]
</fieldset>
</div>
<div class="modal-footer">
<button id="cancelModalConfirmBtn" type="button" class="btn btn-danger">Confirm cancellation</button>
<a href="#" data-dismiss="modal">Cancel</a>
</div>
</div>
</div>
</div>
[% MACRO jsinclude BLOCK %]
[% INCLUDE 'datatables.inc' %]
[% INCLUDE 'columns_settings.inc' %]
<script>
var MSG_CANCEL_SELECTED = _("Cancel selected (%s)");
var holdst_columns_settings = [% TablesSettings.GetColumns( 'circ', 'holds_awaiting_pickup', 'holdst', 'json' ) | $raw %];
var holdso_columns_settings = [% TablesSettings.GetColumns( 'circ', 'holds_awaiting_pickup', 'holdso', 'json' ) | $raw %];
$(document).ready(function() {
KohaTable("holdst", {
"sPaginationType": "full",
"order": [[1, 'asc']]
}, holdst_columns_settings);
KohaTable("holdso", {
"sPaginationType": "full",
"order": [[1, 'asc']]
}, holdso_columns_settings);
$('#resultlist').tabs();
let cancel_link;
$("#cancelModalConfirmBtn").on("click",function(e) {
var ids = cancel_link.data('ids');
localStorage.selectedWaitingHolds = JSON.stringify(JSON.parse(localStorage.selectedWaitingHolds).filter(id => !ids.includes(id)));
let link = `waitingreserves.pl?cancelBulk=1&amp;ids=${ids.join(',')}`;
let reason = $("#modal-cancellation-reason").val();
if ( reason ) {
link += "&amp;cancellation-reason=" + reason
}
window.location.href = link;
return false;
});
if(!localStorage.selectedWaitingHolds || document.referrer.replace(/\?.*/, '') !== document.location.origin+document.location.pathname) {
localStorage.selectedWaitingHolds = '[]';
}
try {
JSON.parse(localStorage.selectedWaitingHolds);
} catch(e) {
localStorage.selectedWaitingHolds = '[]';
}
$('.holds_table .select_hold').each(function() {
if(JSON.parse(localStorage.selectedWaitingHolds).includes($(this).data('id'))) {
$(this).prop('checked', true);
}
});
$('.holds_table').each(function() {
var table = $(this);
var parent = table.parents('.ui-tabs-panel');
$('.holds_table .select_hold_all', parent).each(function() {
var count = $('.select_hold:not(:checked)', table).length;
$('.select_hold_all', table).prop('checked', !count);
});
$('.cancel_selected_holds', parent).html(MSG_CANCEL_SELECTED.format($('.holds_table .select_hold:checked', parent).length));
$('.holds_table .select_hold_all', parent).click(function() {
var count = $('.select_hold:checked', table).length;
$('.select_hold', table).prop('checked', !count);
$(this).prop('checked', !count);
$('.cancel_selected_holds', parent).data('ids', $('.holds_table .select_hold:checked', parent).toArray().map(el => $(el).data('id'))).html(MSG_CANCEL_SELECTED.format($('.holds_table .select_hold:checked', parent).length));
localStorage.selectedWaitingHolds = JSON.stringify($('.holds_table .select_hold:checked').toArray().map(el => $(el).data('id')));
});
$('.holds_table .select_hold', parent).click(function() {
var count = $('.select_hold:not(:checked)', table).length;
$('.select_hold_all', table).prop('checked', !count);
$('.cancel_selected_holds', parent).data('ids', $('.holds_table .select_hold:checked', parent).toArray().map(el => $(el).data('id'))).html(MSG_CANCEL_SELECTED.format($('.holds_table .select_hold:checked', parent).length));
localStorage.selectedWaitingHolds = JSON.stringify($('.holds_table .select_hold:checked').toArray().map(el => $(el).data('id')));
});
$('.cancel_selected_holds', parent).click(function(e) {
e.preventDefault();
if($('.select_hold:checked', table).length) {
cancel_link = $(this);
$('#cancelModal').modal();
}
return false;
});
});
});
</script>
[% END %]
[% INCLUDE 'intranet-bottom.inc' %]