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

(-)a/C4/Reserves.pm (+23 lines)
Lines 409-414 sub CanItemBeReserved { Link Here
409
        return 'tooManyReserves';
409
        return 'tooManyReserves';
410
    }
410
    }
411
411
412
    # Now we need to check hold limits by patron category
413
    my $schema = Koha::Database->new()->schema();
414
    my $rule = $schema->resultset('BranchBorrowerCircRule')->find(
415
        {
416
            branchcode   => $borrower->{branchcode},
417
            categorycode => $borrower->{categorycode},
418
        }
419
    );
420
    $rule ||= $schema->resultset('DefaultBorrowerCircRule')->find(
421
        {
422
            categorycode => $borrower->{categorycode}
423
        }
424
    );
425
    if ( $rule && defined $rule->max_holds ) {
426
        my $total_holds_count = Koha::Holds->search(
427
            {
428
                borrowernumber => $borrower->{borrowernumber}
429
            }
430
        )->count();
431
432
        return 'tooManyReserves' if $total_holds_count >= $rule->max_holds;
433
    }
434
412
    my $circ_control_branch =
435
    my $circ_control_branch =
413
      C4::Circulation::_GetCircControlBranch( $item->unblessed(), $borrower );
436
      C4::Circulation::_GetCircControlBranch( $item->unblessed(), $borrower );
414
    my $branchitemrule =
437
    my $branchitemrule =
