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

(-)a/C4/Reserves.pm (-33 / +104 lines)
Lines 43-48 use Koha::Holds; Link Here
43
use Koha::Libraries;
43
use Koha::Libraries;
44
use Koha::Items;
44
use Koha::Items;
45
use Koha::ItemTypes;
45
use Koha::ItemTypes;
46
use Koha::Patrons;
46
47
47
use List::MoreUtils qw( firstidx any );
48
use List::MoreUtils qw( firstidx any );
48
use Carp;
49
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,        $itemtype
171
        $title,    $checkitem,      $found,        $itemtype
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 458-464 sub CanItemBeReserved { Link Here
458
456
459
    my $dbh = C4::Context->dbh;
457
    my $dbh = C4::Context->dbh;
460
    my $ruleitemtype;    # itemtype of the matching issuing rule
458
    my $ruleitemtype;    # itemtype of the matching issuing rule
461
    my $allowedreserves = 0;
459
    my $allowedreserves  = 0; # Total number of holds allowed across all records
460
    my $holds_per_record = 1; # Total number of holds allowed for this one given record
462
461
463
    # we retrieve borrowers and items informations #
462
    # we retrieve borrowers and items informations #
464
    # item->{itype} will come for biblioitems if necessery
463
    # item->{itype} will come for biblioitems if necessery
Lines 471-496 sub CanItemBeReserved { Link Here
471
      if ( $item->{damaged}
470
      if ( $item->{damaged}
472
        && !C4::Context->preference('AllowHoldsOnDamagedItems') );
471
        && !C4::Context->preference('AllowHoldsOnDamagedItems') );
473
472
474
    #Check for the age restriction
473
    # Check for the age restriction
475
    my ( $ageRestriction, $daysToAgeRestriction ) =
474
    my ( $ageRestriction, $daysToAgeRestriction ) =
476
      C4::Circulation::GetAgeRestriction( $biblioData->{agerestriction}, $borrower );
475
      C4::Circulation::GetAgeRestriction( $biblioData->{agerestriction}, $borrower );
477
    return 'ageRestricted' if $daysToAgeRestriction && $daysToAgeRestriction > 0;
476
    return 'ageRestricted' if $daysToAgeRestriction && $daysToAgeRestriction > 0;
478
477
479
    my $controlbranch = C4::Context->preference('ReservesControlBranch');
478
    # Check that the patron doesn't have an item level hold on this item already
479
    return 'itemAlreadyOnHold'
480
      if Koha::Holds->search( { borrowernumber => $borrowernumber, itemnumber => $itemnumber } )->count();
480
481
481
    # we retrieve user rights on this itemtype and branchcode
482
    my $controlbranch = C4::Context->preference('ReservesControlBranch');
482
    my $sth = $dbh->prepare(
483
        q{
484
         SELECT categorycode, itemtype, branchcode, reservesallowed
485
           FROM issuingrules
486
          WHERE (categorycode in (?,'*') )
487
            AND (itemtype IN (?,'*'))
488
            AND (branchcode IN (?,'*'))
489
       ORDER BY categorycode DESC,
490
                itemtype     DESC,
491
                branchcode   DESC
492
        }
493
    );
494
483
495
    my $querycount = q{
484
    my $querycount = q{
496
        SELECT count(*) AS count
485
        SELECT count(*) AS count
Lines 514-528 sub CanItemBeReserved { Link Here
514
    }
503
    }
515
504
516
    # we retrieve rights
505
    # we retrieve rights
517
    $sth->execute( $borrower->{'categorycode'}, $item->{'itype'}, $branchcode );
506
    if ( my $rights = GetHoldRule( $borrower->{'categorycode'}, $item->{'itype'}, $branchcode ) ) {
518
    if ( my $rights = $sth->fetchrow_hashref() ) {
507
        $ruleitemtype     = $rights->{itemtype};
519
        $ruleitemtype    = $rights->{itemtype};
508
        $allowedreserves  = $rights->{reservesallowed};
520
        $allowedreserves = $rights->{reservesallowed};
509
        $holds_per_record = $rights->{holds_per_record};
521
    }
510
    }
522
    else {
511
    else {
523
        $ruleitemtype = '*';
512
        $ruleitemtype = '*';
524
    }
513
    }
525
514
515
    my $item = Koha::Items->find( $itemnumber );
516
    my $holds = Koha::Holds->search(
517
        {
518
            borrowernumber => $borrowernumber,
519
            biblionumber   => $item->biblionumber,
520
            found          => undef, # Found holds don't count against a patron's holds limit
521
        }
522
    );
523
    if ( $holds->count() >= $holds_per_record ) {
524
        return "tooManyHoldsForThisRecord";
525
    }
526
526
    # we retrieve count
527
    # we retrieve count
527
528
528
    $querycount .= "AND $branchfield = ?";
529
    $querycount .= "AND $branchfield = ?";
Lines 752-759 sub GetReservesToBranch { Link Here
752
    my $dbh = C4::Context->dbh;
753
    my $dbh = C4::Context->dbh;
753
    my $sth = $dbh->prepare(
754
    my $sth = $dbh->prepare(
754
        "SELECT reserve_id,borrowernumber,reservedate,itemnumber,timestamp
755
        "SELECT reserve_id,borrowernumber,reservedate,itemnumber,timestamp
755
         FROM reserves 
756
         FROM reserves
756
         WHERE priority='0' 
757
         WHERE priority='0'
757
           AND branchcode=?"
758
           AND branchcode=?"
758
    );
759
    );
759
    $sth->execute( $frombranch );
760
    $sth->execute( $frombranch );
Lines 778-784 sub GetReservesForBranch { Link Here
778
779
779
    my $query = "
780
    my $query = "
780
        SELECT reserve_id,borrowernumber,reservedate,itemnumber,waitingdate
781
        SELECT reserve_id,borrowernumber,reservedate,itemnumber,waitingdate
781
        FROM   reserves 
782
        FROM   reserves
782
        WHERE   priority='0'
783
        WHERE   priority='0'
783
        AND found='W'
784
        AND found='W'
784
    ";
785
    ";
Lines 1402-1408 sub ModReserveMinusPriority { Link Here
1402
    my $dbh   = C4::Context->dbh;
1403
    my $dbh   = C4::Context->dbh;
1403
    my $query = "
1404
    my $query = "
1404
        UPDATE reserves
1405
        UPDATE reserves
1405
        SET    priority = 0 , itemnumber = ? 
1406
        SET    priority = 0 , itemnumber = ?
1406
        WHERE  reserve_id = ?
1407
        WHERE  reserve_id = ?
1407
    ";
1408
    ";
1408
    my $sth_upd = $dbh->prepare($query);
1409
    my $sth_upd = $dbh->prepare($query);
Lines 1639-1645 sub ToggleLowestPriority { Link Here
1639
1640
1640
    my $sth = $dbh->prepare( "UPDATE reserves SET lowestPriority = NOT lowestPriority WHERE reserve_id = ?");
1641
    my $sth = $dbh->prepare( "UPDATE reserves SET lowestPriority = NOT lowestPriority WHERE reserve_id = ?");
1641
    $sth->execute( $reserve_id );
1642
    $sth->execute( $reserve_id );
1642
    
1643
1643
    _FixPriority({ reserve_id => $reserve_id, rank => '999999' });
1644
    _FixPriority({ reserve_id => $reserve_id, rank => '999999' });
1644
}
1645
}
1645
1646
Lines 1823-1829 sub _FixPriority { Link Here
1823
            $priority[$j]->{'reserve_id'}
1824
            $priority[$j]->{'reserve_id'}
1824
        );
1825
        );
1825
    }
1826
    }
1826
    
1827
1827
    $sth = $dbh->prepare( "SELECT reserve_id FROM reserves WHERE lowestPriority = 1 ORDER BY priority" );
1828
    $sth = $dbh->prepare( "SELECT reserve_id FROM reserves WHERE lowestPriority = 1 ORDER BY priority" );
1828
    $sth->execute();
1829
    $sth->execute();
1829
1830
Lines 2061-2067 sub _koha_notify_reserve { Link Here
2061
    if (! $notification_sent) {
2062
    if (! $notification_sent) {
2062
        &$send_notification('print', 'HOLD');
2063
        &$send_notification('print', 'HOLD');
2063
    }
2064
    }
2064
    
2065
2065
}
2066
}
2066
2067
2067
=head2 _ShiftPriorityByDateAndPriority
2068
=head2 _ShiftPriorityByDateAndPriority
Lines 2490-2495 sub IsItemOnHoldAndFound { Link Here
2490
    return $found;
2491
    return $found;
2491
}
2492
}
2492
2493
2494
=head2 GetMaxPatronHoldsForRecord
2495
2496
my $holds_per_record = ReservesControlBranch( $borrowernumber, $biblionumber );
2497
2498
For multiple holds on a given record for a given patron, the max
2499
number of record level holds that a patron can be placed is the highest
2500
value of the holds_per_record rule for each item if the record for that
2501
patron. This subroutine finds and returns the highest holds_per_record
2502
rule value for a given patron id and record id.
2503
2504
=cut
2505
2506
sub GetMaxPatronHoldsForRecord {
2507
    my ( $borrowernumber, $biblionumber ) = @_;
2508
2509
    my $patron = Koha::Patrons->find($borrowernumber);
2510
    my @items = Koha::Items->search( { biblionumber => $biblionumber } );
2511
2512
    my $controlbranch = C4::Context->preference('ReservesControlBranch');
2513
2514
    my $categorycode = $patron->categorycode;
2515
    my $branchcode;
2516
    $branchcode = $patron->branchcode if ( $controlbranch eq "PatronLibrary" );
2517
2518
    my $max = 0;
2519
    foreach my $item (@items) {
2520
        my $itemtype = $item->effective_itemtype();
2521
2522
        $branchcode = $item->homebranch if ( $controlbranch eq "ItemHomeLibrary" );
2523
2524
        my $rule = GetHoldRule( $categorycode, $itemtype, $branchcode );
2525
        my $holds_per_record = $rule ? $rule->{holds_per_record} : 0;
2526
        $max = $holds_per_record if $holds_per_record > $max;
2527
    }
2528
2529
    return $max;
2530
}
2531
2532
=head2 GetHoldRule
2533
2534
my $rule = GetHoldRule( $categorycode, $itemtype, $branchcode );
2535
2536
Returns the matching hold related issuingrule fields for a given
2537
patron category, itemtype, and library.
2538
2539
=cut
2540
2541
sub GetHoldRule {
2542
    my ( $categorycode, $itemtype, $branchcode ) = @_;
2543
2544
    my $dbh = C4::Context->dbh;
2545
2546
    my $sth = $dbh->prepare(
2547
        q{
2548
         SELECT categorycode, itemtype, branchcode, reservesallowed, holds_per_record
2549
           FROM issuingrules
2550
          WHERE (categorycode in (?,'*') )
2551
            AND (itemtype IN (?,'*'))
2552
            AND (branchcode IN (?,'*'))
2553
       ORDER BY categorycode DESC,
2554
                itemtype     DESC,
2555
                branchcode   DESC
2556
        }
2557
    );
2558
2559
    $sth->execute( $categorycode, $itemtype, $branchcode );
2560
2561
    return $sth->fetchrow_hashref();
2562
}
2563
2493
=head1 AUTHOR
2564
=head1 AUTHOR
2494
2565
2495
Koha Development Team <http://koha-community.org/>
2566
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 62-110 $(document).ready(function() { Link Here
62
});
62
});
63
63
64
function check() {
64
function check() {
65
	var msg = "";
65
    var msg = "";
66
	var count_reserv = 0;
66
    var count_reserv = 0;
67
	var alreadyreserved = 0;
68
67
69
    // check if we have checkitem form
68
    // check if we have checkitem form
70
    if (document.form.checkitem){
69
    if (document.form.checkitem){
71
        for (i=0;i<document.form.checkitem.length;i++){
70
        for (i=0;i<document.form.checkitem.length;i++){
72
            if (document.form.checkitem[i].checked == true) {
71
            if (document.form.checkitem[i].checked == true) {
73
				count_reserv++ ;
72
                count_reserv++ ;
74
			}
73
            }
75
        }
74
        }
76
        // for only one item, check the checkitem without consider the loop checkitem
75
        // for only one item, check the checkitem without consider the loop checkitem
77
        if (i==0){
76
        if (i==0){
78
		    if (document.form.checkitem.checked == true) {
77
            if (document.form.checkitem.checked == true) {
79
			    count_reserv++;
78
                count_reserv++;
80
		    }
79
            }
81
	    }
80
        }
82
    }
83
84
    if (document.form.request.checked == true){
85
		count_reserv++ ;
86
    }
81
    }
87
82
88
    if (document.form.alreadyreserved && document.form.alreadyreserved.value == "1"){
83
    if (document.form.requestany.checked == true){
89
		 alreadyreserved++ ;
84
        count_reserv++ ;
90
    }
85
    }
91
86
92
    if (count_reserv == "0"){
87
    if (count_reserv == "0"){
93
		msg += (_("- Please select an item to place a hold") + "\n");
88
        msg += (_("- Please select an item to place a hold") + "\n");
94
    }
95
    if (count_reserv >= "2"){
96
		msg += (_("- You may only place a hold on one item at a time") + "\n");
97
    }
89
    }
98
90
99
    if (alreadyreserved > "0"){
91
    if (msg == "") {
100
		msg += (_("- This patron had already placed a hold on this item") + "\n" + _("Please cancel the previous hold first") + "\n");
92
        $('#hold-request-form').preventDoubleFormSubmit();
93
        return(true);
94
    } else {
95
        alert(msg);
96
        return(false);
101
    }
97
    }
102
103
	if (msg == "") return(true);
104
	else	{
105
		alert(msg);
106
		return(false);
107
	}
108
}
98
}
109
99
110
function checkMultiHold() {
100
function checkMultiHold() {
Lines 130-135 function checkMultiHold() { Link Here
130
    $("#multi_hold_bibs").val(biblionumbers);
120
    $("#multi_hold_bibs").val(biblionumbers);
131
    $("#bad_bibs").val(badBibs);
121
    $("#bad_bibs").val(badBibs);
132
122
123
    $('#hold-request-form').preventDoubleFormSubmit();
124
133
    return true;
125
    return true;
134
}
126
}
135
127
Lines 177-183 function checkMultiHold() { Link Here
177
        $("#" + fieldID).val("");
169
        $("#" + fieldID).val("");
178
    });
