Line 0
Link Here
|
|
|
1 |
package Koha::Illrequest; |
2 |
|
3 |
# Copyright PTFS Europe 2016 |
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 |
14 |
# FOR A PARTICULAR PURPOSE. See the GNU General Public License for more |
15 |
# details. |
16 |
# |
17 |
# You should have received a copy of the GNU General Public License along with |
18 |
# Koha; if not, write to the Free Software Foundation, Inc., 51 Franklin |
19 |
# Street, Fifth Floor, Boston, MA 02110-1301 USA. |
20 |
|
21 |
# use Modern::Perl; |
22 |
|
23 |
use Clone 'clone'; |
24 |
use File::Basename qw/basename/; |
25 |
use Koha::Database; |
26 |
use Koha::Email; |
27 |
use Koha::Illrequestattributes; |
28 |
use Koha::Patron; |
29 |
use Mail::Sendmail; |
30 |
|
31 |
use base qw(Koha::Object); |
32 |
|
33 |
=head1 NAME |
34 |
|
35 |
Koha::Illrequest - Koha Illrequest Object class |
36 |
|
37 |
=head1 (Re)Design |
38 |
|
39 |
An ILLRequest consists of two parts; the Illrequest Koha::Object, and a series |
40 |
of related Illrequestattributes. |
41 |
|
42 |
The former encapsulates the basic necessary information that any ILL requires |
43 |
to be usable in Koha. The latter is a set of additional properties used by |
44 |
one of the backends. |
45 |
|
46 |
The former subsumes the legacy "Status" object. The latter remains |
47 |
encapsulated in the "Record" object. |
48 |
|
49 |
TODO: |
50 |
|
51 |
- Anything invoking the ->status method; annotated with: |
52 |
+ # Old use of ->status ! |
53 |
|
54 |
=head1 API |
55 |
|
56 |
=head2 Backend API Response Principles |
57 |
|
58 |
All methods should return a hashref in the following format: |
59 |
|
60 |
=item * error |
61 |
|
62 |
This should be set to 1 if an error was encountered. |
63 |
|
64 |
=item * status |
65 |
|
66 |
The status should be a string from the list of statuses detailed below. |
67 |
|
68 |
=item * message |
69 |
|
70 |
The message is a free text field that can be passed on to the end user. |
71 |
|
72 |
=item * value |
73 |
|
74 |
The value returned by the method. |
75 |
|
76 |
=over |
77 |
|
78 |
=head2 Interface Status Messages |
79 |
|
80 |
=over |
81 |
|
82 |
=item * branch_address_incomplete |
83 |
|
84 |
An interface request has determined branch address details are incomplete. |
85 |
|
86 |
=item * cancel_success |
87 |
|
88 |
The interface's cancel_request method was successful in cancelling the |
89 |
Illrequest using the API. |
90 |
|
91 |
=item * cancel_fail |
92 |
|
93 |
The interface's cancel_request method failed to cancel the Illrequest using |
94 |
the API. |
95 |
|
96 |
=item * unavailable |
97 |
|
98 |
The interface's request method returned saying that the desired item is not |
99 |
available for request. |
100 |
|
101 |
=head2 Class Methods |
102 |
|
103 |
=cut |
104 |
|
105 |
=head3 type |
106 |
|
107 |
=cut |
108 |
|
109 |
sub _type { |
110 |
return 'Illrequest'; |
111 |
} |
112 |
|
113 |
# XXX: Method to expose DBIx accessor for illrequestattributes relationship |
114 |
sub illrequestattributes { |
115 |
my ( $self ) = @_; |
116 |
return Koha::Illrequestattributes->_new_from_dbic( |
117 |
scalar $self->_result->illrequestattributes |
118 |
); |
119 |
} |
120 |
|
121 |
# XXX: Method to expose DBIx accessor for borrower relationship |
122 |
sub patron { |
123 |
my ( $self ) = @_; |
124 |
return Koha::Patron->_new_from_dbic( |
125 |
scalar $self->_result->borrowernumber |
126 |
); |
127 |
} |
128 |
|
129 |
sub load_backend { |
130 |
my ( $self, $backend_id ) = @_; |
131 |
|
132 |
my @raw = qw/Koha Illbackends/; # Base Path |
133 |
|
134 |
my $backend_name = $backend_id || $self->backend; |
135 |
$location = join "/", @raw, $backend_name, "Base.pm"; # File to load |
136 |
$backend_class = join "::", @raw, $backend_name, "Base"; # Package name |
137 |
require $location; |
138 |
$self->{_my_backend} = $backend_class->new({ config => $self->_config }); |
139 |
return $self; |
140 |
} |
141 |
|
142 |
=head3 _backend |
143 |
|
144 |
my $backend = $abstract->_backend($new_backend); |
145 |
my $backend = $abstract->_backend; |
146 |
|
147 |
Getter/Setter for our API object. |
148 |
|
149 |
=cut |
150 |
|
151 |
sub _backend { |
152 |
my ( $self, $backend ) = @_; |
153 |
$self->{_my_backend} = $backend if ( $backend ); |
154 |
# Dynamically load our backend object, as late as possible. |
155 |
$self->load_backend unless ( $self->{_my_backend} ); |
156 |
return $self->{_my_backend}; |
157 |
} |
158 |
|
159 |
=head3 _config |
160 |
|
161 |
my $config = $abstract->_config($config); |
162 |
my $config = $abstract->_config; |
163 |
|
164 |
Getter/Setter for our config object. |
165 |
|
166 |
=cut |
167 |
|
168 |
sub _config { |
169 |
my ( $self, $config ) = @_; |
170 |
$self->{_my_config} = $config if ( $config ); |
171 |
# Load our config object, as late as possible. |
172 |
unless ( $self->{_my_config} ) { |
173 |
$self->{_my_config} = Koha::Illrequest::Config->new; |
174 |
} |
175 |
return $self->{_my_config}; |
176 |
} |
177 |
|
178 |
=head3 metadata |
179 |
|
180 |
=cut |
181 |
|
182 |
sub metadata { |
183 |
my ( $self ) = @_; |
184 |
return $self->_backend->metadata($self); |
185 |
} |
186 |
|
187 |
=head3 _core_status_graph |
188 |
|
189 |
=cut |
190 |
|
191 |
sub _core_status_graph { |
192 |
my ( $self ) = @_; |
193 |
return { |
194 |
NEW => { |
195 |
prev_actions => [ ], # Actions containing buttons |
196 |
# leading to this status |
197 |
id => 'NEW', # ID of this status |
198 |
name => 'New request', # UI name of this status |
199 |
ui_method_name => 'New request', # UI name of method leading |
200 |
# to this status |
201 |
method => 'create', # method to this status |
202 |
next_actions => [ 'REQ', 'GENREQ', 'KILL' ], # buttons to add to all |
203 |
# requests with this status |
204 |
ui_method_icon => 'fa-plus', # UI Style class |
205 |
}, |
206 |
REQ => { |
207 |
prev_actions => [ 'NEW', 'REQREV', 'QUEUED' ], |
208 |
id => 'REQ', |
209 |
name => 'Requested', |
210 |
ui_method_name => 'Confirm request', |
211 |
method => 'confirm', |
212 |
next_actions => [ 'REQREV', 'CANCREQ' ], |
213 |
ui_method_icon => 'fa-check', |
214 |
}, |
215 |
GENREQ => { |
216 |
prev_actions => [ 'NEW', 'REQREV' ], |
217 |
id => 'GENREQ', |
218 |
name => 'Requested from partners', |
219 |
ui_method_name => 'Place request with partners', |
220 |
method => 'generic_confirm', |
221 |
next_actions => [ 'COMP' ], |
222 |
ui_method_icon => 'fa-send-o', |
223 |
}, |
224 |
REQREV => { |
225 |
prev_actions => [ 'CANCREQ', 'REQ' ], |
226 |
id => 'REQREV', |
227 |
name => 'Request reverted', |
228 |
ui_method_name => 'Revert Request', |
229 |
method => 'cancel', |
230 |
next_actions => [ 'REQ', 'GENREQ', 'KILL' ], |
231 |
ui_method_icon => 'fa-times', |
232 |
}, |
233 |
QUEUED => { |
234 |
prev_actions => [ ], |
235 |
id => 'QUEUED', |
236 |
name => 'Queued request', |
237 |
ui_method_name => 0, |
238 |
method => 0, |
239 |
next_actions => [ 'REQ', 'KILL' ], |
240 |
ui_method_icon => 0, |
241 |
}, |
242 |
CANCREQ => { |
243 |
prev_actions => [ 'REQ' ], |
244 |
id => 'CANCREQ', |
245 |
name => 'Cancellation requested', |
246 |
ui_method_name => 0, |
247 |
method => 0, |
248 |
next_actions => [ 'REQREV' ], |
249 |
ui_method_icon => 0, |
250 |
}, |
251 |
COMP => { |
252 |
prev_actions => [ 'REQ' ], |
253 |
id => 'COMP', |
254 |
name => 'Completed', |
255 |
ui_method_name => 0, |
256 |
method => 0, |
257 |
next_actions => [ ], |
258 |
ui_method_icon => 0, |
259 |
}, |
260 |
KILL => { |
261 |
prev_actions => [ 'QUEUED', 'REQREV', 'NEW' ], |
262 |
id => 'KILL', |
263 |
name => 0, |
264 |
ui_method_name => 'Delete request', |
265 |
method => 'delete', |
266 |
next_actions => [ ], |
267 |
ui_method_icon => 'fa-trash', |
268 |
}, |
269 |
}; |
270 |
} |
271 |
|
272 |
sub _status_graph_union { |
273 |
my ( $self, $core_status_graph, $backend_status_graph ) = @_; |
274 |
# Create new status graph with: |
275 |
# - all core_status_graph |
276 |
# - for-each each backend_status_graph |
277 |
# + add to new status graph |
278 |
# + for each core prev_action: |
279 |
# * locate core_status |
280 |
# * update next_actions with additional next action. |
281 |
# + for each core next_action: |
282 |
# * locate core_status |
283 |
# * update prev_actions with additional prev action |
284 |
|
285 |
my @core_status_ids = keys %{$core_status_graph}; |
286 |
my $status_graph = clone($core_status_graph); |
287 |
|
288 |
foreach my $backend_status_key ( keys %{$backend_status_graph} ) { |
289 |
$backend_status = $backend_status_graph->{$backend_status_key}; |
290 |
# Add to new status graph |
291 |
$status_graph->{$backend_status_key} = $backend_status; |
292 |
# Update all core methods' next_actions. |
293 |
foreach my $prev_action ( @{$backend_status->{prev_actions}} ) { |
294 |
if ( grep $prev_action, @core_status_ids ) { |
295 |
my @next_actions = |
296 |
@{$status_graph->{$prev_action}->{next_actions}}; |
297 |
push @next_actions, $backend_status_key; |
298 |
$status_graph->{$prev_action}->{next_actions} |
299 |
= \@next_actions; |
300 |
} |
301 |
} |
302 |
# Update all core methods' prev_actions |
303 |
foreach my $next_action ( @{$backend_status->{next_actions}} ) { |
304 |
if ( grep $next_action, @core_status_ids ) { |
305 |
my @prev_actions = |
306 |
@{$status_graph->{$next_action}->{prev_actions}}; |
307 |
push @prev_actions, $backend_status_key; |
308 |
$status_graph->{$next_action}->{prev_actions} |
309 |
= \@prev_actions; |
310 |
} |
311 |
} |
312 |
} |
313 |
|
314 |
return $status_graph; |
315 |
} |
316 |
|
317 |
### Core API methods |
318 |
|
319 |
=head3 capabilities |
320 |
|
321 |
my $capabilities = $illrequest->capabilities; |
322 |
|
323 |
Return a hashref mapping methods to operation names supported by the queried |
324 |
backend. |
325 |
|
326 |
Example return value: |
327 |
|
328 |
{ create => "Create Request", confirm => "Progress Request" } |
329 |
|
330 |
=cut |
331 |
|
332 |
sub capabilities { |
333 |
my ( $self, $status ) = @_; |
334 |
# Generate up to date status_graph |
335 |
my $status_graph = $self->_status_graph_union( |
336 |
$self->_core_status_graph, |
337 |
$self->_backend->status_graph({ |
338 |
request => $self, |
339 |
other => {} |
340 |
}) |
341 |
); |
342 |
# Extract available actions from graph. |
343 |
return $status_graph->{$status} if $status; |
344 |
# Or return entire graph. |
345 |
return $status_graph; |
346 |
} |
347 |
|
348 |
=head3 custom_capability |
349 |
|
350 |
Return the result of invoking $CANDIDATE on this request's backend with |
351 |
$PARAMS, or 0 if $CANDIDATE is an unknown method on backend. |
352 |
|
353 |
=cut |
354 |
|
355 |
sub custom_capability { |
356 |
my ( $self, $candidate, $params ) = @_; |
357 |
foreach my $capability ( values $self->capabilities ) { |
358 |
if ( $candidate eq $capability->{method} ) { |
359 |
my $response = |
360 |
$self->_backend->$candidate({ |
361 |
request => $self, |
362 |
other => $params, |
363 |
}); |
364 |
return $self->expandTemplate($response); |
365 |
} |
366 |
} |
367 |
return 0; |
368 |
} |
369 |
|
370 |
sub available_backends { |
371 |
my ( $self ) = @_; |
372 |
my $backend_dir = $self->_config->backend_dir; |
373 |
my @backends = (); |
374 |
my @backends = <$backend_dir/*> if ( $backend_dir ); |
375 |
my @backends = map { basename($_) } @backends; |
376 |
return \@backends; |
377 |
} |
378 |
|
379 |
sub available_actions { |
380 |
my ( $self ) = @_; |
381 |
my $current_action = $self->capabilities($self->status); |
382 |
my @available_actions = map { $self->capabilities($_) } |
383 |
@{$current_action->{next_actions}}; |
384 |
return \@available_actions; |
385 |
} |
386 |
|
387 |
sub backend_confirm { |
388 |
my ( $self, $params ) = @_; |
389 |
|
390 |
# The backend handles setting of mandatory fields in the commit stage: |
391 |
# - orderid |
392 |
# - accessurl, cost (if available). |
393 |
my $response = $self->_backend->confirm({ |
394 |
request => $self, |
395 |
other => $params, |
396 |
}); |
397 |
return $self->expandTemplate($response); |
398 |
} |
399 |
|
400 |
sub backend_update_status { |
401 |
my ( $self, $params ) = @_; |
402 |
return $self->expandTemplate($self->_backend->update_status($params)); |
403 |
} |
404 |
|
405 |
=head3 backend_cancel |
406 |
|
407 |
my $ILLResponse = $illRequest->backend_cancel; |
408 |
|
409 |
The standard interface method allowing for request cancellation. |
410 |
|
411 |
=cut |
412 |
|
413 |
sub backend_cancel { |
414 |
my ( $self, $params ) = @_; |
415 |
|
416 |
my $result = $self->_backend->cancel({ |
417 |
request => $self, |
418 |
other => $params |
419 |
}); |
420 |
|
421 |
return $self->expandTemplate($result); |
422 |
} |
423 |
|
424 |
=head3 backend_renew |
425 |
|
426 |
my $renew_response = $illRequest->backend_renew; |
427 |
|
428 |
The standard interface method allowing for request renewal queries. |
429 |
|
430 |
=cut |
431 |
|
432 |
sub backend_renew { |
433 |
my ( $self ) = @_; |
434 |
return $self->expandTemplate( |
435 |
$self->_backend->renew({ |
436 |
request => $self, |
437 |
}) |
438 |
); |
439 |
} |
440 |
|
441 |
=head3 backend_create |
442 |
|
443 |
my $create_response = $abstractILL->backend_create($params); |
444 |
|
445 |
Return an array of Record objects created by querying our backend with |
446 |
a Search query. |
447 |
|
448 |
In the context of the other ILL methods, this is a special method: we only |
449 |
pass it $params, as it does not yet have any other data associated with it. |
450 |
|
451 |
=cut |
452 |
|
453 |
sub backend_create { |
454 |
my ( $self, $params ) = @_; |
455 |
|
456 |
# First perform API action, then... |
457 |
my $args = { |
458 |
request => $self, |
459 |
other => $params, |
460 |
}; |
461 |
my $result = $self->_backend->create($args); |
462 |
|
463 |
# ... simple case: we're not at 'commit' stage. |
464 |
my $stage = $result->{stage}; |
465 |
return $self->expandTemplate($result) |
466 |
unless ( 'commit' eq $stage ); |
467 |
|
468 |
# ... complex case: commit! |
469 |
|
470 |
# Do we still have space for an ILL or should we queue? |
471 |
my $permitted = $self->check_limits( |
472 |
{ patron => $self->patron }, { librarycode => $self->branchcode } |
473 |
); |
474 |
|
475 |
# Now augment our committed request. |
476 |
|
477 |
$result->{permitted} = $permitted; # Queue request? |
478 |
|
479 |
# This involves... |
480 |
|
481 |
# ...Updating status! |
482 |
$self->status('QUEUED')->store unless ( $permitted ); |
483 |
|
484 |
# FIXME: Fix Unmediated ILLs! |
485 |
# Handle Unmediated ILLs |
486 |
# if ( C4::Context->preference("UnmediatedILL") && $permitted |
487 |
# # XXX: Why && result manual? |
488 |
# && $result->{manual} ) { |
489 |
# # FIXME: Also carry out privilege checks |
490 |
# my ( $response, $new_rq ) = $self->place_request; # WTF? |
491 |
# if ( $response ) { |
492 |
# $result->{value}->{request} = $new_rq; |
493 |
# return $result; |
494 |
# } else { |
495 |
# die "Placing the request failed."; |
496 |
# } |
497 |
# } else { |
498 |
# $result->{value}->{request} = $request; |
499 |
# return $result; |
500 |
# } |
501 |
|
502 |
return $self->expandTemplate($result); |
503 |
} |
504 |
|
505 |
=head3 expandTemplate |
506 |
|
507 |
my $params = $abstract->expandTemplate($params); |
508 |
|
509 |
Return a version of $PARAMS augmented with our required template path. |
510 |
|
511 |
=cut |
512 |
|
513 |
sub expandTemplate { |
514 |
my ( $self, $params ) = @_; |
515 |
my $backend = $self->_backend->name; |
516 |
# Generate path to file to load |
517 |
my $backend_dir = $self->_config->backend_dir; |
518 |
my $backend_tmpl = join "/", $backend_dir, $backend; |
519 |
my $intra_tmpl = join "/", $backend_tmpl, "intra-includes", |
520 |
$params->{method} . ".inc"; |
521 |
my $opac_tmpl = join "/", $backend_tmpl, "opac-includes", |
522 |
$params->{method} . ".inc"; |
523 |
# Set files to load |
524 |
$params->{template} = $intra_tmpl; |
525 |
$params->{opac_template} = $opac_tmpl; |
526 |
return $params; |
527 |
} |
528 |
|
529 |
#### Abstract Imports |
530 |
|
531 |
=head3 getCensorNotesStaff |
532 |
|
533 |
my $bool = $abstract->getCensorNotesStaff; |
534 |
|
535 |
Return a boolean indicating whether we should be censoring staff notes or not, |
536 |
as determined by our configuration file. |
537 |
|
538 |
=cut |
539 |
|
540 |
sub getCensorNotesStaff { |
541 |
my ( $self ) = @_; |
542 |
my $censorship = $self->_config->censorship; |
543 |
return $censorship->{censor_notes_staff}; |
544 |
} |
545 |
|
546 |
=head3 getDisplayReplyDate |
547 |
|
548 |
my 0 = $abstract->getDisplayReplyDate; |
549 |
|
550 |
Return a 0 if we want to hide it or 1 if not. |
551 |
|
552 |
=cut |
553 |
|
554 |
sub getDisplayReplyDate { |
555 |
my ( $self ) = @_; |
556 |
my $censorship = $self->_config->censorship; |
557 |
# If censor is yes, don't display and vice versa. |
558 |
return ( $censorship->{censor_reply_date} ) ? 0 : 1; |
559 |
} |
560 |
|
561 |
=head3 getLimits |
562 |
|
563 |
my $limit_rules = $abstract->getLimits( { |
564 |
type => 'brw_cat' | 'branch', |
565 |
value => $value |
566 |
} ); |
567 |
|
568 |
Return the ILL limit rules for the supplied combination of type / value. |
569 |
|
570 |
As the config may have no rules for this particular type / value combination, |
571 |
or for the default, we must define fall-back values here. |
572 |
|
573 |
=cut |
574 |
|
575 |
# FIXME: Needs unit tests! |
576 |
sub getLimits { |
577 |
my ( $self, $params ) = @_; |
578 |
my $limits = $self->_config->getLimitRules($params->{type}); |
579 |
|
580 |
return $limits->{$params->{value}} |
581 |
|| $limits->{default} |
582 |
|| { count => -1, method => 'active' }; |
583 |
} |
584 |
|
585 |
=head3 getPrefix |
586 |
|
587 |
my $prefix = $abstract->getPrefix( { |
588 |
brw_cat => $brw_cat, |
589 |
branch => $branch_code, |
590 |
} ); |
591 |
|
592 |
Return the ILL prefix as defined by our $params: either per borrower category, |
593 |
per branch or the default. |
594 |
|
595 |
=cut |
596 |
|
597 |
sub getPrefix { |
598 |
my ( $self, $params ) = @_; |
599 |
my $brn_prefixes = $self->_config->getPrefixes('branch'); |
600 |
my $brw_prefixes = $self->_config->getPrefixes('brw_cat'); |
601 |
|
602 |
return $brw_prefixes->{$params->{brw_cat}} |
603 |
|| $brn_prefixes->{$params->{branch}} |
604 |
|| $brw_prefixes->{default} |
605 |
|| ""; # "the empty prefix" |
606 |
} |
607 |
|
608 |
#### Illrequests Imports |
609 |
|
610 |
=head3 check_limits |
611 |
|
612 |
my $ok = $illRequests->check_limits( { |
613 |
borrower => $borrower, |
614 |
branchcode => 'branchcode' | undef, |
615 |
} ); |
616 |
|
617 |
Given $PARAMS, a hashref containing a $borrower object and a $branchcode, |
618 |
see whether we are still able to place ILLs. |
619 |
|
620 |
LimitRules are derived from koha-conf.xml: |
621 |
+ default limit counts, and counting method |
622 |
+ branch specific limit counts & counting method |
623 |
+ borrower category specific limit counts & counting method |
624 |
+ err on the side of caution: a counting fail will cause fail, even if |
625 |
the other counts passes. |
626 |
|
627 |
=cut |
628 |
|
629 |
# FIXME: Needs unit tests! |
630 |
sub check_limits { |
631 |
my ( $self, $params ) = @_; |
632 |
my $patron = $params->{patron}; |
633 |
my $branchcode = $params->{librarycode} || $patron->branchcode; |
634 |
|
635 |
# Establish rules |
636 |
my ( $branch_rules, $brw_rules ) = ( |
637 |
$self->getLimits( { |
638 |
type => 'branch', |
639 |
value => $branchcode |
640 |
} ), |
641 |
$self->getLimits( { |
642 |
type => 'brw_cat', |
643 |
value => $patron->categorycode, |
644 |
} ), |
645 |
); |
646 |
# Almost there, but category code didn't quite work. |
647 |
my ( $branch_limit, $brw_limit ) |
648 |
= ( $branch_rules->{count}, $brw_rules->{count} ); |
649 |
my ( $branch_count, $brw_count ) = ( |
650 |
$self->_limit_counter( |
651 |
$branch_rules->{method}, { branch_id => $branchcode } |
652 |
), |
653 |
$self->_limit_counter( |
654 |
$brw_rules->{method}, { borrower_id => $patron->borrowernumber } |
655 |
), |
656 |
); |
657 |
|
658 |
# Compare and return |
659 |
# A limit of -1 means no limit exists. |
660 |
# We return blocked if either branch limit or brw limit is reached. |
661 |
if ( ( $branch_limit != -1 && $branch_limit <= $branch_count ) |
662 |
|| ( $brw_limit != -1 && $brw_limit <= $brw_count ) ) { |
663 |
return 0; |
664 |
} else { |
665 |
return 1; |
666 |
} |
667 |
} |
668 |
|
669 |
# FIXME: Needs unit tests! |
670 |
sub _limit_counter { |
671 |
my ( $self, $method, $target ) = @_; |
672 |
|
673 |
# Establish parameters of counts |
674 |
my $where; |
675 |
if ($method && $method eq 'annual') { |
676 |
$where = \"YEAR(placement_date) = YEAR(NOW())"; |
677 |
} else { # assume 'active' |
678 |
# FIXME: This status list is ugly. There should be a method in config |
679 |
# to return these. |
680 |
$where = { status => { -not_in => [ 'QUEUED', 'COMP' ] } }; |
681 |
} |
682 |
|
683 |
# Create resultset |
684 |
my $resultset = Koha::Illrequests->search({ %{$target}, %{$where} }); |
685 |
|
686 |
# Fetch counts |
687 |
return $resultset->count; |
688 |
} |
689 |
|
690 |
=head3 requires_moderation |
691 |
|
692 |
my $status = $illRequest->requires_moderation; |
693 |
|
694 |
Return the name of the status if moderation by staff is required; or 0 |
695 |
otherwise. |
696 |
|
697 |
=cut |
698 |
|
699 |
sub requires_moderation { |
700 |
my ( $self ) = @_; |
701 |
my $require_moderation = { |
702 |
'CANCREQ' => 'CANCREQ', |
703 |
}; |
704 |
return $require_moderation->{$self->status}; |
705 |
} |
706 |
|
707 |
=head3 generic_confirm |
708 |
|
709 |
my $stage_summary = $illRequest->generic_confirm; |
710 |
|
711 |
Handle the generic_confirm extended method. The first stage involves creating |
712 |
a template email for the end user to edit in the browser. The second stage |
713 |
attempts to submit the email. |
714 |
|
715 |
=cut |
716 |
|
717 |
sub generic_confirm { |
718 |
my ( $self, $params ) = @_; |
719 |
if ( !$params->{stage}|| $params->{stage} eq 'init' ) { |
720 |
my $draft->{subject} = "ILL Request"; |
721 |
$draft->{body} = <<EOF; |
722 |
Dear Sir/Madam, |
723 |
|
724 |
We would like to request an interlibrary loan for a title matching the |
725 |
following description: |
726 |
|
727 |
EOF |
728 |
|
729 |
my $details = $self->metadata; |
730 |
while (my ($title, $value) = each %{$details}) { |
731 |
$draft->{body} .= " - " . $title . ": " . $value . "\n" |
732 |
if $value; |
733 |
} |
734 |
$draft->{body} .= <<EOF; |
735 |
|
736 |
Please let us know if you are able to supply this to us. |
737 |
|
738 |
Kind Regards |
739 |
EOF |
740 |
|
741 |
my $partners = Koha::Patrons->search({ |
742 |
categorycode => $self->_config->partner_code |
743 |
}); |
744 |
return { |
745 |
error => 0, |
746 |
status => '', |
747 |
message => '', |
748 |
method => 'generic_confirm', |
749 |
stage => 'draft', |
750 |
value => { |
751 |
draft => $draft, |
752 |
partners => $partners, |
753 |
} |
754 |
}; |
755 |
|
756 |
} elsif ( 'draft' eq $params->{stage} ) { |
757 |
# Create the to header |
758 |
my $to = $params->{partners}; |
759 |
$to =~ s/^\x00//; # Strip leading NULLs |
760 |
$to =~ s/\x00/; /; # Replace others with '; ' |
761 |
die "No target email addresses found. Either select at least one partner or check your ILL partner library records." if ( !$to ); |
762 |
# Create the from, replyto and sender headers |
763 |
$branch = Koha::Libraries->find($params->{current_branchcode}) |
764 |
|| die "Invalid current branchcode. Are you logged in as the database user?"; |
765 |
my $from = $branch->branchemail; |
766 |
my $replyto = $branch->branchreplyto || $from; |
767 |
die "Your branch has no email address. Please set it." |
768 |
if ( !$from ); |
769 |
# Create the email |
770 |
my $message = Koha::Email->new; |
771 |
my %mail = $message->create_message_headers( |
772 |
{ |
773 |
to => $to, |
774 |
from => $from, |
775 |
replyto => $replyto, |
776 |
subject => Encode::encode( "utf8", $params->{subject} ), |
777 |
message => Encode::encode( "utf8", $params->{body} ), |
778 |
contenttype => 'text/plain', |
779 |
} |
780 |
); |
781 |
# Send it |
782 |
my $result = sendmail(%mail); |
783 |
if ( $result ) { |
784 |
$self->status("GENREQ")->store; |
785 |
return { |
786 |
error => 0, |
787 |
status => '', |
788 |
message => '', |
789 |
method => 'generic_confirm', |
790 |
stage => 'commit', |
791 |
next => 'illview', |
792 |
}; |
793 |
} else { |
794 |
return { |
795 |
error => 1, |
796 |
status => 'email_failed', |
797 |
message => $Mail::Sendmail::error, |
798 |
method => 'generic_confirm', |
799 |
stage => 'draft', |
800 |
}; |
801 |
} |
802 |
} else { |
803 |
die "Unknown stage, should not have happened." |
804 |
} |
805 |
} |
806 |
|
807 |
=head3 id_prefix |
808 |
|
809 |
my $prefix = $record->id_prefix; |
810 |
|
811 |
Return the prefix appropriate for the current Illrequest as derived from the |
812 |
borrower and branch associated with this request's Status, and the config |
813 |
file. |
814 |
|
815 |
=cut |
816 |
|
817 |
sub id_prefix { |
818 |
my ( $self ) = @_; |
819 |
# FIXME: can we automatically use borrowernumber as borrower? |
820 |
my $brw = $self->patron; |
821 |
my $brw_cat = "dummy"; |
822 |
$brw_cat = $brw->categorycode |
823 |
unless ( 'HASH' eq ref($brw) && $brw->{deleted} ); |
824 |
my $prefix = $self->getPrefix( { |
825 |
brw_cat => $brw_cat, |
826 |
branch => $self->branchcode, |
827 |
} ); |
828 |
$prefix .= "-" if ( $prefix ); |
829 |
return $prefix; |
830 |
} |
831 |
|
832 |
=head3 _censor |
833 |
|
834 |
my $params = $illRequest->_censor($params); |
835 |
|
836 |
Return $params, modified to reflect our censorship requirements. |
837 |
|
838 |
=cut |
839 |
|
840 |
sub _censor { |
841 |
my ( $self, $params ) = @_; |
842 |
$params->{censor_notes_staff} = $self->getCensorNotesStaff |
843 |
if ( $params->{opac} ); |
844 |
$params->{display_reply_date} = $self->getDisplayReplyDate; |
845 |
|
846 |
return $params; |
847 |
} |
848 |
|
849 |
=head1 AUTHOR |
850 |
|
851 |
Alex Sassmannshausen <alex.sassmannshausen@ptfs-europe.com> |
852 |
|
853 |
=cut |
854 |
|
855 |
1; |