From 56ef9e753acae06347b31ca3c84a964767c52f06 Mon Sep 17 00:00:00 2001 From: Jonathan Druart Date: Mon, 16 Mar 2020 11:45:44 +0100 Subject: [PATCH] Bug 24846: Add new tool to batch extend due dates With events sometimes leading to unforeseen branch closures (think Coronavirus as an example), it would be helpful to have a tool that would allow librarians to update due dates in bulk based on branch and current due date of the material. It allows to select checkouts given the following parameters: * libraries * patron's categories * range of the due date You can set a hard due date, or define a number of days to extend the due date. Test plan: Check some items out Use the new tool to extend the due dates Test the different filters to make sure they all work Note: What about holidays? Signed-off-by: Bernardo Gonzalez Kriegel Signed-off-by: Martin Renvoize --- .../modules/tools/batch_extend_due_dates.tt | 269 ++++++++++++++++++ tools/batch_extend_due_dates.pl | 145 ++++++++++ 2 files changed, 414 insertions(+) create mode 100644 koha-tmpl/intranet-tmpl/prog/en/modules/tools/batch_extend_due_dates.tt create mode 100755 tools/batch_extend_due_dates.pl diff --git a/koha-tmpl/intranet-tmpl/prog/en/modules/tools/batch_extend_due_dates.tt b/koha-tmpl/intranet-tmpl/prog/en/modules/tools/batch_extend_due_dates.tt new file mode 100644 index 0000000000..1286337220 --- /dev/null +++ b/koha-tmpl/intranet-tmpl/prog/en/modules/tools/batch_extend_due_dates.tt @@ -0,0 +1,269 @@ +[% USE raw %] +[% USE Asset %] +[% SET footerjs = 1 %] +[% USE Branches %] +[% USE Categories %] +[% USE KohaDates %] +[% USE ItemTypes %] +[% PROCESS 'html_helpers.inc' %] + +[% INCLUDE 'doc-head-open.inc' %] +Koha › Tools › Batch extend due dates +[% INCLUDE 'doc-head-close.inc' %] +[% Asset.css("css/humanmsg.css") | $raw %] + + + + [% INCLUDE 'header.inc' %] + [% INCLUDE 'cat-search.inc' %] + + + +
+
+
+
+ +

Batch extend due dates

+ + [% IF ( messages ) %] +
+ + [% FOREACH message IN messages %] + [% IF message.type == 'success' %] +
+ [% ELSIF message.type == 'warning' %] +
+ [% ELSIF message.type == 'error' %] +
+ [% END %] + [% IF message.error %] + (The error was: [% message.error | html %]. See the Koha logfile for more information). + [% END %] +
+ [% END %] + +
+ [% END %] + + [% IF view == 'form' %] +
+
+ Checkout criteria: +
    +
  1. + + [% SET categories = Categories.all() %] + +
  2. + +
  3. + + +
  4. + +
  5. + + +
  6. + +
  7. + + +
  8. +
+
+
+ New due date: +
    +
  1. + + +
  2. + +
  3. + + +
  4. +
+
+
+ + + Cancel +
+
+ [% ELSIF view == 'list' %] + [% IF checkouts.count %] +
+ + + + + + + + + + + + + + + + [% FOR checkout IN checkouts %] + + + + + + + + + + + [% END %] + +
 Due dateTitleItem typeHome libraryChecked out onChecked out fromNew due date