170
    });
179
171
180
    $('#hold-request-form').preventDoubleFormSubmit();
181
172
182
[% UNLESS ( borrowernumber || borrowers || noitems ) %]
173
[% UNLESS ( borrowernumber || borrowers || noitems ) %]
183
    [% IF ( CircAutocompl ) %]
174
    [% IF ( CircAutocompl ) %]
Lines 309-315 function checkMultiHold() { Link Here
309
        [% IF ( exceeded_maxreserves ) %]
300
        [% IF ( exceeded_maxreserves ) %]
310
          <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>
301
          <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>
311
        [% ELSIF ( alreadypossession ) %]
302
        [% ELSIF ( alreadypossession ) %]
312
          <li> <a href="/cgi-bin/koha/members/moremember.pl?borrowernumber=[% borrowernumber %]">[% borrowerfirstname %] [% borrowersurname %]</a> <strong>is already in possession</strong> of one item</li>
303
          <li> <a href="/cgi-bin/koha/members/moremember.pl?borrowernumber=[% borrowernumber %]">[% borrowerfirstname %] [% borrowersurname %]</a> <strong>is already in possession</strong> of one item</lie
313
        [% ELSIF ( alreadyreserved ) %]
304
        [% ELSIF ( alreadyreserved ) %]
314
          <li><a href="/cgi-bin/koha/members/moremember.pl?borrowernumber=[% borrowernumber %]">[% borrowerfirstname %] [% borrowersurname %]</a> <strong>already has a hold</strong> on this item </li>
305
          <li><a href="/cgi-bin/koha/members/moremember.pl?borrowernumber=[% borrowernumber %]">[% borrowerfirstname %] [% borrowersurname %]</a> <strong>already has a hold</strong> on this item </li>
315
        [% ELSIF ( ageRestricted ) %]
306
        [% ELSIF ( ageRestricted ) %]
Lines 440-450 function checkMultiHold() { Link Here
440
	</li>
431
	</li>
441
432
442
        [% UNLESS ( multi_hold ) %]
433
        [% UNLESS ( multi_hold ) %]
443
          <li> <label for="requestany">Place a hold on the next available item </label>
434
          <li> <label for="requestany">Hold next available item </label>
444
               <input type="checkbox" id="requestany" name="request" checked="checked" value="Any" />
435
               [% IF force_hold_level == 'item' %]
436
                   <input type="checkbox" id="requestany" name="request" disabled="true" />
437
               [% ELSIF force_hold_level == 'record' %]
438
                   <input type="checkbox" id="requestany" checked="checked" value="Any" disabled="true"/>
439
                   <input type="hidden" name="request" value="Any"/>
440
               [% ELSE %]
441
                   <input type="checkbox" id="requestany" name="request" checked="checked" value="Any" />
442
                [% END %]
445
               <input type="hidden" name="biblioitem" value="[% biblioitemnumber %]" />
443
               <input type="hidden" name="biblioitem" value="[% biblioitemnumber %]" />
446
               <input type="hidden" name="alreadyreserved" value="[% alreadyreserved %]" />
444
               <input type="hidden" name="alreadyreserved" value="[% alreadyreserved %]" />
447
          </li>
445
          </li>
446
447
          [% IF max_holds_for_record > 1 %]
448
              [% SET count = 1 %]
449
              <li>
450
                   <label for="holds_to_place_count">Holds to place (count)</label>
451
                   <select name="holds_to_place_count" id="holds_to_place_count">
452
                   [% WHILE count <= max_holds_for_record %]
453
                        <option value="[% count %]">[% count %]</option>
454
                        [% SET count = count + 1 %]
455
                   [% END %]
456
457
                   </select>
458
              </li>
459
            [% ELSE %]
460
                <input type="hidden" name="holds_to_place_count" value="1";
461
            [% END %]
448
        [% END %]
462
        [% END %]
449
463
450
</ol>
464
</ol>
Lines 469-475 function checkMultiHold() { Link Here
469
            [% IF ( bibitemloo.publicationyear ) %]<li><span class="label">Publication year:</span> [% bibitemloo.publicationyear %]</li>[% END %]
483
            [% IF ( bibitemloo.publicationyear ) %]<li><span class="label">Publication year:</span> [% bibitemloo.publicationyear %]</li>[% END %]
470
          </ol>
484
          </ol>
471
485
472
        <h2 style="padding: 0 1em;">Place a hold on a specific item</h2>
486
        <h2 style="padding: 0 1em;">
487
            Place a hold on a specific item
488
            [% IF bibitemloo.force_hold_level == 'item' %]
489
                <span class="error"><i>(Required)</i></span>
490
            [% END %]
491
        </h2>
473
        <table id="requestspecific">
492
        <table id="requestspecific">
474
            <thead>
493
            <thead>
475
                <tr>
494
                <tr>
Lines 489-506 function checkMultiHold() { Link Here
489
                </tr>
508
                </tr>
490
            </thead>
509
            </thead>
491
            <tbody>
510
            <tbody>
511
            [% SET selected = 0 %]
492
            [% FOREACH itemloo IN bibitemloo.itemloop %]
512
            [% FOREACH itemloo IN bibitemloo.itemloop %]
493
            [% UNLESS ( itemloo.hide ) %]
513
            [% UNLESS ( itemloo.hide ) %]
494
                <tr class="[% itemloo.backgroundcolor %]">
514
                <tr class="[% itemloo.backgroundcolor %]">
495
                    <td>
515
                    <td>
496
                [% IF ( itemloo.available ) %]
516
                [% IF itemloo.force_hold_level == 'record' # Patron has placed a record level hold previously for this record %]
517
                    <span class="error">
518
                        <i class="fa fa-times fa-lg" alt="Cannot be put on hold"></i>
519
                        Hold must be record level
520
                    </span>
521
                [% ELSIF ( itemloo.available ) %]
497
                    <input type="radio" name="checkitem" value="[% itemloo.itemnumber %]" />
522
                    <input type="radio" name="checkitem" value="[% itemloo.itemnumber %]" />
498
                [% ELSIF ( itemloo.override ) %]
523
                [% ELSIF ( itemloo.override ) %]
499
                    <input type="radio" name="checkitem" class="needsoverride" value="[% itemloo.itemnumber %]" />
524
                    <input type="radio" name="checkitem" class="needsoverride" value="[% itemloo.itemnumber %]" />
500
                    <img src="[% interface %]/[% theme %]/img/famfamfam/silk/error.png" alt="Requires override of hold policy" />
525
                    <i class="fa fa-exclamation-triangle fa-lg" style="color:gold" alt="Requires override of hold policy"/></i>
501
                [% ELSE %]
526
                [% ELSE %]
502
                    <input disabled="disabled" type="radio" name="checkitem" value="[% itemloo.itemnumber %]" />
527
                    <span class="error">
503
                    <img src="[% interface %]/[% theme %]/img/famfamfam/silk/cross.png" alt="Cannot be put on hold" />
528
                        <i class="fa fa-times fa-lg" alt="Cannot be put on hold"></i>
529
                        [% IF itemloo.not_holdable %]
530
                            [% IF itemloo.not_holdable == 'damaged' %]
531
                                Item damaged
532
                            [% ELSIF itemloo.not_holdable == 'ageRestricted' %]
533
                                Age restricted
534
                            [% ELSIF itemloo.not_holdable == 'tooManyHoldsForThisRecord' %]
535
                                Exceeded max holds per record
536
                            [% ELSIF itemloo.not_holdable == 'tooManyReserves' %]
537
                                Too many holds
538
                            [% ELSIF itemloo.not_holdable == 'notReservable' %]
539
                                Not holdable
540
                            [% ELSIF itemloo.not_holdable == 'cannotReserveFromOtherBranches' %]
541
                                Patron is from different library
542
                            [% ELSIF itemloo.not_holdable == 'itemAlreadyOnHold' %]
543
                                Patron already has hold for this item
544
                            [% ELSE %]
545
                                [% itemloo.not_holdable %]
546
                            [% END %]
547
                        [% END %]
548
                    </span>
504
                [% END %]
549
                [% END %]
505
                    </td>
550
                    </td>
506
                [% IF ( item_level_itypes ) %]
551
                [% IF ( item_level_itypes ) %]
(-)a/opac/opac-reserve.pl (-20 / +24 lines)
Lines 359-375 unless ( $noreserves ) { Link Here
359
    }
359
    }
360
}
360
}
361
361
362
foreach my $res (@reserves) {
363
    foreach my $biblionumber (@biblionumbers) {
364
        if ( $res->{'biblionumber'} == $biblionumber && $res->{'borrowernumber'} == $borrowernumber) {
365
#            $template->param( message => 1 );
366
#            $noreserves = 1;
367
#            $template->param( already_reserved => 1 );
368
            $biblioDataHash{$biblionumber}->{already_reserved} = 1;
369
        }
370
    }
371
}
372
373
unless ($noreserves) {
362
unless ($noreserves) {
374
    $template->param( select_item_types => 1 );
363
    $template->param( select_item_types => 1 );
375
}
364
}
Lines 468-476 foreach my $biblioNum (@biblionumbers) { Link Here
468
457
469
        # the item could be reserved for this borrower vi a host record, flag this
458
        # the item could be reserved for this borrower vi a host record, flag this
470
        $reservedfor //= '';
459
        $reservedfor //= '';
471
        if ($reservedfor eq $borrowernumber){
472
            $itemLoopIter->{already_reserved} = 1;
473
        }
474
460
475
        if ( defined $reservedate ) {
461
        if ( defined $reservedate ) {
476
            $itemLoopIter->{backgroundcolor} = 'reserved';
462
            $itemLoopIter->{backgroundcolor} = 'reserved';
Lines 515-526 foreach my $biblioNum (@biblionumbers) { Link Here
515
            $itemLoopIter->{nocancel} = 1;
501
            $itemLoopIter->{nocancel} = 1;
516
        }
502
        }
517
503
518
	# if the items belongs to a host record, show link to host record
504
        # if the items belongs to a host record, show link to host record
519
	if ($itemInfo->{biblionumber} ne $biblioNum){
505
        if ( $itemInfo->{biblionumber} ne $biblioNum ) {
520
		$biblioLoopIter{hostitemsflag} = 1;
506
            $biblioLoopIter{hostitemsflag}    = 1;
521
		$itemLoopIter->{hostbiblionumber} = $itemInfo->{biblionumber};
507
            $itemLoopIter->{hostbiblionumber} = $itemInfo->{biblionumber};
522
		$itemLoopIter->{hosttitle} = GetBiblioData($itemInfo->{biblionumber})->{title};
508
            $itemLoopIter->{hosttitle}        = GetBiblioData( $itemInfo->{biblionumber} )->{title};
523
	}
509
        }
524
510
525
        # If there is no loan, return and transfer, we show a checkbox.
511
        # If there is no loan, return and transfer, we show a checkbox.
526
        $itemLoopIter->{notforloan} = $itemLoopIter->{notforloan} || 0;
512
        $itemLoopIter->{notforloan} = $itemLoopIter->{notforloan} || 0;
Lines 569-574 foreach my $biblioNum (@biblionumbers) { Link Here
569
555
570
    $biblioLoopIter{holdable} &&= CanBookBeReserved($borrowernumber,$biblioNum) eq 'OK';
556
    $biblioLoopIter{holdable} &&= CanBookBeReserved($borrowernumber,$biblioNum) eq 'OK';
571
557
558
    # For multiple holds per record, if a patron has previously placed a hold,
559
    # the patron can only place more holds of the same type. That is, if the
560
    # patron placed a record level hold, all the holds the patron places must
561
    # be record level. If the patron placed an item level hold, all holds
562
    # the patron places must be item level
563
    my $forced_hold_level = Koha::Holds->search(
564
        {
565
            borrowernumber => $borrowernumber,
566
            biblionumber   => $biblioNum,
567
            found          => undef,
568
        }
569
    )->forced_hold_level();
570
    if ($forced_hold_level) {
571
        $biblioLoopIter{force_hold}   = 1 if $forced_hold_level eq 'item';
572
        $biblioLoopIter{itemholdable} = 0 if $forced_hold_level eq 'record';
573
    }
574
575
572
    push @$biblioLoop, \%biblioLoopIter;
576
    push @$biblioLoop, \%biblioLoopIter;
573
577
574
    $anyholdable = 1 if $biblioLoopIter{holdable};
578
    $anyholdable = 1 if $biblioLoopIter{holdable};
(-)a/reserve/placerequest.pl (-26 / +32 lines)
Lines 56-61 my $borrower = GetMember( 'borrowernumber' => $borrowernumber ); Link Here
56
my $multi_hold = $input->param('multi_hold');
56
my $multi_hold = $input->param('multi_hold');
57
my $biblionumbers = $multi_hold ? $input->param('biblionumbers') : ($biblionumber . '/');
57
my $biblionumbers = $multi_hold ? $input->param('biblionumbers') : ($biblionumber . '/');
58
my $bad_bibs = $input->param('bad_bibs');
58
my $bad_bibs = $input->param('bad_bibs');
59
my $holds_to_place_count = $input->param('holds_to_place_count') || 1;
59
60
60
my %bibinfos = ();
61
my %bibinfos = ();
61
my @biblionumbers = split '/', $biblionumbers;
62
my @biblionumbers = split '/', $biblionumbers;
Lines 79-108 if (defined $checkitem && $checkitem ne ''){ Link Here
79
    }
80
    }
80
}
81
}
81
82
82
if ($type eq 'str8' && $borrower){
83
if ( $type eq 'str8' && $borrower ) {
83
84
84
    foreach my $biblionumber (keys %bibinfos) {
85
    foreach my $biblionumber ( keys %bibinfos ) {
85
        my $count=@bibitems;
86
        my $count = @bibitems;
86
        @bibitems=sort @bibitems;
87
        @bibitems = sort @bibitems;
87
        my $i2=1;
88
        my $i2 = 1;
88
        my @realbi;
89
        my @realbi;
89
        $realbi[0]=$bibitems[0];
90
        $realbi[0] = $bibitems[0];
90
        for (my $i=1;$i<$count;$i++) {
91
        for ( my $i = 1 ; $i < $count ; $i++ ) {
91
            my $i3=$i2-1;
92
            my $i3 = $i2 - 1;
92
            if ($realbi[$i3] ne $bibitems[$i]) {
93
            if ( $realbi[$i3] ne $bibitems[$i] ) {
93
                $realbi[$i2]=$bibitems[$i];
94
                $realbi[$i2] = $bibitems[$i];
94
                $i2++;
95
                $i2++;
95
            }
96
            }
96
        }
97
        }
97
98
98
    if (defined $checkitem && $checkitem ne ''){
99
        if ( defined $checkitem && $checkitem ne '' ) {
99
		my $item = GetItem($checkitem);
100
            my $item = GetItem($checkitem);
100
        	if ($item->{'biblionumber'} ne $biblionumber) {
101
            if ( $item->{'biblionumber'} ne $biblionumber ) {
101
                	$biblionumber = $item->{'biblionumber'};
102
                $biblionumber = $item->{'biblionumber'};
102
        	}
103
            }
103
	}
104
        }
104
105
106
105
107
        if ($multi_hold) {
106
        if ($multi_hold) {
108
            my $bibinfo = $bibinfos{$biblionumber};
107
            my $bibinfo = $bibinfos{$biblionumber};
Lines 110-116 if ($type eq 'str8' && $borrower){ Link Here
110
                       $bibinfo->{rank},$startdate,$expirationdate,$notes,$bibinfo->{title},$checkitem,$found);
109
                       $bibinfo->{rank},$startdate,$expirationdate,$notes,$bibinfo->{title},$checkitem,$found);
111
        } else {
110
        } else {
112
            # place a request on 1st available
111
            # place a request on 1st available
113
            AddReserve($branch,$borrower->{'borrowernumber'},$biblionumber,\@realbi,$rank[0],$startdate,$expirationdate,$notes,$title,$checkitem,$found, $itemtype);
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, $itemtype );
116
            }
114
        }
117
        }
115
    }
118
    }
116
119
Lines 119-131 if ($type eq 'str8' && $borrower){ Link Here
119
            $biblionumbers .= $bad_bibs;
122
            $biblionumbers .= $bad_bibs;
120
        }
123
        }
