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

(-)a/C4/Reserves.pm (+23 lines)
Lines 401-406 sub CanItemBeReserved { Link Here
401
        return 'tooManyReserves';
401
        return 'tooManyReserves';
402
    }
402
    }
403
403
404
    # Now we need to check hold limits by patron category
405
    my $schema = Koha::Database->new()->schema();
406
    my $rule = $schema->resultset('BranchBorrowerCircRule')->find(
407
        {
408
            branchcode   => $borrower->{branchcode},
409
            categorycode => $borrower->{categorycode},
410
        }
411
    );
412
    $rule ||= $schema->resultset('DefaultBorrowerCircRule')->find(
413
        {
414
            categorycode => $borrower->{categorycode}
415
        }
416
    );
417
    if ( $rule && defined $rule->max_holds ) {
418
        my $total_holds_count = Koha::Holds->search(
419
            {
420
                borrowernumber => $borrower->{borrowernumber}
421
            }
422
        )->count();
423
424
        return 'tooManyReserves' if $total_holds_count >= $rule->max_holds;
425
    }
426
404
    my $circ_control_branch =
427
    my $circ_control_branch =
405
      C4::Circulation::_GetCircControlBranch( $item->unblessed(), $borrower );
428
      C4::Circulation::_GetCircControlBranch( $item->unblessed(), $borrower );
406
    my $branchitemrule =
