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

(-)a/C4/Reserves.pm (-2 / +34 lines)
Lines 461-466 See CanItemBeReserved() for possible return values. Link Here
461
sub CanBookBeReserved{
461
sub CanBookBeReserved{
462
    my ($borrowernumber, $biblionumber) = @_;
462
    my ($borrowernumber, $biblionumber) = @_;
463
463
464
    # Check if borrower already has reserved the same biblio.
465
    my @reserves = GetReservesFromBorrowernumber($borrowernumber);
466
    foreach my $reserve (@reserves) {
467
        if ($reserve->{biblionumber} == $biblionumber) {
468
            return 'alreadyReserved';
469
        }
470
    }
471
472
    # Check if borrower has reached the maximum number of holds allowed
473
    my $maxreserves = C4::Context->preference('maxreserves');
474
    if ($maxreserves && scalar(@reserves) >= $maxreserves) {
475
        return 'tooManyReserves';
476
    }
477
464
    my $items = GetItemnumbersForBiblio($biblionumber);
478
    my $items = GetItemnumbersForBiblio($biblionumber);
465
    #get items linked via host records
479
    #get items linked via host records
466
    my @hostitems = get_hostitemnumbers_of($biblionumber);
480
    my @hostitems = get_hostitemnumbers_of($biblionumber);
Lines 473-479 sub CanBookBeReserved{ Link Here
473
        $canReserve = CanItemBeReserved( $borrowernumber, $item );
487
        $canReserve = CanItemBeReserved( $borrowernumber, $item );
474
        return 'OK' if $canReserve eq 'OK';
488
        return 'OK' if $canReserve eq 'OK';
475
    }
489
    }
476
    return $canReserve;
490
491
    return 'none_available';
477
}
492
}
478
493
479
=head2 CanItemBeReserved
494
=head2 CanItemBeReserved
Lines 486-491 sub CanBookBeReserved{ Link Here
486
         damaged,         if the Item is damaged.
501
         damaged,         if the Item is damaged.
487
         cannotReserveFromOtherBranches, if syspref 'canreservefromotherbranches' is OK.
502
         cannotReserveFromOtherBranches, if syspref 'canreservefromotherbranches' is OK.
488
         tooManyReserves, if the borrower has exceeded his maximum reserve amount.
503
         tooManyReserves, if the borrower has exceeded his maximum reserve amount.
504
         alreadyReserved, if the borrower has already reserved this item.
489
505
490
=cut
506
=cut
491
507
Lines 495-501 sub CanItemBeReserved{ Link Here
495
    my $dbh             = C4::Context->dbh;
511
    my $dbh             = C4::Context->dbh;
496
    my $ruleitemtype; # itemtype of the matching issuing rule
512
    my $ruleitemtype; # itemtype of the matching issuing rule
497
    my $allowedreserves = 0;
513
    my $allowedreserves = 0;
498
            
514
499
    # we retrieve borrowers and items informations #
515
    # we retrieve borrowers and items informations #
500
    # item->{itype} will come for biblioitems if necessery
516
    # item->{itype} will come for biblioitems if necessery
501
    my $item = GetItem($itemnumber);
517
    my $item = GetItem($itemnumber);
Lines 509-514 sub CanItemBeReserved{ Link Here
509
    my ($ageRestriction, $daysToAgeRestriction) = C4::Circulation::GetAgeRestriction( $biblioData->{agerestriction}, $borrower );
525
    my ($ageRestriction, $daysToAgeRestriction) = C4::Circulation::GetAgeRestriction( $biblioData->{agerestriction}, $borrower );
510
    return 'ageRestricted' if $daysToAgeRestriction && $daysToAgeRestriction > 0;
526
    return 'ageRestricted' if $daysToAgeRestriction && $daysToAgeRestriction > 0;
511
527
528
    # Check if borrower already has reserved the same item or biblio.
529
    my @reserves = GetReservesFromBorrowernumber($borrowernumber);
530
    foreach my $reserve (@reserves) {
531
        if (   $reserve->{itemnumber} == $itemnumber
532
            or $reserve->{biblionumber} == $item->{biblionumber} )
533
        {
534
            return 'alreadyReserved';
535
        }
536
    }
537
538
    # Check if borrower has reached the maximum number of holds allowed
539
    my $maxreserves = C4::Context->preference('maxreserves');
540
    if ($maxreserves && scalar(@reserves) >= $maxreserves) {
541
        return 'tooManyReserves';
542
    }
543
512
    my $controlbranch = C4::Context->preference('ReservesControlBranch');
544
    my $controlbranch = C4::Context->preference('ReservesControlBranch');
513
    my $itemtypefield = C4::Context->preference('item-level_itypes') ? "itype" : "itemtype";
545
    my $itemtypefield = C4::Context->preference('item-level_itypes') ? "itype" : "itemtype";
514
546
(-)a/koha-tmpl/intranet-tmpl/prog/en/modules/reserve/request.tt (-2 / +2 lines)
Lines 247-253 function checkMultiHold() { Link Here
247
    </form>
247
    </form>
248
  [% ELSE %]
248
  [% ELSE %]
249
249
250
[% IF ( maxreserves || alreadyreserved || none_available || alreadypossession || ageRestricted ) %]
250
[% IF ( maxreserves || alreadyReserved || none_available || alreadypossession || ageRestricted ) %]
251
    <div class="dialog alert">
251
    <div class="dialog alert">
252
252
253
    [% UNLESS ( multi_hold ) %]
253
    [% UNLESS ( multi_hold ) %]
Lines 259-265 function checkMultiHold() { Link Here
259
        [% IF ( ageRestricted ) %]
259
        [% IF ( ageRestricted ) %]
260
          <li><strong>Age restricted</strong></li>
260
          <li><strong>Age restricted</strong></li>
261
        [% END %]
261
        [% END %]
262
        [% IF ( alreadyreserved ) %]
262
        [% IF ( alreadyReserved ) %]
263
          <li><a href="/cgi-bin/koha/members/moremember.pl?borrowernumber=[% borrowernumber %]">[% borrowerfirstname %] [% borrowersurname %]</a> <strong>already has a hold</strong> on this item </li>
263
          <li><a href="/cgi-bin/koha/members/moremember.pl?borrowernumber=[% borrowernumber %]">[% borrowerfirstname %] [% borrowersurname %]</a> <strong>already has a hold</strong> on this item </li>
264
        [% END %]
264
        [% END %]
265
        [% IF ( none_available ) %]
265
        [% IF ( none_available ) %]
(-)a/reserve/request.pl (-15 / +4 lines)
Lines 199-205 foreach my $biblionumber (@biblionumbers) { Link Here
199
    elsif ( $canReserve eq 'tooManyReserves' ) {
199
    elsif ( $canReserve eq 'tooManyReserves' ) {
200
        $maxreserves = 1;
200
        $maxreserves = 1;
201
    }
201
    }
202
    elsif ( $canReserve eq 'ageRestricted' ) {
202
    elsif ( grep { $canReserve eq $_ }
203
        (qw(ageRestricted alreadyReserved none_available)) )
204
    {
203
        $template->param( $canReserve => 1 );
205
        $template->param( $canReserve => 1 );
204
        $biblioloopiter{$canReserve} = 1;
206
        $biblioloopiter{$canReserve} = 1;
205
    }
207
    }
Lines 216-242 foreach my $biblionumber (@biblionumbers) { Link Here
216
    my $reserves = GetReservesFromBiblionumber({ biblionumber => $biblionumber, all_dates => 1 });
218
    my $reserves = GetReservesFromBiblionumber({ biblionumber => $biblionumber, all_dates => 1 });
217
    my $count = scalar( @$reserves );
219
    my $count = scalar( @$reserves );
218
    my $totalcount = $count;
220
    my $totalcount = $count;
219
    my $holds_count = 0;
220
    my $alreadyreserved = 0;
221
221
222
    foreach my $res (@$reserves) {
222
    foreach my $res (@$reserves) {
223
        if ( defined $res->{found} ) { # found can be 'W' or 'T'
223
        if ( defined $res->{found} ) { # found can be 'W' or 'T'
224
            $count--;
224
            $count--;
225
        }
225
        }
226
227
        if ( defined $borrowerinfo && defined($borrowerinfo->{borrowernumber}) && ($borrowerinfo->{borrowernumber} eq $res->{borrowernumber}) ) {
228
            $holds_count++;
229
        }
230
    }
231
232
    if ( $holds_count ) {
233
            $alreadyreserved = 1;
234
            $biblioloopiter{warn} = 1;
235
            $biblioloopiter{alreadyres} = 1;
236
    }
226
    }
237
227
238
    $template->param(
228
    $template->param(
239
        alreadyreserved => $alreadyreserved,
240
        alreadypossession => $alreadypossession,
229
        alreadypossession => $alreadypossession,
241
    );
230
    );
242
231
Lines 420-426 foreach my $biblionumber (@biblionumbers) { Link Here
420
                     $borrowerinfo->{'branchcode'} ne $item->{'homebranch'} ) ) {
409
                     $borrowerinfo->{'branchcode'} ne $item->{'homebranch'} ) ) {
421
                $policy_holdallowed = 0;
410
                $policy_holdallowed = 0;
422
            }
411
            }
423
            
412
424
            if (
413
            if (
425
                   $policy_holdallowed
414
                   $policy_holdallowed
426
                && !$item->{cantreserve}
415
                && !$item->{cantreserve}
(-)a/t/db_dependent/Holds.t (-17 / +39 lines)
Lines 6-12 use t::lib::Mocks; Link Here
6
use C4::Context;
6
use C4::Context;
7
use C4::Branch;
7
use C4::Branch;
8
8
9
use Test::More tests => 38;
9
use Test::More tests => 41;
10
use MARC::Record;
10
use MARC::Record;
11
use C4::Biblio;
11
use C4::Biblio;
12
use C4::Items;
12
use C4::Items;
Lines 56-63 foreach (1..$borrowers_count) { Link Here
56
56
57
my $biblionumber   = $bibnum;
57
my $biblionumber   = $bibnum;
58
58
59
my @branches = GetBranchesLoop();
59
my $branches = GetBranchesLoop();
60
my $branch = $branches[0][0]{value};
60
my $branch = $branches->[0]{value};
61
61
62
# Create five item level holds
62
# Create five item level holds
63
foreach my $borrowernumber ( @borrowernumbers ) {
63
foreach my $borrowernumber ( @borrowernumbers ) {
Lines 205-211 $dbh->do( Link Here
205
);
205
);
206
206
207
# make sure some basic sysprefs are set
207
# make sure some basic sysprefs are set
208
t::lib::Mocks::mock_preference('ReservesControlBranch', 'homebranch');
208
t::lib::Mocks::mock_preference('ReservesControlBranch', 'ItemHomeLibrary');
209
t::lib::Mocks::mock_preference('item-level_itypes', 1);
209
t::lib::Mocks::mock_preference('item-level_itypes', 1);
210
210
211
# if IndependentBranches is OFF, a CPL patron can reserve an MPL item
211
# if IndependentBranches is OFF, a CPL patron can reserve an MPL item
Lines 293-302 ModReserve({ reserve_id => $reserveid2, rank => 'del' }); Link Here
293
$reserve3 = GetReserve( $reserveid3 );
293
$reserve3 = GetReserve( $reserveid3 );
294
is( $reserve3->{priority}, 1, "After ModReserve, the 3rd reserve becomes the first on the waiting list" );
294
is( $reserve3->{priority}, 1, "After ModReserve, the 3rd reserve becomes the first on the waiting list" );
295
295
296
ok( defined( ( CheckReserves($itemnumber) )[1] ), "Hold can be trapped for damaged item with AllowHoldsOnDamagedItems enabled" );
297
296
ModItem({ damaged => 1 }, $item_bibnum, $itemnumber);
298
ModItem({ damaged => 1 }, $item_bibnum, $itemnumber);
299
CancelReserve({reserve_id => $reserveid3});
297
C4::Context->set_preference( 'AllowHoldsOnDamagedItems', 1 );
300
C4::Context->set_preference( 'AllowHoldsOnDamagedItems', 1 );
298
ok( CanItemBeReserved( $borrowernumbers[0], $itemnumber) eq 'OK', "Patron can reserve damaged item with AllowHoldsOnDamagedItems enabled" );
301
ok( CanItemBeReserved( $borrowernumbers[0], $itemnumber) eq 'OK', "Patron can reserve damaged item with AllowHoldsOnDamagedItems enabled" );
299
ok( defined( ( CheckReserves($itemnumber) )[1] ), "Hold can be trapped for damaged item with AllowHoldsOnDamagedItems enabled" );
300
C4::Context->set_preference( 'AllowHoldsOnDamagedItems', 0 );
302
C4::Context->set_preference( 'AllowHoldsOnDamagedItems', 0 );
301
ok( CanItemBeReserved( $borrowernumbers[0], $itemnumber) eq 'damaged', "Patron cannot reserve damaged item with AllowHoldsOnDamagedItems disabled" );
303
ok( CanItemBeReserved( $borrowernumbers[0], $itemnumber) eq 'damaged', "Patron cannot reserve damaged item with AllowHoldsOnDamagedItems disabled" );
302
ok( !defined( ( CheckReserves($itemnumber) )[1] ), "Hold cannot be trapped for damaged item with AllowHoldsOnDamagedItems disabled" );
304
ok( !defined( ( CheckReserves($itemnumber) )[1] ), "Hold cannot be trapped for damaged item with AllowHoldsOnDamagedItems disabled" );
Lines 312-334 AddReserve( Link Here
312
    '',
314
    '',
313
    1,
315
    1,
314
);
316
);
317
my ($bibnum2, $title2, $bibitemnum2) = create_helper_biblio('CANNOT');
318
my ($item_bibnum2, $item_bibitemnum2, $itemnumber2) = AddItem({ homebranch => $branch, holdingbranch => $branch, itype => 'CANNOT' } , $bibnum2);
315
ok(
319
ok(
316
    CanItemBeReserved( $borrowernumbers[0], $itemnumber) eq 'tooManyReserves',
320
    CanItemBeReserved( $borrowernumbers[0], $itemnumber2) eq 'tooManyReserves',
317
    "cannot request item if policy that matches on item-level item type forbids it"
321
    "cannot request item if policy that matches on item-level item type forbids it"
318
);
322
);
319
ModItem({ itype => 'CAN' }, $item_bibnum, $itemnumber);
323
ModItem({ itype => 'CAN' }, $item_bibnum2, $itemnumber2);
320
ok(
324
ok(
321
    CanItemBeReserved( $borrowernumbers[0], $itemnumber) eq 'OK',
325
    CanItemBeReserved( $borrowernumbers[0], $itemnumber2) eq 'OK',
322
    "can request item if policy that matches on item type allows it"
326
    "can request item if policy that matches on item type allows it"
323
);
327
);
324
328
325
t::lib::Mocks::mock_preference('item-level_itypes', 0);
329
t::lib::Mocks::mock_preference('item-level_itypes', 0);
326
ModItem({ itype => undef }, $item_bibnum, $itemnumber);
330
ModItem({ itype => undef }, $item_bibnum2, $itemnumber2);
327
ok(
331
ok(
328
    CanItemBeReserved( $borrowernumbers[0], $itemnumber) eq 'tooManyReserves',
332
    CanItemBeReserved( $borrowernumbers[0], $itemnumber2) eq 'tooManyReserves',
329
    "cannot request item if policy that matches on bib-level item type forbids it (bug 9532)"
333
    "cannot request item if policy that matches on bib-level item type forbids it (bug 9532)"
330
);
334
);
331
335
336
is(CanBookBeReserved($borrowernumbers[0], $bibnum), 'alreadyReserved');
337
338
C4::Context->set_preference('maxreserves', 1);
339
ok(CanBookBeReserved($borrowernumbers[0], $biblionumber) eq 'tooManyReserves');
340
341
C4::Context->set_preference('maxreserves', 0);
342
t::lib::Mocks::mock_preference('IndependentBranches', 1);
343
t::lib::Mocks::mock_preference('canreservefromotherbranches', 0);
344
ok(CanBookBeReserved($borrowernumbers[0], $foreign_bibnum) eq 'none_available');
345
332
# Test CancelExpiredReserves
346
# Test CancelExpiredReserves
333
C4::Context->set_preference('ExpireReservesMaxPickUpDelay', 1);
347
C4::Context->set_preference('ExpireReservesMaxPickUpDelay', 1);
334
C4::Context->set_preference('ReservesMaxPickUpDelay', 1);
348
C4::Context->set_preference('ReservesMaxPickUpDelay', 1);
Lines 370-379 sub create_helper_biblio { Link Here
370
    my $itemtype = shift;
384
    my $itemtype = shift;
371
    my $bib = MARC::Record->new();
385
    my $bib = MARC::Record->new();
372
    my $title = 'Silence in the library';
386
    my $title = 'Silence in the library';
373
    $bib->append_fields(
387
    if (C4::Context->preference('marcflavour') eq 'UNIMARC') {
374
        MARC::Field->new('100', ' ', ' ', a => 'Moffat, Steven'),
388
        $bib->append_fields(
375
        MARC::Field->new('245', ' ', ' ', a => $title),
389
            MARC::Field->new('700', ' ', '0', a => 'Moffat, Steven'),
376
        MARC::Field->new('942', ' ', ' ', c => $itemtype),
390
            MARC::Field->new('200', ' ', ' ', a => $title),
377
    );
391
            MARC::Field->new('099', ' ', ' ', t => $itemtype),
378
    return ($bibnum, $title, $bibitemnum) = AddBiblio($bib, '');
392
        );
393
    } else {
394
        $bib->append_fields(
395
            MARC::Field->new('100', ' ', ' ', a => 'Moffat, Steven'),
396
            MARC::Field->new('245', ' ', ' ', a => $title),
397
            MARC::Field->new('942', ' ', ' ', c => $itemtype),
398
        );
399
    }
400
401
    return AddBiblio($bib, '');
379
}
402
}
380
- 

Return to bug 11999