Bug 27610: (follow-up) Hide <h1> on opac-main.tt
[koha.git] / tools / stockrotation.pl
1 #!/usr/bin/perl
2
3 # Copyright 2016 PTFS-Europe Ltd
4 #
5 # This file is part of Koha.
6 #
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.
11 #
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.
16 #
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>.
19
20 =head1 stockrotation.pl
21
22  Script to handle stockrotation. Including rotas, their associated stages
23  and items
24
25 =cut
26
27 use Modern::Perl;
28 use CGI;
29
30 use C4::Auth;
31 use C4::Context;
32 use C4::Output;
33
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);
40
41 my $input = CGI->new;
42
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;
47 }
48
49 my ( $template, $loggedinuser, $cookie ) = get_template_and_user(
50     {
51         template_name   => 'tools/stockrotation.tt',
52         query           => $input,
53         type            => 'intranet',
54         flagsrequired   => {
55             tools => '*',
56             stockrotation => '*',
57         },
58     }
59 );
60
61 # Grab all passed data
62 # 'our' since Plack changes the scoping
63 # of 'my'
64 our %params = $input->Vars();
65
66 my $op = $params{op};
67
68 if (!defined $op) {
69
70     # No operation is supplied, we're just displaying the list of rotas
71     my $rotas = Koha::StockRotationRotas->search(
72         undef,
73         {
74             order_by => { -asc => 'title' }
75         }
76     )->as_list;
77
78     $template->param(
79         existing_rotas => $rotas,
80         no_op_set      => 1
81     );
82
83 } elsif ($op eq 'create_edit_rota') {
84
85     # Edit an existing rota or define a new one
86     my $rota_id = $params{rota_id};
87
88     my $rota = {};
89
90     if (!defined $rota_id) {
91
92         # No ID supplied, we're creating a new rota
93         # Create a shell rota hashref
94         $rota = {
95             cyclical => 1
96         };
97
98     } else {
99
100         # ID supplied, we're editing an existing rota
101         $rota = Koha::StockRotationRotas->find($rota_id);
102
103     }
104
105     $template->param(
106         rota => $rota,
107         op   => $op
108     );
109
110 } elsif ($op eq 'toggle_rota') {
111
112     # Find and update the active status of the rota
113     my $rota = Koha::StockRotationRotas->find($params{rota_id});
114
115     my $new_active = ($rota->active == 1) ? 0 : 1;
116
117     $rota->active($new_active)->store;
118
119     # Return to rotas page
120     print $input->redirect('stockrotation.pl');
121
122 } elsif ($op eq 'process_rota') {
123
124     # Get a hashref of the submitted rota data
125     my $rota = get_rota_from_form();
126
127     if (!process_rota($rota)) {
128
129         # The submitted rota was invalid
130         $template->param(
131             error => 'invalid_form',
132             rota => $rota,
133             op   => 'create_edit_rota'
134         );
135
136     } else {
137
138         # All was well, return to the rotas list
139         print $input->redirect('stockrotation.pl');
140
141     }
142
143 } elsif ($op eq 'manage_stages') {
144
145     my $rota = Koha::StockRotationRotas->find($params{rota_id});
146
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
153     );
154
155 } elsif ($op eq 'create_edit_stage') {
156
157     # Edit an existing stage or define a new one
158     my $stage_id = $params{stage_id};
159
160     my $rota_id = $params{rota_id};
161
162     if (!defined $stage_id) {
163
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
170         );
171
172     } else {
173
174         # ID supplied, we're editing an existing stage
175         my $stage = Koha::StockRotationStages->find($stage_id);
176
177         $template->param(
178             branches => get_branches(),
179             stage    => $stage,
180             rota_id  => $stage->rota->rota_id,
181             op       => $op
182         );
183
184     }
185
186 } elsif ($op eq 'confirm_remove_from_rota') {
187
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}
194     );
195
196 } elsif ($op eq 'confirm_delete_stage') {
197
198     # Get the stage we're deleting
199     my $stage = Koha::StockRotationStages->find($params{stage_id});
200
201     $template->param(
202         op    => $op,
203         stage => $stage
204     );
205
206 } elsif ($op eq 'delete_stage') {
207
208     # Get the stage we're deleting
209     my $stage = Koha::StockRotationStages->find($params{stage_id});
210
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;
214
215     $stage->delete;
216
217     # Return to the stages list
218     print $input->redirect("?op=manage_stages&rota_id=$rota_id");
219
220 } elsif ($op eq 'process_stage') {
221
222     # Get a hashref of the submitted stage data
223     my $stage = get_stage_from_form();
224
225     # The rota we're managing
226     my $rota_id = $params{rota_id};
227
228     if (!process_stage($stage, $rota_id)) {
229
230         # The submitted stage was invalid
231         # Get all branches
232         my $branches = get_branches();
233
234         $template->param(
235             error        => 'invalid_form',
236             all_branches => $branches,
237             stage        => $stage,
238             rota_id      => $rota_id,
239             op           => 'create_edit_stage'
240         );
241
242     } else {
243
244         # All was well, return to the stages list
245         print $input->redirect("?op=manage_stages&rota_id=$rota_id");
246
247     }
248
249 } elsif ($op eq 'manage_items') {
250
251     my $rota = Koha::StockRotationRotas->find($params{rota_id});
252
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} },
257         {
258             prefetch => {
259                 stage => {
260                     'stockrotationitems' => {
261                         'itemnumber' => 'biblionumber'
262                     }
263                 }
264             }
265         }
266     );
267
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
276     );
277
278 } elsif ($op eq 'move_to_next_stage') {
279
280     move_to_next_stage($params{item_id}, $params{stage_id});
281
282     # Return to the items list
283     print $input->redirect("?op=manage_items&rota_id=" . $params{rota_id});
284
285 } elsif ($op eq 'toggle_in_demand') {
286
287     # Toggle the item's in_demand
288     toggle_indemand($params{item_id}, $params{stage_id});
289
290     # Return to the items list
291     print $input->redirect("?op=manage_items&rota_id=".$params{rota_id});
292
293 } elsif ($op eq 'remove_item_from_stage') {
294
295     # Remove the item from the stage
296     remove_from_stage($params{item_id}, $params{stage_id});
297
298     # Return to the items list
299     print $input->redirect("?op=manage_items&rota_id=".$params{rota_id});
300
301 } elsif ($op eq 'add_items_to_rota') {
302
303     # The item's barcode,
304     # which we may or may not have been passed
305     my $barcode = $params{barcode};
306
307     # The rota we're adding the item to
308     my $rota_id = $params{rota_id};
309
310     # The uploaded file filehandle,
311     # which we may or may not have been passed
312     my $barcode_file = $input->upload("barcodefile");
313
314     # We need to create an array of one or more barcodes to
315     # insert
316     my @barcodes = ();
317
318     # If the barcode input box was populated, use it
319     push @barcodes, $barcode if $barcode;
320
321     # Only parse the uploaded file if necessary
322     if ($barcode_file) {
323
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;
333         }
334
335     }
336
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 => []
343     };
344
345     # If we have something to work with, do it
346     get_barcodes_status($rota_id, \@barcodes, $barcode_status) if (@barcodes);
347
348     # Now we know the status of each barcode, add those that
349     # need it
350     if (scalar @{$barcode_status->{ok}} > 0) {
351
352         add_items_to_rota($rota_id, $barcode_status->{ok});
353
354     }
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)
361     ) {
362
363         print $input->redirect("?op=manage_items&rota_id=$rota_id");
364
365     } else {
366
367         # Report on the outcome
368         $template->param(
369             barcode_status => $barcode_status,
370             rota_id        => $rota_id,
371             op             => $op
372         );
373
374     }
375
376 } elsif ($op eq 'move_items_to_rota') {
377
378     # The barcodes of the items we're moving
379     my @move = $input->param('move_item');
380
381     foreach my $item(@move) {
382
383         # The item we're moving
384         my $item = Koha::Items->find($item);
385
386         # Move it to the new rota
387         $item->add_to_rota($params{rota_id});
388
389     }
390
391     # Return to the items list
392     print $input->redirect("?op=manage_items&rota_id=".$params{rota_id});
393
394 }
395
396 output_html_with_http_headers $input, $cookie, $template->output;
397
398 sub get_rota_from_form {
399
400     return {
401         id          => $params{id},
402         title       => $params{title},
403         cyclical    => $params{cyclical},
404         description => $params{description}
405     };
406 }
407
408 sub get_stage_from_form {
409
410     return {
411         stage_id    => $params{stage_id},
412         branchcode  => $params{branchcode},
413         duration    => $params{duration}
414     };
415 }
416
417 sub process_rota {
418
419     my $sub_rota = shift;
420
421     # Fields we require
422     my @required = ('title','cyclical');
423
424     # Count of the number of required fields we have
425     my $valid = 0;
426
427     # Ensure we have everything we require
428     foreach my $req(@required) {
429
430         if (exists $sub_rota->{$req}) {
431
432             chomp(my $value = $sub_rota->{$req});
433             if (length $value > 0) {
434                 $valid++;
435             }
436
437         }
438
439     }
440
441     # If we don't have everything we need
442     return 0 if $valid != scalar @required;
443
444     # Passed validation
445     # Find the rota we're updating
446     my $rota = Koha::StockRotationRotas->find($sub_rota->{id});
447
448     if ($rota) {
449
450         $rota->title(
451             $sub_rota->{title}
452         )->cyclical(
453             $sub_rota->{cyclical}
454         )->description(
455             $sub_rota->{description}
456         )->store;
457
458     } else {
459
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;
466
467     }
468
469     return 1;
470 }
471
472 sub process_stage {
473
474     my ($sub_stage, $rota_id) = @_;
475
476     # Fields we require
477     my @required = ('branchcode','duration');
478
479     # Count of the number of required fields we have
480     my $valid = 0;
481
482     # Ensure we have everything we require
483     foreach my $req(@required) {
484
485         if (exists $sub_stage->{$req}) {
486
487             chomp(my $value = $sub_stage->{$req});
488             if (length $value > 0) {
489                 $valid++;
490             }
491
492         }
493
494     }
495
496     # If we don't have everything we need
497     return 0 if $valid != scalar @required;
498
499     # Passed validation
500     # Find the stage we're updating
501     my $stage = Koha::StockRotationStages->find($sub_stage->{stage_id});
502
503     if ($stage) {
504
505         # Updating an existing stage
506         $stage->branchcode_id(
507             $sub_stage->{branchcode}
508         )->duration(
509             $sub_stage->{duration}
510         )->store;
511
512     } else {
513
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;
520
521     }
522
523     return 1;
524 }
525
526 =head1 AUTHOR
527
528 Andrew Isherwood <andrew.isherwood@ptfs-europe.com>
529
530 =cut