[% checkout.date_due | $KohaDates as_due_date => 1 %][% checkout.item.biblio.title | html %][% ItemTypes.GetDescription( checkout.item.effective_itemtype ) | html %][% checkout.item.home_branch.branchname | html %][% checkout.issuedate | $KohaDates %][% Branches.GetName( checkout.branchcode ) | html %] + [% IF new_hard_due_date %] + [% new_hard_due_date | $KohaDates %] + [% ELSE %] + [% new_due_dates.shift | $KohaDates %] + [% END %] +
+
Reminder: this action will modify all selected checkouts!
+
+ + + + + Cancel +
+
+ [% ELSE %] +
+ No checkouts for the selected filters. +
+ [% END %] + [% ELSIF view == 'report' %] +
+ Due dates have been modified! +
+ + + + + + + + + + + + + + [% FOR checkout IN checkouts %] + + + + + + + + + [% END %] + +
Due dateTitleItem typeHome libraryChecked out onChecked out from
[% checkout.date_due | $KohaDates as_due_date => 1 %][% checkout.item.biblio.title | html %][% ItemTypes.GetDescription( checkout.item.effective_itemtype ) | html %][% checkout.item.home_branch.branchname | html %][% checkout.issuedate | $KohaDates %][% Branches.GetName( checkout.branchcode ) | html %]
+ [% END %] +
+
+ +
+ +
+
+ +[% MACRO jsinclude BLOCK %] + [% Asset.js("js/tools-menu.js") | $raw %] + [% INCLUDE 'calendar.inc' %] + [% INCLUDE 'datatables.inc' %] + [% Asset.js("lib/jquery/plugins/jquery.checkboxes.min.js") | $raw %] + [% Asset.js("lib/jquery/plugins/humanmsg.js") | $raw %] + +[% END %] + +[% INCLUDE 'intranet-bottom.inc' %] diff --git a/tools/batch_extend_due_dates.pl b/tools/batch_extend_due_dates.pl new file mode 100755 index 0000000000..5f0001df1c --- /dev/null +++ b/tools/batch_extend_due_dates.pl @@ -0,0 +1,145 @@ +#!/usr/bin/perl + +# This file is part of Koha. +# +# Copyright 2020 Koha Development Team +# +# 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 +# + +use Modern::Perl; + +use CGI; + +use C4::Auth qw( get_template_and_user ); +use C4::Output qw( output_html_with_http_headers ); +use Koha::Checkouts; +use Koha::DateUtils qw( dt_from_string ); + +my $input = new CGI; +my $op = $input->param('op') // q|form|; + +my ( $template, $loggedinuser, $cookie ) = get_template_and_user( + { + template_name => 'tools/batch_extend_due_dates.tt', + query => $input, + type => "intranet", + authnotrequired => 0, + flagsrequired => { tools => 'batch_extend_due_dates' }, + } +); + +if ( $op eq 'form' ) { + $template->param( view => 'form', ); +} +elsif ( $op eq 'list' ) { + + my @categorycodes = $input->multi_param('categorycodes'); + my @branchcodes = $input->multi_param('branchcodes'); + my $from_due_date = $input->param('from_due_date'); + my $to_due_date = $input->param('to_due_date'); + my $new_hard_due_date = $input->param('new_hard_due_date'); + my $due_date_days = $input->param('due_date_days'); + + my $dtf = Koha::Database->new->schema->storage->datetime_parser; + my $search_params; + if (@categorycodes) { + $search_params->{'borrower.categorycode'} = { -in => \@categorycodes }; + } + if (@branchcodes) { + $search_params->{'me.branchcode'} = { -in => \@branchcodes }; + } + if ( $from_due_date and $to_due_date ) { + my $to_due_date_endday = dt_from_string($to_due_date); + $to_due_date_endday + ->set( # We set last second of day to see all checkouts from that day + hour => 23, + minute => 59, + second => 59 + ); + $search_params->{'me.date_due'} = { + -between => [ + $dtf->format_datetime( dt_from_string($from_due_date) ), + $dtf->format_datetime($to_due_date_endday), + ] + }; + } + elsif ($from_due_date) { + $search_params->{'me.date_due'} = + { '>=' => $dtf->format_datetime( dt_from_string($from_due_date) ) }; + } + elsif ($to_due_date) { + my $to_due_date_endday = dt_from_string($to_due_date); + $to_due_date_endday + ->set( # We set last second of day to see all checkouts from that day + hour => 23, + minute => 59, + second => 59 + ); + $search_params->{'me.date_due'} = + { '<=' => $dtf->format_datetime($to_due_date_endday) }; + } + + my $checkouts = Koha::Checkouts->search( + $search_params, + { + join => [ 'item', 'borrower' ] + } + ); + + my @new_due_dates; + if ( not $new_hard_due_date && $due_date_days ) { + while ( my $checkout = $checkouts->next ) { + my $due_date = dt_from_string( $checkout->date_due ); + push @new_due_dates, $due_date->add( days => $due_date_days ); + } + } + $template->param( + checkouts => $checkouts, + new_hard_due_date => $new_hard_due_date + ? dt_from_string($new_hard_due_date) + : undef, + due_date_days => $due_date_days, + new_due_dates => \@new_due_dates, + view => 'list', + ); +} +elsif ( $op eq 'modify' ) { + + # We want to modify selected checkouts! + my @issue_ids = $input->multi_param('issue_id'); + my $new_hard_due_date = $input->param('new_hard_due_date'); + my $due_date_days = $input->param('due_date_days'); + + $new_hard_due_date &&= dt_from_string($new_hard_due_date); + my $checkouts = + Koha::Checkouts->search( { issue_id => { -in => \@issue_ids } } ); + while ( my $checkout = $checkouts->next ) { + if ($new_hard_due_date) { + $checkout->date_due($new_hard_due_date)->store; + } + else { + my $dt = dt_from_string( $checkout->date_due ) + ->add( days => $due_date_days ); + $checkout->date_due($dt)->store; + } + } + + $template->param( + view => 'report', + checkouts => $checkouts, + ); +} + +output_html_with_http_headers $input, $cookie, $template->output; -- 2.20.1