Bug 24254: Compare itemlost with 0
[koha.git] / t / db_dependent / Koha / Items.t
1 #!/usr/bin/perl
2
3 # Copyright 2016 Koha Development team
4 #
5 # This file is part of Koha
6 #
7 # Koha is free software; you can redistribute it and/or modify it
8 # under the terms of the GNU General Public License as published by
9 # the Free Software Foundation; either version 3 of the License, or
10 # (at your option) any later version.
11 #
12 # Koha is distributed in the hope that it will be useful, but
13 # WITHOUT ANY WARRANTY; without even the implied warranty of
14 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 # GNU General Public License for more details.
16 #
17 # You should have received a copy of the GNU General Public License
18 # along with Koha; if not, see <http://www.gnu.org/licenses>.
19
20 use Modern::Perl;
21
22 use Test::More tests => 13;
23 use Test::Exception;
24 use Time::Fake;
25
26 use C4::Circulation;
27 use C4::Context;
28 use Koha::Item;
29 use Koha::Item::Transfer::Limits;
30 use Koha::Items;
31 use Koha::Database;
32 use Koha::DateUtils qw( dt_from_string );
33
34 use t::lib::TestBuilder;
35 use t::lib::Mocks;
36 use t::lib::Dates;
37
38 my $schema = Koha::Database->new->schema;
39 $schema->storage->txn_begin;
40
41 my $dbh     = C4::Context->dbh;
42
43 my $builder     = t::lib::TestBuilder->new;
44 my $library     = $builder->build( { source => 'Branch' } );
45 my $nb_of_items = Koha::Items->search->count;
46 my $biblio      = $builder->build_sample_biblio();
47 my $new_item_1   = $builder->build_sample_item({
48     biblionumber => $biblio->biblionumber,
49     homebranch       => $library->{branchcode},
50     holdingbranch    => $library->{branchcode},
51 });
52 my $new_item_2   = $builder->build_sample_item({
53     biblionumber => $biblio->biblionumber,
54     homebranch       => $library->{branchcode},
55     holdingbranch    => $library->{branchcode},
56 });
57
58
59 t::lib::Mocks::mock_userenv({ branchcode => $library->{branchcode} });
60
61 like( $new_item_1->itemnumber, qr|^\d+$|, 'Adding a new item should have set the itemnumber' );
62 is( Koha::Items->search->count, $nb_of_items + 2, 'The 2 items should have been added' );
63
64 my $retrieved_item_1 = Koha::Items->find( $new_item_1->itemnumber );
65 is( $retrieved_item_1->barcode, $new_item_1->barcode, 'Find a item by id should return the correct item' );
66
67 subtest 'store' => sub {
68     plan tests => 7;
69
70     my $biblio = $builder->build_sample_biblio;
71     my $today  = dt_from_string->set( hour => 0, minute => 0, second => 0 );
72     my $item   = Koha::Item->new(
73         {
74             homebranch    => $library->{branchcode},
75             holdingbranch => $library->{branchcode},
76             biblionumber  => $biblio->biblionumber,
77             location      => 'my_loc',
78         }
79     )->store->get_from_storage;
80
81     is( t::lib::Dates::compare( $item->replacementpricedate, $today ),
82         0, 'replacementpricedate must have been set to today if not given' );
83     is( t::lib::Dates::compare( $item->datelastseen, $today ),
84         0, 'datelastseen must have been set to today if not given' );
85     is(
86         $item->itype,
87         $biblio->biblioitem->itemtype,
88         'items.itype must have been set to biblioitem.itemtype is not given'
89     );
90     is( $item->permanent_location, $item->location,
91         'permanent_location must have been set to location if not given' );
92     $item->delete;
93
94     subtest '*_on updates' => sub {
95         plan tests => 9;
96
97         # Once the '_on' value is set (triggered by the related field turning from false to true)
98         # it should not be re-set for any changes outside of the related field being 'unset'.
99
100         my @fields = qw( itemlost withdrawn damaged );
101         my $today = dt_from_string();
102         my $yesterday = $today->clone()->subtract( days => 1 );
103
104         for my $field ( @fields ) {
105             my $item = $builder->build_sample_item(
106                 {
107                     itemlost     => 0,
108                     itemlost_on  => undef,
109                     withdrawn    => 0,
110                     withdrawn_on => undef,
111                     damaged      => 0,
112                     damaged_on   => undef
113                 }
114             );
115             my $field_on = $field . '_on';
116
117             # Set field for the first time
118             Time::Fake->offset( $yesterday->epoch );
119             $item->$field(1)->store;
120             $item->get_from_storage;
121             is( t::lib::Dates::compare( $item->$field_on, $yesterday ),
122                 0, $field_on . " was set upon first truthy setting" );
123
124             # Update the field to a new 'true' value
125             Time::Fake->offset( $today->epoch );
126             $item->$field(2)->store;
127             $item->get_from_storage;
128             is( t::lib::Dates::compare( $item->$field_on, $yesterday ),
129                 0, $field_on . " was not updated upon second truthy setting" );
130
131             # Update the field to a new 'false' value
132             $item->$field(0)->store;
133             $item->get_from_storage;
134             is($item->$field_on, undef, $field_on . " was unset upon untruthy setting");
135
136             Time::Fake->reset;
137         }
138     };
139
140     subtest '_lost_found_trigger' => sub {
141         plan tests => 10;
142
143         t::lib::Mocks::mock_preference( 'WhenLostChargeReplacementFee', 1 );
144         t::lib::Mocks::mock_preference( 'WhenLostForgiveFine',          0 );
145
146         my $processfee_amount  = 20;
147         my $replacement_amount = 99.00;
148         my $item_type          = $builder->build_object(
149             {
150                 class => 'Koha::ItemTypes',
151                 value => {
152                     notforloan         => undef,
153                     rentalcharge       => 0,
154                     defaultreplacecost => undef,
155                     processfee         => $processfee_amount,
156                     rentalcharge_daily => 0,
157                 }
158             }
159         );
160         my $library = $builder->build_object( { class => 'Koha::Libraries' } );
161
162         $biblio = $builder->build_sample_biblio( { author => 'Hall, Daria' } );
163
164         subtest 'Full write-off tests' => sub {
165
166             plan tests => 12;
167
168             my $patron = $builder->build_object( { class => 'Koha::Patrons' } );
169             my $manager =
170               $builder->build_object( { class => "Koha::Patrons" } );
171             t::lib::Mocks::mock_userenv(
172                 { patron => $manager, branchcode => $manager->branchcode } );
173
174             my $item = $builder->build_sample_item(
175                 {
176                     biblionumber     => $biblio->biblionumber,
177                     library          => $library->branchcode,
178                     replacementprice => $replacement_amount,
179                     itype            => $item_type->itemtype,
180                 }
181             );
182
183             C4::Circulation::AddIssue( $patron->unblessed, $item->barcode );
184
185             # Simulate item marked as lost
186             $item->itemlost(3)->store;
187             C4::Circulation::LostItem( $item->itemnumber, 1 );
188
189             my $processing_fee_lines = Koha::Account::Lines->search(
190                 {
191                     borrowernumber  => $patron->id,
192                     itemnumber      => $item->itemnumber,
193                     debit_type_code => 'PROCESSING'
194                 }
195             );
196             is( $processing_fee_lines->count,
197                 1, 'Only one processing fee produced' );
198             my $processing_fee_line = $processing_fee_lines->next;
199             is( $processing_fee_line->amount + 0,
200                 $processfee_amount,
201                 'The right PROCESSING amount is generated' );
202             is( $processing_fee_line->amountoutstanding + 0,
203                 $processfee_amount,
204                 'The right PROCESSING amountoutstanding is generated' );
205
206             my $lost_fee_lines = Koha::Account::Lines->search(
207                 {
208                     borrowernumber  => $patron->id,
209                     itemnumber      => $item->itemnumber,
210                     debit_type_code => 'LOST'
211                 }
212             );
213             is( $lost_fee_lines->count, 1, 'Only one lost item fee produced' );
214             my $lost_fee_line = $lost_fee_lines->next;
215             is( $lost_fee_line->amount + 0,
216                 $replacement_amount, 'The right LOST amount is generated' );
217             is( $lost_fee_line->amountoutstanding + 0,
218                 $replacement_amount,
219                 'The right LOST amountoutstanding is generated' );
220             is( $lost_fee_line->status, undef, 'The LOST status was not set' );
221
222             my $account = $patron->account;
223             my $debts   = $account->outstanding_debits;
224
225             # Write off the debt
226             my $credit = $account->add_credit(
227                 {
228                     amount    => $account->balance,
229                     type      => 'WRITEOFF',
230                     interface => 'test',
231                 }
232             );
233             $credit->apply(
234                 { debits => [ $debts->as_list ], offset_type => 'Writeoff' } );
235
236             # Simulate item marked as found
237             $item->itemlost(0)->store;
238             is( $item->{_refunded}, undef, 'No LOST_FOUND account line added' );
239
240             $lost_fee_line->discard_changes;    # reload from DB
241             is( $lost_fee_line->amountoutstanding + 0,
242                 0, 'Lost fee has no outstanding amount' );
243             is( $lost_fee_line->debit_type_code,
244                 'LOST', 'Lost fee now still has account type of LOST' );
245             is( $lost_fee_line->status, 'FOUND',
246                 "Lost fee now has account status of FOUND - No Refund" );
247
248             is( $patron->account->balance,
249                 -0, 'The patron balance is 0, everything was written off' );
250         };
251
252         subtest 'Full payment tests' => sub {
253
254             plan tests => 14;
255
256             my $patron = $builder->build_object( { class => 'Koha::Patrons' } );
257
258             my $item = $builder->build_sample_item(
259                 {
260                     biblionumber     => $biblio->biblionumber,
261                     library          => $library->branchcode,
262                     replacementprice => $replacement_amount,
263                     itype            => $item_type->itemtype
264                 }
265             );
266
267             my $issue =
268               C4::Circulation::AddIssue( $patron->unblessed, $item->barcode );
269
270             # Simulate item marked as lost
271             $item->itemlost(1)->store;
272             C4::Circulation::LostItem( $item->itemnumber, 1 );
273
274             my $processing_fee_lines = Koha::Account::Lines->search(
275                 {
276                     borrowernumber  => $patron->id,
277                     itemnumber      => $item->itemnumber,
278                     debit_type_code => 'PROCESSING'
279                 }
280             );
281             is( $processing_fee_lines->count,
282                 1, 'Only one processing fee produced' );
283             my $processing_fee_line = $processing_fee_lines->next;
284             is( $processing_fee_line->amount + 0,
285                 $processfee_amount,
286                 'The right PROCESSING amount is generated' );
287             is( $processing_fee_line->amountoutstanding + 0,
288                 $processfee_amount,
289                 'The right PROCESSING amountoutstanding is generated' );
290
291             my $lost_fee_lines = Koha::Account::Lines->search(
292                 {
293                     borrowernumber  => $patron->id,
294                     itemnumber      => $item->itemnumber,
295                     debit_type_code => 'LOST'
296                 }
297             );
298             is( $lost_fee_lines->count, 1, 'Only one lost item fee produced' );
299             my $lost_fee_line = $lost_fee_lines->next;
300             is( $lost_fee_line->amount + 0,
301                 $replacement_amount, 'The right LOST amount is generated' );
302             is( $lost_fee_line->amountoutstanding + 0,
303                 $replacement_amount,
304                 'The right LOST amountountstanding is generated' );
305
306             my $account = $patron->account;
307             my $debts   = $account->outstanding_debits;
308
309             # Pay off the debt
310             my $credit = $account->add_credit(
311                 {
312                     amount    => $account->balance,
313                     type      => 'PAYMENT',
314                     interface => 'test',
315                 }
316             );
317             $credit->apply(
318                 { debits => [ $debts->as_list ], offset_type => 'Payment' } );
319
320             # Simulate item marked as found
321             $item->itemlost(0)->store;
322             is( $item->{_refunded}, 1, 'Refund triggered' );
323
324             my $credit_return = Koha::Account::Lines->search(
325                 {
326                     itemnumber       => $item->itemnumber,
327                     credit_type_code => 'LOST_FOUND'
328                 },
329                 { rows => 1 }
330             )->single;
331
332             ok( $credit_return, 'An account line of type LOST_FOUND is added' );
333             is( $credit_return->amount + 0,
334                 -99.00,
335                 'The account line of type LOST_FOUND has an amount of -99' );
336             is(
337                 $credit_return->amountoutstanding + 0,
338                 -99.00,
339 'The account line of type LOST_FOUND has an amountoutstanding of -99'
340             );
341
342             $lost_fee_line->discard_changes;
343             is( $lost_fee_line->amountoutstanding + 0,
344                 0, 'Lost fee has no outstanding amount' );
345             is( $lost_fee_line->debit_type_code,
346                 'LOST', 'Lost fee now still has account type of LOST' );
347             is( $lost_fee_line->status, 'FOUND',
348                 "Lost fee now has account status of FOUND" );
349
350             is( $patron->account->balance, -99,
351 'The patron balance is -99, a credit that equals the lost fee payment'
352             );
353         };
354
355         subtest 'Test without payment or write off' => sub {
356
357             plan tests => 14;
358
359             my $patron = $builder->build_object( { class => 'Koha::Patrons' } );
360
361             my $item = $builder->build_sample_item(
362                 {
363                     biblionumber     => $biblio->biblionumber,
364                     library          => $library->branchcode,
365                     replacementprice => 23.00,
366                     replacementprice => $replacement_amount,
367                     itype            => $item_type->itemtype
368                 }
369             );
370
371             my $issue =
372               C4::Circulation::AddIssue( $patron->unblessed, $item->barcode );
373
374             # Simulate item marked as lost
375             $item->itemlost(3)->store;
376             C4::Circulation::LostItem( $item->itemnumber, 1 );
377
378             my $processing_fee_lines = Koha::Account::Lines->search(
379                 {
380                     borrowernumber  => $patron->id,
381                     itemnumber      => $item->itemnumber,
382                     debit_type_code => 'PROCESSING'
383                 }
384             );
385             is( $processing_fee_lines->count,
386                 1, 'Only one processing fee produced' );
387             my $processing_fee_line = $processing_fee_lines->next;
388             is( $processing_fee_line->amount + 0,
389                 $processfee_amount,
390                 'The right PROCESSING amount is generated' );
391             is( $processing_fee_line->amountoutstanding + 0,
392                 $processfee_amount,
393                 'The right PROCESSING amountoutstanding is generated' );
394
395             my $lost_fee_lines = Koha::Account::Lines->search(
396                 {
397                     borrowernumber  => $patron->id,
398                     itemnumber      => $item->itemnumber,
399                     debit_type_code => 'LOST'
400                 }
401             );
402             is( $lost_fee_lines->count, 1, 'Only one lost item fee produced' );
403             my $lost_fee_line = $lost_fee_lines->next;
404             is( $lost_fee_line->amount + 0,
405                 $replacement_amount, 'The right LOST amount is generated' );
406             is( $lost_fee_line->amountoutstanding + 0,
407                 $replacement_amount,
408                 'The right LOST amountountstanding is generated' );
409
410             # Simulate item marked as found
411             $item->itemlost(0)->store;
412             is( $item->{_refunded}, 1, 'Refund triggered' );
413
414             my $credit_return = Koha::Account::Lines->search(
415                 {
416                     itemnumber       => $item->itemnumber,
417                     credit_type_code => 'LOST_FOUND'
418                 },
419                 { rows => 1 }
420             )->single;
421
422             ok( $credit_return, 'An account line of type LOST_FOUND is added' );
423             is( $credit_return->amount + 0,
424                 -99.00,
425                 'The account line of type LOST_FOUND has an amount of -99' );
426             is(
427                 $credit_return->amountoutstanding + 0,
428                 0,
429 'The account line of type LOST_FOUND has an amountoutstanding of 0'
430             );
431
432             $lost_fee_line->discard_changes;
433             is( $lost_fee_line->amountoutstanding + 0,
434                 0, 'Lost fee has no outstanding amount' );
435             is( $lost_fee_line->debit_type_code,
436                 'LOST', 'Lost fee now still has account type of LOST' );
437             is( $lost_fee_line->status, 'FOUND',
438                 "Lost fee now has account status of FOUND" );
439
440             is( $patron->account->balance,
441                 20, 'The patron balance is 20, still owes the processing fee' );
442         };
443
444         subtest
445           'Test with partial payement and write off, and remaining debt' =>
446           sub {
447
448             plan tests => 17;
449
450             my $patron = $builder->build_object( { class => 'Koha::Patrons' } );
451             my $item = $builder->build_sample_item(
452                 {
453                     biblionumber     => $biblio->biblionumber,
454                     library          => $library->branchcode,
455                     replacementprice => $replacement_amount,
456                     itype            => $item_type->itemtype
457                 }
458             );
459
460             my $issue =
461               C4::Circulation::AddIssue( $patron->unblessed, $item->barcode );
462
463             # Simulate item marked as lost
464             $item->itemlost(1)->store;
465             C4::Circulation::LostItem( $item->itemnumber, 1 );
466
467             my $processing_fee_lines = Koha::Account::Lines->search(
468                 {
469                     borrowernumber  => $patron->id,
470                     itemnumber      => $item->itemnumber,
471                     debit_type_code => 'PROCESSING'
472                 }
473             );
474             is( $processing_fee_lines->count,
475                 1, 'Only one processing fee produced' );
476             my $processing_fee_line = $processing_fee_lines->next;
477             is( $processing_fee_line->amount + 0,
478                 $processfee_amount,
479                 'The right PROCESSING amount is generated' );
480             is( $processing_fee_line->amountoutstanding + 0,
481                 $processfee_amount,
482                 'The right PROCESSING amountoutstanding is generated' );
483
484             my $lost_fee_lines = Koha::Account::Lines->search(
485                 {
486                     borrowernumber  => $patron->id,
487                     itemnumber      => $item->itemnumber,
488                     debit_type_code => 'LOST'
489                 }
490             );
491             is( $lost_fee_lines->count, 1, 'Only one lost item fee produced' );
492             my $lost_fee_line = $lost_fee_lines->next;
493             is( $lost_fee_line->amount + 0,
494                 $replacement_amount, 'The right LOST amount is generated' );
495             is( $lost_fee_line->amountoutstanding + 0,
496                 $replacement_amount,
497                 'The right LOST amountountstanding is generated' );
498
499             my $account = $patron->account;
500             is(
501                 $account->balance,
502                 $processfee_amount + $replacement_amount,
503                 'Balance is PROCESSING + L'
504             );
505
506             # Partially pay fee
507             my $payment_amount = 27;
508             my $payment        = $account->add_credit(
509                 {
510                     amount    => $payment_amount,
511                     type      => 'PAYMENT',
512                     interface => 'test',
513                 }
514             );
515
516             $payment->apply(
517                 { debits => [$lost_fee_line], offset_type => 'Payment' } );
518
519             # Partially write off fee
520             my $write_off_amount = 25;
521             my $write_off        = $account->add_credit(
522                 {
523                     amount    => $write_off_amount,
524                     type      => 'WRITEOFF',
525                     interface => 'test',
526                 }
527             );
528             $write_off->apply(
529                 { debits => [$lost_fee_line], offset_type => 'Writeoff' } );
530
531             is(
532                 $account->balance,
533                 $processfee_amount +
534                   $replacement_amount -
535                   $payment_amount -
536                   $write_off_amount,
537                 'Payment and write off applied'
538             );
539
540             # Store the amountoutstanding value
541             $lost_fee_line->discard_changes;
542             my $outstanding = $lost_fee_line->amountoutstanding;
543
544             # Simulate item marked as found
545             $item->itemlost(0)->store;
546             is( $item->{_refunded}, 1, 'Refund triggered' );
547
548             my $credit_return = Koha::Account::Lines->search(
549                 {
550                     itemnumber       => $item->itemnumber,
551                     credit_type_code => 'LOST_FOUND'
552                 },
553                 { rows => 1 }
554             )->single;
555
556             ok( $credit_return, 'An account line of type LOST_FOUND is added' );
557
558             is(
559                 $account->balance,
560                 $processfee_amount - $payment_amount,
561                 'Balance is PROCESSING - PAYMENT (LOST_FOUND)'
562             );
563
564             $lost_fee_line->discard_changes;
565             is( $lost_fee_line->amountoutstanding + 0,
566                 0, 'Lost fee has no outstanding amount' );
567             is( $lost_fee_line->debit_type_code,
568                 'LOST', 'Lost fee now still has account type of LOST' );
569             is( $lost_fee_line->status, 'FOUND',
570                 "Lost fee now has account status of FOUND" );
571
572             is(
573                 $credit_return->amount + 0,
574                 ( $payment_amount + $outstanding ) * -1,
575 'The account line of type LOST_FOUND has an amount equal to the payment + outstanding'
576             );
577             is(
578                 $credit_return->amountoutstanding + 0,
579                 $payment_amount * -1,
580 'The account line of type LOST_FOUND has an amountoutstanding equal to the payment'
581             );
582
583             is(
584                 $account->balance,
585                 $processfee_amount - $payment_amount,
586 'The patron balance is the difference between the PROCESSING and the credit'
587             );
588           };
589
590         subtest 'Partial payment, existing debits and AccountAutoReconcile' =>
591           sub {
592
593             plan tests => 10;
594
595             my $patron = $builder->build_object( { class => 'Koha::Patrons' } );
596             my $barcode            = 'KD123456793';
597             my $replacement_amount = 100;
598             my $processfee_amount  = 20;
599
600             my $item_type = $builder->build_object(
601                 {
602                     class => 'Koha::ItemTypes',
603                     value => {
604                         notforloan         => undef,
605                         rentalcharge       => 0,
606                         defaultreplacecost => undef,
607                         processfee         => 0,
608                         rentalcharge_daily => 0,
609                     }
610                 }
611             );
612             my $item = Koha::Item->new(
613                 {
614                     biblionumber     => $biblio->biblionumber,
615                     homebranch       => $library->branchcode,
616                     holdingbranch    => $library->branchcode,
617                     barcode          => $barcode,
618                     replacementprice => $replacement_amount,
619                     itype            => $item_type->itemtype
620                 },
621             )->store;
622
623             my $issue =
624               C4::Circulation::AddIssue( $patron->unblessed, $barcode );
625
626             # Simulate item marked as lost
627             $item->itemlost(1)->store;
628             C4::Circulation::LostItem( $item->itemnumber, 1 );
629
630             my $lost_fee_lines = Koha::Account::Lines->search(
631                 {
632                     borrowernumber  => $patron->id,
633                     itemnumber      => $item->itemnumber,
634                     debit_type_code => 'LOST'
635                 }
636             );
637             is( $lost_fee_lines->count, 1, 'Only one lost item fee produced' );
638             my $lost_fee_line = $lost_fee_lines->next;
639             is( $lost_fee_line->amount + 0,
640                 $replacement_amount, 'The right LOST amount is generated' );
641             is( $lost_fee_line->amountoutstanding + 0,
642                 $replacement_amount,
643                 'The right LOST amountountstanding is generated' );
644
645             my $account = $patron->account;
646             is( $account->balance, $replacement_amount, 'Balance is L' );
647
648             # Partially pay fee
649             my $payment_amount = 27;
650             my $payment        = $account->add_credit(
651                 {
652                     amount    => $payment_amount,
653                     type      => 'PAYMENT',
654                     interface => 'test',
655                 }
656             );
657             $payment->apply(
658                 { debits => [$lost_fee_line], offset_type => 'Payment' } );
659
660             is(
661                 $account->balance,
662                 $replacement_amount - $payment_amount,
663                 'Payment applied'
664             );
665
666             my $manual_debit_amount = 80;
667             $account->add_debit(
668                 {
669                     amount    => $manual_debit_amount,
670                     type      => 'OVERDUE',
671                     interface => 'test'
672                 }
673             );
674
675             is(
676                 $account->balance,
677                 $manual_debit_amount + $replacement_amount - $payment_amount,
678                 'Manual debit applied'
679             );
680
681             t::lib::Mocks::mock_preference( 'AccountAutoReconcile', 1 );
682
683             # Simulate item marked as found
684             $item->itemlost(0)->store;
685             is( $item->{_refunded}, 1, 'Refund triggered' );
686
687             my $credit_return = Koha::Account::Lines->search(
688                 {
689                     itemnumber       => $item->itemnumber,
690                     credit_type_code => 'LOST_FOUND'
691                 },
692                 { rows => 1 }
693             )->single;
694
695             ok( $credit_return, 'An account line of type LOST_FOUND is added' );
696
697             is(
698                 $account->balance,
699                 $manual_debit_amount - $payment_amount,
700                 'Balance is PROCESSING - payment (LOST_FOUND)'
701             );
702
703             my $manual_debit = Koha::Account::Lines->search(
704                 {
705                     borrowernumber  => $patron->id,
706                     debit_type_code => 'OVERDUE',
707                     status          => 'UNRETURNED'
708                 }
709             )->next;
710             is(
711                 $manual_debit->amountoutstanding + 0,
712                 $manual_debit_amount - $payment_amount,
713                 'reconcile_balance was called'
714             );
715           };
716
717         subtest 'Patron deleted' => sub {
718             plan tests => 1;
719
720             my $patron = $builder->build_object( { class => 'Koha::Patrons' } );
721             my $barcode            = 'KD123456794';
722             my $replacement_amount = 100;
723             my $processfee_amount  = 20;
724
725             my $item_type = $builder->build_object(
726                 {
727                     class => 'Koha::ItemTypes',
728                     value => {
729                         notforloan         => undef,
730                         rentalcharge       => 0,
731                         defaultreplacecost => undef,
732                         processfee         => 0,
733                         rentalcharge_daily => 0,
734                     }
735                 }
736             );
737             my $item = Koha::Item->new(
738                 {
739                     biblionumber     => $biblio->biblionumber,
740                     homebranch       => $library->branchcode,
741                     holdingbranch    => $library->branchcode,
742                     barcode          => $barcode,
743                     replacementprice => $replacement_amount,
744                     itype            => $item_type->itemtype
745                 },
746             )->store;
747
748             my $issue =
749               C4::Circulation::AddIssue( $patron->unblessed, $barcode );
750
751             # Simulate item marked as lost
752             $item->itemlost(1)->store;
753             C4::Circulation::LostItem( $item->itemnumber, 1 );
754
755             $issue->delete();
756             $patron->delete();
757
758             # Simulate item marked as found
759             $item->itemlost(0)->store;
760             is( $item->{_refunded}, undef, 'No refund triggered' );
761
762         };
763
764         subtest 'restore fine | no overdue' => sub {
765
766             plan tests => 8;
767
768             my $manager =
769               $builder->build_object( { class => "Koha::Patrons" } );
770             t::lib::Mocks::mock_userenv(
771                 { patron => $manager, branchcode => $manager->branchcode } );
772
773             # Set lostreturn_policy to 'restore' for tests
774             my $specific_rule_restore = $builder->build(
775                 {
776                     source => 'CirculationRule',
777                     value  => {
778                         branchcode   => $manager->branchcode,
779                         categorycode => undef,
780                         itemtype     => undef,
781                         rule_name    => 'lostreturn',
782                         rule_value   => 'restore'
783                     }
784                 }
785             );
786
787             my $patron = $builder->build_object( { class => 'Koha::Patrons' } );
788
789             my $item = $builder->build_sample_item(
790                 {
791                     biblionumber     => $biblio->biblionumber,
792                     library          => $library->branchcode,
793                     replacementprice => $replacement_amount,
794                     itype            => $item_type->itemtype
795                 }
796             );
797
798             my $issue =
799               C4::Circulation::AddIssue( $patron->unblessed, $item->barcode );
800
801             # Simulate item marked as lost
802             $item->itemlost(1)->store;
803             C4::Circulation::LostItem( $item->itemnumber, 1 );
804
805             my $processing_fee_lines = Koha::Account::Lines->search(
806                 {
807                     borrowernumber  => $patron->id,
808                     itemnumber      => $item->itemnumber,
809                     debit_type_code => 'PROCESSING'
810                 }
811             );
812             is( $processing_fee_lines->count,
813                 1, 'Only one processing fee produced' );
814             my $processing_fee_line = $processing_fee_lines->next;
815             is( $processing_fee_line->amount + 0,
816                 $processfee_amount,
817                 'The right PROCESSING amount is generated' );
818             is( $processing_fee_line->amountoutstanding + 0,
819                 $processfee_amount,
820                 'The right PROCESSING amountoutstanding is generated' );
821
822             my $lost_fee_lines = Koha::Account::Lines->search(
823                 {
824                     borrowernumber  => $patron->id,
825                     itemnumber      => $item->itemnumber,
826                     debit_type_code => 'LOST'
827                 }
828             );
829             is( $lost_fee_lines->count, 1, 'Only one lost item fee produced' );
830             my $lost_fee_line = $lost_fee_lines->next;
831             is( $lost_fee_line->amount + 0,
832                 $replacement_amount, 'The right LOST amount is generated' );
833             is( $lost_fee_line->amountoutstanding + 0,
834                 $replacement_amount,
835                 'The right LOST amountountstanding is generated' );
836
837             my $account = $patron->account;
838             my $debts   = $account->outstanding_debits;
839
840             # Pay off the debt
841             my $credit = $account->add_credit(
842                 {
843                     amount    => $account->balance,
844                     type      => 'PAYMENT',
845                     interface => 'test',
846                 }
847             );
848             $credit->apply(
849                 { debits => [ $debts->as_list ], offset_type => 'Payment' } );
850
851             # Simulate item marked as found
852             $item->itemlost(0)->store;
853             is( $item->{_refunded}, 1, 'Refund triggered' );
854             is( $item->{_restored}, undef, 'Restore not triggered when there is no overdue fine found' );
855         };
856
857         subtest 'restore fine | unforgiven overdue' => sub {
858
859             plan tests => 10;
860
861             # Set lostreturn_policy to 'restore' for tests
862             my $manager =
863               $builder->build_object( { class => "Koha::Patrons" } );
864             t::lib::Mocks::mock_userenv(
865                 { patron => $manager, branchcode => $manager->branchcode } );
866             my $specific_rule_restore = $builder->build(
867                 {
868                     source => 'CirculationRule',
869                     value  => {
870                         branchcode   => $manager->branchcode,
871                         categorycode => undef,
872                         itemtype     => undef,
873                         rule_name    => 'lostreturn',
874                         rule_value   => 'restore'
875                     }
876                 }
877             );
878
879             my $patron = $builder->build_object( { class => 'Koha::Patrons' } );
880
881             my $item = $builder->build_sample_item(
882                 {
883                     biblionumber     => $biblio->biblionumber,
884                     library          => $library->branchcode,
885                     replacementprice => $replacement_amount,
886                     itype            => $item_type->itemtype
887                 }
888             );
889
890             my $issue =
891               C4::Circulation::AddIssue( $patron->unblessed, $item->barcode );
892
893             # Simulate item marked as lost
894             $item->itemlost(1)->store;
895             C4::Circulation::LostItem( $item->itemnumber, 1 );
896
897             my $processing_fee_lines = Koha::Account::Lines->search(
898                 {
899                     borrowernumber  => $patron->id,
900                     itemnumber      => $item->itemnumber,
901                     debit_type_code => 'PROCESSING'
902                 }
903             );
904             is( $processing_fee_lines->count,
905                 1, 'Only one processing fee produced' );
906             my $processing_fee_line = $processing_fee_lines->next;
907             is( $processing_fee_line->amount + 0,
908                 $processfee_amount,
909                 'The right PROCESSING amount is generated' );
910             is( $processing_fee_line->amountoutstanding + 0,
911                 $processfee_amount,
912                 'The right PROCESSING amountoutstanding is generated' );
913
914             my $lost_fee_lines = Koha::Account::Lines->search(
915                 {
916                     borrowernumber  => $patron->id,
917                     itemnumber      => $item->itemnumber,
918                     debit_type_code => 'LOST'
919                 }
920             );
921             is( $lost_fee_lines->count, 1, 'Only one lost item fee produced' );
922             my $lost_fee_line = $lost_fee_lines->next;
923             is( $lost_fee_line->amount + 0,
924                 $replacement_amount, 'The right LOST amount is generated' );
925             is( $lost_fee_line->amountoutstanding + 0,
926                 $replacement_amount,
927                 'The right LOST amountountstanding is generated' );
928
929             my $account = $patron->account;
930             my $debts   = $account->outstanding_debits;
931
932             # Pay off the debt
933             my $credit = $account->add_credit(
934                 {
935                     amount    => $account->balance,
936                     type      => 'PAYMENT',
937                     interface => 'test',
938                 }
939             );
940             $credit->apply(
941                 { debits => [ $debts->as_list ], offset_type => 'Payment' } );
942
943             # Fine not forgiven
944             my $overdue = $account->add_debit(
945                 {
946                     amount     => 30.00,
947                     user_id    => $manager->borrowernumber,
948                     library_id => $library->branchcode,
949                     interface  => 'test',
950                     item_id    => $item->itemnumber,
951                     type       => 'OVERDUE',
952                 }
953             )->store();
954             $overdue->status('LOST')->store();
955             $overdue->discard_changes;
956             is( $overdue->status, 'LOST',
957                 'Overdue status set to LOST' );
958
959             # Simulate item marked as found
960             $item->itemlost(0)->store;
961             is( $item->{_refunded}, 1, 'Refund triggered' );
962             is( $item->{_restored}, undef, 'Restore not triggered when overdue was not forgiven' );
963             $overdue->discard_changes;
964             is( $overdue->status, 'FOUND',
965                 'Overdue status updated to FOUND' );
966         };
967
968         subtest 'restore fine | forgiven overdue' => sub {
969
970             plan tests => 12;
971
972             # Set lostreturn_policy to 'restore' for tests
973             my $manager =
974               $builder->build_object( { class => "Koha::Patrons" } );
975             t::lib::Mocks::mock_userenv(
976                 { patron => $manager, branchcode => $manager->branchcode } );
977             my $specific_rule_restore = $builder->build(
978                 {
979                     source => 'CirculationRule',
980                     value  => {
981                         branchcode   => $manager->branchcode,
982                         categorycode => undef,
983                         itemtype     => undef,
984                         rule_name    => 'lostreturn',
985                         rule_value   => 'restore'
986                     }
987                 }
988             );
989
990             my $patron = $builder->build_object( { class => 'Koha::Patrons' } );
991
992             my $item = $builder->build_sample_item(
993                 {
994                     biblionumber     => $biblio->biblionumber,
995                     library          => $library->branchcode,
996                     replacementprice => $replacement_amount,
997                     itype            => $item_type->itemtype
998                 }
999             );
1000
1001             my $issue =
1002               C4::Circulation::AddIssue( $patron->unblessed, $item->barcode );
1003
1004             # Simulate item marked as lost
1005             $item->itemlost(1)->store;
1006             C4::Circulation::LostItem( $item->itemnumber, 1 );
1007
1008             my $processing_fee_lines = Koha::Account::Lines->search(
1009                 {
1010                     borrowernumber  => $patron->id,
1011                     itemnumber      => $item->itemnumber,
1012                     debit_type_code => 'PROCESSING'
1013                 }
1014             );
1015             is( $processing_fee_lines->count,
1016                 1, 'Only one processing fee produced' );
1017             my $processing_fee_line = $processing_fee_lines->next;
1018             is( $processing_fee_line->amount + 0,
1019                 $processfee_amount,
1020                 'The right PROCESSING amount is generated' );
1021             is( $processing_fee_line->amountoutstanding + 0,
1022                 $processfee_amount,
1023                 'The right PROCESSING amountoutstanding is generated' );
1024
1025             my $lost_fee_lines = Koha::Account::Lines->search(
1026                 {
1027                     borrowernumber  => $patron->id,
1028                     itemnumber      => $item->itemnumber,
1029                     debit_type_code => 'LOST'
1030                 }
1031             );
1032             is( $lost_fee_lines->count, 1, 'Only one lost item fee produced' );
1033             my $lost_fee_line = $lost_fee_lines->next;
1034             is( $lost_fee_line->amount + 0,
1035                 $replacement_amount, 'The right LOST amount is generated' );
1036             is( $lost_fee_line->amountoutstanding + 0,
1037                 $replacement_amount,
1038                 'The right LOST amountountstanding is generated' );
1039
1040             my $account = $patron->account;
1041             my $debts   = $account->outstanding_debits;
1042
1043             # Pay off the debt
1044             my $credit = $account->add_credit(
1045                 {
1046                     amount    => $account->balance,
1047                     type      => 'PAYMENT',
1048                     interface => 'test',
1049                 }
1050             );
1051             $credit->apply(
1052                 { debits => [ $debts->as_list ], offset_type => 'Payment' } );
1053
1054             # Add overdue
1055             my $overdue = $account->add_debit(
1056                 {
1057                     amount     => 30.00,
1058                     user_id    => $manager->borrowernumber,
1059                     library_id => $library->branchcode,
1060                     interface  => 'test',
1061                     item_id    => $item->itemnumber,
1062                     type       => 'OVERDUE',
1063                 }
1064             )->store();
1065             $overdue->status('LOST')->store();
1066             is( $overdue->status, 'LOST',
1067                 'Overdue status set to LOST' );
1068
1069             t::lib::Mocks::mock_preference( 'AccountAutoReconcile', 0 );
1070
1071             # Forgive fine
1072             $credit = $account->add_credit(
1073                 {
1074                     amount     => 30.00,
1075                     user_id    => $manager->borrowernumber,
1076                     library_id => $library->branchcode,
1077                     interface  => 'test',
1078                     type       => 'FORGIVEN',
1079                     item_id    => $item->itemnumber
1080                 }
1081             );
1082             $credit->apply(
1083                 { debits => [$overdue], offset_type => 'Forgiven' } );
1084
1085             # Simulate item marked as found
1086             $item->itemlost(0)->store;
1087             is( $item->{_refunded}, 1, 'Refund triggered' );
1088             is( $item->{_restored}, 1, 'Restore triggered when overdue was forgiven' );
1089             $overdue->discard_changes;
1090             is( $overdue->status, 'FOUND', 'Overdue status updated to FOUND' );
1091             is( $overdue->amountoutstanding, $overdue->amount, 'Overdue outstanding has been restored' );
1092             $credit->discard_changes;
1093             is( $credit->status, 'VOID', 'Overdue Forgival has been marked as VOID');
1094         };
1095
1096         subtest 'Continue when userenv is not set' => sub {
1097             plan tests => 1;
1098
1099             my $patron = $builder->build_object( { class => 'Koha::Patrons' } );
1100             my $barcode            = 'KD123456795';
1101             my $replacement_amount = 100;
1102             my $processfee_amount  = 20;
1103
1104             my $item_type = $builder->build_object(
1105                 {
1106                     class => 'Koha::ItemTypes',
1107                     value => {
1108                         notforloan         => undef,
1109                         rentalcharge       => 0,
1110                         defaultreplacecost => undef,
1111                         processfee         => 0,
1112                         rentalcharge_daily => 0,
1113                     }
1114                 }
1115             );
1116             my $item = $builder->build_sample_item(
1117                 {
1118                     biblionumber     => $biblio->biblionumber,
1119                     homebranch       => $library->branchcode,
1120                     holdingbranch    => $library->branchcode,
1121                     barcode          => $barcode,
1122                     replacementprice => $replacement_amount,
1123                     itype            => $item_type->itemtype
1124                 }
1125             );
1126
1127             my $issue =
1128               C4::Circulation::AddIssue( $patron->unblessed, $barcode );
1129
1130             # Simulate item marked as lost
1131             $item->itemlost(1)->store;
1132             C4::Circulation::LostItem( $item->itemnumber, 1 );
1133
1134             # Unset the userenv
1135             C4::Context->_new_userenv(undef);
1136
1137             # Simluate item marked as found
1138             $item->itemlost(0)->store;
1139             is( $item->{_refunded}, 1, 'No refund triggered' );
1140
1141         };
1142     };
1143
1144     subtest 'log_action' => sub {
1145         plan tests => 2;
1146         t::lib::Mocks::mock_preference( 'CataloguingLog', 1 );
1147
1148         my $item = Koha::Item->new(
1149             {
1150                 homebranch    => $library->{branchcode},
1151                 holdingbranch => $library->{branchcode},
1152                 biblionumber  => $biblio->biblionumber,
1153                 location      => 'my_loc',
1154             }
1155         )->store;
1156         is(
1157             Koha::ActionLogs->search(
1158                 {
1159                     module => 'CATALOGUING',
1160                     action => 'ADD',
1161                     object => $item->itemnumber,
1162                     info   => 'item'
1163                 }
1164             )->count,
1165             1,
1166             "Item creation logged"
1167         );
1168
1169         $item->location('another_loc')->store;
1170         is(
1171             Koha::ActionLogs->search(
1172                 {
1173                     module => 'CATALOGUING',
1174                     action => 'MODIFY',
1175                     object => $item->itemnumber
1176                 }
1177             )->count,
1178             1,
1179             "Item modification logged"
1180         );
1181     };
1182 };
1183
1184 subtest 'get_transfer' => sub {
1185     plan tests => 3;
1186
1187     my $transfer = $new_item_1->get_transfer();
1188     is( $transfer, undef, 'Koha::Item->get_transfer should return undef if the item is not in transit' );
1189
1190     my $library_to = $builder->build( { source => 'Branch' } );
1191
1192     C4::Circulation::transferbook({
1193         from_branch => $new_item_1->holdingbranch,
1194         to_branch => $library_to->{branchcode},
1195         barcode => $new_item_1->barcode,
1196     });
1197
1198     $transfer = $new_item_1->get_transfer();
1199     is( ref($transfer), 'Koha::Item::Transfer', 'Koha::Item->get_transfer should return a Koha::Item::Transfers object' );
1200
1201     is( $transfer->itemnumber, $new_item_1->itemnumber, 'Koha::Item->get_transfer should return a valid Koha::Item::Transfers object' );
1202 };
1203
1204 subtest 'holds' => sub {
1205     plan tests => 5;
1206
1207     my $biblio = $builder->build_sample_biblio();
1208     my $item   = $builder->build_sample_item({
1209         biblionumber => $biblio->biblionumber,
1210     });
1211     is($item->holds->count, 0, "Nothing returned if no holds");
1212     my $hold1 = $builder->build({ source => 'Reserve', value => { itemnumber=>$item->itemnumber, found => 'T' }});
1213     my $hold2 = $builder->build({ source => 'Reserve', value => { itemnumber=>$item->itemnumber, found => 'W' }});
1214     my $hold3 = $builder->build({ source => 'Reserve', value => { itemnumber=>$item->itemnumber, found => 'W' }});
1215
1216     is($item->holds()->count,3,"Three holds found");
1217     is($item->holds({found => 'W'})->count,2,"Two waiting holds found");
1218     is_deeply($item->holds({found => 'T'})->next->unblessed,$hold1,"Found transit holds matches the hold");
1219     is($item->holds({found => undef})->count, 0,"Nothing returned if no matching holds");
1220 };
1221
1222 subtest 'biblio' => sub {
1223     plan tests => 2;
1224
1225     my $biblio = $retrieved_item_1->biblio;
1226     is( ref( $biblio ), 'Koha::Biblio', 'Koha::Item->biblio should return a Koha::Biblio' );
1227     is( $biblio->biblionumber, $retrieved_item_1->biblionumber, 'Koha::Item->biblio should return the correct biblio' );
1228 };
1229
1230 subtest 'biblioitem' => sub {
1231     plan tests => 2;
1232
1233     my $biblioitem = $retrieved_item_1->biblioitem;
1234     is( ref( $biblioitem ), 'Koha::Biblioitem', 'Koha::Item->biblioitem should return a Koha::Biblioitem' );
1235     is( $biblioitem->biblionumber, $retrieved_item_1->biblionumber, 'Koha::Item->biblioitem should return the correct biblioitem' );
1236 };
1237
1238 # Restore userenv
1239 t::lib::Mocks::mock_userenv({ branchcode => $library->{branchcode} });
1240 subtest 'checkout' => sub {
1241     plan tests => 5;
1242     my $item = Koha::Items->find( $new_item_1->itemnumber );
1243     # No checkout yet
1244     my $checkout = $item->checkout;
1245     is( $checkout, undef, 'Koha::Item->checkout should return undef if there is no current checkout on this item' );
1246
1247     # Add a checkout
1248     my $patron = $builder->build({ source => 'Borrower' });
1249     C4::Circulation::AddIssue( $patron, $item->barcode );
1250     $checkout = $retrieved_item_1->checkout;
1251     is( ref( $checkout ), 'Koha::Checkout', 'Koha::Item->checkout should return a Koha::Checkout' );
1252     is( $checkout->itemnumber, $item->itemnumber, 'Koha::Item->checkout should return the correct checkout' );
1253     is( $checkout->borrowernumber, $patron->{borrowernumber}, 'Koha::Item->checkout should return the correct checkout' );
1254
1255     # Do the return
1256     C4::Circulation::AddReturn( $item->barcode );
1257
1258     # There is no more checkout on this item, making sure it will not return old checkouts
1259     $checkout = $item->checkout;
1260     is( $checkout, undef, 'Koha::Item->checkout should return undef if there is no *current* checkout on this item' );
1261 };
1262
1263 subtest 'can_be_transferred' => sub {
1264     plan tests => 5;
1265
1266     t::lib::Mocks::mock_preference('UseBranchTransferLimits', 1);
1267     t::lib::Mocks::mock_preference('BranchTransferLimitsType', 'itemtype');
1268
1269     my $biblio   = $builder->build_sample_biblio();
1270     my $library1 = $builder->build_object( { class => 'Koha::Libraries' } );
1271     my $library2 = $builder->build_object( { class => 'Koha::Libraries' } );
1272     my $item  = $builder->build_sample_item({
1273         biblionumber     => $biblio->biblionumber,
1274         homebranch       => $library1->branchcode,
1275         holdingbranch    => $library1->branchcode,
1276     });
1277
1278     is(Koha::Item::Transfer::Limits->search({
1279         fromBranch => $library1->branchcode,
1280         toBranch => $library2->branchcode,
1281     })->count, 0, 'There are no transfer limits between libraries.');
1282     ok($item->can_be_transferred({ to => $library2 }),
1283        'Item can be transferred between libraries.');
1284
1285     my $limit = Koha::Item::Transfer::Limit->new({
1286         fromBranch => $library1->branchcode,
1287         toBranch => $library2->branchcode,
1288         itemtype => $item->effective_itemtype,
1289     })->store;
1290     is(Koha::Item::Transfer::Limits->search({
1291         fromBranch => $library1->branchcode,
1292         toBranch => $library2->branchcode,
1293     })->count, 1, 'Given we have added a transfer limit,');
1294     is($item->can_be_transferred({ to => $library2 }), 0,
1295        'Item can no longer be transferred between libraries.');
1296     is($item->can_be_transferred({ to => $library2, from => $library1 }), 0,
1297        'We get the same result also if we pass the from-library parameter.');
1298 };
1299
1300 subtest 'filter_by_for_loan' => sub {
1301     plan tests => 3;
1302
1303     my $biblio = $builder->build_sample_biblio;
1304     is( $biblio->items->filter_by_for_loan->count, 0, 'no item yet' );
1305     $builder->build_sample_item( { biblionumber => $biblio->biblionumber, notforloan => 1 } );
1306     is( $biblio->items->filter_by_for_loan->count, 0, 'no item for loan' );
1307     $builder->build_sample_item( { biblionumber => $biblio->biblionumber, notforloan => 0 } );
1308     is( $biblio->items->filter_by_for_loan->count, 1, '1 item for loan' );
1309
1310     $biblio->delete;
1311 };
1312
1313 # Reset nb_of_items prior to testing delete
1314 $nb_of_items = Koha::Items->search->count;
1315
1316 # Test delete
1317 $retrieved_item_1->delete;
1318 is( Koha::Items->search->count, $nb_of_items - 1, 'Delete should have deleted the item' );
1319
1320 $schema->storage->txn_rollback;
1321
1322 subtest 'filter_by_visible_in_opac() tests' => sub {
1323
1324     plan tests => 8;
1325
1326     $schema->storage->txn_begin;
1327
1328     # have a fresh biblio
1329     my $biblio = $builder->build_sample_biblio;
1330     # have two itemtypes
1331     my $itype_1 = $builder->build_object({ class => 'Koha::ItemTypes' });
1332     my $itype_2 = $builder->build_object({ class => 'Koha::ItemTypes' });
1333     # have 5 items on that biblio
1334     my $item_1 = $builder->build_sample_item(
1335         {
1336             biblionumber => $biblio->biblionumber,
1337             itemlost     => -1,
1338             itype        => $itype_1->itemtype,
1339             withdrawn    => 1
1340         }
1341     );
1342     my $item_2 = $builder->build_sample_item(
1343         {
1344             biblionumber => $biblio->biblionumber,
1345             itemlost     => 0,
1346             itype        => $itype_2->itemtype,
1347             withdrawn    => 2
1348         }
1349     );
1350     my $item_3 = $builder->build_sample_item(
1351         {
1352             biblionumber => $biblio->biblionumber,
1353             itemlost     => 1,
1354             itype        => $itype_1->itemtype,
1355             withdrawn    => 3
1356         }
1357     );
1358     my $item_4 = $builder->build_sample_item(
1359         {
1360             biblionumber => $biblio->biblionumber,
1361             itemlost     => 0,
1362             itype        => $itype_2->itemtype,
1363             withdrawn    => 4
1364         }
1365     );
1366     my $item_5 = $builder->build_sample_item(
1367         {
1368             biblionumber => $biblio->biblionumber,
1369             itemlost     => 0,
1370             itype        => $itype_1->itemtype,
1371             withdrawn    => 5
1372         }
1373     );
1374     my $item_6 = $builder->build_sample_item(
1375         {
1376             biblionumber => $biblio->biblionumber,
1377             itemlost     => 2,
1378             itype        => $itype_1->itemtype,
1379             withdrawn    => 5
1380         }
1381     );
1382
1383     my $rules = {};
1384
1385     t::lib::Mocks::mock_preference( 'hidelostitems', 0 );
1386     is( $biblio->items->filter_by_visible_in_opac->count,
1387         6, 'No rules passed, hidelostitems unset' );
1388
1389     t::lib::Mocks::mock_preference( 'hidelostitems', 1 );
1390     is(
1391         $biblio->items->filter_by_visible_in_opac( { rules => $rules } )->count,
1392         3,
1393         'No rules passed, hidelostitems set'
1394     );
1395
1396     $rules = { withdrawn => [ 1, 2 ] };
1397     is(
1398         $biblio->items->filter_by_visible_in_opac( { rules => $rules } )->count,
1399         2,
1400         'Rules on withdrawn, hidelostitems set'
1401     );
1402
1403     $rules = { itype => [ $itype_1->itemtype ] };
1404     is(
1405         $biblio->items->filter_by_visible_in_opac( { rules => $rules } )->count,
1406         2,
1407         'Rules on itype, hidelostitems set'
1408     );
1409
1410     $rules = { withdrawn => [ 1, 2 ], itype => [ $itype_1->itemtype ] };
1411     is(
1412         $biblio->items->filter_by_visible_in_opac( { rules => $rules } )->count,
1413         1,
1414         'Rules on itype and withdrawn, hidelostitems set'
1415     );
1416     is(
1417         $biblio->items->filter_by_visible_in_opac( { rules => $rules } )
1418           ->next->itemnumber,
1419         $item_4->itemnumber,
1420         'The right item is returned'
1421     );
1422
1423     $rules = { withdrawn => [ 1, 2 ], itype => [ $itype_2->itemtype ] };
1424     is(
1425         $biblio->items->filter_by_visible_in_opac( { rules => $rules } )->count,
1426         1,
1427         'Rules on itype and withdrawn, hidelostitems set'
1428     );
1429     is(
1430         $biblio->items->filter_by_visible_in_opac( { rules => $rules } )
1431           ->next->itemnumber,
1432         $item_5->itemnumber,
1433         'The right item is returned'
1434     );
1435
1436     $schema->storage->txn_rollback;
1437 };