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

(-)a/C4/Reserves.pm (-33 / +104 lines)
Lines 41-46 use Koha::Calendar; Link Here
41
use Koha::Database;
41
use Koha::Database;
42
use Koha::Hold;
42
use Koha::Hold;
43
use Koha::Holds;
43
use Koha::Holds;
44
use Koha::Borrowers;
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 1390-1396 sub ModReserveMinusPriority { Link Here
1390
    my $dbh   = C4::Context->dbh;
1391
    my $dbh   = C4::Context->dbh;
1391
    my $query = "
1392
    my $query = "
1392
        UPDATE reserves
1393
        UPDATE reserves
1393
        SET    priority = 0 , itemnumber = ? 
1394
        SET    priority = 0 , itemnumber = ?
1394
        WHERE  reserve_id = ?
1395
        WHERE  reserve_id = ?
1395
    ";
1396
    ";
1396
    my $sth_upd = $dbh->prepare($query);
1397
    my $sth_upd = $dbh->prepare($query);
Lines 1605-1611 sub ToggleLowestPriority { Link Here
1605
1606
1606
    my $sth = $dbh->prepare( "UPDATE reserves SET lowestPriority = NOT lowestPriority WHERE reserve_id = ?");
1607
    my $sth = $dbh->prepare( "UPDATE reserves SET lowestPriority = NOT lowestPriority WHERE reserve_id = ?");
1607
    $sth->execute( $reserve_id );
1608
    $sth->execute( $reserve_id );
1608
    
1609
1609
    _FixPriority({ reserve_id => $reserve_id, rank => '999999' });
1610
    _FixPriority({ reserve_id => $reserve_id, rank => '999999' });
1610
}
1611
}
1611
1612
Lines 1815-1821 sub _FixPriority { Link Here
1815
            $priority[$j]->{'reserve_id'}
1816
            $priority[$j]->{'reserve_id'}
1816
        );
1817
        );
1817
    }
1818
    }
1818
    
1819
1819
    $sth = $dbh->prepare( "SELECT reserve_id FROM reserves WHERE lowestPriority = 1 ORDER BY priority" );
1820
    $sth = $dbh->prepare( "SELECT reserve_id FROM reserves WHERE lowestPriority = 1 ORDER BY priority" );
1820
    $sth->execute();
1821
    $sth->execute();
