Bug 25873: Ignore malformed data for Elasticsearch integer fields
[koha.git] / tools / stockrotation.pl
blobda23c83cc7464c04e041059b5cccb527d91bb2b1
1 #!/usr/bin/perl
3 # Copyright 2016 PTFS-Europe Ltd
5 # This file is part of Koha.
7 # Koha is free software; you can redistribute it and/or modify it
8 # under the terms of the GNU General Public License as published by
9 # the Free Software Foundation; either version 3 of the License, or
10 # (at your option) any later version.
12 # Koha is distributed in the hope that it will be useful, but
13 # WITHOUT ANY WARRANTY; without even the implied warranty of
14 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 # GNU General Public License for more details.
17 # You should have received a copy of the GNU General Public License
18 # along with Koha; if not, see <http://www.gnu.org/licenses>.
20 =head1 stockrotation.pl
22 Script to handle stockrotation. Including rotas, their associated stages
23 and items
25 =cut
27 use Modern::Perl;
28 use CGI;
30 use C4::Auth;
31 use C4::Context;
32 use C4::Output;
34 use Koha::Libraries;
35 use Koha::StockRotationRotas;
36 use Koha::StockRotationItems;
37 use Koha::StockRotationStages;
38 use Koha::Item;
39 use Koha::Util::StockRotation qw(:ALL);
41 my $input = new CGI;
43 unless (C4::Context->preference('StockRotation')) {
44 # redirect to Intranet home if self-check is not enabled
45 print $input->redirect("/cgi-bin/koha/mainpage.pl");
46 exit;
49 my ( $template, $loggedinuser, $cookie ) = get_template_and_user(
51 template_name => 'tools/stockrotation.tt',
52 query => $input,
53 type => 'intranet',
54 flagsrequired => {
55 tools => '*',
56 stockrotation => '*',
58 authnotrequired => 0
62 # Grab all passed data
63 # 'our' since Plack changes the scoping
64 # of 'my'
65 our %params = $input->Vars();
67 my $op = $params{op};
69 if (!defined $op) {
71 # No operation is supplied, we're just displaying the list of rotas
72 my $rotas = Koha::StockRotationRotas->search(
73 undef,
75 order_by => { -asc => 'title' }
77 )->as_list;
79 $template->param(
80 existing_rotas => $rotas,
81 no_op_set => 1
84 } elsif ($op eq 'create_edit_rota') {
86 # Edit an existing rota or define a new one
87 my $rota_id = $params{rota_id};
89 my $rota = {};
91 if (!defined $rota_id) {
93 # No ID supplied, we're creating a new rota
94 # Create a shell rota hashref
95 $rota = {
96 cyclical => 1
99 } else {
101 # ID supplied, we're editing an existing rota
102 $rota = Koha::StockRotationRotas->find($rota_id);
106 $template->param(
107 rota => $rota,
108 op => $op
111 } elsif ($op eq 'toggle_rota') {
113 # Find and update the active status of the rota
114 my $rota = Koha::StockRotationRotas->find($params{rota_id});
116 my $new_active = ($rota->active == 1) ? 0 : 1;
118 $rota->active($new_active)->store;
120 # Return to rotas page
121 print $input->redirect('stockrotation.pl');
123 } elsif ($op eq 'process_rota') {
125 # Get a hashref of the submitted rota data
126 my $rota = get_rota_from_form();
128 if (!process_rota($rota)) {
130 # The submitted rota was invalid
131 $template->param(
132 error => 'invalid_form',
133 rota => $rota,
134 op => 'create_edit_rota'
137 } else {
139 # All was well, return to the rotas list
140 print $input->redirect('stockrotation.pl');
144 } elsif ($op eq 'manage_stages') {
146 my $rota = Koha::StockRotationRotas->find($params{rota_id});
148 $template->param(
149 rota => $rota,
150 branches => get_branches(),
151 existing_stages => get_stages($rota),
152 rota_id => $params{rota_id},
153 op => $op
156 } elsif ($op eq 'create_edit_stage') {
158 # Edit an existing stage or define a new one
159 my $stage_id = $params{stage_id};
161 my $rota_id = $params{rota_id};
163 if (!defined $stage_id) {
165 # No ID supplied, we're creating a new stage
166 $template->param(
167 branches => get_branches(),
168 stage => {},
169 rota_id => $rota_id,
170 op => $op
173 } else {
175 # ID supplied, we're editing an existing stage
176 my $stage = Koha::StockRotationStages->find($stage_id);
178 $template->param(
179 branches => get_branches(),
180 stage => $stage,
181 rota_id => $stage->rota->rota_id,
182 op => $op
187 } elsif ($op eq 'confirm_remove_from_rota') {
189 # Get the stage we're deleting
190 $template->param(
191 op => $op,
192 rota_id => $params{rota_id},
193 stage_id => $params{stage_id},
194 item_id => $params{item_id}
197 } elsif ($op eq 'confirm_delete_stage') {
199 # Get the stage we're deleting
200 my $stage = Koha::StockRotationStages->find($params{stage_id});
202 $template->param(
203 op => $op,
204 stage => $stage
207 } elsif ($op eq 'delete_stage') {
209 # Get the stage we're deleting
210 my $stage = Koha::StockRotationStages->find($params{stage_id});
212 # Get the ID of the rota with which this stage is associated
213 # (so we can return to the "Manage stages" page after deletion)
214 my $rota_id = $stage->rota->rota_id;
216 $stage->delete;
218 # Return to the stages list
219 print $input->redirect("?op=manage_stages&rota_id=$rota_id");
221 } elsif ($op eq 'process_stage') {
223 # Get a hashref of the submitted stage data
224 my $stage = get_stage_from_form();
226 # The rota we're managing
227 my $rota_id = $params{rota_id};
229 if (!process_stage($stage, $rota_id)) {
231 # The submitted stage was invalid
232 # Get all branches
233 my $branches = get_branches();
235 $template->param(
236 error => 'invalid_form',
237 all_branches => $branches,
238 stage => $stage,
239 rota_id => $rota_id,
240 op => 'create_edit_stage'
243 } else {
245 # All was well, return to the stages list
246 print $input->redirect("?op=manage_stages&rota_id=$rota_id");
250 } elsif ($op eq 'manage_items') {
252 my $rota = Koha::StockRotationRotas->find($params{rota_id});
254 # Get all items on this rota, for each prefetch their
255 # stage and biblio objects
256 my $items = Koha::StockRotationItems->search(
257 { 'stage.rota_id' => $params{rota_id} },
259 prefetch => {
260 stage => {
261 'stockrotationitems' => {
262 'itemnumber' => 'biblionumber'
269 $template->param(
270 rota_id => $params{rota_id},
271 error => $params{error},
272 items => $items,
273 branches => get_branches(),
274 stages => get_stages($rota),
275 rota => $rota,
276 op => $op
279 } elsif ($op eq 'move_to_next_stage') {
281 move_to_next_stage($params{item_id}, $params{stage_id});
283 # Return to the items list
284 print $input->redirect("?op=manage_items&rota_id=" . $params{rota_id});
286 } elsif ($op eq 'toggle_in_demand') {
288 # Toggle the item's in_demand
289 toggle_indemand($params{item_id}, $params{stage_id});
291 # Return to the items list
292 print $input->redirect("?op=manage_items&rota_id=".$params{rota_id});
294 } elsif ($op eq 'remove_item_from_stage') {
296 # Remove the item from the stage
297 remove_from_stage($params{item_id}, $params{stage_id});
299 # Return to the items list
300 print $input->redirect("?op=manage_items&rota_id=".$params{rota_id});
302 } elsif ($op eq 'add_items_to_rota') {
304 # The item's barcode,
305 # which we may or may not have been passed
306 my $barcode = $params{barcode};
308 # The rota we're adding the item to
309 my $rota_id = $params{rota_id};
311 # The uploaded file filehandle,
312 # which we may or may not have been passed
313 my $barcode_file = $input->upload("barcodefile");
315 # We need to create an array of one or more barcodes to
316 # insert
317 my @barcodes = ();
319 # If the barcode input box was populated, use it
320 push @barcodes, $barcode if $barcode;
322 # Only parse the uploaded file if necessary
323 if ($barcode_file) {
325 # Call binmode on the filehandle as we want to set a
326 # UTF-8 layer on it
327 binmode($barcode_file, ":encoding(UTF-8)");
328 # Parse the file into an array of barcodes
329 while (my $barcode = <$barcode_file>) {
330 $barcode =~ s/\r/\n/g;
331 $barcode =~ s/\n+/\n/g;
332 my @data = split(/\n/, $barcode);
333 push @barcodes, @data;
338 # A hashref to hold the status of each barcode
339 my $barcode_status = {
340 ok => [],
341 on_other => [],
342 on_this => [],
343 not_found => []
346 # If we have something to work with, do it
347 get_barcodes_status($rota_id, \@barcodes, $barcode_status) if (@barcodes);
349 # Now we know the status of each barcode, add those that
350 # need it
351 if (scalar @{$barcode_status->{ok}} > 0) {
353 add_items_to_rota($rota_id, $barcode_status->{ok});
356 # If we were only passed one barcode and it was successfully
357 # added, redirect back to ourselves, we don't want to display
358 # a report, redirect also if we were passed no barcodes
359 if (
360 scalar @barcodes == 0 ||
361 (scalar @barcodes == 1 && scalar @{$barcode_status->{ok}} == 1)
364 print $input->redirect("?op=manage_items&rota_id=$rota_id");
366 } else {
368 # Report on the outcome
369 $template->param(
370 barcode_status => $barcode_status,
371 rota_id => $rota_id,
372 op => $op
377 } elsif ($op eq 'move_items_to_rota') {
379 # The barcodes of the items we're moving
380 my @move = $input->param('move_item');
382 foreach my $item(@move) {
384 # The item we're moving
385 my $item = Koha::Items->find($item);
387 # Move it to the new rota
388 $item->add_to_rota($params{rota_id});
392 # Return to the items list
393 print $input->redirect("?op=manage_items&rota_id=".$params{rota_id});
397 output_html_with_http_headers $input, $cookie, $template->output;
399 sub get_rota_from_form {
401 return {
402 id => $params{id},
403 title => $params{title},
404 cyclical => $params{cyclical},
405 description => $params{description}
409 sub get_stage_from_form {
411 return {
412 stage_id => $params{stage_id},
413 branchcode => $params{branchcode},
414 duration => $params{duration}
418 sub process_rota {
420 my $sub_rota = shift;
422 # Fields we require
423 my @required = ('title','cyclical');
425 # Count of the number of required fields we have
426 my $valid = 0;
428 # Ensure we have everything we require
429 foreach my $req(@required) {
431 if (exists $sub_rota->{$req}) {
433 chomp(my $value = $sub_rota->{$req});
434 if (length $value > 0) {
435 $valid++;
442 # If we don't have everything we need
443 return 0 if $valid != scalar @required;
445 # Passed validation
446 # Find the rota we're updating
447 my $rota = Koha::StockRotationRotas->find($sub_rota->{id});
449 if ($rota) {
451 $rota->title(
452 $sub_rota->{title}
453 )->cyclical(
454 $sub_rota->{cyclical}
455 )->description(
456 $sub_rota->{description}
457 )->store;
459 } else {
461 $rota = Koha::StockRotationRota->new({
462 title => $sub_rota->{title},
463 cyclical => $sub_rota->{cyclical},
464 active => 0,
465 description => $sub_rota->{description}
466 })->store;
470 return 1;
473 sub process_stage {
475 my ($sub_stage, $rota_id) = @_;
477 # Fields we require
478 my @required = ('branchcode','duration');
480 # Count of the number of required fields we have
481 my $valid = 0;
483 # Ensure we have everything we require
484 foreach my $req(@required) {
486 if (exists $sub_stage->{$req}) {
488 chomp(my $value = $sub_stage->{$req});
489 if (length $value > 0) {
490 $valid++;
497 # If we don't have everything we need
498 return 0 if $valid != scalar @required;
500 # Passed validation
501 # Find the stage we're updating
502 my $stage = Koha::StockRotationStages->find($sub_stage->{stage_id});
504 if ($stage) {
506 # Updating an existing stage
507 $stage->branchcode_id(
508 $sub_stage->{branchcode}
509 )->duration(
510 $sub_stage->{duration}
511 )->store;
513 } else {
515 # Creating a new stage
516 $stage = Koha::StockRotationStage->new({
517 branchcode_id => $sub_stage->{branchcode},
518 rota_id => $rota_id,
519 duration => $sub_stage->{duration}
520 })->store;
524 return 1;
527 =head1 AUTHOR
529 Andrew Isherwood <andrew.isherwood@ptfs-europe.com>
531 =cut