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 141-146 BEGIN { Link Here
141
        &GetReservesControlBranch
142
        &GetReservesControlBranch
142
143
143
        IsItemOnHoldAndFound
144
        IsItemOnHoldAndFound
145
146
        GetMaxPatronHoldsForRecord
144
    );
147
    );
145
    @EXPORT_OK = qw( MergeHolds );
148
    @EXPORT_OK = qw( MergeHolds );
146
}
149
}
Lines 168-179 sub AddReserve { Link Here
168
        $title,      $checkitem, $found
171
        $title,      $checkitem, $found
169
    ) = @_;
172
    ) = @_;
170
173
171
    if ( Koha::Holds->search( { borrowernumber => $borrowernumber, biblionumber => $biblionumber } )->count() > 0 ) {
174
    my $dbh = C4::Context->dbh;
172
        carp("AddReserve: borrower $borrowernumber already has a hold for biblionumber $biblionumber");
173
        return;
174
    }
175
176
    my $dbh     = C4::Context->dbh;
177
175
178
    $resdate = output_pref( { str => dt_from_string( $resdate ), dateonly => 1, dateformat => 'iso' })
176
    $resdate = output_pref( { str => dt_from_string( $resdate ), dateonly => 1, dateformat => 'iso' })
179
        or output_pref({ dt => dt_from_string, dateonly => 1, dateformat => 'iso' });
177
        or output_pref({ dt => dt_from_string, dateonly => 1, dateformat => 'iso' });
