From 92c34de79f57e0d81afc3f0778f502d91c9dd0d5 Mon Sep 17 00:00:00 2001 From: Emmi Takkinen Date: Wed, 28 Feb 2024 10:39:35 +0200 Subject: [PATCH] Bug 36135: Add new permission batch_modify_holds Sponsored-by: Koha-Suomi Oy --- .../data/mysql/atomicupdate/bug_36135.pl | 16 + .../data/mysql/mandatory/userpermissions.sql | 1 + .../prog/en/includes/permissions.inc | 5 + .../prog/en/includes/tools-menu.inc | 3 + .../en/modules/tools/batch_modify_holds.tt | 313 ++++++++++++++++++ .../prog/en/modules/tools/tools-home.tt | 7 +- tools/batch_modify_holds.pl | 177 ++++++++++ 7 files changed, 521 insertions(+), 1 deletion(-) create mode 100755 installer/data/mysql/atomicupdate/bug_36135.pl create mode 100644 koha-tmpl/intranet-tmpl/prog/en/modules/tools/batch_modify_holds.tt create mode 100755 tools/batch_modify_holds.pl diff --git a/installer/data/mysql/atomicupdate/bug_36135.pl b/installer/data/mysql/atomicupdate/bug_36135.pl new file mode 100755 index 00000000000..40aa318e1ec --- /dev/null +++ b/installer/data/mysql/atomicupdate/bug_36135.pl @@ -0,0 +1,16 @@ +use Modern::Perl; + +return { + bug_number => "36135", + description => "Add new permission batch_modify_holds", + up => sub { + my ($args) = @_; + my ( $dbh, $out ) = @$args{qw(dbh out)}; + + $dbh->do( + q{INSERT IGNORE permissions (module_bit, code, description) VALUES (13, 'batch_modify_holds', 'Perform batch modification of holds')} + ); + + say $out "Added new permission 'batch_modify_holds'"; + }, +}; diff --git a/installer/data/mysql/mandatory/userpermissions.sql b/installer/data/mysql/mandatory/userpermissions.sql index d5121c91fd2..768aa9cee18 100644 --- a/installer/data/mysql/mandatory/userpermissions.sql +++ b/installer/data/mysql/mandatory/userpermissions.sql @@ -112,6 +112,7 @@ INSERT INTO permissions (module_bit, code, description) VALUES (13, 'edit_patrons', 'Perform batch modification of patrons'), (13, 'delete_anonymize_patrons', 'Delete old borrowers and anonymize circulation history (deletes borrower checkout history)'), (13, 'batch_extend_due_dates', 'Perform batch extend due dates'), + (13, 'batch_modify_holds', 'Perform batch modification of holds'), (13, 'batch_upload_patron_images', 'Upload patron images in a batch or one at a time'), (13, 'schedule_tasks', 'Schedule tasks to run'), (13, 'items_batchmod', 'Perform batch modification of items'), diff --git a/koha-tmpl/intranet-tmpl/prog/en/includes/permissions.inc b/koha-tmpl/intranet-tmpl/prog/en/includes/permissions.inc index a0e17a2d418..e42fcf8e67d 100644 --- a/koha-tmpl/intranet-tmpl/prog/en/includes/permissions.inc +++ b/koha-tmpl/intranet-tmpl/prog/en/includes/permissions.inc @@ -368,6 +368,11 @@ [%- CASE 'batch_extend_due_dates' -%] Perform batch extend due dates ([% name | html %]) + [%- CASE 'batch_modify_holds' -%] + + Perform batch modification of holds + + ([% name | html %]) Define days when the library is closed ([% name | html %]) diff --git a/koha-tmpl/intranet-tmpl/prog/en/includes/tools-menu.inc b/koha-tmpl/intranet-tmpl/prog/en/includes/tools-menu.inc index 28cd71f6b7c..15bdd2fc970 100644 --- a/koha-tmpl/intranet-tmpl/prog/en/includes/tools-menu.inc +++ b/koha-tmpl/intranet-tmpl/prog/en/includes/tools-menu.inc @@ -39,6 +39,9 @@ [% IF ( CAN_user_tools_batch_extend_due_dates ) %]
  • Batch extend due dates
  • [% END %] + [% IF ( CAN_user_tools_batch_modify_holds ) %] +
  • Batch modify holds
  • + [% END %] [% IF ( CAN_user_tools_moderate_tags ) %]
  • Tags
  • [% END %] 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 new file mode 100644 index 00000000000..7705c36beb1 --- /dev/null +++ b/koha-tmpl/intranet-tmpl/prog/en/modules/tools/batch_modify_holds.tt @@ -0,0 +1,313 @@ +[% USE raw %] +[% USE Asset %] +[% SET footerjs = 1 %] +[% USE Branches %] +[% USE Categories %] +[% USE KohaDates %] +[% USE ItemTypes %] +[% PROCESS 'html_helpers.inc' %] + +[% INCLUDE 'doc-head-open.inc' %] +Batch modify holds › Tools › Koha +[% INCLUDE 'doc-head-close.inc' %] +[% Asset.css("css/humanmsg.css") | $raw %] + + + + [% WRAPPER 'header.inc' %] + [% INCLUDE 'cat-search.inc' %] +[% END %] + + [% WRAPPER 'sub-header.inc' %] + + [% END %] + +
    +
    +
    +
    + +

    Batch modify holds

    + + [% IF view == 'form' %] +
    +
    + Hold search criteria: + NOTE! Column patron_expiration_date is used as a main filter for expiration date field. Otherwise column expirationdate is used. +
      +
    1. + +
    2. + + +
    3. +
    4. + + +
    5. + +
    6. + + +
    7. +
    8. + + +
    9. +
    10. + + +
    11. +
    12. + +
    13. + + +
    14. +
    15. + + +
    16. + +
    17. + + +
    18. +
    +
    +
    + + + Cancel +
    +
    + [% ELSIF view == 'list' %] +
    + [% IF holds.count %] +
    + + + + + + + + + + + + + + + + [% FOREACH hold IN holds %] + + + + + + + + + + + [% END %] + +
     Expiration datePatron expiration dateStatusHold pickup librarySuspendedSuspended untilNote
    [% hold.expirationdate | $KohaDates %][% hold.patron_expiration_date | $KohaDates %] + [% IF hold.found == "T" %] + In transit + [% ELSIF hold.found == "P" %] + In processing + [% ELSIF hold.found == "W" %] + Waiting + [% ELSE %] + No status + [% END %] + [% Branches.GetName( hold.branchcode ) | html %][% IF hold.suspend %]Yes[% ELSE %]No[% END %][% hold.suspend_until | $KohaDates %][% hold.reservenotes | html %]
    +
    +

    Modify holds

    + + + + + + + + + + + + + + + + + + + +
    New expiration dateNew pickup librarySuspend holdsSuspend untilNew reserve note
    + + + + + + + + + +
    +
    + + + Cancel +
    +
    + [% ELSE %] +
    + No holds were found for the selected filters. +
    + Return + [% END %] + [% ELSIF view == 'report' %] +
    + Holds have been modified! +
    +
    + + + + + + + + + + + + + [% FOREACH hold IN updated_holds %] + + + + + + + + [% END %] + +
    Expiration dateHold pickup librarySuspendedSuspended untilNote
    [% hold.expirationdate | $KohaDates %][% Branches.GetName( hold.branchcode ) | html %][% IF hold.suspend == 0 %]No[% ELSE %]Yes[% END %][% hold.suspend_until | $KohaDates %][% hold.reservenotes | html %]
    +
    + Return to batch hold modification + [% END %] +
    +
    + + +
    + +
    +
    + +[% MACRO jsinclude BLOCK %] + [% Asset.js("js/tools-menu.js") | $raw %] + [% INCLUDE 'calendar.inc' %] + [% INCLUDE 'datatables.inc' %] + [% Asset.js("lib/jquery/plugins/humanmsg.js") | $raw %] + +[% END %] + +[% INCLUDE 'intranet-bottom.inc' %] \ No newline at end of file diff --git a/koha-tmpl/intranet-tmpl/prog/en/modules/tools/tools-home.tt b/koha-tmpl/intranet-tmpl/prog/en/modules/tools/tools-home.tt index 3aa18b39636..f7839ed8938 100644 --- a/koha-tmpl/intranet-tmpl/prog/en/modules/tools/tools-home.tt +++ b/koha-tmpl/intranet-tmpl/prog/en/modules/tools/tools-home.tt @@ -29,7 +29,7 @@

    Tools

    - [% IF ( CAN_user_tools_manage_patron_lists || CAN_user_clubs || CAN_user_tools_moderate_comments || CAN_user_tools_import_patrons || CAN_user_tools_edit_notices || CAN_user_tools_edit_notice_status_triggers || CAN_user_tools_label_creator || CAN_user_tools_delete_anonymize_patrons || CAN_user_tools_edit_patrons || CAN_user_tools_batch_extend_due_dates || CAN_user_tools_moderate_tags || CAN_user_tools_rotating_collections || ( CAN_user_tools_batch_upload_patron_images && Koha.Preference('patronimages') ) ) %] + [% IF ( CAN_user_tools_manage_patron_lists || CAN_user_clubs || CAN_user_tools_moderate_comments || CAN_user_tools_import_patrons || CAN_user_tools_edit_notices || CAN_user_tools_edit_notice_status_triggers || CAN_user_tools_label_creator || CAN_user_tools_delete_anonymize_patrons || CAN_user_tools_edit_patrons || CAN_user_tools_batch_extend_due_dates || CAN_user_tools_batch_modify_holds || CAN_user_tools_moderate_tags || CAN_user_tools_rotating_collections || ( CAN_user_tools_batch_upload_patron_images && Koha.Preference('patronimages') ) ) %]

    Patrons and circulation

    [% END %]
    @@ -88,6 +88,11 @@
    Modify the due date checkouts in batch
    [% END %] + [% IF ( CAN_user_tools_batch_modify_holds ) %] +
    Batch modify holds
    +
    Modify holds in batch
    + [% END %] + [% IF ( CAN_user_tools_moderate_tags ) %]
    Tags diff --git a/tools/batch_modify_holds.pl b/tools/batch_modify_holds.pl new file mode 100755 index 00000000000..af37d5500ed --- /dev/null +++ b/tools/batch_modify_holds.pl @@ -0,0 +1,177 @@ +#!/usr/bin/perl + +# This file is part of Koha. +# +# Copyright 2024 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::DateUtils qw( dt_from_string ); +use Koha::Holds; + +my $input = CGI->new; +my $op = $input->param('op') // q|form|; + +my ( $template, $loggedinuser, $cookie ) = get_template_and_user( + { + template_name => 'tools/batch_modify_holds.tt', + query => $input, + type => "intranet", + flagsrequired => { tools => 'batch_modify_holds.tt' }, + } +); + +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); + + $template->param( + holds => $holds, + view => 'list', + ); +} + +if ( $op eq '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'); + + @hold_ids = $input->multi_param('hold_id'); + + my $holds_to_update = Koha::Holds->search( { reserve_id => { -in => \@hold_ids } } ); + + while ( my $hold = $holds_to_update->next ) { + + if ($new_expiration_date) { + $hold->expirationdate($new_expiration_date)->store; + } + + unless ( $hold->branchcode eq $new_pickup_loc ) { + $hold->branchcode($new_pickup_loc)->store; + } + + if ( $new_suspend_status && !$hold->is_found ) { + if ( $new_suspend_status eq "suspend" ) { + $hold->suspend(1)->store; + if ($new_suspend_date) { + $hold->suspend_until($new_suspend_date)->store; + } else { + $hold->suspend_until(undef)->store; + } + } else { + $hold->suspend(0)->store; + $hold->suspend_until(undef)->store; + } + } + + if ($new_reserve_note) { + $hold->reservenotes($new_reserve_note)->store; + } + } + + $template->param( + updated_holds => $holds_to_update, + view => 'report', + ); + +} + +output_html_with_http_headers $input, $cookie, $template->output; -- 2.39.5