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

(-)a/C4/Reserves.pm (-33 / +104 lines)
Lines 41-46 use Koha::Database; Link Here
41
use Koha::Hold;
41
use Koha::Hold;
42
use Koha::Holds;
42
use Koha::Holds;
43
use Koha::Libraries;
43
use Koha::Libraries;
44
use Koha::Patrons;
44
45
45
use List::MoreUtils qw( firstidx any );
46
use List::MoreUtils qw( firstidx any );
46
use Carp;
47
use Carp;
Lines 139-144 BEGIN { Link Here
139
        &GetReservesControlBranch
140
        &GetReservesControlBranch
140
141
141
        IsItemOnHoldAndFound
142
        IsItemOnHoldAndFound
143
144
        GetMaxPatronHoldsForRecord
142
    );
145
    );
143
    @EXPORT_OK = qw( MergeHolds );
146
    @EXPORT_OK = qw( MergeHolds );
144
}
147
}
Lines 166-177 sub AddReserve { Link Here
166
        $title,      $checkitem, $found
169
        $title,      $checkitem, $found
167
    ) = @_;
170
    ) = @_;
168
171
169
    if ( Koha::Holds->search( { borrowernumber => $borrowernumber, biblionumber => $biblionumber } )->count() > 0 ) {
172
    my $dbh = C4::Context->dbh;
170
        carp("AddReserve: borrower $borrowernumber already has a hold for biblionumber $biblionumber");
171
        return;
172
    }
173
174
    my $dbh     = C4::Context->dbh;
175
173
176
    $resdate = output_pref( { str => dt_from_string( $resdate ), dateonly => 1, dateformat => 'iso' })
174
    $resdate = output_pref( { str => dt_from_string( $resdate ), dateonly => 1, dateformat => 'iso' })
177
        or output_pref({ dt => dt_from_string, dateonly => 1, dateformat => 'iso' });
175
        or output_pref({ dt => dt_from_string, dateonly => 1, dateformat => 'iso' });