429
    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 377-382 CREATE TABLE `branch_borrower_circ_rules` ( -- includes default circulation rule Link Here
377
  `categorycode` VARCHAR(10) NOT NULL, -- the patron category this rule applies to (categories.categorycode)
377
  `categorycode` VARCHAR(10) NOT NULL, -- the patron category this rule applies to (categories.categorycode)
378
  `maxissueqty` int(4) default NULL, -- the maximum number of checkouts this patron category can have at this branch
378
  `maxissueqty` int(4) default NULL, -- the maximum number of checkouts this patron category can have at this branch
379
  `maxonsiteissueqty` int(4) default NULL, -- the maximum number of on-site checkouts this patron category can have at this branch
379
  `maxonsiteissueqty` int(4) default NULL, -- the maximum number of on-site checkouts this patron category can have at this branch
380
  max_holds INT(4) NULL DEFAULT NULL, -- the maximum number of holds a patron may have at a time
380
  PRIMARY KEY (`categorycode`, `branchcode`),
381
  PRIMARY KEY (`categorycode`, `branchcode`),
381
  CONSTRAINT `branch_borrower_circ_rules_ibfk_1` FOREIGN KEY (`categorycode`) REFERENCES `categories` (`categorycode`)
382
  CONSTRAINT `branch_borrower_circ_rules_ibfk_1` FOREIGN KEY (`categorycode`) REFERENCES `categories` (`categorycode`)
382
    ON DELETE CASCADE ON UPDATE CASCADE,
383
    ON DELETE CASCADE ON UPDATE CASCADE,
Lines 393-398 CREATE TABLE `default_borrower_circ_rules` ( -- default checkout rules found und Link Here
393
  `categorycode` VARCHAR(10) NOT NULL, -- patron category this rul
394
  `categorycode` VARCHAR(10) NOT NULL, -- patron category this rul
394
  `maxissueqty` int(4) default NULL,
395
  `maxissueqty` int(4) default NULL,
395
  `maxonsiteissueqty` int(4) default NULL,
396
  `maxonsiteissueqty` int(4) default NULL,
397
  max_holds INT(4) NULL DEFAULT NULL, -- the maximum number of holds a patron may have at a time
396
  PRIMARY KEY (`categorycode`),
398
  PRIMARY KEY (`categorycode`),
397
  CONSTRAINT `borrower_borrower_circ_rules_ibfk_1` FOREIGN KEY (`categorycode`) REFERENCES `categories` (`categorycode`)
399
  CONSTRAINT `borrower_borrower_circ_rules_ibfk_1` FOREIGN KEY (`categorycode`) REFERENCES `categories` (`categorycode`)
398
    ON DELETE CASCADE ON UPDATE CASCADE
400
    ON DELETE CASCADE ON UPDATE CASCADE
(-)a/koha-tmpl/intranet-tmpl/prog/en/modules/admin/smart-rules.tt (+8 lines)
Lines 568-573 $(document).ready(function() { Link Here
568
                    <th>Patron category</th>
568
                    <th>Patron category</th>
569
                    <th>Total current checkouts allowed</th>
569
                    <th>Total current checkouts allowed</th>
570
                    <th>Total current on-site checkouts allowed</th>
570
                    <th>Total current on-site checkouts allowed</th>
571
                    <th>Maximum total holds allowed (count)</th>
571
                    <th>&nbsp;</th>
572
                    <th>&nbsp;</th>
572
                </tr>
573
                </tr>
573
                [% FOREACH branch_cat_rule_loo IN branch_cat_rule_loop %]
574
                [% FOREACH branch_cat_rule_loo IN branch_cat_rule_loop %]
Lines 594-599 $(document).ready(function() { Link Here
594
                                [% branch_cat_rule_loo.maxonsiteissueqty %]
595
                                [% branch_cat_rule_loo.maxonsiteissueqty %]
595
                            [% END %]
596
                            [% END %]
596
                        </td>
597
                        </td>
598
                        <td>[% IF ( branch_cat_rule_loo.unlimited_max_holds ) %]
599
                                Unlimited
600
                            [% ELSE %]
601
                                [% branch_cat_rule_loo.max_holds %]
602
                            [% END %]
603
                        </td>
597
604
598
                        <td class="actions">
605
                        <td class="actions">
599
                            <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>
606
                            <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 610-615 $(document).ready(function() { Link Here
610
                    </td>
617
                    </td>
611
                    <td><input name="maxissueqty" size="3" /></td>
618
                    <td><input name="maxissueqty" size="3" /></td>
612
                    <td><input name="maxonsiteissueqty" size="3" /></td>
619
                    <td><input name="maxonsiteissueqty" size="3" /></td>
620
                    <td><input name="max_holds" size="3" /></td>
613
                    <td class="actions"><button type="submit" class="btn btn-default btn-xs"><i class="fa fa-plus"></i> Add</td>
621
                    <td class="actions"><button type="submit" class="btn btn-default btn-xs"><i class="fa fa-plus"></i> Add</td>
614
                </tr>
622
                </tr>
615
            </table>
623
            </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 => 53;
10
use Test::More tests => 54;
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 401-406 my $res_id = AddReserve( $branch_1, $borrowernumbers[0], $bibnum, '', 1, ); Link Here
401
is( CanItemBeReserved( $borrowernumbers[0], $itemnumber ),
401
is( CanItemBeReserved( $borrowernumbers[0], $itemnumber ),
402
    'tooManyReserves', 'Patron cannot reserve item with hold limit of 1, 1 bib level hold placed' );
402
    'tooManyReserves', 'Patron cannot reserve item with hold limit of 1, 1 bib level hold placed' );
403
403
404
subtest 'Test max_holds per library/patron category' => sub {
405
    plan tests => 6;
406
407
    $dbh->do('DELETE FROM reserves');
408
    $dbh->do('DELETE FROM issuingrules');
409
410
    ( $bibnum, $title, $bibitemnum ) = create_helper_biblio('TEST');
411
    ( $item_bibnum, $item_bibitemnum, $itemnumber ) =
412
      AddItem( { homebranch => $branch_1, holdingbranch => $branch_1 },
413
        $bibnum );
414
    $dbh->do(
415
        q{
416
            INSERT INTO issuingrules (categorycode, branchcode, itemtype, reservesallowed, holds_per_record)
417
            VALUES (?, ?, ?, ?, ?)
418
        },
419
        {},
420
        '*', '*', 'TEST', 99, 99
421
    );
422
    AddReserve( $branch_1, $borrowernumbers[0], $bibnum, '', 1, );
423
    AddReserve( $branch_1, $borrowernumbers[0], $bibnum, '', 1, );
424
    AddReserve( $branch_1, $borrowernumbers[0], $bibnum, '', 1, );
425
426
    my $count =
427
      Koha::Holds->search( { borrowernumber => $borrowernumbers[0] } )->count();
428
    is( $count, 3, 'Patron now has 3 holds' );
429
430
    my $ret = CanItemBeReserved( $borrowernumbers[0], $itemnumber );
431
    is( $ret, 'OK', 'Patron can place hold with no borrower circ rules' );
432
433
    my $rule_all = $schema->resultset('DefaultBorrowerCircRule')->new(
434
        {
435
            categorycode => $category->{categorycode},
436
            max_holds    => 3,
437
        }
438
    )->insert();
439
440
    my $rule_branch = $schema->resultset('BranchBorrowerCircRule')->new(
441
        {
442
            branchcode   => $branch_1,
443
            categorycode => $category->{categorycode},
444
            max_holds    => 5,
445
        }
446
    )->insert();
447
448
    $ret = CanItemBeReserved( $borrowernumbers[0], $itemnumber );
449
    is( $ret, 'OK', 'Patron can place hold with branch/category rule of 5, category rule of 3' );
450
451
    $rule_branch->delete();
452
453
    $ret = CanItemBeReserved( $borrowernumbers[0], $itemnumber );
454
    is( $ret, 'tooManyReserves', 'Patron cannot place hold with only a category rule of 3' );
455
456
    $rule_all->delete();
457
    $rule_branch->max_holds(3);
458
    $rule_branch->insert();
459
460
    $ret = CanItemBeReserved( $borrowernumbers[0], $itemnumber );
461
    is( $ret, 'tooManyReserves', 'Patron cannot place hold with only a branch/category rule of 3' );
462
463
    $rule_branch->max_holds(5);
464
    $rule_branch->update();
465
    $rule_branch->max_holds(5);
466
    $rule_branch->insert();
467
468
    $ret = CanItemBeReserved( $borrowernumbers[0], $itemnumber );
469
    is( $ret, 'OK', 'Patron can place hold with branch/category rule of 5, category rule of 5' );
470
};
404
471
405
# Helper method to set up a Biblio.
472
# Helper method to set up a Biblio.
406
sub create_helper_biblio {
473
sub create_helper_biblio {
407
- 

Return to bug 15524