View | Details | Raw Unified | Return to bug 29483
Collapse All | Expand All

(-)a/C4/Circulation.pm (-16 / +35 lines)
Lines 27-33 use Encode; Link Here
27
use Koha::DateUtils qw( dt_from_string output_pref );
27
use Koha::DateUtils qw( dt_from_string output_pref );
28
use C4::Context;
28
use C4::Context;
29
use C4::Stats qw( UpdateStats );
29
use C4::Stats qw( UpdateStats );
30
use C4::Reserves qw( CheckReserves CanItemBeReserved MoveReserve ModReserve ModReserveMinusPriority RevertWaitingStatus IsItemOnHoldAndFound IsAvailableForItemLevelRequest );
30
use C4::Reserves qw( CheckReserves CanItemBeReserved MoveReserve ModReserve ModReserveMinusPriority RevertWaitingStatus IsItemOnHoldAndFound IsAvailableForItemLevelRequest ItemsAnyAvailableAndNotRestricted );
31
use C4::Biblio qw( UpdateTotalIssues );
31
use C4::Biblio qw( UpdateTotalIssues );
32
use C4::Items qw( ModItemTransfer ModDateLastSeen CartToShelf );
32
use C4::Items qw( ModItemTransfer ModDateLastSeen CartToShelf );
33
use C4::Accounts;
33
use C4::Accounts;
Lines 2835-2871 sub CanBookBeRenewed { Link Here
2835
        else {
2835
        else {
2836
2836
2837
            # Get all other items that could possibly fill reserves
2837
            # Get all other items that could possibly fill reserves
2838
            # FIXME We could join reserves (or more tables) here to eliminate some checks later
2838
            my $items = Koha::Items->search({
2839
            my $items = Koha::Items->search({
2839
                biblionumber => $resrec->{biblionumber},
2840
                biblionumber => $resrec->{biblionumber},
2840
                onloan       => undef,
2841
                onloan       => undef,
2841
                notforloan   => 0,
2842
                notforloan   => 0,
2842
                -not         => { itemnumber => $itemnumber }
2843
                -not         => { itemnumber => $itemnumber }
2843
            });
2844
            });
2845
            my $item_count = $items->count();
2844
2846
2845
            # Get all other reserves that could have been filled by this item
2847
            # Get all other reserves that could have been filled by this item
2846
            my @borrowernumbers = map { $_->{borrowernumber} } @$possible_reserves;
2848
            my @borrowernumbers = map { $_->{borrowernumber} } @$possible_reserves;
2849
            # Note: fetching the patrons in this manner means that a patron with 2 holds will
2850
            # not block renewal if one reserve can be satisfied i.e. each patron is checked once
2847
            my $patrons = Koha::Patrons->search({
2851
            my $patrons = Koha::Patrons->search({
2848
                borrowernumber => { -in => \@borrowernumbers }
2852
                borrowernumber => { -in => \@borrowernumbers }
2849
            });
2853
            });
2850
2854
            my $patron_count = $patrons->count();
2851
            # If the count of the union of the lists of reservable items for each borrower
2855
2852
            # is equal or greater than the number of borrowers, we know that all reserves
2856
            return ( 0, "on_reserve" ) if ($patron_count > $item_count);
2853
            # can be filled with available items. We can get the union of the sets simply
2857
            # We cannot possibly fill all reserves if we don't have enough items
2854
            # by pushing all the elements onto an array and removing the duplicates.
2858
2855
            my @reservable;
2859
            # If we can fill each hold that has been found with the available items on the record
2856
            ITEM: while ( my $item = $items->next ) {
2860
            # then the patron can renew. If we cannot, they cannot renew.
2857
                next if IsItemOnHoldAndFound( $item->itemnumber );
2861
            # FIXME This code does not check whether the item we are renewing can fill
2858
                while ( my $patron = $patrons->next ) {
2862
            # any of the existing reserves.
2859
                    next unless IsAvailableForItemLevelRequest($item, $patron);
2863
            my $reservable = 0;
2860
                    next unless CanItemBeReserved($patron->borrowernumber,$item->itemnumber,undef,{ignore_hold_counts=>1})->{status} eq 'OK';
2864
            my %matched_items;
2861
                    push @reservable, $item->itemnumber;
2865
            my $seen = 0;
2862
                    if (@reservable >= @borrowernumbers) {
2866
            PATRON: while ( my $patron = $patrons->next ) {
2867
                # If there is a reserve that cannot be filled we are done
2868
                return ( 0, "on_reserve" ) if ( $seen > $reservable );
2869
                my $items_any_available = ItemsAnyAvailableAndNotRestricted( { biblionumber => $item->biblionumber, patron => $patron });
2870
                while ( my $other_item = $items->next ) {
2871
                    next if defined $matched_items{$other_item->itemnumber};
2872
                    next if IsItemOnHoldAndFound( $other_item->itemnumber );
2873
                    next unless IsAvailableForItemLevelRequest($other_item, $patron, undef, $items_any_available);
2874
                    next unless CanItemBeReserved($patron->borrowernumber,$other_item->itemnumber,undef,{ignore_hold_counts=>1})->{status} eq 'OK';
2875
                    # NOTE: At checkin we call 'CheckReserves' which checks hold 'policy'
2876
                    # CanItemBeReserved checks 'rules' and 'policies' which means
2877
                    # items will fill holds at checkin that are rejected here
2878
                    $reservable++;
2879
                    if ($reservable >= $patron_count) {
2863
                        $resfound = 0;
2880
                        $resfound = 0;
2864
                        last ITEM;
2881
                        last PATRON;
2865
                    }
2882
                    }
2883
                    $matched_items{$other_item->itemnumber} = 1;
2866
                    last;
2884
                    last;
2867
                }
2885
                }
2868
                $patrons->reset;
2886
                $items->reset;
2887
                $seen++;
2869
            }
2888
            }
2870
        }
2889
        }
2871
    }
