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 955-961 sub CheckReserves { Link Here
955
           LEFT JOIN itemtypes   ON biblioitems.itemtype   = itemtypes.itemtype
956
           LEFT JOIN itemtypes   ON biblioitems.itemtype   = itemtypes.itemtype
956
        ";
957
        ";
957
    }
958
    }
958
   
959
959
    if ($item) {
960
    if ($item) {
960
        $sth = $dbh->prepare("$select WHERE itemnumber = ?");
961
        $sth = $dbh->prepare("$select WHERE itemnumber = ?");
961
        $sth->execute($item);
962
        $sth->execute($item);
Lines 1053-1059 sub CancelExpiredReserves { Link Here
1053
    # Cancel reserves that have passed their expiration date.
1054
    # Cancel reserves that have passed their expiration date.
1054
    my $dbh = C4::Context->dbh;
1055
    my $dbh = C4::Context->dbh;
1055
    my $sth = $dbh->prepare( "
1056
    my $sth = $dbh->prepare( "
1056
        SELECT * FROM reserves WHERE DATE(expirationdate) < DATE( CURDATE() ) 
1057
        SELECT * FROM reserves WHERE DATE(expirationdate) < DATE( CURDATE() )
1057
        AND expirationdate IS NOT NULL
1058
        AND expirationdate IS NOT NULL
1058
        AND found IS NULL
1059
        AND found IS NULL
1059
    " );
1060
    " );
Lines 1062-1068 sub CancelExpiredReserves { Link Here
1062
    while ( my $res = $sth->fetchrow_hashref() ) {
1063
    while ( my $res = $sth->fetchrow_hashref() ) {
1063
        CancelReserve({ reserve_id => $res->{'reserve_id'} });
1064
        CancelReserve({ reserve_id => $res->{'reserve_id'} });
1064
    }
1065
    }
1065
  
1066
1066
    # Cancel reserves that have been waiting too long
1067
    # Cancel reserves that have been waiting too long
1067
    if ( C4::Context->preference("ExpireReservesMaxPickUpDelay") ) {
1068
    if ( C4::Context->preference("ExpireReservesMaxPickUpDelay") ) {
1068
        my $max_pickup_delay = C4::Context->preference("ReservesMaxPickUpDelay");
1069
        my $max_pickup_delay = C4::Context->preference("ReservesMaxPickUpDelay");
Lines 1188-1199 If C<$rank> is 'del', the hold request is cancelled. Link Here
1188
1189
1189
If C<$rank> is an integer greater than zero, the priority of
1190
If C<$rank> is an integer greater than zero, the priority of
1190
the request is set to that value.  Since priority != 0 means
1191
the request is set to that value.  Since priority != 0 means
1191
that the item is not waiting on the hold shelf, setting the 
1192
that the item is not waiting on the hold shelf, setting the
1192
priority to a non-zero value also sets the request's found
1193
priority to a non-zero value also sets the request's found
1193
status and waiting date to NULL. 
1194
status and waiting date to NULL.
1194
1195
1195
The optional C<$itemnumber> parameter is used only when
1196
The optional C<$itemnumber> parameter is used only when
1196
C<$rank> is a non-zero integer; if supplied, the itemnumber 
1197
C<$rank> is a non-zero integer; if supplied, the itemnumber
1197
of the hold request is set accordingly; if omitted, the itemnumber
1198
of the hold request is set accordingly; if omitted, the itemnumber
1198
is cleared.
1199
is cleared.
1199
1200
Lines 1305-1311 sub ModReserveFill { Link Here
1305
                ";
1306
                ";
1306
    $sth = $dbh->prepare($query);
1307
    $sth = $dbh->prepare($query);
1307
    $sth->execute( $biblionumber, $resdate, $borrowernumber );
1308
    $sth->execute( $biblionumber, $resdate, $borrowernumber );
1308
    
1309
1309
    # now fix the priority on the others (if the priority wasn't
1310
    # now fix the priority on the others (if the priority wasn't
1310
    # already sorted!)....
1311
    # already sorted!)....
1311
    unless ( $priority == 0 ) {
1312
    unless ( $priority == 0 ) {
Lines 1350-1356 with the biblionumber & the borrowernumber, we can affect the itemnumber Link Here
1350
to the correct reserve.
1351
to the correct reserve.
1351
1352
1352
if $transferToDo is not set, then the status is set to "Waiting" as well.
1353
if $transferToDo is not set, then the status is set to "Waiting" as well.
1353
otherwise, a transfer is on the way, and the end of the transfer will 
1354
otherwise, a transfer is on the way, and the end of the transfer will
1354
take care of the waiting status
1355
take care of the waiting status
1355
1356
1356
=cut
1357
=cut
Lines 1511-1532 sub GetReserveInfo { Link Here
1511
1512
1512
=head2 IsAvailableForItemLevelRequest
1513
=head2 IsAvailableForItemLevelRequest
1513
1514
1514
  my $is_available = IsAvailableForItemLevelRequest($itemnumber);
1515
  my $is_available = IsAvailableForItemLevelRequest($item_record,$borrower_record);
1515
1516
1516
Checks whether a given item record is available for an
1517
Checks whether a given item record is available for an
1517
item-level hold request.  An item is available if
1518
item-level hold request.  An item is available if
1518
1519
1519
* it is not lost AND 
1520
* it is not lost AND
1520
* it is not damaged AND 
1521
* it is not damaged AND
1521
* it is not withdrawn AND 
1522
* it is not withdrawn AND
1522
* does not have a not for loan value > 0
1523
* does not have a not for loan value > 0
1523
1524
1524
Whether or not the item is currently on loan is 
1525
Need to check the issuingrules onshelfholds column,
1525
also checked - if the AllowOnShelfHolds system preference
1526
if this is set items on the shelf can be placed on hold
1526
is ON, an item can be requested even if it is currently
1527
on loan to somebody else.  If the system preference
1528
is OFF, an item that is currently checked out cannot
1529
be the target of an item-level hold request.
1530
1527
1531
Note that IsAvailableForItemLevelRequest() does not
1528
Note that IsAvailableForItemLevelRequest() does not
1532
check if the staff operator is authorized to place
1529
check if the staff operator is authorized to place
Lines 1537-1584 and canreservefromotherbranches. Link Here
1537
=cut
1534
=cut
1538
1535
1539
sub IsAvailableForItemLevelRequest {
1536
sub IsAvailableForItemLevelRequest {
1540
    my $itemnumber = shift;
1537
    my $item = shift;
1541
   
1538
    my $borrower = shift;
1542
    my $item = GetItem($itemnumber);
1543
1539
1540
    my $dbh = C4::Context->dbh;
1544
    # must check the notforloan setting of the itemtype
1541
    # must check the notforloan setting of the itemtype
1545
    # FIXME - a lot of places in the code do this
1542
    # FIXME - a lot of places in the code do this
1546
    #         or something similar - need to be
1543
    #         or something similar - need to be
1547
    #         consolidated
1544
    #         consolidated
1548
    my $dbh = C4::Context->dbh;
1545
    my $itype = _get_itype($item);
1549
    my $notforloan_query;
1546
    my $notforloan_per_itemtype
1547
      = $dbh->selectrow_array("SELECT notforloan FROM itemtypes WHERE itemtype = ?",
1548
                              undef, $itype);
1549
1550
    return 0 if
1551
        $notforloan_per_itemtype ||
1552
        $item->{itemlost}        ||
1553
        $item->{notforloan} > 0  ||
1554
        $item->{wthdrawn}        ||
1555
        ($item->{damaged} && !C4::Context->preference('AllowHoldsOnDamagedItems'));
1556
1557
1558
    return 1 if _OnShelfHoldsAllowed($itype,$borrower->{categorycode},$item->{holdingbranch});
1559
1560
    return $item->{onloan} || GetReserveStatus($item->{itemnumber}) eq "Waiting";
1561
}
1562
1563
=head2 OnShelfHoldsAllowed
1564
1565
  OnShelfHoldsAllowed($itemtype,$borrowercategory,$branchcode);
1566
1567
Checks issuingrules, using the borrowers categorycode, the itemtype, and branchcode to see if onshelf
1568
holds are allowed, returns true if so.
1569
1570
=cut
1571
1572
sub OnShelfHoldsAllowed {
1573
    my ($item, $borrower) = @_;
1574
1575
    my $itype = _get_itype($item);
1576
    return _OnShelfHoldsAllowed($itype,$borrower->{categorycode},$item->{holdingbranch});
1577
}
1578
1579
sub _get_itype {
1580
    my $item = shift;
1581
1582
    my $itype;
1550
    if (C4::Context->preference('item-level_itypes')) {
1583
    if (C4::Context->preference('item-level_itypes')) {
1551
        $notforloan_query = "SELECT itemtypes.notforloan
1584
        # We cant trust GetItem to honour the syspref, so safest to do it ourselves
1552
                             FROM items
1585
        # When GetItem is fixed, we can remove this
1553
                             JOIN itemtypes ON (itemtypes.itemtype = items.itype)
1586
        $itype = $item->{itype};
1554
                             WHERE itemnumber = ?";
1555
    } else {
1556
        $notforloan_query = "SELECT itemtypes.notforloan
1557
                             FROM items
1558
                             JOIN biblioitems USING (biblioitemnumber)
1559
                             JOIN itemtypes USING (itemtype)
1560
                             WHERE itemnumber = ?";
1561
    }
1587
    }
1562
    my $sth = $dbh->prepare($notforloan_query);
1588
    else {
1563
    $sth->execute($itemnumber);
1589
        # XXX This is a bit dodgy. It relies on biblio itemtype column having different name.
1564
    my $notforloan_per_itemtype = 0;
1590
        # So if we already have a biblioitems join when calling this function,
1565
    if (my ($notforloan) = $sth->fetchrow_array) {
1591
        # we don't need to access the database again
1566
        $notforloan_per_itemtype = 1 if $notforloan;
1592
        $itype = $item->{itemtype};
1593
    }
1594
    unless ($itype) {
1595
        my $dbh = C4::Context->dbh;
1596
        my $query = "SELECT itemtype FROM biblioitems WHERE biblioitemnumber = ? ";
1597
        my $sth = $dbh->prepare($query);
1598
        $sth->execute($item->{biblioitemnumber});
1599
        if (my $data = $sth->fetchrow_hashref()){
1600
            $itype = $data->{itemtype};
1601
        }
1567
    }
1602
    }
1603
    return $itype;
1604
}
1568
1605
1569
    my $available_per_item = 1;
1606
sub _OnShelfHoldsAllowed {
1570
    $available_per_item = 0 if $item->{itemlost} or
1607
    my ($itype,$borrowercategory,$branchcode) = @_;
1571
                               ( $item->{notforloan} > 0 ) or
1572
                               ($item->{damaged} and not C4::Context->preference('AllowHoldsOnDamagedItems')) or
1573
                               $item->{withdrawn} or
1574
                               $notforloan_per_itemtype;
1575
1576
1608
1577
    if (C4::Context->preference('AllowOnShelfHolds')) {
1609
    my $rule = C4::Circulation::GetIssuingRule($borrowercategory, $itype, $branchcode);
1578
        return $available_per_item;
1610
    return $rule->{onshelfholds};
1579
    } else {
1580
        return ($available_per_item and ($item->{onloan} or GetReserveStatus($itemnumber) eq "Waiting"));
1581
    }
1582
}
1611
}
1583
1612
1584
=head2 AlterPriority
1613
=head2 AlterPriority
Lines 1846-1852 sub _FixPriority { Link Here
1846
    
1875
    
1847
    $sth = $dbh->prepare( "SELECT reserve_id FROM reserves WHERE lowestPriority = 1 ORDER BY priority" );
1876
    $sth = $dbh->prepare( "SELECT reserve_id FROM reserves WHERE lowestPriority = 1 ORDER BY priority" );
1848
    $sth->execute();
1877
    $sth->execute();
1849
    
1878
1850
    unless ( $ignoreSetLowestRank ) {
1879
    unless ( $ignoreSetLowestRank ) {
1851
      while ( my $res = $sth->fetchrow_hashref() ) {
1880
      while ( my $res = $sth->fetchrow_hashref() ) {
1852
        _FixPriority({
1881
        _FixPriority({
Lines 1914-1920 sub _Findgroupreserve { Link Here
1914
          unless any{ $data->{borrowernumber} eq $_ } @$ignore_borrowers ;
1943
          unless any{ $data->{borrowernumber} eq $_ } @$ignore_borrowers ;
1915
    }
1944
    }
1916
    return @results if @results;
1945
    return @results if @results;
1917
    
1946
1918
    # check for title-level targetted match
1947
    # check for title-level targetted match
1919
    my $title_level_target_query = qq{
1948
    my $title_level_target_query = qq{
1920
        SELECT reserves.biblionumber        AS biblionumber,
1949
        SELECT reserves.biblionumber        AS biblionumber,
Lines 1999-2005 sub _koha_notify_reserve { Link Here
1999
2028
2000
    my $dbh = C4::Context->dbh;
2029
    my $dbh = C4::Context->dbh;
2001
    my $borrower = C4::Members::GetMember(borrowernumber => $borrowernumber);
2030
    my $borrower = C4::Members::GetMember(borrowernumber => $borrowernumber);
2002
    
2031
2003
    # Try to get the borrower's email address
2032
    # Try to get the borrower's email address
2004
    my $to_address = C4::Members::GetNoticeEmailAddress($borrowernumber);
2033
    my $to_address = C4::Members::GetNoticeEmailAddress($borrowernumber);
2005
2034
Lines 2119-2124 sub _ShiftPriorityByDateAndPriority { Link Here
2119
    return $new_priority;  # so the caller knows what priority they wind up receiving
2148
    return $new_priority;  # so the caller knows what priority they wind up receiving
2120
}
2149
}
2121
2150
2151
=head2 OPACItemHoldsAllowed
2152
2153
  OPACItemHoldsAllowed($item_record,$borrower_record);
2154
2155
Checks issuingrules, using the borrowers categorycode, the itemtype, and branchcode to see
2156
if specific item holds are allowed, returns true if so.
2157
2158
=cut
2159
2160
sub OPACItemHoldsAllowed {
2161
    my ($item,$borrower) = @_;
2162
2163
    my $branchcode = $item->{homebranch} or die "No homebranch";
2164
    my $itype;
2165
    my $dbh = C4::Context->dbh;
2166
    if (C4::Context->preference('item-level_itypes')) {
2167
       # We cant trust GetItem to honour the syspref, so safest to do it ourselves
2168
       # When GetItem is fixed, we can remove this
2169
       $itype = $item->{itype};
2170
    }
2171
    else {
2172
       my $query = "SELECT itemtype FROM biblioitems WHERE biblioitemnumber = ? ";
2173
       my $sth = $dbh->prepare($query);
2174
       $sth->execute($item->{biblioitemnumber});
2175
       if (my $data = $sth->fetchrow_hashref()){
2176
           $itype = $data->{itemtype};
2177
       }
2178
    }
2179
2180
    my $query = "SELECT opacitemholds,categorycode,itemtype,branchcode FROM issuingrules WHERE
2181
          (issuingrules.categorycode = ? OR issuingrules.categorycode = '*')
2182
        AND
2183
          (issuingrules.itemtype = ? OR issuingrules.itemtype = '*')
2184
        AND
2185
          (issuingrules.branchcode = ? OR issuingrules.branchcode = '*')
2186
        ORDER BY
2187
          issuingrules.categorycode desc,
2188
          issuingrules.itemtype desc,
2189
          issuingrules.branchcode desc
2190
       LIMIT 1";
2191
    my $sth = $dbh->prepare($query);
2192
    $sth->execute($borrower->{categorycode},$itype,$branchcode);
2193
    my $data = $sth->fetchrow_hashref;
2194
    my $opacitemholds = uc substr ($data->{opacitemholds}, 0, 1);
2195
    return '' if $opacitemholds eq 'N';
2196
    return $opacitemholds;
2197
}
2198
2122
=head2 MoveReserve
2199
=head2 MoveReserve
2123
2200
2124
  MoveReserve( $itemnumber, $borrowernumber, $cancelreserve )
2201
  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 / +3 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 238-244 sub shelfpage { Link Here
238
            my ($shelfnumber2,$shelfname,$owner,$category,$sorton) = GetShelf($shelfnumber);
239
            my ($shelfnumber2,$shelfname,$owner,$category,$sorton) = GetShelf($shelfnumber);
239
240
240
            $template->param(
241
            $template->param(
241
                'AllowOnShelfHolds'     => C4::Context->preference('AllowOnShelfHolds'),
242
                'DisplayMultiPlaceHold' => C4::Context->preference('DisplayMultiPlaceHold'),
242
                'DisplayMultiPlaceHold' => C4::Context->preference('DisplayMultiPlaceHold'),
243
            );
243
            );
244
            if (C4::Context->preference('TagsEnabled')) {
244
            if (C4::Context->preference('TagsEnabled')) {
Lines 259-264 sub shelfpage { Link Here
259
                    direction => $direction,
259
                    direction => $direction,
260
                );
260
                );
261
                ( $items, $totitems ) = GetShelfContents( $shelfnumber, $shelflimit, $shelfoffset, $sortfield, $direction );
261
                ( $items, $totitems ) = GetShelfContents( $shelfnumber, $shelflimit, $shelfoffset, $sortfield, $direction );
262
                my $borrower = GetMember( 'borrowernumber' => $loggedinuser );
262
                for my $this_item (@$items) {
263
                for my $this_item (@$items) {
263
                    my $biblionumber = $this_item->{'biblionumber'};
264
                    my $biblionumber = $this_item->{'biblionumber'};
264
                    my $record = GetMarcBiblio($biblionumber);
265
                    my $record = GetMarcBiblio($biblionumber);
Lines 296-301 sub shelfpage { Link Here
296
                            });
297
                            });
297
                    }
298
                    }
298
299
300
                    $this_item->{'allow_onshelf_holds'} = C4::Reserves::OnShelfHoldsAllowed($this_item, $borrower);
299
                }
301
                }
300
                if($type eq 'intranet'){
302
                if($type eq 'intranet'){
301
                    # Build drop-down list for 'Add To:' menu...
303
                    # 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 9666-9671 if ( CheckVersion($DBversion) ) { Link Here
9666
    $dbh->do(q|SET foreign_key_checks = 1|);;
9666
    $dbh->do(q|SET foreign_key_checks = 1|);;
9667
9667
9668
    print "Upgrade to $DBversion done (Bug 11944 - Convert DB tables to utf8_unicode_ci)\n";
9668
    print "Upgrade to $DBversion done (Bug 11944 - Convert DB tables to utf8_unicode_ci)\n";
9669
    SetVersion($DBversion);
9670
}
9671
9672
$DBversion = '3.19.00.XXX';
9673
if (C4::Context->preference("Version") < TransformToNum($DBversion)) {
9674
    # First create the column
9675
    $dbh->do("ALTER TABLE issuingrules ADD onshelfholds tinyint(1) default 0 NOT NULL");
9676
    # Now update the column
9677
    if (C4::Context->preference("AllowOnShelfHolds")){
9678
        # Pref is on, set allow for all rules
9679
        $dbh->do("UPDATE issuingrules SET onshelfholds=1");
9680
    } else {
9681
        # If the preference is not set, leave off
9682
        $dbh->do("UPDATE issuingrules SET onshelfholds=0");
9683
    }
9684
    # Remove from the systempreferences table
9685
    $dbh->do("DELETE FROM systempreferences WHERE variable = 'AllowOnShelfHolds'");
9686
9687
    # First create the column
9688
    $dbh->do("ALTER TABLE issuingrules ADD opacitemholds char(1) DEFAULT 'N' NOT NULL");
9689
    # Now update the column
9690
    my $opacitemholds = C4::Context->preference("OPACItemHolds") || '';
9691
    if (lc ($opacitemholds) eq 'force') {
9692
        $opacitemholds = 'F';
9693
    }
9694
    else {
9695
        $opacitemholds = $opacitemholds ? 'Y' : 'N';
9696
    }
9697
    # Set allow for all rules
9698
    $dbh->do("UPDATE issuingrules SET opacitemholds='$opacitemholds'");
9699
9700
    # Remove from the systempreferences table
9701
    $dbh->do("DELETE FROM systempreferences WHERE variable = 'OPACItemHolds'");
9702
9703
    print "Upgrade to $DBversion done (Bug 5786 - Move AllowOnShelfHolds to circulation matrix; Move OPACItemHolds system preference to circulation matrix)\n";
9669
    SetVersion ($DBversion);
9704
    SetVersion ($DBversion);
9670
}
9705
}
9671
9706
(-)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'%]Force[% ELSIF rule.opacitemholds == 'Y'%]Yes[% ELSE %]No[% 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 (-29 / +19 lines)
Lines 243-268 Link Here
243
                                                            </li>
243
                                                            </li>
244
                                                        [% END # / IF OpacHoldNotes %]
244
                                                        [% END # / IF OpacHoldNotes %]
245
245
246
                                                        [% IF OPACItemHolds == '1' or OPACItemHolds == 'force' %]
246
                                                        <li>
247
                                                        </li>
248
                                                        [% IF bibitemloo.itemholdable %]
247
                                                            <!-- ITEM HOLDS -->
249
                                                            <!-- ITEM HOLDS -->
248
                                                            <li class="lradio place_on_type" style="display:none;">
250
                                                            <li class="lradio place_on_type" style="display:none;">
249
                                                                <label class="radio inline" for="reqany_[% bibitemloo.biblionumber %]">Next available item</label>
251
                                                                <label class="radio inline" for="reqany_[% bibitemloo.biblionumber %]">Next available item</label>
250
                                                                [% IF OPACItemHolds == "1" %]
252
                                                                [% IF NOT bibitemloo.force_hold %]
251
                                                                    <input type="radio" name="reqtype_[% bibitemloo.biblionumber %]"
253
                                                                    <input type="radio" name="reqtype_[% bibitemloo.biblionumber %]"
252
                                                                            id="reqany_[% bibitemloo.biblionumber %]"
254
                                                                            id="reqany_[% bibitemloo.biblionumber %]"
253
                                                                            class="selectany"
255
                                                                            class="selectany"
254
                                                                            value="Any"
256
                                                                            value="Any"
255
                                                                            checked="checked"
257
                                                                            checked="checked"
256
                                                                    />
258
                                                                    />
257
                                                                [% ELSE %]
258
                                                                    <input type="radio" name="reqtype_[% bibitemloo.biblionumber %]"
259
                                                                            id="reqany_[% bibitemloo.biblionumber %]"
260
                                                                            class="selectany"
261
                                                                            value="Any"
262
                                                                     />
263
                                                                [% END %]
259
                                                                [% END %]
264
                                                                <label class="radio inline" for="reqspecific_[% bibitemloo.biblionumber %]">A specific item</label>
260
                                                                <label class="radio inline" for="reqspecific_[% bibitemloo.biblionumber %]">A specific item</label>
265
                                                                [% IF OPACItemHolds == "force" %]
261
                                                                [% IF bibitemloo.force_hold %]
266
                                                                    <input type="radio" name="reqtype_[% bibitemloo.biblionumber %]"
262
                                                                    <input type="radio" name="reqtype_[% bibitemloo.biblionumber %]"
267
                                                                           id="reqspecific_[% bibitemloo.biblionumber %]"
263
                                                                           id="reqspecific_[% bibitemloo.biblionumber %]"
268
                                                                           class="selectspecific"
264
                                                                           class="selectspecific"
Lines 277-286 Link Here
277
                                                                    />
273
                                                                    />
278
                                                                [% END %]
274
                                                                [% END %]
279
                                                            </li>
275
                                                            </li>
280
                                                        [% END # / IF OPACItemHolds %]
276
                                                        [% END # / IF bibitemloo.itemholdable %]
281
                                                    </ul>
277
                                                    </ul>
282
278
283
                                                    [% IF OPACItemHolds == '1' || OPACItemHolds == 'force' %]
279
                                                    [% IF bibitemloo.itemholdable %]
284
                                                        <table class="copiesrow table table-bordered table-striped" id="copiesrow_[% bibitemloo.biblionumber %]">
280
                                                        <table class="copiesrow table table-bordered table-striped" id="copiesrow_[% bibitemloo.biblionumber %]">
285
                                                            <caption>Select a specific item:</caption>
281
                                                            <caption>Select a specific item:</caption>
286
                                                            <tr>
282
                                                            <tr>
Lines 380-386 Link Here
380
                                                                </tr>
376
                                                                </tr>
381
                                                            [% END # / FOREACH itemLoo IN bibitemloo.itemLoop%]
377
                                                            [% END # / FOREACH itemLoo IN bibitemloo.itemLoop%]
382
                                                        </table> <!-- / #copiesrow_[% bibitemloo.biblionumber %] -->
378
                                                        </table> <!-- / #copiesrow_[% bibitemloo.biblionumber %] -->
383
                                                    [% END # / IF ( OPACItemHolds )%]
379
                                                    [% END # / IF ( bibitemloo.itemholdable )%]
384
                                                </div> <!-- / #hold-options-[% bibitemloo.biblionumber %] -->
380
                                                </div> <!-- / #hold-options-[% bibitemloo.biblionumber %] -->
385
                                            </fieldset>
381
                                            </fieldset>
386
                                        [% END # / IF ( bibitemloo.holdable ) %]
382
                                        [% END # / IF ( bibitemloo.holdable ) %]
Lines 460-471 Link Here
460
        // Hides all 'specific copy' table rows on load.
456
        // Hides all 'specific copy' table rows on load.
461
        $(".copiesrow").hide();
457
        $(".copiesrow").hide();
462
458
463
        [% IF OPACItemHolds == 'force' %]
459
        [% FOREACH bibitemloo IN bibitemloop %]
464
          [% FOREACH bibitemloo IN bibitemloop %]
460
          [% IF bibitemloo.force_hold %]
465
            [% IF bibitemloo.holdable %]
461
            $("#toggle-hold-options-[% bibitemloo.biblionumber %]").click();
466
              $("#toggle-hold-options-[% bibitemloo.biblionumber %]").click();
462
            $("#copiesrow_[% bibitemloo.biblionumber %]").show();
467
              $("#copiesrow_[% bibitemloo.biblionumber %]").show();
468
            [% END %]
469
          [% END %]
463
          [% END %]
470
        [% END %]
464
        [% END %]
471
465
Lines 523-542 Link Here
523
517
524
        $("#place_on_hdr").show();
518
        $("#place_on_hdr").show();
525
519
526
        [% IF OPACItemHolds == '1' %]
520
        $(".place_on_type").show();
527
            $(".place_on_type").show();
521
        // onload, selectany is checked
528
            // onload, selectany is checked
522
        $(".selectany").attr("checked", "checked");
529
            $(".selectany").attr("checked", "checked");
530
        [% END %]
531
523
532
        // If the user is *allowed* to choose a specific item
524
        // If the user is *allowed* to choose a specific item
533
        // The first one is preselected
525
        // The first one is preselected
534
        [% IF OPACItemHolds =="1" %]
526
        $("table.copiesrow").each(function(){
535
            $("table.copiesrow").each(function(){
527
            var id = suffixOf($(this).attr("id"), "_");
536
                var id = suffixOf($(this).attr("id"), "_");
528
            select_first_available(id);
537
                select_first_available(id);
529
        });
538
            });
539
        [% END %]
540
530
541
        // On confirmsjs change
531
        // On confirmsjs change
542
        $(".confirmjs").change(function(){
532
        $(".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