6140139c0cd6ddb5537dfb95686ca06f62bf86c9
[koha-equinox.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($item->$field_on, DateTime::Format::MySQL->format_datetime($yesterday), $field_on . " was set upon first truthy setting");
122
123             # Update the field to a new 'true' value
124             Time::Fake->offset( $today->epoch );
125             $item->$field(2)->store;
126             $item->get_from_storage;
127             is($item->$field_on, DateTime::Format::MySQL->format_datetime($yesterday), $field_on . " was not updated upon second truthy setting");
128
129             # Update the field to a new 'false' value
130             $item->$field(0)->store;
131             $item->get_from_storage;
132             is($item->$field_on, undef, $field_on . " was unset upon untruthy setting");
133
134             Time::Fake->reset;
135         }
136     };
137
138     subtest '_lost_found_trigger' => sub {
139         plan tests => 7;
140
141         t::lib::Mocks::mock_preference( 'WhenLostChargeReplacementFee', 1 );
142         t::lib::Mocks::mock_preference( 'WhenLostForgiveFine',          0 );
143
144         my $processfee_amount  = 20;
145         my $replacement_amount = 99.00;
146         my $item_type          = $builder->build_object(
147             {
148                 class => 'Koha::ItemTypes',
149                 value => {
150                     notforloan         => undef,
151                     rentalcharge       => 0,
152                     defaultreplacecost => undef,
153                     processfee         => $processfee_amount,
154                     rentalcharge_daily => 0,
155                 }
156             }
157         );
158         my $library = $builder->build_object( { class => 'Koha::Libraries' } );
159
160         $biblio = $builder->build_sample_biblio( { author => 'Hall, Daria' } );
161
162         subtest 'Full write-off tests' => sub {
163
164             plan tests => 12;
165
166             my $patron = $builder->build_object( { class => 'Koha::Patrons' } );
167             my $manager =
168               $builder->build_object( { class => "Koha::Patrons" } );
169             t::lib::Mocks::mock_userenv(
170                 { patron => $manager, branchcode => $manager->branchcode } );
171
172             my $item = $builder->build_sample_item(
173                 {
174                     biblionumber     => $biblio->biblionumber,
175                     library          => $library->branchcode,
176                     replacementprice => $replacement_amount,
177                     itype            => $item_type->itemtype,
178                 }
179             );
180
181             C4::Circulation::AddIssue( $patron->unblessed, $item->barcode );
182
183             # Simulate item marked as lost
184             $item->itemlost(3)->store;
185             C4::Circulation::LostItem( $item->itemnumber, 1 );
186
187             my $processing_fee_lines = Koha::Account::Lines->search(
188                 {
189                     borrowernumber  => $patron->id,
190                     itemnumber      => $item->itemnumber,
191                     debit_type_code => 'PROCESSING'
192                 }
193             );
194             is( $processing_fee_lines->count,
195                 1, 'Only one processing fee produced' );
196             my $processing_fee_line = $processing_fee_lines->next;
197             is( $processing_fee_line->amount + 0,
198                 $processfee_amount,
199                 'The right PROCESSING amount is generated' );
200             is( $processing_fee_line->amountoutstanding + 0,
201                 $processfee_amount,
202                 'The right PROCESSING amountoutstanding is generated' );
203
204             my $lost_fee_lines = Koha::Account::Lines->search(
205                 {
206                     borrowernumber  => $patron->id,
207                     itemnumber      => $item->itemnumber,
208                     debit_type_code => 'LOST'
209                 }
210             );
211             is( $lost_fee_lines->count, 1, 'Only one lost item fee produced' );
212             my $lost_fee_line = $lost_fee_lines->next;
213             is( $lost_fee_line->amount + 0,
214                 $replacement_amount, 'The right LOST amount is generated' );
215             is( $lost_fee_line->amountoutstanding + 0,
216                 $replacement_amount,
217                 'The right LOST amountoutstanding is generated' );
218             is( $lost_fee_line->status, undef, 'The LOST status was not set' );
219
220             my $account = $patron->account;
221             my $debts   = $account->outstanding_debits;
222
223             # Write off the debt
224             my $credit = $account->add_credit(
225                 {
226                     amount    => $account->balance,
227                     type      => 'WRITEOFF',
228                     interface => 'test',
229                 }
230             );
231             $credit->apply(
232                 { debits => [ $debts->as_list ], offset_type => 'Writeoff' } );
233
234             # Simulate item marked as found
235             $item->itemlost(0)->store;
236             is( $item->{_refunded}, undef, 'No LOST_FOUND account line added' );
237
238             $lost_fee_line->discard_changes;    # reload from DB
239             is( $lost_fee_line->amountoutstanding + 0,
240                 0, 'Lost fee has no outstanding amount' );
241             is( $lost_fee_line->debit_type_code,
242                 'LOST', 'Lost fee now still has account type of LOST' );
243             is( $lost_fee_line->status, 'FOUND',
244                 "Lost fee now has account status of FOUND - No Refund" );
245
246             is( $patron->account->balance,
247                 -0, 'The patron balance is 0, everything was written off' );
248         };
249
250         subtest 'Full payment tests' => sub {
251
252             plan tests => 14;
253
254             my $patron = $builder->build_object( { class => 'Koha::Patrons' } );
255
256             my $item = $builder->build_sample_item(
257                 {
258                     biblionumber     => $biblio->biblionumber,
259                     library          => $library->branchcode,
260                     replacementprice => $replacement_amount,
261                     itype            => $item_type->itemtype
262                 }
263             );
264
265             my $issue =
266               C4::Circulation::AddIssue( $patron->unblessed, $item->barcode );
267
268             # Simulate item marked as lost
269             $item->itemlost(1)->store;
270             C4::Circulation::LostItem( $item->itemnumber, 1 );
271
272             my $processing_fee_lines = Koha::Account::Lines->search(
273                 {
274                     borrowernumber  => $patron->id,
275                     itemnumber      => $item->itemnumber,
276                     debit_type_code => 'PROCESSING'
277                 }
278             );
279             is( $processing_fee_lines->count,
280                 1, 'Only one processing fee produced' );
281             my $processing_fee_line = $processing_fee_lines->next;
282             is( $processing_fee_line->amount + 0,
283                 $processfee_amount,
284                 'The right PROCESSING amount is generated' );
285             is( $processing_fee_line->amountoutstanding + 0,
286                 $processfee_amount,
287                 'The right PROCESSING amountoutstanding is generated' );
288
289             my $lost_fee_lines = Koha::Account::Lines->search(
290                 {
291                     borrowernumber  => $patron->id,
292                     itemnumber      => $item->itemnumber,
293                     debit_type_code => 'LOST'
294                 }
295             );
296             is( $lost_fee_lines->count, 1, 'Only one lost item fee produced' );
297             my $lost_fee_line = $lost_fee_lines->next;
298             is( $lost_fee_line->amount + 0,
299                 $replacement_amount, 'The right LOST amount is generated' );
300             is( $lost_fee_line->amountoutstanding + 0,
301                 $replacement_amount,
302                 'The right LOST amountountstanding is generated' );
303
304             my $account = $patron->account;
305             my $debts   = $account->outstanding_debits;
306
307             # Pay off the debt
308             my $credit = $account->add_credit(
309                 {
310                     amount    => $account->balance,
311                     type      => 'PAYMENT',
312                     interface => 'test',
313                 }
314             );
315             $credit->apply(
316                 { debits => [ $debts->as_list ], offset_type => 'Payment' } );
317
318             # Simulate item marked as found
319             $item->itemlost(0)->store;
320             is( $item->{_refunded}, 1, 'Refund triggered' );
321
322             my $credit_return = Koha::Account::Lines->search(
323                 {
324                     itemnumber       => $item->itemnumber,
325                     credit_type_code => 'LOST_FOUND'
326                 },
327                 { rows => 1 }
328             )->single;
329
330             ok( $credit_return, 'An account line of type LOST_FOUND is added' );
331             is( $credit_return->amount + 0,
332                 -99.00,
333                 'The account line of type LOST_FOUND has an amount of -99' );
334             is(
335                 $credit_return->amountoutstanding + 0,
336                 -99.00,
337 'The account line of type LOST_FOUND has an amountoutstanding of -99'
338             );
339
340             $lost_fee_line->discard_changes;
341             is( $lost_fee_line->amountoutstanding + 0,
342                 0, 'Lost fee has no outstanding amount' );
343             is( $lost_fee_line->debit_type_code,
344                 'LOST', 'Lost fee now still has account type of LOST' );
345             is( $lost_fee_line->status, 'FOUND',
346                 "Lost fee now has account status of FOUND" );
347
348             is( $patron->account->balance, -99,
349 'The patron balance is -99, a credit that equals the lost fee payment'
350             );
351         };
352
353         subtest 'Test without payment or write off' => sub {
354
355             plan tests => 14;
356
357             my $patron = $builder->build_object( { class => 'Koha::Patrons' } );
358
359             my $item = $builder->build_sample_item(
360                 {
361                     biblionumber     => $biblio->biblionumber,
362                     library          => $library->branchcode,
363                     replacementprice => 23.00,
364                     replacementprice => $replacement_amount,
365                     itype            => $item_type->itemtype
366                 }
367             );
368
369             my $issue =
370               C4::Circulation::AddIssue( $patron->unblessed, $item->barcode );
371
372             # Simulate item marked as lost
373             $item->itemlost(3)->store;
374             C4::Circulation::LostItem( $item->itemnumber, 1 );
375
376             my $processing_fee_lines = Koha::Account::Lines->search(
377                 {
378                     borrowernumber  => $patron->id,
379                     itemnumber      => $item->itemnumber,
380                     debit_type_code => 'PROCESSING'
381                 }
382             );
383             is( $processing_fee_lines->count,
384                 1, 'Only one processing fee produced' );
385             my $processing_fee_line = $processing_fee_lines->next;
386             is( $processing_fee_line->amount + 0,
387                 $processfee_amount,
388                 'The right PROCESSING amount is generated' );
389             is( $processing_fee_line->amountoutstanding + 0,
390                 $processfee_amount,
391                 'The right PROCESSING amountoutstanding is generated' );
392
393             my $lost_fee_lines = Koha::Account::Lines->search(
394                 {
395                     borrowernumber  => $patron->id,
396                     itemnumber      => $item->itemnumber,
397                     debit_type_code => 'LOST'
398                 }
399             );
400             is( $lost_fee_lines->count, 1, 'Only one lost item fee produced' );
401             my $lost_fee_line = $lost_fee_lines->next;
402             is( $lost_fee_line->amount + 0,
403                 $replacement_amount, 'The right LOST amount is generated' );
404             is( $lost_fee_line->amountoutstanding + 0,
405                 $replacement_amount,
406                 'The right LOST amountountstanding is generated' );
407
408             # Simulate item marked as found
409             $item->itemlost(0)->store;
410             is( $item->{_refunded}, 1, 'Refund triggered' );
411
412             my $credit_return = Koha::Account::Lines->search(
413                 {
414                     itemnumber       => $item->itemnumber,
415                     credit_type_code => 'LOST_FOUND'
416                 },
417                 { rows => 1 }
418             )->single;
419
420             ok( $credit_return, 'An account line of type LOST_FOUND is added' );
421             is( $credit_return->amount + 0,
422                 -99.00,
423                 'The account line of type LOST_FOUND has an amount of -99' );
424             is(
425                 $credit_return->amountoutstanding + 0,
426                 0,
427 'The account line of type LOST_FOUND has an amountoutstanding of 0'
428             );
429
430             $lost_fee_line->discard_changes;
431             is( $lost_fee_line->amountoutstanding + 0,
432                 0, 'Lost fee has no outstanding amount' );
433             is( $lost_fee_line->debit_type_code,
434                 'LOST', 'Lost fee now still has account type of LOST' );
435             is( $lost_fee_line->status, 'FOUND',
436                 "Lost fee now has account status of FOUND" );
437
438             is( $patron->account->balance,
439                 20, 'The patron balance is 20, still owes the processing fee' );
440         };
441
442         subtest
443           'Test with partial payement and write off, and remaining debt' =>
444           sub {
445
446             plan tests => 17;
447
448             my $patron = $builder->build_object( { class => 'Koha::Patrons' } );
449             my $item = $builder->build_sample_item(
450                 {
451                     biblionumber     => $biblio->biblionumber,
452                     library          => $library->branchcode,
453                     replacementprice => $replacement_amount,
454                     itype            => $item_type->itemtype
455                 }
456             );
457
458             my $issue =
459               C4::Circulation::AddIssue( $patron->unblessed, $item->barcode );
460
461             # Simulate item marked as lost
462             $item->itemlost(1)->store;
463             C4::Circulation::LostItem( $item->itemnumber, 1 );
464
465             my $processing_fee_lines = Koha::Account::Lines->search(
466                 {
467                     borrowernumber  => $patron->id,
468                     itemnumber      => $item->itemnumber,
469                     debit_type_code => 'PROCESSING'
470                 }
471             );
472             is( $processing_fee_lines->count,
473                 1, 'Only one processing fee produced' );
474             my $processing_fee_line = $processing_fee_lines->next;
475             is( $processing_fee_line->amount + 0,
476                 $processfee_amount,
477                 'The right PROCESSING amount is generated' );
478             is( $processing_fee_line->amountoutstanding + 0,
479                 $processfee_amount,
480                 'The right PROCESSING amountoutstanding is generated' );
481
482             my $lost_fee_lines = Koha::Account::Lines->search(
483                 {
484                     borrowernumber  => $patron->id,
485                     itemnumber      => $item->itemnumber,
486                     debit_type_code => 'LOST'
487                 }
488             );
489             is( $lost_fee_lines->count, 1, 'Only one lost item fee produced' );
490             my $lost_fee_line = $lost_fee_lines->next;
491             is( $lost_fee_line->amount + 0,
492                 $replacement_amount, 'The right LOST amount is generated' );
493             is( $lost_fee_line->amountoutstanding + 0,
494                 $replacement_amount,
495                 'The right LOST amountountstanding is generated' );
496
497             my $account = $patron->account;
498             is(
499                 $account->balance,
500                 $processfee_amount + $replacement_amount,
501                 'Balance is PROCESSING + L'
502             );
503
504             # Partially pay fee
505             my $payment_amount = 27;
506             my $payment        = $account->add_credit(
507                 {
508                     amount    => $payment_amount,
509                     type      => 'PAYMENT',
510                     interface => 'test',
511                 }
512             );
513
514             $payment->apply(
515                 { debits => [$lost_fee_line], offset_type => 'Payment' } );
516
517             # Partially write off fee
518             my $write_off_amount = 25;
519             my $write_off        = $account->add_credit(
520                 {
521                     amount    => $write_off_amount,
522                     type      => 'WRITEOFF',
523                     interface => 'test',
524                 }
525             );
526             $write_off->apply(
527                 { debits => [$lost_fee_line], offset_type => 'Writeoff' } );
528
529             is(
530                 $account->balance,
531                 $processfee_amount +
532                   $replacement_amount -
533                   $payment_amount -
534                   $write_off_amount,
535                 'Payment and write off applied'
536             );
537
538             # Store the amountoutstanding value
539             $lost_fee_line->discard_changes;
540             my $outstanding = $lost_fee_line->amountoutstanding;
541
542             # Simulate item marked as found
543             $item->itemlost(0)->store;
544             is( $item->{_refunded}, 1, 'Refund triggered' );
545
546             my $credit_return = Koha::Account::Lines->search(
547                 {
548                     itemnumber       => $item->itemnumber,
549                     credit_type_code => 'LOST_FOUND'
550                 },
551                 { rows => 1 }
552             )->single;
553
554             ok( $credit_return, 'An account line of type LOST_FOUND is added' );
555
556             is(
557                 $account->balance,
558                 $processfee_amount - $payment_amount,
559                 'Balance is PROCESSING - PAYMENT (LOST_FOUND)'
560             );
561
562             $lost_fee_line->discard_changes;
563             is( $lost_fee_line->amountoutstanding + 0,
564                 0, 'Lost fee has no outstanding amount' );
565             is( $lost_fee_line->debit_type_code,
566                 'LOST', 'Lost fee now still has account type of LOST' );
567             is( $lost_fee_line->status, 'FOUND',
568                 "Lost fee now has account status of FOUND" );
569
570             is(
571                 $credit_return->amount + 0,
572                 ( $payment_amount + $outstanding ) * -1,
573 'The account line of type LOST_FOUND has an amount equal to the payment + outstanding'
574             );
575             is(
576                 $credit_return->amountoutstanding + 0,
577                 $payment_amount * -1,
578 'The account line of type LOST_FOUND has an amountoutstanding equal to the payment'
579             );
580
581             is(
582                 $account->balance,
583                 $processfee_amount - $payment_amount,
584 'The patron balance is the difference between the PROCESSING and the credit'
585             );
586           };
587
588         subtest 'Partial payment, existing debits and AccountAutoReconcile' =>
589           sub {
590
591             plan tests => 10;
592
593             my $patron = $builder->build_object( { class => 'Koha::Patrons' } );
594             my $barcode            = 'KD123456793';
595             my $replacement_amount = 100;
596             my $processfee_amount  = 20;
597
598             my $item_type = $builder->build_object(
599                 {
600                     class => 'Koha::ItemTypes',
601                     value => {
602                         notforloan         => undef,
603                         rentalcharge       => 0,
604                         defaultreplacecost => undef,
605                         processfee         => 0,
606                         rentalcharge_daily => 0,
607                     }
608                 }
609             );
610             my $item = Koha::Item->new(
611                 {
612                     biblionumber     => $biblio->biblionumber,
613                     homebranch       => $library->branchcode,
614                     holdingbranch    => $library->branchcode,
615                     barcode          => $barcode,
616                     replacementprice => $replacement_amount,
617                     itype            => $item_type->itemtype
618                 },
619             )->store;
620
621             my $issue =
622               C4::Circulation::AddIssue( $patron->unblessed, $barcode );
623
624             # Simulate item marked as lost
625             $item->itemlost(1)->store;
626             C4::Circulation::LostItem( $item->itemnumber, 1 );
627
628             my $lost_fee_lines = Koha::Account::Lines->search(
629                 {
630                     borrowernumber  => $patron->id,
631                     itemnumber      => $item->itemnumber,
632                     debit_type_code => 'LOST'
633                 }
634             );
635             is( $lost_fee_lines->count, 1, 'Only one lost item fee produced' );
636             my $lost_fee_line = $lost_fee_lines->next;
637             is( $lost_fee_line->amount + 0,
638                 $replacement_amount, 'The right LOST amount is generated' );
639             is( $lost_fee_line->amountoutstanding + 0,
640                 $replacement_amount,
641                 'The right LOST amountountstanding is generated' );
642
643             my $account = $patron->account;
644             is( $account->balance, $replacement_amount, 'Balance is L' );
645
646             # Partially pay fee
647             my $payment_amount = 27;
648             my $payment        = $account->add_credit(
649                 {
650                     amount    => $payment_amount,
651                     type      => 'PAYMENT',
652                     interface => 'test',
653                 }
654             );
655             $payment->apply(
656                 { debits => [$lost_fee_line], offset_type => 'Payment' } );
657
658             is(
659                 $account->balance,
660                 $replacement_amount - $payment_amount,
661                 'Payment applied'
662             );
663
664             my $manual_debit_amount = 80;
665             $account->add_debit(
666                 {
667                     amount    => $manual_debit_amount,
668                     type      => 'OVERDUE',
669                     interface => 'test'
670                 }
671             );
672
673             is(
674                 $account->balance,
675                 $manual_debit_amount + $replacement_amount - $payment_amount,
676                 'Manual debit applied'
677             );
678
679             t::lib::Mocks::mock_preference( 'AccountAutoReconcile', 1 );
680
681             # Simulate item marked as found
682             $item->itemlost(0)->store;
683             is( $item->{_refunded}, 1, 'Refund triggered' );
684
685             my $credit_return = Koha::Account::Lines->search(
686                 {
687                     itemnumber       => $item->itemnumber,
688                     credit_type_code => 'LOST_FOUND'
689                 },
690                 { rows => 1 }
691             )->single;
692
693             ok( $credit_return, 'An account line of type LOST_FOUND is added' );
694
695             is(
696                 $account->balance,
697                 $manual_debit_amount - $payment_amount,
698                 'Balance is PROCESSING - payment (LOST_FOUND)'
699             );
700
701             my $manual_debit = Koha::Account::Lines->search(
702                 {
703                     borrowernumber  => $patron->id,
704                     debit_type_code => 'OVERDUE',
705                     status          => 'UNRETURNED'
706                 }
707             )->next;
708             is(
709                 $manual_debit->amountoutstanding + 0,
710                 $manual_debit_amount - $payment_amount,
711                 'reconcile_balance was called'
712             );
713           };
714
715         subtest 'Patron deleted' => sub {
716             plan tests => 1;
717
718             my $patron = $builder->build_object( { class => 'Koha::Patrons' } );
719             my $barcode            = 'KD123456794';
720             my $replacement_amount = 100;
721             my $processfee_amount  = 20;
722
723             my $item_type = $builder->build_object(
724                 {
725                     class => 'Koha::ItemTypes',
726                     value => {
727                         notforloan         => undef,
728                         rentalcharge       => 0,
729                         defaultreplacecost => undef,
730                         processfee         => 0,
731                         rentalcharge_daily => 0,
732                     }
733                 }
734             );
735             my $item = Koha::Item->new(
736                 {
737                     biblionumber     => $biblio->biblionumber,
738                     homebranch       => $library->branchcode,
739                     holdingbranch    => $library->branchcode,
740                     barcode          => $barcode,
741                     replacementprice => $replacement_amount,
742                     itype            => $item_type->itemtype
743                 },
744             )->store;
745
746             my $issue =
747               C4::Circulation::AddIssue( $patron->unblessed, $barcode );
748
749             # Simulate item marked as lost
750             $item->itemlost(1)->store;
751             C4::Circulation::LostItem( $item->itemnumber, 1 );
752
753             $issue->delete();
754             $patron->delete();
755
756             # Simulate item marked as found
757             $item->itemlost(0)->store;
758             is( $item->{_refunded}, undef, 'No refund triggered' );
759
760         };
761
762         subtest 'Continue when userenv is not set' => sub {
763             plan tests => 1;
764
765             my $patron = $builder->build_object( { class => 'Koha::Patrons' } );
766             my $barcode            = 'KD123456795';
767             my $replacement_amount = 100;
768             my $processfee_amount  = 20;
769
770             my $item_type = $builder->build_object(
771                 {
772                     class => 'Koha::ItemTypes',
773                     value => {
774                         notforloan         => undef,
775                         rentalcharge       => 0,
776                         defaultreplacecost => undef,
777                         processfee         => 0,
778                         rentalcharge_daily => 0,
779                     }
780                 }
781             );
782             my $item = $builder->build_sample_item(
783                 {
784                     biblionumber     => $biblio->biblionumber,
785                     homebranch       => $library->branchcode,
786                     holdingbranch    => $library->branchcode,
787                     barcode          => $barcode,
788                     replacementprice => $replacement_amount,
789                     itype            => $item_type->itemtype
790                 }
791             );
792
793             my $issue =
794               C4::Circulation::AddIssue( $patron->unblessed, $barcode );
795
796             # Simulate item marked as lost
797             $item->itemlost(1)->store;
798             C4::Circulation::LostItem( $item->itemnumber, 1 );
799
800             # Unset the userenv
801             C4::Context->_new_userenv(undef);
802
803             # Simluate item marked as found
804             $item->itemlost(0)->store;
805             is( $item->{_refunded}, 1, 'No refund triggered' );
806
807         };
808     };
809 };
810
811 subtest 'get_transfer' => sub {
812     plan tests => 3;
813
814     my $transfer = $new_item_1->get_transfer();
815     is( $transfer, undef, 'Koha::Item->get_transfer should return undef if the item is not in transit' );
816
817     my $library_to = $builder->build( { source => 'Branch' } );
818
819     C4::Circulation::transferbook({
820         from_branch => $new_item_1->holdingbranch,
821         to_branch => $library_to->{branchcode},
822         barcode => $new_item_1->barcode,
823     });
824
825     $transfer = $new_item_1->get_transfer();
826     is( ref($transfer), 'Koha::Item::Transfer', 'Koha::Item->get_transfer should return a Koha::Item::Transfers object' );
827
828     is( $transfer->itemnumber, $new_item_1->itemnumber, 'Koha::Item->get_transfer should return a valid Koha::Item::Transfers object' );
829 };
830
831 subtest 'holds' => sub {
832     plan tests => 5;
833
834     my $biblio = $builder->build_sample_biblio();
835     my $item   = $builder->build_sample_item({
836         biblionumber => $biblio->biblionumber,
837     });
838     is($item->holds->count, 0, "Nothing returned if no holds");
839     my $hold1 = $builder->build({ source => 'Reserve', value => { itemnumber=>$item->itemnumber, found => 'T' }});
840     my $hold2 = $builder->build({ source => 'Reserve', value => { itemnumber=>$item->itemnumber, found => 'W' }});
841     my $hold3 = $builder->build({ source => 'Reserve', value => { itemnumber=>$item->itemnumber, found => 'W' }});
842
843     is($item->holds()->count,3,"Three holds found");
844     is($item->holds({found => 'W'})->count,2,"Two waiting holds found");
845     is_deeply($item->holds({found => 'T'})->next->unblessed,$hold1,"Found transit holds matches the hold");
846     is($item->holds({found => undef})->count, 0,"Nothing returned if no matching holds");
847 };
848
849 subtest 'biblio' => sub {
850     plan tests => 2;
851
852     my $biblio = $retrieved_item_1->biblio;
853     is( ref( $biblio ), 'Koha::Biblio', 'Koha::Item->biblio should return a Koha::Biblio' );
854     is( $biblio->biblionumber, $retrieved_item_1->biblionumber, 'Koha::Item->biblio should return the correct biblio' );
855 };
856
857 subtest 'biblioitem' => sub {
858     plan tests => 2;
859
860     my $biblioitem = $retrieved_item_1->biblioitem;
861     is( ref( $biblioitem ), 'Koha::Biblioitem', 'Koha::Item->biblioitem should return a Koha::Biblioitem' );
862     is( $biblioitem->biblionumber, $retrieved_item_1->biblionumber, 'Koha::Item->biblioitem should return the correct biblioitem' );
863 };
864
865 # Restore userenv
866 t::lib::Mocks::mock_userenv({ branchcode => $library->{branchcode} });
867 subtest 'checkout' => sub {
868     plan tests => 5;
869     my $item = Koha::Items->find( $new_item_1->itemnumber );
870     # No checkout yet
871     my $checkout = $item->checkout;
872     is( $checkout, undef, 'Koha::Item->checkout should return undef if there is no current checkout on this item' );
873
874     # Add a checkout
875     my $patron = $builder->build({ source => 'Borrower' });
876     C4::Circulation::AddIssue( $patron, $item->barcode );
877     $checkout = $retrieved_item_1->checkout;
878     is( ref( $checkout ), 'Koha::Checkout', 'Koha::Item->checkout should return a Koha::Checkout' );
879     is( $checkout->itemnumber, $item->itemnumber, 'Koha::Item->checkout should return the correct checkout' );
880     is( $checkout->borrowernumber, $patron->{borrowernumber}, 'Koha::Item->checkout should return the correct checkout' );
881
882     # Do the return
883     C4::Circulation::AddReturn( $item->barcode );
884
885     # There is no more checkout on this item, making sure it will not return old checkouts
886     $checkout = $item->checkout;
887     is( $checkout, undef, 'Koha::Item->checkout should return undef if there is no *current* checkout on this item' );
888 };
889
890 subtest 'can_be_transferred' => sub {
891     plan tests => 5;
892
893     t::lib::Mocks::mock_preference('UseBranchTransferLimits', 1);
894     t::lib::Mocks::mock_preference('BranchTransferLimitsType', 'itemtype');
895
896     my $biblio   = $builder->build_sample_biblio();
897     my $library1 = $builder->build_object( { class => 'Koha::Libraries' } );
898     my $library2 = $builder->build_object( { class => 'Koha::Libraries' } );
899     my $item  = $builder->build_sample_item({
900         biblionumber     => $biblio->biblionumber,
901         homebranch       => $library1->branchcode,
902         holdingbranch    => $library1->branchcode,
903     });
904
905     is(Koha::Item::Transfer::Limits->search({
906         fromBranch => $library1->branchcode,
907         toBranch => $library2->branchcode,
908     })->count, 0, 'There are no transfer limits between libraries.');
909     ok($item->can_be_transferred({ to => $library2 }),
910        'Item can be transferred between libraries.');
911
912     my $limit = Koha::Item::Transfer::Limit->new({
913         fromBranch => $library1->branchcode,
914         toBranch => $library2->branchcode,
915         itemtype => $item->effective_itemtype,
916     })->store;
917     is(Koha::Item::Transfer::Limits->search({
918         fromBranch => $library1->branchcode,
919         toBranch => $library2->branchcode,
920     })->count, 1, 'Given we have added a transfer limit,');
921     is($item->can_be_transferred({ to => $library2 }), 0,
922        'Item can no longer be transferred between libraries.');
923     is($item->can_be_transferred({ to => $library2, from => $library1 }), 0,
924        'We get the same result also if we pass the from-library parameter.');
925 };
926
927 # Reset nb_of_items prior to testing delete
928 $nb_of_items = Koha::Items->search->count;
929
930 # Test delete
931 $retrieved_item_1->delete;
932 is( Koha::Items->search->count, $nb_of_items - 1, 'Delete should have deleted the item' );
933
934 $schema->storage->txn_rollback;