Lines 451-457 sub CanItemBeReserved { Link Here
451
449
452
    my $dbh = C4::Context->dbh;
450
    my $dbh = C4::Context->dbh;
453
    my $ruleitemtype;    # itemtype of the matching issuing rule
451
    my $ruleitemtype;    # itemtype of the matching issuing rule
454
    my $allowedreserves = 0;
452
    my $allowedreserves  = 0; # Total number of holds allowed across all records
453
    my $holds_per_record = 1; # Total number of holds allowed for this one given record
455
454
456
    # we retrieve borrowers and items informations #
455
    # we retrieve borrowers and items informations #
457
    # item->{itype} will come for biblioitems if necessery
456
    # item->{itype} will come for biblioitems if necessery
Lines 464-489 sub CanItemBeReserved { Link Here
464
      if ( $item->{damaged}
463
      if ( $item->{damaged}
465
        && !C4::Context->preference('AllowHoldsOnDamagedItems') );
464
        && !C4::Context->preference('AllowHoldsOnDamagedItems') );
466
465
467
    #Check for the age restriction
466
    # Check for the age restriction
468
    my ( $ageRestriction, $daysToAgeRestriction ) =
467
    my ( $ageRestriction, $daysToAgeRestriction ) =
469
      C4::Circulation::GetAgeRestriction( $biblioData->{agerestriction}, $borrower );
468
      C4::Circulation::GetAgeRestriction( $biblioData->{agerestriction}, $borrower );
470
    return 'ageRestricted' if $daysToAgeRestriction && $daysToAgeRestriction > 0;
469
    return 'ageRestricted' if $daysToAgeRestriction && $daysToAgeRestriction > 0;
471
470
472
    my $controlbranch = C4::Context->preference('ReservesControlBranch');
471
    # Check that the patron doesn't have an item level hold on this item already
472
    return 'itemAlreadyOnHold'
473
      if Koha::Holds->search( { borrowernumber => $borrowernumber, itemnumber => $itemnumber } )->count();
473
474
474
    # we retrieve user rights on this itemtype and branchcode
475
    my $controlbranch = C4::Context->preference('ReservesControlBranch');
475
    my $sth = $dbh->prepare(
476
        q{
477
         SELECT categorycode, itemtype, branchcode, reservesallowed
478
           FROM issuingrules
479
          WHERE (categorycode in (?,'*') )
480
            AND (itemtype IN (?,'*'))
481
            AND (branchcode IN (?,'*'))
482
       ORDER BY categorycode DESC,
483
                itemtype     DESC,
484
                branchcode   DESC
485
        }
486
    );
487
476
488
    my $querycount = q{
477
    my $querycount = q{
489
        SELECT count(*) AS count
478
        SELECT count(*) AS count
Lines 507-521 sub CanItemBeReserved { Link Here
507
    }
496
    }
508
497
509
    # we retrieve rights
498
    # we retrieve rights
510
    $sth->execute( $borrower->{'categorycode'}, $item->{'itype'}, $branchcode );
499
    if ( my $rights = GetHoldRule( $borrower->{'categorycode'}, $item->{'itype'}, $branchcode ) ) {
511
    if ( my $rights = $sth->fetchrow_hashref() ) {
500
        $ruleitemtype     = $rights->{itemtype};
512
        $ruleitemtype    = $rights->{itemtype};
501
        $allowedreserves  = $rights->{reservesallowed};
513
        $allowedreserves = $rights->{reservesallowed};
502
        $holds_per_record = $rights->{holds_per_record};
514
    }
503
    }
515
    else {
504
    else {
516
        $ruleitemtype = '*';
505
        $ruleitemtype = '*';
517
    }
506
    }
518
507
508
    my $item = Koha::Items->find( $itemnumber );
509
    my $holds = Koha::Holds->search(
510
        {
511
            borrowernumber => $borrowernumber,
512
            biblionumber   => $item->biblionumber,
513
            found          => undef, # Found holds don't count against a patron's holds limit
514
        }
515
    );
516
    if ( $holds->count() >= $holds_per_record ) {
517
        return "tooManyHoldsForThisRecord";
518
    }
519
519
    # we retrieve count
520
    # we retrieve count
520
521
521
    $querycount .= "AND $branchfield = ?";
522
    $querycount .= "AND $branchfield = ?";
Lines 745-752 sub GetReservesToBranch { Link Here
745
    my $dbh = C4::Context->dbh;
746
    my $dbh = C4::Context->dbh;
746
    my $sth = $dbh->prepare(
747
    my $sth = $dbh->prepare(
747
        "SELECT reserve_id,borrowernumber,reservedate,itemnumber,timestamp
748
        "SELECT reserve_id,borrowernumber,reservedate,itemnumber,timestamp
748
         FROM reserves 
749
         FROM reserves
749
         WHERE priority='0' 
750
         WHERE priority='0'
750
           AND branchcode=?"
751
           AND branchcode=?"
751
    );
752
    );
752
    $sth->execute( $frombranch );
753
    $sth->execute( $frombranch );
Lines 771-777 sub GetReservesForBranch { Link Here
771
772
772
    my $query = "
773
    my $query = "
773
        SELECT reserve_id,borrowernumber,reservedate,itemnumber,waitingdate
774
        SELECT reserve_id,borrowernumber,reservedate,itemnumber,waitingdate
774
        FROM   reserves 
775
        FROM   reserves
775
        WHERE   priority='0'
776
        WHERE   priority='0'
776
        AND found='W'
777
        AND found='W'
777
    ";
778
    ";
Lines 1393-1399 sub ModReserveMinusPriority { Link Here
1393
    my $dbh   = C4::Context->dbh;
1394
    my $dbh   = C4::Context->dbh;
1394
    my $query = "
1395
    my $query = "
1395
        UPDATE reserves
1396
        UPDATE reserves
1396
        SET    priority = 0 , itemnumber = ? 
1397
        SET    priority = 0 , itemnumber = ?
1397
        WHERE  reserve_id = ?
1398
        WHERE  reserve_id = ?
1398
    ";
1399
    ";
1399
    my $sth_upd = $dbh->prepare($query);
1400
    my $sth_upd = $dbh->prepare($query);
Lines 1608-1614 sub ToggleLowestPriority { Link Here
1608
1609
1609
    my $sth = $dbh->prepare( "UPDATE reserves SET lowestPriority = NOT lowestPriority WHERE reserve_id = ?");
1610
    my $sth = $dbh->prepare( "UPDATE reserves SET lowestPriority = NOT lowestPriority WHERE reserve_id = ?");
1610
    $sth->execute( $reserve_id );
1611
    $sth->execute( $reserve_id );
1611
    
1612
1612
    _FixPriority({ reserve_id => $reserve_id, rank => '999999' });
1613
    _FixPriority({ reserve_id => $reserve_id, rank => '999999' });
1613
}
1614
}
1614
1615
Lines 1792-1798 sub _FixPriority { Link Here
1792
            $priority[$j]->{'reserve_id'}
1793
            $priority[$j]->{'reserve_id'}
1793
        );
1794
        );
1794
    }
1795
    }
1795
    
1796
1796
    $sth = $dbh->prepare( "SELECT reserve_id FROM reserves WHERE lowestPriority = 1 ORDER BY priority" );
1797
    $sth = $dbh->prepare( "SELECT reserve_id FROM reserves WHERE lowestPriority = 1 ORDER BY priority" );
1797
    $sth->execute();
1798
    $sth->execute();
1798
1799
Lines 2027-2033 sub _koha_notify_reserve { Link Here
2027
    if (! $notification_sent) {
2028
    if (! $notification_sent) {
2028
        &$send_notification('print', 'HOLD');
2029
        &$send_notification('print', 'HOLD');
2029
    }
2030
    }
2030
    
2031
2031
}
2032
}
2032
2033
2033
=head2 _ShiftPriorityByDateAndPriority
2034
=head2 _ShiftPriorityByDateAndPriority
Lines 2456-2461 sub IsItemOnHoldAndFound { Link Here
2456
    return $found;
2457
    return $found;
2457
}
2458
}
2458
2459
2460
=head2 GetMaxPatronHoldsForRecord
2461
2462
my $holds_per_record = ReservesControlBranch( $borrowernumber, $biblionumber );
2463
2464
For multiple holds on a given record for a given patron, the max
2465
number of record level holds that a patron can be placed is the highest
2466
value of the holds_per_record rule for each item if the record for that
2467
patron. This subroutine finds and returns the highest holds_per_record
2468
rule value for a given patron id and record id.
2469
2470
=cut
2471
2472
sub GetMaxPatronHoldsForRecord {
2473
    my ( $borrowernumber, $biblionumber ) = @_;
2474
2475
    my $patron = Koha::Patrons->find($borrowernumber);
2476
    my @items = Koha::Items->search( { biblionumber => $biblionumber } );
2477
2478
    my $controlbranch = C4::Context->preference('ReservesControlBranch');
2479
2480
    my $categorycode = $patron->categorycode;
2481
    my $branchcode;
2482
    $branchcode = $patron->branchcode if ( $controlbranch eq "PatronLibrary" );
2483
2484
    my $max = 0;
2485
    foreach my $item (@items) {
2486
        my $itemtype = $item->effective_itemtype();
2487
2488
        $branchcode = $item->homebranch if ( $controlbranch eq "ItemHomeLibrary" );
2489
2490
        my $rule = GetHoldRule( $categorycode, $itemtype, $branchcode );
2491
        my $holds_per_record = $rule ? $rule->{holds_per_record} : 0;
2492
        $max = $holds_per_record if $holds_per_record > $max;
2493
    }
2494
2495
    return $max;
2496
}
2497
2498
=head2 GetHoldRule
2499
2500
my $rule = GetHoldRule( $categorycode, $itemtype, $branchcode );
2501
2502
Returns the matching hold related issuingrule fields for a given
2503
patron category, itemtype, and library.
2504
2505
=cut
2506
2507
sub GetHoldRule {
2508
    my ( $categorycode, $itemtype, $branchcode ) = @_;
2509
2510
    my $dbh = C4::Context->dbh;
2511
2512
    my $sth = $dbh->prepare(
2513
        q{
2514
         SELECT categorycode, itemtype, branchcode, reservesallowed, holds_per_record
2515
           FROM issuingrules
2516
          WHERE (categorycode in (?,'*') )
2517
            AND (itemtype IN (?,'*'))
2518
            AND (branchcode IN (?,'*'))
2519
       ORDER BY categorycode DESC,
2520
                itemtype     DESC,
2521
                branchcode   DESC
2522
        }
2523
    );
2524
2525
    $sth->execute( $categorycode, $itemtype, $branchcode );
2526
2527
    return $sth->fetchrow_hashref();
2528
}
2529
2459
=head1 AUTHOR
2530
=head1 AUTHOR
2460
2531
2461
Koha Development Team <http://koha-community.org/>
2532
Koha Development Team <http://koha-community.org/>
(-)a/Koha/Holds.pm (+32 lines)
Lines 49-54 sub waiting { Link Here
49
    return $self->search( { found => 'W' } );
49
    return $self->search( { found => 'W' } );
50
}
50
}
51
51
52
=head3 forced_hold_level
53
54
If a patron has multiple holds for a single record,
55
those holds must be either all record level holds,
56
or they must all be item level holds.
57
58
This method should be used with Hold sets where all
59
Hold objects share the same patron and record.
60
61
This method will return 'item' if the patron has
62
at least one item level hold. It will return 'record'
63
if the patron has holds but none are item level,
64
Finally, if the patron has no holds, it will return
65
undef which indicateds the patron may select either
66
record or item level holds, barring any other rules
67
that would prevent one or the other.
68
=cut
69
70
sub forced_hold_level {
71
    my ($self) = @_;
72
73
    my $force_hold_level;
74
75
    if ( $self->count() ) {
76
        my $has_item_level_holds;
77
        map { $has_item_level_holds ||= $_->itemnumber } $self->as_list();
78
        $force_hold_level = $has_item_level_holds ? 'item' : 'record';
79
    }
80
81
    return $force_hold_level;
82
}
83
52
=head3 type
84
=head3 type
53
85
54
=cut
86
=cut
(-)a/koha-tmpl/intranet-tmpl/prog/en/modules/reserve/request.tt (-36 / +81 lines)
Lines 61-109 $(document).ready(function() { Link Here
61
});
61
});
62
62
63
function check() {
63
function check() {
64
	var msg = "";
64
    var msg = "";
65
	var count_reserv = 0;
65
    var count_reserv = 0;
66
	var alreadyreserved = 0;
67
66
68
    // check if we have checkitem form
67
    // check if we have checkitem form
69
    if (document.form.checkitem){
68
    if (document.form.checkitem){
70
        for (i=0;i<document.form.checkitem.length;i++){
69
        for (i=0;i<document.form.checkitem.length;i++){
71
            if (document.form.checkitem[i].checked == true) {
70
            if (document.form.checkitem[i].checked == true) {
72
				count_reserv++ ;
71
                count_reserv++ ;
73
			}
72
            }
74
        }
73
        }
75
        // for only one item, check the checkitem without consider the loop checkitem
74
        // for only one item, check the checkitem without consider the loop checkitem
76
        if (i==0){
75
        if (i==0){
77
		    if (document.form.checkitem.checked == true) {
76
            if (document.form.checkitem.checked == true) {
78
			    count_reserv++;
77
                count_reserv++;
79
		    }
78
            }
80
	    }
79
        }
81
    }
82
83
    if (document.form.request.checked == true){
84
		count_reserv++ ;
85
    }
80
    }
86
81
87
    if (document.form.alreadyreserved && document.form.alreadyreserved.value == "1"){
82
    if (document.form.requestany.checked == true){
88
		 alreadyreserved++ ;
83
        count_reserv++ ;
89
    }
84
    }
90
85
91
    if (count_reserv == "0"){
86
    if (count_reserv == "0"){
92
		msg += (_("- Please select an item to place a hold") + "\n");
87
        msg += (_("- Please select an item to place a hold") + "\n");
93
    }
94
    if (count_reserv >= "2"){
95
		msg += (_("- You may only place a hold on one item at a time") + "\n");
96
    }
88
    }
97
89
98
    if (alreadyreserved > "0"){
90
    if (msg == "") {
99
		msg += (_("- This patron had already placed a hold on this item") + "\n" + _("Please cancel the previous hold first") + "\n");
91
        $('#hold-request-form').preventDoubleFormSubmit();
92
        return(true);
93
    } else {
94
        alert(msg);
95
        return(false);
100
    }
96
    }
101
102
	if (msg == "") return(true);
103
	else	{
104
		alert(msg);
105
		return(false);
106
	}
107
}
97
}
108
98
109
function checkMultiHold() {
99
function checkMultiHold() {
Lines 129-134 function checkMultiHold() { Link Here
129
    $("#multi_hold_bibs").val(biblionumbers);
119
    $("#multi_hold_bibs").val(biblionumbers);
130
    $("#bad_bibs").val(badBibs);
120
    $("#bad_bibs").val(badBibs);
131
121
122
    $('#hold-request-form').preventDoubleFormSubmit();
123
132
    return true;
124
    return true;
133
}
125
}
134
126
Lines 176-182 function checkMultiHold() { Link Here
176
        $("#" + fieldID).val("");
168
        $("#" + fieldID).val("");
177
    });
