|
Lines 20-26
Link Here
|
| 20 |
use Modern::Perl; |
20 |
use Modern::Perl; |
| 21 |
|
21 |
|
| 22 |
use Test::NoWarnings; |
22 |
use Test::NoWarnings; |
| 23 |
use Test::More tests => 21; |
23 |
use Test::More tests => 22; |
| 24 |
|
24 |
|
| 25 |
use Test::Exception; |
25 |
use Test::Exception; |
| 26 |
use Test::MockModule; |
26 |
use Test::MockModule; |
|
Lines 32-37
use t::lib::Mocks;
Link Here
|
| 32 |
use C4::Reserves qw(AddReserve); |
32 |
use C4::Reserves qw(AddReserve); |
| 33 |
|
33 |
|
| 34 |
use Koha::ActionLogs; |
34 |
use Koha::ActionLogs; |
|
|
35 |
use Koha::CirculationRules; |
| 35 |
use Koha::DateUtils qw(dt_from_string); |
36 |
use Koha::DateUtils qw(dt_from_string); |
| 36 |
use Koha::Holds; |
37 |
use Koha::Holds; |
| 37 |
use Koha::Libraries; |
38 |
use Koha::Libraries; |
|
Lines 138-156
subtest 'fill() tests' => sub {
Link Here
|
| 138 |
|
139 |
|
| 139 |
my $fee = 15; |
140 |
my $fee = 15; |
| 140 |
|
141 |
|
| 141 |
my $category = $builder->build_object( |
142 |
my $category = $builder->build_object( { class => 'Koha::Patron::Categories' } ); |
| 142 |
{ |
143 |
my $patron = $builder->build_object( |
| 143 |
class => 'Koha::Patron::Categories', |
|
|
| 144 |
value => { reservefee => $fee } |
| 145 |
} |
| 146 |
); |
| 147 |
my $patron = $builder->build_object( |
| 148 |
{ |
144 |
{ |
| 149 |
class => 'Koha::Patrons', |
145 |
class => 'Koha::Patrons', |
| 150 |
value => { categorycode => $category->id } |
146 |
value => { categorycode => $category->id } |
| 151 |
} |
147 |
} |
| 152 |
); |
148 |
); |
| 153 |
my $manager = $builder->build_object( { class => 'Koha::Patrons' } ); |
149 |
my $manager = $builder->build_object( { class => 'Koha::Patrons' } ); |
|
|
150 |
my $library = $builder->build_object( { class => 'Koha::Libraries' } ); |
| 151 |
|
| 152 |
# Set up circulation rules for hold fees |
| 153 |
Koha::CirculationRules->set_rules( |
| 154 |
{ |
| 155 |
branchcode => undef, |
| 156 |
categorycode => $category->id, |
| 157 |
itemtype => undef, |
| 158 |
rules => { |
| 159 |
hold_fee => $fee, |
| 160 |
} |
| 161 |
} |
| 162 |
); |
| 154 |
|
163 |
|
| 155 |
my $title = 'Do what you want'; |
164 |
my $title = 'Do what you want'; |
| 156 |
my $biblio = $builder->build_sample_biblio( { title => $title } ); |
165 |
my $biblio = $builder->build_sample_biblio( { title => $title } ); |
|
Lines 162-167
subtest 'fill() tests' => sub {
Link Here
|
| 162 |
biblionumber => $biblio->id, |
171 |
biblionumber => $biblio->id, |
| 163 |
borrowernumber => $patron->id, |
172 |
borrowernumber => $patron->id, |
| 164 |
itemnumber => $item->id, |
173 |
itemnumber => $item->id, |
|
|
174 |
branchcode => $library->branchcode, |
| 165 |
timestamp => dt_from_string('2021-06-25 14:05:35'), |
175 |
timestamp => dt_from_string('2021-06-25 14:05:35'), |
| 166 |
priority => 10, |
176 |
priority => 10, |
| 167 |
} |
177 |
} |
|
Lines 190-201
subtest 'fill() tests' => sub {
Link Here
|
| 190 |
|
200 |
|
| 191 |
subtest 'item_id parameter' => sub { |
201 |
subtest 'item_id parameter' => sub { |
| 192 |
plan tests => 1; |
202 |
plan tests => 1; |
| 193 |
$category->reservefee(0)->store; # do not disturb later accounts |
203 |
|
|
|
204 |
# Clear hold fee rule to not disturb later accounts |
| 205 |
Koha::CirculationRules->set_rules( |
| 206 |
{ |
| 207 |
branchcode => undef, |
| 208 |
categorycode => $category->id, |
| 209 |
itemtype => undef, |
| 210 |
rules => { |
| 211 |
hold_fee => 0, |
| 212 |
} |
| 213 |
} |
| 214 |
); |
| 194 |
$hold = $builder->build_object( |
215 |
$hold = $builder->build_object( |
| 195 |
{ |
216 |
{ |
| 196 |
class => 'Koha::Holds', |
217 |
class => 'Koha::Holds', |
| 197 |
value => |
218 |
value => { |
| 198 |
{ biblionumber => $biblio->id, borrowernumber => $patron->id, itemnumber => undef, priority => 1 } |
219 |
biblionumber => $biblio->id, borrowernumber => $patron->id, itemnumber => undef, priority => 1, |
|
|
220 |
branchcode => $library->branchcode |
| 221 |
} |
| 199 |
} |
222 |
} |
| 200 |
); |
223 |
); |
| 201 |
|
224 |
|
|
Lines 204-210
subtest 'fill() tests' => sub {
Link Here
|
| 204 |
$old_hold = Koha::Old::Holds->find( $hold->id ); |
227 |
$old_hold = Koha::Old::Holds->find( $hold->id ); |
| 205 |
is( $old_hold->itemnumber, $item->itemnumber, 'The itemnumber has been saved in old_reserves by fill' ); |
228 |
is( $old_hold->itemnumber, $item->itemnumber, 'The itemnumber has been saved in old_reserves by fill' ); |
| 206 |
$old_hold->delete; |
229 |
$old_hold->delete; |
| 207 |
$category->reservefee($fee)->store; # restore |
230 |
|
|
|
231 |
# Restore hold fee rule |
| 232 |
Koha::CirculationRules->set_rules( |
| 233 |
{ |
| 234 |
branchcode => undef, |
| 235 |
categorycode => $category->id, |
| 236 |
itemtype => undef, |
| 237 |
rules => { |
| 238 |
hold_fee => $fee, |
| 239 |
} |
| 240 |
} |
| 241 |
); |
| 208 |
}; |
242 |
}; |
| 209 |
|
243 |
|
| 210 |
subtest 'fee applied tests' => sub { |
244 |
subtest 'fee applied tests' => sub { |
|
Lines 1884-1889
subtest 'move_hold() tests' => sub {
Link Here
|
| 1884 |
|
1918 |
|
| 1885 |
$schema->storage->txn_rollback; |
1919 |
$schema->storage->txn_rollback; |
| 1886 |
}; |
1920 |
}; |
|
|
1921 |
|
| 1887 |
subtest 'is_hold_group_target, cleanup_hold_group and set_as_hold_group_target tests' => sub { |
1922 |
subtest 'is_hold_group_target, cleanup_hold_group and set_as_hold_group_target tests' => sub { |
| 1888 |
|
1923 |
|
| 1889 |
plan tests => 16; |
1924 |
plan tests => 16; |
|
Lines 2188-2195
subtest '_Findgroupreserve in the context of hold groups' => sub {
Link Here
|
| 2188 |
); |
2223 |
); |
| 2189 |
}; |
2224 |
}; |
| 2190 |
|
2225 |
|
|
|
2226 |
subtest 'calculate_hold_fee() tests' => sub { |
| 2227 |
plan tests => 12; |
| 2228 |
|
| 2229 |
$schema->storage->txn_begin; |
| 2230 |
|
| 2231 |
# Create patron category and patron |
| 2232 |
my $cat = $builder->build( { source => 'Category', value => { categorycode => 'XYZ1' } } ); |
| 2233 |
my $patron1 = $builder->build_object( |
| 2234 |
{ |
| 2235 |
class => 'Koha::Patrons', |
| 2236 |
value => { categorycode => 'XYZ1' } |
| 2237 |
} |
| 2238 |
); |
| 2239 |
my $patron2 = $builder->build_object( |
| 2240 |
{ |
| 2241 |
class => 'Koha::Patrons', |
| 2242 |
value => { categorycode => 'XYZ1' } |
| 2243 |
} |
| 2244 |
); |
| 2245 |
|
| 2246 |
# Create itemtypes with different fees |
| 2247 |
my $itemtype1 = $builder->build( { source => 'Itemtype' } ); |
| 2248 |
my $itemtype2 = $builder->build( { source => 'Itemtype' } ); |
| 2249 |
my $itemtype3 = $builder->build( { source => 'Itemtype' } ); |
| 2250 |
|
| 2251 |
# Set up circulation rules with different hold fees for different itemtypes |
| 2252 |
Koha::CirculationRules->set_rules( |
| 2253 |
{ |
| 2254 |
branchcode => undef, |
| 2255 |
categorycode => 'XYZ1', |
| 2256 |
itemtype => $itemtype1->{itemtype}, |
| 2257 |
rules => { hold_fee => 1.00, reservesallowed => 10 } |
| 2258 |
} |
| 2259 |
); |
| 2260 |
Koha::CirculationRules->set_rules( |
| 2261 |
{ |
| 2262 |
branchcode => undef, |
| 2263 |
categorycode => 'XYZ1', |
| 2264 |
itemtype => $itemtype2->{itemtype}, |
| 2265 |
rules => { hold_fee => 3.00, reservesallowed => 10 } |
| 2266 |
} |
| 2267 |
); |
| 2268 |
Koha::CirculationRules->set_rules( |
| 2269 |
{ |
| 2270 |
branchcode => undef, |
| 2271 |
categorycode => 'XYZ1', |
| 2272 |
itemtype => $itemtype3->{itemtype}, |
| 2273 |
rules => { hold_fee => 2.00, reservesallowed => 10 } |
| 2274 |
} |
| 2275 |
); |
| 2276 |
|
| 2277 |
# Set generic rules for permission checking |
| 2278 |
Koha::CirculationRules->set_rules( |
| 2279 |
{ |
| 2280 |
branchcode => undef, |
| 2281 |
categorycode => undef, |
| 2282 |
itemtype => undef, |
| 2283 |
rules => { reservesallowed => 10, holds_per_record => 10, holds_per_day => 10 } |
| 2284 |
} |
| 2285 |
); |
| 2286 |
|
| 2287 |
my $biblio = $builder->build_sample_biblio(); |
| 2288 |
|
| 2289 |
# Create multiple items with different itemtypes and fees |
| 2290 |
my $item1 = $builder->build_sample_item( |
| 2291 |
{ |
| 2292 |
biblionumber => $biblio->biblionumber, |
| 2293 |
itype => $itemtype1->{itemtype}, |
| 2294 |
notforloan => 0, |
| 2295 |
} |
| 2296 |
); |
| 2297 |
my $item2 = $builder->build_sample_item( |
| 2298 |
{ |
| 2299 |
biblionumber => $biblio->biblionumber, |
| 2300 |
itype => $itemtype2->{itemtype}, |
| 2301 |
notforloan => 0, |
| 2302 |
} |
| 2303 |
); |
| 2304 |
my $item3 = $builder->build_sample_item( |
| 2305 |
{ |
| 2306 |
biblionumber => $biblio->biblionumber, |
| 2307 |
itype => $itemtype3->{itemtype}, |
| 2308 |
notforloan => 0, |
| 2309 |
} |
| 2310 |
); |
| 2311 |
|
| 2312 |
# Test 1: Item-level hold calculates fee correctly |
| 2313 |
my $item_hold = $builder->build_object( |
| 2314 |
{ |
| 2315 |
class => 'Koha::Holds', |
| 2316 |
value => { |
| 2317 |
borrowernumber => $patron1->borrowernumber, |
| 2318 |
biblionumber => $biblio->biblionumber, |
| 2319 |
itemnumber => $item2->itemnumber, # Use item2 with 3.00 fee |
| 2320 |
} |
| 2321 |
} |
| 2322 |
); |
| 2323 |
|
| 2324 |
my $fee = $item_hold->calculate_hold_fee(); |
| 2325 |
is( $fee, 3.00, 'Item-level hold calculates fee correctly' ); |
| 2326 |
|
| 2327 |
# Create title-level hold for comprehensive testing |
| 2328 |
my $title_hold = $builder->build_object( |
| 2329 |
{ |
| 2330 |
class => 'Koha::Holds', |
| 2331 |
value => { |
| 2332 |
borrowernumber => $patron2->borrowernumber, |
| 2333 |
biblionumber => $biblio->biblionumber, |
| 2334 |
itemnumber => undef, |
| 2335 |
} |
| 2336 |
} |
| 2337 |
); |
| 2338 |
|
| 2339 |
# Test 2: Default 'highest' strategy should return 3.00 (highest fee) |
| 2340 |
t::lib::Mocks::mock_preference( 'TitleHoldFeeStrategy', 'highest' ); |
| 2341 |
$fee = $title_hold->calculate_hold_fee(); |
| 2342 |
is( $fee, 3.00, 'Title-level hold: highest strategy returns highest fee (3.00)' ); |
| 2343 |
|
| 2344 |
# Test 3: 'lowest' strategy should return 1.00 (lowest fee) |
| 2345 |
t::lib::Mocks::mock_preference( 'TitleHoldFeeStrategy', 'lowest' ); |
| 2346 |
$fee = $title_hold->calculate_hold_fee(); |
| 2347 |
is( $fee, 1.00, 'Title-level hold: lowest strategy returns lowest fee (1.00)' ); |
| 2348 |
|
| 2349 |
# Test 4: 'most_common' strategy with unique fees should return highest |
| 2350 |
t::lib::Mocks::mock_preference( 'TitleHoldFeeStrategy', 'most_common' ); |
| 2351 |
$fee = $title_hold->calculate_hold_fee(); |
| 2352 |
is( $fee, 3.00, 'Title-level hold: most_common strategy with unique fees returns highest fee (3.00)' ); |
| 2353 |
|
| 2354 |
# Test 5: Add another item with same fee to test most_common properly |
| 2355 |
my $item4 = $builder->build_sample_item( |
| 2356 |
{ |
| 2357 |
biblionumber => $biblio->biblionumber, |
| 2358 |
itype => $itemtype1->{itemtype}, |
| 2359 |
notforloan => 0, |
| 2360 |
} |
| 2361 |
); |
| 2362 |
$fee = $title_hold->calculate_hold_fee(); |
| 2363 |
is( $fee, 1.00, 'Title-level hold: most_common strategy returns most common fee (1.00 - 2 items)' ); |
| 2364 |
|
| 2365 |
# Test 6: Unknown/invalid strategy defaults to 'highest' |
| 2366 |
t::lib::Mocks::mock_preference( 'TitleHoldFeeStrategy', 'invalid_strategy' ); |
| 2367 |
$fee = $title_hold->calculate_hold_fee(); |
| 2368 |
is( $fee, 3.00, 'Title-level hold: invalid strategy defaults to highest fee (3.00)' ); |
| 2369 |
|
| 2370 |
# Test 7: Empty preference defaults to 'highest' |
| 2371 |
t::lib::Mocks::mock_preference( 'TitleHoldFeeStrategy', '' ); |
| 2372 |
$fee = $title_hold->calculate_hold_fee(); |
| 2373 |
is( $fee, 3.00, 'Title-level hold: empty strategy preference defaults to highest fee (3.00)' ); |
| 2374 |
|
| 2375 |
# Test 8: No holdable items returns 0 |
| 2376 |
# Make all items not holdable |
| 2377 |
$item1->notforloan(1)->store; |
| 2378 |
$item2->notforloan(1)->store; |
| 2379 |
$item3->notforloan(1)->store; |
| 2380 |
$item4->notforloan(1)->store; |
| 2381 |
|
| 2382 |
$fee = $title_hold->calculate_hold_fee(); |
| 2383 |
is( $fee, 0, 'Title-level hold: no holdable items returns 0 fee' ); |
| 2384 |
|
| 2385 |
# Test 9: Mix of holdable and non-holdable items (highest) |
| 2386 |
# Make some items holdable again |
| 2387 |
$item2->notforloan(0)->store; # Fee: 3.00 |
| 2388 |
$item3->notforloan(0)->store; # Fee: 2.00 |
| 2389 |
|
| 2390 |
t::lib::Mocks::mock_preference( 'TitleHoldFeeStrategy', 'highest' ); |
| 2391 |
$fee = $title_hold->calculate_hold_fee(); |
| 2392 |
is( $fee, 3.00, 'Title-level hold: highest strategy with mixed holdable items returns 3.00' ); |
| 2393 |
|
| 2394 |
# Test 10: Mix of holdable and non-holdable items (lowest) |
| 2395 |
t::lib::Mocks::mock_preference( 'TitleHoldFeeStrategy', 'lowest' ); |
| 2396 |
$fee = $title_hold->calculate_hold_fee(); |
| 2397 |
is( $fee, 2.00, 'Title-level hold: lowest strategy with mixed holdable items returns 2.00' ); |
| 2398 |
|
| 2399 |
# Test 11: Items with 0 fee |
| 2400 |
my $itemtype_free = $builder->build( { source => 'Itemtype' } ); |
| 2401 |
Koha::CirculationRules->set_rules( |
| 2402 |
{ |
| 2403 |
branchcode => undef, |
| 2404 |
categorycode => 'XYZ1', |
| 2405 |
itemtype => $itemtype_free->{itemtype}, |
| 2406 |
rules => { hold_fee => 0, reservesallowed => 10 } |
| 2407 |
} |
| 2408 |
); |
| 2409 |
|
| 2410 |
my $item_free = $builder->build_sample_item( |
| 2411 |
{ |
| 2412 |
biblionumber => $biblio->biblionumber, |
| 2413 |
itype => $itemtype_free->{itemtype}, |
| 2414 |
notforloan => 0, |
| 2415 |
} |
| 2416 |
); |
| 2417 |
|
| 2418 |
t::lib::Mocks::mock_preference( 'TitleHoldFeeStrategy', 'lowest' ); |
| 2419 |
$fee = $title_hold->calculate_hold_fee(); |
| 2420 |
is( $fee, 0, 'Title-level hold: lowest strategy with free item returns 0' ); |
| 2421 |
|
| 2422 |
# Test 12: All items have same fee |
| 2423 |
# Set all holdable items to same fee |
| 2424 |
Koha::CirculationRules->set_rules( |
| 2425 |
{ |
| 2426 |
branchcode => undef, |
| 2427 |
categorycode => 'XYZ1', |
| 2428 |
itemtype => $itemtype2->{itemtype}, |
| 2429 |
rules => { hold_fee => 2.50, reservesallowed => 10 } |
| 2430 |
} |
| 2431 |
); |
| 2432 |
Koha::CirculationRules->set_rules( |
| 2433 |
{ |
| 2434 |
branchcode => undef, |
| 2435 |
categorycode => 'XYZ1', |
| 2436 |
itemtype => $itemtype3->{itemtype}, |
| 2437 |
rules => { hold_fee => 2.50, reservesallowed => 10 } |
| 2438 |
} |
| 2439 |
); |
| 2440 |
Koha::CirculationRules->set_rules( |
| 2441 |
{ |
| 2442 |
branchcode => undef, |
| 2443 |
categorycode => 'XYZ1', |
| 2444 |
itemtype => $itemtype_free->{itemtype}, |
| 2445 |
rules => { hold_fee => 2.50, reservesallowed => 10 } |
| 2446 |
} |
| 2447 |
); |
| 2448 |
|
| 2449 |
$fee = $title_hold->calculate_hold_fee(); |
| 2450 |
is( $fee, 2.50, 'Title-level hold: all items with same fee returns that fee regardless of strategy' ); |
| 2451 |
|
| 2452 |
$schema->storage->txn_rollback; |
| 2453 |
}; |
| 2454 |
|
| 2455 |
subtest 'should_charge() tests' => sub { |
| 2456 |
plan tests => 6; |
| 2457 |
|
| 2458 |
$schema->storage->txn_begin; |
| 2459 |
|
| 2460 |
my $patron = $builder->build_object( { class => 'Koha::Patrons' } ); |
| 2461 |
my $biblio = $builder->build_sample_biblio(); |
| 2462 |
my $item = $builder->build_sample_item( { biblionumber => $biblio->biblionumber } ); |
| 2463 |
|
| 2464 |
my $hold = $builder->build_object( |
| 2465 |
{ |
| 2466 |
class => 'Koha::Holds', |
| 2467 |
value => { |
| 2468 |
borrowernumber => $patron->borrowernumber, |
| 2469 |
biblionumber => $biblio->biblionumber, |
| 2470 |
itemnumber => $item->itemnumber, |
| 2471 |
} |
| 2472 |
} |
| 2473 |
); |
| 2474 |
|
| 2475 |
# Test any_time_is_placed mode |
| 2476 |
t::lib::Mocks::mock_preference( 'HoldFeeMode', 'any_time_is_placed' ); |
| 2477 |
ok( $hold->should_charge('placement'), 'any_time_is_placed charges at placement' ); |
| 2478 |
ok( !$hold->should_charge('collection'), 'any_time_is_placed does not charge at collection' ); |
| 2479 |
|
| 2480 |
# Test any_time_is_collected mode |
| 2481 |
t::lib::Mocks::mock_preference( 'HoldFeeMode', 'any_time_is_collected' ); |
| 2482 |
ok( !$hold->should_charge('placement'), 'any_time_is_collected does not charge at placement' ); |
| 2483 |
ok( $hold->should_charge('collection'), 'any_time_is_collected charges at collection' ); |
| 2484 |
|
| 2485 |
# Test not_always mode (basic case - items available) |
| 2486 |
t::lib::Mocks::mock_preference( 'HoldFeeMode', 'not_always' ); |
| 2487 |
ok( !$hold->should_charge('placement'), 'not_always does not charge at placement when items available' ); |
| 2488 |
ok( !$hold->should_charge('collection'), 'not_always does not charge at collection' ); |
| 2489 |
|
| 2490 |
$schema->storage->txn_rollback; |
| 2491 |
}; |
| 2492 |
|
| 2493 |
subtest 'charge_hold_fee() tests' => sub { |
| 2494 |
plan tests => 2; |
| 2495 |
|
| 2496 |
$schema->storage->txn_begin; |
| 2497 |
|
| 2498 |
my $cat = $builder->build( { source => 'Category', value => { categorycode => 'XYZ1' } } ); |
| 2499 |
|
| 2500 |
# Set up circulation rules for hold fees |
| 2501 |
Koha::CirculationRules->set_rules( |
| 2502 |
{ |
| 2503 |
branchcode => undef, |
| 2504 |
categorycode => 'XYZ1', |
| 2505 |
itemtype => undef, |
| 2506 |
rules => { |
| 2507 |
hold_fee => 2.00, |
| 2508 |
} |
| 2509 |
} |
| 2510 |
); |
| 2511 |
|
| 2512 |
my $patron = $builder->build_object( |
| 2513 |
{ |
| 2514 |
class => 'Koha::Patrons', |
| 2515 |
value => { categorycode => 'XYZ1' } |
| 2516 |
} |
| 2517 |
); |
| 2518 |
t::lib::Mocks::mock_userenv( { patron => $patron, branchcode => $patron->branchcode } ); |
| 2519 |
|
| 2520 |
my $biblio = $builder->build_sample_biblio(); |
| 2521 |
my $item = $builder->build_sample_item( { biblionumber => $biblio->biblionumber } ); |
| 2522 |
|
| 2523 |
my $hold = $builder->build_object( |
| 2524 |
{ |
| 2525 |
class => 'Koha::Holds', |
| 2526 |
value => { |
| 2527 |
borrowernumber => $patron->borrowernumber, |
| 2528 |
biblionumber => $biblio->biblionumber, |
| 2529 |
itemnumber => $item->itemnumber, |
| 2530 |
} |
| 2531 |
} |
| 2532 |
); |
| 2533 |
|
| 2534 |
my $account = $patron->account; |
| 2535 |
my $balance_before = $account->balance; |
| 2536 |
|
| 2537 |
# Charge fee |
| 2538 |
my $account_line = $hold->charge_hold_fee(); |
| 2539 |
is( $account_line->amount, 2.00, 'charge_hold_fee returns account line with correct amount' ); |
| 2540 |
|
| 2541 |
my $balance_after = $account->balance; |
| 2542 |
is( $balance_after, $balance_before + 2.00, 'Patron account charged correctly' ); |
| 2543 |
|
| 2544 |
$schema->storage->txn_rollback; |
| 2545 |
}; |
| 2546 |
|
| 2191 |
sub set_userenv { |
2547 |
sub set_userenv { |
| 2192 |
my ($library) = @_; |
2548 |
my ($library) = @_; |
| 2193 |
my $staff = $builder->build_object( { class => "Koha::Patrons" } ); |
2549 |
my $staff = $builder->build_object( { class => "Koha::Patrons" } ); |
| 2194 |
t::lib::Mocks::mock_userenv( { patron => $staff, branchcode => $library->{branchcode} } ); |
2550 |
t::lib::Mocks::mock_userenv( { patron => $staff, branchcode => $library->{branchcode} } ); |
| 2195 |
} |
2551 |
} |
|
|
2552 |
|