121
        print $input->redirect("request.pl?biblionumbers=$biblionumbers&multi_hold=1");
124
        print $input->redirect("request.pl?biblionumbers=$biblionumbers&multi_hold=1");
122
    } else {
125
    }
126
    else {
123
        print $input->redirect("request.pl?biblionumber=$biblionumber");
127
        print $input->redirect("request.pl?biblionumber=$biblionumber");
124
    }
128
    }
125
} elsif ($borrower eq ''){
129
}
126
	print $input->header();
130
elsif ( $borrower eq '' ) {
127
	print "Invalid borrower number please try again";
131
    print $input->header();
128
# Not sure that Dump() does HTML escaping. Use firebug or something to trace
132
    print "Invalid borrower number please try again";
129
# instead.
133
130
#	print $input->Dump;
134
    # Not sure that Dump() does HTML escaping. Use firebug or something to trace
135
    # instead.
136
    #	print $input->Dump;
131
}
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 328-333 foreach my $biblionumber (@biblionumbers) { Link Here
328
        my $num_override  = 0;
334
        my $num_override  = 0;
329
        my $hiddencount   = 0;
335
        my $hiddencount   = 0;
330
336
337
        $biblioitem->{force_hold_level} = $force_hold_level;
338
331
        if ( $biblioitem->{biblioitemnumber} ne $biblionumber ) {
339
        if ( $biblioitem->{biblioitemnumber} ne $biblionumber ) {
332
            $biblioitem->{hostitemsflag} = 1;
340
            $biblioitem->{hostitemsflag} = 1;
333
        }
341
        }
Lines 347-352 foreach my $biblionumber (@biblionumbers) { Link Here
347
        foreach my $itemnumber ( @{ $itemnumbers_of_biblioitem{$biblioitemnumber} } )    {
355
        foreach my $itemnumber ( @{ $itemnumbers_of_biblioitem{$biblioitemnumber} } )    {
348
            my $item = $iteminfos_of->{$itemnumber};
356
            my $item = $iteminfos_of->{$itemnumber};
349
357
358
            $item->{force_hold_level} = $force_hold_level;
359
350
            unless (C4::Context->preference('item-level_itypes')) {
360
            unless (C4::Context->preference('item-level_itypes')) {
351
                $item->{itype} = $biblioitem->{itemtype};
361
                $item->{itype} = $biblioitem->{itemtype};
352
            }
362
            }
Lines 444-456 foreach my $biblionumber (@biblionumbers) { Link Here
444
454
445
            $item->{'holdallowed'} = $branchitemrule->{'holdallowed'};
455
            $item->{'holdallowed'} = $branchitemrule->{'holdallowed'};
446
456
457
            my $can_item_be_reserved = CanItemBeReserved( $borrowerinfo->{borrowernumber}, $itemnumber );
458
            $item->{not_holdable} = $can_item_be_reserved unless ( $can_item_be_reserved eq 'OK' );
459
447
            if (
460
            if (
448
                   !$item->{cantreserve}
461
                   !$item->{cantreserve}
449
                && !$exceeded_maxreserves
462
                && !$exceeded_maxreserves
450
                && IsAvailableForItemLevelRequest($item, $borrowerinfo)
463
                && IsAvailableForItemLevelRequest($item, $borrowerinfo)
451
                && CanItemBeReserved(
464
                && $can_item_be_reserved eq 'OK'
452
                    $borrowerinfo->{borrowernumber}, $itemnumber
453
                ) eq 'OK'
454
              )
465
              )
455
            {
466
            {
456
                $item->{available} = 1;
467
                $item->{available} = 1;
457
- 

Return to bug 14695