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

(-)a/C4/Auth.pm (-1 lines)
Lines 471-477 sub get_template_and_user { Link Here
471
            OPACAmazonCoverImages                 => C4::Context->preference("OPACAmazonCoverImages"),
471
            OPACAmazonCoverImages                 => C4::Context->preference("OPACAmazonCoverImages"),
472
            OPACFRBRizeEditions                   => C4::Context->preference("OPACFRBRizeEditions"),
472
            OPACFRBRizeEditions                   => C4::Context->preference("OPACFRBRizeEditions"),
473
            OpacHighlightedWords                  => C4::Context->preference("OpacHighlightedWords"),
473
            OpacHighlightedWords                  => C4::Context->preference("OpacHighlightedWords"),
474
            OPACItemHolds                         => C4::Context->preference("OPACItemHolds"),
475
            OPACShelfBrowser                      => "" . C4::Context->preference("OPACShelfBrowser"),
474
            OPACShelfBrowser                      => "" . C4::Context->preference("OPACShelfBrowser"),
476
            OPACURLOpenInNewWindow                => "" . C4::Context->preference("OPACURLOpenInNewWindow"),
475
            OPACURLOpenInNewWindow                => "" . C4::Context->preference("OPACURLOpenInNewWindow"),
477
            OPACUserCSS                           => "" . C4::Context->preference("OPACUserCSS"),
476
            OPACUserCSS                           => "" . C4::Context->preference("OPACUserCSS"),
(-)a/C4/Circulation.pm (-3 / +5 lines)
Lines 1468-1477 Returns a hashref from the issuingrules table. Link Here
1468
sub GetIssuingRule {
1468
sub GetIssuingRule {
1469
    my ( $borrowertype, $itemtype, $branchcode ) = @_;
1469
    my ( $borrowertype, $itemtype, $branchcode ) = @_;
1470
    my $dbh = C4::Context->dbh;
1470
    my $dbh = C4::Context->dbh;
1471
    my $sth =  $dbh->prepare( "select * from issuingrules where categorycode=? and itemtype=? and branchcode=? and issuelength is not null"  );
1471
    my $sth =  $dbh->prepare( "select * from issuingrules where categorycode=? and itemtype=? and branchcode=?"  );
1472
    my $irule;
1472
    my $irule;
1473
1473
1474
	$sth->execute( $borrowertype, $itemtype, $branchcode );
1474
    $sth->execute( $borrowertype, $itemtype, $branchcode );
1475
    $irule = $sth->fetchrow_hashref;
1475
    $irule = $sth->fetchrow_hashref;
1476
    return $irule if defined($irule) ;
1476
    return $irule if defined($irule) ;
1477
1477
Lines 2699-2706 sub CanBookBeRenewed { Link Here
2699
        # by pushing all the elements onto an array and removing the duplicates.
2699
        # by pushing all the elements onto an array and removing the duplicates.
2700
        my @reservable;
2700
        my @reservable;
2701
        foreach my $b (@borrowernumbers) {
2701
        foreach my $b (@borrowernumbers) {
2702
            my ( $borr ) = C4::Members::GetMemberDetails( $b );
2702
            foreach my $i (@itemnumbers) {
2703
            foreach my $i (@itemnumbers) {
2703
                if (   IsAvailableForItemLevelRequest($i)
2704
                my $item   = GetItem($i);
2705
                if (   IsAvailableForItemLevelRequest($item, $borr)
2704
                    && CanItemBeReserved( $b, $i )
2706
                    && CanItemBeReserved( $b, $i )
2705
                    && !IsItemOnHoldAndFound($i) )
2707
                    && !IsItemOnHoldAndFound($i) )
2706
                {
2708
                {
(-)a/C4/ILSDI/Services.pm (-1 / +1 lines)
Lines 491-497 sub GetServices { Link Here
491
    my $canbookbereserved = CanBookBeReserved( $borrower, $biblionumber );
491
    my $canbookbereserved = CanBookBeReserved( $borrower, $biblionumber );
492
    if ($canbookbereserved eq 'OK') {
492
    if ($canbookbereserved eq 'OK') {
493
        push @availablefor, 'title level hold';
493
        push @availablefor, 'title level hold';
494
        my $canitembereserved = IsAvailableForItemLevelRequest($itemnumber);
494
        my $canitembereserved = IsAvailableForItemLevelRequest($item, $borrower);
495
        if ($canitembereserved) {
495
        if ($canitembereserved) {
496
            push @availablefor, 'item level hold';
496
            push @availablefor, 'item level hold';
497
        }
497
        }
(-)a/C4/Items.pm (+2 lines)
Lines 174-179 sub GetItem { Link Here
174
        ($data->{'serialseq'} , $data->{'publisheddate'}) = $ssth->fetchrow_array();
174
        ($data->{'serialseq'} , $data->{'publisheddate'}) = $ssth->fetchrow_array();
175
    }
175
    }
176
	#if we don't have an items.itype, use biblioitems.itemtype.
176
	#if we don't have an items.itype, use biblioitems.itemtype.
177
    # FIXME this should respect the itypes systempreference
178
    # if (C4::Context->preference('item-level_itypes')) {
177
	if( ! $data->{'itype'} ) {
179
	if( ! $data->{'itype'} ) {
178
		my $sth = $dbh->prepare("SELECT itemtype FROM biblioitems  WHERE biblionumber = ?");
180
		my $sth = $dbh->prepare("SELECT itemtype FROM biblioitems  WHERE biblionumber = ?");
179
		$sth->execute($data->{'biblionumber'});
181
		$sth->execute($data->{'biblionumber'});
(-)a/C4/Reserves.pm (-78 / +155 lines)
Lines 70-82 This modules provides somes functions to deal with reservations. Link Here
70
  The complete workflow is :
70
  The complete workflow is :
71
  ==== 1st use case ====
71
  ==== 1st use case ====
72
  patron request a document, 1st available :                      P >0, F=NULL, I=NULL
72
  patron request a document, 1st available :                      P >0, F=NULL, I=NULL
73
  a library having it run "transfertodo", and clic on the list    
73
  a library having it run "transfertodo", and clic on the list
74
         if there is no transfer to do, the reserve waiting
74
         if there is no transfer to do, the reserve waiting
75
         patron can pick it up                                    P =0, F=W,    I=filled 
75
         patron can pick it up                                    P =0, F=W,    I=filled
76
         if there is a transfer to do, write in branchtransfer    P =0, F=T,    I=filled
76
         if there is a transfer to do, write in branchtransfer    P =0, F=T,    I=filled
77
           The pickup library recieve the book, it check in       P =0, F=W,    I=filled
77
           The pickup library recieve the book, it check in       P =0, F=W,    I=filled
78
  The patron borrow the book                                      P =0, F=F,    I=filled
78
  The patron borrow the book                                      P =0, F=F,    I=filled
79
  
79
80
  ==== 2nd use case ====
80
  ==== 2nd use case ====
81
  patron requests a document, a given item,
81
  patron requests a document, a given item,
82
    If pickup is holding branch                                   P =0, F=W,   I=filled
82
    If pickup is holding branch                                   P =0, F=W,   I=filled
Lines 106-114 BEGIN { Link Here
106
        &GetReserveFee
106
        &GetReserveFee
107
        &GetReserveInfo
107
        &GetReserveInfo
108
        &GetReserveStatus
108
        &GetReserveStatus
109
        
109
110
        &GetOtherReserves
110
        &GetOtherReserves
111
        
111
112
        &ModReserveFill
112
        &ModReserveFill
113
        &ModReserveAffect
113
        &ModReserveAffect
114
        &ModReserve
114
        &ModReserve
Lines 116-122 BEGIN { Link Here
116
        &ModReserveCancelAll
116
        &ModReserveCancelAll
117
        &ModReserveMinusPriority
117
        &ModReserveMinusPriority
118
        &MoveReserve
118
        &MoveReserve
119
        
119
120
        &CheckReserves
120
        &CheckReserves
121
        &CanBookBeReserved
121
        &CanBookBeReserved
122
	&CanItemBeReserved
122
	&CanItemBeReserved
Lines 127-133 BEGIN { Link Here
127
        &AutoUnsuspendReserves
127
        &AutoUnsuspendReserves
128
128
129
        &IsAvailableForItemLevelRequest
129
        &IsAvailableForItemLevelRequest
130
        
130
131
        &OPACItemHoldsAllowed
132
131
        &AlterPriority
133
        &AlterPriority
132
        &ToggleLowestPriority
134
        &ToggleLowestPriority
133
135
Lines 140-146 BEGIN { Link Here
140
        IsItemOnHoldAndFound
142
        IsItemOnHoldAndFound
141
    );
143
    );
142
    @EXPORT_OK = qw( MergeHolds );
144
    @EXPORT_OK = qw( MergeHolds );
143
}    
145
}
144
146
145
=head2 AddReserve
147
=head2 AddReserve
146
148
Lines 351-357 sub GetReservesFromBiblionumber { Link Here
351
                push( @bibitemno, $bibitemnos );    # FIXME: inefficient: use fetchall_arrayref
353
                push( @bibitemno, $bibitemnos );    # FIXME: inefficient: use fetchall_arrayref
352
            }
354
            }
353
            my $count = scalar @bibitemno;
355
            my $count = scalar @bibitemno;
354
    
356
355
            # if we have two or more different specific itemtypes
357
            # if we have two or more different specific itemtypes
356
            # reserved by same person on same day
358
            # reserved by same person on same day
357
            my $bdata;
359
            my $bdata;
Lines 491-497 sub CanBookBeReserved{ Link Here
491
493
492
sub CanItemBeReserved{
494
sub CanItemBeReserved{
493
    my ($borrowernumber, $itemnumber) = @_;
495
    my ($borrowernumber, $itemnumber) = @_;
494
    
496
495
    my $dbh             = C4::Context->dbh;
497
    my $dbh             = C4::Context->dbh;
496
    my $ruleitemtype; # itemtype of the matching issuing rule
498
    my $ruleitemtype; # itemtype of the matching issuing rule
497
    my $allowedreserves = 0;
499
    my $allowedreserves = 0;
Lines 513-530 sub CanItemBeReserved{ Link Here
513
    my $itemtypefield = C4::Context->preference('item-level_itypes') ? "itype" : "itemtype";
515
    my $itemtypefield = C4::Context->preference('item-level_itypes') ? "itype" : "itemtype";
514
516
515
    # we retrieve user rights on this itemtype and branchcode
517
    # we retrieve user rights on this itemtype and branchcode
516
    my $sth = $dbh->prepare("SELECT categorycode, itemtype, branchcode, reservesallowed 
518
    my $sth = $dbh->prepare("SELECT categorycode, itemtype, branchcode, reservesallowed
517
                             FROM issuingrules 
519
                             FROM issuingrules
518
                             WHERE (categorycode in (?,'*') ) 
520
                             WHERE (categorycode in (?,'*') )
519
                             AND (itemtype IN (?,'*')) 
521
                             AND (itemtype IN (?,'*'))
520
                             AND (branchcode IN (?,'*')) 
522
                             AND (branchcode IN (?,'*'))
521
                             ORDER BY 
523
                             ORDER BY
522
                               categorycode DESC, 
524
                               categorycode DESC,
523
                               itemtype     DESC, 
525
                               itemtype     DESC,
524
                               branchcode   DESC;"
526
                               branchcode   DESC;"
525
                           );
527
                           );
526
                           
528
527
    my $querycount ="SELECT 
529
    my $querycount ="SELECT
528
                            count(*) as count
530
                            count(*) as count
529
                            FROM reserves
531
                            FROM reserves
530
                                LEFT JOIN items USING (itemnumber)
532
                                LEFT JOIN items USING (itemnumber)
Lines 536-542 sub CanItemBeReserved{ Link Here
536
    
538
    
537
    my $branchcode   = "";
539
    my $branchcode   = "";
538
    my $branchfield  = "reserves.branchcode";
540
    my $branchfield  = "reserves.branchcode";
539
    
541
540
    if( $controlbranch eq "ItemHomeLibrary" ){
542
    if( $controlbranch eq "ItemHomeLibrary" ){
541
        $branchfield = "items.homebranch";
543
        $branchfield = "items.homebranch";
542
        $branchcode = $item->{homebranch};
544
        $branchcode = $item->{homebranch};
Lines 553-561 sub CanItemBeReserved{ Link Here
553
    }else{
555
    }else{
554
        $ruleitemtype = '*';
556
        $ruleitemtype = '*';
555
    }
557
    }
556
    
558
557
    # we retrieve count
559
    # we retrieve count
558
    
560
559
    $querycount .= "AND $branchfield = ?";
561
    $querycount .= "AND $branchfield = ?";
560
    
562
    
561
    $querycount .= " AND $itemtypefield = ?" if ($ruleitemtype ne "*");
563
    $querycount .= " AND $itemtypefield = ?" if ($ruleitemtype ne "*");
Lines 566-577 sub CanItemBeReserved{ Link Here
566
    }else{
568
    }else{
567
        $sthcount->execute($borrowernumber, $branchcode, $ruleitemtype);
569
        $sthcount->execute($borrowernumber, $branchcode, $ruleitemtype);
568
    }
570
    }
569
    
571
570
    my $reservecount = "0";
572
    my $reservecount = "0";
571
    if(my $rowcount = $sthcount->fetchrow_hashref()){
573
    if(my $rowcount = $sthcount->fetchrow_hashref()){
572
        $reservecount = $rowcount->{count};
574
        $reservecount = $rowcount->{count};
573
    }
575
    }
574
    
575
    # we check if it's ok or not
576
    # we check if it's ok or not
576
    if( $reservecount >= $allowedreserves ){
577
    if( $reservecount >= $allowedreserves ){
577
        return 'tooManyReserves';
578
        return 'tooManyReserves';
Lines 949-955 sub CheckReserves { Link Here
949
           LEFT JOIN itemtypes   ON biblioitems.itemtype   = itemtypes.itemtype
950
           LEFT JOIN itemtypes   ON biblioitems.itemtype   = itemtypes.itemtype
950
        ";
951
        ";
951
    }
952
    }
952
   
953
953
    if ($item) {
954
    if ($item) {
954
        $sth = $dbh->prepare("$select WHERE itemnumber = ?");
955
        $sth = $dbh->prepare("$select WHERE itemnumber = ?");
955
        $sth->execute($item);
956
        $sth->execute($item);
Lines 1047-1053 sub CancelExpiredReserves { Link Here
1047
    # Cancel reserves that have passed their expiration date.
1048
    # Cancel reserves that have passed their expiration date.
1048
    my $dbh = C4::Context->dbh;
1049
    my $dbh = C4::Context->dbh;
1049
    my $sth = $dbh->prepare( "
1050
    my $sth = $dbh->prepare( "
1050
        SELECT * FROM reserves WHERE DATE(expirationdate) < DATE( CURDATE() ) 
1051
        SELECT * FROM reserves WHERE DATE(expirationdate) < DATE( CURDATE() )
1051
        AND expirationdate IS NOT NULL
1052
        AND expirationdate IS NOT NULL
1052
        AND found IS NULL
1053
        AND found IS NULL
1053
    " );
1054
    " );
Lines 1056-1062 sub CancelExpiredReserves { Link Here
1056
    while ( my $res = $sth->fetchrow_hashref() ) {
1057
    while ( my $res = $sth->fetchrow_hashref() ) {
1057
        CancelReserve({ reserve_id => $res->{'reserve_id'} });
1058
        CancelReserve({ reserve_id => $res->{'reserve_id'} });
1058
    }
1059
    }
1059
  
1060
1060
    # Cancel reserves that have been waiting too long
1061
    # Cancel reserves that have been waiting too long
1061
    if ( C4::Context->preference("ExpireReservesMaxPickUpDelay") ) {
1062
    if ( C4::Context->preference("ExpireReservesMaxPickUpDelay") ) {
1062
        my $max_pickup_delay = C4::Context->preference("ReservesMaxPickUpDelay");
1063
        my $max_pickup_delay = C4::Context->preference("ReservesMaxPickUpDelay");
Lines 1182-1193 If C<$rank> is 'del', the hold request is cancelled. Link Here
1182
1183
1183
If C<$rank> is an integer greater than zero, the priority of
1184
If C<$rank> is an integer greater than zero, the priority of
1184
the request is set to that value.  Since priority != 0 means
1185
the request is set to that value.  Since priority != 0 means
1185
that the item is not waiting on the hold shelf, setting the 
1186
that the item is not waiting on the hold shelf, setting the
1186
priority to a non-zero value also sets the request's found
1187
priority to a non-zero value also sets the request's found
1187
status and waiting date to NULL. 
1188
status and waiting date to NULL.
1188
1189
1189
The optional C<$itemnumber> parameter is used only when
1190
The optional C<$itemnumber> parameter is used only when
1190
C<$rank> is a non-zero integer; if supplied, the itemnumber 
1191
C<$rank> is a non-zero integer; if supplied, the itemnumber
1191
of the hold request is set accordingly; if omitted, the itemnumber
1192
of the hold request is set accordingly; if omitted, the itemnumber
1192
is cleared.
1193
is cleared.
1193
1194
Lines 1299-1305 sub ModReserveFill { Link Here
1299
                ";
1300
                ";
1300
    $sth = $dbh->prepare($query);
1301
    $sth = $dbh->prepare($query);
1301
    $sth->execute( $biblionumber, $resdate, $borrowernumber );
1302
    $sth->execute( $biblionumber, $resdate, $borrowernumber );
1302
    
1303
1303
    # now fix the priority on the others (if the priority wasn't
1304
    # now fix the priority on the others (if the priority wasn't
1304
    # already sorted!)....
1305
    # already sorted!)....
1305
    unless ( $priority == 0 ) {
1306
    unless ( $priority == 0 ) {
Lines 1344-1350 with the biblionumber & the borrowernumber, we can affect the itemnumber Link Here
1344
to the correct reserve.
1345
to the correct reserve.
1345
1346
1346
if $transferToDo is not set, then the status is set to "Waiting" as well.
1347
if $transferToDo is not set, then the status is set to "Waiting" as well.
1347
otherwise, a transfer is on the way, and the end of the transfer will 
1348
otherwise, a transfer is on the way, and the end of the transfer will
1348
take care of the waiting status
1349
take care of the waiting status
1349
1350
1350
=cut
1351
=cut
Lines 1505-1526 sub GetReserveInfo { Link Here
1505
1506
1506
=head2 IsAvailableForItemLevelRequest
1507
=head2 IsAvailableForItemLevelRequest
1507
1508
1508
  my $is_available = IsAvailableForItemLevelRequest($itemnumber);
1509
  my $is_available = IsAvailableForItemLevelRequest($item_record,$borrower_record);
1509
1510
1510
Checks whether a given item record is available for an
1511
Checks whether a given item record is available for an
1511
item-level hold request.  An item is available if
1512
item-level hold request.  An item is available if
1512
1513
1513
* it is not lost AND 
1514
* it is not lost AND
1514
* it is not damaged AND 
1515
* it is not damaged AND
1515
* it is not withdrawn AND 
1516
* it is not withdrawn AND
1516
* does not have a not for loan value > 0
1517
* does not have a not for loan value > 0
1517
1518
1518
Whether or not the item is currently on loan is 
1519
Need to check the issuingrules onshelfholds column,
1519
also checked - if the AllowOnShelfHolds system preference
1520
if this is set items on the shelf can be placed on hold
1520
is ON, an item can be requested even if it is currently
1521
on loan to somebody else.  If the system preference
1522
is OFF, an item that is currently checked out cannot
1523
be the target of an item-level hold request.
1524
1521
1525
Note that IsAvailableForItemLevelRequest() does not
1522
Note that IsAvailableForItemLevelRequest() does not
1526
check if the staff operator is authorized to place
1523
check if the staff operator is authorized to place
Lines 1531-1578 and canreservefromotherbranches. Link Here
1531
=cut
1528
=cut
1532
1529
1533
sub IsAvailableForItemLevelRequest {
1530
sub IsAvailableForItemLevelRequest {
1534
    my $itemnumber = shift;
1531
    my $item = shift;
1535
   
1532
    my $borrower = shift;
1536
    my $item = GetItem($itemnumber);
1537
1533
1534
    my $dbh = C4::Context->dbh;
1538
    # must check the notforloan setting of the itemtype
1535
    # must check the notforloan setting of the itemtype
1539
    # FIXME - a lot of places in the code do this
1536
    # FIXME - a lot of places in the code do this
1540
    #         or something similar - need to be
1537
    #         or something similar - need to be
1541
    #         consolidated
1538
    #         consolidated
1542
    my $dbh = C4::Context->dbh;
1539
    my $itype = _get_itype($item);
1543
    my $notforloan_query;
1540
    my $notforloan_per_itemtype
1541
      = $dbh->selectrow_array("SELECT notforloan FROM itemtypes WHERE itemtype = ?",
1542
                              undef, $itype);
1543
1544
    return 0 if
1545
        $notforloan_per_itemtype ||
1546
        $item->{itemlost}        ||
1547
        $item->{notforloan} > 0  ||
1548
        $item->{wthdrawn}        ||
1549
        ($item->{damaged} && !C4::Context->preference('AllowHoldsOnDamagedItems'));
1550
1551
1552
    return 1 if _OnShelfHoldsAllowed($itype,$borrower->{categorycode},$item->{holdingbranch});
1553
1554
    return $item->{onloan} || GetReserveStatus($item->{itemnumber}) eq "Waiting";
1555
}
1556
1557
=head2 OnShelfHoldsAllowed
1558
1559
  OnShelfHoldsAllowed($itemtype,$borrowercategory,$branchcode);
1560
1561
Checks issuingrules, using the borrowers categorycode, the itemtype, and branchcode to see if onshelf
1562
holds are allowed, returns true if so.
1563
1564
=cut
1565
1566
sub OnShelfHoldsAllowed {
1567
    my ($item, $borrower) = @_;
1568
1569
    my $itype = _get_itype($item);
1570
    return _OnShelfHoldsAllowed($itype,$borrower->{categorycode},$item->{holdingbranch});
1571
}
1572
1573
sub _get_itype {
1574
    my $item = shift;
1575
1576
    my $itype;
1544
    if (C4::Context->preference('item-level_itypes')) {
1577
    if (C4::Context->preference('item-level_itypes')) {
1545
        $notforloan_query = "SELECT itemtypes.notforloan
1578
        # We cant trust GetItem to honour the syspref, so safest to do it ourselves
1546
                             FROM items
1579
        # When GetItem is fixed, we can remove this
1547
                             JOIN itemtypes ON (itemtypes.itemtype = items.itype)
1580
        $itype = $item->{itype};
1548
                             WHERE itemnumber = ?";
1549
    } else {
1550
        $notforloan_query = "SELECT itemtypes.notforloan
1551
                             FROM items
1552
                             JOIN biblioitems USING (biblioitemnumber)
1553
                             JOIN itemtypes USING (itemtype)
1554
                             WHERE itemnumber = ?";
1555
    }
1581
    }
1556
    my $sth = $dbh->prepare($notforloan_query);
1582
    else {
1557
    $sth->execute($itemnumber);
1583
        # XXX This is a bit dodgy. It relies on biblio itemtype column having different name.
1558
    my $notforloan_per_itemtype = 0;
1584
        # So if we already have a biblioitems join when calling this function,
1559
    if (my ($notforloan) = $sth->fetchrow_array) {
1585
        # we don't need to access the database again
1560
        $notforloan_per_itemtype = 1 if $notforloan;
1586
        $itype = $item->{itemtype};
1587
    }
1588
    unless ($itype) {
1589
        my $dbh = C4::Context->dbh;
1590
        my $query = "SELECT itemtype FROM biblioitems WHERE biblioitemnumber = ? ";
1591
        my $sth = $dbh->prepare($query);
1592
        $sth->execute($item->{biblioitemnumber});
1593
        if (my $data = $sth->fetchrow_hashref()){
1594
            $itype = $data->{itemtype};
1595
        }
1561
    }
1596
    }
1597
    return $itype;
1598
}
1562
1599
1563
    my $available_per_item = 1;
1600
sub _OnShelfHoldsAllowed {
1564
    $available_per_item = 0 if $item->{itemlost} or
1601
    my ($itype,$borrowercategory,$branchcode) = @_;
1565
                               ( $item->{notforloan} > 0 ) or
1566
                               ($item->{damaged} and not C4::Context->preference('AllowHoldsOnDamagedItems')) or
1567
                               $item->{withdrawn} or
1568
                               $notforloan_per_itemtype;
1569
1570
1602
1571
    if (C4::Context->preference('AllowOnShelfHolds')) {
1603
    my $rule = C4::Circulation::GetIssuingRule($borrowercategory, $itype, $branchcode);
1572
        return $available_per_item;
1604
    return $rule->{onshelfholds};
1573
    } else {
1574
        return ($available_per_item and ($item->{onloan} or GetReserveStatus($itemnumber) eq "Waiting"));
1575
    }
1576
}
1605
}
1577
1606
1578
=head2 AlterPriority
1607
=head2 AlterPriority
Lines 1840-1846 sub _FixPriority { Link Here
1840
    
1869
    
1841
    $sth = $dbh->prepare( "SELECT reserve_id FROM reserves WHERE lowestPriority = 1 ORDER BY priority" );
1870
    $sth = $dbh->prepare( "SELECT reserve_id FROM reserves WHERE lowestPriority = 1 ORDER BY priority" );
1842
    $sth->execute();
1871
    $sth->execute();
1843
    
1872
1844
    unless ( $ignoreSetLowestRank ) {
1873
    unless ( $ignoreSetLowestRank ) {
1845
      while ( my $res = $sth->fetchrow_hashref() ) {
1874
      while ( my $res = $sth->fetchrow_hashref() ) {
1846
        _FixPriority({
1875
        _FixPriority({
Lines 1908-1914 sub _Findgroupreserve { Link Here
1908
          unless any{ $data->{borrowernumber} eq $_ } @$ignore_borrowers ;
1937
          unless any{ $data->{borrowernumber} eq $_ } @$ignore_borrowers ;
1909
    }
1938
    }
1910
    return @results if @results;
1939
    return @results if @results;
1911
    
1940
1912
    # check for title-level targetted match
1941
    # check for title-level targetted match
1913
    my $title_level_target_query = qq{
1942
    my $title_level_target_query = qq{
1914
        SELECT reserves.biblionumber        AS biblionumber,
1943
        SELECT reserves.biblionumber        AS biblionumber,
Lines 1993-1999 sub _koha_notify_reserve { Link Here
1993
2022
1994
    my $dbh = C4::Context->dbh;
2023
    my $dbh = C4::Context->dbh;
1995
    my $borrower = C4::Members::GetMember(borrowernumber => $borrowernumber);
2024
    my $borrower = C4::Members::GetMember(borrowernumber => $borrowernumber);
1996
    
2025
1997
    # Try to get the borrower's email address
2026
    # Try to get the borrower's email address
1998
    my $to_address = C4::Members::GetNoticeEmailAddress($borrowernumber);
2027
    my $to_address = C4::Members::GetNoticeEmailAddress($borrowernumber);
1999
2028
Lines 2113-2118 sub _ShiftPriorityByDateAndPriority { Link Here
2113
    return $new_priority;  # so the caller knows what priority they wind up receiving
2142
    return $new_priority;  # so the caller knows what priority they wind up receiving
2114
}
2143
}
2115
2144
2145
=head2 OPACItemHoldsAllowed
2146
2147
  OPACItemHoldsAllowed($item_record,$borrower_record);
2148
2149
Checks issuingrules, using the borrowers categorycode, the itemtype, and branchcode to see
2150
if specific item holds are allowed, returns true if so.
2151
2152
=cut
2153
2154
sub OPACItemHoldsAllowed {
2155
    my ($item,$borrower) = @_;
2156
2157
    my $branchcode = $item->{homebranch} or die "No homebranch";
2158
    my $itype;
2159
    my $dbh = C4::Context->dbh;
2160
    if (C4::Context->preference('item-level_itypes')) {
2161
       # We cant trust GetItem to honour the syspref, so safest to do it ourselves
2162
       # When GetItem is fixed, we can remove this
2163
       $itype = $item->{itype};
2164
    }
2165
    else {
2166
       my $query = "SELECT itemtype FROM biblioitems WHERE biblioitemnumber = ? ";
2167
       my $sth = $dbh->prepare($query);
2168
       $sth->execute($item->{biblioitemnumber});
2169
       if (my $data = $sth->fetchrow_hashref()){
2170
           $itype = $data->{itemtype};
2171
       }
2172
    }
2173
2174
    my $query = "SELECT opacitemholds,categorycode,itemtype,branchcode FROM issuingrules WHERE
2175
          (issuingrules.categorycode = ? OR issuingrules.categorycode = '*')
2176
        AND
2177
          (issuingrules.itemtype = ? OR issuingrules.itemtype = '*')
2178
        AND
2179
          (issuingrules.branchcode = ? OR issuingrules.branchcode = '*')
2180
        ORDER BY
2181
          issuingrules.categorycode desc,
2182
          issuingrules.itemtype desc,
2183
          issuingrules.branchcode desc
2184
       LIMIT 1";
2185
    my $sth = $dbh->prepare($query);
2186
    $sth->execute($borrower->{categorycode},$itype,$branchcode);
2187
    my $data = $sth->fetchrow_hashref;
2188
    my $opacitemholds = uc substr ($data->{opacitemholds}, 0, 1);
2189
    return '' if $opacitemholds eq 'N';
2190
    return $opacitemholds;
2191
}
2192
2116
=head2 MoveReserve
2193
=head2 MoveReserve
2117
2194
2118
  MoveReserve( $itemnumber, $borrowernumber, $cancelreserve )
2195
  MoveReserve( $itemnumber, $borrowernumber, $cancelreserve )
(-)a/C4/UsageStats.pm (-2 lines)
Lines 155-161 sub BuildReport { Link Here
155
        AllowHoldPolicyOverride
155
        AllowHoldPolicyOverride
156
        AllowHoldsOnDamagedItems
156
        AllowHoldsOnDamagedItems
157
        AllowHoldsOnPatronsPossessions
157
        AllowHoldsOnPatronsPossessions
158
        AllowOnShelfHolds
159
        AutoResumeSuspendedHolds
158
        AutoResumeSuspendedHolds
160
        canreservefromotherbranches
159
        canreservefromotherbranches
161
        decreaseLoanHighHolds
160
        decreaseLoanHighHolds
Lines 265-271 sub BuildReport { Link Here
265
        AllowPurchaseSuggestionBranchChoice
264
        AllowPurchaseSuggestionBranchChoice
266
        OpacAllowPublicListCreation
265
        OpacAllowPublicListCreation
267
        OpacAllowSharingPrivateLists
266
        OpacAllowSharingPrivateLists
268
        OPACItemHolds
269
        OpacRenewalAllowed
267
        OpacRenewalAllowed
270
        OpacRenewalBranch
268
        OpacRenewalBranch
271
        OPACViewOthersSuggestions
269
        OPACViewOthersSuggestions
(-)a/C4/VirtualShelves/Page.pm (-1 / +4 lines)
Lines 30-35 use Data::Dumper; Link Here
30
use C4::VirtualShelves qw/:DEFAULT ShelvesMax/;
30
use C4::VirtualShelves qw/:DEFAULT ShelvesMax/;
31
use C4::Biblio;
31
use C4::Biblio;
32
use C4::Items;
32
use C4::Items;
33
use C4::Reserves;
33
use C4::Koha;
34
use C4::Koha;
34
use C4::Auth qw/get_session/;
35
use C4::Auth qw/get_session/;
35
use C4::Members;
36
use C4::Members;
Lines 226-232 sub shelfpage { Link Here
226
            my ($shelfnumber2,$shelfname,$owner,$category,$sorton) = GetShelf($shelfnumber);
227
            my ($shelfnumber2,$shelfname,$owner,$category,$sorton) = GetShelf($shelfnumber);
227
228
228
            $template->param(
229
            $template->param(
229
                'AllowOnShelfHolds'     => C4::Context->preference('AllowOnShelfHolds'),
230
                'DisplayMultiPlaceHold' => C4::Context->preference('DisplayMultiPlaceHold'),
230
                'DisplayMultiPlaceHold' => C4::Context->preference('DisplayMultiPlaceHold'),
231
            );
231
            );
232
            if (C4::Context->preference('TagsEnabled')) {
232
            if (C4::Context->preference('TagsEnabled')) {
Lines 257-262 sub shelfpage { Link Here
257
                    @cart_list = split(/\//, $cart_list);
257
                    @cart_list = split(/\//, $cart_list);
258
                }
258
                }
259
259
260
                my $borrower = GetMember( 'borrowernumber' => $loggedinuser );
261
260
                for my $this_item (@$items) {
262
                for my $this_item (@$items) {
261
                    my $biblionumber = $this_item->{'biblionumber'};
263
                    my $biblionumber = $this_item->{'biblionumber'};
262
                    my $record = GetMarcBiblio($biblionumber);
264
                    my $record = GetMarcBiblio($biblionumber);
Lines 294-299 sub shelfpage { Link Here
294
                            });
296
                            });
295
                    }
297
                    }
296
298
299
                    $this_item->{'allow_onshelf_holds'} = C4::Reserves::OnShelfHoldsAllowed($this_item, $borrower);
297
                }
300
                }
298
                if($type eq 'intranet'){
301
                if($type eq 'intranet'){
299
                    # Build drop-down list for 'Add To:' menu...
302
                    # Build drop-down list for 'Add To:' menu...
(-)a/admin/smart-rules.pl (-8 / +10 lines)
Lines 101-112 elsif ($op eq 'delete-branch-item') { Link Here
101
# save the values entered
101
# save the values entered
102
elsif ($op eq 'add') {
102
elsif ($op eq 'add') {
103
    my $sth_search = $dbh->prepare('SELECT COUNT(*) AS total FROM issuingrules WHERE branchcode=? AND categorycode=? AND itemtype=?');
103
    my $sth_search = $dbh->prepare('SELECT COUNT(*) AS total FROM issuingrules WHERE branchcode=? AND categorycode=? AND itemtype=?');
104
    my $sth_insert = $dbh->prepare('INSERT INTO issuingrules (branchcode, categorycode, itemtype, maxissueqty, renewalsallowed, renewalperiod, norenewalbefore, auto_renew, reservesallowed, issuelength, lengthunit, hardduedate, hardduedatecompare, fine, finedays, maxsuspensiondays, firstremind, chargeperiod,rentaldiscount, overduefinescap) VALUES(?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?)');
104
    my $sth_insert = $dbh->prepare('INSERT INTO issuingrules (branchcode, categorycode, itemtype, maxissueqty, renewalsallowed, reservesallowed, norenewalbefore, auto_renew, reservesallowed, issuelength, lengthunit, hardduedate, hardduedatecompare, fine, finedays, maxsuspensiondays, firstremind, chargeperiod,rentaldiscount, onshelfholds, opacitemholds, overduefinescap) VALUES(?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?)');
105
    my $sth_update=$dbh->prepare("UPDATE issuingrules SET fine=?, finedays=?, maxsuspensiondays=?, firstremind=?, chargeperiod=?, maxissueqty=?, renewalsallowed=?, renewalperiod=?, norenewalbefore=?, auto_renew=?, reservesallowed=?, issuelength=?, lengthunit = ?, hardduedate=?, hardduedatecompare=?, rentaldiscount=?, overduefinescap=?  WHERE branchcode=? AND categorycode=? AND itemtype=?");
105
    my $sth_update=$dbh->prepare("UPDATE issuingrules SET fine=?, finedays=?, maxsuspensiondays=?, firstremind=?, chargeperiod=?, maxissueqty=?, renewalsallowed=?, renewalperiod=?, norenewalbefore=?, auto_renew=?, reservesallowed=?, issuelength=?, lengthunit=?, hardduedate=?, hardduedatecompare=?, rentaldiscount=?, onshelfholds=?, opacitemholds=?, overduefinescap=? WHERE branchcode=? AND categorycode=? AND itemtype=?");
106
    
106
    
107
    my $br = $branch; # branch
107
    my $br = $branch; # branch
108
    my $bor  = $input->param('categorycode'); # borrower category
108
    my $bor  = $input->param('categorycode'); # borrower category
109
    my $cat  = $input->param('itemtype');     # item type
109
    my $itemtype  = $input->param('itemtype');     # item type
110
    my $fine = $input->param('fine');
110
    my $fine = $input->param('fine');
111
    my $finedays     = $input->param('finedays');
111
    my $finedays     = $input->param('finedays');
112
    my $maxsuspensiondays = $input->param('maxsuspensiondays');
112
    my $maxsuspensiondays = $input->param('maxsuspensiondays');
Lines 120-125 elsif ($op eq 'add') { Link Here
120
    $norenewalbefore = undef if $norenewalbefore eq '0' or $norenewalbefore =~ /^\s*$/;
120
    $norenewalbefore = undef if $norenewalbefore eq '0' or $norenewalbefore =~ /^\s*$/;
121
    my $auto_renew = $input->param('auto_renew') eq 'yes' ? 1 : 0;
121
    my $auto_renew = $input->param('auto_renew') eq 'yes' ? 1 : 0;
122
    my $reservesallowed  = $input->param('reservesallowed');
122
    my $reservesallowed  = $input->param('reservesallowed');
123
    my $onshelfholds     = $input->param('onshelfholds') || 0;
123
    $maxissueqty =~ s/\s//g;
124
    $maxissueqty =~ s/\s//g;
124
    $maxissueqty = undef if $maxissueqty !~ /^\d+/;
125
    $maxissueqty = undef if $maxissueqty !~ /^\d+/;
125
    my $issuelength  = $input->param('issuelength');
126
    my $issuelength  = $input->param('issuelength');
Lines 128-142 elsif ($op eq 'add') { Link Here
128
    $hardduedate = format_date_in_iso($hardduedate);
129
    $hardduedate = format_date_in_iso($hardduedate);
129
    my $hardduedatecompare = $input->param('hardduedatecompare');
130
    my $hardduedatecompare = $input->param('hardduedatecompare');
130
    my $rentaldiscount = $input->param('rentaldiscount');
131
    my $rentaldiscount = $input->param('rentaldiscount');
132
    my $opacitemholds = $input->param('opacitemholds') || 0;
131
    my $overduefinescap = $input->param('overduefinescap') || undef;
133
    my $overduefinescap = $input->param('overduefinescap') || undef;
132
    $debug and warn "Adding $br, $bor, $cat, $fine, $maxissueqty";
134
    $debug and warn "Adding $br, $bor, $itemtype, $fine, $maxissueqty";
133
135
134
    $sth_search->execute($br,$bor,$cat);
136
    $sth_search->execute($br,$bor,$itemtype);
135
    my $res = $sth_search->fetchrow_hashref();
137
    my $res = $sth_search->fetchrow_hashref();
136
    if ($res->{total}) {
138
    if ($res->{total}) {
137
        $sth_update->execute($fine, $finedays, $maxsuspensiondays, $firstremind, $chargeperiod, $maxissueqty, $renewalsallowed, $renewalperiod, $norenewalbefore, $auto_renew, $reservesallowed, $issuelength,$lengthunit, $hardduedate,$hardduedatecompare,$rentaldiscount,$overduefinescap, $br,$bor,$cat);
139
        $sth_update->execute($fine, $finedays, $maxsuspensiondays, $firstremind, $chargeperiod, $maxissueqty, $renewalsallowed, $renewalperiod, $norenewalbefore, $auto_renew, $reservesallowed, $issuelength,$lengthunit, $hardduedate,$hardduedatecompare,$rentaldiscount, $onshelfholds, $opacitemholds, $overduefinescap, $br,$bor,$itemtype);
138
    } else {
140
    } else {
139
        $sth_insert->execute($br,$bor,$cat,$maxissueqty,$renewalsallowed, $renewalperiod, $norenewalbefore, $auto_renew, $reservesallowed,$issuelength,$lengthunit,$hardduedate,$hardduedatecompare,$fine,$finedays, $maxsuspensiondays, $firstremind,$chargeperiod,$rentaldiscount,$overduefinescap);
141
        $sth_insert->execute($br,$bor,$itemtype,$maxissueqty,$renewalsallowed, $renewalperiod, $norenewalbefore, $auto_renew, $reservesallowed,$issuelength,$lengthunit,$hardduedate,$hardduedatecompare,$fine,$finedays, $maxsuspensiondays, $firstremind,$chargeperiod,$rentaldiscount,$onshelfholds,$opacitemholds,$overduefinescap);
140
    }
142
    }
141
} 
143
} 
142
elsif ($op eq "set-branch-defaults") {
144
elsif ($op eq "set-branch-defaults") {
Lines 391-397 while (my $row = $sth2->fetchrow_hashref) { Link Here
391
    $row->{'humancategorycode'} ||= $row->{'categorycode'};
393
    $row->{'humancategorycode'} ||= $row->{'categorycode'};
392
    $row->{'default_humancategorycode'} = 1 if $row->{'humancategorycode'} eq '*';
394
    $row->{'default_humancategorycode'} = 1 if $row->{'humancategorycode'} eq '*';
393
    $row->{'fine'} = sprintf('%.2f', $row->{'fine'});
395
    $row->{'fine'} = sprintf('%.2f', $row->{'fine'});
394
    if ($row->{'hardduedate'} ne '0000-00-00') {
396
    if ($row->{'hardduedate'} && $row->{'hardduedate'} ne '0000-00-00') {
395
       $row->{'hardduedate'} = format_date( $row->{'hardduedate'});
397
       $row->{'hardduedate'} = format_date( $row->{'hardduedate'});
396
       $row->{'hardduedatebefore'} = 1 if ($row->{'hardduedatecompare'} == -1);
398
       $row->{'hardduedatebefore'} = 1 if ($row->{'hardduedatecompare'} == -1);
397
       $row->{'hardduedateexact'} = 1 if ($row->{'hardduedatecompare'} ==  0);
399
       $row->{'hardduedateexact'} = 1 if ($row->{'hardduedatecompare'} ==  0);
(-)a/installer/data/mysql/it-IT/necessari/system_preferences.sql (-1 lines)
Lines 17-23 Link Here
17
-- 51 Franklin Street' WHERE variable = ' Fifth Floor' WHERE variable = ' Boston' WHERE variable = ' MA 02110-1301 USA.
17
-- 51 Franklin Street' WHERE variable = ' Fifth Floor' WHERE variable = ' Boston' WHERE variable = ' MA 02110-1301 USA.
18
18
19
UPDATE systempreferences SET value = 'cataloguing' WHERE variable = 'AcqCreateItem';
19
UPDATE systempreferences SET value = 'cataloguing' WHERE variable = 'AcqCreateItem';
20
UPDATE systempreferences SET value = '1' WHERE variable = 'AllowOnShelfHolds';
21
UPDATE systempreferences SET value = '1' WHERE variable = 'AllowRenewalLimitOverride';
20
UPDATE systempreferences SET value = '1' WHERE variable = 'AllowRenewalLimitOverride';
22
UPDATE systempreferences SET value = 'annual' WHERE variable = 'autoBarcode';
21
UPDATE systempreferences SET value = 'annual' WHERE variable = 'autoBarcode';
23
UPDATE systempreferences SET value = 'email' WHERE variable = 'AutoEmailPrimaryAddress';
22
UPDATE systempreferences SET value = 'email' WHERE variable = 'AutoEmailPrimaryAddress';
(-)a/installer/data/mysql/kohastructure.sql (+2 lines)
Lines 1192-1197 CREATE TABLE `issuingrules` ( -- circulation and fine rules Link Here
1192
  `reservesallowed` smallint(6) NOT NULL default "0", -- how many holds are allowed
1192
  `reservesallowed` smallint(6) NOT NULL default "0", -- how many holds are allowed
1193
  `branchcode` varchar(10) NOT NULL default '', -- the branch this rule is for (branches.branchcode)
1193
  `branchcode` varchar(10) NOT NULL default '', -- the branch this rule is for (branches.branchcode)
1194
  overduefinescap decimal(28,6) default NULL, -- the maximum amount of an overdue fine
1194
  overduefinescap decimal(28,6) default NULL, -- the maximum amount of an overdue fine
1195
  onshelfholds tinyint(1) NOT NULL default 0, -- allow holds for items that are on shelf
1196
  opacitemholds char(1) NOT NULL default 'N', -- allow opac users to place specific items on hold
1195
  PRIMARY KEY  (`branchcode`,`categorycode`,`itemtype`),
1197
  PRIMARY KEY  (`branchcode`,`categorycode`,`itemtype`),
1196
  KEY `categorycode` (`categorycode`),
1198
  KEY `categorycode` (`categorycode`),
1197
  KEY `itemtype` (`itemtype`)
1199
  KEY `itemtype` (`itemtype`)
(-)a/installer/data/mysql/sysprefs.sql (-2 lines)
Lines 25-31 INSERT INTO systempreferences ( `variable`, `value`, `options`, `explanation`, ` Link Here
25
('AllowMultipleIssuesOnABiblio',1,'Allow/Don\'t allow patrons to check out multiple items from one biblio','','YesNo'),
25
('AllowMultipleIssuesOnABiblio',1,'Allow/Don\'t allow patrons to check out multiple items from one biblio','','YesNo'),
26
('AllowNotForLoanOverride','0','','If ON, Koha will allow the librarian to loan a not for loan item.','YesNo'),
26
('AllowNotForLoanOverride','0','','If ON, Koha will allow the librarian to loan a not for loan item.','YesNo'),
27
('AllowOfflineCirculation','0','','If on, enables HTML5 offline circulation functionality.','YesNo'),
27
('AllowOfflineCirculation','0','','If on, enables HTML5 offline circulation functionality.','YesNo'),
28
('AllowOnShelfHolds','0','','Allow hold requests to be placed on items that are not on loan','YesNo'),
29
('AllowPKIAuth','None','None|Common Name|emailAddress','Use the field from a client-side SSL certificate to look a user in the Koha database','Choice'),
28
('AllowPKIAuth','None','None|Common Name|emailAddress','Use the field from a client-side SSL certificate to look a user in the Koha database','Choice'),
30
('AllowPurchaseSuggestionBranchChoice','0','1','Allow user to choose branch when making a purchase suggestion','YesNo'),
29
('AllowPurchaseSuggestionBranchChoice','0','1','Allow user to choose branch when making a purchase suggestion','YesNo'),
31
('AllowRenewalIfOtherItemsAvailable','0',NULL,'If enabled, allow a patron to renew an item with unfilled holds if other available items can fill that hold.','YesNo'),
30
('AllowRenewalIfOtherItemsAvailable','0',NULL,'If enabled, allow a patron to renew an item with unfilled holds if other available items can fill that hold.','YesNo'),
Lines 262-268 INSERT INTO systempreferences ( `variable`, `value`, `options`, `explanation`, ` Link Here
262
('OpacHiddenItems','','','This syspref allows to define custom rules for hiding specific items at opac. See docs/opac/OpacHiddenItems.txt for more informations.','Textarea'),
261
('OpacHiddenItems','','','This syspref allows to define custom rules for hiding specific items at opac. See docs/opac/OpacHiddenItems.txt for more informations.','Textarea'),
263
('OpacHighlightedWords','1','','If Set, then queried words are higlighted in OPAC','YesNo'),
262
('OpacHighlightedWords','1','','If Set, then queried words are higlighted in OPAC','YesNo'),
264
('OpacHoldNotes','0','','Show hold notes on OPAC','YesNo'),
263
('OpacHoldNotes','0','','Show hold notes on OPAC','YesNo'),
265
('OPACItemHolds','1','0|1|force','Allow OPAC users to place hold on specific items. If No, users can only request next available copy. If Yes, users can choose between next available and specific copy. If Force, users *must* choose a specific copy.','Choice'),
266
('OpacItemLocation','callnum','callnum|ccode|location','Show the shelving location of items in the opac','Choice'),
264
('OpacItemLocation','callnum','callnum|ccode|location','Show the shelving location of items in the opac','Choice'),
267
('OPACItemsResultsDisplay','0','','If OFF : show only the status of items in result list.If ON : show full location of items (branch+location+callnumber) as in staff interface','YesNo'),
265
('OPACItemsResultsDisplay','0','','If OFF : show only the status of items in result list.If ON : show full location of items (branch+location+callnumber) as in staff interface','YesNo'),
268
('OpacKohaUrl','1',NULL,'Show \'Powered by Koha\' text on OPAC footer.',NULL),
266
('OpacKohaUrl','1',NULL,'Show \'Powered by Koha\' text on OPAC footer.',NULL),
(-)a/installer/data/mysql/updatedatabase.pl (+35 lines)
Lines 9672-9677 if ( CheckVersion($DBversion) ) { Link Here
9672
    $dbh->do(q|SET foreign_key_checks = 1|);;
9672
    $dbh->do(q|SET foreign_key_checks = 1|);;
9673
9673
9674
    print "Upgrade to $DBversion done (Bug 11944 - Convert DB tables to utf8_unicode_ci)\n";
9674
    print "Upgrade to $DBversion done (Bug 11944 - Convert DB tables to utf8_unicode_ci)\n";
9675
    SetVersion($DBversion);
9676
}
9677
9678
$DBversion = '3.19.00.XXX';
9679
if (C4::Context->preference("Version") < TransformToNum($DBversion)) {
9680
    # First create the column
9681
    $dbh->do("ALTER TABLE issuingrules ADD onshelfholds tinyint(1) default 0 NOT NULL");
9682
    # Now update the column
9683
    if (C4::Context->preference("AllowOnShelfHolds")){
9684
        # Pref is on, set allow for all rules
9685
        $dbh->do("UPDATE issuingrules SET onshelfholds=1");
9686
    } else {
9687
        # If the preference is not set, leave off
9688
        $dbh->do("UPDATE issuingrules SET onshelfholds=0");
9689
    }
9690
    # Remove from the systempreferences table
9691
    $dbh->do("DELETE FROM systempreferences WHERE variable = 'AllowOnShelfHolds'");
9692
9693
    # First create the column
9694
    $dbh->do("ALTER TABLE issuingrules ADD opacitemholds char(1) DEFAULT 'N' NOT NULL");
9695
    # Now update the column
9696
    my $opacitemholds = C4::Context->preference("OPACItemHolds") || '';
9697
    if (lc ($opacitemholds) eq 'force') {
9698
        $opacitemholds = 'F';
9699
    }
9700
    else {
9701
        $opacitemholds = $opacitemholds ? 'Y' : 'N';
9702
    }
9703
    # Set allow for all rules
9704
    $dbh->do("UPDATE issuingrules SET opacitemholds='$opacitemholds'");
9705
9706
    # Remove from the systempreferences table
9707
    $dbh->do("DELETE FROM systempreferences WHERE variable = 'OPACItemHolds'");
9708
9709
    print "Upgrade to $DBversion done (Bug 5786 - Move AllowOnShelfHolds to circulation matrix; Move OPACItemHolds system preference to circulation matrix)\n";
9675
    SetVersion ($DBversion);
9710
    SetVersion ($DBversion);
9676
}
9711
}
9677
9712
(-)a/installer/html-template-to-template-toolkit.pl (-1 / +1 lines)
Lines 32-38 my @globals = ("themelang","JacketImages","OPACAmazonCoverImages","GoogleJackets Link Here
32
"SyndeticsEnabled", "OpacRenewalAllowed", "item_level_itypes","noItemTypeImages",
32
"SyndeticsEnabled", "OpacRenewalAllowed", "item_level_itypes","noItemTypeImages",
33
"virtualshelves", "RequestOnOpac", "COinSinOPACResults", "OPACXSLTResultsDisplay",
33
"virtualshelves", "RequestOnOpac", "COinSinOPACResults", "OPACXSLTResultsDisplay",
34
"OPACItemsResultsDisplay", "LibraryThingForLibrariesID", "opacuserlogin", "TagsEnabled",
34
"OPACItemsResultsDisplay", "LibraryThingForLibrariesID", "opacuserlogin", "TagsEnabled",
35
"TagsShowOnList", "TagsInputOnList","loggedinusername","AllowOnShelfHolds","opacbookbag",
35
"TagsShowOnList", "TagsInputOnList","loggedinusername","opacbookbag",
36
"OPACAmazonEnabled", "SyndeticsCoverImages","using_https");
36
"OPACAmazonEnabled", "SyndeticsCoverImages","using_https");
37
37
38
# Arguments:
38
# Arguments:
(-)a/koha-tmpl/intranet-tmpl/prog/en/modules/admin/preferences/circulation.pref (-6 lines)
Lines 439-450 Circulation: Link Here
439
                  no: "Don't allow"
439
                  no: "Don't allow"
440
            - hold requests to be placed on and filled by damaged items.
440
            - hold requests to be placed on and filled by damaged items.
441
        -
441
        -
442
            - pref: AllowOnShelfHolds
443
              choices:
444
                  yes: Allow
445
                  no: "Don't allow"
446
            - hold requests to be placed on items that are not checked out.
447
        -
448
            - pref: AllowHoldDateInFuture
442
            - pref: AllowHoldDateInFuture
449
              choices:
443
              choices:
450
                  yes: Allow
444
                  yes: Allow
(-)a/koha-tmpl/intranet-tmpl/prog/en/modules/admin/preferences/opac.pref (-7 lines)
Lines 494-506 OPAC: Link Here
494
#              choices:
494
#              choices:
495
#            - If ON, enables subject cloud on OPAC
495
#            - If ON, enables subject cloud on OPAC
496
        -
496
        -
497
            - pref: OPACItemHolds
498
              choices:
499
                  no: "Don't allow"
500
                  yes: Allow
501
                  force: Force
502
            - patrons to place holds on specific items in the OPAC. If this is disabled, users can only put a hold on the next available item. If this is forced, users must put a hold on a specific item.
503
        -
504
            - pref: OpacRenewalAllowed
497
            - pref: OpacRenewalAllowed
505
              choices:
498
              choices:
506
                  yes: Allow
499
                  yes: Allow
(-)a/koha-tmpl/intranet-tmpl/prog/en/modules/admin/smart-rules.tt (+17 lines)
Lines 156-161 for="tobranch"><strong>Clone these rules to:</strong></label> <input type="hidde Link Here
156
                <th>No renewal before</th>
156
                <th>No renewal before</th>
157
                <th>Automatic renewal</th>
157
                <th>Automatic renewal</th>
158
                <th>Holds allowed (count)</th>
158
                <th>Holds allowed (count)</th>
159
                <th>On shelf holds allowed</th>
160
                <th>Item level holds</th>
159
                <th>Rental discount (%)</th>
161
                <th>Rental discount (%)</th>
160
                <th colspan="2">&nbsp;</th>
162
                <th colspan="2">&nbsp;</th>
161
            </tr>
163
            </tr>
Lines 218-223 for="tobranch"><strong>Clone these rules to:</strong></label> <input type="hidde Link Here
218
                                [% END %]
220
                                [% END %]
219
                            </td>
221
                            </td>
220
							<td>[% rule.reservesallowed %]</td>
222
							<td>[% rule.reservesallowed %]</td>
223
                                                        <td>[% IF rule.onshelfholds %]Yes[% ELSE %]No[% END %]</td>
224
                                                        <td>[% IF rule.opacitemholds == 'F'%]Enforce[% ELSIF rule.opacitemholds == 'Y'%]Allow[% ELSE %]Don't allow[% END %]</td>
221
							<td>[% rule.rentaldiscount %]</td>
225
							<td>[% rule.rentaldiscount %]</td>
222
                            <td><a href="#" class="editrule">Edit</a></td>
226
                            <td><a href="#" class="editrule">Edit</a></td>
223
							<td>
227
							<td>
Lines 275-280 for="tobranch"><strong>Clone these rules to:</strong></label> <input type="hidde Link Here
275
                        </select>
279
                        </select>
276
                    </td>
280
                    </td>
277
                    <td><input type="text" name="reservesallowed" id="reservesallowed" size="2" /></td>
281
                    <td><input type="text" name="reservesallowed" id="reservesallowed" size="2" /></td>
282
                    <td>
283
                        <select name="onshelfholds" id="onshelfholds">
284
                            <option value="0" selected>No</option>
285
                            <option value="1">Yes</option>
286
                        </select>
287
                    </td>
288
                    <td>
289
                        <select id="opacitemholds" name="opacitemholds">
290
                            <option value="N">Don't allow</option>
291
                            <option value="Y">Allow</option>
292
                            <option value="F">Enforce</option>
293
                        </select>
294
                    </td>
278
                    <td><input type="text" name="rentaldiscount" id="rentaldiscount" size="2" /></td>
295
                    <td><input type="text" name="rentaldiscount" id="rentaldiscount" size="2" /></td>
279
                    <td colspan="2">
296
                    <td colspan="2">
280
                        <input type="hidden" name="branch" value="[% current_branch %]"/>
297
                        <input type="hidden" name="branch" value="[% current_branch %]"/>
(-)a/koha-tmpl/opac-tmpl/bootstrap/en/includes/opac-detail-sidebar.inc (-5 / +1 lines)
Lines 2-13 Link Here
2
    [% UNLESS ( norequests ) %]
2
    [% UNLESS ( norequests ) %]
3
        [% IF Koha.Preference( 'opacuserlogin' ) == 1 %]
3
        [% IF Koha.Preference( 'opacuserlogin' ) == 1 %]
4
            [% IF Koha.Preference( 'RequestOnOpac' ) == 1 %]
4
            [% IF Koha.Preference( 'RequestOnOpac' ) == 1 %]
5
                [% IF ( AllowOnShelfHolds ) %]
5
                [% IF ( AllowOnShelfHolds OR ItemsIssued ) %]
6
                    <li><a class="reserve" href="/cgi-bin/koha/opac-reserve.pl?biblionumber=[% biblionumber %]">Place hold</a></li>
6
                    <li><a class="reserve" href="/cgi-bin/koha/opac-reserve.pl?biblionumber=[% biblionumber %]">Place hold</a></li>
7
                [% ELSE %]
8
                    [% IF ( ItemsIssued ) %]
9
                        <li><a class="reserve" href="/cgi-bin/koha/opac-reserve.pl?biblionumber=[% biblionumber %]">Place hold</a></li>
10
                    [% END %]
11
                [% END %]
7
                [% END %]
12
            [% END %]
8
            [% END %]
13
        [% END %]
9
        [% END %]
(-)a/koha-tmpl/opac-tmpl/bootstrap/en/modules/opac-reserve.tt (-43 / +25 lines)
Lines 249-292 Link Here
249
                                                            </li>
249
                                                            </li>
250
                                                        [% END # / IF OpacHoldNotes %]
250
                                                        [% END # / IF OpacHoldNotes %]
251
251
252
                                                        [% IF OPACItemHolds == '1' or OPACItemHolds == 'force' %]
252
                                                        <li>
253
                                                        </li>
254
                                                        [% IF bibitemloo.itemholdable %]
253
                                                            <!-- ITEM HOLDS -->
255
                                                            <!-- ITEM HOLDS -->
254
                                                            <li class="lradio place_on_type" style="display:none;">
256
                                                            <li class="lradio place_on_type" style="display:none;">
255
                                                                <label class="radio inline" for="reqany_[% bibitemloo.biblionumber %]">Next available item</label>
257
                                                                [% IF NOT bibitemloo.force_hold %]
256
                                                                [% IF OPACItemHolds == "1" %]
258
                                                                    <label class="radio inline" for="reqany_[% bibitemloo.biblionumber %]">Next available item</label>
257
                                                                    <input type="radio" name="reqtype_[% bibitemloo.biblionumber %]"
259
                                                                    <input type="radio" name="reqtype_[% bibitemloo.biblionumber %]"
258
                                                                            id="reqany_[% bibitemloo.biblionumber %]"
260
                                                                            id="reqany_[% bibitemloo.biblionumber %]"
259
                                                                            class="selectany"
261
                                                                            class="selectany"
260
                                                                            value="Any"
262
                                                                            value="Any"
261
                                                                            checked="checked"
263
                                                                            checked="checked"
262
                                                                    />
264
                                                                    />
263
                                                                [% ELSE %]
264
                                                                    <input type="radio" name="reqtype_[% bibitemloo.biblionumber %]"
265
                                                                            id="reqany_[% bibitemloo.biblionumber %]"
266
                                                                            class="selectany"
267
                                                                            value="Any"
268
                                                                     />
269
                                                                [% END %]
265
                                                                [% END %]
270
                                                                <label class="radio inline" for="reqspecific_[% bibitemloo.biblionumber %]">A specific item</label>
266
                                                                <label class="radio inline" for="reqspecific_[% bibitemloo.biblionumber %]">A specific item</label>
271
                                                                [% IF OPACItemHolds == "force" %]
267
                                                                <input type="radio" name="reqtype_[% bibitemloo.biblionumber %]"
272
                                                                    <input type="radio" name="reqtype_[% bibitemloo.biblionumber %]"
268
                                                                       id="reqspecific_[% bibitemloo.biblionumber %]"
273
                                                                           id="reqspecific_[% bibitemloo.biblionumber %]"
269
                                                                       class="selectspecific"
274
                                                                           class="selectspecific"
270
                                                                       value="Specific"
275
                                                                           value="Specific"
271
                                                                />
276
                                                                           checked="checked"
277
                                                                    />
278
                                                                [% ELSE %]
279
                                                                    <input type="radio" name="reqtype_[% bibitemloo.biblionumber %]"
280
                                                                           id="reqspecific_[% bibitemloo.biblionumber %]"
281
                                                                           class="selectspecific"
282
                                                                           value="Specific"
283
                                                                    />
284
                                                                [% END %]
285
                                                            </li>
272
                                                            </li>
286
                                                        [% END # / IF OPACItemHolds %]
273
                                                        [% END # / IF bibitemloo.itemholdable %]
287
                                                    </ul>
274
                                                    </ul>
288
275
289
                                                    [% IF OPACItemHolds == '1' || OPACItemHolds == 'force' %]
276
                                                    [% IF bibitemloo.itemholdable %]
290
                                                        <table class="copiesrow table table-bordered table-striped" id="copiesrow_[% bibitemloo.biblionumber %]">
277
                                                        <table class="copiesrow table table-bordered table-striped" id="copiesrow_[% bibitemloo.biblionumber %]">
291
                                                            <caption>Select a specific item:</caption>
278
                                                            <caption>Select a specific item:</caption>
292
                                                            <tr>
279
                                                            <tr>
Lines 386-392 Link Here
386
                                                                </tr>
373
                                                                </tr>
387
                                                            [% END # / FOREACH itemLoo IN bibitemloo.itemLoop%]
374
                                                            [% END # / FOREACH itemLoo IN bibitemloo.itemLoop%]
388
                                                        </table> <!-- / #copiesrow_[% bibitemloo.biblionumber %] -->
375
                                                        </table> <!-- / #copiesrow_[% bibitemloo.biblionumber %] -->
389
                                                    [% END # / IF ( OPACItemHolds )%]
376
                                                    [% END # / IF ( bibitemloo.itemholdable )%]
390
                                                </div> <!-- / #hold-options-[% bibitemloo.biblionumber %] -->
377
                                                </div> <!-- / #hold-options-[% bibitemloo.biblionumber %] -->
391
                                            </fieldset>
378
                                            </fieldset>
392
                                        [% END # / IF ( bibitemloo.holdable ) %]
379
                                        [% END # / IF ( bibitemloo.holdable ) %]
Lines 466-477 Link Here
466
        // Hides all 'specific copy' table rows on load.
453
        // Hides all 'specific copy' table rows on load.
467
        $(".copiesrow").hide();
454
        $(".copiesrow").hide();
468
455
469
        [% IF OPACItemHolds == 'force' %]
456
        [% FOREACH bibitemloo IN bibitemloop %]
470
          [% FOREACH bibitemloo IN bibitemloop %]
457
          [% IF bibitemloo.force_hold %]
471
            [% IF bibitemloo.holdable %]
458
            $("#toggle-hold-options-[% bibitemloo.biblionumber %]").click();
472
              $("#toggle-hold-options-[% bibitemloo.biblionumber %]").click();
459
            $("#reqspecific_[% bibitemloo.biblionumber %]").click();
473
              $("#copiesrow_[% bibitemloo.biblionumber %]").show();
460
            $("#copiesrow_[% bibitemloo.biblionumber %]").show();
474
            [% END %]
475
          [% END %]
461
          [% END %]
476
        [% END %]
462
        [% END %]
477
463
Lines 529-548 Link Here
529
515
530
        $("#place_on_hdr").show();
516
        $("#place_on_hdr").show();
531
517
532
        [% IF OPACItemHolds == '1' %]
518
        $(".place_on_type").show();
533
            $(".place_on_type").show();
519
        // onload, selectany is checked
534
            // onload, selectany is checked
520
        $(".selectany").attr("checked", "checked");
535
            $(".selectany").attr("checked", "checked");
536
        [% END %]
537
521
538
        // If the user is *allowed* to choose a specific item
522
        // If the user is *allowed* to choose a specific item
539
        // The first one is preselected
523
        // The first one is preselected
540
        [% IF OPACItemHolds =="1" %]
524
        $("table.copiesrow").each(function(){
541
            $("table.copiesrow").each(function(){
525
            var id = suffixOf($(this).attr("id"), "_");
542
                var id = suffixOf($(this).attr("id"), "_");
526
            select_first_available(id);
543
                select_first_available(id);
527
        });
544
            });
545
        [% END %]
546
528
547
        // On confirmsjs change
529
        // On confirmsjs change
548
        $(".confirmjs").change(function(){
530
        $(".confirmjs").change(function(){
(-)a/koha-tmpl/opac-tmpl/bootstrap/en/modules/opac-results-grouped.tt (-8 / +2 lines)
Lines 227-240 href="/cgi-bin/koha/opac-rss.pl?[% query_cgi %][% limit_cgi |html %]" /> Link Here
227
                                                            <p>
227
                                                            <p>
228
                                                                [% IF Koha.Preference( 'RequestOnOpac' ) == 1 %]
228
                                                                [% IF Koha.Preference( 'RequestOnOpac' ) == 1 %]
229
                                                                    [% UNLESS ( GROUP_RESULT.norequests ) %]
229
                                                                    [% UNLESS ( GROUP_RESULT.norequests ) %]
230
                                                                        [% IF Koha.Preference( 'opacuserlogin' ) == 1 %]
230
                                                                        [% IF Koha.Preference( 'opacuserlogin' ) == 1 && GROUP_RESULT.holdable %]
231
                                                                            [% IF ( AllowOnShelfHolds ) %]
231
                                                                            <a class="hold" href="/cgi-bin/koha/opac-reserve.pl?biblionumber=[% GROUP_RESULT.biblionumber %]">Place hold</a><!-- add back when available 0 holds in queue-->
232
                                                                                <a class="hold" href="/cgi-bin/koha/opac-reserve.pl?biblionumber=[% GROUP_RESULT.biblionumber %]">Place hold</a><!-- add back when available 0 holds in queue-->
233
                                                                            [% ELSE %]
234
                                                                                [% IF ( GROUP_RESULT.itemsissued ) %]
235
                                                                                    <a class="hold" href="/cgi-bin/koha/opac-reserve.pl?biblionumber=[% GROUP_RESULT.biblionumber %]">Place hold</a><!-- add back when available 0 holds in queue-->
236
                                                                                [% END %]
237
                                                                            [% END %]
238
                                                                        [% END %]
232
                                                                        [% END %]
239
                                                                    [% END %]
233
                                                                    [% END %]
240
                                                                [% END %]
234
                                                                [% END %]
(-)a/koha-tmpl/opac-tmpl/bootstrap/en/modules/opac-results.tt (-6 / +2 lines)
Lines 471-483 Link Here
471
                                                <div class="actions-menu">
471
                                                <div class="actions-menu">
472
                                                        [% IF Koha.Preference( 'RequestOnOpac' ) == 1 %]
472
                                                        [% IF Koha.Preference( 'RequestOnOpac' ) == 1 %]
473
                                                            [% UNLESS ( SEARCH_RESULT.norequests ) %]
473
                                                            [% UNLESS ( SEARCH_RESULT.norequests ) %]
474
                                                                [% IF ( ( Koha.Preference( 'opacuserlogin' ) == 1 ) && AllowOnShelfHolds ) %]
474
                                                                [% IF ( Koha.Preference( 'opacuserlogin' ) == 1 ) && SEARCH_RESULT.holdable %]
475
                                                                    <span class="actions"><a class="hold" href="/cgi-bin/koha/opac-reserve.pl?biblionumber=[% SEARCH_RESULT.biblionumber %]">Place hold</a></span><!-- add back when available 0 holds in queue-->
475
                                                                    <span class="actions"><a class="hold" href="/cgi-bin/koha/opac-reserve.pl?biblionumber=[% SEARCH_RESULT.biblionumber %]">Place hold</a></span><!-- add back when available 0 holds in queue-->
476
                                                                [% ELSE %]
476
                                                                [% END # / IF opacuserlogin && holdable %]
477
                                                                    [% IF ( SEARCH_RESULT.itemsissued ) %]
478
                                                                        <a class="hold" href="/cgi-bin/koha/opac-reserve.pl?biblionumber=[% SEARCH_RESULT.biblionumber %]">Place hold</a><!-- add back when available 0 holds in queue-->
479
                                                                    [% END %]
480
                                                                [% END # / IF opacuserlogin && AllowOnShelfHolds %]
481
                                                            [% END # UNLESS SEARCH_RESULT.norequests %]
477
                                                            [% END # UNLESS SEARCH_RESULT.norequests %]
482
                                                        [% END # IF RequestOnOpac %]
478
                                                        [% END # IF RequestOnOpac %]
483
479
(-)a/koha-tmpl/opac-tmpl/bootstrap/en/modules/opac-shelves.tt (-1 / +1 lines)
Lines 418-424 Link Here
418
                                                        [% IF Koha.Preference( 'RequestOnOpac' ) == 1 %]
418
                                                        [% IF Koha.Preference( 'RequestOnOpac' ) == 1 %]
419
                                                            [% UNLESS ( itemsloo.norequests ) %]
419
                                                            [% UNLESS ( itemsloo.norequests ) %]
420
                                                                [% IF Koha.Preference( 'opacuserlogin' ) == 1 %]
420
                                                                [% IF Koha.Preference( 'opacuserlogin' ) == 1 %]
421
                                                                    [% IF ( AllowOnShelfHolds ) %]
421
                                                                    [% IF ( itemsloo.allow_onshelf_holds ) %]
422
                                                                        <span class="actions"><a class="hold" href="/cgi-bin/koha/opac-reserve.pl?biblionumber=[% itemsloo.biblionumber %]">Place hold</a><!-- add back when available 0 holds in queue--></span>
422
                                                                        <span class="actions"><a class="hold" href="/cgi-bin/koha/opac-reserve.pl?biblionumber=[% itemsloo.biblionumber %]">Place hold</a><!-- add back when available 0 holds in queue--></span>
423
                                                                    [% ELSE %]
423
                                                                    [% ELSE %]
424
                                                                        [% IF ( itemsloo.itemsissued ) %]
424
                                                                        [% IF ( itemsloo.itemsissued ) %]
(-)a/opac/opac-ISBDdetail.pl (-12 / +15 lines)
Lines 49-54 use CGI qw ( -utf8 ); Link Here
49
use MARC::Record;
49
use MARC::Record;
50
use C4::Biblio;
50
use C4::Biblio;
51
use C4::Items;
51
use C4::Items;
52
use C4::Reserves;
52
use C4::Acquisition;
53
use C4::Acquisition;
53
use C4::Review;
54
use C4::Review;
54
use C4::Serials;    # uses getsubscriptionfrom biblionumber
55
use C4::Serials;    # uses getsubscriptionfrom biblionumber
Lines 70-86 my $biblionumber = $query->param('biblionumber'); Link Here
70
$biblionumber = int($biblionumber);
71
$biblionumber = int($biblionumber);
71
72
72
# get biblionumbers stored in the cart
73
# get biblionumbers stored in the cart
73
my @cart_list;
74
if(my $cart_list = $query->cookie("bib_list")){
74
75
    my @cart_list = split(/\//, $cart_list);
75
if($query->cookie("bib_list")){
76
    my $cart_list = $query->cookie("bib_list");
77
    @cart_list = split(/\//, $cart_list);
78
    if ( grep {$_ eq $biblionumber} @cart_list) {
76
    if ( grep {$_ eq $biblionumber} @cart_list) {
79
        $template->param( incart => 1 );
77
        $template->param( incart => 1 );
80
    }
78
    }
81
}
79
}
82
80
83
$template->param( 'AllowOnShelfHolds' => C4::Context->preference('AllowOnShelfHolds') );
84
$template->param( 'ItemsIssued' => CountItemsIssued( $biblionumber ) );
81
$template->param( 'ItemsIssued' => CountItemsIssued( $biblionumber ) );
85
82
86
my $marcflavour      = C4::Context->preference("marcflavour");
83
my $marcflavour      = C4::Context->preference("marcflavour");
Lines 148-163 $template->param( Link Here
148
);
145
);
149
146
150
my $norequests = 1;
147
my $norequests = 1;
148
my $allow_onshelf_holds;
151
my $res = GetISBDView($biblionumber, "opac");
149
my $res = GetISBDView($biblionumber, "opac");
152
150
153
my $itemtypes = GetItemTypes();
151
my $itemtypes = GetItemTypes();
152
my $borrower = GetMember( 'borrowernumber' => $loggedinuser );
154
for my $itm (@items) {
153
for my $itm (@items) {
155
    $norequests = 0
154
    $norequests = 0
156
       if ( (not $itm->{'withdrawn'} )
155
      if $norequests
157
         && (not $itm->{'itemlost'} )
156
        && !$itm->{'withdrawn'}
158
         && ($itm->{'itemnotforloan'}<0 || not $itm->{'itemnotforloan'} )
157
        && !$itm->{'itemlost'}
159
		 && (not $itemtypes->{$itm->{'itype'}}->{notforloan} )
158
        && ($itm->{'itemnotforloan'}<0 || not $itm->{'itemnotforloan'})
160
         && ($itm->{'itemnumber'} ) );
159
        && !$itemtypes->{$itm->{'itype'}}->{notforloan}
160
        && $itm->{'itemnumber'};
161
162
    $allow_onshelf_holds = C4::Reserves::OnShelfHoldsAllowed($itm, $borrower)
163
      unless $allow_onshelf_holds;
161
}
164
}
162
165
163
my $reviews = getreviews( $biblionumber, 1 );
166
my $reviews = getreviews( $biblionumber, 1 );
Lines 173-179 foreach ( @$reviews ) { Link Here
173
176
174
$template->param(
177
$template->param(
175
    RequestOnOpac       => C4::Context->preference("RequestOnOpac"),
178
    RequestOnOpac       => C4::Context->preference("RequestOnOpac"),
176
    AllowOnShelfHolds   => C4::Context->preference('AllowOnShelfHolds'),
179
    AllowOnShelfHolds   => $allow_onshelf_holds,
177
    norequests   => $norequests,
180
    norequests   => $norequests,
178
    ISBD         => $res,
181
    ISBD         => $res,
179
    biblionumber => $biblionumber,
182
    biblionumber => $biblionumber,
(-)a/opac/opac-MARCdetail.pl (-6 / +12 lines)
Lines 52-57 use CGI qw ( -utf8 ); Link Here
52
use MARC::Record;
52
use MARC::Record;
53
use C4::Biblio;
53
use C4::Biblio;
54
use C4::Items;
54
use C4::Items;
55
use C4::Reserves;
56
use C4::Members;
55
use C4::Acquisition;
57
use C4::Acquisition;
56
use C4::Koha;
58
use C4::Koha;
57
use List::MoreUtils qw/any/;
59
use List::MoreUtils qw/any/;
Lines 103-119 $template->param( Link Here
103
);
105
);
104
106
105
# get biblionumbers stored in the cart
107
# get biblionumbers stored in the cart
106
my @cart_list;
108
if(my $cart_list = $query->cookie("bib_list")){
107
109
    my @cart_list = split(/\//, $cart_list);
108
if($query->cookie("bib_list")){
109
    my $cart_list = $query->cookie("bib_list");
110
    @cart_list = split(/\//, $cart_list);
111
    if ( grep {$_ eq $biblionumber} @cart_list) {
110
    if ( grep {$_ eq $biblionumber} @cart_list) {
112
        $template->param( incart => 1 );
111
        $template->param( incart => 1 );
113
    }
112
    }
114
}
113
}
115
114
116
$template->param( 'AllowOnShelfHolds' => C4::Context->preference('AllowOnShelfHolds') );
115
my $allow_onshelf_holds;
116
my $borrower = GetMember( 'borrowernumber' => $loggedinuser );
117
for my $itm (@all_items) {
118
    $allow_onshelf_holds = C4::Reserves::OnShelfHoldsAllowed($itm, $borrower);
119
    last if $allow_onshelf_holds;
120
}
121
122
$template->param( 'AllowOnShelfHolds' => $allow_onshelf_holds );
117
$template->param( 'ItemsIssued' => CountItemsIssued( $biblionumber ) );
123
$template->param( 'ItemsIssued' => CountItemsIssued( $biblionumber ) );
118
124
119
# adding the $RequestOnOpac param
125
# adding the $RequestOnOpac param
(-)a/opac/opac-detail.pl (-10 / +12 lines)
Lines 437-448 if ($session->param('busc')) { Link Here
437
}
437
}
438
438
439
439
440
441
$template->param( 'AllowOnShelfHolds' => C4::Context->preference('AllowOnShelfHolds') );
442
$template->param( 'ItemsIssued' => CountItemsIssued( $biblionumber ) );
440
$template->param( 'ItemsIssued' => CountItemsIssued( $biblionumber ) );
443
444
445
446
$template->param('OPACShowCheckoutName' => C4::Context->preference("OPACShowCheckoutName") );
441
$template->param('OPACShowCheckoutName' => C4::Context->preference("OPACShowCheckoutName") );
447
$template->param('OPACShowBarcode' => C4::Context->preference("OPACShowBarcode") );
442
$template->param('OPACShowBarcode' => C4::Context->preference("OPACShowBarcode") );
448
443
Lines 627-641 if ( not $viewallitems and @items > $max_items_to_display ) { Link Here
627
        items_count => scalar( @items ),
622
        items_count => scalar( @items ),
628
    );
623
    );
629
} else {
624
} else {
625
  my $allow_onshelf_holds;
626
  my $borrower = GetMember( 'borrowernumber' => $borrowernumber );
630
  for my $itm (@items) {
627
  for my $itm (@items) {
631
    $itm->{holds_count} = $item_reserves{ $itm->{itemnumber} };
628
    $itm->{holds_count} = $item_reserves{ $itm->{itemnumber} };
632
    $itm->{priority} = $priority{ $itm->{itemnumber} };
629
    $itm->{priority} = $priority{ $itm->{itemnumber} };
633
    $norequests = 0
630
    $norequests = 0
634
       if ( (not $itm->{'withdrawn'} )
631
      if $norequests
635
         && (not $itm->{'itemlost'} )
632
        && !$itm->{'withdrawn'}
636
         && ($itm->{'itemnotforloan'}<0 || not $itm->{'itemnotforloan'} )
633
        && !$itm->{'itemlost'}
637
		 && (not $itemtypes->{$itm->{'itype'}}->{notforloan} )
634
        && ($itm->{'itemnotforloan'}<0 || not $itm->{'itemnotforloan'})
638
         && ($itm->{'itemnumber'} ) );
635
        && !$itemtypes->{$itm->{'itype'}}->{notforloan}
636
        && $itm->{'itemnumber'};
637
638
    $allow_onshelf_holds = C4::Reserves::OnShelfHoldsAllowed($itm, $borrower)
639
      unless $allow_onshelf_holds;
639
640
640
    # get collection code description, too
641
    # get collection code description, too
641
    my $ccode = $itm->{'ccode'};
642
    my $ccode = $itm->{'ccode'};
Lines 691-696 if ( not $viewallitems and @items > $max_items_to_display ) { Link Here
691
        push @itemloop, $itm;
692
        push @itemloop, $itm;
692
    }
693
    }
693
  }
694
  }
695
  $template->param( 'AllowOnShelfHolds' => $allow_onshelf_holds );
694
}
696
}
695
697
696
# Display only one tab if one items list is empty
698
# Display only one tab if one items list is empty
(-)a/opac/opac-reserve.pl (-15 / +26 lines)
Lines 423-428 foreach my $biblioNum (@biblionumbers) { Link Here
423
423
424
    $biblioLoopIter{itemLoop} = [];
424
    $biblioLoopIter{itemLoop} = [];
425
    my $numCopiesAvailable = 0;
425
    my $numCopiesAvailable = 0;
426
    my $numCopiesOPACAvailable = 0;
426
    foreach my $itemInfo (@{$biblioData->{itemInfos}}) {
427
    foreach my $itemInfo (@{$biblioData->{itemInfos}}) {
427
        my $itemNum = $itemInfo->{itemnumber};
428
        my $itemNum = $itemInfo->{itemnumber};
428
        my $itemLoopIter = {};
429
        my $itemLoopIter = {};
Lines 517-532 foreach my $biblioNum (@biblionumbers) { Link Here
517
518
518
        my $branch = GetReservesControlBranch( $itemInfo, $borr );
519
        my $branch = GetReservesControlBranch( $itemInfo, $borr );
519
520
520
        my $branchitemrule = GetBranchItemRule( $branch, $itemInfo->{'itype'} );
521
        my $policy_holdallowed = !$itemLoopIter->{already_reserved};
521
        my $policy_holdallowed = 1;
522
        if ($policy_holdallowed) {
522
523
            if (my $branchitemrule = GetBranchItemRule( $branch, $itemInfo->{'itype'} )) {
523
        if ( $branchitemrule->{'holdallowed'} == 0 ||
524
                $policy_holdallowed =
524
                ( $branchitemrule->{'holdallowed'} == 1 && $borr->{'branchcode'} ne $itemInfo->{'homebranch'} ) ) {
525
                  ($branchitemrule->{'holdallowed'} == 2) ||
525
            $policy_holdallowed = 0;
526
                  ($branchitemrule->{'holdallowed'} == 1
527
                      && $borr->{'branchcode'} eq $itemInfo->{'homebranch'});
528
            } else {
529
                $policy_holdallowed = 0; # No rule - not allowed
530
            }
526
        }
531
        }
527
532
        $policy_holdallowed &&=
528
        if (IsAvailableForItemLevelRequest($itemNum) and $policy_holdallowed and CanItemBeReserved($borrowernumber,$itemNum) eq 'OK' and ($itemLoopIter->{already_reserved} ne 1)) {
533
            IsAvailableForItemLevelRequest($itemInfo,$borr) &&
529
            $itemLoopIter->{available} = 1;
534
            CanItemBeReserved($borrowernumber,$itemNum) eq 'OK';
535
536
        if ($policy_holdallowed) {
537
            if ( my $hold_allowed = OPACItemHoldsAllowed( $itemInfo, $borr ) ) {
538
                $itemLoopIter->{available} = 1;
539
                $numCopiesOPACAvailable++;
540
                $biblioLoopIter{force_hold} = 1 if $hold_allowed eq 'F';
541
            }
530
            $numCopiesAvailable++;
542
            $numCopiesAvailable++;
531
        }
543
        }
532
544
Lines 545-567 foreach my $biblioNum (@biblionumbers) { Link Here
545
        $numBibsAvailable++;
557
        $numBibsAvailable++;
546
        $biblioLoopIter{bib_available} = 1;
558
        $biblioLoopIter{bib_available} = 1;
547
        $biblioLoopIter{holdable} = 1;
559
        $biblioLoopIter{holdable} = 1;
560
        $biblioLoopIter{itemholdable} = 1 if $numCopiesOPACAvailable;
548
    }
561
    }
549
    if ($biblioLoopIter{already_reserved}) {
562
    if ($biblioLoopIter{already_reserved}) {
550
        $biblioLoopIter{holdable} = undef;
563
        $biblioLoopIter{holdable} = undef;
551
    }
564
        $biblioLoopIter{itemholdable} = undef;
552
    my $canReserve = CanBookBeReserved($borrowernumber,$biblioNum);
553
    unless ($canReserve eq 'OK') {
554
        $biblioLoopIter{holdable} = undef;
555
        $biblioLoopIter{ $canReserve } = 1;
556
    }
565
    }
557
    if(not C4::Context->preference('AllowHoldsOnPatronsPossessions') and CheckIfIssuedToPatron($borrowernumber,$biblioNum)) {
566
    if(not C4::Context->preference('AllowHoldsOnPatronsPossessions') and CheckIfIssuedToPatron($borrowernumber,$biblioNum)) {
558
        $biblioLoopIter{holdable} = undef;
567
        $biblioLoopIter{holdable} = undef;
559
        $biblioLoopIter{already_patron_possession} = 1;
568
        $biblioLoopIter{already_patron_possession} = 1;
560
    }
569
    }
561
570
562
    if( $biblioLoopIter{holdable} ){ $anyholdable++; }
571
    $biblioLoopIter{holdable} &&= CanBookBeReserved($borrowernumber,$biblioNum) eq 'OK';
563
572
564
    push @$biblioLoop, \%biblioLoopIter;
573
    push @$biblioLoop, \%biblioLoopIter;
574
575
    $anyholdable = 1 if $biblioLoopIter{holdable};
565
}
576
}
566
577
567
if ( $numBibsAvailable == 0 || $anyholdable == 0) {
578
if ( $numBibsAvailable == 0 || $anyholdable == 0) {
(-)a/opac/opac-search.pl (-1 / +18 lines)
Lines 42-47 use C4::Branch; # GetBranches Link Here
42
use C4::SocialData;
42
use C4::SocialData;
43
use C4::Ratings;
43
use C4::Ratings;
44
use C4::External::OverDrive;
44
use C4::External::OverDrive;
45
use C4::Members;
46
use C4::Reserves;
45
47
46
use POSIX qw(ceil floor strftime);
48
use POSIX qw(ceil floor strftime);
47
use URI::Escape;
49
use URI::Escape;
Lines 136-142 if (C4::Context->preference("marcflavour") eq "UNIMARC" ) { Link Here
136
elsif (C4::Context->preference("marcflavour") eq "MARC21" ) {
138
elsif (C4::Context->preference("marcflavour") eq "MARC21" ) {
137
    $template->param('usmarc' => 1);
139
    $template->param('usmarc' => 1);
138
}
140
}
139
$template->param( 'AllowOnShelfHolds' => C4::Context->preference('AllowOnShelfHolds') );
141
140
$template->param( 'OPACNoResultsFound' => C4::Context->preference('OPACNoResultsFound') );
142
$template->param( 'OPACNoResultsFound' => C4::Context->preference('OPACNoResultsFound') );
141
143
142
$template->param(
144
$template->param(
Lines 567-574 if ($@ || $error) { Link Here
567
    exit;
569
    exit;
568
}
570
}
569
571
572
my $borrower = $borrowernumber ? GetMember( borrowernumber => $borrowernumber ) : undef;
573
570
# At this point, each server has given us a result set
574
# At this point, each server has given us a result set
571
# now we build that set for template display
575
# now we build that set for template display
576
my %allow_onshelf_holds;
572
my @sup_results_array;
577
my @sup_results_array;
573
for (my $i=0;$i<@servers;$i++) {
578
for (my $i=0;$i<@servers;$i++) {
574
    my $server = $servers[$i];
579
    my $server = $servers[$i];
Lines 583-593 for (my $i=0;$i<@servers;$i++) { Link Here
583
                # we need to set the offset parameter of searchResults to 0
588
                # we need to set the offset parameter of searchResults to 0
584
                my @group_results = searchResults( 'opac', $query_desc, $group->{'group_count'},$results_per_page, 0, $scan,
589
                my @group_results = searchResults( 'opac', $query_desc, $group->{'group_count'},$results_per_page, 0, $scan,
585
                                                   $group->{"RECORDS"});
590
                                                   $group->{"RECORDS"});
591
                if ($borrower) {
592
                    $_->{holdable} =
593
                        IsAvailableForItemLevelRequest($_, $borrower) &&
594
                        OPACItemHoldsAllowed($_, $borrower)
595
                      foreach @group_results;
596
                }
586
                push @newresults, { group_label => $group->{'group_label'}, GROUP_RESULTS => \@group_results };
597
                push @newresults, { group_label => $group->{'group_label'}, GROUP_RESULTS => \@group_results };
587
            }
598
            }
588
        } else {
599
        } else {
589
            @newresults = searchResults('opac', $query_desc, $hits, $results_per_page, $offset, $scan,
600
            @newresults = searchResults('opac', $query_desc, $hits, $results_per_page, $offset, $scan,
590
                                        $results_hashref->{$server}->{"RECORDS"});
601
                                        $results_hashref->{$server}->{"RECORDS"});
602
            if ($borrower) {
603
                $_->{holdable} =
604
                    IsAvailableForItemLevelRequest($_, $borrower) &&
605
                    OPACItemHoldsAllowed($_, $borrower)
606
                  foreach @newresults;
607
            }
591
        }
608
        }
592
        $hits = 0 unless @newresults;
609
        $hits = 0 unless @newresults;
593
610
(-)a/reserve/request.pl (-7 / +4 lines)
Lines 334-342 foreach my $biblionumber (@biblionumbers) { Link Here
334
		        $item->{hosttitle} = GetBiblioData($item->{biblionumber})->{title};
334
		        $item->{hosttitle} = GetBiblioData($item->{biblionumber})->{title};
335
		}
335
		}
336
		
336
		
337
            #   add information
338
            $item->{itemcallnumber} = $item->{itemcallnumber};
339
340
            # if the item is currently on loan, we display its return date and
337
            # if the item is currently on loan, we display its return date and
341
            # change the background color
338
            # change the background color
342
            my $issues= GetItemIssue($itemnumber);
339
            my $issues= GetItemIssue($itemnumber);
Lines 347-355 foreach my $biblionumber (@biblionumbers) { Link Here
347
344
348
            # checking reserve
345
            # checking reserve
349
            my ($reservedate,$reservedfor,$expectedAt,$reserve_id,$wait) = GetReservesFromItemnumber($itemnumber);
346
            my ($reservedate,$reservedfor,$expectedAt,$reserve_id,$wait) = GetReservesFromItemnumber($itemnumber);
350
            my $ItemBorrowerReserveInfo = GetMember( borrowernumber => $reservedfor );
351
352
            if ( defined $reservedate ) {
347
            if ( defined $reservedate ) {
348
                my $ItemBorrowerReserveInfo = GetMember( borrowernumber => $reservedfor );
349
353
                $item->{backgroundcolor} = 'reserved';
350
                $item->{backgroundcolor} = 'reserved';
354
                $item->{reservedate}     = format_date($reservedate);
351
                $item->{reservedate}     = format_date($reservedate);
355
                $item->{ReservedForBorrowernumber}     = $reservedfor;
352
                $item->{ReservedForBorrowernumber}     = $reservedfor;
Lines 394-400 foreach my $biblionumber (@biblionumbers) { Link Here
394
            }
391
            }
395
392
396
            # If there is no loan, return and transfer, we show a checkbox.
393
            # If there is no loan, return and transfer, we show a checkbox.
397
            $item->{notforloan} = $item->{notforloan} || 0;
394
            $item->{notforloan} ||= 0;
398
395
399
            # if independent branches is on we need to check if the person can reserve
396
            # if independent branches is on we need to check if the person can reserve
400
            # for branches they arent logged in to
397
            # for branches they arent logged in to
Lines 424-430 foreach my $biblionumber (@biblionumbers) { Link Here
424
            if (
421
            if (
425
                   $policy_holdallowed
422
                   $policy_holdallowed
426
                && !$item->{cantreserve}
423
                && !$item->{cantreserve}
427
                && IsAvailableForItemLevelRequest($itemnumber)
424
                && IsAvailableForItemLevelRequest($item, $borrowerinfo)
428
                && CanItemBeReserved(
425
                && CanItemBeReserved(
429
                    $borrowerinfo->{borrowernumber}, $itemnumber
426
                    $borrowerinfo->{borrowernumber}, $itemnumber
430
                ) eq 'OK'
427
                ) eq 'OK'
(-)a/t/db_dependent/Circulation.t (-1 / +1 lines)
Lines 291-297 C4::Context->dbh->do("DELETE FROM accountlines"); Link Here
291
    );
291
    );
292
292
293
    # Testing of feature to allow the renewal of reserved items if other items on the record can fill all needed holds
293
    # Testing of feature to allow the renewal of reserved items if other items on the record can fill all needed holds
294
    C4::Context->set_preference('AllowOnShelfHolds', 1 );
294
    C4::Context->dbh->do("UPDATE issuingrules SET onshelfholds = 1");
295
    C4::Context->set_preference('AllowRenewalIfOtherItemsAvailable', 1 );
295
    C4::Context->set_preference('AllowRenewalIfOtherItemsAvailable', 1 );
296
    ( $renewokay, $error ) = CanBookBeRenewed($renewing_borrowernumber, $itemnumber);
296
    ( $renewokay, $error ) = CanBookBeRenewed($renewing_borrowernumber, $itemnumber);
297
    is( $renewokay, 1, 'Bug 11634 - Allow renewal of item with unfilled holds if other available items can fill those holds');
297
    is( $renewokay, 1, 'Bug 11634 - Allow renewal of item with unfilled holds if other available items can fill those holds');
(-)a/t/db_dependent/Circulation_Issuingrule.t (+6 lines)
Lines 132-137 my $sampleissuingrule1 = { Link Here
132
    itemtype           => 'BOOK',
132
    itemtype           => 'BOOK',
133
    categorycode       => $samplecat->{categorycode},
133
    categorycode       => $samplecat->{categorycode},
134
    maxsuspensiondays  => 0,
134
    maxsuspensiondays  => 0,
135
    onshelfholds       => 0,
136
    opacitemholds      => 'N',
135
};
137
};
136
my $sampleissuingrule2 = {
138
my $sampleissuingrule2 = {
137
    branchcode         => $samplebranch2->{branchcode},
139
    branchcode         => $samplebranch2->{branchcode},
Lines 158-163 my $sampleissuingrule2 = { Link Here
158
    chargename         => 'Null',
160
    chargename         => 'Null',
159
    restrictedtype     => 'Null',
161
    restrictedtype     => 'Null',
160
    maxsuspensiondays  => 0,
162
    maxsuspensiondays  => 0,
163
    onshelfholds       => 1,
164
    opacitemholds      => 'Y',
161
};
165
};
162
my $sampleissuingrule3 = {
166
my $sampleissuingrule3 = {
163
    branchcode         => $samplebranch1->{branchcode},
167
    branchcode         => $samplebranch1->{branchcode},
Lines 184-189 my $sampleissuingrule3 = { Link Here
184
    chargename         => 'Null',
188
    chargename         => 'Null',
185
    restrictedtype     => 'Null',
189
    restrictedtype     => 'Null',
186
    maxsuspensiondays  => 0,
190
    maxsuspensiondays  => 0,
191
    onshelfholds       => 1,
192
    opacitemholds      => 'F',
187
};
193
};
188
$query = 'INSERT INTO issuingrules (
194
$query = 'INSERT INTO issuingrules (
189
                branchcode,
195
                branchcode,
(-)a/t/db_dependent/Reserves.t (-2 / +23 lines)
Lines 17-23 Link Here
17
17
18
use Modern::Perl;
18
use Modern::Perl;
19
19
20
use Test::More tests => 53;
20
use Test::More tests => 56;
21
21
22
use MARC::Record;
22
use MARC::Record;
23
use DateTime::Duration;
23
use DateTime::Duration;
Lines 506-511 is( C4::Reserves::CanBookBeReserved($borrowernumber, $biblionumber) , 'OK', "Res Link Here
506
####### EO Bug 13113 <<<
506
####### EO Bug 13113 <<<
507
       ####
507
       ####
508
508
509
my $item = GetItem($itemnumber);
510
511
ok( C4::Reserves::IsAvailableForItemLevelRequest($item, $borrower), "Reserving a book on item level" );
512
513
my $itype = C4::Reserves::_get_itype($item);
514
my $categorycode = $borrower->{categorycode};
515
my $holdingbranch = $item->{holdingbranch};
516
my $rule = C4::Circulation::GetIssuingRule($categorycode, $itype, $holdingbranch);
517
518
$dbh->do(
519
    "UPDATE issuingrules SET onshelfholds = 1 WHERE categorycode = ? AND itemtype= ? and branchcode = ?",
520
    undef,
521
    $rule->{categorycode}, $rule->{itemtype}, $rule->{branchcode}
522
);
523
ok( C4::Reserves::OnShelfHoldsAllowed($item, $borrower), "OnShelfHoldsAllowed() allowed" );
524
$dbh->do(
525
    "UPDATE issuingrules SET onshelfholds = 0 WHERE categorycode = ? AND itemtype= ? and branchcode = ?",
526
    undef,
527
    $rule->{categorycode}, $rule->{itemtype}, $rule->{branchcode}
528
);
529
ok( !C4::Reserves::OnShelfHoldsAllowed($item, $borrower), "OnShelfHoldsAllowed() disallowed" );
530
509
$dbh->rollback;
531
$dbh->rollback;
510
532
511
sub count_hold_print_messages {
533
sub count_hold_print_messages {
512
- 

Return to bug 5786