169
    });
178
170
179
    $('#hold-request-form').preventDoubleFormSubmit();
180
171
181
[% UNLESS ( borrowernumber || borrowers || noitems ) %]
172
[% UNLESS ( borrowernumber || borrowers || noitems ) %]
182
    [% IF ( CircAutocompl ) %]
173
    [% IF ( CircAutocompl ) %]
Lines 308-314 function checkMultiHold() { Link Here
308
        [% IF ( exceeded_maxreserves ) %]
299
        [% IF ( exceeded_maxreserves ) %]
309
          <li><strong>Too many holds: </strong> <a href="/cgi-bin/koha/members/moremember.pl?borrowernumber=[% borrowernumber %]">[% borrowerfirstname %] [% borrowersurname %] </a> can only place a maximum of [% maxreserves %] total holds.</li>
300
          <li><strong>Too many holds: </strong> <a href="/cgi-bin/koha/members/moremember.pl?borrowernumber=[% borrowernumber %]">[% borrowerfirstname %] [% borrowersurname %] </a> can only place a maximum of [% maxreserves %] total holds.</li>
310
        [% ELSIF ( alreadypossession ) %]
301
        [% ELSIF ( alreadypossession ) %]
311
          <li> <a href="/cgi-bin/koha/members/moremember.pl?borrowernumber=[% borrowernumber %]">[% borrowerfirstname %] [% borrowersurname %]</a> <strong>is already in possession</strong> of one item</li>
302
          <li> <a href="/cgi-bin/koha/members/moremember.pl?borrowernumber=[% borrowernumber %]">[% borrowerfirstname %] [% borrowersurname %]</a> <strong>is already in possession</strong> of one item</lie
312
        [% ELSIF ( alreadyreserved ) %]
303
        [% ELSIF ( alreadyreserved ) %]
313
          <li><a href="/cgi-bin/koha/members/moremember.pl?borrowernumber=[% borrowernumber %]">[% borrowerfirstname %] [% borrowersurname %]</a> <strong>already has a hold</strong> on this item </li>
304
          <li><a href="/cgi-bin/koha/members/moremember.pl?borrowernumber=[% borrowernumber %]">[% borrowerfirstname %] [% borrowersurname %]</a> <strong>already has a hold</strong> on this item </li>
314
        [% ELSIF ( ageRestricted ) %]
305
        [% ELSIF ( ageRestricted ) %]
Lines 425-435 function checkMultiHold() { Link Here
425
	</li>
416
	</li>
426
417
427
        [% UNLESS ( multi_hold ) %]
418
        [% UNLESS ( multi_hold ) %]
428
          <li> <label for="requestany">Place a hold on the next available item </label>
419
          <li> <label for="requestany">Hold next available item </label>
429
               <input type="checkbox" id="requestany" name="request" checked="checked" value="Any" />
420
               [% IF force_hold_level == 'item' %]
421
                   <input type="checkbox" id="requestany" name="request" disabled="true" />
422
               [% ELSIF force_hold_level == 'record' %]
423
                   <input type="checkbox" id="requestany" checked="checked" value="Any" disabled="true"/>
424
                   <input type="hidden" name="request" value="Any"/>
425
               [% ELSE %]
426
                   <input type="checkbox" id="requestany" name="request" checked="checked" value="Any" />
427
                [% END %]
430
               <input type="hidden" name="biblioitem" value="[% biblioitemnumber %]" />
428
               <input type="hidden" name="biblioitem" value="[% biblioitemnumber %]" />
431
               <input type="hidden" name="alreadyreserved" value="[% alreadyreserved %]" />
429
               <input type="hidden" name="alreadyreserved" value="[% alreadyreserved %]" />
432
          </li>
430
          </li>
431
432
          [% IF max_holds_for_record > 1 %]
433
              [% SET count = 1 %]
434
              <li>
435
                   <label for="holds_to_place_count">Holds to place (count)</label>
436
                   <select name="holds_to_place_count" id="holds_to_place_count">
437
                   [% WHILE count <= max_holds_for_record %]
438
                        <option value="[% count %]">[% count %]</option>
439
                        [% SET count = count + 1 %]
440
                   [% END %]
441
442
                   </select>
443
              </li>
444
            [% ELSE %]
445
                <input type="hidden" name="holds_to_place_count" value="1";
446
            [% END %]
433
        [% END %]
447
        [% END %]
434
448
435
</ol>
449
</ol>
Lines 454-460 function checkMultiHold() { Link Here
454
            [% IF ( bibitemloo.publicationyear ) %]<li><span class="label">Publication year:</span> [% bibitemloo.publicationyear %]</li>[% END %]
468
            [% IF ( bibitemloo.publicationyear ) %]<li><span class="label">Publication year:</span> [% bibitemloo.publicationyear %]</li>[% END %]
455
          </ol>
469
          </ol>
456
470
457
        <h2 style="padding: 0 1em;">Place a hold on a specific item</h2>
471
        <h2 style="padding: 0 1em;">
472
            Place a hold on a specific item
473
            [% IF bibitemloo.force_hold_level == 'item' %]
474
                <span class="error"><i>(Required)</i></span>
475
            [% END %]
476
        </h2>
458
        <table id="requestspecific">
477
        <table id="requestspecific">
459
            <thead>
478
            <thead>
460
                <tr>
479
                <tr>
Lines 474-491 function checkMultiHold() { Link Here
474
                </tr>
493
                </tr>
475
            </thead>
494
            </thead>
476
            <tbody>
495
            <tbody>
496
            [% SET selected = 0 %]
477
            [% FOREACH itemloo IN bibitemloo.itemloop %]
497
            [% FOREACH itemloo IN bibitemloo.itemloop %]
478
            [% UNLESS ( itemloo.hide ) %]
498
            [% UNLESS ( itemloo.hide ) %]
479
                <tr class="[% itemloo.backgroundcolor %]">
499
                <tr class="[% itemloo.backgroundcolor %]">
480
                    <td>
500
                    <td>
481
                [% IF ( itemloo.available ) %]
501
                [% IF itemloo.force_hold_level == 'record' # Patron has placed a record level hold previously for this record %]
502
                    <span class="error">
503
                        <i class="fa fa-times fa-lg" alt="Cannot be put on hold"></i>
504
                        Hold must be record level
505
                    </span>
506
                [% ELSIF ( itemloo.available ) %]
482
                    <input type="radio" name="checkitem" value="[% itemloo.itemnumber %]" />
507
                    <input type="radio" name="checkitem" value="[% itemloo.itemnumber %]" />
483
                [% ELSIF ( itemloo.override ) %]
508
                [% ELSIF ( itemloo.override ) %]
484
                    <input type="radio" name="checkitem" class="needsoverride" value="[% itemloo.itemnumber %]" />
509
                    <input type="radio" name="checkitem" class="needsoverride" value="[% itemloo.itemnumber %]" />
485
                    <img src="[% interface %]/[% theme %]/img/famfamfam/silk/error.png" alt="Requires override of hold policy" />
510
                    <i class="fa fa-exclamation-triangle fa-lg" style="color:gold" alt="Requires override of hold policy"/></i>
486
                [% ELSE %]
511
                [% ELSE %]
487
                    <input disabled="disabled" type="radio" name="checkitem" value="[% itemloo.itemnumber %]" />
512
                    <span class="error">
488
                    <img src="[% interface %]/[% theme %]/img/famfamfam/silk/cross.png" alt="Cannot be put on hold" />
513
                        <i class="fa fa-times fa-lg" alt="Cannot be put on hold"></i>
514
                        [% IF itemloo.not_holdable %]
515
                            [% IF itemloo.not_holdable == 'damaged' %]
516
                                Item damaged
517
                            [% ELSIF itemloo.not_holdable == 'ageRestricted' %]
518
                                Age restricted
519
                            [% ELSIF itemloo.not_holdable == 'tooManyHoldsForThisRecord' %]
520
                                Exceeded max holds per record
521
                            [% ELSIF itemloo.not_holdable == 'tooManyReserves' %]
522
                                Too many holds
523
                            [% ELSIF itemloo.not_holdable == 'notReservable' %]
524
                                Not holdable
525
                            [% ELSIF itemloo.not_holdable == 'cannotReserveFromOtherBranches' %]
526
                                Patron is from different library
527
                            [% ELSIF itemloo.not_holdable == 'itemAlreadyOnHold' %]
528
                                Patron already has hold for this item
529
                            [% ELSE %]
530
                                [% itemloo.not_holdable %]
531
                            [% END %]
532
                        [% END %]
533
                    </span>
489
                [% END %]
534
                [% END %]
490
                    </td>
535
                    </td>
491
                [% IF ( item_level_itypes ) %]
536
                [% IF ( item_level_itypes ) %]
(-)a/opac/opac-reserve.pl (-20 / +24 lines)
Lines 357-373 unless ( $noreserves ) { Link Here
357
    }
357
    }
358
}
358
}
359
359
360
foreach my $res (@reserves) {
361
    foreach my $biblionumber (@biblionumbers) {
362
        if ( $res->{'biblionumber'} == $biblionumber && $res->{'borrowernumber'} == $borrowernumber) {
363
#            $template->param( message => 1 );
364
#            $noreserves = 1;
365
#            $template->param( already_reserved => 1 );
366
            $biblioDataHash{$biblionumber}->{already_reserved} = 1;
367
        }
368
    }
369
}
370
371
unless ($noreserves) {
360
unless ($noreserves) {
372
    $template->param( select_item_types => 1 );
361
    $template->param( select_item_types => 1 );
373
}
362
}
Lines 465-473 foreach my $biblioNum (@biblionumbers) { Link Here
465
454
466
        # the item could be reserved for this borrower vi a host record, flag this
455
        # the item could be reserved for this borrower vi a host record, flag this
467
        $reservedfor //= '';
456
        $reservedfor //= '';
468
        if ($reservedfor eq $borrowernumber){
469
            $itemLoopIter->{already_reserved} = 1;
470
        }
471
457
472
        if ( defined $reservedate ) {
458
        if ( defined $reservedate ) {
473
            $itemLoopIter->{backgroundcolor} = 'reserved';
459
            $itemLoopIter->{backgroundcolor} = 'reserved';
Lines 512-523 foreach my $biblioNum (@biblionumbers) { Link Here
512
            $itemLoopIter->{nocancel} = 1;
498
            $itemLoopIter->{nocancel} = 1;
513
        }
499
        }
514
500
515
	# if the items belongs to a host record, show link to host record
501
        # if the items belongs to a host record, show link to host record
516
	if ($itemInfo->{biblionumber} ne $biblioNum){
502
        if ( $itemInfo->{biblionumber} ne $biblioNum ) {
517
		$biblioLoopIter{hostitemsflag} = 1;
503
            $biblioLoopIter{hostitemsflag}    = 1;
518
		$itemLoopIter->{hostbiblionumber} = $itemInfo->{biblionumber};
504
            $itemLoopIter->{hostbiblionumber} = $itemInfo->{biblionumber};
519
		$itemLoopIter->{hosttitle} = GetBiblioData($itemInfo->{biblionumber})->{title};
505
            $itemLoopIter->{hosttitle}        = GetBiblioData( $itemInfo->{biblionumber} )->{title};
520
	}
506
        }
521
507
522
        # If there is no loan, return and transfer, we show a checkbox.
508
        # If there is no loan, return and transfer, we show a checkbox.
523
        $itemLoopIter->{notforloan} = $itemLoopIter->{notforloan} || 0;
509
        $itemLoopIter->{notforloan} = $itemLoopIter->{notforloan} || 0;
Lines 566-571 foreach my $biblioNum (@biblionumbers) { Link Here
566
552
567
    $biblioLoopIter{holdable} &&= CanBookBeReserved($borrowernumber,$biblioNum) eq 'OK';
553
    $biblioLoopIter{holdable} &&= CanBookBeReserved($borrowernumber,$biblioNum) eq 'OK';
568
554
555
    # For multiple holds per record, if a patron has previously placed a hold,
556
    # the patron can only place more holds of the same type. That is, if the
557
    # patron placed a record level hold, all the holds the patron places must
558
    # be record level. If the patron placed an item level hold, all holds
559
    # the patron places must be item level
560
    my $forced_hold_level = Koha::Holds->search(
561
        {
562
            borrowernumber => $borrowernumber,
563
            biblionumber   => $biblioNum,
564
            found          => undef,
565
        }
566
    )->forced_hold_level();
567
    if ($forced_hold_level) {
568
        $biblioLoopIter{force_hold}   = 1 if $forced_hold_level eq 'item';
569
        $biblioLoopIter{itemholdable} = 0 if $forced_hold_level eq 'record';
570
    }
571
572
569
    push @$biblioLoop, \%biblioLoopIter;
573
    push @$biblioLoop, \%biblioLoopIter;
570
574
571
    $anyholdable = 1 if $biblioLoopIter{holdable};
575
    $anyholdable = 1 if $biblioLoopIter{holdable};
(-)a/reserve/placerequest.pl (-30 / +38 lines)
Lines 54-59 my $expirationdate = $input->param('expiration_date'); Link Here
54
my $multi_hold = $input->param('multi_hold');
54
my $multi_hold = $input->param('multi_hold');
55
my $biblionumbers = $multi_hold ? $input->param('biblionumbers') : ($biblionumber . '/');
55
my $biblionumbers = $multi_hold ? $input->param('biblionumbers') : ($biblionumber . '/');
56
my $bad_bibs = $input->param('bad_bibs');
56
my $bad_bibs = $input->param('bad_bibs');
57
my $holds_to_place_count = $input->param('holds_to_place_count') || 1;
57
58
58
my %bibinfos = ();
59
my %bibinfos = ();
59
my @biblionumbers = split '/', $biblionumbers;
60
my @biblionumbers = split '/', $biblionumbers;
Lines 77-114 if (defined $checkitem && $checkitem ne ''){ Link Here
77
    }
78
    }
78
}
79
}
79
80
80
if ($type eq 'str8' && $borrower){
81
if ( $type eq 'str8' && $borrower ) {
81
82
82
    foreach my $biblionumber (keys %bibinfos) {
83
    foreach my $biblionumber ( keys %bibinfos ) {
83
        my $count=@bibitems;
84
        my $count = @bibitems;
84
        @bibitems=sort @bibitems;
85
        @bibitems = sort @bibitems;
85
        my $i2=1;
86
        my $i2 = 1;
86
        my @realbi;
87
        my @realbi;
87
        $realbi[0]=$bibitems[0];
88
        $realbi[0] = $bibitems[0];
88
        for (my $i=1;$i<$count;$i++) {
89
        for ( my $i = 1 ; $i < $count ; $i++ ) {
89
            my $i3=$i2-1;
90
            my $i3 = $i2 - 1;
90
            if ($realbi[$i3] ne $bibitems[$i]) {
91
            if ( $realbi[$i3] ne $bibitems[$i] ) {
91
                $realbi[$i2]=$bibitems[$i];
92
                $realbi[$i2] = $bibitems[$i];
92
                $i2++;
93
                $i2++;
93
            }
94
            }
94
        }
95
        }
95
96
96
    if (defined $checkitem && $checkitem ne ''){
97
        if ( defined $checkitem && $checkitem ne '' ) {
97
		my $item = GetItem($checkitem);
98
            my $item = GetItem($checkitem);
98
        	if ($item->{'biblionumber'} ne $biblionumber) {
99
            if ( $item->{'biblionumber'} ne $biblionumber ) {
99
                	$biblionumber = $item->{'biblionumber'};
100
                $biblionumber = $item->{'biblionumber'};
100
        	}
101
            }
101
	}
102
        }
102
103
104
103
105
        if ($multi_hold) {
104
        if ($multi_hold) {
106
            my $bibinfo = $bibinfos{$biblionumber};
105
            my $bibinfo = $bibinfos{$biblionumber};
107
            AddReserve($branch,$borrower->{'borrowernumber'},$biblionumber,[$biblionumber],
106
            AddReserve( $branch, $borrower->{'borrowernumber'},
108
                       $bibinfo->{rank},$startdate,$expirationdate,$notes,$bibinfo->{title},$checkitem,$found);
107
                $biblionumber, [$biblionumber],
109
        } else {
108
                $bibinfo->{rank}, $startdate, $expirationdate, $notes, $bibinfo->{title}, $checkitem,
110
            # place a request on 1st available
109
                $found );
111
            AddReserve($branch,$borrower->{'borrowernumber'},$biblionumber,\@realbi,$rank[0],$startdate,$expirationdate,$notes,$title,$checkitem,$found);
110
        }
111
        else {
112
            for ( my $i = 0 ; $i < $holds_to_place_count ; $i++ ) {
113
                AddReserve( $branch, $borrower->{'borrowernumber'},
114
                    $biblionumber, \@realbi, $rank[0], $startdate, $expirationdate, $notes, $title,
115
                    $checkitem, $found );
116
            }
112
        }
117
        }
113
    }
118
    }
114
119
Lines 117-129 if ($type eq 'str8' && $borrower){ Link Here
117
            $biblionumbers .= $bad_bibs;
122
            $biblionumbers .= $bad_bibs;
118
        }
123
        }
119
        print $input->redirect("request.pl?biblionumbers=$biblionumbers&multi_hold=1");
124
        print $input->redirect("request.pl?biblionumbers=$biblionumbers&multi_hold=1");
120
    } else {
125
    }
126
    else {
121
        print $input->redirect("request.pl?biblionumber=$biblionumber");
127
        print $input->redirect("request.pl?biblionumber=$biblionumber");
122
    }
128
    }
123
} elsif ($borrower eq ''){
129
}
124
	print $input->header();