(-)a/admin/smart-rules.pl (-14 / +21 lines)
Lines 256-265 elsif ($op eq "add-branch-cat") { Link Here
256
    my $categorycode  = $input->param('categorycode');
256
    my $categorycode  = $input->param('categorycode');
257
    my $maxissueqty   = $input->param('maxissueqty');
257
    my $maxissueqty   = $input->param('maxissueqty');
258
    my $maxonsiteissueqty = $input->param('maxonsiteissueqty');
258
    my $maxonsiteissueqty = $input->param('maxonsiteissueqty');
259
    my $max_holds = $input->param('max_holds');
259
    $maxissueqty =~ s/\s//g;
260
    $maxissueqty =~ s/\s//g;
260
    $maxissueqty = undef if $maxissueqty !~ /^\d+/;
261
    $maxissueqty = undef if $maxissueqty !~ /^\d+/;
261
    $maxonsiteissueqty =~ s/\s//g;
262
    $maxonsiteissueqty =~ s/\s//g;
262
    $maxonsiteissueqty = undef if $maxonsiteissueqty !~ /^\d+/;
263
    $maxonsiteissueqty = undef if $maxonsiteissueqty !~ /^\d+/;
264
    $max_holds =~ s/\s//g;
265
    $max_holds = undef if $max_holds !~ /^\d+/;
263
266
264
    if ($branch eq "*") {
267
    if ($branch eq "*") {
265
        if ($categorycode eq "*") {
268
        if ($categorycode eq "*") {
Lines 267-287 elsif ($op eq "add-branch-cat") { Link Here
267
                                            FROM default_circ_rules");
270
                                            FROM default_circ_rules");
268
            my $sth_insert = $dbh->prepare(q|
271
            my $sth_insert = $dbh->prepare(q|
269
                INSERT INTO default_circ_rules
272
                INSERT INTO default_circ_rules
270
                    (maxissueqty, maxonsiteissueqty)
273
                    (maxissueqty, maxonsiteissueqty, max_holds)
271
                    VALUES (?, ?)
274
                    VALUES (?, ?, ?)
272
            |);
275
            |);
273
            my $sth_update = $dbh->prepare(q|
276
            my $sth_update = $dbh->prepare(q|
274
                UPDATE default_circ_rules
277
                UPDATE default_circ_rules
275
                SET maxissueqty = ?,
278
                SET maxissueqty = ?,
276
                    maxonsiteissueqty = ?
279
                    maxonsiteissueqty = ?,
280
                    max_holds = ?
277
            |);
281
            |);
278
282
279
            $sth_search->execute();
283
            $sth_search->execute();
280
            my $res = $sth_search->fetchrow_hashref();
284
            my $res = $sth_search->fetchrow_hashref();
281
            if ($res->{total}) {
285
            if ($res->{total}) {
282
                $sth_update->execute($maxissueqty, $maxonsiteissueqty);
286
                $sth_update->execute( $maxissueqty, $maxonsiteissueqty, $max_holds );
283
            } else {
287
            } else {
284
                $sth_insert->execute($maxissueqty, $maxonsiteissueqty);
288
                $sth_insert->execute( $maxissueqty, $maxonsiteissueqty, $max_holds );
285
            }
289
            }
286
        } else {
290
        } else {
287
            my $sth_search = $dbh->prepare("SELECT count(*) AS total
291
            my $sth_search = $dbh->prepare("SELECT count(*) AS total
Lines 289-309 elsif ($op eq "add-branch-cat") { Link Here
289
                                            WHERE categorycode = ?");
293
                                            WHERE categorycode = ?");
290
            my $sth_insert = $dbh->prepare(q|
294
            my $sth_insert = $dbh->prepare(q|
291
                INSERT INTO default_borrower_circ_rules
295
                INSERT INTO default_borrower_circ_rules
292
                    (categorycode, maxissueqty, maxonsiteissueqty)
296
                    (categorycode, maxissueqty, maxonsiteissueqty, max_holds)
293
                    VALUES (?, ?, ?)
297
                    VALUES (?, ?, ?, ?)
294
            |);
298
            |);
295
            my $sth_update = $dbh->prepare(q|
299
            my $sth_update = $dbh->prepare(q|
296
                UPDATE default_borrower_circ_rules
300
                UPDATE default_borrower_circ_rules
297
                SET maxissueqty = ?,
301
                SET maxissueqty = ?,
298
                    maxonsiteissueqty = ?
302
                    maxonsiteissueqty = ?,
303
                    max_holds = ?
299
                WHERE categorycode = ?
304
                WHERE categorycode = ?
300
            |);
305
            |);
301
            $sth_search->execute($categorycode);
306
            $sth_search->execute($categorycode);
302
            my $res = $sth_search->fetchrow_hashref();
307
            my $res = $sth_search->fetchrow_hashref();
303
            if ($res->{total}) {
308
            if ($res->{total}) {
304
                $sth_update->execute($maxissueqty, $maxonsiteissueqty, $categorycode);
309
                $sth_update->execute( $maxissueqty, $maxonsiteissueqty, $categorycode, $max_holds );
305
            } else {
310
            } else {
306
                $sth_insert->execute($categorycode, $maxissueqty, $maxonsiteissueqty);
311
                $sth_insert->execute( $categorycode, $maxissueqty, $maxonsiteissueqty, $max_holds );
307
            }
312
            }
308
        }
313
        }
309
    } elsif ($categorycode eq "*") {
314
    } elsif ($categorycode eq "*") {
Lines 335-347 elsif ($op eq "add-branch-cat") { Link Here
335
                                        AND   categorycode = ?");
340
                                        AND   categorycode = ?");
336
        my $sth_insert = $dbh->prepare(q|
341
        my $sth_insert = $dbh->prepare(q|
337
            INSERT INTO branch_borrower_circ_rules
342
            INSERT INTO branch_borrower_circ_rules
338
            (branchcode, categorycode, maxissueqty, maxonsiteissueqty)
343
            (branchcode, categorycode, maxissueqty, maxonsiteissueqty, max_holds)
339
            VALUES (?, ?, ?, ?)
344
            VALUES (?, ?, ?, ?, ?)
340
        |);
345
        |);
341
        my $sth_update = $dbh->prepare(q|
346
        my $sth_update = $dbh->prepare(q|
342
            UPDATE branch_borrower_circ_rules
347
            UPDATE branch_borrower_circ_rules
343
            SET maxissueqty = ?,
348
            SET maxissueqty = ?,
344
                maxonsiteissueqty = ?
349
                maxonsiteissueqty = ?
350
                max_holds = ?
345
            WHERE branchcode = ?
351
            WHERE branchcode = ?
346
            AND categorycode = ?
352
            AND categorycode = ?
347
        |);
353
        |);
Lines 349-357 elsif ($op eq "add-branch-cat") { Link Here
349
        $sth_search->execute($branch, $categorycode);
355
        $sth_search->execute($branch, $categorycode);
350
        my $res = $sth_search->fetchrow_hashref();
356
        my $res = $sth_search->fetchrow_hashref();
351
        if ($res->{total}) {
357
        if ($res->{total}) {
352
            $sth_update->execute($maxissueqty, $maxonsiteissueqty, $branch, $categorycode);
358
            $sth_update->execute($maxissueqty, $maxonsiteissueqty, $max_holds, $branch, $categorycode);
353
        } else {
359
        } else {
354
            $sth_insert->execute($branch, $categorycode, $maxissueqty, $maxonsiteissueqty);
360
            $sth_insert->execute($branch, $categorycode, $maxissueqty, $maxonsiteissueqty, $max_holds);
355
        }
361
        }
356
    }
362
    }
357
}
363
}
Lines 547-552 my @sorted_branch_cat_rules = sort { $a->{'humancategorycode'} cmp $b->{'humanca Link Here
547
foreach my $entry (@sorted_branch_cat_rules, @sorted_row_loop) {
553
foreach my $entry (@sorted_branch_cat_rules, @sorted_row_loop) {
548
    $entry->{unlimited_maxissueqty} = 1 unless defined($entry->{maxissueqty});
554
    $entry->{unlimited_maxissueqty} = 1 unless defined($entry->{maxissueqty});
549
    $entry->{unlimited_maxonsiteissueqty} = 1 unless defined($entry->{maxonsiteissueqty});
555
    $entry->{unlimited_maxonsiteissueqty} = 1 unless defined($entry->{maxonsiteissueqty});
556
    $entry->{unlimited_max_holds} = 1 unless defined($entry->{max_holds});
550
}
557
}
551
558
552
@sorted_row_loop = sort by_category_and_itemtype @row_loop;
559
@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 485-490 Link Here
485
                    <th>Patron category</th>
485
                    <th>Patron category</th>
486
                    <th>Total current checkouts allowed</th>
486
                    <th>Total current checkouts allowed</th>
487
                    <th>Total current on-site checkouts allowed</th>
487
                    <th>Total current on-site checkouts allowed</th>
488
                    <th>Maximum total holds allowed (count)</th>
488
                    <th>&nbsp;</th>
489
                    <th>&nbsp;</th>
489
                </tr>
490
                </tr>
490
                [% FOREACH branch_cat_rule_loo IN branch_cat_rule_loop %]
491
                [% FOREACH branch_cat_rule_loo IN branch_cat_rule_loop %]
Lines 511-516 Link Here
511
                                [% branch_cat_rule_loo.maxonsiteissueqty | html %]
512
                                [% branch_cat_rule_loo.maxonsiteissueqty | html %]
512
                            [% END %]
513
                            [% END %]
513
                        </td>
514
                        </td>
515
                        <td>[% IF ( branch_cat_rule_loo.unlimited_max_holds ) %]
516
                                Unlimited
517
                            [% ELSE %]
518
                                [% branch_cat_rule_loo.max_holds %]
519
                            [% END %]
520
                        </td>
514
521
515
                        <td class="actions">
522
                        <td class="actions">
516
                            <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 | html %]&amp;branch=[% current_branch | html %]"><i class="fa fa-trash"></i> Delete</a>
523
                            <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 | html %]&amp;branch=[% current_branch | html %]"><i class="fa fa-trash"></i> Delete</a>
Lines 527-532 Link Here
527
                    </td>
534
                    </td>
528
                    <td><input name="maxissueqty" size="3" /></td>
535
                    <td><input name="maxissueqty" size="3" /></td>
529
                    <td><input name="maxonsiteissueqty" size="3" /></td>
536
                    <td><input name="maxonsiteissueqty" size="3" /></td>
537
                    <td><input name="max_holds" size="3" /></td>
530
                    <td class="actions"><button type="submit" class="btn btn-default btn-xs"><i class="fa fa-plus"></i> Add</td>
538
                    <td class="actions"><button type="submit" class="btn btn-default btn-xs"><i class="fa fa-plus"></i> Add</td>
531
                </tr>
539
                </tr>
532
            </table>
540
            </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 => 54;
10
use Test::More tests => 56;
11
use MARC::Record;
11
use MARC::Record;
12
use Koha::Patrons;
12
use Koha::Patrons;
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