Bug 33963: Remove C4::BackgroundJob
[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 qw( get_template_and_user );
31 use C4::Context;
32 use C4::Output qw( output_html_with_http_headers );
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( get_branches get_stages move_to_next_stage toggle_indemand remove_from_stage add_items_to_rota get_barcodes_status );
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_rota') {
197
198     # Get the rota we're deleting
199     my $rota = Koha::StockRotationRotas->find($params{rota_id});
200
201     # Get all items on this rota, for each prefetch their
202     # stage and biblio objects
203     my $sritems = Koha::StockRotationItems->search(
204         { 'stage.rota_id' => $params{rota_id} },
205         {
206             prefetch => {
207                 stage => {
208                     'stockrotationitems' => {
209                         'itemnumber' => 'biblionumber'
210                     }
211                 }
212             }
213         }
214     );
215
216     $template->param(
217         rota_id  => $params{rota_id},
218         sritemstotal  => $sritems->count,
219         op       => $op
220     );
221
222 } elsif ($op eq 'delete_rota') {
223
224     # Get the rota we're deleting
225     my $rota = Koha::StockRotationRotas->find($params{rota_id});
226
227     $rota->delete;
228
229     # Return to the rotas list
230     print $input->redirect("/cgi-bin/koha/tools/stockrotation.pl");
231
232 } elsif ($op eq 'confirm_delete_stage') {
233
234     # Get the stage we're deleting
235     my $stage = Koha::StockRotationStages->find($params{stage_id});
236
237     $template->param(
238         op    => $op,
239         stage => $stage
240     );
241
242 } elsif ($op eq 'delete_stage') {
243
244     # Get the stage we're deleting
245     my $stage = Koha::StockRotationStages->find($params{stage_id});
246
247     # Get the ID of the rota with which this stage is associated
248     # (so we can return to the "Manage stages" page after deletion)
249     my $rota_id = $stage->rota->rota_id;
250
251     $stage->delete;
252
253     # Return to the stages list
254     print $input->redirect("?op=manage_stages&rota_id=$rota_id");
255
256 } elsif ($op eq 'process_stage') {
257
258     # Get a hashref of the submitted stage data
259     my $stage = get_stage_from_form();
260
261     # The rota we're managing
262     my $rota_id = $params{rota_id};
263
264     if (!process_stage($stage, $rota_id)) {
265
266         # The submitted stage was invalid
267         # Get all branches
268         my $branches = get_branches();
269
270         $template->param(
271             error        => 'invalid_form',
272             all_branches => $branches,
273             stage        => $stage,
274             rota_id      => $rota_id,
275             op           => 'create_edit_stage'
276         );
277
278     } else {
279
280         # All was well, return to the stages list
281         print $input->redirect("?op=manage_stages&rota_id=$rota_id");
282
283     }
284
285 } elsif ($op eq 'manage_items') {
286
287     my $rota = Koha::StockRotationRotas->find($params{rota_id});
288
289     # Get all items on this rota, for each prefetch their
290     # stage and biblio objects
291     my $sritems = Koha::StockRotationItems->search(
292         { 'stage.rota_id' => $params{rota_id} },
293         {
294             prefetch => {
295                 stage => {
296                     'stockrotationitems' => {
297                         'itemnumber' => 'biblionumber'
298                     }
299                 }
300             }
301         }
302     );
303
304     $template->param(
305         rota_id  => $params{rota_id},
306         error    => $params{error},
307         sritems  => $sritems,
308         branches => get_branches(),
309         stages   => get_stages($rota),
310         rota     => $rota,
311         op       => $op
312     );
313
314 } elsif ($op eq 'move_to_next_stage') {
315
316     move_to_next_stage($params{item_id}, $params{stage_id});
317
318     # Return to the items list
319     print $input->redirect("?op=manage_items&rota_id=" . $params{rota_id});
320
321 } elsif ($op eq 'toggle_in_demand') {
322
323     # Toggle the item's in_demand
324     toggle_indemand($params{item_id}, $params{stage_id});
325
326     # Return to the items list
327     print $input->redirect("?op=manage_items&rota_id=".$params{rota_id});
328
329 } elsif ($op eq 'remove_item_from_stage') {
330
331     # Remove the item from the stage
332     remove_from_stage($params{item_id}, $params{stage_id});
333
334     # Return to the items list
335     print $input->redirect("?op=manage_items&rota_id=".$params{rota_id});
336
337 } elsif ($op eq 'add_items_to_rota') {
338
339     # The item's barcode,
340     # which we may or may not have been passed
341     my $barcode = $params{barcode};
342
343     # The rota we're adding the item to
344     my $rota_id = $params{rota_id};
345
346     # The uploaded file filehandle,
347     # which we may or may not have been passed
348     my $barcode_file = $input->upload("barcodefile");
349
350     # We need to create an array of one or more barcodes to
351     # insert
352     my @barcodes = ();
353
354     # If the barcode input box was populated, use it
355     push @barcodes, $barcode if $barcode;
356
357     # Only parse the uploaded file if necessary
358     if ($barcode_file) {
359
360         # Call binmode on the filehandle as we want to set a
361         # UTF-8 layer on it
362         binmode($barcode_file, ":encoding(UTF-8)");
363         # Parse the file into an array of barcodes
364         while (my $barcode = <$barcode_file>) {
365             $barcode =~ s/\r/\n/g;
366             $barcode =~ s/\n+/\n/g;
367             my @data = split(/\n/, $barcode);
368             push @barcodes, @data;
369         }
370
371     }
372
373     # A hashref to hold the status of each barcode
374     my $barcode_status = {
375         ok        => [],
376         on_other  => [],
377         on_this   => [],
378         not_found => []
379     };
380
381     # If we have something to work with, do it
382     get_barcodes_status($rota_id, \@barcodes, $barcode_status) if (@barcodes);
383
384     # Now we know the status of each barcode, add those that
385     # need it
386     if (scalar @{$barcode_status->{ok}} > 0) {
387
388         add_items_to_rota($rota_id, $barcode_status->{ok});
389
390     }
391     # If we were only passed one barcode and it was successfully
392     # added, redirect back to ourselves, we don't want to display
393     # a report, redirect also if we were passed no barcodes
394     if (
395         scalar @barcodes == 0 ||
396         (scalar @barcodes == 1 && scalar @{$barcode_status->{ok}} == 1)
397     ) {
398
399         print $input->redirect("?op=manage_items&rota_id=$rota_id");
400
401     } else {
402
403         # Report on the outcome
404         $template->param(
405             barcode_status => $barcode_status,
406             rota_id        => $rota_id,
407             op             => $op
408         );
409
410     }
411
412 } elsif ($op eq 'move_items_to_rota') {
413
414     # The barcodes of the items we're moving
415     my @move = $input->multi_param('move_item');
416
417     foreach my $item(@move) {
418
419         # The item we're moving
420         my $item = Koha::Items->find($item);
421
422         # Move it to the new rota
423         $item->add_to_rota($params{rota_id});
424
425     }
426
427     # Return to the items list
428     print $input->redirect("?op=manage_items&rota_id=".$params{rota_id});
429
430 }
431
432 output_html_with_http_headers $input, $cookie, $template->output;
433
434 sub get_rota_from_form {
435
436     return {
437         id          => $params{id},
438         title       => $params{title},
439         cyclical    => $params{cyclical},
440         description => $params{description}
441     };
442 }
443
444 sub get_stage_from_form {
445
446     return {
447         stage_id    => $params{stage_id},
448         branchcode  => $params{branchcode},
449         duration    => $params{duration}
450     };
451 }
452
453 sub process_rota {
454
455     my $sub_rota = shift;
456
457     # Fields we require
458     my @required = ('title','cyclical');
459
460     # Count of the number of required fields we have
461     my $valid = 0;
462
463     # Ensure we have everything we require
464     foreach my $req(@required) {
465
466         if (exists $sub_rota->{$req}) {
467
468             chomp(my $value = $sub_rota->{$req});
469             if (length $value > 0) {
470                 $valid++;
471             }
472
473         }
474
475     }
476
477     # If we don't have everything we need
478     return 0 if $valid != scalar @required;
479
480     # Passed validation
481     # Find the rota we're updating
482     my $rota = Koha::StockRotationRotas->find($sub_rota->{id});
483
484     if ($rota) {
485
486         $rota->title(
487             $sub_rota->{title}
488         )->cyclical(
489             $sub_rota->{cyclical}
490         )->description(
491             $sub_rota->{description}
492         )->store;
493
494     } else {
495
496         $rota = Koha::StockRotationRota->new({
497             title       => $sub_rota->{title},
498             cyclical    => $sub_rota->{cyclical},
499             active      => 0,
500             description => $sub_rota->{description}
501         })->store;
502
503     }
504
505     return 1;
506 }
507
508 sub process_stage {
509
510     my ($sub_stage, $rota_id) = @_;
511
512     # Fields we require
513     my @required = ('branchcode','duration');
514
515     # Count of the number of required fields we have
516     my $valid = 0;
517
518     # Ensure we have everything we require
519     foreach my $req(@required) {
520
521         if (exists $sub_stage->{$req}) {
522
523             chomp(my $value = $sub_stage->{$req});
524             if (length $value > 0) {
525                 $valid++;
526             }
527
528         }
529
530     }
531
532     # If we don't have everything we need
533     return 0 if $valid != scalar @required;
534
535     # Passed validation
536     # Find the stage we're updating
537     my $stage = Koha::StockRotationStages->find($sub_stage->{stage_id});
538
539     if ($stage) {
540
541         # Updating an existing stage
542         $stage->branchcode_id(
543             $sub_stage->{branchcode}
544         )->duration(
545             $sub_stage->{duration}
546         )->store;
547
548     } else {
549
550         # Creating a new stage
551         $stage = Koha::StockRotationStage->new({
552             branchcode_id  => $sub_stage->{branchcode},
553             rota_id        => $rota_id,
554             duration       => $sub_stage->{duration}
555         })->store;
556
557     }
558
559     return 1;
560 }
561
562 =head1 AUTHOR
563
564 Andrew Isherwood <andrew.isherwood@ptfs-europe.com>
565
566 =cut