|
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; |