Bug 26265: (QA follow-up) Remove g option from regex, add few dirs
[koha-equinox.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 under the
8 # terms of the GNU General Public License as published by the Free Software
9 # Foundation; either version 3 of the License, or (at your option) any later
10 # version.
11 #
12 # Koha is distributed in the hope that it will be useful, but WITHOUT ANY
13 # WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
14 # A PARTICULAR PURPOSE.  See the GNU General Public License for more details.
15 #
16 # You should have received a copy of the GNU General Public License along
17 # with Koha; if not, write to the Free Software Foundation, Inc.,
18 # 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
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 = new CGI;
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         authnotrequired => 0
59     }
60 );
61
62 # Grab all passed data
63 # 'our' since Plack changes the scoping
64 # of 'my'
65 our %params = $input->Vars();
66
67 my $op = $params{op};
68
69 if (!defined $op) {
70
71     # No operation is supplied, we're just displaying the list of rotas
72     my $rotas = Koha::StockRotationRotas->search(
73         undef,
74         {
75             order_by => { -asc => 'title' }
76         }
77     )->as_list;
78
79     $template->param(
80         existing_rotas => $rotas,
81         no_op_set      => 1
82     );
83
84 } elsif ($op eq 'create_edit_rota') {
85
86     # Edit an existing rota or define a new one
87     my $rota_id = $params{rota_id};
88
89     my $rota = {};
90
91     if (!defined $rota_id) {
92
93         # No ID supplied, we're creating a new rota
94         # Create a shell rota hashref
95         $rota = {
96             cyclical => 1
97         };
98
99     } else {
100
101         # ID supplied, we're editing an existing rota
102         $rota = Koha::StockRotationRotas->find($rota_id);
103
104     }
105
106     $template->param(
107         rota => $rota,
108         op   => $op
109     );
110
111 } elsif ($op eq 'toggle_rota') {
112
113     # Find and update the active status of the rota
114     my $rota = Koha::StockRotationRotas->find($params{rota_id});
115
116     my $new_active = ($rota->active == 1) ? 0 : 1;
117
118     $rota->active($new_active)->store;
119
120     # Return to rotas page
121     print $input->redirect('stockrotation.pl');
122
123 } elsif ($op eq 'process_rota') {
124
125     # Get a hashref of the submitted rota data
126     my $rota = get_rota_from_form();
127
128     if (!process_rota($rota)) {
129
130         # The submitted rota was invalid
131         $template->param(
132             error => 'invalid_form',
133             rota => $rota,
134             op   => 'create_edit_rota'
135         );
136
137     } else {
138
139         # All was well, return to the rotas list
140         print $input->redirect('stockrotation.pl');
141
142     }
143
144 } elsif ($op eq 'manage_stages') {
145
146     my $rota = Koha::StockRotationRotas->find($params{rota_id});
147
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
154     );
155
156 } elsif ($op eq 'create_edit_stage') {
157
158     # Edit an existing stage or define a new one
159     my $stage_id = $params{stage_id};
160
161     my $rota_id = $params{rota_id};
162
163     if (!defined $stage_id) {
164
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
171         );
172
173     } else {
174
175         # ID supplied, we're editing an existing stage
176         my $stage = Koha::StockRotationStages->find($stage_id);
177
178         $template->param(
179             branches => get_branches(),
180             stage    => $stage,
181             rota_id  => $stage->rota->rota_id,
182             op       => $op
183         );
184
185     }
186
187 } elsif ($op eq 'confirm_remove_from_rota') {
188
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}
195     );
196
197 } elsif ($op eq 'confirm_delete_stage') {
198
199     # Get the stage we're deleting
200     my $stage = Koha::StockRotationStages->find($params{stage_id});
201
202     $template->param(
203         op    => $op,
204         stage => $stage
205     );
206
207 } elsif ($op eq 'delete_stage') {
208
209     # Get the stage we're deleting
210     my $stage = Koha::StockRotationStages->find($params{stage_id});
211
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;
215
216     $stage->delete;
217
218     # Return to the stages list
219     print $input->redirect("?op=manage_stages&rota_id=$rota_id");
220
221 } elsif ($op eq 'process_stage') {
222
223     # Get a hashref of the submitted stage data
224     my $stage = get_stage_from_form();
225
226     # The rota we're managing
227     my $rota_id = $params{rota_id};
228
229     if (!process_stage($stage, $rota_id)) {
230
231         # The submitted stage was invalid
232         # Get all branches
233         my $branches = get_branches();
234
235         $template->param(
236             error        => 'invalid_form',
237             all_branches => $branches,
238             stage        => $stage,
239             rota_id      => $rota_id,
240             op           => 'create_edit_stage'
241         );
242
243     } else {
244
245         # All was well, return to the stages list
246         print $input->redirect("?op=manage_stages&rota_id=$rota_id");
247
248     }
249
250 } elsif ($op eq 'manage_items') {
251
252     my $rota = Koha::StockRotationRotas->find($params{rota_id});
253
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} },
258         {
259             prefetch => {
260                 stage => {
261                     'stockrotationitems' => {
262                         'itemnumber' => 'biblionumber'
263                     }
264                 }
265             }
266         }
267     );
268
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
277     );
278
279 } elsif ($op eq 'move_to_next_stage') {
280
281     move_to_next_stage($params{item_id}, $params{stage_id});
282
283     # Return to the items list
284     print $input->redirect("?op=manage_items&rota_id=" . $params{rota_id});
285
286 } elsif ($op eq 'toggle_in_demand') {
287
288     # Toggle the item's in_demand
289     toggle_indemand($params{item_id}, $params{stage_id});
290
291     # Return to the items list
292     print $input->redirect("?op=manage_items&rota_id=".$params{rota_id});
293
294 } elsif ($op eq 'remove_item_from_stage') {
295
296     # Remove the item from the stage
297     remove_from_stage($params{item_id}, $params{stage_id});
298
299     # Return to the items list
300     print $input->redirect("?op=manage_items&rota_id=".$params{rota_id});
301
302 } elsif ($op eq 'add_items_to_rota') {
303
304     # The item's barcode,
305     # which we may or may not have been passed
306     my $barcode = $params{barcode};
307
308     # The rota we're adding the item to
309     my $rota_id = $params{rota_id};
310
311     # The uploaded file filehandle,
312     # which we may or may not have been passed
313     my $barcode_file = $input->upload("barcodefile");
314
315     # We need to create an array of one or more barcodes to
316     # insert
317     my @barcodes = ();
318
319     # If the barcode input box was populated, use it
320     push @barcodes, $barcode if $barcode;
321
322     # Only parse the uploaded file if necessary
323     if ($barcode_file) {
324
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;
334         }
335
336     }
337
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 => []
344     };
345
346     # If we have something to work with, do it
347     get_barcodes_status($rota_id, \@barcodes, $barcode_status) if (@barcodes);
348
349     # Now we know the status of each barcode, add those that
350     # need it
351     if (scalar @{$barcode_status->{ok}} > 0) {
352
353         add_items_to_rota($rota_id, $barcode_status->{ok});
354
355     }
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)
362     ) {
363
364         print $input->redirect("?op=manage_items&rota_id=$rota_id");
365
366     } else {
367
368         # Report on the outcome
369         $template->param(
370             barcode_status => $barcode_status,
371             rota_id        => $rota_id,
372             op             => $op
373         );
374
375     }
376
377 } elsif ($op eq 'move_items_to_rota') {
378
379     # The barcodes of the items we're moving
380     my @move = $input->param('move_item');
381
382     foreach my $item(@move) {
383
384         # The item we're moving
385         my $item = Koha::Items->find($item);
386
387         # Move it to the new rota
388         $item->add_to_rota($params{rota_id});
389
390     }
391
392     # Return to the items list
393     print $input->redirect("?op=manage_items&rota_id=".$params{rota_id});
394
395 }
396
397 output_html_with_http_headers $input, $cookie, $template->output;
398
399 sub get_rota_from_form {
400
401     return {
402         id          => $params{id},
403         title       => $params{title},
404         cyclical    => $params{cyclical},
405         description => $params{description}
406     };
407 }
408
409 sub get_stage_from_form {
410
411     return {
412         stage_id    => $params{stage_id},
413         branchcode  => $params{branchcode},
414         duration    => $params{duration}
415     };
416 }
417
418 sub process_rota {
419
420     my $sub_rota = shift;
421
422     # Fields we require
423     my @required = ('title','cyclical');
424
425     # Count of the number of required fields we have
426     my $valid = 0;
427
428     # Ensure we have everything we require
429     foreach my $req(@required) {
430
431         if (exists $sub_rota->{$req}) {
432
433             chomp(my $value = $sub_rota->{$req});
434             if (length $value > 0) {
435                 $valid++;
436             }
437
438         }
439
440     }
441
442     # If we don't have everything we need
443     return 0 if $valid != scalar @required;
444
445     # Passed validation
446     # Find the rota we're updating
447     my $rota = Koha::StockRotationRotas->find($sub_rota->{id});
448
449     if ($rota) {
450
451         $rota->title(
452             $sub_rota->{title}
453         )->cyclical(
454             $sub_rota->{cyclical}
455         )->description(
456             $sub_rota->{description}
457         )->store;
458
459     } else {
460
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;
467
468     }
469
470     return 1;
471 }
472
473 sub process_stage {
474
475     my ($sub_stage, $rota_id) = @_;
476
477     # Fields we require
478     my @required = ('branchcode','duration');
479
480     # Count of the number of required fields we have
481     my $valid = 0;
482
483     # Ensure we have everything we require
484     foreach my $req(@required) {
485
486         if (exists $sub_stage->{$req}) {
487
488             chomp(my $value = $sub_stage->{$req});
489             if (length $value > 0) {
490                 $valid++;
491             }
492
493         }
494
495     }
496
497     # If we don't have everything we need
498     return 0 if $valid != scalar @required;
499
500     # Passed validation
501     # Find the stage we're updating
502     my $stage = Koha::StockRotationStages->find($sub_stage->{stage_id});
503
504     if ($stage) {
505
506         # Updating an existing stage
507         $stage->branchcode_id(
508             $sub_stage->{branchcode}
509         )->duration(
510             $sub_stage->{duration}
511         )->store;
512
513     } else {
514
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;
521
522     }
523
524     return 1;
525 }
526
527 =head1 AUTHOR
528
529 Andrew Isherwood <andrew.isherwood@ptfs-europe.com>
530
531 =cut