1821
1822
Lines 2050-2056 sub _koha_notify_reserve { Link Here
2050
    if (! $notification_sent) {
2051
    if (! $notification_sent) {
2051
        &$send_notification('print', 'HOLD');
2052
        &$send_notification('print', 'HOLD');
2052
    }
2053
    }
2053
    
2054
2054
}
2055
}
2055
2056
2056
=head2 _ShiftPriorityByDateAndPriority
2057
=head2 _ShiftPriorityByDateAndPriority
Lines 2479-2484 sub IsItemOnHoldAndFound { Link Here
2479
    return $found;
2480
    return $found;
2480
}
2481
}
2481
2482
2483
=head2 GetMaxPatronHoldsForRecord
2484
2485
my $holds_per_record = ReservesControlBranch( $borrowernumber, $biblionumber );
2486
2487
For multiple holds on a given record for a given patron, the max
2488
number of record level holds that a patron can be placed is the highest
2489
value of the holds_per_record rule for each item if the record for that
2490
patron. This subroutine finds and returns the highest holds_per_record
2491
rule value for a given patron id and record id.
2492
2493
=cut
2494
2495
sub GetMaxPatronHoldsForRecord {
2496
    my ( $borrowernumber, $biblionumber ) = @_;
2497
2498
    my $patron = Koha::Borrowers->find($borrowernumber);
2499
    my @items = Koha::Items->search( { biblionumber => $biblionumber } );
2500
2501
    my $controlbranch = C4::Context->preference('ReservesControlBranch');
2502
2503
    my $categorycode = $patron->categorycode;
2504
    my $branchcode;
2505
    $branchcode = $patron->branchcode if ( $controlbranch eq "PatronLibrary" );
2506
2507
    my $max = 0;
2508
    foreach my $item (@items) {
2509
        my $itemtype = $item->effective_itemtype();
2510
2511
        $branchcode = $item->homebranch if ( $controlbranch eq "ItemHomeLibrary" );
2512
2513
        my $rule = GetHoldRule( $categorycode, $itemtype, $branchcode );
2514
        my $holds_per_record = $rule ? $rule->{holds_per_record} : 0;
2515
        $max = $holds_per_record if $holds_per_record > $max;
2516
    }
2517
2518
    return $max;
2519
}
2520
2521
=head2 GetHoldRule
2522
2523
my $rule = GetHoldRule( $categorycode, $itemtype, $branchcode );
2524
2525
Returns the matching hold related issuingrule fields for a given
2526
patron category, itemtype, and library.
2527
2528
=cut
2529
2530
sub GetHoldRule {
2531
    my ( $categorycode, $itemtype, $branchcode ) = @_;
2532
2533
    my $dbh = C4::Context->dbh;
2534
2535
    my $sth = $dbh->prepare(
2536
        q{
2537
         SELECT categorycode, itemtype, branchcode, reservesallowed, holds_per_record
2538
           FROM issuingrules
2539
          WHERE (categorycode in (?,'*') )
2540
            AND (itemtype IN (?,'*'))
2541
            AND (branchcode IN (?,'*'))
2542
       ORDER BY categorycode DESC,
2543
                itemtype     DESC,
2544
                branchcode   DESC
2545
        }
2546
    );
2547
2548
    $sth->execute( $categorycode, $itemtype, $branchcode );
2549
2550
    return $sth->fetchrow_hashref();
2551
}
2552
2482
=head1 AUTHOR
2553
=head1 AUTHOR
2483
2554
2484
Koha Development Team <http://koha-community.org/>
2555
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 307-313 function checkMultiHold() { Link Here
307
        [% IF ( exceeded_maxreserves ) %]
298
        [% IF ( exceeded_maxreserves ) %]
308
          <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>
299
          <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>
309
        [% ELSIF ( alreadypossession ) %]
300
        [% ELSIF ( alreadypossession ) %]
310
          <li> <a href="/cgi-bin/koha/members/moremember.pl?borrowernumber=[% borrowernumber %]">[% borrowerfirstname %] [% borrowersurname %]</a> <strong>is already in possession</strong> of one item</li>
301
          <li> <a href="/cgi-bin/koha/members/moremember.pl?borrowernumber=[% borrowernumber %]">[% borrowerfirstname %] [% borrowersurname %]</a> <strong>is already in possession</strong> of one item</lie
311
        [% ELSIF ( alreadyreserved ) %]
302
        [% ELSIF ( alreadyreserved ) %]
312
          <li><a href="/cgi-bin/koha/members/moremember.pl?borrowernumber=[% borrowernumber %]">[% borrowerfirstname %] [% borrowersurname %]</a> <strong>already has a hold</strong> on this item </li>
303
          <li><a href="/cgi-bin/koha/members/moremember.pl?borrowernumber=[% borrowernumber %]">[% borrowerfirstname %] [% borrowersurname %]</a> <strong>already has a hold</strong> on this item </li>
313
        [% ELSIF ( ageRestricted ) %]
304
        [% ELSIF ( ageRestricted ) %]
Lines 424-434 function checkMultiHold() { Link Here
424
	</li>
415
	</li>
425
416
426
        [% UNLESS ( multi_hold ) %]
417
        [% UNLESS ( multi_hold ) %]
427
          <li> <label for="requestany">Place a hold on the next available item </label>
418
          <li> <label for="requestany">Hold next available item </label>
428
               <input type="checkbox" id="requestany" name="request" checked="checked" value="Any" />
419
               [% IF force_hold_level == 'item' %]
420
                   <input type="checkbox" id="requestany" name="request" disabled="true" />
421
               [% ELSIF force_hold_level == 'record' %]
422
                   <input type="checkbox" id="requestany" checked="checked" value="Any" disabled="true"/>
423
                   <input type="hidden" name="request" value="Any"/>
424
               [% ELSE %]
425
                   <input type="checkbox" id="requestany" name="request" checked="checked" value="Any" />
426
                [% END %]
429
               <input type="hidden" name="biblioitem" value="[% biblioitemnumber %]" />
427
               <input type="hidden" name="biblioitem" value="[% biblioitemnumber %]" />
430
               <input type="hidden" name="alreadyreserved" value="[% alreadyreserved %]" />
428
               <input type="hidden" name="alreadyreserved" value="[% alreadyreserved %]" />
431
          </li>
429
          </li>
430
431
          [% IF max_holds_for_record > 1 %]
432
              [% SET count = 1 %]
433
              <li>
434
                   <label for="holds_to_place_count">Holds to place (count)</label>
435
                   <select name="holds_to_place_count" id="holds_to_place_count">
436
                   [% WHILE count <= max_holds_for_record %]
437
                        <option value="[% count %]">[% count %]</option>
438
                        [% SET count = count + 1 %]
439
                   [% END %]
440
441
                   </select>
442
              </li>
443
            [% ELSE %]
444
                <input type="hidden" name="holds_to_place_count" value="1";
445
            [% END %]
432
        [% END %]
446
        [% END %]
433
447
434
</ol>
448
</ol>
Lines 453-459 function checkMultiHold() { Link Here
453
            [% IF ( bibitemloo.publicationyear ) %]<li><span class="label">Publication year:</span> [% bibitemloo.publicationyear %]</li>[% END %]
467
            [% IF ( bibitemloo.publicationyear ) %]<li><span class="label">Publication year:</span> [% bibitemloo.publicationyear %]</li>[% END %]
454
          </ol>
468
          </ol>
455
469
456
        <h2 style="padding: 0 1em;">Place a hold on a specific item</h2>
470
        <h2 style="padding: 0 1em;">
471
            Place a hold on a specific item
472
            [% IF bibitemloo.force_hold_level == 'item' %]
473
                <span class="error"><i>(Required)</i></span>
474
            [% END %]
475
        </h2>
457
        <table id="requestspecific">
476
        <table id="requestspecific">
458
            <thead>
477
            <thead>
459
                <tr>
478
                <tr>
Lines 473-490 function checkMultiHold() { Link Here
473
                </tr>
492
                </tr>
474
            </thead>
493
            </thead>
475
            <tbody>
494
            <tbody>
495
            [% SET selected = 0 %]
476
            [% FOREACH itemloo IN bibitemloo.itemloop %]
496
            [% FOREACH itemloo IN bibitemloo.itemloop %]
477
            [% UNLESS ( itemloo.hide ) %]
497
            [% UNLESS ( itemloo.hide ) %]
478
                <tr class="[% itemloo.backgroundcolor %]">
498
                <tr class="[% itemloo.backgroundcolor %]">
479
                    <td>
499
                    <td>
480
                [% IF ( itemloo.available ) %]
500
                [% IF itemloo.force_hold_level == 'record' # Patron has placed a record level hold previously for this record %]
501
                    <span class="error">
502
                        <i class="fa fa-times fa-lg" alt="Cannot be put on hold"></i>
503
                        Hold must be record level
504
                    </span>
505
                [% ELSIF ( itemloo.available ) %]
481
                    <input type="radio" name="checkitem" value="[% itemloo.itemnumber %]" />
506
                    <input type="radio" name="checkitem" value="[% itemloo.itemnumber %]" />
482
                [% ELSIF ( itemloo.override ) %]
507
                [% ELSIF ( itemloo.override ) %]
483
                    <input type="radio" name="checkitem" class="needsoverride" value="[% itemloo.itemnumber %]" />
508
                    <input type="radio" name="checkitem" class="needsoverride" value="[% itemloo.itemnumber %]" />
484
                    <img src="[% interface %]/[% theme %]/img/famfamfam/silk/error.png" alt="Requires override of hold policy" />
509
                    <i class="fa fa-exclamation-triangle fa-lg" style="color:gold" alt="Requires override of hold policy"/></i>
485
                [% ELSE %]
510
                [% ELSE %]
486
                    <input disabled="disabled" type="radio" name="checkitem" value="[% itemloo.itemnumber %]" />
511
                    <span class="error">
487
                    <img src="[% interface %]/[% theme %]/img/famfamfam/silk/cross.png" alt="Cannot be put on hold" />
512
                        <i class="fa fa-times fa-lg" alt="Cannot be put on hold"></i>
513
                        [% IF itemloo.not_holdable %]
514
                            [% IF itemloo.not_holdable == 'damaged' %]
515
                                Item damaged
516
                            [% ELSIF itemloo.not_holdable == 'ageRestricted' %]
517
                                Age restricted
518
                            [% ELSIF itemloo.not_holdable == 'tooManyHoldsForThisRecord' %]
519
                                Exceeded max holds per record
520
                            [% ELSIF itemloo.not_holdable == 'tooManyReserves' %]
521
                                Too many holds
522
                            [% ELSIF itemloo.not_holdable == 'notReservable' %]
523
                                Not holdable
524
                            [% ELSIF itemloo.not_holdable == 'cannotReserveFromOtherBranches' %]
525
                                Patron is from different library
526
                            [% ELSIF itemloo.not_holdable == 'itemAlreadyOnHold' %]
527
                                Patron already has hold for this item
528
                            [% ELSE %]
529
                                [% itemloo.not_holdable %]
530
                            [% END %]
531
                        [% END %]
532
                    </span>
488
                [% END %]
533
                [% END %]
489
                    </td>
534
                    </td>
490
                [% IF ( item_level_itypes ) %]
535
                [% IF ( item_level_itypes ) %]
(-)a/opac/opac-reserve.pl (-20 / +24 lines)
Lines 352-368 unless ( $noreserves ) { Link Here
352
    }
352
    }
353
}
353
}
354
354
355
foreach my $res (@reserves) {
356
    foreach my $biblionumber (@biblionumbers) {
357
        if ( $res->{'biblionumber'} == $biblionumber && $res->{'borrowernumber'} == $borrowernumber) {
358
#            $template->param( message => 1 );
359
#            $noreserves = 1;
360
#            $template->param( already_reserved => 1 );
361
            $biblioDataHash{$biblionumber}->{already_reserved} = 1;
362
        }
363
    }
364
}
365
366
unless ($noreserves) {
355
unless ($noreserves) {
367
    $template->param( select_item_types => 1 );
356
    $template->param( select_item_types => 1 );
368
}
357
}
Lines 460-468 foreach my $biblioNum (@biblionumbers) { Link Here
460
449
461
        # the item could be reserved for this borrower vi a host record, flag this
450
        # the item could be reserved for this borrower vi a host record, flag this
462
        $reservedfor //= '';
451
        $reservedfor //= '';
463
        if ($reservedfor eq $borrowernumber){
464
            $itemLoopIter->{already_reserved} = 1;
465
        }
466
452
467
        if ( defined $reservedate ) {
453
        if ( defined $reservedate ) {
468
            $itemLoopIter->{backgroundcolor} = 'reserved';
454
            $itemLoopIter->{backgroundcolor} = 'reserved';
Lines 507-518 foreach my $biblioNum (@biblionumbers) { Link Here
507
            $itemLoopIter->{nocancel} = 1;
493
            $itemLoopIter->{nocancel} = 1;
508
        }
494
        }
509
495
510
	# if the items belongs to a host record, show link to host record
496
        # if the items belongs to a host record, show link to host record
511
	if ($itemInfo->{biblionumber} ne $biblioNum){
497
        if ( $itemInfo->{biblionumber} ne $biblioNum ) {
512
		$biblioLoopIter{hostitemsflag} = 1;
498
            $biblioLoopIter{hostitemsflag}    = 1;
513
		$itemLoopIter->{hostbiblionumber} = $itemInfo->{biblionumber};
499
            $itemLoopIter->{hostbiblionumber} = $itemInfo->{biblionumber};
514
		$itemLoopIter->{hosttitle} = GetBiblioData($itemInfo->{biblionumber})->{title};
500
            $itemLoopIter->{hosttitle}        = GetBiblioData( $itemInfo->{biblionumber} )->{title};
515
	}
501
        }
516
502
517
        # If there is no loan, return and transfer, we show a checkbox.
503
        # If there is no loan, return and transfer, we show a checkbox.
518
        $itemLoopIter->{notforloan} = $itemLoopIter->{notforloan} || 0;
504
        $itemLoopIter->{notforloan} = $itemLoopIter->{notforloan} || 0;
Lines 561-566 foreach my $biblioNum (@biblionumbers) { Link Here
561
547
562
    $biblioLoopIter{holdable} &&= CanBookBeReserved($borrowernumber,$biblioNum) eq 'OK';
548
    $biblioLoopIter{holdable} &&= CanBookBeReserved($borrowernumber,$biblioNum) eq 'OK';
563
549
550
    # For multiple holds per record, if a patron has previously placed a hold,
551
    # the patron can only place more holds of the same type. That is, if the
552
    # patron placed a record level hold, all the holds the patron places must
553
    # be record level. If the patron placed an item level hold, all holds
554
    # the patron places must be item level
555
    my $forced_hold_level = Koha::Holds->search(
556
        {
557
            borrowernumber => $borrowernumber,
558
            biblionumber   => $biblioNum,
559
            found          => undef,
560
        }
561
    )->forced_hold_level();
562
    if ($forced_hold_level) {
563
        $biblioLoopIter{force_hold}   = 1 if $forced_hold_level eq 'item';
564
        $biblioLoopIter{itemholdable} = 0 if $forced_hold_level eq 'record';
565
    }
566
567
564
    push @$biblioLoop, \%biblioLoopIter;
568
    push @$biblioLoop, \%biblioLoopIter;
565
569
566
    $anyholdable = 1 if $biblioLoopIter{holdable};
570
    $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 233-270 foreach my $biblionumber (@biblionumbers) { Link Here
233
        $biblioloopiter{$canReserve} = 1;
233
        $biblioloopiter{$canReserve} = 1;
234
    }
234
    }
235
235
236
    my $alreadypossession;
236
    my $force_hold_level;
237
    if (not C4::Context->preference('AllowHoldsOnPatronsPossessions') and CheckIfIssuedToPatron($borrowerinfo->{borrowernumber},$biblionumber)) {
237
    if ( $borrowerinfo->{borrowernumber} ) {
238
        $alreadypossession = 1;
238
        # For multiple holds per record, if a patron has previously placed a hold,
239
        # the patron can only place more holds of the same type. That is, if the
240
        # patron placed a record level hold, all the holds the patron places must
241
        # be record level. If the patron placed an item level hold, all holds
242
        # the patron places must be item level
243
        my $holds = Koha::Holds->search(
244
            {
245
                borrowernumber => $borrowerinfo->{borrowernumber},
246
                biblionumber   => $biblionumber,
247
                found          => undef,
248
            }
249
        );
250
        $force_hold_level = $holds->forced_hold_level();
251
        $biblioloopiter{force_hold_level} = $force_hold_level;
252
        $template->param( force_hold_level => $force_hold_level );
253
254
        # For a librarian to be able to place multiple record holds for a patron for a record,
255
        # we must find out what the maximum number of holds they can place for the patron is
256
        my $max_holds_for_record = GetMaxPatronHoldsForRecord( $borrowerinfo->{borrowernumber}, $biblionumber );
257
        $max_holds_for_record = $max_holds_for_record - $holds->count();
258
        $biblioloopiter{max_holds_for_record} = $max_holds_for_record;
259
        $template->param( max_holds_for_record => $max_holds_for_record );
239
    }
260
    }
240
261
241
    # get existing reserves .....
262
    # Check to see if patron is allowed to place holds on records where the
242
    my $reserves = GetReservesFromBiblionumber({ biblionumber => $biblionumber, all_dates => 1 });
263
    # patron already has an item from that record checked out
243
    my $count = scalar( @$reserves );
264
    my $alreadypossession;
244
    my $totalcount = $count;
265
    if ( !C4::Context->preference('AllowHoldsOnPatronsPossessions')
245
    my $holds_count = 0;
266
        && CheckIfIssuedToPatron( $borrowerinfo->{borrowernumber}, $biblionumber ) )
246
    my $alreadyreserved = 0;
267
    {
247
268
        $template->param( alreadypossession => $alreadypossession, );
248
    foreach my $res (@$reserves) {
249
        if ( defined $res->{found} ) { # found can be 'W' or 'T'
250
            $count--;
251
        }
252
253
        if ( defined $borrowerinfo && defined($borrowerinfo->{borrowernumber}) && ($borrowerinfo->{borrowernumber} eq $res->{borrowernumber}) ) {
254
            $holds_count++;
255
        }
256
    }
269
    }
257
270
258
    if ( $holds_count ) {
259
            $alreadyreserved = 1;
260
            $biblioloopiter{warn} = 1;
261
            $biblioloopiter{alreadyres} = 1;
262
    }
263
271
264
    $template->param(
272
    my $count = Koha::Holds->search( { biblionumber => $biblionumber } )->count();
265
        alreadyreserved => $alreadyreserved,
273
    my $totalcount = $count;
266
        alreadypossession => $alreadypossession,
267
    );
268
274
269
    # FIXME think @optionloop, is maybe obsolete, or  must be switchable by a systeme preference fixed rank or not
275
    # FIXME think @optionloop, is maybe obsolete, or  must be switchable by a systeme preference fixed rank or not
270
    # make priorities options
276
    # make priorities options
Lines 326-331 foreach my $biblionumber (@biblionumbers) { Link Here
326
        my $num_override  = 0;
332
        my $num_override  = 0;
327
        my $hiddencount   = 0;
333
        my $hiddencount   = 0;
328
334
335
        $biblioitem->{force_hold_level} = $force_hold_level;
336
329
        if ( $biblioitem->{biblioitemnumber} ne $biblionumber ) {
337
        if ( $biblioitem->{biblioitemnumber} ne $biblionumber ) {
330
            $biblioitem->{hostitemsflag} = 1;
338
            $biblioitem->{hostitemsflag} = 1;
331
        }
339
        }
Lines 345-350 foreach my $biblionumber (@biblionumbers) { Link Here
345
        foreach my $itemnumber ( @{ $itemnumbers_of_biblioitem{$biblioitemnumber} } )    {
353
        foreach my $itemnumber ( @{ $itemnumbers_of_biblioitem{$biblioitemnumber} } )    {
346
            my $item = $iteminfos_of->{$itemnumber};
354
            my $item = $iteminfos_of->{$itemnumber};
347
355
356
            $item->{force_hold_level} = $force_hold_level;
357
348
            unless (C4::Context->preference('item-level_itypes')) {
358
            unless (C4::Context->preference('item-level_itypes')) {
349
                $item->{itype} = $biblioitem->{itemtype};
359
                $item->{itype} = $biblioitem->{itemtype};
350
            }
360
            }
Lines 442-454 foreach my $biblionumber (@biblionumbers) { Link Here
442
452
443
            $item->{'holdallowed'} = $branchitemrule->{'holdallowed'};
453
            $item->{'holdallowed'} = $branchitemrule->{'holdallowed'};
444
454
455
            my $can_item_be_reserved = CanItemBeReserved( $borrowerinfo->{borrowernumber}, $itemnumber );
456
            $item->{not_holdable} = $can_item_be_reserved unless ( $can_item_be_reserved eq 'OK' );
457
445
            if (
458
            if (
446
                   !$item->{cantreserve}
459
                   !$item->{cantreserve}
447
                && !$exceeded_maxreserves
460
                && !$exceeded_maxreserves
448
                && IsAvailableForItemLevelRequest($item, $borrowerinfo)
461
                && IsAvailableForItemLevelRequest($item, $borrowerinfo)
449
                && CanItemBeReserved(
462
                && $can_item_be_reserved eq 'OK'
450
                    $borrowerinfo->{borrowernumber}, $itemnumber
451
                ) eq 'OK'
452
              )
463
              )
453
            {
464
            {
454
                $item->{available} = 1;
465
                $item->{available} = 1;
455
- 

Return to bug 14695