Line 0
Link Here
|
0 |
- |
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::Output; |
32 |
|
33 |
use Koha::Libraries; |
34 |
use Koha::Stockrotationrotas; |
35 |
use Koha::Stockrotationitems; |
36 |
use Koha::Stockrotationstages; |
37 |
use Koha::Item; |
38 |
use Koha::Util::Stockrotation qw(:ALL); |
39 |
|
40 |
my $input = new CGI; |
41 |
|
42 |
my ( $template, $loggedinuser, $cookie ) = get_template_and_user( |
43 |
{ |
44 |
template_name => 'tools/stockrotation.tt', |
45 |
query => $input, |
46 |
type => 'intranet', |
47 |
authnotrequired => 0 |
48 |
} |
49 |
); |
50 |
|
51 |
# Grab all passed data |
52 |
# 'our' since Plack changes the scoping |
53 |
# of 'my' |
54 |
our %params = $input->Vars(); |
55 |
|
56 |
my $op = $params{op}; |
57 |
|
58 |
if (!defined $op) { |
59 |
|
60 |
# No operation is supplied, we're just displaying the list of rotas |
61 |
my $rotas = Koha::Stockrotationrotas->search->as_list; |
62 |
|
63 |
$template->param( |
64 |
existing_rotas => $rotas, |
65 |
no_op_set => 1 |
66 |
); |
67 |
|
68 |
} elsif ($op eq 'create_edit_rota') { |
69 |
|
70 |
# Edit an existing rota or define a new one |
71 |
my $rota_id = $params{rota_id}; |
72 |
|
73 |
my $rota = {}; |
74 |
|
75 |
if (!defined $rota_id) { |
76 |
|
77 |
# No ID supplied, we're creating a new rota |
78 |
# Create a shell rota hashref |
79 |
$rota = { |
80 |
cyclical => 1, |
81 |
active => 0 |
82 |
}; |
83 |
|
84 |
} else { |
85 |
|
86 |
# ID supplied, we're editing an existing rota |
87 |
$rota = Koha::Stockrotationrotas->find($rota_id); |
88 |
|
89 |
} |
90 |
|
91 |
$template->param( |
92 |
rota => $rota, |
93 |
op => $op |
94 |
); |
95 |
|
96 |
} elsif ($op eq 'toggle_rota') { |
97 |
|
98 |
# Find and update the active status of the rota |
99 |
my $rota = Koha::Stockrotationrotas->find($params{rota_id}); |
100 |
|
101 |
my $new_active = ($rota->active == 1) ? 0 : 1; |
102 |
|
103 |
$rota->active($new_active)->store; |
104 |
|
105 |
# Return to rotas page |
106 |
print $input->redirect('stockrotation.pl'); |
107 |
|
108 |
} elsif ($op eq 'process_rota') { |
109 |
|
110 |
# Get a hashref of the submitted rota data |
111 |
my $rota = get_rota_from_form(); |
112 |
|
113 |
if (!process_rota($rota)) { |
114 |
|
115 |
# The submitted rota was invalid |
116 |
$template->param( |
117 |
error => 'invalid_form', |
118 |
rota => $rota, |
119 |
op => 'create_edit_rota' |
120 |
); |
121 |
|
122 |
} else { |
123 |
|
124 |
# All was well, return to the rotas list |
125 |
print $input->redirect('stockrotation.pl'); |
126 |
|
127 |
} |
128 |
|
129 |
} elsif ($op eq 'manage_stages') { |
130 |
|
131 |
my $rota = Koha::Stockrotationrotas->find($params{rota_id}); |
132 |
|
133 |
$template->param( |
134 |
rota => $rota, |
135 |
branches => get_branches(), |
136 |
existing_stages => get_stages($rota), |
137 |
rota_id => $params{rota_id}, |
138 |
op => $op |
139 |
); |
140 |
|
141 |
} elsif ($op eq 'create_edit_stage') { |
142 |
|
143 |
# Edit an existing stage or define a new one |
144 |
my $stage_id = $params{stage_id}; |
145 |
|
146 |
my $rota_id = $params{rota_id}; |
147 |
|
148 |
if (!defined $stage_id) { |
149 |
|
150 |
# No ID supplied, we're creating a new stage |
151 |
$template->param( |
152 |
branches => get_branches(), |
153 |
stage => {}, |
154 |
rota_id => $rota_id, |
155 |
op => $op |
156 |
); |
157 |
|
158 |
} else { |
159 |
|
160 |
# ID supplied, we're editing an existing stage |
161 |
my $stage = Koha::Stockrotationstages->find($stage_id); |
162 |
|
163 |
$template->param( |
164 |
branches => get_branches(), |
165 |
stage => $stage, |
166 |
rota_id => $stage->rota->rota_id, |
167 |
op => $op |
168 |
); |
169 |
|
170 |
} |
171 |
|
172 |
} elsif ($op eq 'confirm_remove_from_rota') { |
173 |
|
174 |
# Get the stage we're deleting |
175 |
$template->param( |
176 |
op => $op, |
177 |
rota_id => $params{rota_id}, |
178 |
stage_id => $params{stage_id}, |
179 |
item_id => $params{item_id} |
180 |
); |
181 |
|
182 |
} elsif ($op eq 'confirm_delete_stage') { |
183 |
|
184 |
# Get the stage we're deleting |
185 |
my $stage = Koha::Stockrotationstages->find($params{stage_id}); |
186 |
|
187 |
$template->param( |
188 |
op => $op, |
189 |
stage => $stage |
190 |
); |
191 |
|
192 |
} elsif ($op eq 'delete_stage') { |
193 |
|
194 |
# Get the stage we're deleting |
195 |
my $stage = Koha::Stockrotationstages->find($params{stage_id}); |
196 |
|
197 |
# Get the ID of the rota with which this stage is associated |
198 |
# (so we can return to the "Manage stages" page after deletion) |
199 |
my $rota_id = $stage->rota->rota_id; |
200 |
|
201 |
$stage->delete; |
202 |
|
203 |
# Return to the stages list |
204 |
print $input->redirect("?op=manage_stages&rota_id=$rota_id"); |
205 |
|
206 |
} elsif ($op eq 'process_stage') { |
207 |
|
208 |
# Get a hashref of the submitted stage data |
209 |
my $stage = get_stage_from_form(); |
210 |
|
211 |
# The rota we're managing |
212 |
my $rota_id = $params{rota_id}; |
213 |
|
214 |
if (!process_stage($stage, $rota_id)) { |
215 |
|
216 |
# The submitted stage was invalid |
217 |
# Get all branches |
218 |
my $branches = get_branches(); |
219 |
|
220 |
$template->param( |
221 |
error => 'invalid_form', |
222 |
all_branches => $branches, |
223 |
stage => $stage, |
224 |
rota_id => $rota_id, |
225 |
op => 'create_edit_stage' |
226 |
); |
227 |
|
228 |
} else { |
229 |
|
230 |
# All was well, return to the stages list |
231 |
print $input->redirect("?op=manage_stages&rota_id=$rota_id"); |
232 |
|
233 |
} |
234 |
|
235 |
} elsif ($op eq 'manage_items') { |
236 |
|
237 |
my $rota = Koha::Stockrotationrotas->find($params{rota_id}); |
238 |
|
239 |
# Get all items on this rota, for each prefetch their |
240 |
# stage and biblio objects |
241 |
my $items = Koha::Stockrotationitems->search( |
242 |
{ 'stage.rota_id' => $params{rota_id} }, |
243 |
{ |
244 |
prefetch => { |
245 |
stage => { |
246 |
'stockrotationitems' => { |
247 |
'itemnumber' => 'biblionumber' |
248 |
} |
249 |
} |
250 |
} |
251 |
} |
252 |
); |
253 |
|
254 |
$template->param( |
255 |
rota_id => $params{rota_id}, |
256 |
error => $params{error}, |
257 |
items => $items, |
258 |
branches => get_branches(), |
259 |
stages => get_stages($rota), |
260 |
rota => $rota, |
261 |
op => $op |
262 |
); |
263 |
|
264 |
} elsif ($op eq 'move_to_next_stage') { |
265 |
|
266 |
move_to_next_stage($params{item_id}, $params{stage_id}); |
267 |
|
268 |
# Return to the items list |
269 |
print $input->redirect("?op=manage_items&rota_id=" . $params{rota_id}); |
270 |
|
271 |
} elsif ($op eq 'toggle_in_demand') { |
272 |
|
273 |
# Toggle the item's in_demand |
274 |
toggle_indemand($params{item_id}, $params{stage_id}); |
275 |
|
276 |
# Return to the items list |
277 |
print $input->redirect("?op=manage_items&rota_id=".$params{rota_id}); |
278 |
|
279 |
} elsif ($op eq 'remove_item_from_stage') { |
280 |
|
281 |
# Remove the item from the stage |
282 |
remove_from_stage($params{item_id}, $params{stage_id}); |
283 |
|
284 |
# Return to the items list |
285 |
print $input->redirect("?op=manage_items&rota_id=".$params{rota_id}); |
286 |
|
287 |
} elsif ($op eq 'add_items_to_rota') { |
288 |
|
289 |
# The item's barcode, |
290 |
# which we may or may not have been passed |
291 |
my $barcode = $params{barcode}; |
292 |
|
293 |
# The rota we're adding the item to |
294 |
my $rota_id = $params{rota_id}; |
295 |
|
296 |
# The uploaded file filehandle, |
297 |
# which we may or may not have been passed |
298 |
my $barcode_file = $input->upload("barcodefile"); |
299 |
|
300 |
# We need to create an array of one or more barcodes to |
301 |
# insert |
302 |
my @barcodes = (); |
303 |
|
304 |
# If the barcode input box was populated, use it |
305 |
push @barcodes, $barcode if $barcode; |
306 |
|
307 |
# Only parse the uploaded file if necessary |
308 |
if ($barcode_file) { |
309 |
|
310 |
# Call binmode on the filehandle as we want to set a |
311 |
# UTF-8 layer on it |
312 |
binmode($barcode_file, ":encoding(UTF-8)"); |
313 |
# Parse the file into an array of barcodes |
314 |
while (my $barcode = <$barcode_file>) { |
315 |
$barcode =~ s/\r/\n/g; |
316 |
$barcode =~ s/\n+/\n/g; |
317 |
my @data = split(/\n/, $barcode); |
318 |
push @barcodes, @data; |
319 |
} |
320 |
|
321 |
} |
322 |
|
323 |
# A hashref to hold the status of each barcode |
324 |
my $barcode_status = { |
325 |
ok => [], |
326 |
on_other => [], |
327 |
on_this => [], |
328 |
not_found => [] |
329 |
}; |
330 |
|
331 |
# If we have something to work with, do it |
332 |
get_barcodes_status($rota_id, \@barcodes, $barcode_status) if (@barcodes); |
333 |
|
334 |
# Now we know the status of each barcode, add those that |
335 |
# need it |
336 |
if (scalar @{$barcode_status->{ok}} > 0) { |
337 |
|
338 |
add_items_to_rota($rota_id, $barcode_status->{ok}); |
339 |
|
340 |
} |
341 |
# If we were only passed one barcode and it was successfully |
342 |
# added, redirect back to ourselves, we don't want to display |
343 |
# a report, redirect also if we were passed no barcodes |
344 |
if ( |
345 |
scalar @barcodes == 0 || |
346 |
(scalar @barcodes == 1 && scalar @{$barcode_status->{ok}} == 1) |
347 |
) { |
348 |
|
349 |
print $input->redirect("?op=manage_items&rota_id=$rota_id"); |
350 |
|
351 |
} else { |
352 |
|
353 |
# Report on the outcome |
354 |
$template->param( |
355 |
barcode_status => $barcode_status, |
356 |
rota_id => $rota_id, |
357 |
op => $op |
358 |
); |
359 |
|
360 |
} |
361 |
|
362 |
} elsif ($op eq 'move_items_to_rota') { |
363 |
|
364 |
# The barcodes of the items we're moving |
365 |
my @move = $input->param('move_item'); |
366 |
|
367 |
foreach my $item(@move) { |
368 |
|
369 |
# The item we're moving |
370 |
my $item = Koha::Items->find($item); |
371 |
|
372 |
# Move it to the new rota |
373 |
$item->add_to_rota($params{rota_id}); |
374 |
|
375 |
} |
376 |
|
377 |
# Return to the items list |
378 |
print $input->redirect("?op=manage_items&rota_id=".$params{rota_id}); |
379 |
|
380 |
} |
381 |
|
382 |
output_html_with_http_headers $input, $cookie, $template->output; |
383 |
|
384 |
sub get_rota_from_form { |
385 |
|
386 |
return { |
387 |
id => $params{id}, |
388 |
title => $params{title}, |
389 |
cyclical => $params{cyclical}, |
390 |
active => $params{active}, |
391 |
description => $params{description} |
392 |
}; |
393 |
} |
394 |
|
395 |
sub get_stage_from_form { |
396 |
|
397 |
return { |
398 |
stage_id => $params{stage_id}, |
399 |
branchcode => $params{branchcode}, |
400 |
duration => $params{duration} |
401 |
}; |
402 |
} |
403 |
|
404 |
sub process_rota { |
405 |
|
406 |
my $sub_rota = shift; |
407 |
|
408 |
# Fields we require |
409 |
my @required = ('title','cyclical','active'); |
410 |
|
411 |
# Count of the number of required fields we have |
412 |
my $valid = 0; |
413 |
|
414 |
# Ensure we have everything we require |
415 |
foreach my $req(@required) { |
416 |
|
417 |
if (exists $sub_rota->{$req}) { |
418 |
|
419 |
chomp(my $value = $sub_rota->{$req}); |
420 |
if (length $value > 0) { |
421 |
$valid++; |
422 |
} |
423 |
|
424 |
} |
425 |
|
426 |
} |
427 |
|
428 |
# If we don't have everything we need |
429 |
return 0 if $valid != scalar @required; |
430 |
|
431 |
# Passed validation |
432 |
# Find the rota we're updating |
433 |
my $rota = Koha::Stockrotationrotas->find($sub_rota->{id}); |
434 |
|
435 |
if ($rota) { |
436 |
|
437 |
$rota->title( |
438 |
$sub_rota->{title} |
439 |
)->cyclical( |
440 |
$sub_rota->{cyclical} |
441 |
)->active( |
442 |
$sub_rota->{active} |
443 |
)->description( |
444 |
$sub_rota->{description} |
445 |
)->store; |
446 |
|
447 |
} else { |
448 |
|
449 |
$rota = Koha::Stockrotationrota->new({ |
450 |
title => $sub_rota->{title}, |
451 |
cyclical => $sub_rota->{cyclical}, |
452 |
active => $sub_rota->{active}, |
453 |
description => $sub_rota->{description} |
454 |
})->store; |
455 |
|
456 |
} |
457 |
|
458 |
return 1; |
459 |
} |
460 |
|
461 |
sub process_stage { |
462 |
|
463 |
my ($sub_stage, $rota_id) = @_; |
464 |
|
465 |
# Fields we require |
466 |
my @required = ('branchcode','duration'); |
467 |
|
468 |
# Count of the number of required fields we have |
469 |
my $valid = 0; |
470 |
|
471 |
# Ensure we have everything we require |
472 |
foreach my $req(@required) { |
473 |
|
474 |
if (exists $sub_stage->{$req}) { |
475 |
|
476 |
chomp(my $value = $sub_stage->{$req}); |
477 |
if (length $value > 0) { |
478 |
$valid++; |
479 |
} |
480 |
|
481 |
} |
482 |
|
483 |
} |
484 |
|
485 |
# If we don't have everything we need |
486 |
return 0 if $valid != scalar @required; |
487 |
|
488 |
# Passed validation |
489 |
# Find the stage we're updating |
490 |
my $stage = Koha::Stockrotationstages->find($sub_stage->{stage_id}); |
491 |
|
492 |
if ($stage) { |
493 |
|
494 |
# Updating an existing stage |
495 |
$stage->branchcode_id( |
496 |
$sub_stage->{branchcode} |
497 |
)->duration( |
498 |
$sub_stage->{duration} |
499 |
)->store; |
500 |
|
501 |
} else { |
502 |
|
503 |
# Creating a new stage |
504 |
$stage = Koha::Stockrotationstage->new({ |
505 |
branchcode_id => $sub_stage->{branchcode}, |
506 |
rota_id => $rota_id, |
507 |
duration => $sub_stage->{duration} |
508 |
})->store; |
509 |
|
510 |
} |
511 |
|
512 |
return 1; |
513 |
} |
514 |
|
515 |
=head1 AUTHOR |
516 |
|
517 |
Andrew Isherwood <andrew.isherwood@ptfs-europe.com> |
518 |
|
519 |
=cut |