6c0eb32a08
So that pickup delay can have a different value per patron category, item type or branch. To test: 1) Update database, restart services 2) Set ReservesMaxPickUpDelay syspref (if not already set) 3) Edit your circulation rules and set a value under 'Holds pickup period (day) that is DIFFERENT from ReservesMaxPickUpDelay. Set a few different numbers for different branches as well. 4) Place a hold on a biblio from the staff client. 5) Check in an item from that biblio and confirm the hold as waiting 6) Confirm the expiration date is calculated using the 'Holds pickup period' value instead of the ReservesMaxPickUpDelay syspref 7) Revert the waiting status and delete the hold 8) Re-place the hold on the biblio on the OPAC. Notice that when you change the pick up location, the number of days in the pickup message below the dropdown changes based on the circ rules. 9) Create a holiday with a date that will overlap with the 'Holds pickup period' 10) Check in an item from that biblio and confirm the hold as waiting 11) Confirm the expiration date is calculated using the 'Holds pickup period' value AND considers the special holiday 12) Confirm tests pass t/db_dependent/Holds/WaitingReserves.t 13) Test Talking Tech: 13a) Enable TalkingTechItivaPhoneNotification 13b) Go to Tools -> Notices & slips. Add content to the HOLD phone (itiva) notice. 13c) In your terminal, run perl /path/to/koha/misc/cronjobs/thirdparty/TalkingTech_itiva_outbound.pl -o ~/itiva.tmp -w 0 --type=RESERVE Sponsored-by: Catalyst IT Signed-off-by: Emmi Takkinen <emmi.takkinen@koha-suomi.fi> Signed-off-by: Nick Clemens <nick@bywatersolutions.com> Signed-off-by: Kelly <kelly@bywatersolutions.com> Signed-off-by: Katrin Fischer <katrin.fischer.83@web.de> Signed-off-by: Kelly <kelly@bywatersolutions.com> Signed-off-by: Katrin Fischer <katrin.fischer.83@web.de> Signed-off-by: Tomas Cohen Arazi <tomascohen@theke.io>
766 lines
21 KiB
Perl
766 lines
21 KiB
Perl
package Koha::CirculationRules;
|
|
|
|
# Copyright ByWater Solutions 2017
|
|
#
|
|
# This file is part of Koha.
|
|
#
|
|
# Koha is free software; you can redistribute it and/or modify it
|
|
# under the terms of the GNU General Public License as published by
|
|
# the Free Software Foundation; either version 3 of the License, or
|
|
# (at your option) any later version.
|
|
#
|
|
# Koha is distributed in the hope that it will be useful, but
|
|
# WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
# GNU General Public License for more details.
|
|
#
|
|
# You should have received a copy of the GNU General Public License
|
|
# along with Koha; if not, see <http://www.gnu.org/licenses>.
|
|
|
|
use Modern::Perl;
|
|
use Carp qw( croak );
|
|
|
|
use Koha::Exceptions;
|
|
use Koha::Exceptions::CirculationRule;
|
|
use Koha::CirculationRule;
|
|
use Koha::Caches;
|
|
use Koha::Cache::Memory::Lite;
|
|
use Koha::Number::Price;
|
|
|
|
use base qw(Koha::Objects);
|
|
|
|
use constant GUESSED_ITEMTYPES_KEY => 'Koha_CirculationRules_last_guess';
|
|
|
|
=head1 NAME
|
|
|
|
Koha::CirculationRules - Koha CirculationRule Object set class
|
|
|
|
=head1 API
|
|
|
|
=head2 Class Methods
|
|
|
|
=cut
|
|
|
|
=head3 rule_kinds
|
|
|
|
This structure describes the possible rules that may be set, and what scopes they can be set at.
|
|
|
|
Any attempt to set a rule with a nonsensical scope (for instance, setting the C<patron_maxissueqty> for a branchcode and itemtype), is an error.
|
|
|
|
=cut
|
|
|
|
our $RULE_KINDS = {
|
|
lostreturn => {
|
|
scope => [ 'branchcode' ],
|
|
},
|
|
processingreturn => {
|
|
scope => [ 'branchcode' ],
|
|
},
|
|
patron_maxissueqty => {
|
|
scope => [ 'branchcode', 'categorycode' ],
|
|
},
|
|
patron_maxonsiteissueqty => {
|
|
scope => [ 'branchcode', 'categorycode' ],
|
|
},
|
|
max_holds => {
|
|
scope => [ 'branchcode', 'categorycode' ],
|
|
},
|
|
|
|
holdallowed => {
|
|
scope => [ 'branchcode', 'itemtype' ],
|
|
can_be_blank => 0,
|
|
},
|
|
hold_fulfillment_policy => {
|
|
scope => [ 'branchcode', 'itemtype' ],
|
|
can_be_blank => 0,
|
|
},
|
|
returnbranch => {
|
|
scope => [ 'branchcode', 'itemtype' ],
|
|
can_be_blank => 0,
|
|
},
|
|
|
|
article_requests => {
|
|
scope => [ 'branchcode', 'categorycode', 'itemtype' ],
|
|
},
|
|
article_request_fee => {
|
|
scope => [ 'branchcode', 'categorycode' ],
|
|
is_monetary => 1,
|
|
},
|
|
open_article_requests_limit => {
|
|
scope => [ 'branchcode', 'categorycode' ],
|
|
},
|
|
|
|
auto_renew => {
|
|
scope => [ 'branchcode', 'categorycode', 'itemtype' ],
|
|
},
|
|
cap_fine_to_replacement_price => {
|
|
scope => [ 'branchcode', 'categorycode', 'itemtype' ],
|
|
},
|
|
chargeperiod => {
|
|
scope => [ 'branchcode', 'categorycode', 'itemtype' ],
|
|
},
|
|
chargeperiod_charge_at => {
|
|
scope => [ 'branchcode', 'categorycode', 'itemtype' ],
|
|
},
|
|
fine => {
|
|
scope => [ 'branchcode', 'categorycode', 'itemtype' ],
|
|
is_monetary => 1,
|
|
},
|
|
finedays => {
|
|
scope => [ 'branchcode', 'categorycode', 'itemtype' ],
|
|
},
|
|
firstremind => {
|
|
scope => [ 'branchcode', 'categorycode', 'itemtype' ],
|
|
},
|
|
hardduedate => {
|
|
scope => [ 'branchcode', 'categorycode', 'itemtype' ],
|
|
},
|
|
hardduedatecompare => {
|
|
scope => [ 'branchcode', 'categorycode', 'itemtype' ],
|
|
},
|
|
waiting_hold_cancellation => {
|
|
scope => [ 'branchcode', 'categorycode', 'itemtype' ],
|
|
can_be_blank => 0,
|
|
},
|
|
holds_per_day => {
|
|
scope => [ 'branchcode', 'categorycode', 'itemtype' ],
|
|
},
|
|
holds_per_record => {
|
|
scope => [ 'branchcode', 'categorycode', 'itemtype' ],
|
|
},
|
|
issuelength => {
|
|
scope => [ 'branchcode', 'categorycode', 'itemtype' ],
|
|
},
|
|
daysmode => {
|
|
scope => [ 'branchcode', 'categorycode', 'itemtype' ],
|
|
},
|
|
lengthunit => {
|
|
scope => [ 'branchcode', 'categorycode', 'itemtype' ],
|
|
},
|
|
maxissueqty => {
|
|
scope => [ 'branchcode', 'categorycode', 'itemtype' ],
|
|
},
|
|
maxonsiteissueqty => {
|
|
scope => [ 'branchcode', 'categorycode', 'itemtype' ],
|
|
},
|
|
maxsuspensiondays => {
|
|
scope => [ 'branchcode', 'categorycode', 'itemtype' ],
|
|
},
|
|
no_auto_renewal_after => {
|
|
scope => [ 'branchcode', 'categorycode', 'itemtype' ],
|
|
},
|
|
no_auto_renewal_after_hard_limit => {
|
|
scope => [ 'branchcode', 'categorycode', 'itemtype' ],
|
|
},
|
|
norenewalbefore => {
|
|
scope => [ 'branchcode', 'categorycode', 'itemtype' ],
|
|
},
|
|
noautorenewalbefore => {
|
|
scope => [ 'branchcode', 'categorycode', 'itemtype' ],
|
|
},
|
|
onshelfholds => {
|
|
scope => [ 'branchcode', 'categorycode', 'itemtype' ],
|
|
},
|
|
opacitemholds => {
|
|
scope => [ 'branchcode', 'categorycode', 'itemtype' ],
|
|
},
|
|
overduefinescap => {
|
|
scope => [ 'branchcode', 'categorycode', 'itemtype' ],
|
|
is_monetary => 1,
|
|
can_be_blank => 1,
|
|
},
|
|
renewalperiod => {
|
|
scope => [ 'branchcode', 'categorycode', 'itemtype' ],
|
|
},
|
|
renewalsallowed => {
|
|
scope => [ 'branchcode', 'categorycode', 'itemtype' ],
|
|
},
|
|
unseen_renewals_allowed => {
|
|
scope => [ 'branchcode', 'categorycode', 'itemtype' ],
|
|
},
|
|
rentaldiscount => {
|
|
scope => [ 'branchcode', 'categorycode', 'itemtype' ],
|
|
can_be_blank => 0,
|
|
},
|
|
reservesallowed => {
|
|
scope => [ 'branchcode', 'categorycode', 'itemtype' ],
|
|
},
|
|
suspension_chargeperiod => {
|
|
scope => [ 'branchcode', 'categorycode', 'itemtype' ],
|
|
},
|
|
note => { # This is not really a rule. Maybe we will want to separate this later.
|
|
scope => [ 'branchcode', 'categorycode', 'itemtype' ],
|
|
},
|
|
decreaseloanholds => {
|
|
scope => [ 'branchcode', 'categorycode', 'itemtype' ],
|
|
},
|
|
recalls_allowed => {
|
|
scope => [ 'branchcode', 'categorycode', 'itemtype' ],
|
|
},
|
|
recalls_per_record => {
|
|
scope => [ 'branchcode', 'categorycode', 'itemtype' ],
|
|
},
|
|
on_shelf_recalls => {
|
|
scope => [ 'branchcode', 'categorycode', 'itemtype' ],
|
|
},
|
|
recall_due_date_interval => {
|
|
scope => [ 'branchcode', 'categorycode', 'itemtype' ],
|
|
},
|
|
recall_overdue_fine => {
|
|
scope => [ 'branchcode', 'categorycode', 'itemtype' ],
|
|
is_monetary => 1,
|
|
},
|
|
recall_shelf_time => {
|
|
scope => [ 'branchcode', 'categorycode', 'itemtype' ],
|
|
},
|
|
holds_pickup_period => {
|
|
scope => [ 'branchcode', 'categorycode', 'itemtype' ],
|
|
},
|
|
# Not included (deprecated?):
|
|
# * accountsent
|
|
# * reservecharge
|
|
# * restrictedtype
|
|
};
|
|
|
|
sub rule_kinds {
|
|
return $RULE_KINDS;
|
|
}
|
|
|
|
=head3 get_effective_rule
|
|
|
|
my $effective_rule = Koha::CirculationRules->get_effective_rule(
|
|
{
|
|
rule_name => $name,
|
|
categorycode => $categorycode,
|
|
itemtype => $itemtype,
|
|
branchcode => $branchcode
|
|
}
|
|
);
|
|
|
|
Return the effective rule object for the rule associated with the criteria passed.
|
|
|
|
|
|
=cut
|
|
|
|
sub get_effective_rule {
|
|
my ( $self, $params ) = @_;
|
|
|
|
$params->{categorycode} //= undef;
|
|
$params->{branchcode} //= undef;
|
|
$params->{itemtype} //= undef;
|
|
|
|
my $rule_name = $params->{rule_name};
|
|
my $categorycode = $params->{categorycode};
|
|
my $itemtype = $params->{itemtype};
|
|
my $branchcode = $params->{branchcode};
|
|
|
|
Koha::Exceptions::MissingParameter->throw(
|
|
"Required parameter 'rule_name' missing")
|
|
unless $rule_name;
|
|
|
|
for my $v ( $branchcode, $categorycode, $itemtype ) {
|
|
$v = undef if $v and $v eq '*';
|
|
}
|
|
|
|
my $order_by = $params->{order_by}
|
|
// { -desc => [ 'branchcode', 'categorycode', 'itemtype' ] };
|
|
|
|
my $search_params;
|
|
$search_params->{rule_name} = $rule_name;
|
|
|
|
$search_params->{categorycode} = defined $categorycode ? [ $categorycode, undef ] : undef;
|
|
$search_params->{itemtype} = defined $itemtype ? [ $itemtype, undef ] : undef;
|
|
$search_params->{branchcode} = defined $branchcode ? [ $branchcode, undef ] : undef;
|
|
|
|
my $rule = $self->search(
|
|
$search_params,
|
|
{
|
|
order_by => $order_by,
|
|
rows => 1,
|
|
}
|
|
)->single;
|
|
|
|
return $rule;
|
|
}
|
|
|
|
=head3 get_effective_rule_value
|
|
|
|
my $effective_rule_value = Koha::CirculationRules->get_effective_rule_value(
|
|
{
|
|
rule_name => $name,
|
|
categorycode => $categorycode,
|
|
itemtype => $itemtype,
|
|
branchcode => $branchcode
|
|
}
|
|
);
|
|
|
|
Return the effective value for the rule associated with the criteria passed.
|
|
|
|
This is a cached method so should be used in preference to get_effective_rule where possible
|
|
to aid performance.
|
|
|
|
=cut
|
|
|
|
sub get_effective_rule_value {
|
|
my ( $self, $params ) = @_;
|
|
|
|
my $rule_name = $params->{rule_name};
|
|
my $categorycode = $params->{categorycode};
|
|
my $itemtype = $params->{itemtype};
|
|
my $branchcode = $params->{branchcode};
|
|
|
|
my $memory_cache = Koha::Cache::Memory::Lite->get_instance;
|
|
my $cache_key = sprintf "CircRules:%s:%s:%s:%s", $rule_name // q{},
|
|
$categorycode // q{}, $branchcode // q{}, $itemtype // q{};
|
|
|
|
my $cached = $memory_cache->get_from_cache($cache_key);
|
|
return $cached if $cached;
|
|
|
|
my $rule = $self->get_effective_rule($params);
|
|
|
|
my $value= $rule ? $rule->rule_value : undef;
|
|
$memory_cache->set_in_cache( $cache_key, $value );
|
|
return $value;
|
|
}
|
|
|
|
=head3 get_effective_rules
|
|
|
|
=cut
|
|
|
|
sub get_effective_rules {
|
|
my ( $self, $params ) = @_;
|
|
|
|
my $rules = $params->{rules};
|
|
my $categorycode = $params->{categorycode};
|
|
my $itemtype = $params->{itemtype};
|
|
my $branchcode = $params->{branchcode};
|
|
|
|
my $r;
|
|
foreach my $rule (@$rules) {
|
|
my $effective_rule = $self->get_effective_rule_value(
|
|
{
|
|
rule_name => $rule,
|
|
categorycode => $categorycode,
|
|
itemtype => $itemtype,
|
|
branchcode => $branchcode,
|
|
}
|
|
);
|
|
|
|
$r->{$rule} = $effective_rule if defined $effective_rule;
|
|
}
|
|
|
|
return $r;
|
|
}
|
|
|
|
=head3 set_rule
|
|
|
|
=cut
|
|
|
|
sub set_rule {
|
|
my ( $self, $params ) = @_;
|
|
|
|
for my $mandatory_parameter (qw( rule_name rule_value ) ) {
|
|
Koha::Exceptions::MissingParameter->throw(
|
|
"Required parameter '$mandatory_parameter' missing")
|
|
unless exists $params->{$mandatory_parameter};
|
|
}
|
|
|
|
my $kind_info = $RULE_KINDS->{ $params->{rule_name} };
|
|
Koha::Exceptions::MissingParameter->throw(
|
|
"set_rule given unknown rule '$params->{rule_name}'!")
|
|
unless defined $kind_info;
|
|
|
|
# Enforce scope; a rule should be set for its defined scope, no more, no less.
|
|
foreach my $scope_level ( qw( branchcode categorycode itemtype ) ) {
|
|
if ( grep /$scope_level/, @{ $kind_info->{scope} } ) {
|
|
croak "set_rule needs '$scope_level' to set '$params->{rule_name}'!"
|
|
unless exists $params->{$scope_level};
|
|
} else {
|
|
croak "set_rule cannot set '$params->{rule_name}' for a '$scope_level'!"
|
|
if exists $params->{$scope_level};
|
|
}
|
|
}
|
|
|
|
my $branchcode = $params->{branchcode};
|
|
my $categorycode = $params->{categorycode};
|
|
my $itemtype = $params->{itemtype};
|
|
my $rule_name = $params->{rule_name};
|
|
my $rule_value = $params->{rule_value};
|
|
my $can_be_blank = defined $kind_info->{can_be_blank} ? $kind_info->{can_be_blank} : 1;
|
|
$rule_value = undef if defined $rule_value && $rule_value eq "" && !$can_be_blank;
|
|
my $is_monetary = defined $kind_info->{is_monetary} ? $kind_info->{is_monetary} : 0;
|
|
Koha::Exceptions::CirculationRule::NotDecimal->throw( name => $rule_name, value => $rule_value )
|
|
if ( $is_monetary && defined($rule_value) && $rule_value ne '' && $rule_value !~ /^\d+(\.\d+)?$/ );
|
|
|
|
for my $v ( $branchcode, $categorycode, $itemtype ) {
|
|
$v = undef if $v and $v eq '*';
|
|
}
|
|
my $rule = $self->search(
|
|
{
|
|
rule_name => $rule_name,
|
|
branchcode => $branchcode,
|
|
categorycode => $categorycode,
|
|
itemtype => $itemtype,
|
|
}
|
|
)->next();
|
|
|
|
if ($rule) {
|
|
if ( defined $rule_value ) {
|
|
$rule->rule_value($rule_value);
|
|
$rule->update();
|
|
}
|
|
else {
|
|
$rule->delete();
|
|
}
|
|
}
|
|
else {
|
|
if ( defined $rule_value ) {
|
|
$rule = Koha::CirculationRule->new(
|
|
{
|
|
branchcode => $branchcode,
|
|
categorycode => $categorycode,
|
|
itemtype => $itemtype,
|
|
rule_name => $rule_name,
|
|
rule_value => $rule_value,
|
|
}
|
|
);
|
|
$rule->store();
|
|
}
|
|
}
|
|
|
|
my $memory_cache = Koha::Cache::Memory::Lite->get_instance;
|
|
for my $k ( $memory_cache->all_keys ) {
|
|
$memory_cache->clear_from_cache($k) if $k =~ m{^CircRules:};
|
|
}
|
|
|
|
return $rule;
|
|
}
|
|
|
|
=head3 set_rules
|
|
|
|
=cut
|
|
|
|
sub set_rules {
|
|
my ( $self, $params ) = @_;
|
|
|
|
my %set_params;
|
|
$set_params{branchcode} = $params->{branchcode} if exists $params->{branchcode};
|
|
$set_params{categorycode} = $params->{categorycode} if exists $params->{categorycode};
|
|
$set_params{itemtype} = $params->{itemtype} if exists $params->{itemtype};
|
|
my $rules = $params->{rules};
|
|
|
|
my $rule_objects = [];
|
|
while ( my ( $rule_name, $rule_value ) = each %$rules ) {
|
|
my $rule_object = Koha::CirculationRules->set_rule(
|
|
{
|
|
%set_params,
|
|
rule_name => $rule_name,
|
|
rule_value => $rule_value,
|
|
}
|
|
);
|
|
push( @$rule_objects, $rule_object );
|
|
}
|
|
|
|
return $rule_objects;
|
|
}
|
|
|
|
=head3 delete
|
|
|
|
Delete a set of circulation rules, needed for cleaning up when deleting issuingrules
|
|
|
|
=cut
|
|
|
|
sub delete {
|
|
my ( $self ) = @_;
|
|
|
|
while ( my $rule = $self->next ){
|
|
$rule->delete;
|
|
}
|
|
}
|
|
|
|
=head3 clone
|
|
|
|
Clone a set of circulation rules to another branch
|
|
|
|
=cut
|
|
|
|
sub clone {
|
|
my ( $self, $to_branch ) = @_;
|
|
|
|
while ( my $rule = $self->next ){
|
|
$rule->clone($to_branch);
|
|
}
|
|
}
|
|
|
|
=head2 get_return_branch_policy
|
|
|
|
my $returnbranch = Koha::CirculationRules->get_return_branch_policy($item);
|
|
|
|
Returns the branch to use for returning the item based on the
|
|
item type, and a branch selected via CircControlReturnsBranch.
|
|
|
|
The return value is the branch to which to return the item. Possible values:
|
|
noreturn: do not return, let item remain where checked in (floating collections)
|
|
homebranch: return to item's home branch
|
|
holdingbranch: return to issuer branch
|
|
|
|
This searches branchitemrules in the following order:
|
|
* Same branchcode and itemtype
|
|
* Same branchcode, itemtype '*'
|
|
* branchcode '*', same itemtype
|
|
* branchcode '*' and itemtype '*'
|
|
|
|
=cut
|
|
|
|
sub get_return_branch_policy {
|
|
my ( $self, $item ) = @_;
|
|
|
|
my $pref = C4::Context->preference('CircControlReturnsBranch');
|
|
|
|
my $branchcode =
|
|
$pref eq 'ItemHomeLibrary' ? $item->homebranch
|
|
: $pref eq 'ItemHoldingLibrary' ? $item->holdingbranch
|
|
: $pref eq 'CheckInLibrary' ? C4::Context->userenv
|
|
? C4::Context->userenv->{branch}
|
|
: $item->homebranch
|
|
: $item->homebranch;
|
|
|
|
my $itemtype = $item->effective_itemtype;
|
|
|
|
my $rule = Koha::CirculationRules->get_effective_rule(
|
|
{
|
|
rule_name => 'returnbranch',
|
|
itemtype => $itemtype,
|
|
branchcode => $branchcode,
|
|
}
|
|
);
|
|
|
|
return $rule ? $rule->rule_value : 'homebranch';
|
|
}
|
|
|
|
|
|
=head3 get_opacitemholds_policy
|
|
|
|
my $can_place_a_hold_at_item_level = Koha::CirculationRules->get_opacitemholds_policy( { patron => $patron, item => $item } );
|
|
|
|
Return 'Y' or 'F' if the patron can place a hold on this item according to the issuing rules
|
|
and the "Item level holds" (opacitemholds).
|
|
Can be 'N' - Don't allow, 'Y' - Allow, and 'F' - Force
|
|
|
|
=cut
|
|
|
|
sub get_opacitemholds_policy {
|
|
my ( $class, $params ) = @_;
|
|
|
|
my $item = $params->{item};
|
|
my $patron = $params->{patron};
|
|
|
|
return unless $item or $patron;
|
|
|
|
my $rule = Koha::CirculationRules->get_effective_rule(
|
|
{
|
|
categorycode => $patron->categorycode,
|
|
itemtype => $item->effective_itemtype,
|
|
branchcode => $item->homebranch,
|
|
rule_name => 'opacitemholds',
|
|
}
|
|
);
|
|
|
|
return $rule ? $rule->rule_value : undef;
|
|
}
|
|
|
|
=head3 get_onshelfholds_policy
|
|
|
|
my $on_shelf_holds = Koha::CirculationRules->get_onshelfholds_policy({ item => $item, patron => $patron });
|
|
|
|
=cut
|
|
|
|
sub get_onshelfholds_policy {
|
|
my ( $class, $params ) = @_;
|
|
my $item = $params->{item};
|
|
my $itemtype = $item->effective_itemtype;
|
|
my $patron = $params->{patron};
|
|
my $rule = Koha::CirculationRules->get_effective_rule(
|
|
{
|
|
categorycode => ( $patron ? $patron->categorycode : undef ),
|
|
itemtype => $itemtype,
|
|
branchcode => $item->holdingbranch,
|
|
rule_name => 'onshelfholds',
|
|
}
|
|
);
|
|
return $rule ? $rule->rule_value : 0;
|
|
}
|
|
|
|
=head3 get_lostreturn_policy
|
|
|
|
my $lost_proc_refund_policy = Koha::CirculationRules->get_lostreturn_policy( { return_branch => $return_branch, item => $item } );
|
|
|
|
lostreturn return values are:
|
|
|
|
=over 2
|
|
|
|
=item '0' - Do not refund
|
|
|
|
=item 'refund' - Refund the lost item charge
|
|
|
|
=item 'restore' - Refund the lost item charge and restore the original overdue fine
|
|
|
|
=item 'charge' - Refund the lost item charge and charge a new overdue fine
|
|
|
|
=back
|
|
|
|
processing return return values are:
|
|
|
|
=over 2
|
|
|
|
=item '0' - Do not refund
|
|
|
|
=item 'refund' - Refund the lost item processing charge
|
|
|
|
=item 'restore' - Refund the lost item processing charge and restore the original overdue fine
|
|
|
|
=item 'charge' - Refund the lost item processing charge and charge a new overdue fine
|
|
|
|
=back
|
|
|
|
|
|
=cut
|
|
|
|
sub get_lostreturn_policy {
|
|
my ( $class, $params ) = @_;
|
|
|
|
my $item = $params->{item};
|
|
|
|
my $behaviour = C4::Context->preference( 'RefundLostOnReturnControl' ) // 'CheckinLibrary';
|
|
my $behaviour_mapping = {
|
|
CheckinLibrary => $params->{'return_branch'} // $item->homebranch,
|
|
ItemHomeBranch => $item->homebranch,
|
|
ItemHoldingBranch => $item->holdingbranch
|
|
};
|
|
|
|
my $branch = $behaviour_mapping->{ $behaviour };
|
|
|
|
my $rules = Koha::CirculationRules->get_effective_rules(
|
|
{
|
|
branchcode => $branch,
|
|
rules => ['lostreturn','processingreturn']
|
|
}
|
|
);
|
|
|
|
$rules->{lostreturn} //= 'refund';
|
|
$rules->{processingreturn} //= 'refund';
|
|
return $rules;
|
|
}
|
|
|
|
=head3 article_requestable_rules
|
|
|
|
Return rules that allow article requests, optionally filtered by
|
|
patron categorycode.
|
|
|
|
Use with care; see guess_article_requestable_itemtypes.
|
|
|
|
=cut
|
|
|
|
sub article_requestable_rules {
|
|
my ( $class, $params ) = @_;
|
|
my $category = $params->{categorycode};
|
|
|
|
return if !C4::Context->preference('ArticleRequests');
|
|
return $class->search({
|
|
$category ? ( categorycode => [ $category, undef ] ) : (),
|
|
rule_name => 'article_requests',
|
|
rule_value => { '!=' => 'no' },
|
|
});
|
|
}
|
|
|
|
=head3 guess_article_requestable_itemtypes
|
|
|
|
Return item types in a hashref that are likely possible to be
|
|
'article requested'. Constructed by an intelligent guess in the
|
|
issuing rules (see article_requestable_rules).
|
|
|
|
Note: pref ArticleRequestsLinkControl overrides the algorithm.
|
|
|
|
Optional parameters: categorycode.
|
|
|
|
Note: the routine is used in opac-search to obtain a reasonable
|
|
estimate within performance borders (not looking at all items but
|
|
just using default itemtype). Also we are not looking at the
|
|
branchcode here, since home or holding branch of the item is
|
|
leading and branch may be unknown too (anonymous opac session).
|
|
|
|
=cut
|
|
|
|
sub guess_article_requestable_itemtypes {
|
|
my ( $class, $params ) = @_;
|
|
my $category = $params->{categorycode};
|
|
return {} if !C4::Context->preference('ArticleRequests');
|
|
return { '*' => 1 } if C4::Context->preference('ArticleRequestsLinkControl') eq 'always';
|
|
|
|
my $cache = Koha::Caches->get_instance;
|
|
my $last_article_requestable_guesses = $cache->get_from_cache(GUESSED_ITEMTYPES_KEY);
|
|
my $key = $category || '*';
|
|
return $last_article_requestable_guesses->{$key}
|
|
if $last_article_requestable_guesses && exists $last_article_requestable_guesses->{$key};
|
|
|
|
my $res = {};
|
|
my $rules = $class->article_requestable_rules({
|
|
$category ? ( categorycode => $category ) : (),
|
|
});
|
|
return $res if !$rules;
|
|
foreach my $rule ( $rules->as_list ) {
|
|
$res->{ $rule->itemtype // '*' } = 1;
|
|
}
|
|
$last_article_requestable_guesses->{$key} = $res;
|
|
$cache->set_in_cache(GUESSED_ITEMTYPES_KEY, $last_article_requestable_guesses);
|
|
return $res;
|
|
}
|
|
|
|
=head3 get_effective_daysmode
|
|
|
|
Return the value for daysmode defined in the circulation rules.
|
|
If not defined (or empty string), the value of the system preference useDaysMode is returned
|
|
|
|
=cut
|
|
|
|
sub get_effective_daysmode {
|
|
my ( $class, $params ) = @_;
|
|
|
|
my $categorycode = $params->{categorycode};
|
|
my $itemtype = $params->{itemtype};
|
|
my $branchcode = $params->{branchcode};
|
|
|
|
my $daysmode_rule = $class->get_effective_rule(
|
|
{
|
|
categorycode => $categorycode,
|
|
itemtype => $itemtype,
|
|
branchcode => $branchcode,
|
|
rule_name => 'daysmode',
|
|
}
|
|
);
|
|
|
|
return ( defined($daysmode_rule)
|
|
and $daysmode_rule->rule_value ne '' )
|
|
? $daysmode_rule->rule_value
|
|
: C4::Context->preference('useDaysMode');
|
|
|
|
}
|
|
|
|
|
|
=head3 type
|
|
|
|
=cut
|
|
|
|
sub _type {
|
|
return 'CirculationRule';
|
|
}
|
|
|
|
=head3 object_class
|
|
|
|
=cut
|
|
|
|
sub object_class {
|
|
return 'Koha::CirculationRule';
|
|
}
|
|
|
|
1;
|