Bug 26581: Only reindex records once per checkin
[koha.git] / tools / stockrotation.pl
blob18891fa3db6baa9a4b9034211433f39597819e69
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 = CGI->new;
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 => '*',
61 # Grab all passed data
62 # 'our' since Plack changes the scoping
63 # of 'my'
64 our %params = $input->Vars();
66 my $op = $params{op};
68 if (!defined $op) {
70 # No operation is supplied, we're just displaying the list of rotas
71 my $rotas = Koha::StockRotationRotas->search(
72 undef,
74 order_by => { -asc => 'title' }
76 )->as_list;
78 $template->param(
79 existing_rotas => $rotas,
80 no_op_set => 1
83 } elsif ($op eq 'create_edit_rota') {
85 # Edit an existing rota or define a new one
86 my $rota_id = $params{rota_id};
88 my $rota = {};
90 if (!defined $rota_id) {
92 # No ID supplied, we're creating a new rota
93 # Create a shell rota hashref
94 $rota = {
95 cyclical => 1
98 } else {
100 # ID supplied, we're editing an existing rota
101 $rota = Koha::StockRotationRotas->find($rota_id);
105 $template->param(
106 rota => $rota,
107 op => $op
110 } elsif ($op eq 'toggle_rota') {
112 # Find and update the active status of the rota
113 my $rota = Koha::StockRotationRotas->find($params{rota_id});
115 my $new_active = ($rota->active == 1) ? 0 : 1;
117 $rota->active($new_active)->store;
119 # Return to rotas page
120 print $input->redirect('stockrotation.pl');
122 } elsif ($op eq 'process_rota') {
124 # Get a hashref of the submitted rota data
125 my $rota = get_rota_from_form();
127 if (!process_rota($rota)) {
129 # The submitted rota was invalid
130 $template->param(
131 error => 'invalid_form',
132 rota => $rota,
133 op => 'create_edit_rota'
136 } else {
138 # All was well, return to the rotas list
139 print $input->redirect('stockrotation.pl');
143 } elsif ($op eq 'manage_stages') {
145 my $rota = Koha::StockRotationRotas->find($params{rota_id});
147 $template->param(
148 rota => $rota,
149 branches => get_branches(),
150 existing_stages => get_stages($rota),
151 rota_id => $params{rota_id},
152 op => $op
155 } elsif ($op eq 'create_edit_stage') {
157 # Edit an existing stage or define a new one
158 my $stage_id = $params{stage_id};
160 my $rota_id = $params{rota_id};
162 if (!defined $stage_id) {
164 # No ID supplied, we're creating a new stage
165 $template->param(
166 branches => get_branches(),
167 stage => {},
168 rota_id => $rota_id,
169 op => $op
172 } else {
174 # ID supplied, we're editing an existing stage
175 my $stage = Koha::StockRotationStages->find($stage_id);
177 $template->param(
178 branches => get_branches(),
179 stage => $stage,
180 rota_id => $stage->rota->rota_id,
181 op => $op
186 } elsif ($op eq 'confirm_remove_from_rota') {
188 # Get the stage we're deleting
189 $template->param(
190 op => $op,
191 rota_id => $params{rota_id},
192 stage_id => $params{stage_id},
193 item_id => $params{item_id}
196 } elsif ($op eq 'confirm_delete_stage') {
198 # Get the stage we're deleting
199 my $stage = Koha::StockRotationStages->find($params{stage_id});
201 $template->param(
202 op => $op,
203 stage => $stage
206 } elsif ($op eq 'delete_stage') {
208 # Get the stage we're deleting
209 my $stage = Koha::StockRotationStages->find($params{stage_id});
211 # Get the ID of the rota with which this stage is associated
212 # (so we can return to the "Manage stages" page after deletion)
213 my $rota_id = $stage->rota->rota_id;
215 $stage->delete;
217 # Return to the stages list
218 print $input->redirect("?op=manage_stages&rota_id=$rota_id");
220 } elsif ($op eq 'process_stage') {
222 # Get a hashref of the submitted stage data
223 my $stage = get_stage_from_form();
225 # The rota we're managing
226 my $rota_id = $params{rota_id};
228 if (!process_stage($stage, $rota_id)) {
230 # The submitted stage was invalid
231 # Get all branches
232 my $branches = get_branches();
234 $template->param(
235 error => 'invalid_form',
236 all_branches => $branches,
237 stage => $stage,
238 rota_id => $rota_id,
239 op => 'create_edit_stage'
242 } else {
244 # All was well, return to the stages list
245 print $input->redirect("?op=manage_stages&rota_id=$rota_id");
249 } elsif ($op eq 'manage_items') {
251 my $rota = Koha::StockRotationRotas->find($params{rota_id});
253 # Get all items on this rota, for each prefetch their
254 # stage and biblio objects
255 my $items = Koha::StockRotationItems->search(
256 { 'stage.rota_id' => $params{rota_id} },
258 prefetch => {
259 stage => {
260 'stockrotationitems' => {
261 'itemnumber' => 'biblionumber'
268 $template->param(
269 rota_id => $params{rota_id},
270 error => $params{error},
271 items => $items,
272 branches => get_branches(),
273 stages => get_stages($rota),
274 rota => $rota,
275 op => $op
278 } elsif ($op eq 'move_to_next_stage') {
280 move_to_next_stage($params{item_id}, $params{stage_id});
282 # Return to the items list
283 print $input->redirect("?op=manage_items&rota_id=" . $params{rota_id});
285 } elsif ($op eq 'toggle_in_demand') {
287 # Toggle the item's in_demand
288 toggle_indemand($params{item_id}, $params{stage_id});
290 # Return to the items list
291 print $input->redirect("?op=manage_items&rota_id=".$params{rota_id});
293 } elsif ($op eq 'remove_item_from_stage') {
295 # Remove the item from the stage
296 remove_from_stage($params{item_id}, $params{stage_id});
298 # Return to the items list
299 print $input->redirect("?op=manage_items&rota_id=".$params{rota_id});
301 } elsif ($op eq 'add_items_to_rota') {
303 # The item's barcode,
304 # which we may or may not have been passed
305 my $barcode = $params{barcode};
307 # The rota we're adding the item to
308 my $rota_id = $params{rota_id};
310 # The uploaded file filehandle,
311 # which we may or may not have been passed
312 my $barcode_file = $input->upload("barcodefile");
314 # We need to create an array of one or more barcodes to
315 # insert
316 my @barcodes = ();
318 # If the barcode input box was populated, use it
319 push @barcodes, $barcode if $barcode;
321 # Only parse the uploaded file if necessary
322 if ($barcode_file) {
324 # Call binmode on the filehandle as we want to set a
325 # UTF-8 layer on it
326 binmode($barcode_file, ":encoding(UTF-8)");
327 # Parse the file into an array of barcodes
328 while (my $barcode = <$barcode_file>) {
329 $barcode =~ s/\r/\n/g;
330 $barcode =~ s/\n+/\n/g;
331 my @data = split(/\n/, $barcode);
332 push @barcodes, @data;
337 # A hashref to hold the status of each barcode
338 my $barcode_status = {
339 ok => [],
340 on_other => [],
341 on_this => [],
342 not_found => []
345 # If we have something to work with, do it
346 get_barcodes_status($rota_id, \@barcodes, $barcode_status) if (@barcodes);
348 # Now we know the status of each barcode, add those that
349 # need it
350 if (scalar @{$barcode_status->{ok}} > 0) {
352 add_items_to_rota($rota_id, $barcode_status->{ok});
355 # If we were only passed one barcode and it was successfully
356 # added, redirect back to ourselves, we don't want to display
357 # a report, redirect also if we were passed no barcodes
358 if (
359 scalar @barcodes == 0 ||
360 (scalar @barcodes == 1 && scalar @{$barcode_status->{ok}} == 1)
363 print $input->redirect("?op=manage_items&rota_id=$rota_id");
365 } else {
367 # Report on the outcome
368 $template->param(
369 barcode_status => $barcode_status,
370 rota_id => $rota_id,
371 op => $op
376 } elsif ($op eq 'move_items_to_rota') {
378 # The barcodes of the items we're moving
379 my @move = $input->param('move_item');
381 foreach my $item(@move) {
383 # The item we're moving
384 my $item = Koha::Items->find($item);
386 # Move it to the new rota
387 $item->add_to_rota($params{rota_id});
391 # Return to the items list
392 print $input->redirect("?op=manage_items&rota_id=".$params{rota_id});
396 output_html_with_http_headers $input, $cookie, $template->output;
398 sub get_rota_from_form {
400 return {
401 id => $params{id},
402 title => $params{title},
403 cyclical => $params{cyclical},
404 description => $params{description}
408 sub get_stage_from_form {
410 return {
411 stage_id => $params{stage_id},
412 branchcode => $params{branchcode},
413 duration => $params{duration}
417 sub process_rota {
419 my $sub_rota = shift;
421 # Fields we require
422 my @required = ('title','cyclical');
424 # Count of the number of required fields we have
425 my $valid = 0;
427 # Ensure we have everything we require
428 foreach my $req(@required) {
430 if (exists $sub_rota->{$req}) {
432 chomp(my $value = $sub_rota->{$req});
433 if (length $value > 0) {
434 $valid++;
441 # If we don't have everything we need
442 return 0 if $valid != scalar @required;
444 # Passed validation
445 # Find the rota we're updating
446 my $rota = Koha::StockRotationRotas->find($sub_rota->{id});
448 if ($rota) {
450 $rota->title(
451 $sub_rota->{title}
452 )->cyclical(
453 $sub_rota->{cyclical}
454 )->description(
455 $sub_rota->{description}
456 )->store;
458 } else {
460 $rota = Koha::StockRotationRota->new({
461 title => $sub_rota->{title},
462 cyclical => $sub_rota->{cyclical},
463 active => 0,
464 description => $sub_rota->{description}
465 })->store;
469 return 1;
472 sub process_stage {
474 my ($sub_stage, $rota_id) = @_;
476 # Fields we require
477 my @required = ('branchcode','duration');
479 # Count of the number of required fields we have
480 my $valid = 0;
482 # Ensure we have everything we require
483 foreach my $req(@required) {
485 if (exists $sub_stage->{$req}) {
487 chomp(my $value = $sub_stage->{$req});
488 if (length $value > 0) {
489 $valid++;
496 # If we don't have everything we need
497 return 0 if $valid != scalar @required;
499 # Passed validation
500 # Find the stage we're updating
501 my $stage = Koha::StockRotationStages->find($sub_stage->{stage_id});
503 if ($stage) {
505 # Updating an existing stage
506 $stage->branchcode_id(
507 $sub_stage->{branchcode}
508 )->duration(
509 $sub_stage->{duration}
510 )->store;
512 } else {
514 # Creating a new stage
515 $stage = Koha::StockRotationStage->new({
516 branchcode_id => $sub_stage->{branchcode},
517 rota_id => $rota_id,
518 duration => $sub_stage->{duration}
519 })->store;
523 return 1;
526 =head1 AUTHOR
528 Andrew Isherwood <andrew.isherwood@ptfs-europe.com>
530 =cut