Bug 27272: Remove GetItemsInfo, GetItemsLocationInfo and GetHostItemsInfo
[koha-ffzg.git] / t / db_dependent / StockRotationItems.t
old mode 100644 (file)
new mode 100755 (executable)
index 2d9bf89..c6b3afe
@@ -4,28 +4,32 @@
 #
 # This file is part of Koha.
 #
-# Koha is free software; you can redistribute it and/or modify it under the
-# terms of the GNU General Public License as published by the Free Software
-# Foundation; either version 3 of the License, or (at your option) any later
-# version.
+# Koha is free software; you can redistribute it and/or modify it
+# under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 3 of the License, or
+# (at your option) any later version.
 #
-# Koha is distributed in the hope that it will be useful, but WITHOUT ANY
-# WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
-# A PARTICULAR PURPOSE.  See the GNU General Public License for more details.
+# Koha is distributed in the hope that it will be useful, but
+# WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
 #
-# You should have received a copy of the GNU General Public License along
-# with Koha; if not, write to the Free Software Foundation, Inc.,
-# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+# You should have received a copy of the GNU General Public License
+# along with Koha; if not, see <http://www.gnu.org/licenses>.
 
 use Modern::Perl;
 
 use DateTime;
 use DateTime::Duration;
 use Koha::Database;
+use Koha::DateUtils qw( dt_from_string );
 use Koha::Item::Transfer;
+
+use Test::Warn;
 use t::lib::TestBuilder;
+use t::lib::Mocks;
 
-use Test::More tests => 8;
+use Test::More tests => 9;
 
 my $schema = Koha::Database->new->schema;
 
