--- /dev/null
+[% USE raw %]
+[% USE Asset %]
+[% SET footerjs = 1 %]
+[% USE Branches %]
+[% USE Categories %]
+[% USE KohaDates %]
+[% USE ItemTypes %]
+[% PROCESS 'html_helpers.inc' %]
+
+[% INCLUDE 'doc-head-open.inc' %]
+<title>Koha › Tools › Batch extend due dates</title>
+[% INCLUDE 'doc-head-close.inc' %]
+[% Asset.css("css/humanmsg.css") | $raw %]
+</head>
+
+<body id="tools_batch_extend_due_dates" class="tools">
+ [% INCLUDE 'header.inc' %]
+ [% INCLUDE 'cat-search.inc' %]
+
+ <div id="breadcrumbs">
+ <a href="/cgi-bin/koha/mainpage.pl">Home</a> ›
+ <a href="/cgi-bin/koha/tools/tools-home.pl">Tools</a> ›
+ <a href="/cgi-bin/koha/tools/batch_extend_due_dates.pl">Batch extend due dates</a>
+ </div>
+
+ <div class="main container-fluid">
+ <div class="row">
+ <div class="col-sm-10 col-sm-push-2">
+ <main>
+
+ <h1>Batch extend due dates</h1>
+
+ [% IF ( messages ) %]
+ <div class="dialog message">
+
+ [% FOREACH message IN messages %]
+ [% IF message.type == 'success' %]
+ <div><i class="fa fa-check success"></i>
+ [% ELSIF message.type == 'warning' %]
+ <div><i class="fa fa-warning warn"></i>
+ [% ELSIF message.type == 'error' %]
+ <div><i class="fa fa-exclamation error"></i>
+ [% END %]
+ [% IF message.error %]
+ (The error was: [% message.error | html %]. See the Koha logfile for more information).
+ [% END %]
+ </div>
+ [% END %]
+
+ </div> <!-- .dialog.message -->
+ [% END %]
+
+ [% IF view == 'form' %]
+ <form method="post" enctype="multipart/form-data" action="/cgi-bin/koha/tools/batch_extend_due_dates.pl" id="extend_due_dates_form">
+ <fieldset class="rows">
+ <legend>Checkout criteria:</legend>
+ <ol>
+ <li>
+ <label for="categorycodes">Patrons' categories: </label>
+ [% SET categories = Categories.all() %]
+ <select id="categorycodes" name="categorycodes" multiple="multiple">
+ [% FOREACH cat IN categories %]
+ <option value="[% cat.categorycode | html %]">[% cat.description | html %]</option>
+ [% END %]
+ </select>
+ </li>
+
+ <li>
+ <label for="branchcodes">Libraries: </label>
+ <select name="branchcodes" id="branchcodes" multiple="multiple">
+ [% PROCESS options_for_libraries libraries => Branches.all() %]
+ </select>
+ </li>
+
+ <li>
+ <label for="from_due_date">Due date from: </label>
+ <input type="text" size="10" id="from" name="from_due_date" class="datepickerfrom" />
+ </li>
+
+ <li>
+ <label for="to_due_date">Due date to:</label>
+ <input type="text" size="10" id="to" name="to_due_date" class="datepickerto" />
+ </li>
+ </ol>
+ </fieldset>
+ <fieldset class="rows">
+ <legend>New due date:</legend>
+ <ol>
+ <li>
+ <label for="new_hard_due_date">Hard due date: </label>
+ <input type="text" size="10" id="new_hard_due_date" name="new_hard_due_date" class="datepicker" />
+ </li>
+
+ <li>
+ <label for="due_date_days">Or add number of days:</label>
+ <input type="text" size="10" id="due_date_days" name="due_date_days"/>
+ </li>
+ </ol>
+ </fieldset>
+ <fieldset class="action">
+ <input type="hidden" name="op" value="list" />
+ <input type="submit" value="Continue" class="button" />
+ <a class="cancel" href="/cgi-bin/koha/tools/tools-home.pl">Cancel</a>
+ </fieldset>
+ </form> <!-- /#extend_due_dates_form -->
+ [% ELSIF view == 'list' %]
+ [% IF checkouts.count %]
+ <form action="/cgi-bin/koha/tools/batch_extend_due_dates.pl" method="post" id="process">
+ <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>
+ <table id="checkouts">
+ <thead>
+ <tr>
+ <th> </th>
+ <th>Due date</th>
+ <th>Title</th>
+ <th>Item type</th>
+ <th>Home library</th>
+ <th>Checked out on</th>
+ <th>Checked out from</th>
+ <th>New due date</th>
+ </tr>
+ </thead>
+ <tbody>
+ [% FOR checkout IN checkouts %]
+ <tr>
+ <td><input type="checkbox" name="issue_id" value="[% checkout.issue_id | html %]" /></td>
+ <td>[% checkout.date_due | $KohaDates as_due_date => 1 %]</td>
+ <td><a href="/cgi-bin/koha/catalogue/detail.pl?biblionumber=[% checkout.item.biblio.biblionumber | uri %]">[% checkout.item.biblio.title | html %]</a></td>
+ <td>[% ItemTypes.GetDescription( checkout.item.effective_itemtype ) | html %]</td>
+ <td>[% checkout.item.home_branch.branchname | html %]</td>
+ <td>[% checkout.issuedate | $KohaDates %]</td>
+ <td>[% Branches.GetName( checkout.branchcode ) | html %]</td>
+ <td>
+ [% IF new_hard_due_date %]
+ [% new_hard_due_date | $KohaDates %]
+ [% ELSE %]
+ [% new_due_dates.shift | $KohaDates %]
+ [% END %]
+ </td>
+ </tr>
+ [% END %]
+ </tbody>
+ </table> <!-- /#checkouts -->
+ <div class="note"><i class="fa fa-exclamation"></i> Reminder: this action will modify all selected checkouts!</div>
+ <fieldset class="action">
+ <input type="hidden" name="op" value="modify" />
+ <input type="hidden" name="new_hard_due_date" value="[% new_hard_due_date | $KohaDates %]" />
+ <input type="hidden" name="due_date_days" value="[% due_date_days | html %]" />
+ <input type="submit" value="Modify selected checkouts" class="button" />
+ <a class="cancel" href="/cgi-bin/koha/tools/batch_extend_due_dates.pl">Cancel</a>
+ </fieldset>
+ </form> <!-- /#process -->
+ [% ELSE %]
+ <div class="dialog message">
+ No checkouts for the selected filters.
+ </div>
+ [% END %]
+ [% ELSIF view == 'report' %]
+ <div class="dialog message">
+ Due dates have been modified!
+ </div>
+
+ <table id="checkouts_result">
+ <thead>
+ <tr>
+ <th>Due date</th>
+ <th>Title</th>
+ <th>Item type</th>
+ <th>Home library</th>
+ <th>Checked out on</th>
+ <th>Checked out from</th>
+ </tr>
+ </thead>
+ <tbody>
+ [% FOR checkout IN checkouts %]
+ <tr>
+ <td>[% checkout.date_due | $KohaDates as_due_date => 1 %]</td>
+ <td><a href="/cgi-bin/koha/catalogue/detail.pl?biblionumber=[% checkout.item.biblio.biblionumber | uri %]">[% checkout.item.biblio.title | html %]</a></td>
+ <td>[% ItemTypes.GetDescription( checkout.item.effective_itemtype ) | html %]</td>
+ <td>[% checkout.item.home_branch.branchname | html %]</td>
+ <td>[% checkout.issuedate | $KohaDates %]</td>
+ <td>[% Branches.GetName( checkout.branchcode ) | html %]</td>
+ </tr>
+ [% END %]
+ </tbody>
+ </table> <!-- /#checkouts_result -->
+ [% END %]
+ </main>
+ </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 -->
+ </div> <!-- /.row -->
+
+[% 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 %]
+ <script>
+ $(document).ready(function() {
+
+ $("#selectall").click(function(e) {
+ e.preventDefault();
+ $("#checkouts").checkCheckboxes();
+ });
+ $("#clearall").click(function(e) {
+ e.preventDefault();
+ $("#checkouts").unCheckCheckboxes();
+ });
+ $("#selectall").click();
+
+ $("table#checkouts").dataTable($.extend(true, {}, dataTablesDefaults, {
+ "aoColumnDefs": [
+ { "aTargets": [0, 3], "bSortable": false, "bSearchable": false },
+ { "aTargets": [1], "sType": "num-html" }
+ ],
+ "sDom": 't',
+ "aaSorting": [],
+ "bPaginate": false
+ }));
+
+ $("table#checkouts_result").dataTable($.extend(true, {}, dataTablesDefaults, {
+ "aoColumnDefs": [
+ { "aTargets": [0, 3], "bSortable": false, "bSearchable": false },
+ { "aTargets": [1], "sType": "num-html" }
+ ],
+ "sDom": 't',
+ "aaSorting": [],
+ "bPaginate": false
+ }));
+
+ $("#extend_due_dates_form").on('submit', function(e) {
+ var new_hard_due_date = $("#new_hard_due_date").val();
+ var due_date_days = $("#due_date_days").val();
+ if (new_hard_due_date && due_date_days ) {
+ e.preventDefault();
+ alert(_("You must fill only one of the two due date options"));
+ return false;
+ } else if ( !new_hard_due_date && !due_date_days ) {
+ e.preventDefault();
+ alert(_("You must fill at least one of the two due date options"));
+ return false;
+ }
+
+ return true;
+ });
+
+ $("#process").on('submit', function(e) {
+ if ($("input[type=checkbox][name='issue_id']:checked").length == 0) {
+ e.preventDefault();
+ alert(_("Please select at least one checkout to process"));
+ return false;
+ }
+ return true;
+ });
+
+ });
+ </script>
+[% END %]
+
+[% INCLUDE 'intranet-bottom.inc' %]
--- /dev/null
+#!/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
+# <http://www.gnu.org/licenses>
+
+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;