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 255-264 elsif ($op eq "add-branch-cat") { Link Here
255
    my $categorycode  = $input->param('categorycode');
255
    my $categorycode  = $input->param('categorycode');
256
    my $maxissueqty   = $input->param('maxissueqty');
256
    my $maxissueqty   = $input->param('maxissueqty');
257
    my $maxonsiteissueqty = $input->param('maxonsiteissueqty');
257
    my $maxonsiteissueqty = $input->param('maxonsiteissueqty');
258
    my $max_holds = $input->param('max_holds');
258
    $maxissueqty =~ s/\s//g;
259
    $maxissueqty =~ s/\s//g;
259
    $maxissueqty = undef if $maxissueqty !~ /^\d+/;
260
    $maxissueqty = undef if $maxissueqty !~ /^\d+/;
260
    $maxonsiteissueqty =~ s/\s//g;
261
    $maxonsiteissueqty =~ s/\s//g;
261
    $maxonsiteissueqty = undef if $maxonsiteissueqty !~ /^\d+/;
262
    $maxonsiteissueqty = undef if $maxonsiteissueqty !~ /^\d+/;
263
    $max_holds =~ s/\s//g;
264
    $max_holds = undef if $max_holds !~ /^\d+/;
262
265
263
    if ($branch eq "*") {
266
    if ($branch eq "*") {
264
        if ($categorycode eq "*") {
267
        if ($categorycode eq "*") {
Lines 266-286 elsif ($op eq "add-branch-cat") { Link Here
266
                                            FROM default_circ_rules");
269
                                            FROM default_circ_rules");
267
            my $sth_insert = $dbh->prepare(q|
270
            my $sth_insert = $dbh->prepare(q|
268
                INSERT INTO default_circ_rules
271
                INSERT INTO default_circ_rules
269
                    (maxissueqty, maxonsiteissueqty)
272
                    (maxissueqty, maxonsiteissueqty, max_holds)
270
                    VALUES (?, ?)
273
                    VALUES (?, ?, ?)
271
            |);
274
            |);
272
            my $sth_update = $dbh->prepare(q|
275
            my $sth_update = $dbh->prepare(q|
273
                UPDATE default_circ_rules
276
                UPDATE default_circ_rules
274
                SET maxissueqty = ?,
277
                SET maxissueqty = ?,
275
                    maxonsiteissueqty = ?
278
                    maxonsiteissueqty = ?,
279
                    max_holds = ?
276
            |);
280
            |);
277
281
278
            $sth_search->execute();
282
            $sth_search->execute();
279
            my $res = $sth_search->fetchrow_hashref();
283
            my $res = $sth_search->fetchrow_hashref();
280
            if ($res->{total}) {
284
            if ($res->{total}) {
281
                $sth_update->execute($maxissueqty, $maxonsiteissueqty);
285
                $sth_update->execute( $maxissueqty, $maxonsiteissueqty, $max_holds );
282
            } else {
286
            } else {
283
                $sth_insert->execute($maxissueqty, $maxonsiteissueqty);
287
                $sth_insert->execute( $maxissueqty, $maxonsiteissueqty, $max_holds );
284
            }
288
            }
285
        } else {
289
        } else {
286
            my $sth_search = $dbh->prepare("SELECT count(*) AS total
290
            my $sth_search = $dbh->prepare("SELECT count(*) AS total
Lines 288-308 elsif ($op eq "add-branch-cat") { Link Here
288
                                            WHERE categorycode = ?");
292
                                            WHERE categorycode = ?");
289
            my $sth_insert = $dbh->prepare(q|
293
            my $sth_insert = $dbh->prepare(q|
290
                INSERT INTO default_borrower_circ_rules
294
                INSERT INTO default_borrower_circ_rules
291
                    (categorycode, maxissueqty, maxonsiteissueqty)
295
                    (categorycode, maxissueqty, maxonsiteissueqty, max_holds)
292
                    VALUES (?, ?, ?)
296
                    VALUES (?, ?, ?, ?)
293
            |);
297
            |);
294
            my $sth_update = $dbh->prepare(q|
298
            my $sth_update = $dbh->prepare(q|
295
                UPDATE default_borrower_circ_rules
299
                UPDATE default_borrower_circ_rules
296
                SET maxissueqty = ?,
300
                SET maxissueqty = ?,
297
                    maxonsiteissueqty = ?
301
                    maxonsiteissueqty = ?,
302
                    max_holds = ?
298
                WHERE categorycode = ?
303
                WHERE categorycode = ?
299
            |);
304
            |);
300
            $sth_search->execute($branch);
305
            $sth_search->execute($branch);
301
            my $res = $sth_search->fetchrow_hashref();
306
            my $res = $sth_search->fetchrow_hashref();
302
            if ($res->{total}) {
307
            if ($res->{total}) {
303
                $sth_update->execute($maxissueqty, $maxonsiteissueqty, $categorycode);
308
                $sth_update->execute( $maxissueqty, $maxonsiteissueqty, $categorycode, $max_holds );
304
            } else {
309
            } else {
305
                $sth_insert->execute($categorycode, $maxissueqty, $maxonsiteissueqty);
310
                $sth_insert->execute( $categorycode, $maxissueqty, $maxonsiteissueqty, $max_holds );
306
            }
311
            }
307
        }
312
        }
308
    } elsif ($categorycode eq "*") {
313
    } elsif ($categorycode eq "*") {
Lines 334-346 elsif ($op eq "add-branch-cat") { Link Here
334
                                        AND   categorycode = ?");
339
                                        AND   categorycode = ?");
335
        my $sth_insert = $dbh->prepare(q|
340
        my $sth_insert = $dbh->prepare(q|
336
            INSERT INTO branch_borrower_circ_rules
341
            INSERT INTO branch_borrower_circ_rules
337
            (branchcode, categorycode, maxissueqty, maxonsiteissueqty)
342
            (branchcode, categorycode, maxissueqty, maxonsiteissueqty, max_holds)
338
            VALUES (?, ?, ?, ?)
343
            VALUES (?, ?, ?, ?, ?)
339
        |);
344
        |);
340
        my $sth_update = $dbh->prepare(q|
345
        my $sth_update = $dbh->prepare(q|
341
            UPDATE branch_borrower_circ_rules
346
            UPDATE branch_borrower_circ_rules
342
            SET maxissueqty = ?,
347
            SET maxissueqty = ?,
343
                maxonsiteissueqty = ?
348
                maxonsiteissueqty = ?
349
                max_holds = ?
344
            WHERE branchcode = ?
350
            WHERE branchcode = ?
345
            AND categorycode = ?
351
            AND categorycode = ?
346
        |);
352
        |);
Lines 348-356 elsif ($op eq "add-branch-cat") { Link Here
348
        $sth_search->execute($branch, $categorycode);
354
        $sth_search->execute($branch, $categorycode);
349
        my $res = $sth_search->fetchrow_hashref();
355
        my $res = $sth_search->fetchrow_hashref();
350
        if ($res->{total}) {
356
        if ($res->{total}) {
351
            $sth_update->execute($maxissueqty, $maxonsiteissueqty, $branch, $categorycode);
357
            $sth_update->execute($maxissueqty, $maxonsiteissueqty, $max_holds, $branch, $categorycode);
352
        } else {
358
        } else {
353
            $sth_insert->execute($branch, $categorycode, $maxissueqty, $maxonsiteissueqty);
359
            $sth_insert->execute($branch, $categorycode, $maxissueqty, $maxonsiteissueqty, $max_holds);
354
        }
360
        }
355
    }
361
    }
356
}
362
}
Lines 546-551 my @sorted_branch_cat_rules = sort { $a->{'humancategorycode'} cmp $b->{'humanca Link Here
546
foreach my $entry (@sorted_branch_cat_rules, @sorted_row_loop) {
552
foreach my $entry (@sorted_branch_cat_rules, @sorted_row_loop) {
547
    $entry->{unlimited_maxissueqty} = 1 unless defined($entry->{maxissueqty});
553
    $entry->{unlimited_maxissueqty} = 1 unless defined($entry->{maxissueqty});
548
    $entry->{unlimited_maxonsiteissueqty} = 1 unless defined($entry->{maxonsiteissueqty});
554
    $entry->{unlimited_maxonsiteissueqty} = 1 unless defined($entry->{maxonsiteissueqty});
555
    $entry->{unlimited_max_holds} = 1 unless defined($entry->{max_holds});
549
}
556
}
550
557
551
@sorted_row_loop = sort by_category_and_itemtype @row_loop;
558
@sorted_row_loop = sort by_category_and_itemtype @row_loop;
(-)a/installer/data/mysql/atomicupdate/max_holds.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 379-384 CREATE TABLE `branch_borrower_circ_rules` ( -- includes default circulation rule Link Here
379
  `categorycode` VARCHAR(10) NOT NULL, -- the patron category this rule applies to (categories.categorycode)
379
  `categorycode` VARCHAR(10) NOT NULL, -- the patron category this rule applies to (categories.categorycode)
380
  `maxissueqty` int(4) default NULL, -- the maximum number of checkouts this patron category can have at this branch
380
  `maxissueqty` int(4) default NULL, -- the maximum number of checkouts this patron category can have at this branch
381
  `maxonsiteissueqty` int(4) default NULL, -- the maximum number of on-site checkouts this patron category can have at this branch
381
  `maxonsiteissueqty` int(4) default NULL, -- the maximum number of on-site checkouts this patron category can have at this branch
382
  max_holds INT(4) NULL DEFAULT NULL, -- the maximum number of holds a patron may have at a time
382
  PRIMARY KEY (`categorycode`, `branchcode`),
383
  PRIMARY KEY (`categorycode`, `branchcode`),
383
  CONSTRAINT `branch_borrower_circ_rules_ibfk_1` FOREIGN KEY (`categorycode`) REFERENCES `categories` (`categorycode`)
384
  CONSTRAINT `branch_borrower_circ_rules_ibfk_1` FOREIGN KEY (`categorycode`) REFERENCES `categories` (`categorycode`)
384
    ON DELETE CASCADE ON UPDATE CASCADE,
385
    ON DELETE CASCADE ON UPDATE CASCADE,
Lines 395-400 CREATE TABLE `default_borrower_circ_rules` ( -- default checkout rules found und Link Here
395
  `categorycode` VARCHAR(10) NOT NULL, -- patron category this rul
396
  `categorycode` VARCHAR(10) NOT NULL, -- patron category this rul
396
  `maxissueqty` int(4) default NULL,
397
  `maxissueqty` int(4) default NULL,
397
  `maxonsiteissueqty` int(4) default NULL,
398
  `maxonsiteissueqty` int(4) default NULL,
399
  max_holds INT(4) NULL DEFAULT NULL, -- the maximum number of holds a patron may have at a time
398
  PRIMARY KEY (`categorycode`),
400
  PRIMARY KEY (`categorycode`),
399
  CONSTRAINT `borrower_borrower_circ_rules_ibfk_1` FOREIGN KEY (`categorycode`) REFERENCES `categories` (`categorycode`)
401
  CONSTRAINT `borrower_borrower_circ_rules_ibfk_1` FOREIGN KEY (`categorycode`) REFERENCES `categories` (`categorycode`)
400
    ON DELETE CASCADE ON UPDATE CASCADE
402
    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