130
elsif ( $borrower eq '' ) {
125
	print "Invalid borrower number please try again";
131
    print $input->header();
126
# Not sure that Dump() does HTML escaping. Use firebug or something to trace
132
    print "Invalid borrower number please try again";
127
# instead.
133
128
#	print $input->Dump;
134
    # Not sure that Dump() does HTML escaping. Use firebug or something to trace
135
    # instead.
136
    #	print $input->Dump;
129
}
137
}
(-)a/reserve/request.pl (-33 / +43 lines)
Lines 26-33 script to place reserves/requests Link Here
26
26
27
=cut
27
=cut
28
28
29
use strict;
29
use Modern::Perl;
30
use warnings;
30
31
use C4::Branch;
31
use C4::Branch;
32
use CGI qw ( -utf8 );
32
use CGI qw ( -utf8 );
33
use List::MoreUtils qw/uniq/;
33
use List::MoreUtils qw/uniq/;
Lines 234-271 foreach my $biblionumber (@biblionumbers) { Link Here
234
        $biblioloopiter{$canReserve} = 1;
234
        $biblioloopiter{$canReserve} = 1;
235
    }
235
    }
236
236
237
    my $alreadypossession;
237
    my $force_hold_level;
238
    if (not C4::Context->preference('AllowHoldsOnPatronsPossessions') and CheckIfIssuedToPatron($borrowerinfo->{borrowernumber},$biblionumber)) {
238
    if ( $borrowerinfo->{borrowernumber} ) {
239
        $alreadypossession = 1;
239
        # For multiple holds per record, if a patron has previously placed a hold,
240
        # the patron can only place more holds of the same type. That is, if the
241
        # patron placed a record level hold, all the holds the patron places must
242
        # be record level. If the patron placed an item level hold, all holds
243
        # the patron places must be item level
244
        my $holds = Koha::Holds->search(
245
            {
246
                borrowernumber => $borrowerinfo->{borrowernumber},
247
                biblionumber   => $biblionumber,
248
                found          => undef,
249
            }
250
        );
251
        $force_hold_level = $holds->forced_hold_level();
252
        $biblioloopiter{force_hold_level} = $force_hold_level;
253
        $template->param( force_hold_level => $force_hold_level );
254
255
        # For a librarian to be able to place multiple record holds for a patron for a record,
256
        # we must find out what the maximum number of holds they can place for the patron is
257
        my $max_holds_for_record = GetMaxPatronHoldsForRecord( $borrowerinfo->{borrowernumber}, $biblionumber );
258
        $max_holds_for_record = $max_holds_for_record - $holds->count();
259
        $biblioloopiter{max_holds_for_record} = $max_holds_for_record;
260
        $template->param( max_holds_for_record => $max_holds_for_record );
240
    }
261
    }