@@ -40,13 +44,13 @@ subtest 'Basic object tests' => sub {
 
     $schema->storage->txn_begin;
 
-    my $itm = $builder->build({ source => 'Item' });
+    my $itm = $builder->build_sample_item;
     my $stage = $builder->build({ source => 'Stockrotationstage' });
 
     my $item = $builder->build({
         source => 'Stockrotationitem',
         value  => {
-            itemnumber_id => $itm->{itemnumber},
+            itemnumber_id => $itm->itemnumber,
             stage_id      => $stage->{stage_id},
         },
     });
@@ -59,8 +63,8 @@ subtest 'Basic object tests' => sub {
     );
 
     # Relationship to rota
-    isa_ok( $sritem->itemnumber, 'Koha::Item', "Fetched related item." );
-    is( $sritem->itemnumber->itemnumber, $itm->{itemnumber}, "Related rota OK." );
+    isa_ok( $sritem->item, 'Koha::Item', "Fetched related item." );
+    is( $sritem->item->itemnumber, $itm->itemnumber, "Related rota OK." );
 
     # Relationship to stage
     isa_ok( $sritem->stage, 'Koha::StockRotationStage', "Fetched related stage." );
@@ -77,10 +81,16 @@ subtest 'Tests for needs_repatriating' => sub {
     $schema->storage->txn_begin;
 
     # Setup a pristine stockrotation context.
-    my $sritem = $builder->build({ source => 'Stockrotationitem' });
+    my $sritem = $builder->build(
+        {
+            source => 'Stockrotationitem',
+            value =>
+              { itemnumber_id => $builder->build_sample_item->itemnumber }
+        }
+    );
     my $dbitem = Koha::StockRotationItems->find($sritem->{itemnumber_id});
-    $dbitem->itemnumber->homebranch($dbitem->stage->branchcode_id);
-    $dbitem->itemnumber->holdingbranch($dbitem->stage->branchcode_id);
+    $dbitem->item->homebranch($dbitem->stage->branchcode_id);
+    $dbitem->item->holdingbranch($dbitem->stage->branchcode_id);
     $dbitem->stage->position(1);
 
     my $dbrota = $dbitem->stage->rota;
@@ -99,7 +109,7 @@ subtest 'Tests for needs_repatriating' => sub {
     );
 
     my $branch = $builder->build({ source => 'Branch' });
-    $dbitem->itemnumber->holdingbranch($branch->{branchcode});
+    $dbitem->item->holdingbranch($branch->{branchcode});
 
     # - homebranch != holdingbranch [1]
     is(
@@ -108,8 +118,8 @@ subtest 'Tests for needs_repatriating' => sub {
     );
 
     # Set to incorrect homebranch.
-    $dbitem->itemnumber->holdingbranch($dbitem->stage->branchcode_id);
-    $dbitem->itemnumber->homebranch($branch->{branchcode});
+    $dbitem->item->holdingbranch($dbitem->stage->branchcode_id);
+    $dbitem->item->homebranch($branch->{branchcode});
     # - homebranch != stockrotationstage.branch & not in transit [1]
     is(
         $dbitem->needs_repatriating, 1,
@@ -128,44 +138,99 @@ subtest 'Tests for needs_repatriating' => sub {
 };
 
 subtest "Tests for repatriate." => sub {
-    plan tests => 3;
+    plan tests => 9;
     $schema->storage->txn_begin;
-    my $sritem = $builder->build({ source => 'Stockrotationitem' });
-    my $dbitem = Koha::StockRotationItems->find($sritem->{itemnumber_id});
-    $dbitem->stage->position(1);
-    $dbitem->stage->duration(50);
+
+    my $sritem_1 = $builder->build_object(
+        {
+            class => 'Koha::StockRotationItems',
+            value  => {
+                itemnumber_id => $builder->build_sample_item->itemnumber
+            }
+        }
+    );
+    my $item_id = $sritem_1->item->itemnumber;
+    my $srstage_1 = $sritem_1->stage;
+    $sritem_1->discard_changes;
+    $sritem_1->stage->position(1);
+    $sritem_1->stage->duration(50);
     my $branch = $builder->build({ source => 'Branch' });
-    $dbitem->itemnumber->holdingbranch($branch->{branchcode});
+    $sritem_1->item->holdingbranch($branch->{branchcode});
 
     # Test a straight up repatriate
-    ok($dbitem->repatriate, "Repatriation done.");
-    my $intransfer = $dbitem->itemnumber->get_transfer;
+    ok($sritem_1->repatriate, "Repatriation done.");
+    my $intransfer = $sritem_1->item->get_transfer;
     is($intransfer->frombranch, $branch->{branchcode}, "Origin correct.");
-    is($intransfer->tobranch, $dbitem->stage->branchcode_id, "Target Correct.");
+    is($intransfer->tobranch, $sritem_1->stage->branchcode_id, "Target Correct.");
+
+    # Reset
+    $intransfer->datearrived(dt_from_string())->store;
+    $sritem_1->item->holdingbranch($branch->{branchcode});
+
+    # Setup a conflicting manual transfer
+    my $item = Koha::Items->find($item_id);
+    $item->request_transfer({ to => $srstage_1->branchcode, reason => "Manual" });
+    $intransfer = $item->get_transfer;
+    is (ref($intransfer), 'Koha::Item::Transfer', "Conflicting transfer added");
+    is ($intransfer->reason, 'Manual', "Conflicting transfer reason is 'Manual'");
+
+    # Stockrotation should handle transfer clashes
+    is($sritem_1->repatriate, 0, "Repatriation skipped if transfer in progress.");
+
+    # Reset
+    $intransfer->datearrived(dt_from_string())->store;
+    $sritem_1->item->holdingbranch($branch->{branchcode});
+
+    # Confirm that stockrotation ignores transfer limits
+    t::lib::Mocks::mock_preference('UseBranchTransferLimits', 1);
+    t::lib::Mocks::mock_preference('BranchTransferLimitsType', 'itemtype');
+    my $limit = Koha::Item::Transfer::Limit->new(
+        {
+            fromBranch => $branch->{branchcode},
+            toBranch   => $srstage_1->branchcode_id,
+            itemtype   => $sritem_1->item->effective_itemtype,
+        }
+    )->store;
+
+    # Stockrotation should overrule transfer limits
+    ok($sritem_1->repatriate, "Repatriation done regardless of transfer limits.");
+    $intransfer = $sritem_1->item->get_transfer;
+    is($intransfer->frombranch, $branch->{branchcode}, "Origin correct.");
+    is($intransfer->tobranch, $sritem_1->stage->branchcode_id, "Target Correct.");
 
     $schema->storage->txn_rollback;
 };
 
 subtest "Tests for needs_advancing." => sub {
-    plan tests => 6;
+    plan tests => 8;
     $schema->storage->txn_begin;
 
     # Test behaviour of item freshly added to rota.
-    my $sritem = $builder->build({
-        source => 'Stockrotationitem',
-        value  => { 'fresh' => 1, },
-    });
+    my $sritem = $builder->build(
+        {
+            source => 'Stockrotationitem',
+            value  => {
+                'fresh'       => 1,
+                itemnumber_id => $builder->build_sample_item->itemnumber
+            },
+        }
+    );
     my $dbitem = Koha::StockRotationItems->find($sritem->{itemnumber_id});
     is($dbitem->needs_advancing, 1, "An item that is fresh will always need advancing.");
 
     # Setup a pristine stockrotation context.
-    $sritem = $builder->build({
-        source => 'Stockrotationitem',
-        value => { 'fresh' => 0,}
-    });
+    $sritem = $builder->build(
+        {
+            source => 'Stockrotationitem',
+            value  => {
+                'fresh'       => 0,
+                itemnumber_id => $builder->build_sample_item->itemnumber
+            }
+        }
+    );
     $dbitem = Koha::StockRotationItems->find($sritem->{itemnumber_id});
-    $dbitem->itemnumber->homebranch($dbitem->stage->branchcode_id);
-    $dbitem->itemnumber->holdingbranch($dbitem->stage->branchcode_id);
+    $dbitem->item->homebranch($dbitem->stage->branchcode_id);
+    $dbitem->item->holdingbranch($dbitem->stage->branchcode_id);
     $dbitem->stage->position(1);
     $dbitem->stage->duration(50);
 
@@ -173,9 +238,9 @@ subtest "Tests for needs_advancing." => sub {
         'itemnumber'  => $dbitem->itemnumber_id,
         'frombranch'  => $dbitem->stage->branchcode_id,
         'tobranch'    => $dbitem->stage->branchcode_id,
-        'datesent'    => DateTime->now,
+        'datesent'    => dt_from_string(),
         'datearrived' => undef,
-        'comments'    => "StockrotationAdvance",
+        'reason'      => "StockrotationAdvance",
     })->store;
 
     # Test item will not be advanced if in transit.
@@ -186,7 +251,7 @@ subtest "Tests for needs_advancing." => sub {
     $dbitem->fresh(0)->store;
 
     # Test item will not be advanced if it has not spent enough time.
-    $dbtransfer->datearrived(DateTime->now)->store;
+    $dbtransfer->datearrived(dt_from_string())->store;
     is($dbitem->needs_advancing, 0, "Not ready to advance: Not spent enough time.");
     # Test item will be advanced if it has not spent enough time, but is fresh.
     $dbitem->fresh(1)->store;
@@ -195,86 +260,272 @@ subtest "Tests for needs_advancing." => sub {
 
     # Test item will be advanced if it has spent enough time.
     $dbtransfer->datesent(      # Item was sent 100 days ago...
-        DateTime->now - DateTime::Duration->new( days => 100 )
+        dt_from_string() - DateTime::Duration->new( days => 100 )
     )->store;
     $dbtransfer->datearrived(   # And arrived 75 days ago.
-        DateTime->now - DateTime::Duration->new( days => 75 )
+        dt_from_string() - DateTime::Duration->new( days => 75 )
     )->store;
     is($dbitem->needs_advancing, 1, "Ready to be advanced.");
 
+    # Bug 30518: Confirm that DST boundaries do not explode.
+    # mock_config does not work here, because of tz vs timezone subroutines
+    my $context = Test::MockModule->new('C4::Context');
+    $context->mock( 'tz', sub {
+        'Europe/London';
+    });
+    my $bad_date = dt_from_string("2020-09-29T01:15:30", 'iso');
+    $dbtransfer->datesent($bad_date)->store;
+    $dbtransfer->datearrived($bad_date)->store;
+    $dbitem->stage->duration(180)->store;
+    is( $dbitem->needs_advancing, 1, "DST boundary doesn't cause failure." );
+    $context->unmock('tz');
+
+    # Test that missing historical branch transfers do not crash
+    $dbtransfer->delete;
+    warning_is {$dbitem->needs_advancing} "We have no historical branch transfer for item " . $dbitem->item->itemnumber . "; This should not have happened!", "Missing transfer is warned.";
+
     $schema->storage->txn_rollback;
 };
 
 subtest "Tests for advance." => sub {
-    plan tests => 15;
+    plan tests => 48;
     $schema->storage->txn_begin;
 
-    my $sritem = $builder->build({
-        source => 'Stockrotationitem',
-        value => { 'fresh' => 1 }
-    });
-    my $dbitem = Koha::StockRotationItems->find($sritem->{itemnumber_id});
-    $dbitem->itemnumber->holdingbranch($dbitem->stage->branchcode_id);
-    my $dbstage = $dbitem->stage;
-    $dbstage->position(1)->duration(50)->store; # Configure stage.
+    my $sritem_1 = $builder->build_object(
+        {
+            class => 'Koha::StockRotationItems',
+            value  => {
+                'fresh'       => 1,
+                itemnumber_id => $builder->build_sample_item->itemnumber
+            }
+        }
+    );
+    $sritem_1->discard_changes;
+    $sritem_1->item->holdingbranch($sritem_1->stage->branchcode_id);
+    my $item_id = $sritem_1->item->itemnumber;
+    my $srstage_1 = $sritem_1->stage;
+    $srstage_1->position(1)->duration(50)->store; # Configure stage.
     # Configure item
-    $dbitem->itemnumber->holdingbranch($dbstage->branchcode_id)->store;
-    $dbitem->itemnumber->homebranch($dbstage->branchcode_id)->store;
+    $sritem_1->item->holdingbranch($srstage_1->branchcode_id)->store;
+    $sritem_1->item->homebranch($srstage_1->branchcode_id)->store;
     # Sanity check
-    is($dbitem->stage->stage_id, $dbstage->stage_id, "Stage sanity check.");
+    is($sritem_1->stage->stage_id, $srstage_1->stage_id, "Stage sanity check.");
 
     # Test if an item is fresh, always move to first stage.
-    is($dbitem->fresh, 1, "Fresh is correct.");
-    $dbitem->advance;
-    is($dbitem->stage->stage_id, $dbstage->stage_id, "Stage is first stage after fresh advance.");
-    is($dbitem->fresh, 0, "Fresh reset after advance.");
+    is($sritem_1->fresh, 1, "Fresh is correct.");
+    $sritem_1->advance;
+    is($sritem_1->stage->stage_id, $srstage_1->stage_id, "Stage is first stage after fresh advance.");
+    is($sritem_1->fresh, 0, "Fresh reset after advance.");
 
     # Test cases of single stage
-    $dbstage->rota->cyclical(1)->store;         # Set Rota to cyclical.
-    ok($dbitem->advance, "Single stage cyclical advance done.");
-    ## Refetch dbitem
-    $dbitem = Koha::StockRotationItems->find($sritem->{itemnumber_id});
-    is($dbitem->stage->stage_id, $dbstage->stage_id, "Single stage cyclical stage OK.");
+    $srstage_1->rota->cyclical(1)->store;         # Set Rota to cyclical.
+    ok($sritem_1->advance, "Single stage cyclical advance done.");
+    ## Refetch sritem_1
+    $sritem_1->discard_changes;
+    is($sritem_1->stage->stage_id, $srstage_1->stage_id, "Single stage cyclical stage OK.");
 
     # Test with indemand advance
-    $dbitem->indemand(1)->store;
-    ok($dbitem->advance, "Indemand item advance done.");
-    ## Refetch dbitem
-    $dbitem = Koha::StockRotationItems->find($sritem->{itemnumber_id});
-    is($dbitem->indemand, 0, "Indemand OK.");
-    is($dbitem->stage->stage_id, $dbstage->stage_id, "Indemand item advance stage OK.");
+    $sritem_1->indemand(1)->store;
+    ok($sritem_1->advance, "Indemand item advance done.");
+    ## Refetch sritem_1
+    $sritem_1->discard_changes;
+    is($sritem_1->indemand, 0, "Indemand OK.");
+    is($sritem_1->stage->stage_id, $srstage_1->stage_id, "Indemand item advance stage OK.");
 
     # Multi stages
-    my $srstage = $builder->build({
-        source => 'Stockrotationstage',
+    my $srstage_2 = $builder->build_object({
+        class => 'Koha::StockRotationStages',
         value => { duration => 50 }
     });
-    my $dbstage2 = Koha::StockRotationStages->find($srstage->{stage_id});
-    $dbstage2->move_to_group($dbitem->stage->rota_id);
-    $dbstage2->move_last;
+    $srstage_2->discard_changes;
+    $srstage_2->move_to_group($sritem_1->stage->rota_id);
+    $srstage_2->move_last;
 
     # Test a straight up advance
-    ok($dbitem->advance, "Advancement done.");
-    ## Refetch dbitem
-    $dbitem = Koha::StockRotationItems->find($sritem->{itemnumber_id});
+    ok($sritem_1->advance, "Advancement done.");
+    ## Refetch sritem_1
+    $sritem_1->discard_changes;
     ## Test results
-    is($dbitem->stage->stage_id, $dbstage2->stage_id, "Stage updated.");
-    my $intransfer = $dbitem->itemnumber->get_transfer;
-    is($intransfer->frombranch, $dbstage->branchcode_id, "Origin correct.");
-    is($intransfer->tobranch, $dbstage2->branchcode_id, "Target Correct.");
+    is($sritem_1->stage->stage_id, $srstage_2->stage_id, "Stage updated.");
+    is(
+        $sritem_1->item->homebranch,
+        $srstage_2->branchcode_id,
+        "Item homebranch updated"
+    );
+    my $transfer_request = $sritem_1->item->get_transfer;
+    is($transfer_request->frombranch, $srstage_1->branchcode_id, "Origin correct.");
+    is($transfer_request->tobranch, $srstage_2->branchcode_id, "Target Correct.");
+    is($transfer_request->datesent, undef, "Transfer requested, but not sent.");
 
-    $dbstage->rota->cyclical(0)->store;         # Set Rota to non-cyclical.
+    # Arrive at new branch
+    $transfer_request->datearrived(dt_from_string())->store;
+    $sritem_1->item->holdingbranch($srstage_2->branchcode_id)->store;
+
+    # Test a cyclical advance
+    ok($sritem_1->advance, "Cyclical advancement done.");
+    ## Refetch sritem_1
+    $sritem_1->discard_changes;
+    ## Test results
+    is($sritem_1->stage->stage_id, $srstage_1->stage_id, "Stage updated.");
+    is(
+        $sritem_1->item->homebranch,
+        $srstage_1->branchcode_id,
+        "Item homebranch updated"
+    );
+    $transfer_request = $sritem_1->item->get_transfer;
+    is($transfer_request->frombranch, $srstage_2->branchcode_id, "Origin correct.");
+    is($transfer_request->tobranch, $srstage_1->branchcode_id, "Target correct.");
+
+    # Arrive at new branch
+    $transfer_request->datearrived(dt_from_string())->store;
+    $sritem_1->item->holdingbranch($srstage_1->branchcode_id)->store;
+
+    # Confirm that stockrotation ignores transfer limits
+    t::lib::Mocks::mock_preference('UseBranchTransferLimits', 1);
+    t::lib::Mocks::mock_preference('BranchTransferLimitsType', 'itemtype');
+    my $limit = Koha::Item::Transfer::Limit->new(
+        {
+            fromBranch => $srstage_1->branchcode_id,
+            toBranch   => $srstage_2->branchcode_id,
+            itemtype   => $sritem_1->item->effective_itemtype,
+        }
+    )->store;
+
+    ok($sritem_1->advance, "Advancement overrules transfer limits.");
+    ## Refetch sritem_1
+    $sritem_1->discard_changes;
+    ## Test results
+    is($sritem_1->stage->stage_id, $srstage_2->stage_id, "Stage updated ignoring transfer limits.");
+    is(
+        $sritem_1->item->homebranch,
+        $srstage_2->branchcode_id,
+        "Item homebranch updated ignoring transfer limits"
+    );
+    $transfer_request = $sritem_1->item->get_transfer;
+    is($transfer_request->frombranch, $srstage_1->branchcode_id, "Origin correct ignoring transfer limits.");
+    is($transfer_request->tobranch, $srstage_2->branchcode_id, "Target correct ignoring transfer limits.");
 
     # Arrive at new branch
-    $intransfer->datearrived(DateTime->now)->store;
-    $dbitem->itemnumber->holdingbranch($srstage->{branchcode_id})->store;
-    $dbitem->itemnumber->homebranch($srstage->{branchcode_id})->store;
+    $transfer_request->datearrived(dt_from_string())->store;
+    $sritem_1->item->holdingbranch($srstage_2->branchcode_id)->store;
+
+    # Setup a conflicting manual transfer
+    my $item = Koha::Items->find($item_id);
+    $item->request_transfer({ to => $srstage_1->branchcode, reason => "Manual" });
+    $transfer_request = $item->get_transfer;
+    is (ref($transfer_request), 'Koha::Item::Transfer', "Conflicting transfer added");
+    is ($transfer_request->reason, 'Manual', "Conflicting transfer reason is 'Manual'");
+
+    # Advance item whilst conflicting manual transfer exists
+    ok($sritem_1->advance, "Advancement done.");
+    ## Refetch sritem_1
+    $sritem_1->discard_changes;
+
+    ## Refetch conflicted transfer
+    $transfer_request->discard_changes;
+
+    # Conflicted transfer should have been cancelled
+    isnt($transfer_request->datecancelled, undef, "Conflicting manual transfer was cancelled");
+
+    # StockRotationAdvance transfer added
+    $transfer_request = $sritem_1->item->get_transfer;
+    is($transfer_request->reason, 'StockrotationAdvance', "StockrotationAdvance transfer added");
+    is($transfer_request->frombranch, $srstage_2->branchcode_id, "Origin correct.");
+    is($transfer_request->tobranch, $srstage_1->branchcode_id, "Target correct.");
+
+    # Arrive at new branch
+    $transfer_request->datearrived(dt_from_string())->store;
+    $sritem_1->item->holdingbranch($srstage_1->branchcode_id)->store;
+
+    # Setup a conflicting reserve transfer
+    $item->request_transfer({ to => $srstage_2->branchcode, reason => "Reserve" });
+    $transfer_request = $item->get_transfer;
+    is (ref($transfer_request), 'Koha::Item::Transfer', "Conflicting transfer added");
+    is ($transfer_request->reason, 'Reserve', "Conflicting transfer reason is 'Reserve'");
+
+    # Advance item whilst conflicting reserve transfer exists
+    ok($sritem_1->advance, "Advancement done.");
+    ## Refetch sritem_1
+    $sritem_1->discard_changes;
+
+    ## Refetch conflicted transfer
+    $transfer_request->discard_changes;
+
+    # Conflicted transfer should not been cancelled
+    is($transfer_request->datecancelled, undef, "Conflicting reserve transfer was not cancelled");
+
+    # StockRotationAdvance transfer added
+    my $transfer_requests = Koha::Item::Transfers->search(
+        {
+            itemnumber    => $sritem_1->item->itemnumber,
+            datearrived   => undef,
+            datecancelled => undef
+        }
+    );
+    is($transfer_requests->count, '2', "StockrotationAdvance transfer queued");
+
+    # Arrive at new branch
+    $transfer_request->datearrived(dt_from_string())->store;
+    $sritem_1->item->holdingbranch($srstage_2->branchcode_id)->store;
+
+    # StockRotationAdvance transfer added
+    $transfer_request = $sritem_1->item->get_transfer;
+    is($transfer_request->reason, 'StockrotationAdvance', "StockrotationAdvance transfer remains after reserve is met");
+    is($transfer_request->frombranch, $srstage_1->branchcode_id, "Origin correct.");
+    is($transfer_request->tobranch, $srstage_2->branchcode_id, "Target correct.");
+
+    # Arrive at new branch
+    $transfer_request->datearrived(dt_from_string())->store;
+    $sritem_1->item->holdingbranch($srstage_2->branchcode_id)->store;
+
+    # Checked out item, advanced to next stage, checkedout from next stage
+    # transfer should be generated, but not initiated
+    my $issue = $builder->build_object({
+        class => 'Koha::Checkouts',
+        value => {
+             branchcode => $srstage_1->branchcode_id,
+             itemnumber => $sritem_1->item->itemnumber,
+             returndate => undef
+        }
+    });
+    $sritem_1->item->holdingbranch($srstage_1->branchcode_id)->store;
+    ok($sritem_1->advance, "Advancement done.");
+    $transfer_request = $sritem_1->item->get_transfer;
+    is($transfer_request->frombranch, $srstage_1->branchcode_id, "Origin correct.");
+    is($transfer_request->tobranch, $srstage_1->branchcode_id, "Target correct.");
+    is($transfer_request->datesent, undef, "Transfer waiting to initiate until return.");
+
+    $issue->delete; #remove issue
+    $sritem_1->advance; #advance back to second stage
+    # Set arrived
+    $transfer_request->datearrived(dt_from_string())->store;
+    $sritem_1->item->holdingbranch($srstage_2->branchcode_id)->store;
+
+
+    $srstage_1->rota->cyclical(0)->store;         # Set Rota to non-cyclical.
+
+    my $srstage_3 = $builder->build_object({
+        class => 'Koha::StockRotationStages',
+        value => { duration => 50 }
+    });
+    $srstage_3->discard_changes;
+    $srstage_3->move_to_group($sritem_1->stage->rota_id);
+    $srstage_3->move_last;
+
+    # Advance again, to end of rota.
+    ok($sritem_1->advance, "Non-cyclical advance to last stage.");
+
+    # Arrive at new branch
+    $transfer_request->datearrived(dt_from_string())->store;
+    $sritem_1->item->holdingbranch($srstage_3->branchcode_id)->store;
 
     # Advance again, Remove from rota.
-    ok($dbitem->advance, "Non-cyclical advance.");
-    ## Refetch dbitem
-    $dbitem = Koha::StockRotationItems->find($sritem->{itemnumber_id});
-    is($dbitem, undef, "StockRotationItem has been removed.");
+    ok($sritem_1->advance, "Non-cyclical advance.");
+    ## Refetch sritem_1
+    $sritem_1 = Koha::StockRotationItems->find({ itemnumber_id => $item_id });
+    is($sritem_1, undef, "StockRotationItem has been removed.");
+    $item = Koha::Items->find($item_id);
+    is($item->homebranch, $srstage_3->branchcode_id, "Item homebranch remains");
 
     $schema->storage->txn_rollback;
 };
@@ -284,53 +535,87 @@ subtest "Tests for investigate (singular)." => sub {
     $schema->storage->txn_begin;
 
     # Test brand new item's investigation ['initiation']
-    my $sritem = $builder->build({ source => 'Stockrotationitem', value => { fresh => 1 } });
+    my $sritem = $builder->build(
+        {
+            source => 'Stockrotationitem',
+            value  => {
+                fresh         => 1,
+                itemnumber_id => $builder->build_sample_item->itemnumber
+            }
+        }
+    );
     my $dbitem = Koha::StockRotationItems->find($sritem->{itemnumber_id});
     is($dbitem->investigate->{reason}, 'initiation', "fresh item initiates.");
 
     # Test brand new item at stagebranch ['initiation']
-    $sritem = $builder->build({ source => 'Stockrotationitem', value => { fresh => 1 } });
+    $sritem = $builder->build(
+        {
+            source => 'Stockrotationitem',
+            value  => {
+                fresh         => 1,
+                itemnumber_id => $builder->build_sample_item->itemnumber
+            }
+        }
+    );
     $dbitem = Koha::StockRotationItems->find($sritem->{itemnumber_id});
-    $dbitem->itemnumber->homebranch($dbitem->stage->branchcode_id)->store;
-    $dbitem->itemnumber->holdingbranch($dbitem->stage->branchcode_id)->store;
+    $dbitem->item->homebranch($dbitem->stage->branchcode_id)->store;
+    $dbitem->item->holdingbranch($dbitem->stage->branchcode_id)->store;
     is($dbitem->investigate->{reason}, 'initiation', "fresh item at stagebranch initiates.");
 
     # Test item not at stagebranch with branchtransfer history ['repatriation']
-    $sritem = $builder->build({
-        source => 'Stockrotationitem',
-        value => { 'fresh'       => 0,}
-    });
+    $sritem = $builder->build(
+        {
+            source => 'Stockrotationitem',
+            value  => {
+                'fresh'       => 0,
+                itemnumber_id => $builder->build_sample_item->itemnumber
+            }
+        }
+    );
     $dbitem = Koha::StockRotationItems->find($sritem->{itemnumber_id});
     my $dbtransfer = Koha::Item::Transfer->new({
         'itemnumber'  => $dbitem->itemnumber_id,
-        'frombranch'  => $dbitem->itemnumber->homebranch,
-        'tobranch'    => $dbitem->itemnumber->homebranch,
-        'datesent'    => DateTime->now,
-        'datearrived' => DateTime->now,
-        'comments'    => "StockrotationAdvance",
+        'frombranch'  => $dbitem->item->homebranch,
+        'tobranch'    => $dbitem->item->homebranch,
+        'datesent'    => dt_from_string(),
+        'datearrived' => dt_from_string(),
+        'reason'      => "StockrotationAdvance",
     })->store;
     is($dbitem->investigate->{reason}, 'repatriation', "older item repatriates.");
 
     # Test item at stagebranch with branchtransfer history ['not-ready']
-    $sritem = $builder->build({
-        source => 'Stockrotationitem',
-        value => { 'fresh'       => 0,}
-    });
+    $sritem = $builder->build(
+        {
+            source => 'Stockrotationitem',
+            value  => {
+                'fresh'       => 0,
+                itemnumber_id => $builder->build_sample_item->itemnumber
+            }
+        }
+    );
     $dbitem = Koha::StockRotationItems->find($sritem->{itemnumber_id});
     $dbtransfer = Koha::Item::Transfer->new({
         'itemnumber'  => $dbitem->itemnumber_id,
-        'frombranch'  => $dbitem->itemnumber->homebranch,
+        'frombranch'  => $dbitem->item->homebranch,
         'tobranch'    => $dbitem->stage->branchcode_id,
-        'datesent'    => DateTime->now,
-        'datearrived' => DateTime->now,
-        'comments'    => "StockrotationAdvance",
+        'datesent'    => dt_from_string(),
+        'datearrived' => dt_from_string(),
+        'reason'      => "StockrotationAdvance",
     })->store;
-    $dbitem->itemnumber->homebranch($dbitem->stage->branchcode_id)->store;
-    $dbitem->itemnumber->holdingbranch($dbitem->stage->branchcode_id)->store;
+    $dbitem->item->homebranch($dbitem->stage->branchcode_id)->store;
+    $dbitem->item->holdingbranch($dbitem->stage->branchcode_id)->store;
     is($dbitem->investigate->{reason}, 'not-ready', "older item at stagebranch not-ready.");
 
     # Test item due for advancement ['advancement']
-    $sritem = $builder->build({ source => 'Stockrotationitem', value => { fresh => 0 } });
+    $sritem = $builder->build(
+        {
+            source => 'Stockrotationitem',
+            value  => {
+                fresh         => 0,
+                itemnumber_id => $builder->build_sample_item->itemnumber
+            }
+        }
+    );
     $dbitem = Koha::StockRotationItems->find($sritem->{itemnumber_id});
     $dbitem->indemand(0)->store;
     $dbitem->stage->duration(50)->store;
@@ -338,19 +623,27 @@ subtest "Tests for investigate (singular)." => sub {
     my $arrived_duration =  DateTime::Duration->new( days => 52);
     $dbtransfer = Koha::Item::Transfer->new({
         'itemnumber'  => $dbitem->itemnumber_id,
-        'frombranch'  => $dbitem->itemnumber->homebranch,
+        'frombranch'  => $dbitem->item->homebranch,
         'tobranch'    => $dbitem->stage->branchcode_id,
-        'datesent'    => DateTime->now - $sent_duration,
-        'datearrived' => DateTime->now - $arrived_duration,
-        'comments'    => "StockrotationAdvance",
+        'datesent'    => dt_from_string() - $sent_duration,
+        'datearrived' => dt_from_string() - $arrived_duration,
+        'reason'      => "StockrotationAdvance",
     })->store;
-    $dbitem->itemnumber->homebranch($dbitem->stage->branchcode_id)->store;
-    $dbitem->itemnumber->holdingbranch($dbitem->stage->branchcode_id)->store;
+    $dbitem->item->homebranch($dbitem->stage->branchcode_id)->store;
+    $dbitem->item->holdingbranch($dbitem->stage->branchcode_id)->store;
     is($dbitem->investigate->{reason}, 'advancement',
        "Item ready for advancement.");
 
     # Test item due for advancement but in-demand ['in-demand']
-    $sritem = $builder->build({ source => 'Stockrotationitem', value => { fresh => 0 } });
+    $sritem = $builder->build(
+        {
+            source => 'Stockrotationitem',
+            value  => {
+                fresh         => 0,
+                itemnumber_id => $builder->build_sample_item->itemnumber
+            }
+        }
+    );
     $dbitem = Koha::StockRotationItems->find($sritem->{itemnumber_id});
     $dbitem->indemand(1)->store;
     $dbitem->stage->duration(50)->store;
@@ -358,19 +651,27 @@ subtest "Tests for investigate (singular)." => sub {
     $arrived_duration =  DateTime::Duration->new( days => 52);
     $dbtransfer = Koha::Item::Transfer->new({
         'itemnumber'  => $dbitem->itemnumber_id,
-        'frombranch'  => $dbitem->itemnumber->homebranch,
+        'frombranch'  => $dbitem->item->homebranch,
         'tobranch'    => $dbitem->stage->branchcode_id,
-        'datesent'    => DateTime->now - $sent_duration,
-        'datearrived' => DateTime->now - $arrived_duration,
-        'comments'    => "StockrotationAdvance",
+        'datesent'    => dt_from_string() - $sent_duration,
+        'datearrived' => dt_from_string() - $arrived_duration,
+        'reason'      => "StockrotationAdvance",
     })->store;
-    $dbitem->itemnumber->homebranch($dbitem->stage->branchcode_id)->store;
-    $dbitem->itemnumber->holdingbranch($dbitem->stage->branchcode_id)->store;
+    $dbitem->item->homebranch($dbitem->stage->branchcode_id)->store;
+    $dbitem->item->holdingbranch($dbitem->stage->branchcode_id)->store;
     is($dbitem->investigate->{reason}, 'in-demand',
        "Item advances, but in-demand.");
 
     # Test item ready for advancement, but at wrong library ['repatriation']
-    $sritem = $builder->build({ source => 'Stockrotationitem', value => { fresh => 0 } });
+    $sritem = $builder->build(
+        {
+            source => 'Stockrotationitem',
+            value  => {
+                fresh         => 0,
+                itemnumber_id => $builder->build_sample_item->itemnumber
+            }
+        }
+    );
     $dbitem = Koha::StockRotationItems->find($sritem->{itemnumber_id});
     $dbitem->indemand(0)->store;
     $dbitem->stage->duration(50)->store;
@@ -378,11 +679,11 @@ subtest "Tests for investigate (singular)." => sub {
     $arrived_duration =  DateTime::Duration->new( days => 52);
     $dbtransfer = Koha::Item::Transfer->new({
         'itemnumber'  => $dbitem->itemnumber_id,
-        'frombranch'  => $dbitem->itemnumber->homebranch,
+        'frombranch'  => $dbitem->item->homebranch,
         'tobranch'    => $dbitem->stage->branchcode_id,
-        'datesent'    => DateTime->now - $sent_duration,
-        'datearrived' => DateTime->now - $arrived_duration,
-        'comments'    => "StockrotationAdvance",
+        'datesent'    => dt_from_string() - $sent_duration,
+        'datearrived' => dt_from_string() - $arrived_duration,
+        'reason'      => "StockrotationAdvance",
     })->store;
     is($dbitem->investigate->{reason}, 'repatriation',
        "Item advances, but not at stage branch.");
@@ -390,4 +691,60 @@ subtest "Tests for investigate (singular)." => sub {
     $schema->storage->txn_rollback;
 };
 
+subtest "Tests for toggle_indemand" => sub {
+    plan tests => 15;
+    $schema->storage->txn_begin;
+
+    my $sritem = $builder->build({
+        source => 'Stockrotationitem',
+        value => { 'fresh' => 0, 'indemand' => 0 }
+    });
+    my $dbitem = Koha::StockRotationItems->find($sritem->{itemnumber_id});
+    my $firstbranch = $dbitem->stage->branchcode_id;
+    $dbitem->item->holdingbranch($firstbranch)->store;
+    my $dbstage = $dbitem->stage;
+    $dbstage->position(1)->duration(50)->store; # Configure stage.
+    # Configure item
+    $dbitem->item->holdingbranch($firstbranch)->store;
+    $dbitem->item->homebranch($firstbranch)->store;
+    # Sanity check
+    is($dbitem->stage->stage_id, $dbstage->stage_id, "Stage sanity check.");
+
+    # Test if an item is not in transfer, toggle always acts.
+    is($dbitem->indemand, 0, "Item not in transfer starts with indemand disabled.");
+    $dbitem->toggle_indemand;
+    is($dbitem->indemand, 1, "Item not in transfer toggled correctly first time.");
+    $dbitem->toggle_indemand;
+    is($dbitem->indemand, 0, "Item not in transfer toggled correctly second time.");
+
+    # Add stages
+    my $srstage = $builder->build({
+        source => 'Stockrotationstage',
+        value => { duration => 50 }
+    });
+    my $dbstage2 = Koha::StockRotationStages->find($srstage->{stage_id});
+    $dbstage2->move_to_group($dbitem->stage->rota_id);
+    $dbstage2->position(2)->store;
+    my $secondbranch = $dbstage2->branchcode_id;
+
+    # Test an item in transfer, toggle cancels transfer and resets indemand.
+    ok($dbitem->advance, "Advancement done.");
+    $dbitem->get_from_storage;
+    my $transfer = $dbitem->item->get_transfer;
+    is(ref($transfer), 'Koha::Item::Transfer', 'Item set to in transfer as expected');
+    is($transfer->frombranch, $firstbranch, 'Transfer from set correctly');
+    is($transfer->tobranch, $secondbranch, 'Transfer to set correctly');
+    is($transfer->datearrived, undef, 'Transfer datearrived not set');
+    $dbitem->toggle_indemand;
+    my $updated_transfer = $transfer->get_from_storage;
+    is($updated_transfer->frombranch, $firstbranch, 'Transfer from retained correctly');
+    is($updated_transfer->tobranch, $firstbranch, 'Transfer to updated correctly');
+    isnt($updated_transfer->datearrived, undef, 'Transfer datearrived set as expected');
+    is($dbitem->indemand, 0, "Item retains indemand as expected.");
+    is($dbitem->stage_id, $dbstage->id, 'Item stage reset as expected.');
+    is($dbitem->item->homebranch, $firstbranch, 'Item homebranch reset as expected.');
+
+    $schema->storage->txn_rollback;
+};
+
 1;