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

(-)a/C4/Reserves.pm (+23 lines)
Lines 398-403 sub CanItemBeReserved { Link Here
398
        return 'tooManyReserves';
398
        return 'tooManyReserves';
399
    }
399
    }
400
400
401
    # Now we need to check hold limits by patron category
402
    my $schema = Koha::Database->new()->schema();
403
    my $rule = $schema->resultset('BranchBorrowerCircRule')->find(
404
        {
405
            branchcode   => $borrower->{branchcode},
406
            categorycode => $borrower->{categorycode},
407
        }
408
    );
409
    $rule ||= $schema->resultset('DefaultBorrowerCircRule')->find(
410
        {
411
            categorycode => $borrower->{categorycode}
412
        }
413
    );
414
    if ( $rule && defined $rule->max_holds ) {
415
        my $total_holds_count = Koha::Holds->search(
416
            {
417
                borrowernumber => $borrower->{borrowernumber}
418
            }
419
        )->count();
420
421
        return 'tooManyReserves' if $total_holds_count >= $rule->max_holds;
422
    }
423
401
    my $circ_control_branch =
424
    my $circ_control_branch =
402
      C4::Circulation::_GetCircControlBranch( $item->unblessed(), $borrower );
425
      C4::Circulation::_GetCircControlBranch( $item->unblessed(), $borrower );
403
    my $branchitemrule =
426
    my $branchitemrule =
