|
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::Illrequestattributes; |
| 27 |
use Koha::Patron; |
| 28 |
|
| 29 |
use base qw(Koha::Object); |
| 30 |
|
| 31 |
=head1 NAME |
| 32 |
|
| 33 |
Koha::Illrequest - Koha Illrequest Object class |
| 34 |
|
| 35 |
=head1 (Re)Design |
| 36 |
|
| 37 |
An ILLRequest consists of two parts; the Illrequest Koha::Object, and a series |
| 38 |
of related Illrequestattributes. |
| 39 |
|
| 40 |
The former encapsulates the basic necessary information that any ILL requires |
| 41 |
to be usable in Koha. The latter is a set of additional properties used by |
| 42 |
one of the backends. |
| 43 |
|
| 44 |
The former subsumes the legacy "Status" object. The latter remains |
| 45 |
encapsulated in the "Record" object. |
| 46 |
|
| 47 |
TODO: |
| 48 |
|
| 49 |
- Anything invoking the ->status method; annotated with: |
| 50 |
+ # Old use of ->status ! |
| 51 |
|
| 52 |
=head1 API |
| 53 |
|
| 54 |
=head2 Backend API Response Principles |
| 55 |
|
| 56 |
All methods should return a hashref in the following format: |
| 57 |
|
| 58 |
=item * error |
| 59 |
|
| 60 |
This should be set to 1 if an error was encountered. |
| 61 |
|
| 62 |
=item * status |
| 63 |
|
| 64 |
The status should be a string from the list of statuses detailed below. |
| 65 |
|
| 66 |
=item * message |
| 67 |
|
| 68 |
The message is a free text field that can be passed on to the end user. |
| 69 |
|
| 70 |
=item * value |
| 71 |
|
| 72 |
The value returned by the method. |
| 73 |
|
| 74 |
=over |
| 75 |
|
| 76 |
=head2 Interface Status Messages |
| 77 |
|
| 78 |
=over |
| 79 |
|
| 80 |
=item * branch_address_incomplete |
| 81 |
|
| 82 |
An interface request has determined branch address details are incomplete. |
| 83 |
|
| 84 |
=item * cancel_success |
| 85 |
|
| 86 |
The interface's cancel_request method was successful in cancelling the |
| 87 |
Illrequest using the API. |
| 88 |
|
| 89 |
=item * cancel_fail |
| 90 |
|
| 91 |
The interface's cancel_request method failed to cancel the Illrequest using |
| 92 |
the API. |
| 93 |
|
| 94 |
=item * unavailable |
| 95 |
|
| 96 |
The interface's request method returned saying that the desired item is not |
| 97 |
available for request. |
| 98 |
|
| 99 |
=head2 Class Methods |
| 100 |
|
| 101 |
=cut |
| 102 |
|
| 103 |
=head3 type |
| 104 |
|
| 105 |
=cut |
| 106 |
|
| 107 |
sub _type { |
| 108 |
return 'Illrequest'; |
| 109 |
} |
| 110 |
|
| 111 |
# XXX: Method to expose DBIx accessor for illrequestattributes relationship |
| 112 |
sub illrequestattributes { |
| 113 |
my ( $self ) = @_; |
| 114 |
return Koha::Illrequestattributes->_new_from_dbic( |
| 115 |
scalar $self->_result->illrequestattributes |
| 116 |
); |
| 117 |
} |
| 118 |
|
| 119 |
# XXX: Method to expose DBIx accessor for borrower relationship |
| 120 |
sub patron { |
| 121 |
my ( $self ) = @_; |
| 122 |
return Koha::Patron->_new_from_dbic( |
| 123 |
scalar $self->_result->borrowernumber |
| 124 |
); |
| 125 |
} |
| 126 |
|
| 127 |
sub status { |
| 128 |
my ( $self, $new ) = @_; |
| 129 |
# Fetch old status... |
| 130 |
my $old = $self->SUPER::status; |
| 131 |
# If new... |
| 132 |
if ( $new ) { |
| 133 |
# Invoke hook, then update to $new |
| 134 |
$rs = $self->backend_update_status({ |
| 135 |
request => $self, |
| 136 |
other => { |
| 137 |
old_status => $old, |
| 138 |
new_status => $new, |
| 139 |
} |
| 140 |
}); |
| 141 |
die($rs->{message}) if $rs->{error}; |
| 142 |
return $self->SUPER::status($new); |
| 143 |
} |
| 144 |
return $old; |
| 145 |
} |
| 146 |
|
| 147 |
sub load_backend { |
| 148 |
my ( $self, $backend_id ) = @_; |
| 149 |
|
| 150 |
my @raw = qw/Koha Illbackends/; # Base Path |
| 151 |
|
| 152 |
my $backend_name = $backend_id || $self->backend; |
| 153 |
$location = join "/", @raw, $backend_name, "Base.pm"; # File to load |
| 154 |
$backend_class = join "::", @raw, $backend_name, "Base"; # Package name |
| 155 |
require $location; |
| 156 |
$self->{_my_backend} = $backend_class->new({ config => $self->_config }); |
| 157 |
return $self; |
| 158 |
} |
| 159 |
|
| 160 |
=head3 _backend |
| 161 |
|
| 162 |
my $backend = $abstract->_backend($new_backend); |
| 163 |
my $backend = $abstract->_backend; |
| 164 |
|
| 165 |
Getter/Setter for our API object. |
| 166 |
|
| 167 |
=cut |
| 168 |
|
| 169 |
sub _backend { |
| 170 |
my ( $self, $backend ) = @_; |
| 171 |
$self->{_my_backend} = $backend if ( $backend ); |
| 172 |
# Dynamically load our backend object, as late as possible. |
| 173 |
$self->load_backend unless ( $self->{_my_backend} ); |
| 174 |
return $self->{_my_backend}; |
| 175 |
} |
| 176 |
|
| 177 |
=head3 _config |
| 178 |
|
| 179 |
my $config = $abstract->_config($config); |
| 180 |
my $config = $abstract->_config; |
| 181 |
|
| 182 |
Getter/Setter for our config object. |
| 183 |
|
| 184 |
=cut |
| 185 |
|
| 186 |
sub _config { |
| 187 |
my ( $self, $config ) = @_; |
| 188 |
$self->{_my_config} = $config if ( $config ); |
| 189 |
# Load our config object, as late as possible. |
| 190 |
unless ( $self->{_my_config} ) { |
| 191 |
$self->{_my_config} = Koha::Illrequest::Config->new; |
| 192 |
} |
| 193 |
return $self->{_my_config}; |
| 194 |
} |
| 195 |
|
| 196 |
=head3 metadata |
| 197 |
|
| 198 |
=cut |
| 199 |
|
| 200 |
sub metadata { |
| 201 |
my ( $self ) = @_; |
| 202 |
return {}; |
| 203 |
} |
| 204 |
|
| 205 |
=head3 _core_status_graph |
| 206 |
|
| 207 |
=cut |
| 208 |
|
| 209 |
sub _core_status_graph { |
| 210 |
my ( $self ) = @_; |
| 211 |
return { |
| 212 |
NEW => { |
| 213 |
prev_actions => [ ], # Actions containing buttons |
| 214 |
# leading to this status |
| 215 |
id => 'NEW', # ID of this status |
| 216 |
name => 'New request', # UI name of this status |
| 217 |
ui_method_name => 'New request', # UI name of method leading |
| 218 |
# to this status |
| 219 |
method => 'create', # method to this status |
| 220 |
next_actions => [ 'REQ', 'KILL' ], # buttons to add to all |
| 221 |
# requests with this status |
| 222 |
ui_method_icon => 'fa-plus', # UI Style class |
| 223 |
}, |
| 224 |
REQ => { |
| 225 |
prev_actions => [ 'NEW', 'REQREV', 'QUEUED' ], |
| 226 |
id => 'REQ', |
| 227 |
name => 'Requested', |
| 228 |
ui_method_name => 'Confirm request', |
| 229 |
method => 'confirm', |
| 230 |
next_actions => [ 'REQREV', 'CANCREQ' ], |
| 231 |
ui_method_icon => 'fa-check', |
| 232 |
}, |
| 233 |
REQREV => { |
| 234 |
prev_actions => [ 'CANCREQ', 'REQ' ], |
| 235 |
id => 'REQREV', |
| 236 |
name => 'Request reverted', |
| 237 |
ui_method_name => 'Revert Request', |
| 238 |
method => 'cancel', |
| 239 |
next_actions => [ 'REQ', 'KILL' ], |
| 240 |
ui_method_icon => 'fa-times', |
| 241 |
}, |
| 242 |
QUEUED => { |
| 243 |
prev_actions => [ ], |
| 244 |
id => 'QUEUED', |
| 245 |
name => 'Queued request', |
| 246 |
ui_method_name => 0, |
| 247 |
method => 0, |
| 248 |
next_actions => [ 'REQ', 'KILL' ], |
| 249 |
ui_method_icon => 0, |
| 250 |
}, |
| 251 |
CANCREQ => { |
| 252 |
prev_actions => [ 'REQ' ], |
| 253 |
id => 'CANCREQ', |
| 254 |
name => 'Cancellation requested', |
| 255 |
ui_method_name => 0, |
| 256 |
method => 0, |
| 257 |
next_actions => [ 'REQREV' ], |
| 258 |
ui_method_icon => 0, |
| 259 |
}, |
| 260 |
COMP => { |
| 261 |
prev_actions => [ 'REQ' ], |
| 262 |
id => 'COMP', |
| 263 |
name => 'Completed', |
| 264 |
ui_method_name => 0, |
| 265 |
method => 0, |
| 266 |
next_actions => [ ], |
| 267 |
ui_method_icon => 0, |
| 268 |
}, |
| 269 |
KILL => { |
| 270 |
prev_actions => [ 'QUEUED', 'REQREV', 'NEW' ], |
| 271 |
id => 'KILL', |
| 272 |
name => 0, |
| 273 |
ui_method_name => 'Delete request', |
| 274 |
method => 'delete', |
| 275 |
next_actions => [ ], |
| 276 |
ui_method_icon => 'fa-trash', |
| 277 |
}, |
| 278 |
}; |
| 279 |
} |
| 280 |
|
| 281 |
sub _status_graph_union { |
| 282 |
my ( $self, $core_status_graph, $backend_status_graph ) = @_; |
| 283 |
# Create new status graph with: |
| 284 |
# - all core_status_graph |
| 285 |
# - for-each each backend_status_graph |
| 286 |
# + add to new status graph |
| 287 |
# + for each core prev_action: |
| 288 |
# * locate core_status |
| 289 |
# * update next_actions with additional next action. |
| 290 |
# + for each core next_action: |
| 291 |
# * locate core_status |
| 292 |
# * update prev_actions with additional prev action |
| 293 |
|
| 294 |
my @core_status_ids = keys %{$core_status_graph}; |
| 295 |
my $status_graph = clone($core_status_graph); |
| 296 |
|
| 297 |
foreach my $backend_status_key ( keys %{$backend_status_graph} ) { |
| 298 |
$backend_status = $backend_status_graph->{$backend_status_key}; |
| 299 |
# Add to new status graph |
| 300 |
$status_graph->{$backend_status_key} = $backend_status; |
| 301 |
# Update all core methods' next_actions. |
| 302 |
foreach my $prev_action ( @{$backend_status->{prev_actions}} ) { |
| 303 |
if ( grep $prev_action, @core_status_ids ) { |
| 304 |
my @next_actions = |
| 305 |
@{$status_graph->{$prev_action}->{next_actions}}; |
| 306 |
push @next_actions, $backend_status_key; |
| 307 |
$status_graph->{$prev_action}->{next_actions} |
| 308 |
= \@next_actions; |
| 309 |
} |
| 310 |
} |
| 311 |
# Update all core methods' prev_actions |
| 312 |
foreach my $next_action ( @{$backend_status->{next_actions}} ) { |
| 313 |
if ( grep $next_action, @core_status_ids ) { |
| 314 |
my @prev_actions = |
| 315 |
@{$status_graph->{$next_action}->{prev_actions}}; |
| 316 |
push @prev_actions, $backend_status_key; |
| 317 |
$status_graph->{$next_action}->{prev_actions} |
| 318 |
= \@prev_actions; |
| 319 |
} |
| 320 |
} |
| 321 |
} |
| 322 |
|
| 323 |
return $status_graph; |
| 324 |
} |
| 325 |
|
| 326 |
### Core API methods |
| 327 |
|
| 328 |
=head3 capabilities |
| 329 |
|
| 330 |
my $capabilities = $illrequest->capabilities; |
| 331 |
|
| 332 |
Return a hashref mapping methods to operation names supported by the queried |
| 333 |
backend. |
| 334 |
|
| 335 |
Example return value: |
| 336 |
|
| 337 |
{ create => "Create Request", confirm => "Progress Request" } |
| 338 |
|
| 339 |
=cut |
| 340 |
|
| 341 |
sub capabilities { |
| 342 |
my ( $self, $status ) = @_; |
| 343 |
# Generate up to date status_graph |
| 344 |
my $status_graph = $self->_status_graph_union( |
| 345 |
$self->_core_status_graph, |
| 346 |
$self->_backend->status_graph({ |
| 347 |
request => $self, |
| 348 |
other => {} |
| 349 |
}) |
| 350 |
); |
| 351 |
# Extract available actions from graph. |
| 352 |
return $status_graph->{$status} if $status; |
| 353 |
# Or return entire graph. |
| 354 |
return $status_graph; |
| 355 |
} |
| 356 |
|
| 357 |
sub available_backends { |
| 358 |
my ( $self ) = @_; |
| 359 |
my $backend_dir = $self->_config->backend_dir; |
| 360 |
my @backends = (); |
| 361 |
my @backends = <$backend_dir/*> if ( $backend_dir ); |
| 362 |
my @backends = map { basename($_) } @backends; |
| 363 |
return \@backends; |
| 364 |
} |
| 365 |
|
| 366 |
sub available_actions { |
| 367 |
my ( $self ) = @_; |
| 368 |
my $current_action = $self->capabilities($self->status); |
| 369 |
my @available_actions = map { $self->capabilities($_) } |
| 370 |
@{$current_action->{next_actions}}; |
| 371 |
return \@available_actions; |
| 372 |
} |
| 373 |
|
| 374 |
sub backend_confirm { |
| 375 |
my ( $self, $params ) = @_; |
| 376 |
|
| 377 |
# The backend handles setting of mandatory fields in the commit stage: |
| 378 |
# - orderid |
| 379 |
# - accessurl, cost (if available). |
| 380 |
my $response = $self->_backend->confirm({ |
| 381 |
request => $self, |
| 382 |
other => $params, |
| 383 |
}); |
| 384 |
return $self->expandTemplate($response); |
| 385 |
} |
| 386 |
|
| 387 |
sub backend_update_status { |
| 388 |
my ( $self, $params ) = @_; |
| 389 |
return $self->expandTemplate($self->_backend->update_status($params)); |
| 390 |
} |
| 391 |
|
| 392 |
=head3 backend_cancel |
| 393 |
|
| 394 |
my $ILLResponse = $illRequest->backend_cancel; |
| 395 |
|
| 396 |
The standard interface method allowing for request cancellation. |
| 397 |
|
| 398 |
=cut |
| 399 |
|
| 400 |
sub backend_cancel { |
| 401 |
my ( $self ) = @_; |
| 402 |
|
| 403 |
my $result = $self->_backend->cancel({ request => $self }); |
| 404 |
|
| 405 |
# FIXME: API breakage: Untypical old return value (preserved below) may |
| 406 |
# cause issues! |
| 407 |
# return ( $self->expandTemplate($result), $self ); |
| 408 |
|
| 409 |
return $self->expandTemplate($result); |
| 410 |
} |
| 411 |
|
| 412 |
=head3 backend_renew |
| 413 |
|
| 414 |
my $renew_response = $illRequest->backend_renew; |
| 415 |
|
| 416 |
The standard interface method allowing for request renewal queries. |
| 417 |
|
| 418 |
=cut |
| 419 |
|
| 420 |
sub backend_renew { |
| 421 |
my ( $self ) = @_; |
| 422 |
return $self->expandTemplate( |
| 423 |
$self->_backend->renew({ |
| 424 |
request => $self, |
| 425 |
}) |
| 426 |
); |
| 427 |
} |
| 428 |
|
| 429 |
=head3 backend_status |
| 430 |
|
| 431 |
my $status_response = $illRequest->backend_status; |
| 432 |
|
| 433 |
The standard interface method allowing for request status queries. |
| 434 |
|
| 435 |
=cut |
| 436 |
|
| 437 |
sub backend_status { |
| 438 |
my ( $self, $params ) = @_; |
| 439 |
return $self->expandTemplate( |
| 440 |
$self->_backend->status({ |
| 441 |
request => $self, |
| 442 |
other => $params, |
| 443 |
}) |
| 444 |
); |
| 445 |
} |
| 446 |
|
| 447 |
=head3 backend_create |
| 448 |
|
| 449 |
my $create_response = $abstractILL->backend_create($params); |
| 450 |
|
| 451 |
Return an array of Record objects created by querying our backend with |
| 452 |
a Search query. |
| 453 |
|
| 454 |
In the context of the other ILL methods, this is a special method: we only |
| 455 |
pass it $params, as it does not yet have any other data associated with it. |
| 456 |
|
| 457 |
=cut |
| 458 |
|
| 459 |
sub backend_create { |
| 460 |
my ( $self, $params ) = @_; |
| 461 |
|
| 462 |
# First perform API action, then... |
| 463 |
my $args = { |
| 464 |
request => $self, |
| 465 |
other => $params, |
| 466 |
}; |
| 467 |
my $result = $self->_backend->create($args); |
| 468 |
|
| 469 |
# ... simple case: we're not at 'commit' stage. |
| 470 |
my $stage = $result->{stage}; |
| 471 |
return $self->expandTemplate($result) |
| 472 |
unless ( 'commit' eq $stage || 'commit_manual' eq $stage ); |
| 473 |
|
| 474 |
# ... complex case: commit! |
| 475 |
|
| 476 |
# Do we still have space for an ILL or should we queue? |
| 477 |
my $permitted = $self->check_limits( |
| 478 |
{ patron => $self->patron }, { librarycode => $self->branchcode } |
| 479 |
); |
| 480 |
|
| 481 |
# Now augment our committed request. |
| 482 |
|
| 483 |
# FIXME: WTF? may have to deal with manual creation here! |
| 484 |
# if ( $params->{uin} ) { |
| 485 |
# # API guarantees it will return UIN of backend if available. |
| 486 |
# $result->{uin} = $params->{uin}; |
| 487 |
# } else { |
| 488 |
# # Else, considered manual creation |
| 489 |
# $result->{primary_manual} = 1; |
| 490 |
# } |
| 491 |
|
| 492 |
# FIXME: These two are for legacy purposes. Up to date branch and |
| 493 |
# borrower can be found via |
| 494 |
# $result->{request}->{borrower,branch}_id |
| 495 |
$result->{branch} = "FIXME: Incorrect branch reference!"; |
| 496 |
$result->{borrower} = "FIXME: Incorrect borrower reference!"; |
| 497 |
|
| 498 |
$result->{permitted} = $permitted; # Queue request? |
| 499 |
|
| 500 |
# FIXME: Here we should simply populate ourselves with our available data. |
| 501 |
# |
| 502 |
# This involves... |
| 503 |
|
| 504 |
# ...Updating status! |
| 505 |
$self->status('QUEUED')->store unless ( $permitted ); |
| 506 |
|
| 507 |
# FIXME: Fix Unmediated ILLs! |
| 508 |
# Handle Unmediated ILLs |
| 509 |
# if ( C4::Context->preference("UnmediatedILL") && $permitted |
| 510 |
# # XXX: Why && result manual? |
| 511 |
# && $result->{manual} ) { |
| 512 |
# # FIXME: Also carry out privilege checks |
| 513 |
# my ( $response, $new_rq ) = $self->place_request; # WTF? |
| 514 |
# if ( $response ) { |
| 515 |
# $result->{value}->{request} = $new_rq; |
| 516 |
# return $result; |
| 517 |
# } else { |
| 518 |
# die "Placing the request failed."; |
| 519 |
# } |
| 520 |
# } else { |
| 521 |
# $result->{value}->{request} = $request; |
| 522 |
# return $result; |
| 523 |
# } |
| 524 |
|
| 525 |
return $self->expandTemplate($result); |
| 526 |
} |
| 527 |
|
| 528 |
=head3 expandTemplate |
| 529 |
|
| 530 |
my $params = $abstract->expandTemplate($params); |
| 531 |
|
| 532 |
Return a version of $PARAMS augmented with our required template path. |
| 533 |
|
| 534 |
=cut |
| 535 |
|
| 536 |
sub expandTemplate { |
| 537 |
my ( $self, $params ) = @_; |
| 538 |
my $backend = $self->_backend->name; |
| 539 |
# Generate path to file to load |
| 540 |
my $backend_dir = $self->_config->backend_dir; |
| 541 |
my $backend_tmpl = join "/", $backend_dir, $backend; |
| 542 |
my $intra_tmpl = join "/", $backend_tmpl, "intra-includes", |
| 543 |
$params->{method} . ".inc"; |
| 544 |
# Set file to load |
| 545 |
$params->{template} = $intra_tmpl; |
| 546 |
return $params; |
| 547 |
} |
| 548 |
|
| 549 |
#### Abstract Imports |
| 550 |
|
| 551 |
=head3 getCensorNotesStaff |
| 552 |
|
| 553 |
my $bool = $abstract->getCensorNotesStaff; |
| 554 |
|
| 555 |
Return a boolean indicating whether we should be censoring staff notes or not, |
| 556 |
as determined by our configuration file. |
| 557 |
|
| 558 |
=cut |
| 559 |
|
| 560 |
sub getCensorNotesStaff { |
| 561 |
my ( $self ) = @_; |
| 562 |
my $censorship = $self->_config->censorship; |
| 563 |
return $censorship->{censor_notes_staff}; |
| 564 |
} |
| 565 |
|
| 566 |
=head3 getDisplayReplyDate |
| 567 |
|
| 568 |
my 0 = $abstract->getDisplayReplyDate; |
| 569 |
|
| 570 |
Return a 0 if we want to hide it or 1 if not. |
| 571 |
|
| 572 |
=cut |
| 573 |
|
| 574 |
sub getDisplayReplyDate { |
| 575 |
my ( $self ) = @_; |
| 576 |
my $censorship = $self->_config->censorship; |
| 577 |
# If censor is yes, don't display and vice versa. |
| 578 |
return ( $censorship->{censor_reply_date} ) ? 0 : 1; |
| 579 |
} |
| 580 |
|
| 581 |
=head3 getLimits |
| 582 |
|
| 583 |
my $limit_rules = $abstract->getLimits( { |
| 584 |
type => 'brw_cat' | 'branch', |
| 585 |
value => $value |
| 586 |
} ); |
| 587 |
|
| 588 |
Return the ILL limit rules for the supplied combination of type / value. |
| 589 |
|
| 590 |
As the config may have no rules for this particular type / value combination, |
| 591 |
or for the default, we must define fall-back values here. |
| 592 |
|
| 593 |
=cut |
| 594 |
|
| 595 |
# FIXME: Needs unit tests! |
| 596 |
sub getLimits { |
| 597 |
my ( $self, $params ) = @_; |
| 598 |
my $limits = $self->_config->getLimitRules($params->{type}); |
| 599 |
|
| 600 |
return $limits->{$params->{value}} |
| 601 |
|| $limits->{default} |
| 602 |
|| { count => -1, method => 'active' }; |
| 603 |
} |
| 604 |
|
| 605 |
=head3 getPrefix |
| 606 |
|
| 607 |
my $prefix = $abstract->getPrefix( { |
| 608 |
brw_cat => $brw_cat, |
| 609 |
branch => $branch_code, |
| 610 |
} ); |
| 611 |
|
| 612 |
Return the ILL prefix as defined by our $params: either per borrower category, |
| 613 |
per branch or the default. |
| 614 |
|
| 615 |
=cut |
| 616 |
|
| 617 |
sub getPrefix { |
| 618 |
my ( $self, $params ) = @_; |
| 619 |
my $brn_prefixes = $self->_config->getPrefixes('branch'); |
| 620 |
my $brw_prefixes = $self->_config->getPrefixes('brw_cat'); |
| 621 |
|
| 622 |
return $brw_prefixes->{$params->{brw_cat}} |
| 623 |
|| $brn_prefixes->{$params->{branch}} |
| 624 |
|| $brw_prefixes->{default} |
| 625 |
|| ""; # "the empty prefix" |
| 626 |
} |
| 627 |
|
| 628 |
#### Illrequests Imports |
| 629 |
|
| 630 |
=head3 check_limits |
| 631 |
|
| 632 |
my $ok = $illRequests->check_limits( { |
| 633 |
borrower => $borrower, |
| 634 |
branchcode => 'branchcode' | undef, |
| 635 |
} ); |
| 636 |
|
| 637 |
Given $PARAMS, a hashref containing a $borrower object and a $branchcode, |
| 638 |
see whether we are still able to place ILLs. |
| 639 |
|
| 640 |
LimitRules are derived from koha-conf.xml: |
| 641 |
+ default limit counts, and counting method |
| 642 |
+ branch specific limit counts & counting method |
| 643 |
+ borrower category specific limit counts & counting method |
| 644 |
+ err on the side of caution: a counting fail will cause fail, even if |
| 645 |
the other counts passes. |
| 646 |
|
| 647 |
=cut |
| 648 |
|
| 649 |
# FIXME: Needs unit tests! |
| 650 |
sub check_limits { |
| 651 |
my ( $self, $params ) = @_; |
| 652 |
my $patron = $params->{patron}; |
| 653 |
my $branchcode = $params->{librarycode} || $patron->branchcode; |
| 654 |
|
| 655 |
# Establish rules |
| 656 |
my ( $branch_rules, $brw_rules ) = ( |
| 657 |
$self->getLimits( { |
| 658 |
type => 'branch', |
| 659 |
value => $branchcode |
| 660 |
} ), |
| 661 |
$self->getLimits( { |
| 662 |
type => 'brw_cat', |
| 663 |
value => $patron->categorycode, |
| 664 |
} ), |
| 665 |
); |
| 666 |
# Almost there, but category code didn't quite work. |
| 667 |
my ( $branch_limit, $brw_limit ) |
| 668 |
= ( $branch_rules->{count}, $brw_rules->{count} ); |
| 669 |
my ( $branch_count, $brw_count ) = ( |
| 670 |
$self->_limit_counter( |
| 671 |
$branch_rules->{method}, { branch_id => $branchcode } |
| 672 |
), |
| 673 |
$self->_limit_counter( |
| 674 |
$brw_rules->{method}, { borrower_id => $patron->borrowernumber } |
| 675 |
), |
| 676 |
); |
| 677 |
|
| 678 |
# Compare and return |
| 679 |
# A limit of -1 means no limit exists. |
| 680 |
# We return blocked if either branch limit or brw limit is reached. |
| 681 |
if ( ( $branch_limit != -1 && $branch_limit <= $branch_count ) |
| 682 |
|| ( $brw_limit != -1 && $brw_limit <= $brw_count ) ) { |
| 683 |
return 0; |
| 684 |
} else { |
| 685 |
return 1; |
| 686 |
} |
| 687 |
} |
| 688 |
|
| 689 |
# FIXME: Needs unit tests! |
| 690 |
sub _limit_counter { |
| 691 |
my ( $self, $method, $target ) = @_; |
| 692 |
|
| 693 |
# Establish parameters of counts |
| 694 |
my $where; |
| 695 |
if ($method && $method eq 'annual') { |
| 696 |
$where = \"YEAR(placement_date) = YEAR(NOW())"; |
| 697 |
} else { # assume 'active' |
| 698 |
# FIXME: This status list is ugly. There should be a method in config |
| 699 |
# to return these. |
| 700 |
$where = { status => { -not_in => [ 'QUEUED', 'COMP' ] } }; |
| 701 |
} |
| 702 |
|
| 703 |
# Create resultset |
| 704 |
my $resultset = Koha::Illrequests->search({ %{$target}, %{$where} }); |
| 705 |
|
| 706 |
# Fetch counts |
| 707 |
return $resultset->count; |
| 708 |
} |
| 709 |
|
| 710 |
=head3 summary |
| 711 |
|
| 712 |
my $summary = $illRequest->summary(); |
| 713 |
|
| 714 |
Return a data-structure ready for JSON or other format based processing and |
| 715 |
display to the end-user. It returns a composit of $self's Record and Status |
| 716 |
`summary' methods. |
| 717 |
|
| 718 |
=cut |
| 719 |
|
| 720 |
# FIXME: To be handled in templates |
| 721 |
# FIXME: Needs Record handling sorting out |
| 722 |
# sub getSummary { |
| 723 |
# my ( $self, $params ) = @_; |
| 724 |
# $params->{id_prefix} = $self->id_prefix; |
| 725 |
# my $record = $self->record->getSummary($params); |
| 726 |
# my $status = $self->getStatusSummary($params); |
| 727 |
# my %summary = (%{$record}, %{$status}); |
| 728 |
# return \%summary; |
| 729 |
# } |
| 730 |
|
| 731 |
# FIXME: To be handled in templates |
| 732 |
# sub getStatusSummary { |
| 733 |
# my ( $self, $params ) = @_; |
| 734 |
# my $return = { |
| 735 |
# id => [ "Request Number", $self->illrequest_id ], |
| 736 |
# biblionumber => [ "Biblio Number", $self->biblio_id ], |
| 737 |
# status => [ "Status", $self->status ], |
| 738 |
# reqtype => [ "Request Type", $self->medium ], |
| 739 |
# }; |
| 740 |
# # Add borrower or borrowernumber. |
| 741 |
# if ( $params->{brw} ) { |
| 742 |
# $return->{borrower} = [ "Borrower", $self->patron ] |
| 743 |
# } else { |
| 744 |
# $return->{borrowernumber} |
| 745 |
# = [ "Borrower Number", $self->borrower_id ]; |
| 746 |
# } |
| 747 |
# # Add ID with prefix |
| 748 |
# $return->{prefix_id} = [ |
| 749 |
# "Request Number", $params->{id_prefix} . $self->illrequest_id |
| 750 |
# ]; |
| 751 |
# return $return; |
| 752 |
# } |
| 753 |
|
| 754 |
# FIXME: To be handled in templates |
| 755 |
# # FIXME: Missing new canonical fields! |
| 756 |
# sub getFullStatus { |
| 757 |
# my ( $self, $params ) = @_; |
| 758 |
|
| 759 |
# my $return = { |
| 760 |
# id => [ "Request Number", $self->illrequest_id ], |
| 761 |
# biblionumber => [ "Biblio Number", $self->biblio_id ], |
| 762 |
# status => [ "Status", $self->status ], |
| 763 |
# placement_date => [ "Placement Date", $self->placed ], |
| 764 |
# ts => [ "Timestamp", $self->updated ], |
| 765 |
# completion_date => [ "Completion Date", $self->completed ], |
| 766 |
# reqtype => [ "Request Type", $self->medium ], |
| 767 |
# branch => [ "Branch", $self->branch_id ], |
| 768 |
# }; |
| 769 |
# # Add borrower or borrowernumber. |
| 770 |
# if ( $params->{brw} ) { |
| 771 |
# $return->{borrower} = [ "Borrower", $self->patron ] |
| 772 |
# } else { |
| 773 |
# $return->{borrowernumber} |
| 774 |
# = [ "Borrower Number", $self->borrower_id ]; |
| 775 |
# } |
| 776 |
# # Add ID with prefix |
| 777 |
# $return->{prefix_id} = [ |
| 778 |
# "Request Number", $params->{id_prefix} . $self->illrequest_id |
| 779 |
# ]; |
| 780 |
# # Add Reply Date if it's used |
| 781 |
# $return->{reply_date} = [ |
| 782 |
# $params->{display_reply_date}, $self->replied |
| 783 |
# ] if ( $params->{display_reply_date} ); |
| 784 |
# return $return; |
| 785 |
# } |
| 786 |
|
| 787 |
# =head3 getFullDetails |
| 788 |
|
| 789 |
# my $details = $illRequest->getFullDetails; |
| 790 |
# my $details = $illRequest->getFullDetails( { brw => 1 } ); |
| 791 |
|
| 792 |
# Return a data-structure ready for JSON or other format based processing and |
| 793 |
# display to the end-user. It returns a composit of $self's Record and Status |
| 794 |
# `fullDetails' methods. |
| 795 |
|
| 796 |
# =cut |
| 797 |
|
| 798 |
# FIXME: To be handled in templates |
| 799 |
# # FIXME: Needs Record handling sorting out |
| 800 |
# sub getFullDetails { |
| 801 |
# my ( $self, $params ) = @_; |
| 802 |
# $params->{id_prefix} = $self->id_prefix; |
| 803 |
# $params = $self->_censor($params); |
| 804 |
# my $record = $self->record->getFullDetails($params); |
| 805 |
# my $status = $self->getFullStatus($params); |
| 806 |
# my %summary = (%{$record}, %{$status}); |
| 807 |
|
| 808 |
# return \%summary; |
| 809 |
# } |
| 810 |
|
| 811 |
# =head3 getForEditing |
| 812 |
|
| 813 |
# my $partialRequest = $illRequest->getForEditing(); |
| 814 |
|
| 815 |
# Return a data-structure ready-for-JSON-or-other-format conversion and |
| 816 |
# display. The data-structure will be a hashref of 2, with the first entry |
| 817 |
# consisting of a summary of the Record, and the second entry consisting of the |
| 818 |
# full Status details. |
| 819 |
|
| 820 |
# The former is for display and should not be edited by hand. The latter can be edited. |
| 821 |
|
| 822 |
# =cut |
| 823 |
|
| 824 |
# FIXME: To be handled in templates |
| 825 |
# # FIXME: Needs Record handling sorting out |
| 826 |
# sub getForEditing { |
| 827 |
# my ( $self, $params ) = @_; |
| 828 |
# $params->{id_prefix} = $self->id_prefix; |
| 829 |
# $params = $self->_censor($params); |
| 830 |
# my $record = $self->record->getFullDetails($params); |
| 831 |
# my $status = $self->getFullStatus($params); |
| 832 |
|
| 833 |
# return [ $record, $status ]; |
| 834 |
# } |
| 835 |
|
| 836 |
# =head3 _seed_from_manual_entry |
| 837 |
|
| 838 |
# my $request = $illRequest->_seed_from_manual_entry($params); |
| 839 |
|
| 840 |
# When an API does not have any valid items for a customer, they may want to |
| 841 |
# manually enter item details. This procedure provides a way for us to create |
| 842 |
# an Illrequest in Koha using fields populated via Abstract's |
| 843 |
# `manual_entry_fields` method. |
| 844 |
|
| 845 |
# =cut |
| 846 |
|
| 847 |
# sub _seed_from_manual_entry { |
| 848 |
# my ( $self, $opts ) = @_; |
| 849 |
# $self->record($opts->{value}); |
| 850 |
|
| 851 |
# # Our fields |
| 852 |
# $self->reqtype($opts->{"m./type"}); |
| 853 |
# $self->borrowernumber($opts->{borrower}); |
| 854 |
# $self->branch($opts->{branch}); |
| 855 |
# $self->ts(DateTime->now); |
| 856 |
# $self->placement_date(DateTime->now); |
| 857 |
# if ( $opts->{permitted} ) { |
| 858 |
# $self->status('NEW'); |
| 859 |
# } else { |
| 860 |
# $self->status('QUEUED'); |
| 861 |
# } |
| 862 |
|
| 863 |
# # FIXME: No longer exists |
| 864 |
# # $self->save; # save to DB. |
| 865 |
|
| 866 |
# return $self; |
| 867 |
# } |
| 868 |
|
| 869 |
# =head3 _seed_from_api |
| 870 |
|
| 871 |
# my $request = $illRequest->_seed_from_api($params); |
| 872 |
|
| 873 |
# This seeding procedure is designed to populate an Illrequest using a search |
| 874 |
# result from the API in use by Abstract. |
| 875 |
|
| 876 |
# =cut |
| 877 |
|
| 878 |
# sub _seed_from_api { |
| 879 |
# my ( $self, $opts ) = @_; |
| 880 |
|
| 881 |
# # FIXME: illcomm: We are currently creating the Record using |
| 882 |
# # spec.yaml, at the Record level. I think it might be better to |
| 883 |
# # let the API define what values to store in Record. Perhaps we |
| 884 |
# # still use a Spec.yaml, but make that backend defined: BLDSS |
| 885 |
# # might do, but NNCIPP might not. |
| 886 |
# # |
| 887 |
# # All that Record should care about is that we have key-value |
| 888 |
# # pairs that can dessicate and reconstitute data stored in the db. |
| 889 |
# $self->record($opts->{value}); |
| 890 |
|
| 891 |
# # Our fields |
| 892 |
# $self->reqtype($self->record->getProperty('type')); |
| 893 |
# $self->borrowernumber($opts->{borrower}); |
| 894 |
# $self->branch($opts->{branch}); |
| 895 |
# $self->ts(DateTime->now); |
| 896 |
# $self->reply_date(DateTime->now); |
| 897 |
# $self->placement_date(DateTime->now); |
| 898 |
# if ( $opts->{permitted} ) { |
| 899 |
# $self->status('NEW'); |
| 900 |
# } else { |
| 901 |
# $self->status('QUEUED'); |
| 902 |
# } |
| 903 |
|
| 904 |
# # FIXME: No longer exists |
| 905 |
# # $self->save; # save to DB. |
| 906 |
|
| 907 |
# return $self; |
| 908 |
# } |
| 909 |
|
| 910 |
# =head3 _seed_from_store |
| 911 |
|
| 912 |
# my $request = $illRequest->_seed_from_store($params); |
| 913 |
|
| 914 |
# Read a Record from the Koha Database. Here, we simply do a db attribute / |
| 915 |
# Illrequest dump and feed that dump into Record structure: column_names => |
| 916 |
# column values. |
| 917 |
|
| 918 |
# =cut |
| 919 |
|
| 920 |
# # FIXME: Unclear what we do here now: We already load our own values, using |
| 921 |
# # ->search or ->find. Maybe here we just need to create record? |
| 922 |
|
| 923 |
# sub _seed_from_store { |
| 924 |
# my ( $self, $opts ) = @_; |
| 925 |
|
| 926 |
# my $result_set = Koha::Database->new->schema->resultset('Illrequest'); |
| 927 |
# my $result = $result_set->find( $opts->{id} ); |
| 928 |
|
| 929 |
# if ( $result ) { |
| 930 |
# my $linked = $result_set->search_related( |
| 931 |
# 'ill_request_attributes', { req_id => $opts->{id} } |
| 932 |
# ); |
| 933 |
# my $attributes = { $result->get_columns }; |
| 934 |
# while ( my $attribute = $linked->next ) { |
| 935 |
# $attributes->{ $attribute->get_column('type') } |
| 936 |
# = $attribute->get_column('value'); |
| 937 |
# } |
| 938 |
# $attributes->{borrower} = _borrower_from_number({ |
| 939 |
# number => $attributes->{borrowernumber}, strategy => 'brw' |
| 940 |
# }); |
| 941 |
# # XXX: A bit Kludgy. |
| 942 |
# my $tmp = $self->_abstract->build($attributes); |
| 943 |
# $self->record($tmp->{record}); |
| 944 |
# $self->status($tmp->{status}); |
| 945 |
# return $self; |
| 946 |
# } |
| 947 |
|
| 948 |
# return 0; |
| 949 |
# } |
| 950 |
|
| 951 |
=head3 requires_moderation |
| 952 |
|
| 953 |
my $status = $illRequest->requires_moderation; |
| 954 |
|
| 955 |
Return the name of the status if moderation by staff is required; or 0 |
| 956 |
otherwise. |
| 957 |
|
| 958 |
=cut |
| 959 |
|
| 960 |
sub requires_moderation { |
| 961 |
my ( $self ) = @_; |
| 962 |
my $require_moderation = { |
| 963 |
'CANCREQ' => 'CANCREQ', |
| 964 |
}; |
| 965 |
return $require_moderation->{$self->status}; |
| 966 |
} |
| 967 |
|
| 968 |
=head3 is_manual_request |
| 969 |
|
| 970 |
my $bool = $illRequest->is_manual_request; |
| 971 |
|
| 972 |
Return 1 if this request is a manually created request, 0 if it was created |
| 973 |
using the API search method. |
| 974 |
|
| 975 |
=cut |
| 976 |
|
| 977 |
# FIXME: Needs Record handling sorting out. Update 18/01/17 I believe manual |
| 978 |
# should be a separate backend, and thus it can be handled just like any other |
| 979 |
# backend. This should be removed once confirmed working. |
| 980 |
sub is_manual_request { |
| 981 |
my ( $self ) = @_; |
| 982 |
return 1 if ( $self->record->property('manual') ); |
| 983 |
return 0 |
| 984 |
} |
| 985 |
|
| 986 |
=head3 place_generic_request |
| 987 |
|
| 988 |
my ( $result, $email ) = $illRequest->place_generic_request($params); |
| 989 |
|
| 990 |
Create an email from $PARAMS and submit it. If we are successful, return 1 |
| 991 |
and the email summary. If not, then return 0 and the email summary. |
| 992 |
|
| 993 |
=cut |
| 994 |
|
| 995 |
sub place_generic_request { |
| 996 |
my ( $self, $params ) = @_; |
| 997 |
|
| 998 |
my $message = Koha::Email->new; |
| 999 |
$params->{to} = join("; ", @{$params->{to}}); |
| 1000 |
if ( !$params->{from} || $params->{from} eq '' ) { |
| 1001 |
die "No originator for email: ", $params->{from}; |
| 1002 |
} |
| 1003 |
if ( !$params->{replyto} || $params->{replyto} eq '') { |
| 1004 |
$params->{replyto} = $params->{from}; |
| 1005 |
} |
| 1006 |
if ( !$params->{sender} || $params->{sender} eq '' ) { |
| 1007 |
$params->{sender} = $params->{from}; |
| 1008 |
} |
| 1009 |
my %mail = $message->create_message_headers( |
| 1010 |
{ |
| 1011 |
to => $params->{to}, |
| 1012 |
from => $params->{from}, |
| 1013 |
replyto => $params->{replyto}, |
| 1014 |
sender => $params->{sender}, |
| 1015 |
subject => Encode::encode( "utf8", $params->{subject} ), |
| 1016 |
message => Encode::encode( "utf8", $params->{message} ), |
| 1017 |
contenttype => 'text/plain; charset="utf8"', |
| 1018 |
} |
| 1019 |
); |
| 1020 |
|
| 1021 |
my $result = sendmail(%mail); |
| 1022 |
if ( $result ) { |
| 1023 |
# XXX: Needs store? |
| 1024 |
$self->status("GENREQ"); |
| 1025 |
return (1, $params); |
| 1026 |
} else { |
| 1027 |
carp($Mail::Sendmail::error); |
| 1028 |
return (0, $params); |
| 1029 |
} |
| 1030 |
|
| 1031 |
} |
| 1032 |
|
| 1033 |
=head3 prepare_generic_request |
| 1034 |
|
| 1035 |
my $emailTemplate = $illRequest->prepare_generic_request; |
| 1036 |
|
| 1037 |
Return a hashref containing 'subject'and 'body' for an email. |
| 1038 |
|
| 1039 |
=cut |
| 1040 |
|
| 1041 |
# FIXME: Needs Record handling sorting out |
| 1042 |
sub prepare_generic_request { |
| 1043 |
my ( $self ) = @_; |
| 1044 |
|
| 1045 |
|
| 1046 |
my $draft->{subject} = "ILL Request"; |
| 1047 |
$draft->{body} = <<EOF; |
| 1048 |
Dear Sir/Madam, |
| 1049 |
|
| 1050 |
We would like to request an interlibrary loan for title matching the |
| 1051 |
following description: |
| 1052 |
|
| 1053 |
EOF |
| 1054 |
|
| 1055 |
my $details = $self->record->getFullDetails; |
| 1056 |
while (my ($key, $values) = each %{$details}) { |
| 1057 |
if (${$values}[1]) { |
| 1058 |
$draft->{body} .= " - " . ${$values}[0] |
| 1059 |
. ": " . ${$values}[1]. "\n"; |
| 1060 |
} |
| 1061 |
} |
| 1062 |
|
| 1063 |
$draft->{body} .= <<EOF; |
| 1064 |
|
| 1065 |
Please let us know if you are able to supply this to us. |
| 1066 |
|
| 1067 |
Kind Regards |
| 1068 |
EOF |
| 1069 |
|
| 1070 |
return $draft; |
| 1071 |
} |
| 1072 |
|
| 1073 |
=head3 _borrower_from_number |
| 1074 |
|
| 1075 |
my $_borrower_from_number = $illRequest->_borrower_from_number(); |
| 1076 |
|
| 1077 |
Return a borrower from the given card or borrower $NUMBER. The strategy for |
| 1078 |
resolution depends on $strategy: |
| 1079 |
- 'crd' means try only cardnumber, error otherwise. |
| 1080 |
- 'brw' means try only borrowernumber, error otherwise. |
| 1081 |
- else: try both and return the first match. |
| 1082 |
|
| 1083 |
=cut |
| 1084 |
|
| 1085 |
sub _borrower_from_number { |
| 1086 |
my ( $params ) = @_; |
| 1087 |
my $number = $params->{number}; |
| 1088 |
my $strategy = $params->{strategy}; |
| 1089 |
|
| 1090 |
my $patrons = Koha::Patrons->new; |
| 1091 |
my $brws; |
| 1092 |
if ( $strategy && 'crd' eq $strategy ) { |
| 1093 |
$brws = $patrons->search( { cardnumber => $number } ); |
| 1094 |
} elsif ( $strategy && 'brw' eq $strategy ) { |
| 1095 |
$brws = $patrons->search( { borrowernumber => $number } ); |
| 1096 |
} else { |
| 1097 |
$brws = $patrons->search( { borrowernumber => $number } ); |
| 1098 |
$brws = $patrons->search( { cardnumber => $number } ) |
| 1099 |
unless ( $brws->count == 1 ); |
| 1100 |
} |
| 1101 |
|
| 1102 |
# we should have a unique brw. |
| 1103 |
die "Invalid borrower" if ( $brws->count > 1 ); |
| 1104 |
|
| 1105 |
# Check if borrower still exists / has not been deleted. |
| 1106 |
if ( $brws->count == 0 ) { |
| 1107 |
die "Invalid borrower" if ( $params->{required} ); |
| 1108 |
# We allow "deleted" borrowers. |
| 1109 |
return { type => "borrower", deleted => 1 }; |
| 1110 |
} |
| 1111 |
return $brws->next; |
| 1112 |
} |
| 1113 |
|
| 1114 |
=head3 id_prefix |
| 1115 |
|
| 1116 |
my $prefix = $record->id_prefix; |
| 1117 |
|
| 1118 |
Return the prefix appropriate for the current Illrequest as derived from the |
| 1119 |
borrower and branch associated with this request's Status, and the config |
| 1120 |
file. |
| 1121 |
|
| 1122 |
=cut |
| 1123 |
|
| 1124 |
sub id_prefix { |
| 1125 |
my ( $self ) = @_; |
| 1126 |
# FIXME: can we automatically use borrowernumber as borrower? |
| 1127 |
my $brw = $self->patron; |
| 1128 |
my $brw_cat = "dummy"; |
| 1129 |
$brw_cat = $brw->categorycode |
| 1130 |
unless ( 'HASH' eq ref($brw) && $brw->{deleted} ); |
| 1131 |
my $prefix = $self->getPrefix( { |
| 1132 |
brw_cat => $brw_cat, |
| 1133 |
branch => $self->branchcode, |
| 1134 |
} ); |
| 1135 |
$prefix .= "-" if ( $prefix ); |
| 1136 |
return $prefix; |
| 1137 |
} |
| 1138 |
|
| 1139 |
=head3 _censor |
| 1140 |
|
| 1141 |
my $params = $illRequest->_censor($params); |
| 1142 |
|
| 1143 |
Return $params, modified to reflect our censorship requirements. |
| 1144 |
|
| 1145 |
=cut |
| 1146 |
|
| 1147 |
sub _censor { |
| 1148 |
my ( $self, $params ) = @_; |
| 1149 |
$params->{censor_notes_staff} = $self->getCensorNotesStaff |
| 1150 |
if ( $params->{opac} ); |
| 1151 |
$params->{display_reply_date} = $self->getDisplayReplyDate; |
| 1152 |
|
| 1153 |
return $params; |
| 1154 |
} |
| 1155 |
|
| 1156 |
=head1 AUTHOR |
| 1157 |
|
| 1158 |
Alex Sassmannshausen <alex.sassmannshausen@ptfs-europe.com> |
| 1159 |
|
| 1160 |
=cut |
| 1161 |
|
| 1162 |
1; |