2890
    }
(-)a/t/db_dependent/Circulation.t (-36 / +176 lines)
Lines 1556-1571 subtest "Bug 13841 - Do not create new 0 amount fines" => sub { Link Here
1556
};
1556
};
1557
1557
1558
subtest "AllowRenewalIfOtherItemsAvailable tests" => sub {
1558
subtest "AllowRenewalIfOtherItemsAvailable tests" => sub {
1559
    $dbh->do('DELETE FROM issues');
1559
    plan tests => 13;
1560
    $dbh->do('DELETE FROM items');
1560
    my $biblio = $builder->build_sample_biblio();
1561
    $dbh->do('DELETE FROM circulation_rules');
1561
    my $item_1 = $builder->build_sample_item(
1562
        {
1563
            biblionumber     => $biblio->biblionumber,
1564
            library          => $library2->{branchcode},
1565
        }
1566
    );
1567
    my $item_2= $builder->build_sample_item(
1568
        {
1569
            biblionumber     => $biblio->biblionumber,
1570
            library          => $library2->{branchcode},
1571
            itype            => $item_1->effective_itemtype,
1572
        }
1573
    );
1574
1562
    Koha::CirculationRules->set_rules(
1575
    Koha::CirculationRules->set_rules(
1563
        {
1576
        {
1564
            categorycode => undef,
1577
            categorycode => undef,
1565
            itemtype     => undef,
1578
            itemtype     => $item_1->effective_itemtype,
1566
            branchcode   => undef,
1579
            branchcode   => undef,
1567
            rules        => {
1580
            rules        => {
1568
                reservesallowed => 25,
1581
                reservesallowed => 25,
1582
                holds_per_record => 25,
1569
                issuelength     => 14,
1583
                issuelength     => 14,
1570
                lengthunit      => 'days',
1584
                lengthunit      => 'days',
1571
                renewalsallowed => 1,
1585
                renewalsallowed => 1,
Lines 1578-1600 subtest "AllowRenewalIfOtherItemsAvailable tests" => sub { Link Here
1578
            }
1592
            }
1579
        }
1593
        }
1580
    );
1594
    );
1581
    my $biblio = $builder->build_sample_biblio();
1582
1595
1583
    my $item_1 = $builder->build_sample_item(
1584
        {
1585
            biblionumber     => $biblio->biblionumber,
1586
            library          => $library2->{branchcode},
1587
            itype            => $itemtype,
1588
        }
1589
    );
1590
1591
    my $item_2= $builder->build_sample_item(
1592
        {
1593
            biblionumber     => $biblio->biblionumber,
1594
            library          => $library2->{branchcode},
1595
            itype            => $itemtype,
1596
        }
1597
    );
1598
1596
1599
    my $borrowernumber1 = Koha::Patron->new({
1597
    my $borrowernumber1 = Koha::Patron->new({
1600
        firstname    => 'Kyle',
1598
        firstname    => 'Kyle',
Lines 1608-1613 subtest "AllowRenewalIfOtherItemsAvailable tests" => sub { Link Here
1608
        categorycode => $patron_category->{categorycode},
1606
        categorycode => $patron_category->{categorycode},
1609
        branchcode   => $library2->{branchcode},
1607
        branchcode   => $library2->{branchcode},
1610
    })->store->borrowernumber;
1608
    })->store->borrowernumber;
1609
    my $patron_category_2 = $builder->build(
1610
        {
1611
            source => 'Category',
1612
            value  => {
1613
                category_type                 => 'P',
1614
                enrolmentfee                  => 0,
1615
                BlockExpiredPatronOpacActions => -1, # Pick the pref value
1616
            }
1617
        }
1618
    );
1619
    my $borrowernumber3 = Koha::Patron->new({
1620
        firstname    => 'Carnegie',
1621
        surname      => 'Hall',
1622
        categorycode => $patron_category_2->{categorycode},
1623
        branchcode   => $library2->{branchcode},
1624
    })->store->borrowernumber;
1611
1625
1612
    my $borrower1 = Koha::Patrons->find( $borrowernumber1 )->unblessed;
1626
    my $borrower1 = Koha::Patrons->find( $borrowernumber1 )->unblessed;
1613
    my $borrower2 = Koha::Patrons->find( $borrowernumber2 )->unblessed;
1627
    my $borrower2 = Koha::Patrons->find( $borrowernumber2 )->unblessed;
Lines 1629-1635 subtest "AllowRenewalIfOtherItemsAvailable tests" => sub { Link Here
1629
    Koha::CirculationRules->set_rules(
1643
    Koha::CirculationRules->set_rules(
1630
        {
1644
        {
1631
            categorycode => undef,
1645
            categorycode => undef,
1632
            itemtype     => undef,
1646
            itemtype     => $item_1->effective_itemtype,
1633
            branchcode   => undef,
1647
            branchcode   => undef,
1634
            rules        => {
1648
            rules        => {
1635
                onshelfholds => 0,
1649
                onshelfholds => 0,
Lines 1640-1692 subtest "AllowRenewalIfOtherItemsAvailable tests" => sub { Link Here
1640
    ( $renewokay, $error ) = CanBookBeRenewed( $borrowernumber1, $item_1->itemnumber );
1654
    ( $renewokay, $error ) = CanBookBeRenewed( $borrowernumber1, $item_1->itemnumber );
1641
    is( $renewokay, 0, 'Bug 14337 - Verify the borrower cannot renew with a hold on the record if AllowRenewalIfOtherItemsAvailable and onshelfholds are disabled' );
1655
    is( $renewokay, 0, 'Bug 14337 - Verify the borrower cannot renew with a hold on the record if AllowRenewalIfOtherItemsAvailable and onshelfholds are disabled' );
1642
1656
1657
    t::lib::Mocks::mock_preference( 'AllowRenewalIfOtherItemsAvailable', 1 );
1658
    ( $renewokay, $error ) = CanBookBeRenewed( $borrowernumber1, $item_1->itemnumber );
1659
    is( $renewokay, 0, 'Bug 14337 - Verify the borrower cannot renew with a hold on the record if AllowRenewalIfOtherItemsAvailable is enabled and onshelfholds is disabled' );
1660
1643
    Koha::CirculationRules->set_rules(
1661
    Koha::CirculationRules->set_rules(
1644
        {
1662
        {
1645
            categorycode => undef,
1663
            categorycode => undef,
1646
            itemtype     => undef,
1664
            itemtype     => $item_1->effective_itemtype,
1647
            branchcode   => undef,
1665
            branchcode   => undef,
1648
            rules        => {
1666
            rules        => {
1649
                onshelfholds => 0,
1667
                onshelfholds => 1,
1650
            }
1668
            }
1651
        }
1669
        }
1652
    );
1670
    );
1671
    t::lib::Mocks::mock_preference( 'AllowRenewalIfOtherItemsAvailable', 0 );
1672
    ( $renewokay, $error ) = CanBookBeRenewed( $borrowernumber1, $item_1->itemnumber );
1673
    is( $renewokay, 0, 'Bug 14337 - Verify the borrower cannot renew with a hold on the record if AllowRenewalIfOtherItemsAvailable is disabled and onshelfhold is enabled' );
1674
1653
    t::lib::Mocks::mock_preference( 'AllowRenewalIfOtherItemsAvailable', 1 );
1675
    t::lib::Mocks::mock_preference( 'AllowRenewalIfOtherItemsAvailable', 1 );
1654
    ( $renewokay, $error ) = CanBookBeRenewed( $borrowernumber1, $item_1->itemnumber );
1676
    ( $renewokay, $error ) = CanBookBeRenewed( $borrowernumber1, $item_1->itemnumber );
1655
    is( $renewokay, 0, 'Bug 14337 - Verify the borrower cannot renew with a hold on the record if AllowRenewalIfOtherItemsAvailable is enabled and onshelfholds is disabled' );
1677
    is( $renewokay, 1, 'Bug 14337 - Verify the borrower can renew with a hold on the record if AllowRenewalIfOtherItemsAvailable and onshelfhold are enabled' );
1678
1679
    AddReserve(
1680
        {
1681
            branchcode     => $library2->{branchcode},
1682
            borrowernumber => $borrowernumber3,
1683
            biblionumber   => $biblio->biblionumber,
1684
            priority       => 1,
1685
        }
1686
    );
1687
1688
    ( $renewokay, $error ) = CanBookBeRenewed( $borrowernumber1, $item_1->itemnumber );
1689
    is( $renewokay, 0, 'Verify the borrower cannot renew with 2 holds on the record if AllowRenewalIfOtherItemsAvailable and onshelfhold are enabled and one other item on record' );
1690
1691
    my $item_3= $builder->build_sample_item(
1692
        {
1693
            biblionumber     => $biblio->biblionumber,
1694
            library          => $library2->{branchcode},
1695
            itype            => $item_1->effective_itemtype,
1696
        }
1697
    );
1698
1699
    ( $renewokay, $error ) = CanBookBeRenewed( $borrowernumber1, $item_1->itemnumber );
1700
    is( $renewokay, 1, 'Verify the borrower cannot renew with 2 holds on the record if AllowRenewalIfOtherItemsAvailable and onshelfhold are enabled and two other items on record' );
1656
1701
1657
    Koha::CirculationRules->set_rules(
1702
    Koha::CirculationRules->set_rules(
1658
        {
1703
        {
1659
            categorycode => undef,
1704
            categorycode => $patron_category_2->{categorycode},
1660
            itemtype     => undef,
1705
            itemtype     => $item_1->effective_itemtype,
1661
            branchcode   => undef,
1706
            branchcode   => undef,
1662
            rules        => {
1707
            rules        => {
1663
                onshelfholds => 1,
1708
                reservesallowed => 0,
1664
            }
1709
            }
1665
        }
1710
        }
1666
    );
1711
    );
1667
    t::lib::Mocks::mock_preference( 'AllowRenewalIfOtherItemsAvailable', 0 );
1712
1668
    ( $renewokay, $error ) = CanBookBeRenewed( $borrowernumber1, $item_1->itemnumber );
1713
    ( $renewokay, $error ) = CanBookBeRenewed( $borrowernumber1, $item_1->itemnumber );
1669
    is( $renewokay, 0, 'Bug 14337 - Verify the borrower cannot renew with a hold on the record if AllowRenewalIfOtherItemsAvailable is disabled and onshelfhold is enabled' );
1714
    is( $renewokay, 0, 'Verify the borrower cannot renew with 2 holds on the record, but only one of those holds can be filled when AllowRenewalIfOtherItemsAvailable and onshelfhold are enabled and two other items on record' );
1670
1715
1671
    Koha::CirculationRules->set_rules(
1716
    Koha::CirculationRules->set_rules(
1672
        {
1717
        {
1673
            categorycode => undef,
1718
            categorycode => $patron_category_2->{categorycode},
1674
            itemtype     => undef,
1719
            itemtype     => $item_1->effective_itemtype,
1675
            branchcode   => undef,
1720
            branchcode   => undef,
1676
            rules        => {
1721
            rules        => {
1677
                onshelfholds => 1,
1722
                reservesallowed => 25,
1678
            }
1723
            }
1679
        }
1724
        }
1680
    );
1725
    );
1681
    t::lib::Mocks::mock_preference( 'AllowRenewalIfOtherItemsAvailable', 1 );
1682
    ( $renewokay, $error ) = CanBookBeRenewed( $borrowernumber1, $item_1->itemnumber );
1683
    is( $renewokay, 1, 'Bug 14337 - Verify the borrower can renew with a hold on the record if AllowRenewalIfOtherItemsAvailable and onshelfhold are enabled' );
1684
1726
1685
    # Setting item not checked out to be not for loan but holdable
1727
    # Setting item not checked out to be not for loan but holdable
1686
    $item_2->notforloan(-1)->store;
1728
    $item_2->notforloan(-1)->store;
1687
1729
1688
    ( $renewokay, $error ) = CanBookBeRenewed( $borrowernumber1, $item_1->itemnumber );
1730
    ( $renewokay, $error ) = CanBookBeRenewed( $borrowernumber1, $item_1->itemnumber );
1689
    is( $renewokay, 0, 'Bug 14337 - Verify the borrower can not renew with a hold on the record if AllowRenewalIfOtherItemsAvailable is enabled but the only available item is notforloan' );
1731
    is( $renewokay, 0, 'Bug 14337 - Verify the borrower can not renew with a hold on the record if AllowRenewalIfOtherItemsAvailable is enabled but the only available item is notforloan' );
1732
1733
    my $mock_circ = Test::MockModule->new("C4::Circulation");
1734
    $mock_circ->mock( CanItemBeReserved => sub {
1735
        warn "Checked";
1736
        return { status => 'no' }
1737
    } );
1738
1739
    $item_2->notforloan(0)->store;
1740
    $item_3->delete();
1741
    # Two items total, one item available, one issued, two holds on record
1742
1743
    warnings_are{
1744
       ( $renewokay, $error ) = CanBookBeRenewed( $borrowernumber1, $item_1->itemnumber );
1745
    } [], "CanItemBeReserved not called when there are more possible holds than available items";
1746
    is( $renewokay, 0, 'Borrower cannot renew when there are more holds than available items' );
1747
1748
    $item_3 = $builder->build_sample_item(
1749
        {
1750
            biblionumber     => $biblio->biblionumber,
1751
            library          => $library2->{branchcode},
1752
            itype            => $item_1->effective_itemtype,
1753
        }
1754
    );
1755
1756
    Koha::CirculationRules->set_rules(
1757
        {
1758
            categorycode => undef,
1759
            itemtype     => $item_1->effective_itemtype,
1760
            branchcode   => undef,
1761
            rules        => {
1762
                reservesallowed => 0,
1763
            }
1764
        }
1765
    );
1766
1767
    warnings_are{
1768
       ( $renewokay, $error ) = CanBookBeRenewed( $borrowernumber1, $item_1->itemnumber );
1769
    } ["Checked","Checked"], "CanItemBeReserved only called once per available item if it returns a negative result for all items for a borrower";
1770
    is( $renewokay, 0, 'Borrower cannot renew when there are more holds than available items' );
1771
1690
};
1772
};
1691
1773
1692
{
1774
{
Lines 1771-1776 subtest 'CanBookBeIssued & AllowReturnToBranch' => sub { Link Here
1771
            holdingbranch => $holdingbranch->{branchcode},
1853
            holdingbranch => $holdingbranch->{branchcode},
1772
        }
1854
        }
1773
    );
1855
    );
1856
    Koha::CirculationRules->set_rules(
1857
        {
1858
            categorycode => undef,
1859
            itemtype     => $item->effective_itemtype,
1860
            branchcode   => undef,
1861
            rules        => {
1862
                reservesallowed => 25,
1863
                issuelength     => 14,
1864
                lengthunit      => 'days',
1865
                renewalsallowed => 1,
1866
                renewalperiod   => 7,
1867
                norenewalbefore => undef,
1868
                auto_renew      => 0,
1869
                fine            => .10,
1870
                chargeperiod    => 1,
1871
                maxissueqty     => 20
1872
            }
1873
        }
1874
    );
1774
1875
1775
    set_userenv($holdingbranch);
1876
    set_userenv($holdingbranch);
1776
1877
Lines 1924-1929 subtest 'CanBookBeIssued + Koha::Patron->is_debarred|has_overdues' => sub { Link Here
1924
            library => $library->{branchcode},
2025
            library => $library->{branchcode},
1925
        }
2026
        }
1926
    );
2027
    );
2028
    Koha::CirculationRules->set_rules(
2029
        {
2030
            categorycode => undef,
2031
            itemtype     => undef,
2032
            branchcode   => $library->{branchcode},
2033
            rules        => {
2034
                reservesallowed => 25,
2035
                issuelength     => 14,
2036
                lengthunit      => 'days',
2037
                renewalsallowed => 1,
2038
                renewalperiod   => 7,
2039
                norenewalbefore => undef,
2040
                auto_renew      => 0,
2041
                fine            => .10,
2042
                chargeperiod    => 1,
2043
                maxissueqty     => 20
2044
            }
2045
        }
2046
    );
2047
1927
2048
1928
    my ( $error, $question, $alerts );
2049
    my ( $error, $question, $alerts );
1929
2050
Lines 2121-2126 subtest 'CanBookBeIssued + AllowMultipleIssuesOnABiblio' => sub { Link Here
2121
        }
2242
        }
2122
    );
2243
    );
2123
2244
2245
    Koha::CirculationRules->set_rules(
2246
        {
2247
            categorycode => undef,
2248
            itemtype     => undef,
2249
            branchcode   => $library->{branchcode},
2250
            rules        => {
2251
                reservesallowed => 25,
2252
                issuelength     => 14,
2253
                lengthunit      => 'days',
2254
                renewalsallowed => 1,
2255
                renewalperiod   => 7,
2256
                norenewalbefore => undef,
2257
                auto_renew      => 0,
2258
                fine            => .10,
2259
                chargeperiod    => 1,
2260
                maxissueqty     => 20
2261
            }
2262
        }
2263
    );
2264
2124
    my ( $error, $question, $alerts );
2265
    my ( $error, $question, $alerts );
2125
    my $issue = AddIssue( $patron->unblessed, $item_1->barcode, dt_from_string->add( days => 1 ) );
2266
    my $issue = AddIssue( $patron->unblessed, $item_1->barcode, dt_from_string->add( days => 1 ) );
2126
2267
2127
- 

Return to bug 29483