(-)a/admin/smart-rules.pl (-14 / +21 lines)
Lines 254-263 elsif ($op eq "add-branch-cat") { Link Here
254
    my $categorycode  = $input->param('categorycode');
254
    my $categorycode  = $input->param('categorycode');
255
    my $maxissueqty   = $input->param('maxissueqty');
255
    my $maxissueqty   = $input->param('maxissueqty');
256
    my $maxonsiteissueqty = $input->param('maxonsiteissueqty');
256
    my $maxonsiteissueqty = $input->param('maxonsiteissueqty');
257
    my $max_holds = $input->param('max_holds');
257
    $maxissueqty =~ s/\s//g;
258
    $maxissueqty =~ s/\s//g;
258
    $maxissueqty = undef if $maxissueqty !~ /^\d+/;
259
    $maxissueqty = undef if $maxissueqty !~ /^\d+/;
259
    $maxonsiteissueqty =~ s/\s//g;
260
    $maxonsiteissueqty =~ s/\s//g;
260
    $maxonsiteissueqty = undef if $maxonsiteissueqty !~ /^\d+/;
261
    $maxonsiteissueqty = undef if $maxonsiteissueqty !~ /^\d+/;
262
    $max_holds =~ s/\s//g;
263
    $max_holds = undef if $max_holds !~ /^\d+/;
261
264
262
    if ($branch eq "*") {
265
    if ($branch eq "*") {
263
        if ($categorycode eq "*") {
266
        if ($categorycode eq "*") {
Lines 265-285 elsif ($op eq "add-branch-cat") { Link Here
265
                                            FROM default_circ_rules");
268
                                            FROM default_circ_rules");
266
            my $sth_insert = $dbh->prepare(q|
269
            my $sth_insert = $dbh->prepare(q|
267
                INSERT INTO default_circ_rules
270
                INSERT INTO default_circ_rules
268
                    (maxissueqty, maxonsiteissueqty)
271
                    (maxissueqty, maxonsiteissueqty, max_holds)
269
                    VALUES (?, ?)
272
                    VALUES (?, ?, ?)
270
            |);
273
            |);
271
            my $sth_update = $dbh->prepare(q|
274
            my $sth_update = $dbh->prepare(q|
272
                UPDATE default_circ_rules
275
                UPDATE default_circ_rules
273
                SET maxissueqty = ?,
276
                SET maxissueqty = ?,
274
                    maxonsiteissueqty = ?
277
                    maxonsiteissueqty = ?,
278
                    max_holds = ?
275
            |);
279
            |);
276
280
277
            $sth_search->execute();
281
            $sth_search->execute();
278
            my $res = $sth_search->fetchrow_hashref();
282
            my $res = $sth_search->fetchrow_hashref();
279
            if ($res->{total}) {
283
            if ($res->{total}) {
280
                $sth_update->execute($maxissueqty, $maxonsiteissueqty);
284
                $sth_update->execute( $maxissueqty, $maxonsiteissueqty, $max_holds );
281
            } else {
285
            } else {
282
                $sth_insert->execute($maxissueqty, $maxonsiteissueqty);
286
                $sth_insert->execute( $maxissueqty, $maxonsiteissueqty, $max_holds );
283
            }
287
            }
284
        } else {
288
        } else {
285
            my $sth_search = $dbh->prepare("SELECT count(*) AS total
289
            my $sth_search = $dbh->prepare("SELECT count(*) AS total
Lines 287-307 elsif ($op eq "add-branch-cat") { Link Here
287
                                            WHERE categorycode = ?");
291
                                            WHERE categorycode = ?");
288
            my $sth_insert = $dbh->prepare(q|
292
            my $sth_insert = $dbh->prepare(q|
289
                INSERT INTO default_borrower_circ_rules
293
                INSERT INTO default_borrower_circ_rules
290
                    (categorycode, maxissueqty, maxonsiteissueqty)
294
                    (categorycode, maxissueqty, maxonsiteissueqty, max_holds)
291
                    VALUES (?, ?, ?)
295
                    VALUES (?, ?, ?, ?)
292
            |);
296
            |);
293
            my $sth_update = $dbh->prepare(q|
297
            my $sth_update = $dbh->prepare(q|
294
                UPDATE default_borrower_circ_rules
298
                UPDATE default_borrower_circ_rules
295
                SET maxissueqty = ?,
299
                SET maxissueqty = ?,
296
                    maxonsiteissueqty = ?
300
                    maxonsiteissueqty = ?,
301
                    max_holds = ?
297
                WHERE categorycode = ?
302
                WHERE categorycode = ?
298
            |);
303
            |);
299
            $sth_search->execute($branch);
304
            $sth_search->execute($branch);
300
            my $res = $sth_search->fetchrow_hashref();
305
            my $res = $sth_search->fetchrow_hashref();
301
            if ($res->{total}) {
306
            if ($res->{total}) {
302
                $sth_update->execute($maxissueqty, $maxonsiteissueqty, $categorycode);
307
                $sth_update->execute( $maxissueqty, $maxonsiteissueqty, $categorycode, $max_holds );
303
            } else {
308
            } else {
304
                $sth_insert->execute($categorycode, $maxissueqty, $maxonsiteissueqty);
309
                $sth_insert->execute( $categorycode, $maxissueqty, $maxonsiteissueqty, $max_holds );
305
            }
310
            }
306
        }
311
        }
307
    } elsif ($categorycode eq "*") {
312
    } elsif ($categorycode eq "*") {
Lines 333-345 elsif ($op eq "add-branch-cat") { Link Here
333
                                        AND   categorycode = ?");
338
                                        AND   categorycode = ?");
334
        my $sth_insert = $dbh->prepare(q|
339
        my $sth_insert = $dbh->prepare(q|
335
            INSERT INTO branch_borrower_circ_rules
340
            INSERT INTO branch_borrower_circ_rules
336
            (branchcode, categorycode, maxissueqty, maxonsiteissueqty)
341
            (branchcode, categorycode, maxissueqty, maxonsiteissueqty, max_holds)
337
            VALUES (?, ?, ?, ?)
342
            VALUES (?, ?, ?, ?, ?)
338
        |);
343
        |);
339
        my $sth_update = $dbh->prepare(q|
344
        my $sth_update = $dbh->prepare(q|
340
            UPDATE branch_borrower_circ_rules
345
            UPDATE branch_borrower_circ_rules
341
            SET maxissueqty = ?,
346
            SET maxissueqty = ?,
342
                maxonsiteissueqty = ?
347
                maxonsiteissueqty = ?
348
                max_holds = ?
343
            WHERE branchcode = ?
349
            WHERE branchcode = ?
344
            AND categorycode = ?
350
            AND categorycode = ?
345
        |);
351
        |);
Lines 347-355 elsif ($op eq "add-branch-cat") { Link Here
347
        $sth_search->execute($branch, $categorycode);
353
        $sth_search->execute($branch, $categorycode);
348
        my $res = $sth_search->fetchrow_hashref();
354
        my $res = $sth_search->fetchrow_hashref();
349
        if ($res->{total}) {
355
        if ($res->{total}) {
350
            $sth_update->execute($maxissueqty, $maxonsiteissueqty, $branch, $categorycode);
356
            $sth_update->execute($maxissueqty, $maxonsiteissueqty, $max_holds, $branch, $categorycode);
351
        } else {
357
        } else {
352
            $sth_insert->execute($branch, $categorycode, $maxissueqty, $maxonsiteissueqty);
358
            $sth_insert->execute($branch, $categorycode, $maxissueqty, $maxonsiteissueqty, $max_holds);
353
        }
359
        }
354
    }
360
    }
355
}
361
}
Lines 545-550 my @sorted_branch_cat_rules = sort { $a->{'humancategorycode'} cmp $b->{'humanca Link Here
545
foreach my $entry (@sorted_branch_cat_rules, @sorted_row_loop) {
551
foreach my $entry (@sorted_branch_cat_rules, @sorted_row_loop) {
546
    $entry->{unlimited_maxissueqty} = 1 unless defined($entry->{maxissueqty});
552
    $entry->{unlimited_maxissueqty} = 1 unless defined($entry->{maxissueqty});
547
    $entry->{unlimited_maxonsiteissueqty} = 1 unless defined($entry->{maxonsiteissueqty});
553
    $entry->{unlimited_maxonsiteissueqty} = 1 unless defined($entry->{maxonsiteissueqty});
554
    $entry->{unlimited_max_holds} = 1 unless defined($entry->{max_holds});
548
}
555
}
549
556
550
@sorted_row_loop = sort by_category_and_itemtype @row_loop;
557
@sorted_row_loop = sort by_category_and_itemtype @row_loop;
(-)a/installer/data/mysql/atomicupdate/bug_15524.perl (+18 lines)
Line 0 Link Here
1
$DBversion = 'XXX';  # will be replaced by the RM
2
if( CheckVersion( $DBversion ) ) {
3
4
    if( !column_exists( 'branch_borrower_circ_rules', 'max_holds' ) ) {
5
        $dbh->do(q{
6
            ALTER TABLE branch_borrower_circ_rules ADD COLUMN max_holds INT(4) NULL DEFAULT NULL AFTER maxonsiteissueqty
7
        });
8
    }
9
10
    if( !column_exists( 'default_borrower_circ_rules', 'max_holds' ) ) {
11
        $dbh->do(q{
12
            ALTER TABLE default_borrower_circ_rules ADD COLUMN max_holds INT(4) NULL DEFAULT NULL AFTER maxonsiteissueqty
13
        });
14
    }
15
16
    SetVersion( $DBversion );
17
    print "Upgrade to $DBversion done (Bug 15524 - Set limit on maximum possible holds per patron by category)\n";
18
}
(-)a/installer/data/mysql/kohastructure.sql (+2 lines)
Lines 349-354 CREATE TABLE `branch_borrower_circ_rules` ( -- includes default circulation rule Link Here
349
  `categorycode` VARCHAR(10) NOT NULL, -- the patron category this rule applies to (categories.categorycode)
349
  `categorycode` VARCHAR(10) NOT NULL, -- the patron category this rule applies to (categories.categorycode)
350
  `maxissueqty` int(4) default NULL, -- the maximum number of checkouts this patron category can have at this branch
350
  `maxissueqty` int(4) default NULL, -- the maximum number of checkouts this patron category can have at this branch
351
  `maxonsiteissueqty` int(4) default NULL, -- the maximum number of on-site checkouts this patron category can have at this branch
351
  `maxonsiteissueqty` int(4) default NULL, -- the maximum number of on-site checkouts this patron category can have at this branch
352
  max_holds INT(4) NULL DEFAULT NULL, -- the maximum number of holds a patron may have at a time
352
  PRIMARY KEY (`categorycode`, `branchcode`),
353
  PRIMARY KEY (`categorycode`, `branchcode`),
353
  CONSTRAINT `branch_borrower_circ_rules_ibfk_1` FOREIGN KEY (`categorycode`) REFERENCES `categories` (`categorycode`)
354
  CONSTRAINT `branch_borrower_circ_rules_ibfk_1` FOREIGN KEY (`categorycode`) REFERENCES `categories` (`categorycode`)
354
    ON DELETE CASCADE ON UPDATE CASCADE,
355
    ON DELETE CASCADE ON UPDATE CASCADE,
Lines 365-370 CREATE TABLE `default_borrower_circ_rules` ( -- default checkout rules found und Link Here
365
  `categorycode` VARCHAR(10) NOT NULL, -- patron category this rul
366
  `categorycode` VARCHAR(10) NOT NULL, -- patron category this rul
366
  `maxissueqty` int(4) default NULL,
367
  `maxissueqty` int(4) default NULL,
367
  `maxonsiteissueqty` int(4) default NULL,
368
  `maxonsiteissueqty` int(4) default NULL,
369
  max_holds INT(4) NULL DEFAULT NULL, -- the maximum number of holds a patron may have at a time
368
  PRIMARY KEY (`categorycode`),
370
  PRIMARY KEY (`categorycode`),
369
  CONSTRAINT `borrower_borrower_circ_rules_ibfk_1` FOREIGN KEY (`categorycode`) REFERENCES `categories` (`categorycode`)
371
  CONSTRAINT `borrower_borrower_circ_rules_ibfk_1` FOREIGN KEY (`categorycode`) REFERENCES `categories` (`categorycode`)
370
    ON DELETE CASCADE ON UPDATE CASCADE
372
    ON DELETE CASCADE ON UPDATE CASCADE
(-)a/koha-tmpl/intranet-tmpl/prog/en/modules/admin/smart-rules.tt (+8 lines)
Lines 464-469 Link Here
464
                    <th>Patron category</th>
464
                    <th>Patron category</th>
465
                    <th>Total current checkouts allowed</th>
465
                    <th>Total current checkouts allowed</th>
466
                    <th>Total current on-site checkouts allowed</th>
466
                    <th>Total current on-site checkouts allowed</th>
467
                    <th>Maximum total holds allowed (count)</th>
467
                    <th>&nbsp;</th>
468
                    <th>&nbsp;</th>
468
                </tr>
469
                </tr>
469
                [% FOREACH branch_cat_rule_loo IN branch_cat_rule_loop %]
470
                [% FOREACH branch_cat_rule_loo IN branch_cat_rule_loop %]
Lines 490-495 Link Here
490
                                [% branch_cat_rule_loo.maxonsiteissueqty %]
491
                                [% branch_cat_rule_loo.maxonsiteissueqty %]
491
                            [% END %]
492
                            [% END %]
492
                        </td>
493
                        </td>
494
                        <td>[% IF ( branch_cat_rule_loo.unlimited_max_holds ) %]
495
                                Unlimited
496
                            [% ELSE %]
497
                                [% branch_cat_rule_loo.max_holds %]
498
                            [% END %]
499
                        </td>
493
500
494
                        <td class="actions">
501
                        <td class="actions">
495
                            <a class="btn btn-default btn-xs delete" href="/cgi-bin/koha/admin/smart-rules.pl?op=delete-branch-cat&amp;categorycode=[% branch_cat_rule_loo.categorycode %]&amp;branch=[% current_branch %]"><i class="fa fa-trash"></i> Delete</a>
502
                            <a class="btn btn-default btn-xs delete" href="/cgi-bin/koha/admin/smart-rules.pl?op=delete-branch-cat&amp;categorycode=[% branch_cat_rule_loo.categorycode %]&amp;branch=[% current_branch %]"><i class="fa fa-trash"></i> Delete</a>
Lines 506-511 Link Here
506
                    </td>
513
                    </td>
507
                    <td><input name="maxissueqty" size="3" /></td>
514
                    <td><input name="maxissueqty" size="3" /></td>
508
                    <td><input name="maxonsiteissueqty" size="3" /></td>
515
                    <td><input name="maxonsiteissueqty" size="3" /></td>
516
                    <td><input name="max_holds" size="3" /></td>
509
                    <td class="actions"><button type="submit" class="btn btn-default btn-xs"><i class="fa fa-plus"></i> Add</td>
517
                    <td class="actions"><button type="submit" class="btn btn-default btn-xs"><i class="fa fa-plus"></i> Add</td>
510
                </tr>
518
                </tr>
511
            </table>
519
            </table>
(-)a/t/db_dependent/Holds.t (-2 / +68 lines)
Lines 7-13 use t::lib::TestBuilder; Link Here
7
7
8
use C4::Context;
8
use C4::Context;
9
9
10
use Test::More tests => 55;
10
use Test::More tests => 56;
11
use MARC::Record;
11
use MARC::Record;
12
use C4::Biblio;
12
use C4::Biblio;
13
use C4::Items;
13
use C4::Items;
Lines 411-416 my $res_id = AddReserve( $branch_1, $borrowernumbers[0], $bibnum, '', 1, ); Link Here
411
is( CanItemBeReserved( $borrowernumbers[0], $itemnumber ),
411
is( CanItemBeReserved( $borrowernumbers[0], $itemnumber ),
412
    'tooManyReserves', 'Patron cannot reserve item with hold limit of 1, 1 bib level hold placed' );
412
    'tooManyReserves', 'Patron cannot reserve item with hold limit of 1, 1 bib level hold placed' );
413
413
414
subtest 'Test max_holds per library/patron category' => sub {
415
    plan tests => 6;
416
417
    $dbh->do('DELETE FROM reserves');
418
    $dbh->do('DELETE FROM issuingrules');
419
420
    ( $bibnum, $title, $bibitemnum ) = create_helper_biblio('TEST');
421
    ( $item_bibnum, $item_bibitemnum, $itemnumber ) =
422
      AddItem( { homebranch => $branch_1, holdingbranch => $branch_1 },
423
        $bibnum );
424
    $dbh->do(
425
        q{
426
            INSERT INTO issuingrules (categorycode, branchcode, itemtype, reservesallowed, holds_per_record)
427
            VALUES (?, ?, ?, ?, ?)
428
        },
429
        {},
430
        '*', '*', 'TEST', 99, 99
431
    );
432
    AddReserve( $branch_1, $borrowernumbers[0], $bibnum, '', 1, );
433
    AddReserve( $branch_1, $borrowernumbers[0], $bibnum, '', 1, );
434
    AddReserve( $branch_1, $borrowernumbers[0], $bibnum, '', 1, );
435
436
    my $count =
437
      Koha::Holds->search( { borrowernumber => $borrowernumbers[0] } )->count();
438
    is( $count, 3, 'Patron now has 3 holds' );
439
440
    my $ret = CanItemBeReserved( $borrowernumbers[0], $itemnumber );
441
    is( $ret, 'OK', 'Patron can place hold with no borrower circ rules' );
442
443
    my $rule_all = $schema->resultset('DefaultBorrowerCircRule')->new(
444
        {
445
            categorycode => $category->{categorycode},
446
            max_holds    => 3,
447
        }
448
    )->insert();
449
450
    my $rule_branch = $schema->resultset('BranchBorrowerCircRule')->new(
451
        {
452
            branchcode   => $branch_1,
453
            categorycode => $category->{categorycode},
454
            max_holds    => 5,
455
        }
456
    )->insert();
457
458
    $ret = CanItemBeReserved( $borrowernumbers[0], $itemnumber );
459
    is( $ret, 'OK', 'Patron can place hold with branch/category rule of 5, category rule of 3' );
460
461
    $rule_branch->delete();
462
463
    $ret = CanItemBeReserved( $borrowernumbers[0], $itemnumber );
464
    is( $ret, 'tooManyReserves', 'Patron cannot place hold with only a category rule of 3' );
465
466
    $rule_all->delete();
467
    $rule_branch->max_holds(3);
468
    $rule_branch->insert();
469
470
    $ret = CanItemBeReserved( $borrowernumbers[0], $itemnumber );
471
    is( $ret, 'tooManyReserves', 'Patron cannot place hold with only a branch/category rule of 3' );
472
473
    $rule_branch->max_holds(5);
474
    $rule_branch->update();
475
    $rule_branch->max_holds(5);
476
    $rule_branch->insert();
477
478
    $ret = CanItemBeReserved( $borrowernumbers[0], $itemnumber );
479
    is( $ret, 'OK', 'Patron can place hold with branch/category rule of 5, category rule of 5' );
480
};
414
481
415
# Helper method to set up a Biblio.
482
# Helper method to set up a Biblio.
416
sub create_helper_biblio {
483
sub create_helper_biblio {
417
- 

Return to bug 15524