From 81b072736a39c9a067f5a168d4202df47f0f15e4 Mon Sep 17 00:00:00 2001 From: Aleisha Amohia Date: Tue, 15 Jun 2021 13:49:47 +1200 Subject: [PATCH] Bug 15565: Work in progress, calculating holds remaining Not ready for testing! --- Koha/Holds.pm | 135 ++++++++++++++++++++++++++++++ t/db_dependent/Koha/Holds.t | 195 +++++++++++++++++++++++++++++++++++++++++++- 2 files changed, 329 insertions(+), 1 deletion(-) diff --git a/Koha/Holds.pm b/Koha/Holds.pm index 296f99cb0a..dd9abf8805 100644 --- a/Koha/Holds.pm +++ b/Koha/Holds.pm @@ -153,6 +153,141 @@ sub get_items_that_can_fill { ); } +=head3 remaining_holds_total + + my $remaining_holds_total = Koha::Holds->remaining_holds_total({ patron => $patron, biblio => $biblio }); + +Calculates and returns the number of remaining holds a borrower is allowed to place. +This will be firstly based on the maxreserves system preference, the secondly the reservesallowed circulation rules + +=cut + +sub remaining_holds_total { + my ( $self, $params ) = @_; + + unless ( $params->{patron} ) { + # no patron was passed + return 0; + } + + my $current_holds_count = $params->{patron}->holds->count; + my $holds_allowed_total = C4::Context->preference('maxreserves'); + if ( $holds_allowed_total and $current_holds_count >= $holds_allowed_total ) { + # patron has more holds than maxreserves system preference + return 0; + } + + my $branchcode = C4::Context->preference('ReservesControlBranch'); + if ( $params->{biblio} ) { + # iterate through every item attached to this biblio. + # get the highest reservesallowed value out of these item types. + my @items = Koha::Items->search({ biblionumber => $params->{biblio}->biblionumber }); + foreach my $item ( @items ) { + my $reservesallowed = Koha::CirculationRules->get_effective_rule({ + rule_name => 'reservesallowed', + categorycode => $params->{patron}->categorycode, + itemtype => $item->itemtype, + branchcode => $branchcode, + }); + if ( $reservesallowed and $reservesallowed->rule_value and $reservesallowed->rule_value < $holds_allowed_total ) { + # use the lowest value + $holds_allowed_total = $reservesallowed->rule_value; + } + } + } else { + my $reservesallowed = Koha::CirculationRules->get_effective_rule({ + rule_name => 'reservesallowed', + categorycode => $params->{patron}->categorycode, + itemtype => undef, + branchcode => $branchcode, + }); + if ( $reservesallowed and $reservesallowed->rule_value and $reservesallowed->rule_value < $holds_allowed_total ) { + # use the lowest value + $holds_allowed_total = $reservesallowed->rule_value; + } + } + my $remaining_holds_count = $holds_allowed_total - $current_holds_count; + return $remaining_holds_count; +} + +=head3 remaining_holds_record + + my $remaining_holds_record = Koha::Holds->remaining_holds_record({ patron => $patron, biblio => $biblio }); + +Calculates and returns the number of remaining holds a borrower is allowed to place per record. +This will be based on the holds_per_record circulation rules + +=cut + +sub remaining_holds_record { + my ( $self, $params ) = @_; + + unless ( $params->{patron} or $params->{biblio} ) { + # no patron or biblio was passed + return 0; + } + + my $current_holds_count = $self->search({ borrowernumber => $params->{patron}->borrowernumber, biblionumber => $params->{biblio}->biblionumber })->count; + my $holds_allowed_per_record = 0; + + my $branchcode = C4::Context->preference('ReservesControlBranch'); + my @items = Koha::Items->search({ biblionumber => $params->{biblio}->biblionumber }); + foreach my $item ( @items ) { + my $holds_per_record = Koha::CirculationRules->get_effective_rule({ + rule_name => 'holds_per_record', + categorycode => $params->{patron}->categorycode, + itemtype => $item->itemtype, + branchcode => $branchcode, + }); + if ( $holds_per_record and $holds_per_record->rule_value and $holds_per_record->rule_value < $holds_allowed_per_record ) { + # use the lowest value + $holds_allowed_per_record = $holds_per_record->rule_value; + } + } + + my $remaining_holds_count = $holds_allowed_per_record - $current_holds_count; + return $remaining_holds_count; +} + +=head3 remaining_holds_today + + my $remaining_holds_today = Koha::Holds->remaining_holds_today({ patron => $patron, biblio => $biblio }); + +Calculates and returns the number of remaining holds a borrower is allowed to place today. +This will be based on the holds_per_day circulation rules + +=cut + +sub remaining_holds_today { + my ( $self, $params ) = @_; + + unless ( $params->{patron} or $params->{biblio} ) { + # no patron or biblio was passed + return 0; + } + + my $current_holds_count = $self->search({ borrowernumber => $params->{patron}->borrowernumber, reservedate => dt_from_string->date })->count; + my $holds_allowed_per_day = 0; + + my $branchcode = C4::Context->preference('ReservesControlBranch'); + my @items = Koha::Items->search({ biblionumber => $params->{biblio}->biblionumber }); + foreach my $item ( @items ) { + my $holds_per_day = Koha::CirculationRules->get_effective_rule({ + rule_name => 'holds_per_day', + categorycode => $params->{patron}->categorycode, + itemtype => $item->itemtype, + branchcode => $branchcode, + }); + if ( $holds_per_day and $holds_per_day->rule_value and $holds_per_day->rule_value < $holds_allowed_per_day ) { + # use the lowest value + $holds_allowed_per_day = $holds_per_day->rule_value; + } + } + + my $remaining_holds_count = $holds_allowed_per_day - $current_holds_count; + return $remaining_holds_count; +} + =head3 type =cut diff --git a/t/db_dependent/Koha/Holds.t b/t/db_dependent/Koha/Holds.t index 7d19a132f1..b977175bb3 100755 --- a/t/db_dependent/Koha/Holds.t +++ b/t/db_dependent/Koha/Holds.t @@ -19,7 +19,7 @@ use Modern::Perl; -use Test::More tests => 6; +use Test::More tests => 9; use Test::Warn; use C4::Circulation; @@ -500,6 +500,199 @@ subtest 'get_items_that_can_fill' => sub { }; +subtest 'remaining_holds_total' => sub { + plan tests => 3; + + my $biblio = $builder->build_sample_biblio; + my $itype_1 = $builder->build_object({ class => 'Koha::ItemTypes' }); + my $item_1 = $builder->build_sample_item({ biblionumber => $biblio->biblionumber, itype => $itype_1->itemtype }); + my $item_2 = $builder->build_sample_item({ biblionumber => $biblio->biblionumber, itype => $itype_1->itemtype }); + my $item_3 = $builder->build_sample_item({ biblionumber => $biblio->biblionumber, itype => $itype_1->itemtype }); + my $patron_1 = $builder->build_object({ class => 'Koha::Patrons' }); + + t::lib::Mocks::mock_userenv({ patron => $patron_1 }); + t::lib::Mocks::mock_preference('maxreserves', 4); + Koha::CirculationRules->set_rules({ + itemtype => $itype_1->itemtype, + branchcode => undef, + categorycode => $patron_1->categorycode, + rules => { + reservesallowed => 5, + }, + }); + + my $reserve_id_1 = C4::Reserves::AddReserve( + { + borrowernumber => $patron_1->borrowernumber, + biblionumber => $biblio->biblionumber, + priority => 1, + itemnumber => $item_1->itemnumber, + } + ); + + my $reserve_id_2 = C4::Reserves::AddReserve( + { + borrowernumber => $patron_1->borrowernumber, + biblionumber => $biblio->biblionumber, + priority => 1, + itemnumber => $item_2->itemnumber, + } + ); + + my $reserve_id_3 = C4::Reserves::AddReserve( + { + borrowernumber => $patron_1->borrowernumber, + biblionumber => $biblio->biblionumber, + priority => 1, + itemnumber => $item_3->itemnumber, + } + ); + + my $reserve_id_4 = C4::Reserves::AddReserve( + { + borrowernumber => $patron_1->borrowernumber, + biblionumber => $biblio->biblionumber, + priority => 1, + itemnumber => undef, + } + ); + + my $remaining_holds_total = Koha::Holds->remaining_holds_total; + is( $remaining_holds_total, 0, "No patron was passed to calculate their holds" ); + + $remaining_holds_total = Koha::Holds->remaining_holds_total({ patron => $patron_1, biblio => $biblio }); + is( $remaining_holds_total, 0, "Patron has reached maxreserves system preference maximum holds" ); + + t::lib::Mocks::mock_preference('maxreserves', 5); + + $remaining_holds_total = Koha::Holds->remaining_holds_total({ patron => $patron_1, biblio => $biblio }); + is( $remaining_holds_total, 1, "Patron has one more allowed hold based on reservesallowed circulation rule" ); +}; + +subtest 'remaining_holds_record' => sub { + plan tests => 3; + + my $biblio = $builder->build_sample_biblio; + my $itype_1 = $builder->build_object({ class => 'Koha::ItemTypes' }); + my $item_1 = $builder->build_sample_item({ biblionumber => $biblio->biblionumber, itype => $itype_1->itemtype }); + my $item_2 = $builder->build_sample_item({ biblionumber => $biblio->biblionumber, itype => $itype_1->itemtype }); + my $item_3 = $builder->build_sample_item({ biblionumber => $biblio->biblionumber, itype => $itype_1->itemtype }); + my $patron_1 = $builder->build_object({ class => 'Koha::Patrons' }); + + t::lib::Mocks::mock_userenv({ patron => $patron_1 }); + t::lib::Mocks::mock_preference('maxreserves', 10); + Koha::CirculationRules->set_rules({ + itemtype => $itype_1->itemtype, + branchcode => undef, + categorycode => $patron_1->categorycode, + rules => { + holds_per_record => 3, + }, + }); + + my $reserve_id_1 = C4::Reserves::AddReserve( + { + borrowernumber => $patron_1->borrowernumber, + biblionumber => $biblio->biblionumber, + priority => 1, + itemnumber => $item_1->itemnumber, + } + ); + + my $reserve_id_2 = C4::Reserves::AddReserve( + { + borrowernumber => $patron_1->borrowernumber, + biblionumber => $biblio->biblionumber, + priority => 1, + itemnumber => $item_2->itemnumber, + } + ); + + my $reserve_id_3 = C4::Reserves::AddReserve( + { + borrowernumber => $patron_1->borrowernumber, + biblionumber => $biblio->biblionumber, + priority => 1, + itemnumber => $item_3->itemnumber, + } + ); + + my $remaining_holds_record = Koha::Holds->remaining_holds_record; + is( $remaining_holds_record, 0, "No patron or biblio passed" ); + + $remaining_holds_record = Koha::Holds->remaining_holds_record({ patron => $patron_1, biblio => $biblio }); + is( $remaining_holds_record, 0, "Patron has reached holds_per_record circulation rule maximum holds" ); + + Koha::Holds->find( $reserve_id_3 )->delete; + + $remaining_holds_record = Koha::Holds->remaining_holds_record({ patron => $patron_1, biblio => $biblio }); + is( $remaining_holds_record, 1, "Patron has one more allowed hold on this record based on reservesallowed circulation rule" ); +}; + +subtest 'remaining_holds_today' => sub { + plan tests => 3; + + my $biblio = $builder->build_sample_biblio; + my $itype_1 = $builder->build_object({ class => 'Koha::ItemTypes' }); + my $item_1 = $builder->build_sample_item({ biblionumber => $biblio->biblionumber, itype => $itype_1->itemtype }); + my $item_2 = $builder->build_sample_item({ biblionumber => $biblio->biblionumber, itype => $itype_1->itemtype }); + my $item_3 = $builder->build_sample_item({ biblionumber => $biblio->biblionumber, itype => $itype_1->itemtype }); + my $patron_1 = $builder->build_object({ class => 'Koha::Patrons' }); + + t::lib::Mocks::mock_userenv({ patron => $patron_1 }); + t::lib::Mocks::mock_preference('maxreserves', 10); + Koha::CirculationRules->set_rules({ + itemtype => $itype_1->itemtype, + branchcode => undef, + categorycode => $patron_1->categorycode, + rules => { + holds_per_day => 5, + }, + }); + + my $reserve_id_1 = C4::Reserves::AddReserve( + { + borrowernumber => $patron_1->borrowernumber, + biblionumber => $biblio->biblionumber, + priority => 1, + itemnumber => $item_1->itemnumber, + } + ); + + my $reserve_id_2 = C4::Reserves::AddReserve( + { + borrowernumber => $patron_1->borrowernumber, + biblionumber => $biblio->biblionumber, + priority => 1, + itemnumber => $item_2->itemnumber, + } + ); + + my $reserve_id_3 = C4::Reserves::AddReserve( + { + borrowernumber => $patron_1->borrowernumber, + biblionumber => $biblio->biblionumber, + priority => 1, + itemnumber => $item_3->itemnumber, + } + ); + + my $reserve_id_4 = C4::Reserves::AddReserve( + { + borrowernumber => $patron_1->borrowernumber, + biblionumber => $biblio->biblionumber, + priority => 1, + itemnumber => undef, + } + ); + + my $remaining_holds_total = Koha::Holds->remaining_holds_today; + is( $remaining_holds_total, 0, "No patron was passed to calculate their holds" ); + + $remaining_holds_total = Koha::Holds->remaining_holds_today({ patron => $patron_1, biblio => $biblio }); + is( $remaining_holds_total, 1, "Patron has one more allowed hold based on holds_per_day circulation rule" ); +}; + $schema->storage->txn_rollback; 1; -- 2.11.0