Bug 18501: (follow-up) Use t::lib::Dates::compare in tests
[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 => 11;
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 => 6;
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 => 7;
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 'Continue when userenv is not set' => sub {
765             plan tests => 1;
766
767             my $patron = $builder->build_object( { class => 'Koha::Patrons' } );
768             my $barcode            = 'KD123456795';
769             my $replacement_amount = 100;
770             my $processfee_amount  = 20;
771
772             my $item_type = $builder->build_object(
773                 {
774                     class => 'Koha::ItemTypes',
775                     value => {
776                         notforloan         => undef,
777                         rentalcharge       => 0,
778                         defaultreplacecost => undef,
779                         processfee         => 0,
780                         rentalcharge_daily => 0,
781                     }
782                 }
783             );
784             my $item = $builder->build_sample_item(
785                 {
786                     biblionumber     => $biblio->biblionumber,
787                     homebranch       => $library->branchcode,
788                     holdingbranch    => $library->branchcode,
789                     barcode          => $barcode,
790                     replacementprice => $replacement_amount,
791                     itype            => $item_type->itemtype
792                 }
793             );
794
795             my $issue =
796               C4::Circulation::AddIssue( $patron->unblessed, $barcode );
797
798             # Simulate item marked as lost
799             $item->itemlost(1)->store;
800             C4::Circulation::LostItem( $item->itemnumber, 1 );
801
802             # Unset the userenv
803             C4::Context->_new_userenv(undef);
804
805             # Simluate item marked as found
806             $item->itemlost(0)->store;
807             is( $item->{_refunded}, 1, 'No refund triggered' );
808
809         };
810     };
811 };
812
813 subtest 'get_transfer' => sub {
814     plan tests => 3;
815
816     my $transfer = $new_item_1->get_transfer();
817     is( $transfer, undef, 'Koha::Item->get_transfer should return undef if the item is not in transit' );
818
819     my $library_to = $builder->build( { source => 'Branch' } );
820
821     C4::Circulation::transferbook({
822         from_branch => $new_item_1->holdingbranch,
823         to_branch => $library_to->{branchcode},
824         barcode => $new_item_1->barcode,
825     });
826
827     $transfer = $new_item_1->get_transfer();
828     is( ref($transfer), 'Koha::Item::Transfer', 'Koha::Item->get_transfer should return a Koha::Item::Transfers object' );
829
830     is( $transfer->itemnumber, $new_item_1->itemnumber, 'Koha::Item->get_transfer should return a valid Koha::Item::Transfers object' );
831 };
832
833 subtest 'holds' => sub {
834     plan tests => 5;
835
836     my $biblio = $builder->build_sample_biblio();
837     my $item   = $builder->build_sample_item({
838         biblionumber => $biblio->biblionumber,
839     });
840     is($item->holds->count, 0, "Nothing returned if no holds");
841     my $hold1 = $builder->build({ source => 'Reserve', value => { itemnumber=>$item->itemnumber, found => 'T' }});
842     my $hold2 = $builder->build({ source => 'Reserve', value => { itemnumber=>$item->itemnumber, found => 'W' }});
843     my $hold3 = $builder->build({ source => 'Reserve', value => { itemnumber=>$item->itemnumber, found => 'W' }});
844
845     is($item->holds()->count,3,"Three holds found");
846     is($item->holds({found => 'W'})->count,2,"Two waiting holds found");
847     is_deeply($item->holds({found => 'T'})->next->unblessed,$hold1,"Found transit holds matches the hold");
848     is($item->holds({found => undef})->count, 0,"Nothing returned if no matching holds");
849 };
850
851 subtest 'biblio' => sub {
852     plan tests => 2;
853
854     my $biblio = $retrieved_item_1->biblio;
855     is( ref( $biblio ), 'Koha::Biblio', 'Koha::Item->biblio should return a Koha::Biblio' );
856     is( $biblio->biblionumber, $retrieved_item_1->biblionumber, 'Koha::Item->biblio should return the correct biblio' );
857 };
858
859 subtest 'biblioitem' => sub {
860     plan tests => 2;
861
862     my $biblioitem = $retrieved_item_1->biblioitem;
863     is( ref( $biblioitem ), 'Koha::Biblioitem', 'Koha::Item->biblioitem should return a Koha::Biblioitem' );
864     is( $biblioitem->biblionumber, $retrieved_item_1->biblionumber, 'Koha::Item->biblioitem should return the correct biblioitem' );
865 };
866
867 # Restore userenv
868 t::lib::Mocks::mock_userenv({ branchcode => $library->{branchcode} });
869 subtest 'checkout' => sub {
870     plan tests => 5;
871     my $item = Koha::Items->find( $new_item_1->itemnumber );
872     # No checkout yet
873     my $checkout = $item->checkout;
874     is( $checkout, undef, 'Koha::Item->checkout should return undef if there is no current checkout on this item' );
875
876     # Add a checkout
877     my $patron = $builder->build({ source => 'Borrower' });
878     C4::Circulation::AddIssue( $patron, $item->barcode );
879     $checkout = $retrieved_item_1->checkout;
880     is( ref( $checkout ), 'Koha::Checkout', 'Koha::Item->checkout should return a Koha::Checkout' );
881     is( $checkout->itemnumber, $item->itemnumber, 'Koha::Item->checkout should return the correct checkout' );
882     is( $checkout->borrowernumber, $patron->{borrowernumber}, 'Koha::Item->checkout should return the correct checkout' );
883
884     # Do the return
885     C4::Circulation::AddReturn( $item->barcode );
886
887     # There is no more checkout on this item, making sure it will not return old checkouts
888     $checkout = $item->checkout;
889     is( $checkout, undef, 'Koha::Item->checkout should return undef if there is no *current* checkout on this item' );
890 };
891
892 subtest 'can_be_transferred' => sub {
893     plan tests => 5;
894
895     t::lib::Mocks::mock_preference('UseBranchTransferLimits', 1);
896     t::lib::Mocks::mock_preference('BranchTransferLimitsType', 'itemtype');
897
898     my $biblio   = $builder->build_sample_biblio();
899     my $library1 = $builder->build_object( { class => 'Koha::Libraries' } );
900     my $library2 = $builder->build_object( { class => 'Koha::Libraries' } );
901     my $item  = $builder->build_sample_item({
902         biblionumber     => $biblio->biblionumber,
903         homebranch       => $library1->branchcode,
904         holdingbranch    => $library1->branchcode,
905     });
906
907     is(Koha::Item::Transfer::Limits->search({
908         fromBranch => $library1->branchcode,
909         toBranch => $library2->branchcode,
910     })->count, 0, 'There are no transfer limits between libraries.');
911     ok($item->can_be_transferred({ to => $library2 }),
912        'Item can be transferred between libraries.');
913
914     my $limit = Koha::Item::Transfer::Limit->new({
915         fromBranch => $library1->branchcode,
916         toBranch => $library2->branchcode,
917         itemtype => $item->effective_itemtype,
918     })->store;
919     is(Koha::Item::Transfer::Limits->search({
920         fromBranch => $library1->branchcode,
921         toBranch => $library2->branchcode,
922     })->count, 1, 'Given we have added a transfer limit,');
923     is($item->can_be_transferred({ to => $library2 }), 0,
924        'Item can no longer be transferred between libraries.');
925     is($item->can_be_transferred({ to => $library2, from => $library1 }), 0,
926        'We get the same result also if we pass the from-library parameter.');
927 };
928
929 # Reset nb_of_items prior to testing delete
930 $nb_of_items = Koha::Items->search->count;
931
932 # Test delete
933 $retrieved_item_1->delete;
934 is( Koha::Items->search->count, $nb_of_items - 1, 'Delete should have deleted the item' );
935
936 $schema->storage->txn_rollback;