Lines 16-22
Link Here
|
16 |
|
16 |
|
17 |
use Modern::Perl; |
17 |
use Modern::Perl; |
18 |
use Test::NoWarnings; |
18 |
use Test::NoWarnings; |
19 |
use Test::More tests => 12; |
19 |
use Test::More tests => 15; |
20 |
use C4::Context; |
20 |
use C4::Context; |
21 |
|
21 |
|
22 |
use C4::Members; |
22 |
use C4::Members; |
Lines 562-568
subtest '1 BranchBorrowerCircRule exist: 1 CO allowed, 1 OSCO allowed' => sub {
Link Here
|
562 |
subtest 'General vs specific rules limit quantity correctly' => sub { |
562 |
subtest 'General vs specific rules limit quantity correctly' => sub { |
563 |
plan tests => 18; |
563 |
plan tests => 18; |
564 |
|
564 |
|
565 |
t::lib::Mocks::mock_preference( 'CircControl', 'ItemHomeLibrary' ); |
565 |
t::lib::Mocks::mock_preference( 'CircControl', 'ItemHomeLibrary' ); |
|
|
566 |
t::lib::Mocks::mock_preference( 'CircControlCheckoutLimitScope', 'item' ); |
566 |
my $branch = $builder->build( { source => 'Branch', } ); |
567 |
my $branch = $builder->build( { source => 'Branch', } ); |
567 |
my $category = $builder->build( { source => 'Category', } ); |
568 |
my $category = $builder->build( { source => 'Category', } ); |
568 |
my $itemtype = $builder->build( |
569 |
my $itemtype = $builder->build( |
Lines 695-701
subtest 'General vs specific rules limit quantity correctly' => sub {
Link Here
|
695 |
); |
696 |
); |
696 |
|
697 |
|
697 |
# If circcontrol is PatronLibrary we count all the patron's loan, regardless of branch |
698 |
# If circcontrol is PatronLibrary we count all the patron's loan, regardless of branch |
698 |
t::lib::Mocks::mock_preference( 'CircControl', 'PatronLibrary' ); |
699 |
t::lib::Mocks::mock_preference( 'CircControl', 'PatronLibrary' ); |
|
|
700 |
t::lib::Mocks::mock_preference( 'CircControlCheckoutLimitScope', 'all' ); |
699 |
$data = C4::Circulation::TooMany( $patron, $branch_item ); |
701 |
$data = C4::Circulation::TooMany( $patron, $branch_item ); |
700 |
$rule = delete $data->{circulation_rule}; |
702 |
$rule = delete $data->{circulation_rule}; |
701 |
is( ref $rule, 'Koha::CirculationRule', 'Circulation rule was returned' ); |
703 |
is( ref $rule, 'Koha::CirculationRule', 'Circulation rule was returned' ); |
Lines 708-714
subtest 'General vs specific rules limit quantity correctly' => sub {
Link Here
|
708 |
}, |
710 |
}, |
709 |
'We are allowed one from the branch specifically, but have one' |
711 |
'We are allowed one from the branch specifically, but have one' |
710 |
); |
712 |
); |
711 |
t::lib::Mocks::mock_preference( 'CircControl', 'ItemHomeLibrary' ); |
713 |
t::lib::Mocks::mock_preference( 'CircControl', 'ItemHomeLibrary' ); |
|
|
714 |
t::lib::Mocks::mock_preference( 'CircControlCheckoutLimitScope', 'item' ); |
712 |
|
715 |
|
713 |
$issue = C4::Circulation::AddIssue( $patron, $branch_item->barcode, dt_from_string() ); |
716 |
$issue = C4::Circulation::AddIssue( $patron, $branch_item->barcode, dt_from_string() ); |
714 |
|
717 |
|
Lines 735-740
subtest 'General vs specific rules limit quantity correctly' => sub {
Link Here
|
735 |
); |
738 |
); |
736 |
|
739 |
|
737 |
# Now we make another from a different branch |
740 |
# Now we make another from a different branch |
|
|
741 |
# Switch to 'all' scope to test general rules counting all checkouts |
742 |
t::lib::Mocks::mock_preference( 'CircControlCheckoutLimitScope', 'all' ); |
738 |
my $item_2 = $builder->build_sample_item( |
743 |
my $item_2 = $builder->build_sample_item( |
739 |
{ |
744 |
{ |
740 |
itype => $itemtype->{itemtype}, |
745 |
itype => $itemtype->{itemtype}, |
Lines 794-800
subtest 'General vs specific rules limit quantity correctly' => sub {
Link Here
|
794 |
'We are only allowed one for general rule, and have two total (no rule for specific branch)' |
799 |
'We are only allowed one for general rule, and have two total (no rule for specific branch)' |
795 |
); |
800 |
); |
796 |
|
801 |
|
797 |
# Set a branch specific rule for new branch |
802 |
# Set a branch specific rule for new branch and use 'checkout' scope |
|
|
803 |
# With PickupLibrary CircControl, we want to count checkouts made at this branch |
804 |
t::lib::Mocks::mock_preference( 'CircControlCheckoutLimitScope', 'checkout' ); |
798 |
Koha::CirculationRules->set_rules( |
805 |
Koha::CirculationRules->set_rules( |
799 |
{ |
806 |
{ |
800 |
branchcode => $branch2->{branchcode}, |
807 |
branchcode => $branch2->{branchcode}, |
Lines 1225-1230
subtest 'HomeOrHoldingBranch is used' => sub {
Link Here
|
1225 |
teardown(); |
1232 |
teardown(); |
1226 |
}; |
1233 |
}; |
1227 |
|
1234 |
|
|
|
1235 |
subtest 'CircControlCheckoutLimitScope system preference controls checkout counting' => sub { |
1236 |
plan tests => 12; |
1237 |
|
1238 |
t::lib::Mocks::mock_preference( 'item-level_itypes', 1 ); |
1239 |
|
1240 |
my $branch1 = $builder->build( { source => 'Branch' } ); |
1241 |
my $branch2 = $builder->build( { source => 'Branch' } ); |
1242 |
my $category = $builder->build( { source => 'Category' } ); |
1243 |
my $itemtype = $builder->build( { source => 'Itemtype', value => { notforloan => 0 } } ); |
1244 |
|
1245 |
my $patron = $builder->build_object( |
1246 |
{ |
1247 |
class => 'Koha::Patrons', |
1248 |
value => { |
1249 |
categorycode => $category->{categorycode}, |
1250 |
branchcode => $branch1->{branchcode}, |
1251 |
} |
1252 |
} |
1253 |
); |
1254 |
|
1255 |
# Create items from different branches |
1256 |
my $item_branch1 = $builder->build_sample_item( |
1257 |
{ |
1258 |
homebranch => $branch1->{branchcode}, |
1259 |
holdingbranch => $branch1->{branchcode}, |
1260 |
itype => $itemtype->{itemtype} |
1261 |
} |
1262 |
); |
1263 |
|
1264 |
my $item_branch2 = $builder->build_sample_item( |
1265 |
{ |
1266 |
homebranch => $branch2->{branchcode}, |
1267 |
holdingbranch => $branch2->{branchcode}, |
1268 |
itype => $itemtype->{itemtype} |
1269 |
} |
1270 |
); |
1271 |
|
1272 |
# Set circulation rules for both branches |
1273 |
Koha::CirculationRules->set_rules( |
1274 |
{ |
1275 |
branchcode => $branch1->{branchcode}, |
1276 |
categorycode => $category->{categorycode}, |
1277 |
itemtype => $itemtype->{itemtype}, |
1278 |
rules => { |
1279 |
maxissueqty => 1, |
1280 |
} |
1281 |
} |
1282 |
); |
1283 |
|
1284 |
Koha::CirculationRules->set_rules( |
1285 |
{ |
1286 |
branchcode => $branch2->{branchcode}, |
1287 |
categorycode => $category->{categorycode}, |
1288 |
itemtype => $itemtype->{itemtype}, |
1289 |
rules => { |
1290 |
maxissueqty => 1, |
1291 |
} |
1292 |
} |
1293 |
); |
1294 |
|
1295 |
# Issue an item from branch1 |
1296 |
t::lib::Mocks::mock_userenv( { branchcode => $branch1->{branchcode} } ); |
1297 |
my $issue = C4::Circulation::AddIssue( $patron, $item_branch1->barcode, dt_from_string() ); |
1298 |
ok( $issue, 'Item from branch1 issued successfully' ); |
1299 |
|
1300 |
# Test with CircControlCheckoutLimitScope = 'checkout' |
1301 |
t::lib::Mocks::mock_preference( 'CircControlCheckoutLimitScope', 'checkout' ); |
1302 |
|
1303 |
# From branch1 - should count checkout made at branch1 |
1304 |
my $data = C4::Circulation::TooMany( $patron, $item_branch1 ); |
1305 |
ok( $data, 'TooMany should return limit exceeded when checking from same pickup library' ); |
1306 |
is( $data->{count}, 1, 'Should count checkout made at this pickup library' ); |
1307 |
is( $data->{reason}, 'TOO_MANY_CHECKOUTS', 'Correct reason returned' ); |
1308 |
|
1309 |
# From branch2 - should not count checkout made at branch1 |
1310 |
t::lib::Mocks::mock_userenv( { branchcode => $branch2->{branchcode} } ); |
1311 |
$data = C4::Circulation::TooMany( $patron, $item_branch2 ); |
1312 |
ok( !$data, 'TooMany should allow checkout when no checkouts from this pickup library' ); |
1313 |
|
1314 |
# Test with CircControlCheckoutLimitScope = 'all' |
1315 |
t::lib::Mocks::mock_preference( 'CircControlCheckoutLimitScope', 'all' ); |
1316 |
|
1317 |
# Should count all patron checkouts regardless of pickup library |
1318 |
$data = C4::Circulation::TooMany( $patron, $item_branch2 ); |
1319 |
ok( $data, 'TooMany should return limit exceeded when counting all checkouts' ); |
1320 |
is( $data->{count}, 1, 'Should count all patron checkouts regardless of branch' ); |
1321 |
|
1322 |
# Test with CircControlCheckoutLimitScope = 'all' from branch1 |
1323 |
t::lib::Mocks::mock_userenv( { branchcode => $branch1->{branchcode} } ); |
1324 |
$data = C4::Circulation::TooMany( $patron, $item_branch2 ); |
1325 |
ok( $data, 'TooMany should return limit exceeded' ); |
1326 |
is( $data->{count}, 1, 'Should count all patron checkouts' ); |
1327 |
|
1328 |
# Test with CircControlCheckoutLimitScope = 'item' |
1329 |
t::lib::Mocks::mock_preference( 'CircControlCheckoutLimitScope', 'item' ); |
1330 |
|
1331 |
# Item from branch2 - should not count checkout of item from branch1 |
1332 |
$data = C4::Circulation::TooMany( $patron, $item_branch2 ); |
1333 |
ok( !$data, 'TooMany should allow checkout when no items from this home library checked out' ); |
1334 |
|
1335 |
# Item from branch1 - should count checkout of item from branch1 |
1336 |
my $item_branch1_2 = $builder->build_sample_item( |
1337 |
{ |
1338 |
homebranch => $branch1->{branchcode}, |
1339 |
holdingbranch => $branch1->{branchcode}, |
1340 |
itype => $itemtype->{itemtype} |
1341 |
} |
1342 |
); |
1343 |
|
1344 |
$data = C4::Circulation::TooMany( $patron, $item_branch1_2 ); |
1345 |
ok( $data, 'TooMany should return limit exceeded when item from same home library already checked out' ); |
1346 |
is( $data->{count}, 1, 'Should count checkout of item from same home library' ); |
1347 |
|
1348 |
teardown(); |
1349 |
}; |
1350 |
|
1351 |
subtest 'CircControlCheckoutLimitScope with HomeOrHoldingBranch preference' => sub { |
1352 |
plan tests => 8; |
1353 |
|
1354 |
t::lib::Mocks::mock_preference( 'item-level_itypes', 1 ); |
1355 |
t::lib::Mocks::mock_preference( 'CircControl', 'ItemHomeLibrary' ); |
1356 |
|
1357 |
my $branch1 = $builder->build( { source => 'Branch' } ); |
1358 |
my $branch2 = $builder->build( { source => 'Branch' } ); |
1359 |
my $category = $builder->build( { source => 'Category' } ); |
1360 |
my $itemtype = $builder->build( { source => 'Itemtype', value => { notforloan => 0 } } ); |
1361 |
|
1362 |
my $patron = $builder->build_object( |
1363 |
{ |
1364 |
class => 'Koha::Patrons', |
1365 |
value => { |
1366 |
categorycode => $category->{categorycode}, |
1367 |
branchcode => $branch1->{branchcode}, |
1368 |
} |
1369 |
} |
1370 |
); |
1371 |
|
1372 |
# Create an item with different home and holding branches |
1373 |
my $item_home1_holding2 = $builder->build_sample_item( |
1374 |
{ |
1375 |
homebranch => $branch1->{branchcode}, |
1376 |
holdingbranch => $branch2->{branchcode}, |
1377 |
itype => $itemtype->{itemtype} |
1378 |
} |
1379 |
); |
1380 |
|
1381 |
my $item_home2_holding1 = $builder->build_sample_item( |
1382 |
{ |
1383 |
homebranch => $branch2->{branchcode}, |
1384 |
holdingbranch => $branch1->{branchcode}, |
1385 |
itype => $itemtype->{itemtype} |
1386 |
} |
1387 |
); |
1388 |
|
1389 |
# Set circulation rules for both branches |
1390 |
Koha::CirculationRules->set_rules( |
1391 |
{ |
1392 |
branchcode => $branch1->{branchcode}, |
1393 |
categorycode => $category->{categorycode}, |
1394 |
itemtype => $itemtype->{itemtype}, |
1395 |
rules => { |
1396 |
maxissueqty => 1, |
1397 |
} |
1398 |
} |
1399 |
); |
1400 |
|
1401 |
Koha::CirculationRules->set_rules( |
1402 |
{ |
1403 |
branchcode => $branch2->{branchcode}, |
1404 |
categorycode => $category->{categorycode}, |
1405 |
itemtype => $itemtype->{itemtype}, |
1406 |
rules => { |
1407 |
maxissueqty => 1, |
1408 |
} |
1409 |
} |
1410 |
); |
1411 |
|
1412 |
# Test with scope='item' and HomeOrHoldingBranch='homebranch' |
1413 |
t::lib::Mocks::mock_preference( 'CircControlCheckoutLimitScope', 'item' ); |
1414 |
t::lib::Mocks::mock_preference( 'HomeOrHoldingBranch', 'homebranch' ); |
1415 |
t::lib::Mocks::mock_userenv( { branchcode => $branch1->{branchcode} } ); |
1416 |
|
1417 |
# Check out item with homebranch=branch1 |
1418 |
my $issue = C4::Circulation::AddIssue( $patron, $item_home1_holding2->barcode, dt_from_string() ); |
1419 |
ok( $issue, 'Item with homebranch=branch1 issued successfully' ); |
1420 |
|
1421 |
# Reload item to get updated holdingbranch (AddIssue may change it) |
1422 |
$item_home1_holding2->discard_changes; |
1423 |
|
1424 |
# Try to check out another item with homebranch=branch1 |
1425 |
my $item_home1_2 = $builder->build_sample_item( |
1426 |
{ |
1427 |
homebranch => $branch1->{branchcode}, |
1428 |
holdingbranch => $branch1->{branchcode}, |
1429 |
itype => $itemtype->{itemtype} |
1430 |
} |
1431 |
); |
1432 |
|
1433 |
my $data = C4::Circulation::TooMany( $patron, $item_home1_2 ); |
1434 |
ok( $data, 'TooMany should block checkout of another item from same homebranch' ); |
1435 |
is( $data->{count}, 1, 'Should count 1 checkout with same homebranch' ); |
1436 |
|
1437 |
# Try to check out item with different homebranch (branch2) |
1438 |
$data = C4::Circulation::TooMany( $patron, $item_home2_holding1 ); |
1439 |
ok( !$data, 'TooMany should allow checkout of item from different homebranch' ); |
1440 |
|
1441 |
# Now test with HomeOrHoldingBranch='holdingbranch' |
1442 |
t::lib::Mocks::mock_preference( 'HomeOrHoldingBranch', 'holdingbranch' ); |
1443 |
|
1444 |
# After checkout, the item's holdingbranch may have been updated to the checkout library |
1445 |
# Get the current holdingbranch from the checked-out item |
1446 |
my $checked_out_holdingbranch = $item_home1_holding2->holdingbranch; |
1447 |
|
1448 |
# Try to check out another item with the same holdingbranch |
1449 |
my $item_holding_same = $builder->build_sample_item( |
1450 |
{ |
1451 |
homebranch => $branch2->{branchcode}, |
1452 |
holdingbranch => $checked_out_holdingbranch, |
1453 |
itype => $itemtype->{itemtype} |
1454 |
} |
1455 |
); |
1456 |
|
1457 |
$data = C4::Circulation::TooMany( $patron, $item_holding_same ); |
1458 |
ok( $data, 'TooMany should block checkout of another item from same holdingbranch' ); |
1459 |
is( $data->{count}, 1, 'Should count 1 checkout with same holdingbranch' ); |
1460 |
|
1461 |
# Try to check out item with different holdingbranch |
1462 |
my $different_holdingbranch = |
1463 |
$checked_out_holdingbranch eq $branch1->{branchcode} ? $branch2->{branchcode} : $branch1->{branchcode}; |
1464 |
my $item_holding_different = $builder->build_sample_item( |
1465 |
{ |
1466 |
homebranch => $branch1->{branchcode}, |
1467 |
holdingbranch => $different_holdingbranch, |
1468 |
itype => $itemtype->{itemtype} |
1469 |
} |
1470 |
); |
1471 |
|
1472 |
$data = C4::Circulation::TooMany( $patron, $item_holding_different ); |
1473 |
ok( !$data, 'TooMany should allow checkout of item from different holdingbranch' ); |
1474 |
|
1475 |
# Verify that we're actually using holdingbranch not homebranch |
1476 |
# item_home1_2 has homebranch=branch1, verify its holdingbranch determines the result |
1477 |
$data = C4::Circulation::TooMany( $patron, $item_home1_2 ); |
1478 |
if ( $item_home1_2->holdingbranch eq $checked_out_holdingbranch ) { |
1479 |
ok( $data, 'Item with same holdingbranch should be blocked' ); |
1480 |
} else { |
1481 |
ok( !$data, 'Item with different holdingbranch should be allowed' ); |
1482 |
} |
1483 |
|
1484 |
teardown(); |
1485 |
}; |
1486 |
|
1487 |
subtest 'CircControlCheckoutLimitScope with patron_maxissueqty' => sub { |
1488 |
plan tests => 9; |
1489 |
|
1490 |
t::lib::Mocks::mock_preference( 'item-level_itypes', 1 ); |
1491 |
|
1492 |
my $branch1 = $builder->build( { source => 'Branch' } ); |
1493 |
my $branch2 = $builder->build( { source => 'Branch' } ); |
1494 |
my $category = $builder->build( { source => 'Category' } ); |
1495 |
my $itemtype = $builder->build( { source => 'Itemtype', value => { notforloan => 0 } } ); |
1496 |
|
1497 |
my $patron = $builder->build_object( |
1498 |
{ |
1499 |
class => 'Koha::Patrons', |
1500 |
value => { |
1501 |
categorycode => $category->{categorycode}, |
1502 |
branchcode => $branch1->{branchcode}, |
1503 |
} |
1504 |
} |
1505 |
); |
1506 |
|
1507 |
# Create items from different branches |
1508 |
my $item_branch1 = $builder->build_sample_item( |
1509 |
{ |
1510 |
homebranch => $branch1->{branchcode}, |
1511 |
holdingbranch => $branch1->{branchcode}, |
1512 |
itype => $itemtype->{itemtype} |
1513 |
} |
1514 |
); |
1515 |
|
1516 |
my $item_branch2 = $builder->build_sample_item( |
1517 |
{ |
1518 |
homebranch => $branch2->{branchcode}, |
1519 |
holdingbranch => $branch2->{branchcode}, |
1520 |
itype => $itemtype->{itemtype} |
1521 |
} |
1522 |
); |
1523 |
|
1524 |
# Set patron_maxissueqty rules for both branches |
1525 |
Koha::CirculationRules->set_rules( |
1526 |
{ |
1527 |
branchcode => $branch1->{branchcode}, |
1528 |
categorycode => $category->{categorycode}, |
1529 |
rules => { |
1530 |
patron_maxissueqty => 1, |
1531 |
} |
1532 |
} |
1533 |
); |
1534 |
|
1535 |
Koha::CirculationRules->set_rules( |
1536 |
{ |
1537 |
branchcode => $branch2->{branchcode}, |
1538 |
categorycode => $category->{categorycode}, |
1539 |
rules => { |
1540 |
patron_maxissueqty => 1, |
1541 |
} |
1542 |
} |
1543 |
); |
1544 |
|
1545 |
# Issue an item from branch1 |
1546 |
t::lib::Mocks::mock_userenv( { branchcode => $branch1->{branchcode} } ); |
1547 |
my $issue = C4::Circulation::AddIssue( $patron, $item_branch1->barcode, dt_from_string() ); |
1548 |
ok( $issue, 'Item from branch1 issued successfully' ); |
1549 |
|
1550 |
# Test with scope='all' - should count all checkouts |
1551 |
t::lib::Mocks::mock_preference( 'CircControlCheckoutLimitScope', 'all' ); |
1552 |
my $data = C4::Circulation::TooMany( $patron, $item_branch1 ); |
1553 |
ok( $data, 'patron_maxissueqty with scope=all should count all checkouts' ); |
1554 |
is( $data->{count}, 1, 'Should count 1 checkout total' ); |
1555 |
|
1556 |
# Test with scope='item' - should count items from same branch |
1557 |
t::lib::Mocks::mock_preference( 'CircControlCheckoutLimitScope', 'item' ); |
1558 |
$data = C4::Circulation::TooMany( $patron, $item_branch1 ); |
1559 |
ok( $data, 'patron_maxissueqty with scope=item should count items from same branch' ); |
1560 |
is( $data->{count}, 1, 'Should count 1 checkout from branch1' ); |
1561 |
|
1562 |
# Item from different branch should be allowed with scope='item' |
1563 |
$data = C4::Circulation::TooMany( $patron, $item_branch2 ); |
1564 |
ok( !$data, 'patron_maxissueqty with scope=item should allow item from different branch' ); |
1565 |
|
1566 |
# Test with scope='checkout' - should count checkouts made at same location |
1567 |
t::lib::Mocks::mock_preference( 'CircControlCheckoutLimitScope', 'checkout' ); |
1568 |
$data = C4::Circulation::TooMany( $patron, $item_branch1 ); |
1569 |
ok( $data, 'patron_maxissueqty with scope=checkout should count checkouts made here' ); |
1570 |
is( $data->{count}, 1, 'Should count 1 checkout made at branch1' ); |
1571 |
|
1572 |
# From different branch should be allowed with scope='checkout' |
1573 |
t::lib::Mocks::mock_userenv( { branchcode => $branch2->{branchcode} } ); |
1574 |
$data = C4::Circulation::TooMany( $patron, $item_branch2 ); |
1575 |
ok( !$data, 'patron_maxissueqty with scope=checkout should allow checkout from different branch' ); |
1576 |
|
1577 |
teardown(); |
1578 |
}; |
1579 |
|
1228 |
$schema->storage->txn_rollback; |
1580 |
$schema->storage->txn_rollback; |
1229 |
|
1581 |
|
1230 |
sub teardown { |
1582 |
sub teardown { |
1231 |
- |
|
|