Lines 453-459 sub CanItemBeReserved { Link Here
453
451
454
    my $dbh = C4::Context->dbh;
452
    my $dbh = C4::Context->dbh;
455
    my $ruleitemtype;    # itemtype of the matching issuing rule
453
    my $ruleitemtype;    # itemtype of the matching issuing rule
456
    my $allowedreserves = 0;
454
    my $allowedreserves  = 0; # Total number of holds allowed across all records
455
    my $holds_per_record = 1; # Total number of holds allowed for this one given record
457
456
458
    # we retrieve borrowers and items informations #
457
    # we retrieve borrowers and items informations #
459
    # item->{itype} will come for biblioitems if necessery
458
    # item->{itype} will come for biblioitems if necessery
Lines 466-491 sub CanItemBeReserved { Link Here
466
      if ( $item->{damaged}
465
      if ( $item->{damaged}
467
        && !C4::Context->preference('AllowHoldsOnDamagedItems') );
466
        && !C4::Context->preference('AllowHoldsOnDamagedItems') );
468
467
469
    #Check for the age restriction
468
    # Check for the age restriction
470
    my ( $ageRestriction, $daysToAgeRestriction ) =
469
    my ( $ageRestriction, $daysToAgeRestriction ) =
471
      C4::Circulation::GetAgeRestriction( $biblioData->{agerestriction}, $borrower );
470
      C4::Circulation::GetAgeRestriction( $biblioData->{agerestriction}, $borrower );
472
    return 'ageRestricted' if $daysToAgeRestriction && $daysToAgeRestriction > 0;
471
    return 'ageRestricted' if $daysToAgeRestriction && $daysToAgeRestriction > 0;
473
472
474
    my $controlbranch = C4::Context->preference('ReservesControlBranch');
473
    # Check that the patron doesn't have an item level hold on this item already
474
    return 'itemAlreadyOnHold'
475
      if Koha::Holds->search( { borrowernumber => $borrowernumber, itemnumber => $itemnumber } )->count();
475
476
476
    # we retrieve user rights on this itemtype and branchcode
477
    my $controlbranch = C4::Context->preference('ReservesControlBranch');
477
    my $sth = $dbh->prepare(
478
        q{
479
         SELECT categorycode, itemtype, branchcode, reservesallowed
480
           FROM issuingrules
481
          WHERE (categorycode in (?,'*') )
482
            AND (itemtype IN (?,'*'))
483
            AND (branchcode IN (?,'*'))
484
       ORDER BY categorycode DESC,
485
                itemtype     DESC,
486
                branchcode   DESC
487
        }
488
    );
489
478
490
    my $querycount = q{
479
    my $querycount = q{
491
        SELECT count(*) AS count
480
        SELECT count(*) AS count
Lines 509-523 sub CanItemBeReserved { Link Here
509
    }
498
    }
510
499
511
    # we retrieve rights
500
    # we retrieve rights
512
    $sth->execute( $borrower->{'categorycode'}, $item->{'itype'}, $branchcode );
501
    if ( my $rights = GetHoldRule( $borrower->{'categorycode'}, $item->{'itype'}, $branchcode ) ) {
513
    if ( my $rights = $sth->fetchrow_hashref() ) {
502
        $ruleitemtype     = $rights->{itemtype};
514
        $ruleitemtype    = $rights->{itemtype};
503
        $allowedreserves  = $rights->{reservesallowed};
515
        $allowedreserves = $rights->{reservesallowed};
504
        $holds_per_record = $rights->{holds_per_record};
516
    }
505
    }
517
    else {
506
    else {
518
        $ruleitemtype = '*';
507
        $ruleitemtype = '*';
519
    }
508
    }
520
509
510
    my $item = Koha::Items->find( $itemnumber );
511
    my $holds = Koha::Holds->search(
512
        {
513
            borrowernumber => $borrowernumber,
514
            biblionumber   => $item->biblionumber,
515
            found          => undef, # Found holds don't count against a patron's holds limit
516
        }
517
    );
518
    if ( $holds->count() >= $holds_per_record ) {
519
        return "tooManyHoldsForThisRecord";
520
    }
521
521
    # we retrieve count
522
    # we retrieve count
522
523
523
    $querycount .= "AND $branchfield = ?";
524
    $querycount .= "AND $branchfield = ?";
Lines 747-754 sub GetReservesToBranch { Link Here
747
    my $dbh = C4::Context->dbh;
748
    my $dbh = C4::Context->dbh;
748
    my $sth = $dbh->prepare(
749
    my $sth = $dbh->prepare(
749
        "SELECT reserve_id,borrowernumber,reservedate,itemnumber,timestamp
750
        "SELECT reserve_id,borrowernumber,reservedate,itemnumber,timestamp
750
         FROM reserves 
751
         FROM reserves
751
         WHERE priority='0' 
752
         WHERE priority='0'
752
           AND branchcode=?"
753
           AND branchcode=?"
753
    );
754
    );
754
    $sth->execute( $frombranch );
755
    $sth->execute( $frombranch );
Lines 773-779 sub GetReservesForBranch { Link Here
773
774
774
    my $query = "
775
    my $query = "
775
        SELECT reserve_id,borrowernumber,reservedate,itemnumber,waitingdate
776
        SELECT reserve_id,borrowernumber,reservedate,itemnumber,waitingdate
776
        FROM   reserves 
777
        FROM   reserves
777
        WHERE   priority='0'
778
        WHERE   priority='0'
778
        AND found='W'
779
        AND found='W'
779
    ";
780
    ";
Lines 1395-1401 sub ModReserveMinusPriority { Link Here
1395
    my $dbh   = C4::Context->dbh;
1396
    my $dbh   = C4::Context->dbh;
1396
    my $query = "
1397
    my $query = "
1397
        UPDATE reserves
1398
        UPDATE reserves
1398
        SET    priority = 0 , itemnumber = ? 
1399
        SET    priority = 0 , itemnumber = ?
1399
        WHERE  reserve_id = ?
1400
        WHERE  reserve_id = ?
1400
    ";
1401
    ";
1401
    my $sth_upd = $dbh->prepare($query);
1402
    my $sth_upd = $dbh->prepare($query);
Lines 1610-1616 sub ToggleLowestPriority { Link Here
1610
1611
1611
    my $sth = $dbh->prepare( "UPDATE reserves SET lowestPriority = NOT lowestPriority WHERE reserve_id = ?");
1612
    my $sth = $dbh->prepare( "UPDATE reserves SET lowestPriority = NOT lowestPriority WHERE reserve_id = ?");
1612
    $sth->execute( $reserve_id );
1613
    $sth->execute( $reserve_id );
1613
    
1614
1614
    _FixPriority({ reserve_id => $reserve_id, rank => '999999' });
1615
    _FixPriority({ reserve_id => $reserve_id, rank => '999999' });
1615
}
1616
}
1616
1617
Lines 1794-1800 sub _FixPriority { Link Here
1794
            $priority[$j]->{'reserve_id'}
1795
            $priority[$j]->{'reserve_id'}
1795
        );
1796
        );
1796
    }
1797
    }
1797
    
1798
1798
    $sth = $dbh->prepare( "SELECT reserve_id FROM reserves WHERE lowestPriority = 1 ORDER BY priority" );
1799
    $sth = $dbh->prepare( "SELECT reserve_id FROM reserves WHERE lowestPriority = 1 ORDER BY priority" );
1799
    $sth->execute();
1800
    $sth->execute();
1800
1801
Lines 2029-2035 sub _koha_notify_reserve { Link Here
2029
    if (! $notification_sent) {
2030
    if (! $notification_sent) {
2030
        &$send_notification('print', 'HOLD');
2031
        &$send_notification('print', 'HOLD');
2031
    }
2032
    }
2032
    
2033
2033
}
2034
}
2034
2035
2035
=head2 _ShiftPriorityByDateAndPriority
2036
=head2 _ShiftPriorityByDateAndPriority
Lines 2458-2463 sub IsItemOnHoldAndFound { Link Here
2458
    return $found;
2459
    return $found;
2459
}
2460
}
2460
2461
2462
=head2 GetMaxPatronHoldsForRecord
2463
2464
my $holds_per_record = ReservesControlBranch( $borrowernumber, $biblionumber );
2465
2466
For multiple holds on a given record for a given patron, the max
2467
number of record level holds that a patron can be placed is the highest
2468
value of the holds_per_record rule for each item if the record for that
2469
patron. This subroutine finds and returns the highest holds_per_record
2470
rule value for a given patron id and record id.
2471
2472
=cut
2473
2474
sub GetMaxPatronHoldsForRecord {
2475
    my ( $borrowernumber, $biblionumber ) = @_;
2476
2477
    my $patron = Koha::Patrons->find($borrowernumber);
2478
    my @items = Koha::Items->search( { biblionumber => $biblionumber } );
2479
2480
    my $controlbranch = C4::Context->preference('ReservesControlBranch');
2481
2482
    my $categorycode = $patron->categorycode;
2483
    my $branchcode;
2484
    $branchcode = $patron->branchcode if ( $controlbranch eq "PatronLibrary" );
2485
2486
    my $max = 0;
2487
    foreach my $item (@items) {
2488
        my $itemtype = $item->effective_itemtype();
2489
2490
        $branchcode = $item->homebranch if ( $controlbranch eq "ItemHomeLibrary" );
2491
2492
        my $rule = GetHoldRule( $categorycode, $itemtype, $branchcode );
2493
        my $holds_per_record = $rule ? $rule->{holds_per_record} : 0;
2494
        $max = $holds_per_record if $holds_per_record > $max;
2495
    }
2496
2497
    return $max;
2498
}
2499
2500
=head2 GetHoldRule
2501
2502
my $rule = GetHoldRule( $categorycode, $itemtype, $branchcode );
2503
2504
Returns the matching hold related issuingrule fields for a given
2505
patron category, itemtype, and library.
2506
2507
=cut
2508
2509
sub GetHoldRule {
2510
    my ( $categorycode, $itemtype, $branchcode ) = @_;
2511
2512
    my $dbh = C4::Context->dbh;
2513
2514
    my $sth = $dbh->prepare(
2515
        q{
2516
         SELECT categorycode, itemtype, branchcode, reservesallowed, holds_per_record
2517
           FROM issuingrules
2518
          WHERE (categorycode in (?,'*') )
2519
            AND (itemtype IN (?,'*'))
2520
            AND (branchcode IN (?,'*'))
2521
       ORDER BY categorycode DESC,
2522
                itemtype     DESC,
2523
                branchcode   DESC
2524
        }
2525
    );
2526
2527
    $sth->execute( $categorycode, $itemtype, $branchcode );
2528
2529
    return $sth->fetchrow_hashref();
2530
}
2531
2461
=head1 AUTHOR
2532
=head1 AUTHOR
2462
2533
2463
Koha Development Team <http://koha-community.org/>
2534
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