241
262
242
    # get existing reserves .....
263
    # Check to see if patron is allowed to place holds on records where the
243
    my $reserves = GetReservesFromBiblionumber({ biblionumber => $biblionumber, all_dates => 1 });
264
    # patron already has an item from that record checked out
244
    my $count = scalar( @$reserves );
265
    my $alreadypossession;
245
    my $totalcount = $count;
266
    if ( !C4::Context->preference('AllowHoldsOnPatronsPossessions')
246
    my $holds_count = 0;
267
        && CheckIfIssuedToPatron( $borrowerinfo->{borrowernumber}, $biblionumber ) )
247
    my $alreadyreserved = 0;
268
    {
248
269
        $template->param( alreadypossession => $alreadypossession, );
249
    foreach my $res (@$reserves) {
250
        if ( defined $res->{found} ) { # found can be 'W' or 'T'
251
            $count--;
252
        }
253
254
        if ( defined $borrowerinfo && defined($borrowerinfo->{borrowernumber}) && ($borrowerinfo->{borrowernumber} eq $res->{borrowernumber}) ) {
255
            $holds_count++;
256
        }
257
    }
270
    }
258
271
259
    if ( $holds_count ) {
260
            $alreadyreserved = 1;
261
            $biblioloopiter{warn} = 1;
262
            $biblioloopiter{alreadyres} = 1;
263
    }
