Bug 30128: DBrev 21.12.00.017
[koha.git] / tools / batchMod.pl
1 #!/usr/bin/perl
2
3
4 # Copyright 2000-2002 Katipo Communications
5 #
6 # This file is part of Koha.
7 #
8 # Koha is free software; you can redistribute it and/or modify it
9 # under the terms of the GNU General Public License as published by
10 # the Free Software Foundation; either version 3 of the License, or
11 # (at your option) any later version.
12 #
13 # Koha is distributed in the hope that it will be useful, but
14 # WITHOUT ANY WARRANTY; without even the implied warranty of
15 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 # GNU General Public License for more details.
17 #
18 # You should have received a copy of the GNU General Public License
19 # along with Koha; if not, see <http://www.gnu.org/licenses>.
20
21 use CGI qw ( -utf8 );
22 use Modern::Perl;
23 use Try::Tiny qw( catch try );
24
25 use C4::Auth qw( get_template_and_user haspermission );
26 use C4::Output qw( output_html_with_http_headers );
27 use C4::Circulation qw( barcodedecode );
28 use C4::Context;
29 use MARC::File::XML;
30 use List::MoreUtils qw( uniq );
31 use Encode qw( encode_utf8 );
32
33 use Koha::Database;
34 use Koha::DateUtils qw( dt_from_string );
35 use Koha::Exception;
36 use Koha::Biblios;
37 use Koha::Items;
38 use Koha::Patrons;
39 use Koha::Item::Attributes;
40 use Koha::BackgroundJob::BatchDeleteItem;
41 use Koha::BackgroundJob::BatchUpdateItem;
42 use Koha::UI::Form::Builder::Item;
43 use Koha::UI::Table::Builder::Items;
44
45 my $input = CGI->new;
46 my $dbh = C4::Context->dbh;
47 my $error        = $input->param('error');
48 my @itemnumbers  = $input->multi_param('itemnumber');
49 my $biblionumber = $input->param('biblionumber');
50 my $op           = $input->param('op');
51 my $del          = $input->param('del');
52 my $del_records  = $input->param('del_records');
53 my $src          = $input->param('src');
54 my $use_default_values = $input->param('use_default_values');
55 my $exclude_from_local_holds_priority = $input->param('exclude_from_local_holds_priority');
56
57 my $template_name;
58 my $template_flag;
59 if (!defined $op) {
60     $template_name = "tools/batchMod.tt";
61     $template_flag = { tools => '*' };
62     $op = q{};
63 } else {
64     $template_name = ($del) ? "tools/batchMod-del.tt" : "tools/batchMod-edit.tt";
65     $template_flag = ($del) ? { tools => 'items_batchdel' }   : { tools => 'items_batchmod' };
66 }
67
68 my ($template, $loggedinuser, $cookie)
69     = get_template_and_user({template_name => $template_name,
70                  query => $input,
71                  type => "intranet",
72                  flagsrequired => $template_flag,
73                  });
74
75 $template->param( searchid => scalar $input->param('searchid'), );
76
77 # Does the user have a restricted item edition permission?
78 my $uid = $loggedinuser ? Koha::Patrons->find( $loggedinuser )->userid : undef;
79 my $restrictededition = $uid ? haspermission($uid,  {'tools' => 'items_batchmod_restricted'}) : undef;
80 # In case user is a superlibrarian, edition is not restricted
81 $restrictededition = 0 if ($restrictededition != 0 && C4::Context->IsSuperLibrarian());
82
83 my $nextop="";
84 my $display_items;
85
86 my %cookies = parse CGI::Cookie($cookie);
87 my $sessionID = $cookies{'CGISESSID'}->value;
88
89 my @messages;
90
91 if ( $op eq "action" ) {
92
93     if ($del) {
94         try {
95             my $params = {
96                 record_ids     => \@itemnumbers,
97                 delete_biblios => $del_records,
98             };
99             my $job_id =
100               Koha::BackgroundJob::BatchDeleteItem->new->enqueue($params);
101             $nextop = 'enqueued';
102             $template->param( job_id => $job_id, );
103         }
104         catch {
105             warn $_;
106             push @messages,
107               {
108                 type  => 'error',
109                 code  => 'cannot_enqueue_job',
110                 error => $_,
111               };
112             $template->param( view => 'errors' );
113         };
114     }
115
116     else {    # modification
117
118         my @item_columns = Koha::Items->columns;
119
120         my $new_item_data;
121         my ( $columns_with_regex );
122         my @subfields_to_blank = $input->multi_param('disable_input');
123         my @more_subfields = $input->multi_param("items.more_subfields_xml");
124         for my $item_column (@item_columns) {
125             my @attributes       = ($item_column);
126             my $cgi_param_prefix = 'items.';
127             if ( $item_column eq 'more_subfields_xml' ) {
128                 @attributes       = ();
129                 $cgi_param_prefix = 'items.more_subfields_xml_';
130                 for my $subfield (@more_subfields) {
131                     push @attributes, $subfield;
132                 }
133             }
134
135             for my $attr (@attributes) {
136
137                 my $cgi_var_name = $cgi_param_prefix
138                   . encode_utf8($attr)
139                   ;  # We need to deal correctly with encoding on subfield codes
140
141                 if ( grep { $cgi_var_name eq $_ } @subfields_to_blank ) {
142                     # Empty this column
143                     $new_item_data->{$attr} = undef;
144                 }
145                 elsif ( my $regex_search =
146                     $input->param( $cgi_var_name . '_regex_search' ) )
147                 {
148                     $columns_with_regex->{$attr} = {
149                         search => $regex_search,
150                         replace =>
151                           $input->param( $cgi_var_name . '_regex_replace' ),
152                         modifiers =>
153                           $input->param( $cgi_var_name . '_regex_modifiers' )
154                     };
155                 }
156                 else {
157                     my @v =
158                       grep { $_ ne "" } uniq $input->multi_param($cgi_var_name);
159
160                     next unless @v;
161
162                     $new_item_data->{$attr} = join '|', @v;
163                 }
164             }
165         }
166
167         my $params = {
168             record_ids                        => \@itemnumbers,
169             regex_mod                         => $columns_with_regex,
170             new_values                        => $new_item_data,
171             exclude_from_local_holds_priority => (
172                 defined $exclude_from_local_holds_priority
173                   && $exclude_from_local_holds_priority ne ""
174               )
175             ? $exclude_from_local_holds_priority
176             : undef,
177
178         };
179         try {
180             my $job_id =
181               Koha::BackgroundJob::BatchUpdateItem->new->enqueue($params);
182             $nextop = 'enqueued';
183             $template->param( job_id => $job_id, );
184         }
185         catch {
186             push @messages,
187               {
188                 type  => 'error',
189                 code  => 'cannot_enqueue_job',
190                 error => $_,
191               };
192             $template->param( view => 'errors' );
193         };
194     }
195
196 }
197
198 $template->param(
199     messages => \@messages,
200 );
201 #
202 #-------------------------------------------------------------------------------
203 # build screen with existing items. and "new" one
204 #-------------------------------------------------------------------------------
205
206 if ($op eq "show"){
207     my $filefh = $input->upload('uploadfile');
208     my $filecontent = $input->param('filecontent');
209     my ( @notfoundbarcodes, @notfounditemnumbers);
210
211     my $split_chars = C4::Context->preference('BarcodeSeparators');
212     if ($filefh){
213         binmode $filefh, ':encoding(UTF-8)';
214         my @contentlist;
215         while (my $content=<$filefh>){
216             $content =~ s/[\r\n]*$//;
217             push @contentlist, $content if $content;
218         }
219
220         if ($filecontent eq 'barcode_file') {
221             @contentlist = grep /\S/, ( map { split /[$split_chars]/ } @contentlist );
222             @contentlist = uniq @contentlist;
223             # Note: adding lc for case insensitivity
224             my %itemdata = map { lc($_->{barcode}) => $_->{itemnumber} } @{ Koha::Items->search({ barcode => \@contentlist }, { columns => [ 'itemnumber', 'barcode' ] } )->unblessed };
225             @itemnumbers = map { exists $itemdata{lc $_} ? $itemdata{lc $_} : () } @contentlist;
226             @notfoundbarcodes = grep { !exists $itemdata{lc $_} } @contentlist;
227         }
228         elsif ( $filecontent eq 'itemid_file') {
229             @contentlist = uniq @contentlist;
230             my %itemdata = map { $_->{itemnumber} => 1 } @{ Koha::Items->search({ itemnumber => \@contentlist }, { columns => [ 'itemnumber' ] } )->unblessed };
231             @itemnumbers = grep { exists $itemdata{$_} } @contentlist;
232             @notfounditemnumbers = grep { !exists $itemdata{$_} } @contentlist;
233         }
234     } else {
235         if (defined $biblionumber && !@itemnumbers){
236             my $biblio = Koha::Biblios->find($biblionumber);
237             @itemnumbers = $biblio ? $biblio->items->get_column('itemnumber') : ();
238         }
239         if ( my $list = $input->param('barcodelist') ) {
240             my @barcodelist = grep /\S/, ( split /[$split_chars]/, $list );
241             @barcodelist = uniq @barcodelist;
242
243             @barcodelist = map { barcodedecode( $_ ) } @barcodelist;
244
245             # Note: adding lc for case insensitivity
246             my %itemdata = map { lc($_->{barcode}) => $_->{itemnumber} } @{ Koha::Items->search({ barcode => \@barcodelist }, { columns => [ 'itemnumber', 'barcode' ] } )->unblessed };
247             @itemnumbers = map { exists $itemdata{lc $_} ? $itemdata{lc $_} : () } @barcodelist;
248             @notfoundbarcodes = grep { !exists $itemdata{lc $_} } @barcodelist;
249         }
250     }
251
252     # Flag to tell the template there are valid results, hidden or not
253     if(scalar(@itemnumbers) > 0){ $template->param("itemresults" => 1); }
254     # Only display the items if there are no more than pref MaxItemsToProcessForBatchMod or MaxItemsToDisplayForBatchDel
255     my $max_display_items = $del
256         ? C4::Context->preference("MaxItemsToDisplayForBatchDel")
257         : C4::Context->preference("MaxItemsToDisplayForBatchMod");
258     $template->param("too_many_items_process" => scalar(@itemnumbers)) if !$del && scalar(@itemnumbers) > C4::Context->preference("MaxItemsToProcessForBatchMod");
259     if (scalar(@itemnumbers) <= ( $max_display_items // 1000 ) ) {
260         $display_items = 1;
261     } else {
262         $template->param("too_many_items_display" => scalar(@itemnumbers));
263         # Even if we do not display the items, we need the itemnumbers
264         $template->param(itemnumbers_array => \@itemnumbers);
265     }
266
267     # now, build the item form for entering a new item
268
269     # Getting list of subfields to keep when restricted batchmod edit is enabled
270     my @subfields_to_allow = $restrictededition ? split ' ', C4::Context->preference('SubfieldsToAllowForRestrictedBatchmod') : ();
271
272     my $subfields = Koha::UI::Form::Builder::Item->new->edit_form(
273         {
274             restricted_editition => $restrictededition,
275             (
276                 @subfields_to_allow
277                 ? ( subfields_to_allow => \@subfields_to_allow )
278                 : ()
279             ),
280             ignore_not_allowed_subfields => 1,
281             kohafields_to_ignore         => ['items.barcode'],
282             prefill_with_default_values => $use_default_values,
283             default_branches_empty      => 1,
284         }
285     );
286
287     # what's the next op ? it's what we are not in : an add if we're editing, otherwise, and edit.
288     $template->param(
289         subfields           => $subfields,
290         notfoundbarcodes    => \@notfoundbarcodes,
291         notfounditemnumbers => \@notfounditemnumbers
292     );
293     $nextop="action"
294 } # -- End action="show"
295
296 if ( $display_items ) {
297     my $items_table =
298       Koha::UI::Table::Builder::Items->new( { itemnumbers => \@itemnumbers } )
299       ->build_table;
300     $template->param(
301         items        => $items_table->{items},
302         item_header_loop => $items_table->{headers},
303     );
304 }
305
306 $template->param(
307     op  => $nextop,
308     del => $del,
309     ( $op ? ( $op => 1 ) : () ),
310     src          => $src,
311     biblionumber => $biblionumber,
312 );
313
314 output_html_with_http_headers $input, $cookie, $template->output;