Bugzilla – Attachment 63347 Details for
Bug 17712
Move availability calculation to the Koha namespace
Home
|
New
|
Browse
|
Search
|
[?]
|
Reports
|
Help
|
New Account
|
Log In
[x]
|
Forgot Password
Login:
[x]
[patch]
Bug 17712: Item availability for hold
Bug-17712-Item-availability-for-hold.patch (text/plain), 63.68 KB, created by
Benjamin Rokseth
on 2017-05-10 13:40:12 UTC
(
hide
)
Description:
Bug 17712: Item availability for hold
Filename:
MIME Type:
Creator:
Benjamin Rokseth
Created:
2017-05-10 13:40:12 UTC
Size:
63.68 KB
patch
obsolete
>From e8a4b0245354446a008affdf9cd645b8272e193e Mon Sep 17 00:00:00 2001 >From: Lari Taskula <lari.taskula@jns.fi> >Date: Fri, 18 Nov 2016 13:10:01 +0200 >Subject: [PATCH] Bug 17712: Item availability for hold > >Adds Koha::Item::Availability::Hold class for item hold availability. > >This patch adds item holdability queries in two contexts: >1. in_opac (perspective of patron themselves) >2. in_intranet (perspective of a librarian to patron) > >Returns a Koha::Item::Availability::Hold object, which contains all information on >availability for hold in that context. This may include additional notes, reasons >to ask for confirmation or reasons for unavailability. > >Example: To find out if patron can hold an item in OPAC, we will write: > >my $availability = Koha::Availability::Hold->item({ > patron => $patron, > item => $item, > to_branch => $branchcode, # transfer allowed from holdingbranch to to_branch? >})->in_opac; >if ($availability->available) { > # yes! >} else { > foreach my $reason (keys %{$availability->unavailabilities}) { > # each reason for unavailability > } > foreach my $reason (keys %{$availability->confirmations}) { > # each reason that requires confirmation > } > foreach my $reason (keys %{$availability->notes}) { > # each additional note > } >} > >Signed-off-by: Benjamin Rokseth <benjamin.rokseth@kul.oslo.kommune.no> >--- > Koha/Availability/Hold.pm | 38 +++ > Koha/Item/Availability/Hold.pm | 364 ++++++++++++++++++++ > .../Hold/Intranet/HoldPolicyOverride.t | 90 +++++ > .../Koha/Item/Availability/Hold/Opac/HoldRules.t | 290 ++++++++++++++++ > .../Koha/Item/Availability/Hold/Opac/ItemStatus.t | 371 +++++++++++++++++++++ > .../Item/Availability/Hold/Opac/LibraryItemRules.t | 260 +++++++++++++++ > 6 files changed, 1413 insertions(+) > create mode 100644 Koha/Availability/Hold.pm > create mode 100644 Koha/Item/Availability/Hold.pm > create mode 100644 t/db_dependent/Koha/Item/Availability/Hold/Intranet/HoldPolicyOverride.t > create mode 100644 t/db_dependent/Koha/Item/Availability/Hold/Opac/HoldRules.t > create mode 100644 t/db_dependent/Koha/Item/Availability/Hold/Opac/ItemStatus.t > create mode 100644 t/db_dependent/Koha/Item/Availability/Hold/Opac/LibraryItemRules.t > >diff --git a/Koha/Availability/Hold.pm b/Koha/Availability/Hold.pm >new file mode 100644 >index 0000000..f2d7cb9 >--- /dev/null >+++ b/Koha/Availability/Hold.pm >@@ -0,0 +1,38 @@ >+package Koha::Availability::Hold; >+ >+# Copyright Koha-Suomi Oy 2016 >+# >+# 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 Koha::Item::Availability::Hold; >+ >+use Koha::Exceptions; >+ >+sub new { >+ my ($class, $params) = @_; >+ >+ bless $params, $class; >+} >+ >+sub item { >+ my ($self, $params) = @_; >+ >+ return Koha::Item::Availability::Hold->new($params); >+} >+ >+1; >diff --git a/Koha/Item/Availability/Hold.pm b/Koha/Item/Availability/Hold.pm >new file mode 100644 >index 0000000..0f423b7 >--- /dev/null >+++ b/Koha/Item/Availability/Hold.pm >@@ -0,0 +1,364 @@ >+package Koha::Item::Availability::Hold; >+ >+# Copyright Koha-Suomi Oy 2016 >+# >+# 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 base qw(Koha::Item::Availability); >+ >+use Koha::Items; >+use Koha::Patrons; >+ >+use Koha::Availability::Checks::Biblio; >+use Koha::Availability::Checks::Biblioitem; >+use Koha::Availability::Checks::IssuingRule; >+use Koha::Availability::Checks::Item; >+use Koha::Availability::Checks::LibraryItemRule; >+use Koha::Availability::Checks::Patron; >+ >+=head1 NAME >+ >+Koha::Item::Availability::Hold - Koha Item Availability Hold object class >+ >+=head1 SYNOPSIS >+ >+my $holdability = Koha::Item::Availability::Hold->new({ >+ item => $item, # which item this availability is for >+ patron => $patron, # check item availability for this patron >+}) >+ >+=head1 DESCRIPTION >+ >+Class for checking item hold availability. >+ >+This class contains different levels of "recipes" that determine whether or not >+an item should be considered available. >+ >+=head2 Class Methods >+ >+=cut >+ >+=head3 new >+ >+Constructs an item hold availability object. Item is always required. Patron is >+required if patron related checks are needed. >+ >+MANDATORY PARAMETERS >+ >+ item (or itemnumber) >+ >+Item is a Koha::Item -object. >+ >+OPTIONAL PARAMETERS >+ >+ patron (or borrowernumber) >+ to_branch >+ >+Patron is a Koha::Patron -object. To_branch is a branchcode of pickup library. >+ >+Returns a Koha::Item::Availability::Hold -object. >+ >+=cut >+ >+sub new { >+ my ($class, $params) = @_; >+ >+ my $self = $class->SUPER::new($params); >+ >+ # Additionally, consider any transfer limits to pickup library by >+ # providing to_branch parameter with branchcode of pickup library >+ $self->{'to_branch'} = $params->{'to_branch'}; >+ >+ return $self; >+} >+ >+sub in_intranet { >+ my ($self) = @_; >+ my $reason; >+ >+ $self->reset; >+ >+ my $item = $self->item; >+ my $patron; >+ unless ($patron = $self->patron) { >+ Koha::Exceptions::MissingParameter->throw( >+ error => 'Missing parameter patron. This level of availability query ' >+ .'requires Koha::Item::Availability::Hold to have a patron parameter.' >+ ); >+ } >+ >+ $self->common_biblio_checks; >+ $self->common_biblioitem_checks; >+ $self->common_issuing_rule_checks; >+ $self->common_item_checks; >+ $self->common_library_item_rule_checks; >+ $self->common_patron_checks; >+ >+ # Additionally, a librarian can override any unavailabilities if system >+ # preference AllowHoldPolicyOverride is enabled >+ if (C4::Context->preference('AllowHoldPolicyOverride')) { >+ # Copy unavailabilities to reasons to ask for confirmation, and reset >+ # reasons of unavailabilities >+ $self->confirmations({ %{$self->unavailabilities}, %{$self->confirmations} }); >+ $self->unavailabilities({}); >+ $self->available(1); >+ } >+ >+ return $self; >+} >+ >+sub in_opac { >+ my ($self) = @_; >+ my $reason; >+ >+ $self->reset; >+ >+ my $item = $self->item; >+ my $patron; >+ unless ($patron = $self->patron) { >+ Koha::Exceptions::MissingParameter->throw( >+ error => 'Missing parameter patron. This level of availability query ' >+ .'requires Koha::Item::Availability::Hold to have a patron parameter.' >+ ); >+ } >+ >+ # Check if holds are allowed in OPAC >+ if (!C4::Context->preference('RequestOnOpac')) { >+ $self->unavailable(Koha::Exceptions::Hold::NotAllowedInOPAC->new); >+ return $self; >+ } >+ >+ $self->common_biblio_checks; >+ $self->common_biblioitem_checks; >+ $self->common_issuing_rule_checks; >+ $self->common_item_checks; >+ $self->common_library_item_rule_checks; >+ $self->common_patron_checks; >+ $self->opac_specific_issuing_rule_checks; >+ >+ return $self; >+} >+ >+=head3 common_biblio_checks >+ >+Common checks for both OPAC and intranet. >+ >+=cut >+ >+sub common_biblio_checks { >+ my ($self, $biblio) = @_; >+ my $reason; >+ >+ unless ($biblio) { >+ $biblio = Koha::Biblios->find($self->item->biblionumber); >+ } >+ >+ my $bibcalc = Koha::Availability::Checks::Biblio->new($biblio); >+ >+ if ($reason = $bibcalc->forbid_holds_on_patrons_possessions($self->patron)) { >+ $self->unavailable($reason); >+ } >+ >+ return $self; >+} >+ >+=head3 common_biblioitem_checks >+ >+Common checks for both OPAC and intranet. >+ >+=cut >+ >+sub common_biblioitem_checks { >+ my ($self, $bibitem) = @_; >+ my $reason; >+ >+ unless ($bibitem) { >+ $bibitem = Koha::Biblioitems->find($self->item->biblioitemnumber); >+ } >+ >+ my $bibitemcalc = Koha::Availability::Checks::Biblioitem->new($bibitem); >+ >+ if ($reason = $bibitemcalc->age_restricted($self->patron)) { >+ $self->unavailable($reason); >+ } >+ >+ return $self; >+} >+ >+=head3 common_issuing_rule_checks >+ >+Common checks for both OPAC and intranet. >+ >+=cut >+ >+sub common_issuing_rule_checks { >+ my ($self, $params) = @_; >+ my $reason; >+ >+ my $item = $self->item; >+ my $patron = $self->patron; >+ my $branchcode = $params->{'branchcode'} ? $params->{'branchcode'} >+ : $self->_get_reservescontrol_branchcode($item, $patron); >+ my $holdrulecalc = Koha::Availability::Checks::IssuingRule->new({ >+ item => $item, >+ patron => $patron, >+ branchcode => $branchcode, >+ use_cache => $params->{'use_cache'}, >+ }); >+ >+ if ($reason = $holdrulecalc->zero_holds_allowed) { >+ $self->unavailable($reason); >+ } else { >+ if ($reason = $holdrulecalc->maximum_holds_reached) { >+ $self->unavailable($reason); >+ } >+ if ($reason = $holdrulecalc->maximum_holds_for_record_reached($params)) { >+ $self->unavailable($reason); >+ } >+ } >+ >+ return $self; >+} >+ >+=head3 common_item_checks >+ >+Common checks for both OPAC and intranet. >+ >+=cut >+ >+sub common_item_checks { >+ my ($self, $params) = @_; >+ my $reason; >+ >+ my $item = $self->item; >+ my $patron = $self->patron; >+ my $itemcalc = Koha::Availability::Checks::Item->new($item); >+ >+ $self->unavailable($reason) if $reason = $itemcalc->damaged; >+ $self->unavailable($reason) if $reason = $itemcalc->lost; >+ $self->unavailable($reason) if $reason = $itemcalc->restricted; >+ $self->unavailable($reason) if $reason = $itemcalc->unknown_barcode; >+ $self->unavailable($reason) if $reason = $itemcalc->withdrawn; >+ if ($reason = $itemcalc->notforloan) { >+ unless ($reason->status < 0) { >+ $self->unavailable($reason); >+ } else { >+ $self->note($reason); >+ } >+ } >+ $self->unavailable($reason) if $reason = $itemcalc->held_by_patron($patron, $params); >+ $self->unavailable($reason) if $reason = $itemcalc->from_another_library; >+ if ($self->to_branch && ($reason = $itemcalc->transfer_limit($self->to_branch))) { >+ $self->unavailable($reason); >+ } >+ >+ return $self; >+} >+ >+=head3 common_library_item_rule_checks >+ >+Common checks for both OPAC and intranet. >+ >+=cut >+ >+sub common_library_item_rule_checks { >+ my ($self) = @_; >+ my $reason; >+ >+ my $item = $self->item; >+ my $patron = $self->patron; >+ my $libitemrule = Koha::Availability::Checks::LibraryItemRule->new({ >+ item => $item, >+ patron => $patron, >+ }); >+ >+ if ($reason = $libitemrule->hold_not_allowed_by_library) { >+ $self->unavailable($reason); >+ } elsif ($reason = $libitemrule->hold_not_allowed_by_other_library) { >+ $self->unavailable($reason); >+ } >+ >+ return $self; >+} >+ >+=head3 common_patron_checks >+ >+Common checks for both OPAC and intranet. >+ >+=cut >+ >+sub common_patron_checks { >+ my ($self) = @_; >+ my $reason; >+ >+ my $patron = $self->patron; >+ my $patroncalc = Koha::Availability::Checks::Patron->new($patron); >+ >+ $self->unavailable($reason) if $reason = $patroncalc->debt_hold; >+ $self->unavailable($reason) if $reason = $patroncalc->debarred; >+ $self->unavailable($reason) if $reason = $patroncalc->exceeded_maxreserves; >+ $self->unavailable($reason) if $reason = $patroncalc->gonenoaddress; >+ $self->unavailable($reason) if $reason = $patroncalc->lost; >+ if (($reason = $patroncalc->expired) >+ && C4::Context->preference('BlockExpiredPatronOpacActions')) { >+ $self->unavailable($reason); >+ } >+ >+ return $self; >+} >+ >+=head3 opac_specific_checks >+ >+OPAC-specific holdability checks. >+ >+=cut >+ >+sub opac_specific_issuing_rule_checks { >+ my ($self, $branchcode) = @_; >+ my $reason; >+ >+ my $item = $self->item; >+ my $patron = $self->patron; >+ $branchcode ||= $self->_get_reservescontrol_branchcode($item, $patron); >+ my $holdrulecalc = Koha::Availability::Checks::IssuingRule->new({ >+ item => $item, >+ patron => $patron, >+ branchcode => $branchcode, >+ }); >+ if ($reason = $holdrulecalc->opac_item_level_hold_forbidden) { >+ $self->unavailable($reason); >+ } >+ >+ return $self; >+} >+ >+ >+sub _get_reservescontrol_branchcode { >+ my ($self, $item, $patron) = @_; >+ >+ my $branchcode; >+ my $controlbranch = C4::Context->preference('ReservesControlBranch'); >+ if ($patron && $controlbranch eq 'PatronLibrary') { >+ $branchcode = $patron->branchcode; >+ } elsif ($item && $controlbranch eq 'ItemHomeLibrary') { >+ $branchcode = $item->homebranch; >+ } >+ return $branchcode; >+} >+ >+1; >diff --git a/t/db_dependent/Koha/Item/Availability/Hold/Intranet/HoldPolicyOverride.t b/t/db_dependent/Koha/Item/Availability/Hold/Intranet/HoldPolicyOverride.t >new file mode 100644 >index 0000000..901e2f2 >--- /dev/null >+++ b/t/db_dependent/Koha/Item/Availability/Hold/Intranet/HoldPolicyOverride.t >@@ -0,0 +1,90 @@ >+#!/usr/bin/perl >+ >+# Copyright Koha-Suomi Oy 2016 >+# >+# 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 Test::More tests => 9; >+use t::lib::Mocks; >+use t::lib::TestBuilder; >+require t::db_dependent::Koha::Availability::Helpers; >+ >+use Koha::Database; >+use Koha::IssuingRules; >+use Koha::Items; >+use Koha::ItemTypes; >+ >+use Koha::Item::Availability::Hold; >+ >+my $schema = Koha::Database->new->schema; >+$schema->storage->txn_begin; >+ >+my $builder = t::lib::TestBuilder->new; >+ >+set_default_system_preferences(); >+set_default_circulation_rules(); >+ >+t::lib::Mocks::mock_preference('AllowHoldPolicyOverride', 1); >+t::lib::Mocks::mock_preference('AllowHoldsOnDamagedItems', 0); >+ >+# Create test item and patron, and add some reasons that will be need confirmation >+my $item = build_a_test_item(); >+$item->set({ >+ barcode => '', >+ damaged => 1, >+ itemlost => 1, >+ notforloan => 1, >+ restricted => 1, >+ withdrawn => 1, >+})->store; # 6 reasons >+my $patron = build_a_test_patron(); >+Koha::Account::Line->new({ >+ borrowernumber => $patron->borrowernumber, >+ amountoutstanding => 999999999, >+ accounttype => 'F', >+})->store; # 1 reason >+ >+Koha::IssuingRules->search->delete; >+my $rule = Koha::IssuingRule->new({ >+ branchcode => $item->homebranch, >+ itemtype => $item->effective_itemtype, >+ categorycode => '*', >+ holds_per_record => 0, >+ reservesallowed => 0, >+})->store; # 1 reason >+my $availability = Koha::Item::Availability::Hold->new({item => $item, patron => $patron})->in_intranet; >+ok($availability->can('in_intranet'), 'Attempt to check availability in intranet' >+ .' while considering AllowHoldPolicyOverride system preference.'); >+is(C4::Context->preference('AllowHoldPolicyOverride'), 1, 'Given librarians are ' >+ .'allowed to override hold policy restrictions.'); >+ok($availability->available, 'When librarian checks item availability for ' >+ .'patron, they see that the status is available.'); >+ok(!$availability->unavailable, 'There are no reasons for unavailability.'); >+is($availability->confirm, 8, 'There are 8 things to be confirmed.'); >+ >+t::lib::Mocks::mock_preference('AllowHoldPolicyOverride', 0); >+$availability = Koha::Item::Availability::Hold->new({item => $item, patron => $patron})->in_intranet; >+is(C4::Context->preference('AllowHoldPolicyOverride'), 0, 'Changed setting - ' >+ .' librarians are no long allowed to override hold policy restrictions.'); >+ok(!$availability->available, 'When librarian checks item availability for ' >+ .'patron, they see that the it is NOT available.'); >+ok(!$availability->confirm, 'There are no to be confirmed.'); >+is($availability->unavailable, 8, 'There are 8 reasons for unavailability.'); >+ >+$schema->storage->txn_rollback; >+ >+1; >diff --git a/t/db_dependent/Koha/Item/Availability/Hold/Opac/HoldRules.t b/t/db_dependent/Koha/Item/Availability/Hold/Opac/HoldRules.t >new file mode 100644 >index 0000000..eeacd1d >--- /dev/null >+++ b/t/db_dependent/Koha/Item/Availability/Hold/Opac/HoldRules.t >@@ -0,0 +1,290 @@ >+#!/usr/bin/perl >+ >+# Copyright Koha-Suomi Oy 2016 >+# >+# 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 Test::More tests => 5; >+use t::lib::Mocks; >+use t::lib::TestBuilder; >+require t::db_dependent::Koha::Availability::Helpers; >+ >+use Koha::Database; >+use Koha::IssuingRules; >+use Koha::Items; >+use Koha::ItemTypes; >+ >+use Koha::Item::Availability::Hold; >+ >+my $schema = Koha::Database->new->schema; >+$schema->storage->txn_begin; >+ >+my $builder = t::lib::TestBuilder->new; >+ >+set_default_system_preferences(); >+set_default_circulation_rules(); >+ >+subtest 'Given there are no hold rules blocking a hold from me' => \&t_hold_rules_nothing_blocking; >+sub t_hold_rules_nothing_blocking { >+ plan tests => 7; >+ >+ my $item = build_a_test_item(); >+ my $patron = build_a_test_patron(); >+ Koha::IssuingRules->search->delete; >+ my $rule = Koha::IssuingRule->new({ >+ branchcode => $patron->branchcode, >+ itemtype => $item->effective_itemtype, >+ categorycode => '*', >+ holds_per_record => 1, >+ reservesallowed => 1, >+ opacitemholds => 'Y', >+ })->store; >+ >+ my $availability = Koha::Item::Availability::Hold->new({item => $item, patron => $patron})->in_opac; >+ >+ is($rule->reservesallowed, 1, 'As I look at hold rules, I match a rule says reservesallowed is 1.'); >+ is($rule->holds_per_record, 1, 'This rule also says holds_per_record is 1.'); >+ is($rule->opacitemholds, 'Y', 'This rule also says OPAC item holds are allowed.'); >+ ok($availability->available, 'When they request availability, then the item is available.'); >+ ok(!$availability->unavailable, 'Then there are no reasons for unavailability.'); >+ ok(!$availability->confirm, 'Then there is nothing to be confirmed.'); >+ ok(!$availability->note, 'Then there are no additional availability notes.'); >+}; >+ >+subtest 'Given zero holds are allowed' => sub { >+ plan tests => 4; >+ >+ my $item = build_a_test_item(); >+ my $patron = build_a_test_patron(); >+ Koha::IssuingRules->search->delete; >+ my $rule = Koha::IssuingRule->new({ >+ branchcode => '*', >+ itemtype => '*', >+ categorycode => '*', >+ holds_per_record => 0, >+ reservesallowed => 0, >+ opacitemholds => 'Y', >+ })->store; >+ >+ sub t_zero_holds_allowed { >+ my ($item, $patron, $rule) = @_; >+ >+ my $availability = Koha::Item::Availability::Hold->new({item => $item, patron => $patron})->in_opac; >+ my $expecting = 'Koha::Exceptions::Hold::ZeroHoldsAllowed'; >+ >+ is($rule->reservesallowed, 0, 'As I study this rule, it says zero reserves are allowed.'); >+ ok(!$availability->available, 'When I request availability, then the item is not available.'); >+ ok(!$availability->confirm, 'Then there are nothing to be confirmed.'); >+ ok(!$availability->note, 'Then there are no additional notes.'); >+ is($availability->unavailable, 1, 'Then there is only one reason for unavailability.'); >+ is(ref($availability->unavailabilities->{$expecting}), $expecting, >+ 'Then there is an unavailability status indicating that holds are not allowed at all.'); >+ } >+ subtest '...on any item type or in any library' => sub { >+ plan tests => 6; >+ \&t_zero_holds_allowed($item, $patron, $rule); >+ }; >+ subtest '...in item home library' => sub { >+ plan tests => 2; >+ >+ subtest '...while ReservesControlBranch = ItemHomeLibrary' => sub { >+ plan tests => 7; >+ >+ t::lib::Mocks::mock_preference('ReservesControlBranch', 'ItemHomeLibrary'); >+ $rule->branchcode($item->homebranch)->store; >+ is($rule->branchcode, $item->homebranch, 'There is a hold rule matching item homebranch.'); >+ t_zero_holds_allowed($item, $patron, $rule); >+ $rule->branchcode('*')->store; >+ set_default_system_preferences(); >+ }; >+ subtest '...while ReservesControlBranch = PatronLibrary' => sub { >+ plan tests => 7; >+ >+ t::lib::Mocks::mock_preference('ReservesControlBranch', 'PatronLibrary'); >+ $rule->branchcode($item->homebranch)->store; >+ >+ my $availability = Koha::Item::Availability::Hold->new({item => $item, patron => $patron})->in_opac; >+ >+ is($rule->branchcode, $item->homebranch, 'There is a hold rule matching item homebranch.'); >+ is($rule->reservesallowed, 0, 'As I study this rule, it says zero reserves are allowed.'); >+ is(C4::Context->preference('ReservesControlBranch'), 'PatronLibrary', 'However, system preference ' >+ .'ReserveControlBranch says we should use PatronLibrary for matching hold rules.'); >+ ok($availability->available, 'When I availability, then the item is available.'); >+ ok(!$availability->confirm, 'Then there are nothing to be confirmed.'); >+ ok(!$availability->note, 'Then there are no additional notes.'); >+ ok(!$availability->unavailable, 'Then there are no reasons for unavailability.'); >+ >+ $rule->branchcode('*')->store; >+ }; >+ }; >+ subtest '...in patron library' => sub { >+ plan tests => 2; >+ >+ subtest '...while ReservesControlBranch = ItemHomeLibrary' => sub { >+ plan tests => 7; >+ >+ t::lib::Mocks::mock_preference('ReservesControlBranch', 'ItemHomeLibrary'); >+ $rule->branchcode($patron->branchcode)->store; >+ >+ my $availability = Koha::Item::Availability::Hold->new({item => $item, patron => $patron})->in_opac; >+ >+ is($rule->branchcode, $patron->branchcode, 'There is a hold rule matching patron branchcode.'); >+ is($rule->reservesallowed, 0, 'As I study this rule, it says zero reserves are allowed.'); >+ is(C4::Context->preference('ReservesControlBranch'), 'ItemHomeLibrary', 'However, system preference ' >+ .'ReserveControlBranch says we should use ItemHomeLibrary for matching hold rules.'); >+ ok($availability->available, 'When I availability, then the item is available.'); >+ ok(!$availability->confirm, 'Then there are nothing to be confirmed.'); >+ ok(!$availability->note, 'Then there are no additional notes.'); >+ ok(!$availability->unavailable, 'Then there are no reasons for unavailability.'); >+ >+ $rule->branchcode('*')->store; >+ }; >+ subtest '...while ReservesControlBranch = PatronLibrary' => sub { >+ plan tests => 6; >+ >+ t::lib::Mocks::mock_preference('ReservesControlBranch', 'PatronLibrary'); >+ $rule->branchcode($patron->branchcode)->store; >+ >+ my $availability = Koha::Item::Availability::Hold->new({item => $item, patron => $patron})->in_opac; >+ my $expecting = 'Koha::Exceptions::Hold::ZeroHoldsAllowed'; >+ >+ is($rule->branchcode, $patron->branchcode, 'There is a hold rule matching patron branchcode.'); >+ is($rule->reservesallowed, 0, 'As I study this rule, it says zero reserves are allowed.'); >+ ok(!$availability->available, 'When I request availability, then the item is not available.'); >+ ok(!$availability->confirm, 'Then there are nothing to be confirmed.'); >+ ok(!$availability->note, 'Then there are no additional notes.'); >+ is($availability->unavailable, 1, 'Then there is one reason for unavailability.'); >+ >+ $rule->branchcode('*')->store; >+ }; >+ }; >+ >+ subtest '...on effective item type' => sub { >+ plan tests => 7; >+ $rule->itemtype($item->effective_itemtype)->store; >+ is($rule->itemtype, $item->effective_itemtype, 'There is a hold rule matching effective itemtype.'); >+ t_zero_holds_allowed($item, $patron, $rule); >+ $rule->itemtype('*')->store; >+ }; >+}; >+ >+subtest 'Given OPAC item holds are not allowed' => \&t_opac_item_hold_not_allowed; >+sub t_opac_item_hold_not_allowed { >+ plan tests => 6; >+ >+ my $item = build_a_test_item(); >+ my $patron = build_a_test_patron(); >+ Koha::IssuingRules->search->delete; >+ my $rule = Koha::IssuingRule->new({ >+ branchcode => '*', >+ itemtype => '*', >+ categorycode => '*', >+ holds_per_record => 1, >+ reservesallowed => 1, >+ opacitemholds => 'N', >+ })->store; >+ >+ is($rule->opacitemholds, 'N', 'As I look at issuing rules, I find out that OPAC item holds are not allowed.'); >+ >+ my $availability = Koha::Item::Availability::Hold->new({item => $item, patron => $patron})->in_opac; >+ my $expecting = 'Koha::Exceptions::Hold::ItemLevelHoldNotAllowed'; >+ >+ ok(!$availability->available, 'When I request availability, then the item is not available.'); >+ ok(!$availability->confirm, 'Then there are nothing to be confirmed.'); >+ ok(!$availability->note, 'Then there are no additional notes.'); >+ is($availability->unavailable, 1, 'Then there is only one reason for unavailability.'); >+ is(ref($availability->unavailabilities->{$expecting}), $expecting, >+ 'Then there is an unavailability status indicating that item level holds are not allowed.'); >+}; >+ >+subtest 'Given I have too many holds in my library' => \&t_too_many_holds_patron_library; >+sub t_too_many_holds_patron_library { >+ plan tests => 8; >+ >+ t::lib::Mocks::mock_preference('ReservesControlBranch', 'PatronLibrary'); >+ my $patron = build_a_test_patron(); >+ my $item = build_a_test_item(); >+ my $item2 = build_a_test_item(); >+ $item->homebranch($item2->homebranch)->store; >+ $item->itype($item2->itype)->store; >+ my $reserve_id = add_item_level_hold($item2, $patron, $item2->homebranch); >+ my $hold = Koha::Holds->find({ borrowernumber => $patron->borrowernumber }); >+ Koha::IssuingRules->search->delete; >+ my $rule = Koha::IssuingRule->new({ >+ branchcode => $patron->branchcode, >+ itemtype => $item->effective_itemtype, >+ categorycode => $patron->categorycode, >+ holds_per_record => 3, >+ reservesallowed => 1, >+ opacitemholds => 'Y', >+ })->store; >+ my $availability = Koha::Item::Availability::Hold->new({item => $item, patron => $patron})->in_opac; >+ my $expecting = 'Koha::Exceptions::Hold::MaximumHoldsReached'; >+ >+ is(C4::Context->preference('ReservesControlBranch'), 'PatronLibrary', 'We will be checking my library\'s rules for holdability.'); >+ is($rule->reservesallowed, 1, 'As I look at circulation rules, I can see that only one reserve is allowed.'); >+ is($hold->reserve_id, $reserve_id, 'I have placed one hold already.'); >+ ok(!$availability->available, 'When I request availability, then the item is not available.'); >+ is($availability->unavailable, 1, 'Then there is only one reason for unavailability.'); >+ is(ref($availability->unavailabilities->{$expecting}), $expecting, >+ 'Then there is an unavailability status indicating that maximum holds have been reached.'); >+ >+ my $ex = $availability->unavailabilities->{$expecting}; >+ is($ex->max_holds_allowed, 1, 'Then, from the status, I can see the maximum holds allowed.'); >+ is($ex->current_hold_count, 1, 'Then, from the status, I can see my current hold count.'); >+}; >+ >+subtest 'Given I have too many holds in item\'s library' => \&t_too_many_holds_item_home_library ; >+sub t_too_many_holds_item_home_library { >+ plan tests => 8; >+ >+ t::lib::Mocks::mock_preference('ReservesControlBranch', 'ItemHomeLibrary'); >+ my $patron = build_a_test_patron(); >+ my $item = build_a_test_item(); >+ my $item2 = build_a_test_item(); >+ $item->homebranch($item2->homebranch)->store; >+ $item->itype($item2->itype)->store; >+ my $reserve_id = add_item_level_hold($item2, $patron, $item2->homebranch); >+ my $hold = Koha::Holds->find({ borrowernumber => $patron->borrowernumber }); >+ Koha::IssuingRules->search->delete; >+ my $rule = Koha::IssuingRule->new({ >+ branchcode => $item2->homebranch, >+ itemtype => $item2->effective_itemtype, >+ categorycode => $patron->categorycode, >+ holds_per_record => 3, >+ reservesallowed => 1, >+ opacitemholds => 'Y', >+ })->store; >+ my $availability = Koha::Item::Availability::Hold->new({item => $item, patron => $patron})->in_opac; >+ my $expecting = 'Koha::Exceptions::Hold::MaximumHoldsReached'; >+ >+ is(C4::Context->preference('ReservesControlBranch'), 'ItemHomeLibrary', 'We will be checking item\'s home library rules for holdability.'); >+ is($rule->reservesallowed, 1, 'As I look at circulation rules, I can see that only one reserve is allowed.'); >+ is($hold->reserve_id, $reserve_id, 'I have placed one hold already.'); >+ ok(!$availability->available, 'When I request availability, then the item is not available.'); >+ is($availability->unavailable, 1, 'Then there is only one reason for unavailability.'); >+ is(ref($availability->unavailabilities->{$expecting}), $expecting, >+ 'Then there is an unavailability status indicating that maximum holds have been reached.'); >+ >+ my $ex = $availability->unavailabilities->{$expecting}; >+ is($ex->max_holds_allowed, 1, 'Then, from the status, I can see the maximum holds allowed.'); >+ is($ex->current_hold_count, 1, 'Then, from the status, I can see my current hold count.'); >+}; >+ >+$schema->storage->txn_rollback; >+ >+1; >diff --git a/t/db_dependent/Koha/Item/Availability/Hold/Opac/ItemStatus.t b/t/db_dependent/Koha/Item/Availability/Hold/Opac/ItemStatus.t >new file mode 100644 >index 0000000..88b9397 >--- /dev/null >+++ b/t/db_dependent/Koha/Item/Availability/Hold/Opac/ItemStatus.t >@@ -0,0 +1,371 @@ >+#!/usr/bin/perl >+ >+# Copyright Koha-Suomi Oy 2016 >+# >+# 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 t1hat 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 Test::More tests => 14; >+use t::lib::Mocks; >+use t::lib::TestBuilder; >+require t::db_dependent::Koha::Availability::Helpers; >+ >+use Koha::Database; >+use Koha::IssuingRules; >+use Koha::Items; >+use Koha::ItemTypes; >+ >+use Koha::Item::Availability::Hold; >+ >+my $schema = Koha::Database->new->schema; >+$schema->storage->txn_begin; >+ >+my $builder = t::lib::TestBuilder->new; >+ >+set_default_system_preferences(); >+set_default_circulation_rules(); >+ >+subtest 'Given item is in a good state for availability' => \&t_ok_availability; >+sub t_ok_availability { >+ plan tests => 3; >+ >+ my $patron = build_a_test_patron(); >+ my $item = build_a_test_item(); >+ my $availability = Koha::Item::Availability::Hold->new({item => $item, patron => $patron})->in_opac; >+ >+ ok($availability->available, 'When I request availability, then the item is available.'); >+ ok(!$availability->confirm, 'Then nothing needs to be confirmed.'); >+ ok(!$availability->unavailable, 'Then there are no reasons to be unavailable.'); >+} >+ >+subtest 'Given item is damaged' => sub { >+ plan tests => 2; >+ >+ subtest 'Given AllowHoldsOnDamagedItems is disabled' => \&t_damaged_item_allow_disabled; >+ subtest 'Given AllowHoldsOnDamagedItems is enabled' => \&t_damaged_item_allow_enabled; >+ sub t_damaged_item_allow_disabled { >+ plan tests => 4; >+ >+ t::lib::Mocks::mock_preference('AllowHoldsOnDamagedItems', 0); >+ >+ my $patron = build_a_test_patron(); >+ my $item = build_a_test_item()->set({damaged=>1})->store; >+ my $availability = Koha::Item::Availability::Hold->new({item => $item, patron => $patron})->in_opac; >+ my $expecting = 'Koha::Exceptions::Item::Damaged'; >+ >+ is($item->damaged, 1, 'When I look at the item, I see that it is damaged.'); >+ ok(!$availability->available, 'When I request availability, then the item is not available.'); >+ is($availability->unavailable, 1, 'Then there is only one unavailability reason.'); >+ is(ref($availability->unavailabilities->{$expecting}), $expecting, >+ 'Then there is an unavailability status indicating damaged item.'); >+ }; >+ sub t_damaged_item_allow_enabled { >+ plan tests => 4; >+ >+ t::lib::Mocks::mock_preference('AllowHoldsOnDamagedItems', 1); >+ >+ my $patron = build_a_test_patron(); >+ my $item = build_a_test_item()->set({damaged=>1})->store; >+ my $availability = Koha::Item::Availability::Hold->new({item => $item, patron => $patron})->in_opac; >+ >+ is($item->damaged, 1, 'When I look at the item, I see that it is damaged.'); >+ ok($availability->available, 'When I request availability, then the item is available.'); >+ ok(!$availability->unavailable, 'Then there are no statuses for unavailability.'); >+ ok(!$availability->confirm, 'Then there is no reason to have availability confirmed.'); >+ }; >+}; >+ >+subtest 'Given item is lost' => \&t_lost; >+sub t_lost { >+ plan tests => 4; >+ >+ my $patron = build_a_test_patron(); >+ my $item = build_a_test_item()->set({itemlost=>1})->store; >+ my $availability = Koha::Item::Availability::Hold->new({item => $item, patron => $patron})->in_opac; >+ my $expecting = 'Koha::Exceptions::Item::Lost'; >+ >+ is($item->itemlost, 1, 'When I try to look at the item, I find out that it is lost.'); >+ ok(!$availability->available, 'When I request availability, then the item is not available.'); >+ is($availability->unavailable, 1, 'Then there is only one unavailability reason.'); >+ is(ref($availability->unavailabilities->{$expecting}), $expecting, >+ 'Then there is an unavailability status indicating lost item.'); >+}; >+ >+subtest 'Given item is not for loan' => \&t_notforloan; >+sub t_notforloan { >+ plan tests => 4; >+ >+ my $patron = build_a_test_patron(); >+ my $item = build_a_test_item()->set({notforloan=>1})->store; >+ my $availability = Koha::Item::Availability::Hold->new({item => $item, patron => $patron})->in_opac; >+ my $expecting = 'Koha::Exceptions::Item::NotForLoan'; >+ >+ is($item->notforloan, 1, 'When I look at the item, I see that it is not for loan.'); >+ ok(!$availability->available, 'When I request availability, then the item is not available.'); >+ is($availability->unavailable, 1, 'Then there is only one unavailability reason.'); >+ is(ref($availability->unavailabilities->{$expecting}), $expecting, >+ 'Then there is an unavailability status indicating the item is not for loan.'); >+}; >+ >+subtest 'Given item type is not for loan' => sub { >+ >+ subtest 'Given item-level_itypes is on (item-itemtype)' => \&t_itemlevel_itemtype_notforloan_item_level_itypes_on; >+ subtest 'Given item-level_itypes is off (biblioitem-itemtype)' => \&t_itemlevel_itemtype_notforloan_item_level_itypes_off; >+ sub t_itemlevel_itemtype_notforloan_item_level_itypes_on { >+ plan tests => 5; >+ >+ t::lib::Mocks::mock_preference('item-level_itypes', 1); >+ >+ my $patron = build_a_test_patron(); >+ my $item = build_a_test_item(); >+ my $biblioitem = Koha::Biblioitems->find($item->biblioitemnumber); >+ my $itemtype = Koha::ItemTypes->find($item->itype); >+ $itemtype->set({notforloan=>1})->store; >+ my $availability = Koha::Item::Availability::Hold->new({item => $item, patron => $patron})->in_opac; >+ my $expecting = 'Koha::Exceptions::ItemType::NotForLoan'; >+ >+ is(Koha::ItemTypes->find($biblioitem->itemtype)->notforloan, 0, 'Biblioitem itemtype is for loan.'); >+ is(Koha::ItemTypes->find($item->itype)->notforloan, 1, 'Item itemtype is not for loan.'); >+ ok(!$availability->available, 'When I request availability, then the item is not available.'); >+ is($availability->unavailable, 1, 'Then there is only one unavailability reason.'); >+ is(ref($availability->unavailabilities->{$expecting}), $expecting, >+ "Then there is an unavailability status indicating the itemtype is not forloan."); >+ }; >+ sub t_itemlevel_itemtype_notforloan_item_level_itypes_off { >+ plan tests => 5; >+ >+ t::lib::Mocks::mock_preference('item-level_itypes', 0); >+ >+ my $patron = build_a_test_patron(); >+ my $item = build_a_test_item(); >+ my $biblioitem = Koha::Biblioitems->find($item->biblioitemnumber); >+ my $itemtype = Koha::ItemTypes->find($biblioitem->itemtype); >+ $itemtype->set({notforloan=>1})->store; >+ my $availability = Koha::Item::Availability::Hold->new({item => $item, patron => $patron})->in_opac; >+ my $expecting = 'Koha::Exceptions::ItemType::NotForLoan'; >+ >+ is(Koha::ItemTypes->find($biblioitem->itemtype)->notforloan, 1, 'Biblioitem itemtype is not for loan.'); >+ is(Koha::ItemTypes->find($item->itype)->notforloan, 0, 'Item itemtype is for loan.'); >+ ok(!$availability->available, 'When I request availability, then the item is not available.'); >+ is($availability->unavailable, 1, 'Then there is only one unavailability reason.'); >+ is(ref($availability->unavailabilities->{$expecting}), $expecting, >+ "Then there is an unavailability status indicating the itemtype is not forloan."); >+ }; >+}; >+ >+subtest 'Given item is ordered' => \&t_ordered; >+sub t_ordered { >+ plan tests => 4; >+ >+ my $patron = build_a_test_patron(); >+ my $item = build_a_test_item()->set({notforloan=>-1})->store; >+ my $availability = Koha::Item::Availability::Hold->new({item => $item, patron => $patron})->in_opac; >+ my $expecting = 'Koha::Exceptions::Item::NotForLoan'; >+ >+ ok($availability->available, 'When I request availability, then the item is available.'); >+ ok(!$availability->unavailable, 'Then there are no reasons for unavailability.'); >+ is(ref($availability->notes->{$expecting}), $expecting, >+ 'Then there is an additional note indicating not for loan status.'); >+ is($availability->notes->{$expecting}->code, 'Ordered', 'Not for loan code says the item is ordered.') >+}; >+ >+subtest 'Given item is restricted' => \&t_restricted; >+sub t_restricted { >+ plan tests => 4; >+ >+ my $patron = build_a_test_patron(); >+ my $item = build_a_test_item()->set({restricted=>1})->store; >+ my $availability = Koha::Item::Availability::Hold->new({item => $item, patron => $patron})->in_opac; >+ my $expecting = 'Koha::Exceptions::Item::Restricted'; >+ >+ is($item->restricted, 1, 'When I look at the item, I see that it is restricted.'); >+ ok(!$availability->available, 'When I request availability, then the item is not available.'); >+ is($availability->unavailable, 1, 'Then there is only one unavailability reason.'); >+ is(ref($availability->unavailabilities->{$expecting}), $expecting, >+ 'Then there is an unavailability status indicating restricted item.'); >+}; >+ >+subtest 'Transfer is limited' => \&t_transfer_limit; >+sub t_transfer_limit { >+ plan tests => 4; >+ >+ t::lib::Mocks::mock_preference('UseBranchTransferLimits', 1); >+ t::lib::Mocks::mock_preference('BranchTransferLimitsType', 'itemtype'); >+ >+ my $patron = build_a_test_patron(); >+ my $item = build_a_test_item(); >+ my $branch2 = Koha::Libraries->find($builder->build({ source => 'Branch' })->{branchcode}); >+ is(C4::Circulation::CreateBranchTransferLimit( >+ $branch2->branchcode, >+ $item->holdingbranch, >+ $item->effective_itemtype >+ ), 1, 'There is a branch transfer limit for itemtype from ' >+ .$item->holdingbranch.' to '.$branch2->branchcode .'.'); >+ my $availability = Koha::Item::Availability::Hold->new({ >+ item => $item, >+ patron => $patron, >+ to_branch => $branch2->branchcode, >+ })->in_opac; >+ my $expecting = 'Koha::Exceptions::Item::CannotBeTransferred'; >+ ok(!$availability->available, 'When I check availability for hold, then item' >+ .' is not available'); >+ is($availability->unavailable, 1, 'Then there is one reason for unavailability'); >+ is(ref($availability->unavailabilities->{$expecting}), $expecting, 'Then there' >+ .' is an unavailability status indicating unability to transfer the item.'); >+}; >+ >+subtest 'Given item has no barcode' => \&t_unknown_barcode; >+sub t_unknown_barcode { >+ plan tests => 4; >+ >+ my $patron = build_a_test_patron(); >+ my $item = build_a_test_item()->set({barcode=>undef})->store; >+ my $expecting = 'Koha::Exceptions::Item::UnknownBarcode'; >+ my $availability = Koha::Item::Availability::Hold->new({item => $item, patron => $patron})->in_opac; >+ >+ is($item->barcode, undef, 'When I look at the item, we see that it has undefined barcode.'); >+ ok($availability->unavailable, 'When I request availability, then the item is not available.'); >+ is($availability->unavailable, 1, 'Then there is only one unavailability reason.'); >+ is(ref($availability->unavailabilities->{$expecting}), $expecting, >+ 'Then there is an unavailability status indicating unknown barcode.'); >+}; >+ >+subtest 'Given item is withdrawn' => \&t_withdrawn; >+sub t_withdrawn { >+ plan tests => 4; >+ >+ my $patron = build_a_test_patron(); >+ my $item = build_a_test_item()->set({restricted=>1})->store; >+ my $availability = Koha::Item::Availability::Hold->new({item => $item, patron => $patron})->in_opac; >+ my $expecting = 'Koha::Exceptions::Item::Restricted'; >+ >+ is($item->restricted, 1, 'When I look at the item, I see that it is restricted.'); >+ ok(!$availability->available, 'When I request availability, then the item is not available.'); >+ is($availability->unavailable, 1, 'Then there is only one unavailability reason.'); >+ is(ref($availability->unavailabilities->{$expecting}), $expecting, >+ 'Then there is an unavailability status indicating restricted item.'); >+}; >+ >+subtest 'Already held' => \&t_already_held; >+sub t_already_held { >+ plan tests => 8; >+ >+ my $patron = build_a_test_patron(); >+ my $item = build_a_test_item(); >+ my $reserve_id = add_item_level_hold($item, $patron, $item->homebranch); >+ my $hold = Koha::Holds->find({ borrowernumber => $patron->borrowernumber }); >+ Koha::IssuingRules->search->delete; >+ my $rule = Koha::IssuingRule->new({ >+ branchcode => $item->homebranch, >+ itemtype => $item->effective_itemtype, >+ categorycode => $patron->categorycode, >+ holds_per_record => 9001, >+ reservesallowed => 9001, >+ opacitemholds => 'Y', >+ })->store; >+ my $availability = Koha::Item::Availability::Hold->new({item => $item, patron => $patron})->in_opac; >+ my $expecting = 'Koha::Exceptions::Item::AlreadyHeldForThisPatron'; >+ >+ is($rule->reservesallowed, 9001, 'As I look at circulation rules, I can see that many reserves are allowed.'); >+ ok($reserve_id, 'I have placed a hold on an item.'); >+ is($hold->itemnumber, $item->itemnumber, 'The item I have hold for is the same item I will check availability for.'); >+ ok(!$availability->available, 'When I request availability, then the item is not available.'); >+ ok(!$availability->confirm, 'Then there is nothing to be confirmed.'); >+ ok(!$availability->note, 'Then there are no additional notes.'); >+ is($availability->unavailable, 1, 'Then there is only one reason for unavailability.'); >+ is(ref($availability->unavailabilities->{$expecting}), $expecting, >+ 'Then there is an unavailability status indicating that I have already held this.'); >+}; >+ >+subtest 'Less than maxreserves' => \&t_less_than_maxreserves; >+sub t_less_than_maxreserves { >+ plan tests => 5; >+ >+ t::lib::Mocks::mock_preference('maxreserves', 50); >+ >+ my $patron = build_a_test_patron(); >+ my $item = build_a_test_item(); >+ my $item2 = build_a_test_item(); >+ my $reserve_id = add_item_level_hold($item2, $patron, $item2->homebranch); >+ my $holdcount = Koha::Holds->search({ borrowernumber => $patron->borrowernumber })->count; >+ my $availability = Koha::Item::Availability::Hold->new({item => $item, patron => $patron})->in_opac; >+ >+ ok(C4::Context->preference('maxreserves') > $holdcount, 'When I check my holds, I can see that I have less than maximum allowed.'); >+ ok($availability->available, 'When I request availability, then the item is available.'); >+ ok(!$availability->confirm, 'Then there is nothing to be confirmed.'); >+ ok(!$availability->note, 'Then there are no additional notes.'); >+ ok(!$availability->unavailable, 'Then there are no reasons for unavailability.'); >+}; >+ >+subtest 'Equal to maxreserves' => \&t_equal_to_maxreserves; >+sub t_equal_to_maxreserves { >+ plan tests => 8; >+ >+ t::lib::Mocks::mock_preference('maxreserves', 1); >+ >+ my $patron = build_a_test_patron(); >+ my $item = build_a_test_item(); >+ my $item2 = build_a_test_item(); >+ my $reserve_id = add_item_level_hold($item2, $patron, $item2->homebranch); >+ my $holdcount = Koha::Holds->search({ borrowernumber => $patron->borrowernumber })->count; >+ my $availability = Koha::Item::Availability::Hold->new({item => $item, patron => $patron})->in_opac; >+ my $expecting = 'Koha::Exceptions::Hold::MaximumHoldsReached'; >+ >+ ok(C4::Context->preference('maxreserves') == $holdcount, 'When I check my holds, I can see that I maximum allowed holds.'); >+ ok(!$availability->available, 'When I request availability, then the item is not available.'); >+ ok(!$availability->confirm, 'Then there is nothing to be confirmed.'); >+ ok(!$availability->note, 'Then there are no additional notes.'); >+ is($availability->unavailable, 1, 'Then there are is one reason for unavailability.'); >+ is(ref($availability->unavailabilities->{$expecting}), $expecting, 'Then there is an unavailability' >+ .' status indicating maximum holds have been reached.'); >+ >+ my $ex = $availability->unavailabilities->{$expecting}; >+ is($ex->max_holds_allowed, 1, 'Then, from the status, I can see the maximum holds allowed.'); >+ is($ex->current_hold_count, 1, 'Then, from the status, I can see my current hold count.'); >+}; >+ >+subtest 'More than maxreserves' => \&t_more_than_maxreserves; >+sub t_more_than_maxreserves { >+ plan tests => 8; >+ >+ t::lib::Mocks::mock_preference('maxreserves', 1); >+ >+ my $patron = build_a_test_patron(); >+ my $item = build_a_test_item(); >+ my $item2 = build_a_test_item(); >+ my $item3 = build_a_test_item(); >+ my $reserve_id = add_item_level_hold($item2, $patron, $item2->homebranch); >+ my $reserve_id2 = add_item_level_hold($item3, $patron, $item3->homebranch); >+ my $holdcount = Koha::Holds->search({ borrowernumber => $patron->borrowernumber })->count; >+ my $availability = Koha::Item::Availability::Hold->new({item => $item, patron => $patron})->in_opac; >+ my $expecting = 'Koha::Exceptions::Hold::MaximumHoldsReached'; >+ >+ ok(C4::Context->preference('maxreserves') < $holdcount, 'When I check my holds, I can see that I have more holds than allowed. How?!'); >+ ok(!$availability->available, 'When I request availability, then the item is not available.'); >+ ok(!$availability->confirm, 'Then there is nothing to be confirmed.'); >+ ok(!$availability->note, 'Then there are no additional notes.'); >+ is($availability->unavailable, 1, 'Then there are is one reason for unavailability.'); >+ is(ref($availability->unavailabilities->{$expecting}), $expecting, 'Then there is an unavailability' >+ .' status indicating maximum holds have been reached.'); >+ >+ my $ex = $availability->unavailabilities->{$expecting}; >+ is($ex->max_holds_allowed, 1, 'Then, from the status, I can see the maximum holds allowed.'); >+ is($ex->current_hold_count, 2, 'Then, from the status, I can see my current hold count.'); >+}; >+ >+$schema->storage->txn_rollback; >+ >+1; >diff --git a/t/db_dependent/Koha/Item/Availability/Hold/Opac/LibraryItemRules.t b/t/db_dependent/Koha/Item/Availability/Hold/Opac/LibraryItemRules.t >new file mode 100644 >index 0000000..aa24149 >--- /dev/null >+++ b/t/db_dependent/Koha/Item/Availability/Hold/Opac/LibraryItemRules.t >@@ -0,0 +1,260 @@ >+#!/usr/bin/perl >+ >+# Copyright Koha-Suomi Oy 2016 >+# >+# 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 Test::More tests => 6; >+use t::lib::Mocks; >+use t::lib::TestBuilder; >+require t::db_dependent::Koha::Availability::Helpers; >+ >+use Koha::Database; >+use Koha::IssuingRules; >+use Koha::Items; >+use Koha::ItemTypes; >+ >+use Koha::Item::Availability::Hold; >+ >+my $schema = Koha::Database->new->schema; >+$schema->storage->txn_begin; >+ >+my $dbh = C4::Context->dbh; >+$dbh->{RaiseError} = 1; >+ >+my $builder = t::lib::TestBuilder->new; >+ >+set_default_system_preferences(); >+set_default_circulation_rules(); >+ >+subtest 'Given my library does not allow holds in branch item rules' => \&t_holdnotallowed_patronlibrary; >+sub t_holdnotallowed_patronlibrary { >+ plan tests => 7; >+ >+ set_default_circulation_rules(); >+ t::lib::Mocks::mock_preference('CircControl', 'PatronLibrary'); >+ >+ my $item = build_a_test_item(); >+ my $patron = build_a_test_patron(); >+ ok($dbh->do(q{ >+ INSERT INTO branch_item_rules (branchcode, itemtype, holdallowed, returnbranch) >+ VALUES (?, ?, ?, ?) >+ }, {}, $patron->branchcode, $item->effective_itemtype, 0, 'homebranch'), 'There is a branch item' >+ .' rule that says holds are not allowed from my library.'); >+ >+ my $availability = Koha::Item::Availability::Hold->new({item => $item, patron => $patron})->in_opac; >+ my $expecting = 'Koha::Exceptions::Hold::NotAllowedByLibrary'; >+ >+ is(C4::Context->preference('CircControl'), 'PatronLibrary', 'Koha is configured to use patron\'s library for checkout rules.'); >+ ok(!$availability->available, 'When I request availability, then the item is not available.'); >+ is($availability->unavailable, 1, 'Then there is one reason for unavailability.'); >+ ok(!$availability->confirm, 'Then there is nothing to be confirmed.'); >+ ok(!$availability->note, 'Then there are no additional notes.'); >+ is(ref($availability->unavailabilities->{$expecting}), $expecting, 'Then there is an unavailability' >+ .' note indicating library does not allow item to be held.'); >+}; >+ >+subtest 'Given item\'s home library does not allow holds in branch item rules' => \&t_holdnotallowed_itemhomelibrary; >+sub t_holdnotallowed_itemhomelibrary { >+ plan tests => 7; >+ >+ set_default_circulation_rules(); >+ t::lib::Mocks::mock_preference('CircControl', 'ItemHomeLibrary'); >+ >+ my $item = build_a_test_item(); >+ my $patron = build_a_test_patron(); >+ ok($dbh->do(q{ >+ INSERT INTO branch_item_rules (branchcode, itemtype, holdallowed, returnbranch) >+ VALUES (?, ?, ?, ?) >+ }, {}, $item->homebranch, $item->effective_itemtype, 0, 'homebranch'), 'There is a branch item' >+ .' rule that says item\'s library forbids holds.'); >+ >+ my $availability = Koha::Item::Availability::Hold->new({item => $item, patron => $patron})->in_opac; >+ my $expecting = 'Koha::Exceptions::Hold::NotAllowedByLibrary'; >+ >+ is(C4::Context->preference('CircControl'), 'ItemHomeLibrary', 'Koha is configured to use item\'s library for checkout rules.'); >+ ok(!$availability->available, 'When I request availability, then the item is not available.'); >+ is($availability->unavailable, 1, 'Then there is one reason for unavailability.'); >+ ok(!$availability->confirm, 'Then there is nothing to be confirmed.'); >+ ok(!$availability->note, 'Then there are no additional notes.'); >+ is(ref($availability->unavailabilities->{$expecting}), $expecting, 'Then there is an unavailability' >+ .' note indicating library does not allow item to be held.'); >+}; >+ >+subtest 'Given my library allows holds only from my library' => \&t_holdallowed_only_from_patronlibrary; >+sub t_holdallowed_only_from_patronlibrary { >+ plan tests => 8; >+ >+ set_default_circulation_rules(); >+ t::lib::Mocks::mock_preference('CircControl', 'PatronLibrary'); >+ >+ my $item = build_a_test_item(); >+ my $patron = build_a_test_patron(); >+ ok($dbh->do(q{ >+ INSERT INTO branch_item_rules (branchcode, itemtype, holdallowed, returnbranch) >+ VALUES (?, ?, ?, ?) >+ }, {}, $patron->branchcode, $item->effective_itemtype, 1, 'homebranch'), 'There is a branch item' >+ .' rule that says holds are allowed only from my library.'); >+ >+ my $availability = Koha::Item::Availability::Hold->new({item => $item, patron => $patron})->in_opac; >+ my $expecting = 'Koha::Exceptions::Hold::NotAllowedFromOtherLibraries'; >+ >+ is(C4::Context->preference('CircControl'), 'PatronLibrary', 'Koha is configured to use patron\'s library for checkout rules.'); >+ ok($item->homebranch ne $patron->branchcode, 'I am from different library than the item.'); >+ ok(!$availability->available, 'When I request availability, then the item is not available.'); >+ is($availability->unavailable, 1, 'Then there is one reason for unavailability.'); >+ ok(!$availability->confirm, 'Then there is nothing to be confirmed.'); >+ ok(!$availability->note, 'Then there are no additional notes.'); >+ is(ref($availability->unavailabilities->{$expecting}), $expecting, 'Then there is an unavailability' >+ .' status indicating library does not allow item to be held from other libraries.'); >+}; >+ >+subtest 'Given item\'s library allows holds only its library' => \&t_holdallowed_only_from_itemhomelibrary; >+sub t_holdallowed_only_from_itemhomelibrary { >+ plan tests => 8; >+ >+ set_default_circulation_rules(); >+ t::lib::Mocks::mock_preference('CircControl', 'ItemHomeLibrary'); >+ >+ my $item = build_a_test_item(); >+ my $patron = build_a_test_patron(); >+ ok($dbh->do(q{ >+ INSERT INTO branch_item_rules (branchcode, itemtype, holdallowed, returnbranch) >+ VALUES (?, ?, ?, ?) >+ }, {}, $item->homebranch, $item->effective_itemtype, 1, 'homebranch'), 'There is a branch item' >+ .' rule that says holds are allowed only in item home branch.'); >+ >+ my $availability = Koha::Item::Availability::Hold->new({item => $item, patron => $patron})->in_opac; >+ my $expecting = 'Koha::Exceptions::Hold::NotAllowedFromOtherLibraries'; >+ >+ is(C4::Context->preference('CircControl'), 'ItemHomeLibrary', 'Koha is configured to use item\'s library for checkout rules.'); >+ ok($item->homebranch ne $patron->branchcode, 'I am from different library than the item.'); >+ ok(!$availability->available, 'When I request availability, then the item is not available.'); >+ is($availability->unavailable, 1, 'Then there is one reason for unavailability.'); >+ ok(!$availability->confirm, 'Then there is nothing to be confirmed.'); >+ ok(!$availability->note, 'Then there are no additional notes.'); >+ is(ref($availability->unavailabilities->{$expecting}), $expecting, 'Then there is an unavailability' >+ .' status indicating library does not allow item to be held from other libraries.'); >+}; >+ >+subtest 'Given my library allows holds from any other libraries' => \&t_holdallowed_from_any_library_patronlibrary; >+sub t_holdallowed_from_any_library_patronlibrary { >+ plan tests => 3; >+ >+ set_default_circulation_rules(); >+ t::lib::Mocks::mock_preference('CircControl', 'PatronLibrary'); >+ >+ my $item = build_a_test_item(); >+ my $patron = build_a_test_patron(); >+ ok($dbh->do(q{ >+ INSERT INTO branch_item_rules (branchcode, itemtype, holdallowed, returnbranch) >+ VALUES (?, ?, ?, ?) >+ }, {}, $patron->branchcode, $item->effective_itemtype, 2, 'homebranch'), 'There is a branch item' >+ .' rule that says holds are allowed from any library.'); >+ >+ subtest 'Given IndependentBranches is on and canreservefromotherbranches is off' => sub { >+ plan tests => 9; >+ >+ t::lib::Mocks::mock_preference('canreservefromotherbranches', 0); >+ t::lib::Mocks::mock_preference('IndependentBranches', 1); >+ >+ my $availability = Koha::Item::Availability::Hold->new({item => $item, patron => $patron})->in_opac; >+ my $expecting = 'Koha::Exceptions::Hold::NotAllowedFromOtherLibraries'; >+ >+ is(C4::Context->preference('CircControl'), 'PatronLibrary', 'Koha is configured to use patron\'s library for checkout rules.'); >+ is(C4::Context->preference('canreservefromotherbranches'), 0, 'People cannot reserve from other libraries.'); >+ is(C4::Context->preference('IndependentBranches'), 1, 'Libraries are independent.'); >+ ok($item->homebranch ne $patron->branchcode, 'I am from different library than the item.'); >+ ok(!$availability->available, 'When I request availability, then the item is not available.'); >+ is($availability->unavailable, 1, 'Then there are no reasons for unavailability.'); >+ ok(!$availability->confirm, 'Then there is nothing to be confirmed.'); >+ ok(!$availability->note, 'Then there are no additional notes.'); >+ is(ref($availability->unavailabilities->{$expecting}), $expecting, 'Then there is an unavailability' >+ .' status indicating library does not allow item to be held from other libraries.'); >+ }; >+ >+ subtest 'Given IndependentBranches is off and canreservefromotherbranches is on' => sub { >+ plan tests => 8; >+ >+ t::lib::Mocks::mock_preference('canreservefromotherbranches', 1); >+ t::lib::Mocks::mock_preference('IndependentBranches', 0); >+ my $availability = Koha::Item::Availability::Hold->new({item => $item, patron => $patron})->in_opac; >+ >+ is(C4::Context->preference('CircControl'), 'PatronLibrary', 'Koha is configured to use patron\'s library for checkout rules.'); >+ is(C4::Context->preference('canreservefromotherbranches'), 1, 'People can reserve from other libraries.'); >+ is(C4::Context->preference('IndependentBranches'), 0, 'Libraries are not independent.'); >+ ok($item->homebranch ne $patron->branchcode, 'I am from different library than the item.'); >+ ok($availability->available, 'When I request availability, then the item is available.'); >+ ok(!$availability->unavailable, 'Then there are no reasons for unavailability.'); >+ ok(!$availability->confirm, 'Then there is nothing to be confirmed.'); >+ ok(!$availability->note, 'Then there are no additional notes.'); >+ }; >+}; >+ >+subtest 'Given item\'s library allows holds from any other libraries' => \&t_holdallowed_from_any_library_itemhomelibrary; >+sub t_holdallowed_from_any_library_itemhomelibrary { >+ plan tests => 3; >+ >+ set_default_circulation_rules(); >+ t::lib::Mocks::mock_preference('CircControl', 'ItemHomeLibrary'); >+ >+ my $item = build_a_test_item(); >+ my $patron = build_a_test_patron(); >+ ok($dbh->do(q{ >+ INSERT INTO branch_item_rules (branchcode, itemtype, holdallowed, returnbranch) >+ VALUES (?, ?, ?, ?) >+ }, {}, $item->homebranch, $item->effective_itemtype, 2, 'homebranch'), 'There is a branch item' >+ .' rule in item\'s homebranch that says holds are allowed from any library.'); >+ >+ subtest 'Given IndependentBranches is on and canreservefromotherbranches is off' => sub { >+ plan tests => 9; >+ >+ t::lib::Mocks::mock_preference('canreservefromotherbranches', 0); >+ t::lib::Mocks::mock_preference('IndependentBranches', 1); >+ >+ my $availability = Koha::Item::Availability::Hold->new({item => $item, patron => $patron})->in_opac; >+ my $expecting = 'Koha::Exceptions::Hold::NotAllowedFromOtherLibraries'; >+ >+ is(C4::Context->preference('CircControl'), 'ItemHomeLibrary', 'Koha is configured to use item\'s library for checkout rules.'); >+ is(C4::Context->preference('canreservefromotherbranches'), 0, 'People cannot reserve from other libraries.'); >+ is(C4::Context->preference('IndependentBranches'), 1, 'Libraries are independent.'); >+ ok($item->homebranch ne $patron->branchcode, 'I am from different library than the item.'); >+ ok(!$availability->available, 'When I request availability, then the item is not available.'); >+ is($availability->unavailable, 1, 'Then there are no reasons for unavailability.'); >+ ok(!$availability->confirm, 'Then there is nothing to be confirmed.'); >+ ok(!$availability->note, 'Then there are no additional notes.'); >+ is(ref($availability->unavailabilities->{$expecting}), $expecting, 'Then there is an unavailability' >+ .' status indicating library does not allow item to be held from other libraries.'); >+ }; >+ >+ subtest 'Given IndependentBranches is off and canreservefromotherbranches is on' => sub { >+ plan tests => 8; >+ >+ t::lib::Mocks::mock_preference('canreservefromotherbranches', 1); >+ t::lib::Mocks::mock_preference('IndependentBranches', 0); >+ my $availability = Koha::Item::Availability::Hold->new({item => $item, patron => $patron})->in_opac; >+ >+ is(C4::Context->preference('CircControl'), 'ItemHomeLibrary', 'Koha is configured to use item\'s library for checkout rules.'); >+ is(C4::Context->preference('canreservefromotherbranches'), 1, 'People can reserve from other libraries.'); >+ is(C4::Context->preference('IndependentBranches'), 0, 'Libraries are not independent.'); >+ ok($item->homebranch ne $patron->branchcode, 'I am from different library than the item.'); >+ ok($availability->available, 'When I request availability, then the item is available.'); >+ ok(!$availability->unavailable, 'Then there are no reasons for unavailability.'); >+ ok(!$availability->confirm, 'Then there is nothing to be confirmed.'); >+ ok(!$availability->note, 'Then there are no additional notes.'); >+ }; >+}; >-- >2.1.4
You cannot view the attachment while viewing its details because your browser does not support IFRAMEs.
View the attachment on a separate page
.
View Attachment As Diff
View Attachment As Raw
Actions:
View
|
Diff
|
Splinter Review
Attachments on
bug 17712
:
57921
|
57923
|
58446
|
58447
|
58449
|
58450
|
58451
|
58452
|
58453
|
58454
|
60147
|
60148
|
60149
|
60150
|
60151
|
60152
|
60153
|
60154
|
60261
|
60262
|
60263
|
60264
|
60265
|
62719
|
62720
|
62721
|
62722
|
62723
|
62724
|
62725
|
62726
|
63343
|
63344
|
63345
|
63346
|
63347
|
63348
|
63349
|
63350
|
84830
|
84831
|
84832
|
84833
|
84834
|
84835
|
84836
|
84837
|
84838
|
84839