+ amount => 12,
+ amountoutstanding => 12,
+ interface => 'something',
+ }
+ )->store();
+
+ # AddRenewal doesn't call _FixAccountForLostAndFound
+ AddIssue( $patron->unblessed, $item->barcode );
+
+ is( $patron->checkouts->count, 1,
+ 'Renewal should not return the item even if a LOST payment has been made earlier'
+ );
+};
+
+subtest 'Filling a hold should cancel existing transfer' => sub {
+ plan tests => 4;
+
+ t::lib::Mocks::mock_preference('AutomaticItemReturn', 1);
+
+ my $libraryA = $builder->build_object( { class => 'Koha::Libraries' } );
+ my $libraryB = $builder->build_object( { class => 'Koha::Libraries' } );
+ my $patron = $builder->build_object(
+ {
+ class => 'Koha::Patrons',
+ value => {
+ categorycode => $patron_category->{categorycode},
+ branchcode => $libraryA->branchcode,
+ }
+ }
+ )->store;
+
+ my $item = $builder->build_sample_item({
+ homebranch => $libraryB->branchcode,
+ });
+
+ my ( undef, $message ) = AddReturn( $item->barcode, $libraryA->branchcode, undef, undef );
+ is( Koha::Item::Transfers->search({ itemnumber => $item->itemnumber, datearrived => undef })->count, 1, "We generate a transfer on checkin");
+ AddReserve({
+ branchcode => $libraryA->branchcode,
+ borrowernumber => $patron->borrowernumber,
+ biblionumber => $item->biblionumber,
+ itemnumber => $item->itemnumber
+ });
+ my $reserves = Koha::Holds->search({ itemnumber => $item->itemnumber });
+ is( $reserves->count, 1, "Reserve is placed");
+ ( undef, $message ) = AddReturn( $item->barcode, $libraryA->branchcode, undef, undef );
+ my $reserve = $reserves->next;
+ ModReserveAffect( $item->itemnumber, $patron->borrowernumber, 0, $reserve->reserve_id );
+ $reserve->discard_changes;
+ ok( $reserve->found eq 'W', "Reserve is marked waiting" );
+ is( Koha::Item::Transfers->search({ itemnumber => $item->itemnumber, datearrived => undef })->count, 0, "No outstanding transfers when hold is waiting");
+};
+
+subtest 'Tests for NoRefundOnLostReturnedItemsAge with AddReturn' => sub {
+
+ plan tests => 4;
+
+ t::lib::Mocks::mock_preference('BlockReturnOfLostItems', 0);
+ my $library = $builder->build_object( { class => 'Koha::Libraries' } );
+ my $patron = $builder->build_object(
+ {
+ class => 'Koha::Patrons',
+ value => { categorycode => $patron_category->{categorycode} }
+ }
+ );
+
+ my $biblionumber = $builder->build_sample_biblio(
+ {
+ branchcode => $library->branchcode,
+ }
+ )->biblionumber;
+
+ # And the circulation rule
+ Koha::CirculationRules->search->delete;
+ Koha::CirculationRules->set_rules(
+ {
+ categorycode => undef,
+ itemtype => undef,
+ branchcode => undef,
+ rules => {
+ issuelength => 14,
+ lengthunit => 'days',
+ }
+ }
+ );
+ $builder->build(
+ {
+ source => 'CirculationRule',
+ value => {
+ branchcode => undef,
+ categorycode => undef,
+ itemtype => undef,
+ rule_name => 'lostreturn',
+ rule_value => 'refund'
+ }
+ }
+ );
+
+ subtest 'NoRefundOnLostReturnedItemsAge = undef' => sub {
+ plan tests => 3;
+
+ t::lib::Mocks::mock_preference( 'WhenLostChargeReplacementFee', 1 );
+ t::lib::Mocks::mock_preference( 'NoRefundOnLostReturnedItemsAge', undef );
+
+ my $lost_on = dt_from_string->subtract( days => 7 )->date;
+
+ my $item = $builder->build_sample_item(
+ {
+ biblionumber => $biblionumber,
+ library => $library->branchcode,
+ replacementprice => '42',
+ }
+ );
+ my $issue = AddIssue( $patron->unblessed, $item->barcode );
+ LostItem( $item->itemnumber, 'cli', 0 );
+ $item->_result->itemlost(1);
+ $item->_result->itemlost_on( $lost_on );
+ $item->_result->update();
+
+ my $a = Koha::Account::Lines->search(
+ {
+ itemnumber => $item->id,
+ borrowernumber => $patron->borrowernumber
+ }
+ )->next;
+ ok( $a, "Found accountline for lost fee" );
+ is( $a->amountoutstanding + 0, 42, "Lost fee charged correctly" );
+ my ( $doreturn, $messages ) = AddReturn( $item->barcode, $library->branchcode, undef, dt_from_string );
+ $a = $a->get_from_storage;
+ is( $a->amountoutstanding + 0, 0, "Lost fee was refunded" );
+ $a->delete;
+ };
+
+ subtest 'NoRefundOnLostReturnedItemsAge > length of days item has been lost' => sub {
+ plan tests => 3;
+
+ t::lib::Mocks::mock_preference( 'WhenLostChargeReplacementFee', 1 );
+ t::lib::Mocks::mock_preference( 'NoRefundOnLostReturnedItemsAge', 7 );
+
+ my $lost_on = dt_from_string->subtract( days => 6 )->date;
+
+ my $item = $builder->build_sample_item(
+ {
+ biblionumber => $biblionumber,
+ library => $library->branchcode,
+ replacementprice => '42',
+ }
+ );
+ my $issue = AddIssue( $patron->unblessed, $item->barcode );
+ LostItem( $item->itemnumber, 'cli', 0 );
+ $item->_result->itemlost(1);
+ $item->_result->itemlost_on( $lost_on );
+ $item->_result->update();
+
+ my $a = Koha::Account::Lines->search(
+ {
+ itemnumber => $item->id,
+ borrowernumber => $patron->borrowernumber
+ }
+ )->next;
+ ok( $a, "Found accountline for lost fee" );
+ is( $a->amountoutstanding + 0, 42, "Lost fee charged correctly" );
+ my ( $doreturn, $messages ) = AddReturn( $item->barcode, $library->branchcode, undef, dt_from_string );
+ $a = $a->get_from_storage;
+ is( $a->amountoutstanding + 0, 0, "Lost fee was refunded" );
+ $a->delete;
+ };
+
+ subtest 'NoRefundOnLostReturnedItemsAge = length of days item has been lost' => sub {
+ plan tests => 3;
+
+ t::lib::Mocks::mock_preference( 'WhenLostChargeReplacementFee', 1 );
+ t::lib::Mocks::mock_preference( 'NoRefundOnLostReturnedItemsAge', 7 );
+
+ my $lost_on = dt_from_string->subtract( days => 7 )->date;
+
+ my $item = $builder->build_sample_item(
+ {
+ biblionumber => $biblionumber,
+ library => $library->branchcode,
+ replacementprice => '42',
+ }
+ );
+ my $issue = AddIssue( $patron->unblessed, $item->barcode );
+ LostItem( $item->itemnumber, 'cli', 0 );
+ $item->_result->itemlost(1);
+ $item->_result->itemlost_on( $lost_on );
+ $item->_result->update();
+
+ my $a = Koha::Account::Lines->search(
+ {
+ itemnumber => $item->id,
+ borrowernumber => $patron->borrowernumber
+ }
+ )->next;
+ ok( $a, "Found accountline for lost fee" );
+ is( $a->amountoutstanding + 0, 42, "Lost fee charged correctly" );
+ my ( $doreturn, $messages ) = AddReturn( $item->barcode, $library->branchcode, undef, dt_from_string );
+ $a = $a->get_from_storage;
+ is( $a->amountoutstanding + 0, 42, "Lost fee was not refunded" );
+ $a->delete;
+ };
+
+ subtest 'NoRefundOnLostReturnedItemsAge < length of days item has been lost' => sub {
+ plan tests => 3;
+
+ t::lib::Mocks::mock_preference( 'WhenLostChargeReplacementFee', 1 );
+ t::lib::Mocks::mock_preference( 'NoRefundOnLostReturnedItemsAge', 7 );
+
+ my $lost_on = dt_from_string->subtract( days => 8 )->date;
+
+ my $item = $builder->build_sample_item(
+ {
+ biblionumber => $biblionumber,
+ library => $library->branchcode,
+ replacementprice => '42',
+ }
+ );
+ my $issue = AddIssue( $patron->unblessed, $item->barcode );
+ LostItem( $item->itemnumber, 'cli', 0 );
+ $item->_result->itemlost(1);
+ $item->_result->itemlost_on( $lost_on );
+ $item->_result->update();
+
+ my $a = Koha::Account::Lines->search(
+ {
+ itemnumber => $item->id,
+ borrowernumber => $patron->borrowernumber
+ }
+ );
+ $a = $a->next;
+ ok( $a, "Found accountline for lost fee" );
+ is( $a->amountoutstanding + 0, 42, "Lost fee charged correctly" );
+ my ( $doreturn, $messages ) = AddReturn( $item->barcode, $library->branchcode, undef, dt_from_string );
+ $a = $a->get_from_storage;
+ is( $a->amountoutstanding + 0, 42, "Lost fee was not refunded" );
+ $a->delete;
+ };
+};
+
+subtest 'Tests for NoRefundOnLostReturnedItemsAge with AddIssue' => sub {
+
+ plan tests => 4;
+
+ t::lib::Mocks::mock_preference('BlockReturnOfLostItems', 0);
+ my $library = $builder->build_object( { class => 'Koha::Libraries' } );
+ my $patron = $builder->build_object(
+ {
+ class => 'Koha::Patrons',
+ value => { categorycode => $patron_category->{categorycode} }
+ }
+ );
+ my $patron2 = $builder->build_object(
+ {
+ class => 'Koha::Patrons',
+ value => { categorycode => $patron_category->{categorycode} }
+ }
+ );
+
+ my $biblionumber = $builder->build_sample_biblio(
+ {
+ branchcode => $library->branchcode,
+ }
+ )->biblionumber;
+
+ # And the circulation rule
+ Koha::CirculationRules->search->delete;
+ Koha::CirculationRules->set_rules(
+ {
+ categorycode => undef,
+ itemtype => undef,
+ branchcode => undef,
+ rules => {
+ issuelength => 14,
+ lengthunit => 'days',
+ }
+ }
+ );
+ $builder->build(
+ {
+ source => 'CirculationRule',
+ value => {
+ branchcode => undef,
+ categorycode => undef,
+ itemtype => undef,
+ rule_name => 'lostreturn',
+ rule_value => 'refund'
+ }
+ }
+ );
+
+ subtest 'NoRefundOnLostReturnedItemsAge = undef' => sub {
+ plan tests => 3;
+
+ t::lib::Mocks::mock_preference( 'WhenLostChargeReplacementFee', 1 );
+ t::lib::Mocks::mock_preference( 'NoRefundOnLostReturnedItemsAge', undef );
+
+ my $lost_on = dt_from_string->subtract( days => 7 )->date;
+
+ my $item = $builder->build_sample_item(
+ {
+ biblionumber => $biblionumber,
+ library => $library->branchcode,
+ replacementprice => '42',
+ }
+ );
+ my $issue = AddIssue( $patron->unblessed, $item->barcode );
+ LostItem( $item->itemnumber, 'cli', 0 );
+ $item->_result->itemlost(1);
+ $item->_result->itemlost_on( $lost_on );
+ $item->_result->update();
+
+ my $a = Koha::Account::Lines->search(
+ {
+ itemnumber => $item->id,
+ borrowernumber => $patron->borrowernumber
+ }
+ )->next;
+ ok( $a, "Found accountline for lost fee" );
+ is( $a->amountoutstanding + 0, 42, "Lost fee charged correctly" );
+ $issue = AddIssue( $patron2->unblessed, $item->barcode );
+ $a = $a->get_from_storage;
+ is( $a->amountoutstanding + 0, 0, "Lost fee was refunded" );
+ $a->delete;
+ $issue->delete;
+ };
+
+ subtest 'NoRefundOnLostReturnedItemsAge > length of days item has been lost' => sub {
+ plan tests => 3;
+
+ t::lib::Mocks::mock_preference( 'WhenLostChargeReplacementFee', 1 );
+ t::lib::Mocks::mock_preference( 'NoRefundOnLostReturnedItemsAge', 7 );
+
+ my $lost_on = dt_from_string->subtract( days => 6 )->date;
+
+ my $item = $builder->build_sample_item(
+ {
+ biblionumber => $biblionumber,
+ library => $library->branchcode,
+ replacementprice => '42',
+ }
+ );
+ my $issue = AddIssue( $patron->unblessed, $item->barcode );
+ LostItem( $item->itemnumber, 'cli', 0 );
+ $item->_result->itemlost(1);
+ $item->_result->itemlost_on( $lost_on );
+ $item->_result->update();
+
+ my $a = Koha::Account::Lines->search(
+ {
+ itemnumber => $item->id,
+ borrowernumber => $patron->borrowernumber
+ }
+ )->next;
+ ok( $a, "Found accountline for lost fee" );
+ is( $a->amountoutstanding + 0, 42, "Lost fee charged correctly" );
+ $issue = AddIssue( $patron2->unblessed, $item->barcode );
+ $a = $a->get_from_storage;
+ is( $a->amountoutstanding + 0, 0, "Lost fee was refunded" );
+ $a->delete;
+ };
+
+ subtest 'NoRefundOnLostReturnedItemsAge = length of days item has been lost' => sub {
+ plan tests => 3;
+
+ t::lib::Mocks::mock_preference( 'WhenLostChargeReplacementFee', 1 );
+ t::lib::Mocks::mock_preference( 'NoRefundOnLostReturnedItemsAge', 7 );
+
+ my $lost_on = dt_from_string->subtract( days => 7 )->date;
+
+ my $item = $builder->build_sample_item(
+ {
+ biblionumber => $biblionumber,
+ library => $library->branchcode,
+ replacementprice => '42',
+ }
+ );
+ my $issue = AddIssue( $patron->unblessed, $item->barcode );
+ LostItem( $item->itemnumber, 'cli', 0 );
+ $item->_result->itemlost(1);
+ $item->_result->itemlost_on( $lost_on );
+ $item->_result->update();
+
+ my $a = Koha::Account::Lines->search(
+ {
+ itemnumber => $item->id,
+ borrowernumber => $patron->borrowernumber
+ }
+ )->next;
+ ok( $a, "Found accountline for lost fee" );
+ is( $a->amountoutstanding + 0, 42, "Lost fee charged correctly" );
+ $issue = AddIssue( $patron2->unblessed, $item->barcode );
+ $a = $a->get_from_storage;
+ is( $a->amountoutstanding + 0, 42, "Lost fee was not refunded" );
+ $a->delete;
+ };
+
+ subtest 'NoRefundOnLostReturnedItemsAge < length of days item has been lost' => sub {
+ plan tests => 3;
+
+ t::lib::Mocks::mock_preference( 'WhenLostChargeReplacementFee', 1 );
+ t::lib::Mocks::mock_preference( 'NoRefundOnLostReturnedItemsAge', 7 );
+
+ my $lost_on = dt_from_string->subtract( days => 8 )->date;
+
+ my $item = $builder->build_sample_item(
+ {
+ biblionumber => $biblionumber,
+ library => $library->branchcode,
+ replacementprice => '42',
+ }
+ );
+ my $issue = AddIssue( $patron->unblessed, $item->barcode );
+ LostItem( $item->itemnumber, 'cli', 0 );
+ $item->_result->itemlost(1);
+ $item->_result->itemlost_on( $lost_on );
+ $item->_result->update();
+
+ my $a = Koha::Account::Lines->search(
+ {
+ itemnumber => $item->id,
+ borrowernumber => $patron->borrowernumber
+ }
+ );
+ $a = $a->next;
+ ok( $a, "Found accountline for lost fee" );
+ is( $a->amountoutstanding + 0, 42, "Lost fee charged correctly" );
+ $issue = AddIssue( $patron2->unblessed, $item->barcode );
+ $a = $a->get_from_storage;
+ is( $a->amountoutstanding + 0, 42, "Lost fee was not refunded" );
+ $a->delete;
+ };
+};
+
+subtest 'transferbook tests' => sub {
+ plan tests => 9;
+
+ throws_ok
+ { C4::Circulation::transferbook({}); }
+ 'Koha::Exceptions::MissingParameter',
+ 'Koha::Patron->store raises an exception on missing params';
+
+ throws_ok
+ { C4::Circulation::transferbook({to_branch=>'anything'}); }
+ 'Koha::Exceptions::MissingParameter',
+ 'Koha::Patron->store raises an exception on missing params';
+
+ throws_ok
+ { C4::Circulation::transferbook({from_branch=>'anything'}); }
+ 'Koha::Exceptions::MissingParameter',
+ 'Koha::Patron->store raises an exception on missing params';
+
+ my ($doreturn,$messages) = C4::Circulation::transferbook({to_branch=>'there',from_branch=>'here'});
+ is( $doreturn, 0, "No return without barcode");
+ ok( exists $messages->{BadBarcode}, "We get a BadBarcode message if no barcode passed");
+ is( $messages->{BadBarcode}, undef, "No barcode passed means undef BadBarcode" );
+
+ ($doreturn,$messages) = C4::Circulation::transferbook({to_branch=>'there',from_branch=>'here',barcode=>'BadBarcode'});
+ is( $doreturn, 0, "No return without barcode");
+ ok( exists $messages->{BadBarcode}, "We get a BadBarcode message if no barcode passed");
+ is( $messages->{BadBarcode}, 'BadBarcode', "No barcode passed means undef BadBarcode" );
+
+};
+
+subtest 'Checkout should correctly terminate a transfer' => sub {
+ plan tests => 7;
+
+ my $library_1 = $builder->build_object( { class => 'Koha::Libraries' } );
+ my $patron_1 = $builder->build_object(
+ {
+ class => 'Koha::Patrons',
+ value => { branchcode => $library_1->branchcode }
+ }
+ );
+ my $library_2 = $builder->build_object( { class => 'Koha::Libraries' } );
+ my $patron_2 = $builder->build_object(
+ {
+ class => 'Koha::Patrons',
+ value => { branchcode => $library_2->branchcode }
+ }
+ );
+
+ my $item = $builder->build_sample_item(
+ {
+ library => $library_1->branchcode,
+ }
+ );
+
+ t::lib::Mocks::mock_userenv( { branchcode => $library_1->branchcode } );
+ my $reserve_id = AddReserve(
+ {
+ branchcode => $library_2->branchcode,
+ borrowernumber => $patron_2->borrowernumber,
+ biblionumber => $item->biblionumber,
+ itemnumber => $item->itemnumber,
+ priority => 1,
+ }
+ );
+
+ my $do_transfer = 1;
+ ModItemTransfer( $item->itemnumber, $library_1->branchcode,
+ $library_2->branchcode, 'Manual' );
+ ModReserveAffect( $item->itemnumber, undef, $do_transfer, $reserve_id );
+ GetOtherReserves( $item->itemnumber )
+ ; # To put the Reason, it's what does returns.pl...
+ my $hold = Koha::Holds->find($reserve_id);
+ is( $hold->found, 'T', 'Hold is in transit' );
+ my $transfer = $item->get_transfer;
+ is( $transfer->frombranch, $library_1->branchcode );
+ is( $transfer->tobranch, $library_2->branchcode );
+ is( $transfer->reason, 'Reserve' );
+
+ t::lib::Mocks::mock_userenv( { branchcode => $library_2->branchcode } );
+ AddIssue( $patron_1->unblessed, $item->barcode );
+ $transfer = $transfer->get_from_storage;
+ isnt( $transfer->datearrived, undef );
+ $hold = $hold->get_from_storage;
+ is( $hold->found, undef, 'Hold is waiting' );
+ is( $hold->priority, 1, );
+};
+
+subtest 'AddIssue records staff who checked out item if appropriate' => sub {
+ plan tests => 2;
+
+ $module->mock( 'userenv', sub { { branch => $library->{id} } } );
+
+ my $library = $builder->build_object( { class => 'Koha::Libraries' } );
+ my $patron = $builder->build_object(
+ {
+ class => 'Koha::Patrons',
+ value => { categorycode => $patron_category->{categorycode} }
+ }
+ );
+ my $issuer = $builder->build_object(
+ {
+ class => 'Koha::Patrons',
+ value => { categorycode => $patron_category->{categorycode} }
+ }
+ );
+ my $item = $builder->build_sample_item(
+ {
+ library => $library->{branchcode}
+ }
+ );
+
+ $module->mock( 'userenv', sub { { branch => $library->id, number => $issuer->{borrowernumber} } } );
+
+ my $dt_from = dt_from_string();
+ my $dt_to = dt_from_string()->add( days => 7 );
+
+ my $issue = AddIssue( $patron->unblessed, $item->barcode, $dt_to, undef, $dt_from );
+
+ is( $issue->issuer, undef, "Staff who checked out the item not recorded when RecordStaffUserOnCheckout turned off" );
+
+ t::lib::Mocks::mock_preference('RecordStaffUserOnCheckout', 1);
+
+ my $issue2 =
+ AddIssue( $patron->unblessed, $item->barcode, $dt_to, undef, $dt_from );
+
+ is( $issue->issuer, $issuer->{borrowernumber}, "Staff who checked out the item recorded when RecordStaffUserOnCheckout turned on" );
+};
+
+subtest "Item's onloan value should be set if checked out item is checked out to a different patron" => sub {
+ plan tests => 2;
+
+ my $library_1 = $builder->build_object( { class => 'Koha::Libraries' } );
+ my $patron_1 = $builder->build_object(
+ {
+ class => 'Koha::Patrons',
+ value => { branchcode => $library_1->branchcode }
+ }
+ );
+ my $patron_2 = $builder->build_object(
+ {
+ class => 'Koha::Patrons',
+ value => { branchcode => $library_1->branchcode }
+ }
+ );
+
+ my $item = $builder->build_sample_item(
+ {
+ library => $library_1->branchcode,