264
272
265
    $template->param(
273
    my $count = Koha::Holds->search( { biblionumber => $biblionumber } )->count();
266
        alreadyreserved => $alreadyreserved,
274
    my $totalcount = $count;
267
        alreadypossession => $alreadypossession,
268
    );
269
275
270
    # FIXME think @optionloop, is maybe obsolete, or  must be switchable by a systeme preference fixed rank or not
276
    # FIXME think @optionloop, is maybe obsolete, or  must be switchable by a systeme preference fixed rank or not
271
    # make priorities options
277
    # make priorities options
Lines 327-332 foreach my $biblionumber (@biblionumbers) { Link Here
327
        my $num_override  = 0;
333
        my $num_override  = 0;
328
        my $hiddencount   = 0;
334
        my $hiddencount   = 0;
329
335
336
        $biblioitem->{force_hold_level} = $force_hold_level;
337
330
        if ( $biblioitem->{biblioitemnumber} ne $biblionumber ) {
338
        if ( $biblioitem->{biblioitemnumber} ne $biblionumber ) {
331
            $biblioitem->{hostitemsflag} = 1;
339
            $biblioitem->{hostitemsflag} = 1;
332
        }
340
        }
Lines 346-351 foreach my $biblionumber (@biblionumbers) { Link Here
346
        foreach my $itemnumber ( @{ $itemnumbers_of_biblioitem{$biblioitemnumber} } )    {
354
        foreach my $itemnumber ( @{ $itemnumbers_of_biblioitem{$biblioitemnumber} } )    {
347
            my $item = $iteminfos_of->{$itemnumber};
355
            my $item = $iteminfos_of->{$itemnumber};
348
356
357
            $item->{force_hold_level} = $force_hold_level;
358
349
            unless (C4::Context->preference('item-level_itypes')) {
359
            unless (C4::Context->preference('item-level_itypes')) {
350
                $item->{itype} = $biblioitem->{itemtype};
360
                $item->{itype} = $biblioitem->{itemtype};
351
            }
361
            }
Lines 443-455 foreach my $biblionumber (@biblionumbers) { Link Here
443
453
444
            $item->{'holdallowed'} = $branchitemrule->{'holdallowed'};
454
            $item->{'holdallowed'} = $branchitemrule->{'holdallowed'};
445
455
456
            my $can_item_be_reserved = CanItemBeReserved( $borrowerinfo->{borrowernumber}, $itemnumber );
457
            $item->{not_holdable} = $can_item_be_reserved unless ( $can_item_be_reserved eq 'OK' );
458
446
            if (
459
            if (
447
                   !$item->{cantreserve}
460
                   !$item->{cantreserve}
448
                && !$exceeded_maxreserves
461
                && !$exceeded_maxreserves
449
                && IsAvailableForItemLevelRequest($item, $borrowerinfo)
462
                && IsAvailableForItemLevelRequest($item, $borrowerinfo)
450
                && CanItemBeReserved(
463
                && $can_item_be_reserved eq 'OK'
451
                    $borrowerinfo->{borrowernumber}, $itemnumber
452
                ) eq 'OK'
453
              )
464
              )
454
            {
465
            {
455
                $item->{available} = 1;
466
                $item->{available} = 1;
456
- 

Return to bug 14695