|
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 => 20; |
23 |
use Test::More tests => 23; |
| 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 117-135
subtest 'fill() tests' => sub {
Link Here
|
| 117 |
|
118 |
|
| 118 |
my $fee = 15; |
119 |
my $fee = 15; |
| 119 |
|
120 |
|
| 120 |
my $category = $builder->build_object( |
121 |
my $category = $builder->build_object( { class => 'Koha::Patron::Categories' } ); |
| 121 |
{ |
122 |
my $patron = $builder->build_object( |
| 122 |
class => 'Koha::Patron::Categories', |
|
|
| 123 |
value => { reservefee => $fee } |
| 124 |
} |
| 125 |
); |
| 126 |
my $patron = $builder->build_object( |
| 127 |
{ |
123 |
{ |
| 128 |
class => 'Koha::Patrons', |
124 |
class => 'Koha::Patrons', |
| 129 |
value => { categorycode => $category->id } |
125 |
value => { categorycode => $category->id } |
| 130 |
} |
126 |
} |
| 131 |
); |
127 |
); |
| 132 |
my $manager = $builder->build_object( { class => 'Koha::Patrons' } ); |
128 |
my $manager = $builder->build_object( { class => 'Koha::Patrons' } ); |
|
|
129 |
my $library = $builder->build_object( { class => 'Koha::Libraries' } ); |
| 130 |
|
| 131 |
# Set up circulation rules for hold fees |
| 132 |
Koha::CirculationRules->set_rules( |
| 133 |
{ |
| 134 |
branchcode => undef, |
| 135 |
categorycode => $category->id, |
| 136 |
itemtype => undef, |
| 137 |
rules => { |
| 138 |
hold_fee => $fee, |
| 139 |
} |
| 140 |
} |
| 141 |
); |
| 133 |
|
142 |
|
| 134 |
my $title = 'Do what you want'; |
143 |
my $title = 'Do what you want'; |
| 135 |
my $biblio = $builder->build_sample_biblio( { title => $title } ); |
144 |
my $biblio = $builder->build_sample_biblio( { title => $title } ); |
|
Lines 141-146
subtest 'fill() tests' => sub {
Link Here
|
| 141 |
biblionumber => $biblio->id, |
150 |
biblionumber => $biblio->id, |
| 142 |
borrowernumber => $patron->id, |
151 |
borrowernumber => $patron->id, |
| 143 |
itemnumber => $item->id, |
152 |
itemnumber => $item->id, |
|
|
153 |
branchcode => $library->branchcode, |
| 144 |
timestamp => dt_from_string('2021-06-25 14:05:35'), |
154 |
timestamp => dt_from_string('2021-06-25 14:05:35'), |
| 145 |
priority => 10, |
155 |
priority => 10, |
| 146 |
} |
156 |
} |
|
Lines 169-180
subtest 'fill() tests' => sub {
Link Here
|
| 169 |
|
179 |
|
| 170 |
subtest 'item_id parameter' => sub { |
180 |
subtest 'item_id parameter' => sub { |
| 171 |
plan tests => 1; |
181 |
plan tests => 1; |
| 172 |
$category->reservefee(0)->store; # do not disturb later accounts |
182 |
|
|
|
183 |
# Clear hold fee rule to not disturb later accounts |
| 184 |
Koha::CirculationRules->set_rules( |
| 185 |
{ |
| 186 |
branchcode => undef, |
| 187 |
categorycode => $category->id, |
| 188 |
itemtype => undef, |
| 189 |
rules => { |
| 190 |
hold_fee => 0, |
| 191 |
} |
| 192 |
} |
| 193 |
); |
| 173 |
$hold = $builder->build_object( |
194 |
$hold = $builder->build_object( |
| 174 |
{ |
195 |
{ |
| 175 |
class => 'Koha::Holds', |
196 |
class => 'Koha::Holds', |
| 176 |
value => |
197 |
value => { |
| 177 |
{ biblionumber => $biblio->id, borrowernumber => $patron->id, itemnumber => undef, priority => 1 } |
198 |
biblionumber => $biblio->id, borrowernumber => $patron->id, itemnumber => undef, priority => 1, |
|
|
199 |
branchcode => $library->branchcode |
| 200 |
} |
| 178 |
} |
201 |
} |
| 179 |
); |
202 |
); |
| 180 |
|
203 |
|
|
Lines 183-189
subtest 'fill() tests' => sub {
Link Here
|
| 183 |
$old_hold = Koha::Old::Holds->find( $hold->id ); |
206 |
$old_hold = Koha::Old::Holds->find( $hold->id ); |
| 184 |
is( $old_hold->itemnumber, $item->itemnumber, 'The itemnumber has been saved in old_reserves by fill' ); |
207 |
is( $old_hold->itemnumber, $item->itemnumber, 'The itemnumber has been saved in old_reserves by fill' ); |
| 185 |
$old_hold->delete; |
208 |
$old_hold->delete; |
| 186 |
$category->reservefee($fee)->store; # restore |
209 |
|
|
|
210 |
# Restore hold fee rule |
| 211 |
Koha::CirculationRules->set_rules( |
| 212 |
{ |
| 213 |
branchcode => undef, |
| 214 |
categorycode => $category->id, |
| 215 |
itemtype => undef, |
| 216 |
rules => { |
| 217 |
hold_fee => $fee, |
| 218 |
} |
| 219 |
} |
| 220 |
); |
| 187 |
}; |
221 |
}; |
| 188 |
|
222 |
|
| 189 |
subtest 'fee applied tests' => sub { |
223 |
subtest 'fee applied tests' => sub { |
|
Lines 1849-1854
subtest 'move_hold() tests' => sub {
Link Here
|
| 1849 |
|
1883 |
|
| 1850 |
$schema->storage->txn_rollback; |
1884 |
$schema->storage->txn_rollback; |
| 1851 |
}; |
1885 |
}; |
|
|
1886 |
|
| 1852 |
subtest 'is_hold_group_target, cleanup_hold_group and set_as_hold_group_target tests' => sub { |
1887 |
subtest 'is_hold_group_target, cleanup_hold_group and set_as_hold_group_target tests' => sub { |
| 1853 |
|
1888 |
|
| 1854 |
plan tests => 16; |
1889 |
plan tests => 16; |
|
Lines 2153-2160
subtest '_Findgroupreserve in the context of hold groups' => sub {
Link Here
|
| 2153 |
); |
2188 |
); |
| 2154 |
}; |
2189 |
}; |
| 2155 |
|
2190 |
|
|
|
2191 |
subtest 'calculate_hold_fee() tests' => sub { |
| 2192 |
plan tests => 12; |
| 2193 |
|
| 2194 |
$schema->storage->txn_begin; |
| 2195 |
|
| 2196 |
# Create patron category and patron |
| 2197 |
my $cat = $builder->build( { source => 'Category', value => { categorycode => 'XYZ1' } } ); |
| 2198 |
my $patron1 = $builder->build_object( |
| 2199 |
{ |
| 2200 |
class => 'Koha::Patrons', |
| 2201 |
value => { categorycode => 'XYZ1' } |
| 2202 |
} |
| 2203 |
); |
| 2204 |
my $patron2 = $builder->build_object( |
| 2205 |
{ |
| 2206 |
class => 'Koha::Patrons', |
| 2207 |
value => { categorycode => 'XYZ1' } |
| 2208 |
} |
| 2209 |
); |
| 2210 |
|
| 2211 |
# Create itemtypes with different fees |
| 2212 |
my $itemtype1 = $builder->build( { source => 'Itemtype' } ); |
| 2213 |
my $itemtype2 = $builder->build( { source => 'Itemtype' } ); |
| 2214 |
my $itemtype3 = $builder->build( { source => 'Itemtype' } ); |
| 2215 |
|
| 2216 |
# Set up circulation rules with different hold fees for different itemtypes |
| 2217 |
Koha::CirculationRules->set_rules( |
| 2218 |
{ |
| 2219 |
branchcode => undef, |
| 2220 |
categorycode => 'XYZ1', |
| 2221 |
itemtype => $itemtype1->{itemtype}, |
| 2222 |
rules => { hold_fee => 1.00, reservesallowed => 10 } |
| 2223 |
} |
| 2224 |
); |
| 2225 |
Koha::CirculationRules->set_rules( |
| 2226 |
{ |
| 2227 |
branchcode => undef, |
| 2228 |
categorycode => 'XYZ1', |
| 2229 |
itemtype => $itemtype2->{itemtype}, |
| 2230 |
rules => { hold_fee => 3.00, reservesallowed => 10 } |
| 2231 |
} |
| 2232 |
); |
| 2233 |
Koha::CirculationRules->set_rules( |
| 2234 |
{ |
| 2235 |
branchcode => undef, |
| 2236 |
categorycode => 'XYZ1', |
| 2237 |
itemtype => $itemtype3->{itemtype}, |
| 2238 |
rules => { hold_fee => 2.00, reservesallowed => 10 } |
| 2239 |
} |
| 2240 |
); |
| 2241 |
|
| 2242 |
# Set generic rules for permission checking |
| 2243 |
Koha::CirculationRules->set_rules( |
| 2244 |
{ |
| 2245 |
branchcode => undef, |
| 2246 |
categorycode => undef, |
| 2247 |
itemtype => undef, |
| 2248 |
rules => { reservesallowed => 10, holds_per_record => 10, holds_per_day => 10 } |
| 2249 |
} |
| 2250 |
); |
| 2251 |
|
| 2252 |
my $biblio = $builder->build_sample_biblio(); |
| 2253 |
|
| 2254 |
# Create multiple items with different itemtypes and fees |
| 2255 |
my $item1 = $builder->build_sample_item( |
| 2256 |
{ |
| 2257 |
biblionumber => $biblio->biblionumber, |
| 2258 |
itype => $itemtype1->{itemtype}, |
| 2259 |
notforloan => 0, |
| 2260 |
} |
| 2261 |
); |
| 2262 |
my $item2 = $builder->build_sample_item( |
| 2263 |
{ |
| 2264 |
biblionumber => $biblio->biblionumber, |
| 2265 |
itype => $itemtype2->{itemtype}, |
| 2266 |
notforloan => 0, |
| 2267 |
} |
| 2268 |
); |
| 2269 |
my $item3 = $builder->build_sample_item( |
| 2270 |
{ |
| 2271 |
biblionumber => $biblio->biblionumber, |
| 2272 |
itype => $itemtype3->{itemtype}, |
| 2273 |
notforloan => 0, |
| 2274 |
} |
| 2275 |
); |
| 2276 |
|
| 2277 |
# Test 1: Item-level hold calculates fee correctly |
| 2278 |
my $item_hold = $builder->build_object( |
| 2279 |
{ |
| 2280 |
class => 'Koha::Holds', |
| 2281 |
value => { |
| 2282 |
borrowernumber => $patron1->borrowernumber, |
| 2283 |
biblionumber => $biblio->biblionumber, |
| 2284 |
itemnumber => $item2->itemnumber, # Use item2 with 3.00 fee |
| 2285 |
} |
| 2286 |
} |
| 2287 |
); |
| 2288 |
|
| 2289 |
my $fee = $item_hold->calculate_hold_fee(); |
| 2290 |
is( $fee, 3.00, 'Item-level hold calculates fee correctly' ); |
| 2291 |
|
| 2292 |
# Create title-level hold for comprehensive testing |
| 2293 |
my $title_hold = $builder->build_object( |
| 2294 |
{ |
| 2295 |
class => 'Koha::Holds', |
| 2296 |
value => { |
| 2297 |
borrowernumber => $patron2->borrowernumber, |
| 2298 |
biblionumber => $biblio->biblionumber, |
| 2299 |
itemnumber => undef, |
| 2300 |
} |
| 2301 |
} |
| 2302 |
); |
| 2303 |
|
| 2304 |
# Test 2: Default 'highest' strategy should return 3.00 (highest fee) |
| 2305 |
t::lib::Mocks::mock_preference( 'TitleHoldFeeStrategy', 'highest' ); |
| 2306 |
$fee = $title_hold->calculate_hold_fee(); |
| 2307 |
is( $fee, 3.00, 'Title-level hold: highest strategy returns highest fee (3.00)' ); |
| 2308 |
|
| 2309 |
# Test 3: 'lowest' strategy should return 1.00 (lowest fee) |
| 2310 |
t::lib::Mocks::mock_preference( 'TitleHoldFeeStrategy', 'lowest' ); |
| 2311 |
$fee = $title_hold->calculate_hold_fee(); |
| 2312 |
is( $fee, 1.00, 'Title-level hold: lowest strategy returns lowest fee (1.00)' ); |
| 2313 |
|
| 2314 |
# Test 4: 'most_common' strategy with unique fees should return highest |
| 2315 |
t::lib::Mocks::mock_preference( 'TitleHoldFeeStrategy', 'most_common' ); |
| 2316 |
$fee = $title_hold->calculate_hold_fee(); |
| 2317 |
is( $fee, 3.00, 'Title-level hold: most_common strategy with unique fees returns highest fee (3.00)' ); |
| 2318 |
|
| 2319 |
# Test 5: Add another item with same fee to test most_common properly |
| 2320 |
my $item4 = $builder->build_sample_item( |
| 2321 |
{ |
| 2322 |
biblionumber => $biblio->biblionumber, |
| 2323 |
itype => $itemtype1->{itemtype}, |
| 2324 |
notforloan => 0, |
| 2325 |
} |
| 2326 |
); |
| 2327 |
$fee = $title_hold->calculate_hold_fee(); |
| 2328 |
is( $fee, 1.00, 'Title-level hold: most_common strategy returns most common fee (1.00 - 2 items)' ); |
| 2329 |
|
| 2330 |
# Test 6: Unknown/invalid strategy defaults to 'highest' |
| 2331 |
t::lib::Mocks::mock_preference( 'TitleHoldFeeStrategy', 'invalid_strategy' ); |
| 2332 |
$fee = $title_hold->calculate_hold_fee(); |
| 2333 |
is( $fee, 3.00, 'Title-level hold: invalid strategy defaults to highest fee (3.00)' ); |
| 2334 |
|
| 2335 |
# Test 7: Empty preference defaults to 'highest' |
| 2336 |
t::lib::Mocks::mock_preference( 'TitleHoldFeeStrategy', '' ); |
| 2337 |
$fee = $title_hold->calculate_hold_fee(); |
| 2338 |
is( $fee, 3.00, 'Title-level hold: empty strategy preference defaults to highest fee (3.00)' ); |
| 2339 |
|
| 2340 |
# Test 8: No holdable items returns 0 |
| 2341 |
# Make all items not holdable |
| 2342 |
$item1->notforloan(1)->store; |
| 2343 |
$item2->notforloan(1)->store; |
| 2344 |
$item3->notforloan(1)->store; |
| 2345 |
$item4->notforloan(1)->store; |
| 2346 |
|
| 2347 |
$fee = $title_hold->calculate_hold_fee(); |
| 2348 |
is( $fee, 0, 'Title-level hold: no holdable items returns 0 fee' ); |
| 2349 |
|
| 2350 |
# Test 9: Mix of holdable and non-holdable items (highest) |
| 2351 |
# Make some items holdable again |
| 2352 |
$item2->notforloan(0)->store; # Fee: 3.00 |
| 2353 |
$item3->notforloan(0)->store; # Fee: 2.00 |
| 2354 |
|
| 2355 |
t::lib::Mocks::mock_preference( 'TitleHoldFeeStrategy', 'highest' ); |
| 2356 |
$fee = $title_hold->calculate_hold_fee(); |
| 2357 |
is( $fee, 3.00, 'Title-level hold: highest strategy with mixed holdable items returns 3.00' ); |
| 2358 |
|
| 2359 |
# Test 10: Mix of holdable and non-holdable items (lowest) |
| 2360 |
t::lib::Mocks::mock_preference( 'TitleHoldFeeStrategy', 'lowest' ); |
| 2361 |
$fee = $title_hold->calculate_hold_fee(); |
| 2362 |
is( $fee, 2.00, 'Title-level hold: lowest strategy with mixed holdable items returns 2.00' ); |
| 2363 |
|
| 2364 |
# Test 11: Items with 0 fee |
| 2365 |
my $itemtype_free = $builder->build( { source => 'Itemtype' } ); |
| 2366 |
Koha::CirculationRules->set_rules( |
| 2367 |
{ |
| 2368 |
branchcode => undef, |
| 2369 |
categorycode => 'XYZ1', |
| 2370 |
itemtype => $itemtype_free->{itemtype}, |
| 2371 |
rules => { hold_fee => 0, reservesallowed => 10 } |
| 2372 |
} |
| 2373 |
); |
| 2374 |
|
| 2375 |
my $item_free = $builder->build_sample_item( |
| 2376 |
{ |
| 2377 |
biblionumber => $biblio->biblionumber, |
| 2378 |
itype => $itemtype_free->{itemtype}, |
| 2379 |
notforloan => 0, |
| 2380 |
} |
| 2381 |
); |
| 2382 |
|
| 2383 |
t::lib::Mocks::mock_preference( 'TitleHoldFeeStrategy', 'lowest' ); |
| 2384 |
$fee = $title_hold->calculate_hold_fee(); |
| 2385 |
is( $fee, 0, 'Title-level hold: lowest strategy with free item returns 0' ); |
| 2386 |
|
| 2387 |
# Test 12: All items have same fee |
| 2388 |
# Set all holdable items to same fee |
| 2389 |
Koha::CirculationRules->set_rules( |
| 2390 |
{ |
| 2391 |
branchcode => undef, |
| 2392 |
categorycode => 'XYZ1', |
| 2393 |
itemtype => $itemtype2->{itemtype}, |
| 2394 |
rules => { hold_fee => 2.50, reservesallowed => 10 } |
| 2395 |
} |
| 2396 |
); |
| 2397 |
Koha::CirculationRules->set_rules( |
| 2398 |
{ |
| 2399 |
branchcode => undef, |
| 2400 |
categorycode => 'XYZ1', |
| 2401 |
itemtype => $itemtype3->{itemtype}, |
| 2402 |
rules => { hold_fee => 2.50, reservesallowed => 10 } |
| 2403 |
} |
| 2404 |
); |
| 2405 |
Koha::CirculationRules->set_rules( |
| 2406 |
{ |
| 2407 |
branchcode => undef, |
| 2408 |
categorycode => 'XYZ1', |
| 2409 |
itemtype => $itemtype_free->{itemtype}, |
| 2410 |
rules => { hold_fee => 2.50, reservesallowed => 10 } |
| 2411 |
} |
| 2412 |
); |
| 2413 |
|
| 2414 |
$fee = $title_hold->calculate_hold_fee(); |
| 2415 |
is( $fee, 2.50, 'Title-level hold: all items with same fee returns that fee regardless of strategy' ); |
| 2416 |
|
| 2417 |
$schema->storage->txn_rollback; |
| 2418 |
}; |
| 2419 |
|
| 2420 |
subtest 'should_charge() tests' => sub { |
| 2421 |
plan tests => 6; |
| 2422 |
|
| 2423 |
$schema->storage->txn_begin; |
| 2424 |
|
| 2425 |
my $patron = $builder->build_object( { class => 'Koha::Patrons' } ); |
| 2426 |
my $biblio = $builder->build_sample_biblio(); |
| 2427 |
my $item = $builder->build_sample_item( { biblionumber => $biblio->biblionumber } ); |
| 2428 |
|
| 2429 |
my $hold = $builder->build_object( |
| 2430 |
{ |
| 2431 |
class => 'Koha::Holds', |
| 2432 |
value => { |
| 2433 |
borrowernumber => $patron->borrowernumber, |
| 2434 |
biblionumber => $biblio->biblionumber, |
| 2435 |
itemnumber => $item->itemnumber, |
| 2436 |
} |
| 2437 |
} |
| 2438 |
); |
| 2439 |
|
| 2440 |
# Test any_time_is_placed mode |
| 2441 |
t::lib::Mocks::mock_preference( 'HoldFeeMode', 'any_time_is_placed' ); |
| 2442 |
ok( $hold->should_charge('placement'), 'any_time_is_placed charges at placement' ); |
| 2443 |
ok( !$hold->should_charge('collection'), 'any_time_is_placed does not charge at collection' ); |
| 2444 |
|
| 2445 |
# Test any_time_is_collected mode |
| 2446 |
t::lib::Mocks::mock_preference( 'HoldFeeMode', 'any_time_is_collected' ); |
| 2447 |
ok( !$hold->should_charge('placement'), 'any_time_is_collected does not charge at placement' ); |
| 2448 |
ok( $hold->should_charge('collection'), 'any_time_is_collected charges at collection' ); |
| 2449 |
|
| 2450 |
# Test not_always mode (basic case - items available) |
| 2451 |
t::lib::Mocks::mock_preference( 'HoldFeeMode', 'not_always' ); |
| 2452 |
ok( !$hold->should_charge('placement'), 'not_always does not charge at placement when items available' ); |
| 2453 |
ok( !$hold->should_charge('collection'), 'not_always does not charge at collection' ); |
| 2454 |
|
| 2455 |
$schema->storage->txn_rollback; |
| 2456 |
}; |
| 2457 |
|
| 2458 |
subtest 'charge_hold_fee() tests' => sub { |
| 2459 |
plan tests => 2; |
| 2460 |
|
| 2461 |
$schema->storage->txn_begin; |
| 2462 |
|
| 2463 |
my $cat = $builder->build( { source => 'Category', value => { categorycode => 'XYZ1' } } ); |
| 2464 |
|
| 2465 |
# Set up circulation rules for hold fees |
| 2466 |
Koha::CirculationRules->set_rules( |
| 2467 |
{ |
| 2468 |
branchcode => undef, |
| 2469 |
categorycode => 'XYZ1', |
| 2470 |
itemtype => undef, |
| 2471 |
rules => { |
| 2472 |
hold_fee => 2.00, |
| 2473 |
} |
| 2474 |
} |
| 2475 |
); |
| 2476 |
|
| 2477 |
my $patron = $builder->build_object( |
| 2478 |
{ |
| 2479 |
class => 'Koha::Patrons', |
| 2480 |
value => { categorycode => 'XYZ1' } |
| 2481 |
} |
| 2482 |
); |
| 2483 |
t::lib::Mocks::mock_userenv( { patron => $patron, branchcode => $patron->branchcode } ); |
| 2484 |
|
| 2485 |
my $biblio = $builder->build_sample_biblio(); |
| 2486 |
my $item = $builder->build_sample_item( { biblionumber => $biblio->biblionumber } ); |
| 2487 |
|
| 2488 |
my $hold = $builder->build_object( |
| 2489 |
{ |
| 2490 |
class => 'Koha::Holds', |
| 2491 |
value => { |
| 2492 |
borrowernumber => $patron->borrowernumber, |
| 2493 |
biblionumber => $biblio->biblionumber, |
| 2494 |
itemnumber => $item->itemnumber, |
| 2495 |
} |
| 2496 |
} |
| 2497 |
); |
| 2498 |
|
| 2499 |
my $account = $patron->account; |
| 2500 |
my $balance_before = $account->balance; |
| 2501 |
|
| 2502 |
# Charge fee |
| 2503 |
my $account_line = $hold->charge_hold_fee(); |
| 2504 |
is( $account_line->amount, 2.00, 'charge_hold_fee returns account line with correct amount' ); |
| 2505 |
|
| 2506 |
my $balance_after = $account->balance; |
| 2507 |
is( $balance_after, $balance_before + 2.00, 'Patron account charged correctly' ); |
| 2508 |
|
| 2509 |
$schema->storage->txn_rollback; |
| 2510 |
}; |
| 2511 |
|
| 2156 |
sub set_userenv { |
2512 |
sub set_userenv { |
| 2157 |
my ($library) = @_; |
2513 |
my ($library) = @_; |
| 2158 |
my $staff = $builder->build_object( { class => "Koha::Patrons" } ); |
2514 |
my $staff = $builder->build_object( { class => "Koha::Patrons" } ); |
| 2159 |
t::lib::Mocks::mock_userenv( { patron => $staff, branchcode => $library->{branchcode} } ); |
2515 |
t::lib::Mocks::mock_userenv( { patron => $staff, branchcode => $library->{branchcode} } ); |
| 2160 |
} |
2516 |
} |
|
|
2517 |
|