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

(-)a/C4/Reserves.pm (-7 / +40 lines)
Lines 915-921 sub CheckReserves { Link Here
915
           itemtypes.notforloan,
915
           itemtypes.notforloan,
916
           items.notforloan AS itemnotforloan,
916
           items.notforloan AS itemnotforloan,
917
           items.itemnumber,
917
           items.itemnumber,
918
           items.damaged
918
           items.damaged,
919
           items.homebranch,
920
           items.holdingbranch
919
           FROM   items
921
           FROM   items
920
           LEFT JOIN biblioitems ON items.biblioitemnumber = biblioitems.biblioitemnumber
922
           LEFT JOIN biblioitems ON items.biblioitemnumber = biblioitems.biblioitemnumber
921
           LEFT JOIN itemtypes   ON items.itype   = itemtypes.itemtype
923
           LEFT JOIN itemtypes   ON items.itype   = itemtypes.itemtype
Lines 928-934 sub CheckReserves { Link Here
928
           itemtypes.notforloan,
930
           itemtypes.notforloan,
929
           items.notforloan AS itemnotforloan,
931
           items.notforloan AS itemnotforloan,
930
           items.itemnumber,
932
           items.itemnumber,
931
           items.damaged
933
           items.damaged,
934
           items.homebranch,
935
           items.holdingbranch
932
           FROM   items
936
           FROM   items
933
           LEFT JOIN biblioitems ON items.biblioitemnumber = biblioitems.biblioitemnumber
937
           LEFT JOIN biblioitems ON items.biblioitemnumber = biblioitems.biblioitemnumber
934
           LEFT JOIN itemtypes   ON biblioitems.itemtype   = itemtypes.itemtype
938
           LEFT JOIN itemtypes   ON biblioitems.itemtype   = itemtypes.itemtype
Lines 944-957 sub CheckReserves { Link Here
944
        $sth->execute($barcode);
948
        $sth->execute($barcode);
945
    }
949
    }
946
    # note: we get the itemnumber because we might have started w/ just the barcode.  Now we know for sure we have it.
950
    # note: we get the itemnumber because we might have started w/ just the barcode.  Now we know for sure we have it.
947
    my ( $biblio, $bibitem, $notforloan_per_itemtype, $notforloan_per_item, $itemnumber, $damaged ) = $sth->fetchrow_array;
951
    my ( $biblio, $bibitem, $notforloan_per_itemtype, $notforloan_per_item, $itemnumber, $damaged, $item_homebranch, $item_holdingbranch ) = $sth->fetchrow_array;
948
952
949
    return if ( $damaged && !C4::Context->preference('AllowHoldsOnDamagedItems') );
953
    return if ( $damaged && !C4::Context->preference('AllowHoldsOnDamagedItems') );
950
954
951
    return unless $itemnumber; # bail if we got nothing.
955
    return unless $itemnumber; # bail if we got nothing.
952
956
953
    # if item is not for loan it cannot be reserved either.....
957
    # if item is not for loan it cannot be reserved either.....
954
    #    execpt where items.notforloan < 0 :  This indicates the item is holdable. 
958
    # except where items.notforloan < 0 :  This indicates the item is holdable.
955
    return if  ( $notforloan_per_item > 0 ) or $notforloan_per_itemtype;
959
    return if  ( $notforloan_per_item > 0 ) or $notforloan_per_itemtype;
956
960
957
    # Find this item in the reserves
961
    # Find this item in the reserves
Lines 963-983 sub CheckReserves { Link Here
963
    # $highest is the most important item we've seen so far.
967
    # $highest is the most important item we've seen so far.
964
    my $highest;
968
    my $highest;
965
    if (scalar @reserves) {
969
    if (scalar @reserves) {
970
        my $LocalHoldsPriority = C4::Context->preference('LocalHoldsPriority');
971
        my $LocalHoldsPriorityPatronControl = C4::Context->preference('LocalHoldsPriorityPatronControl');
972
        my $LocalHoldsPriorityItemControl = C4::Context->preference('LocalHoldsPriorityItemControl');
973
966
        my $priority = 10000000;
974
        my $priority = 10000000;
967
        foreach my $res (@reserves) {
975
        foreach my $res (@reserves) {
968
            if ( $res->{'itemnumber'} == $itemnumber && $res->{'priority'} == 0) {
976
            if ( $res->{'itemnumber'} == $itemnumber && $res->{'priority'} == 0) {
969
                return ( "Waiting", $res, \@reserves ); # Found it
977
                return ( "Waiting", $res, \@reserves ); # Found it
970
            } else {
978
            } else {
979
                # Lazy fetch for borrower and item. We only need to know about the patron and item
980
                # each and every time if we are using LocalHoldsPriority. This is a great place to
981
                # leverage the inherent lazy fetching of DBIx::Class.
982
                my $borrowerinfo;
983
                my $iteminfo;
984
985
                my $local_hold_match;
986
                if ($LocalHoldsPriority) {
987
                    $borrowerinfo = C4::Members::GetMember( borrowernumber => $res->{'borrowernumber'} );
988
                    $iteminfo = C4::Items::GetItem($itemnumber);
989
990
                    my $local_holds_priority_item_branchcode =
991
                      $iteminfo->{$LocalHoldsPriorityItemControl};
992
                    my $local_holds_priority_patron_branchcode =
993
                      ( $LocalHoldsPriorityPatronControl eq 'PickupLibrary' )
994
                      ? $res->{branchcode}
995
                      : ( $LocalHoldsPriorityPatronControl eq 'HomeLibrary' )
996
                      ? $borrowerinfo->{branchcode}
997
                      : undef;
998
                    $local_hold_match =
999
                      $local_holds_priority_item_branchcode eq
1000
                      $local_holds_priority_patron_branchcode;
1001
                }
1002
971
                # See if this item is more important than what we've got so far
1003
                # See if this item is more important than what we've got so far
972
                if ( $res->{'priority'} && $res->{'priority'} < $priority ) {
1004
                if ( ( $res->{'priority'} && $res->{'priority'} < $priority ) || $local_hold_match ) {
973
                    my $borrowerinfo=C4::Members::GetMember(borrowernumber => $res->{'borrowernumber'});
1005
                    $borrowerinfo ||= C4::Members::GetMember( borrowernumber => $res->{'borrowernumber'} );
974
                    my $iteminfo=C4::Items::GetItem($itemnumber);
1006
                    $iteminfo ||= C4::Items::GetItem($itemnumber);
975
                    my $branch = GetReservesControlBranch( $iteminfo, $borrowerinfo );
1007
                    my $branch = GetReservesControlBranch( $iteminfo, $borrowerinfo );
976
                    my $branchitemrule = C4::Circulation::GetBranchItemRule($branch,$iteminfo->{'itype'});
1008
                    my $branchitemrule = C4::Circulation::GetBranchItemRule($branch,$iteminfo->{'itype'});
977
                    next if ($branchitemrule->{'holdallowed'} == 0);
1009
                    next if ($branchitemrule->{'holdallowed'} == 0);
978
                    next if (($branchitemrule->{'holdallowed'} == 1) && ($branch ne $borrowerinfo->{'branchcode'}));
1010
                    next if (($branchitemrule->{'holdallowed'} == 1) && ($branch ne $borrowerinfo->{'branchcode'}));
979
                    $priority = $res->{'priority'};
1011
                    $priority = $res->{'priority'};
980
                    $highest  = $res;
1012
                    $highest  = $res;
1013
                    last if $local_hold_match;
981
                }
1014
                }
982
            }
1015
            }
983
        }
1016
        }
(-)a/installer/data/mysql/sysprefs.sql (+3 lines)
Lines 182-187 INSERT INTO systempreferences ( `variable`, `value`, `options`, `explanation`, ` Link Here
182
('LinkerOptions','','','A pipe-separated list of options for the linker.','free'),
182
('LinkerOptions','','','A pipe-separated list of options for the linker.','free'),
183
('LinkerRelink','1',NULL,'If ON the authority linker will relink headings that have previously been linked every time it runs.','YesNo'),
183
('LinkerRelink','1',NULL,'If ON the authority linker will relink headings that have previously been linked every time it runs.','YesNo'),
184
('LocalCoverImages','0','1','Display local cover images on intranet details pages.','YesNo'),
184
('LocalCoverImages','0','1','Display local cover images on intranet details pages.','YesNo'),
185
('LocalHoldsPriority',  '0', NULL,  'Enables the LocalHoldsPriority feature',  'YesNo'),
186
('LocalHoldsPriorityItemControl',  'holdingbranch',  'holdingbranch|homebranch',  'decides if the feature operates using the item''s home or holding library.',  'Choice'),
187
('LocalHoldsPriorityPatronControl',  'PickupLibrary',  'HomeLibrary|PickupLibrary',  'decides if the feature operates using the library set as the patron''s home library, or the library set as the pickup library for the given hold.',  'Choice'),
185
('ManInvInNoissuesCharge','1',NULL,'MANUAL_INV charges block checkouts (added to noissuescharge).','YesNo'),
188
('ManInvInNoissuesCharge','1',NULL,'MANUAL_INV charges block checkouts (added to noissuescharge).','YesNo'),
186
('MARCAuthorityControlField008','|| aca||aabn           | a|a     d',NULL,'Define the contents of MARC21 authority control field 008 position 06-39','Textarea'),
189
('MARCAuthorityControlField008','|| aca||aabn           | a|a     d',NULL,'Define the contents of MARC21 authority control field 008 position 06-39','Textarea'),
187
('MarcFieldsToOrder','',NULL,'Set the mapping values for a new order line created from a MARC record in a staged file. In a YAML format.','textarea'),
190
('MarcFieldsToOrder','',NULL,'Set the mapping values for a new order line created from a MARC record in a staged file. In a YAML format.','textarea'),
(-)a/installer/data/mysql/updatedatabase.pl (+12 lines)
Lines 8780-8785 if ( CheckVersion($DBversion) ) { Link Here
8780
    SetVersion($DBversion);
8780
    SetVersion($DBversion);
8781
}
8781
}
8782
8782
8783
$DBversion = "3.17.00.XXX";
8784
if ( CheckVersion($DBversion) ) {
8785
    $dbh->do(q{
8786
        INSERT INTO systempreferences ( variable, value, options, explanation, type ) VALUES
8787
        ('LocalHoldsPriority',  '0', NULL,  'Enables the LocalHoldsPriority feature',  'YesNo'),
8788
        ('LocalHoldsPriorityItemControl',  'holdingbranch',  'holdingbranch|homebranch',  'decides if the feature operates using the item''s home or holding library.',  'Choice'),
8789
        ('LocalHoldsPriorityPatronControl',  'PickupLibrary',  'HomeLibrary|PickupLibrary',  'decides if the feature operates using the library set as the patron''s home library, or the library set as the pickup library for the given hold.',  'Choice')
8790
    });
8791
    print "Upgrade to $DBversion done (Bug 11126 - Make the holds system optionally give precedence to local holds)\n";
8792
    SetVersion($DBversion);
8793
}
8794
8783
=head1 FUNCTIONS
8795
=head1 FUNCTIONS
8784
8796
8785
=head2 TableExists($table)
8797
=head2 TableExists($table)
(-)a/koha-tmpl/intranet-tmpl/prog/en/modules/admin/preferences/circulation.pref (+15 lines)
Lines 534-539 Circulation: Link Here
534
                  yes: Allow
534
                  yes: Allow
535
                  no: "Don't allow"
535
                  no: "Don't allow"
536
            - a patron to place a hold on a record where the patron already has one or more items attached to that record checked out.
536
            - a patron to place a hold on a record where the patron already has one or more items attached to that record checked out.
537
        -
538
            - pref: LocalHoldsPriority
539
              choices:
540
                  yes: Give
541
                  no: "Don't give"
542
            - priority for filling holds to patrons whose
543
            - pref: LocalHoldsPriorityPatronControl
544
              choices:
545
                  PickupLibrary: "pickup library"
546
                  HomeLibrary: "home library"
547
            - matches the item's
548
            - pref: LocalHoldsPriorityItemControl
549
              choices:
550
                  homebranch: "home library"
551
                  holdingbranch: "holding library"
537
    Fines Policy:
552
    Fines Policy:
538
        -
553
        -
539
            - Calculate fines based on days overdue
554
            - Calculate fines based on days overdue
(-)a/t/db_dependent/Holds/LocalHoldsPriority.t (-1 / +119 lines)
Line 0 Link Here
0
- 
1
#!/usr/bin/perl
2
3
use strict;
4
use warnings;
5
6
use t::lib::Mocks;
7
use C4::Context;
8
use C4::Branch;
9
10
use Test::More tests => 6;
11
use MARC::Record;
12
use C4::Biblio;
13
use C4::Items;
14
use C4::Members;
15
16
BEGIN {
17
    use FindBin;
18
    use lib $FindBin::Bin;
19
    use_ok('C4::Reserves');
20
}
21
22
my $dbh = C4::Context->dbh;
23
24
# Start transaction
25
$dbh->{AutoCommit} = 0;
26
$dbh->{RaiseError} = 1;
27
28
my $borrowers_count = 5;
29
30
# Setup Test------------------------
31
# Helper biblio.
32
diag("Creating biblio instance for testing.");
33
my ( $bibnum, $title, $bibitemnum ) = create_helper_biblio();
34
35
# Helper item for that biblio.
36
diag("Creating item instance for testing.");
37
my ( $item_bibnum, $item_bibitemnum, $itemnumber ) =
38
  AddItem( { homebranch => 'MPL', holdingbranch => 'CPL' }, $bibnum );
39
40
my @branchcodes = qw{XXX RPL CPL MPL CPL MPL};
41
42
# Create some borrowers
43
diag("Creating borrowers.");
44
my @borrowernumbers;
45
foreach ( 1 .. $borrowers_count ) {
46
    my $borrowernumber = AddMember(
47
        firstname    => 'my firstname',
48
        surname      => 'my surname ' . $_,
49
        categorycode => 'S',
50
        branchcode   => $branchcodes[$_],
51
    );
52
    push @borrowernumbers, $borrowernumber;
53
}
54
55
my $biblionumber = $bibnum;
56
57
my @branches = GetBranchesLoop();
58
my $branch   = $branches[0][0]{value};
59
60
# Create five item level holds
61
diag("Creating holds.");
62
my $i = 1;
63
foreach my $borrowernumber (@borrowernumbers) {
64
    AddReserve(
65
        $branchcodes[$i],
66
        $borrowernumber,
67
        $biblionumber,
68
        my $constraint = 'a',
69
        my $bibitems   = q{},
70
        my $priority = $i,
71
        my $resdate,
72
        my $expdate,
73
        my $notes = q{},
74
        $title,
75
        my $checkitem,
76
        my $found,
77
    );
78
79
    $i++;
80
}
81
82
my ($status, $reserve, $all_reserves);
83
84
t::lib::Mocks::mock_preference( 'LocalHoldsPriority', 0 );
85
($status, $reserve, $all_reserves) = CheckReserves($itemnumber);
86
ok( $reserve->{borrowernumber} eq $borrowernumbers[0], "Recieved expected results with LocalHoldsPriority disabled" );
87
88
t::lib::Mocks::mock_preference( 'LocalHoldsPriority', 1 );
89
90
t::lib::Mocks::mock_preference( 'LocalHoldsPriorityPatronControl', 'PickupLibrary' );
91
t::lib::Mocks::mock_preference( 'LocalHoldsPriorityItemControl', 'homebranch' );
92
($status, $reserve, $all_reserves) = CheckReserves($itemnumber);
93
ok( $reserve->{borrowernumber} eq $borrowernumbers[2], "Received expected results with PickupLibrary/homebranch" );
94
95
t::lib::Mocks::mock_preference( 'LocalHoldsPriorityPatronControl', 'PickupLibrary' );
96
t::lib::Mocks::mock_preference( 'LocalHoldsPriorityItemControl', 'holdingbranch' );
97
($status, $reserve, $all_reserves) = CheckReserves($itemnumber);
98
ok( $reserve->{borrowernumber} eq $borrowernumbers[1], "Received expected results with PickupLibrary/holdingbranch" );
99
100
t::lib::Mocks::mock_preference( 'LocalHoldsPriorityPatronControl', 'HomeLibrary' );
101
t::lib::Mocks::mock_preference( 'LocalHoldsPriorityItemControl', 'holdingbranch' );
102
($status, $reserve, $all_reserves) = CheckReserves($itemnumber);
103
ok( $reserve->{borrowernumber} eq $borrowernumbers[1], "Received expected results with HomeLibrary/holdingbranch" );
104
105
t::lib::Mocks::mock_preference( 'LocalHoldsPriorityPatronControl', 'HomeLibrary' );
106
t::lib::Mocks::mock_preference( 'LocalHoldsPriorityItemControl', 'homebranch' );
107
($status, $reserve, $all_reserves) = CheckReserves($itemnumber);
108
ok( $reserve->{borrowernumber} eq $borrowernumbers[2], "Received expected results with HomeLibrary/homebranch" );
109
110
# Helper method to set up a Biblio.
111
sub create_helper_biblio {
112
    my $bib   = MARC::Record->new();
113
    my $title = 'Silence in the library';
114
    $bib->append_fields(
115
        MARC::Field->new( '100', ' ', ' ', a => 'Moffat, Steven' ),
116
        MARC::Field->new( '245', ' ', ' ', a => $title ),
117
    );
118
    return ( $bibnum, $title, $bibitemnum ) = AddBiblio( $bib, '' );
119
}

Return to bug 11126